Implement the SVG-to-shapes converter in src/svg.ts with unit tests #209
Closed
opened 2026-09-14 23:13:44 +00:00 by david
·
1 comment
No Branch/Tag specified
main
feature/issue-342/mssql-write-access-check-and-startup-warning-never
feature/issue-331/mssql-write-extensions-mssql-readme-md-setup-tools
feature/issue-330/mssql-register-extension-add-tests-to-root-test-sc
feature/issue-329/mssql-index-ts-wiring-load-env-refuse-on-invalid-l
feature/issue-328/mssql-mssql-table-schema-tool-parameterised
feature/issue-327/mssql-mssql-list-tables-tool
feature/issue-326/mssql-mssql-query-and-mssql-query-one-tools
feature/issue-325/mssql-stream-runner-with-row-cap-cancel-and-timeou
feature/issue-324/mssql-pool-config-builder-encrypt-on-certificate-s
feature/issue-323/mssql-audit-log-writer-pi-mssql-audit-log-mode-060
feature/issue-322/mssql-read-only-guard-module-validatesql-with-corp
feature/issue-321/mssql-certificate-trust-setting-and-limit-validati
feature/issue-320/mssql-loadenvfile-and-mssql-url-parsing
feature/issue-319/mssql-extension-skeleton-permanent-corpora-and-dep
feature/issue-318/mssql-spike-write-result-md-apply-the-gate-open-th
feature/issue-317/mssql-spike-scoring-runner-for-both-candidates
feature/issue-316/mssql-spike-candidate-b-guard-using-the-antlr4-t-s
feature/issue-315/mssql-spike-candidate-a-guard-using-node-sql-parse
feature/issue-314/mssql-spike-must-accept-corpus
feature/issue-313/mssql-spike-scaffold-spike-directory-and-must-reje
feature/issue-311/mssql-spike-check-dependencies-mssql-node-sql-pars
feature/issue-312/mssql-spike-check-whether-extension-tests-run-in-c
feature/issue-299/verify-the-ste-style-live-in-a-short-grill-session
feature/issue-298/bump-the-grillme-extension-to-0-4-0-and-add-the-ch
feature/issue-297/rewrite-extensions-grillme-design-md-in-ste-style
feature/issue-296/rewrite-extensions-grillme-readme-md-in-ste-style
feature/issue-295/rewrite-the-grill-me-command-output-and-read-only
feature/issue-294/rewrite-the-grill-me-tool-metadata-to-the-ste-rule
feature/issue-293/extract-buildgrillprompt-and-add-the-prompt-integr
feature/issue-292/add-ste-style-test-ts-checks-for-the-rule-text-and
feature/issue-291/add-the-pure-ste-style-ts-module-and-inject-the-st
feature/issue-264/verify-the-vision-tool-live-against-the-deepseek-a
feature/issue-263/list-the-vision-extension-in-the-repo-root-readme
feature/issue-262/write-extensions-vision-readme-md-the-10-section-p
feature/issue-261/wire-the-vision-extension-into-package-json-manife
feature/issue-260/implement-extensions-vision-index-ts-factory-regis
feature/issue-258/implement-extensions-vision-src-tool-ts-the-vision
feature/issue-257/implement-extensions-vision-src-client-ts-request
feature/issue-256/implement-extensions-vision-src-usage-ts-usage-map
feature/issue-255/implement-extensions-vision-src-images-ts-read-val
feature/issue-254/implement-extensions-vision-src-config-ts-env-reso
feature/issue-253/implement-extensions-vision-src-errors-ts-toolerro
feature/issue-252/penpot-add-text-stages-zero-area-auto-width-text-p
feature/issue-216/add-the-penpot-skill-and-extension-to-the-repo-roo
feature/issue-217/register-the-penpot-tests-in-npm-test-complete-the
feature/issue-215/write-the-penpot-skill-md-core-workflow-token-conv
feature/issue-214/implement-and-run-fetch-penpot-docs-mjs-and-commit
feature/issue-213/validate-screenshot-reconstruction-against-the-liv
feature/issue-212/add-the-image-to-design-workflow-to-skills-penpot
feature/issue-211/validate-svg-import-icon-and-illustration-against
feature/issue-210/implement-the-penpot-add-svg-tool-with-svg-raw-fal
feature/issue-209/implement-the-svg-to-shapes-converter-in-src-svg-t
feature/issue-208/validate-component-instancing-against-the-live-ins
feature/issue-207/implement-penpot-instance-component-with-id-remapp
feature/issue-206/author-and-commit-the-component-library-artifact-u
feature/issue-205/implement-file-library-linking-and-validate-the-ex
feature/issue-204/implement-penpot-export-library-sse-uri-plus-artif
feature/issue-203/implement-penpot-import-library-multipart-upload-p
feature/issue-202/implement-the-transit-decoder-and-sse-stream-parse
feature/issue-201/validate-a-full-composed-screen-one-commit-one-rev
feature/issue-200/implement-penpot-add-image-with-media-upload
feature/issue-199/implement-asset-reference-resolution-plus-penpot-a
feature/issue-198/implement-penpot-add-frame-with-auto-layout-props
feature/issue-197/validate-asset-creation-atomicity-and-write-safety
feature/issue-196/implement-the-designated-target-write-guard-with-a
feature/issue-195/implement-penpot-commit-with-revn-tracking-conflic
feature/issue-194/implement-the-staged-changeset-store-and-the-colou
feature/issue-193/validate-read-primitives-against-the-live-penpot-i
feature/issue-192/implement-the-penpot-list-library-tool-with-name-t
feature/issue-191/implement-the-penpot-get-file-tool-pages-objects-a
feature/issue-190/implement-the-penpot-list-projects-tool-teams-and
feature/issue-189/implement-the-penpot-whoami-tool-and-wire-the-exte
feature/issue-188/implement-the-rpc-client-and-error-decoding-in-src
feature/issue-187/implement-penpot-url-penpot-token-resolution-in-sr
feature/issue-186/scaffold-the-penpot-extension-directory-and-regist
feature/issue-172/add-end-to-end-main-tests-for-the-fj-rg-tool-state
feature/issue-171/make-reminder-report-dynamic-install-skip-counts-r
feature/issue-170/wire-settings-json-filter-rewrite-into-dedupeandre
feature/issue-169/implement-buildpackagefilters-with-exhaustive-unit
feature/issue-168/refactor-preflight-to-skip-warn-for-missing-fj-rg
feature/issue-167/add-fj-rg-tool-detection-helpers-rgavailable-detec
feature/issue-148/reconcile-design-md-and-implementation-plan-with-t
feature/issue-146/verify-extension-load-behavior-with-and-without-mo
feature/issue-145/write-extensions-mongodb-readme-md
feature/issue-144/write-tool-description-promptsnippet-guidance-for
feature/issue-143/implement-index-ts-async-factory-lifecycle-with-te
feature/issue-142/implement-the-mongo-list-collections-tool-with-uni
feature/issue-141/implement-the-mongo-count-tool-with-unit-tests
feature/issue-140/implement-the-mongo-find-tool-with-unit-tests
feature/issue-139/implement-src-errors-ts-with-unit-tests-toolerror
feature/issue-138/implement-src-serialize-ts-with-unit-tests-ejson-t
feature/issue-136/implement-src-env-ts-with-unit-tests-mongodb-uri-r
feature/issue-135/register-the-mongodb-extension-in-the-repo-root-pa
feature/issue-133/initialize-the-extensions-mongodb-package-package
feature/issue-116/src-actions-ts-downloadrunlogs-zip-guarded-unzip-c
feature/issue-115/src-actions-ts-downloadjoblog-cache-first-attempt
feature/issue-114/src-actions-ts-metadata-queries-listactionruns-get
feature/issue-113/src-actionscache-ts-cache-paths-cache-first-read-w
feature/issue-112/add-zip-extraction-dependency-adm-zip-vs-yauzl
feature/issue-111/tracking-forgejo-actions-tooling-read-only-runs-jo
feature/issue-93/add-unit-tests-for-scripts-local-install-mjs-node
feature/issue-92/validate-install-local-end-to-end-on-the-dev-machi
feature/issue-90/implement-pi-registration-with-url-dedupe-and-relo
feature/issue-89/validate-install-local-from-a-scratch-clone-fresh
feature/issue-88/implement-git-pull-and-npm-install-steps-in-script
feature/issue-74/write-victorialogs-readme-md-and-reconcile-design
feature/issue-73/wire-up-index-ts-extension-factory-and-register-al
feature/issue-72/implement-logs-facets-tool-src-tools-facets-ts
feature/issue-71/implement-logs-hits-tool-src-tools-hits-ts
feature/issue-70/implement-logs-search-tool-src-tools-search-ts
feature/issue-69/implement-parsejsonlines-helper-in-src-client-ts
feature/issue-68/implement-victorialogsrequest-in-src-client-ts
feature/issue-67/implement-resolvebaseurl-in-src-env-ts
feature/issue-66/implement-totoolerror-in-src-errors-ts
feature/issue-65/implement-src-defaults-ts-shared-constants-and-app
feature/issue-64/scaffold-the-victorialogs-pi-extension-project
feature/issue-53/skip-issue-creation-for-chore-documentation-commit
feature/issue-45/fix-local-install-preflight-env-var-requirements-s
feature/issue-41/add-npm-run-local-install-script-to-update-and-ins
feature/issue-26/cross-reference-forgejo-list-milestones-from-issue
feature/issue-25/add-forgejo-milestone-delete-tool
feature/issue-24/add-forgejo-milestone-close-and-forgejo-milestone
feature/issue-23/add-forgejo-milestone-edit-tool
feature/issue-22/add-forgejo-milestone-view-tool
feature/issue-21/add-forgejo-list-milestones-tool
feature/issue-20/add-forgejo-milestone-create-tool
feature/issue-18/add-forgejo-label-create-tool
feature/issue-29/bug-forgejo-issue-create-view-edit-close-etc-crash
No results found.
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills#209
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement
extensions/penpot/src/svg.ts: a bounded SVG reader plus a converter that turns the documented subset —rect,circle,path,text,g, fills, strokes and transforms — into native Penpot shapes, and marks every unsupported node forsvg-rawfallback instead of silently dropping it. Pure, unit-tested, no HTTP.Background
Depends on: #199
Code → design takes SVG as an input format only; the internal currency stays semantic primitives. Penpot's own SVG parser is front-end ClojureScript and is not exposed over RPC, so the converter is ours to build and maintain — which is exactly why its scope must be explicit and its gaps visible.
Scope decisions that this step must honour:
rect,circle,path(a documented command subset),text,g(as a group/parent),svg(the root, contributingviewBox/width/height).pathcommands:M,L,H,V,C,S,Q,T,Z, absolute and relative. Arcs (A) require arc→bézier conversion that is easy to get subtly wrong — mark such nodes unsupported and fall back rather than approximate silently. Record this as a limitation.fill(hex colours,none,currentColortreated as a documented default),fill-opacity,stroke,stroke-width, basicstroke-linecap/stroke-linejoinwhere Penpot has an equivalent.translate,scale,matrix— flattened into geometry by applying the accumulated matrix to the node's coordinates.rotate/skewon a shape whose geometry cannot be expressed axis-aligned after flattening must fall back tosvg-raw(document it); do not emit a shape whoseselrectdisagrees with its geometry, since Penpot's selection/geometry assumptions depend on that consistency.use,mask,filter,clipPath,linearGradientfills, dashed strokes,textPath) →svg-rawfallback, never a silent drop.DOMParser, so this includes writing a bounded XML parser for what SVG actually needs: elements, attributes, self-closing tags, text nodes, comments, CDATA and the standard entities. Do not attempt full XML-spec compliance; document the limits. If, after attempting it, a dependency proves genuinely necessary, raise that as an explicit decision in the issue rather than adding one silently.The output of the converter is a list of staged changes in the same shape as the composition primitives — the next step wraps it in a tool.
Documentation Required
A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting.
docs/reference/svg/rect,circle,path,text,g,svg), including notes onviewBoxand coordinate systems.x,y,width,height,rx,cx,cy,r).dpath-data grammar for the supported command subset.fill,fill-opacity,stroke,stroke-width,stroke-linecap,stroke-linejoin,transform.docs/reference/penpot-api/contentsegment model (move-to/line-to/curve-to/close-path, absolute vs relative) and its required geometry fields. This is what the converter must emit forpathnodes.selrect,points,transform) that must stay consistent for every emitted shape.textnodes.<PENPOT_URL>/api/main/doc/openapi.json— theadd-objchange shape, including thesvg-rawtype the fallback uses.docs/reference/nodejs/node:teststructure for the table-driven tests this module needs.Implementation Details
Create
extensions/penpot/src/svg.ts(plussrc/xml.tsfor the bounded parser, and co-located tests) exporting:src/xml.tsparseXml(text)→ a minimal tree of{ name, attributes, children, text }, handling self-closing tags, attributes with single/double quotes, comments, CDATA, and the five standard entities (& < > " ').src/svg.tsparsePathData(d)→ an ordered list of commands with absolute coordinates resolved (relative commands are resolved against the current point). Emit Penpot path segments; reject arcs explicitly.matrixFrom(transformAttr)→ a 2D affine matrix fortranslate,scale,matrix;rotate/skeware detected and reported as unsupported.applyMatrixToRect/applyMatrixToCircle/applyMatrixToPath→ flat geometry plus a consistency check (selrect/pointsderived from the geometry, never independently).convertSvg(svgText, options)→{ shapes, skipped, report }where:shapesis an ordered list of converter-local shape descriptors (id, parent, type, geometry, style) ready for the next step to turn intoadd-objchanges;skippedlists every node routed tosvg-rawwith a reason (unsupported element "filter",arc path commands not supported,unsupported transform rotate);reportis a summary the tool can print: node counts by type, converted vs fallback, and the distinct reasons.groupToShapes— handlegby producing a Penpot group/frame parent with thetransformapplied to children, or by flattening children when a group cannot be represented.id/nameattributes as layer names where present (e.g.id="logo-mark"→ shape name), and generate stable names otherwise.Table-driven tests are the right shape here: one small fixture per supported node/attribute, one per fallback reason, plus malformed input.
Acceptance Criteria
parseXmlhandles self-closing tags, quoted attributes, comments, CDATA and standard entities, and errors clearly on malformed input with the offending position.rect,circle,path(M/L/H/V/C/S/Q/T/Z, absolute and relative),textandgconvert to shape descriptors with geometry consistent betweenselrect/pointsand the shape's own coordinates.pathnodes containingA/aare reported as unsupported (fallback), with the reason recorded.translate/scale/matrixare flattened into geometry;rotate/skeware reported as unsupported rather than emitted incorrectly.fill(hex/none),fill-opacity,strokeandstroke-widthmap onto Penpot fill/stroke records.skippedwith a specific reason — no node is ever dropped silently (asserted by a test that counts input vs output+skipped).id/nameattributes when present, with stable generated names otherwise.reportgives node counts, converted/fallback totals and the distinct fallback reasons.Test Plan
Fixture-driven checks:
skippedempty.pathwithC/Z→ converted to path shapes; compare segment counts against the fixture's command count.Acommand → the node is reported unsupported (not silently malformed).filter,mask,linearGradientand dashed strokes → every one of those nodes appears inskippedwith its own reason.<) → a clear parse error, not an empty result.pi-loop opened and merged a pull request for this issue: #242