@1agh/maude 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -2
- package/apps/studio/annotations-align.ts +149 -0
- package/apps/studio/annotations-bindings.ts +197 -0
- package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +681 -151
- package/apps/studio/annotations-groups.ts +270 -0
- package/apps/studio/annotations-layer.tsx +4537 -0
- package/apps/studio/annotations-model.ts +2077 -0
- package/apps/studio/annotations-snap.ts +125 -0
- package/{plugins/design/dev-server → apps/studio}/api.ts +616 -70
- package/{plugins/design/dev-server → apps/studio}/artboard-activity-overlay.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/artboard-marquee.tsx +1 -1
- package/apps/studio/bin/_motion-sample-playwright.mjs +129 -0
- package/{plugins/design/dev-server → apps/studio}/bin/_png-playwright.mjs +1 -1
- package/apps/studio/bin/annotate.mjs +732 -0
- package/apps/studio/bin/annotate.sh +17 -0
- package/{plugins/design/dev-server → apps/studio}/bin/canvas-edit.sh +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/draw-proof.sh +37 -2
- package/{plugins/design/dev-server → apps/studio}/bin/preflight.sh +1 -1
- package/apps/studio/bin/read-annotations.mjs +573 -0
- package/apps/studio/bin/read-annotations.sh +11 -0
- package/{plugins/design/dev-server → apps/studio}/bin/runtime-health.sh +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
- package/{plugins/design/dev-server → apps/studio}/bin/server-up.sh +9 -3
- package/apps/studio/bin/to-lottie-verify.html +68 -0
- package/apps/studio/bin/to-lottie.sh +128 -0
- package/{plugins/design/dev-server → apps/studio}/build.ts +6 -4
- package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +79 -10
- package/{plugins/design/dev-server → apps/studio}/canvas-comment-mount.tsx +1 -1
- package/apps/studio/canvas-create.ts +104 -0
- package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +48 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-edit.ts +257 -7
- package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +165 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib-inline.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib-resolver.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib.tsx +31 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +360 -48
- package/apps/studio/client/app.jsx +6742 -0
- package/apps/studio/client/canvas-url.js +81 -0
- package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
- package/apps/studio/client/index.html +16 -0
- package/apps/studio/client/styles/1-tokens-maude.css +296 -0
- package/apps/studio/client/styles/3-shell-maude.css +1161 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/3-shell.css +129 -0
- package/apps/studio/client/styles/4-components-maude.css +74 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/4-components.css +396 -34
- package/apps/studio/client/styles/5-maude-overrides.css +137 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/_index.css +9 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles.css +1 -1
- package/apps/studio/client/tour/overlay.jsx +279 -0
- package/apps/studio/client/tour/usage-tour.js +48 -0
- package/apps/studio/client/whats-new-seen.js +52 -0
- package/apps/studio/client/whats-new.jsx +250 -0
- package/{plugins/design/dev-server → apps/studio}/commands/annotation-strokes-command.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/commands/equal-spacing-command.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/commands/move-artboards-command.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +32 -11
- package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +5 -4
- package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +18 -7
- package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +21 -21
- package/apps/studio/dist/client.bundle.js +25545 -0
- package/apps/studio/dist/comment-mount.js +2046 -0
- package/apps/studio/dist/styles.css +8018 -0
- package/apps/studio/dom-selection.ts +342 -0
- package/apps/studio/draw/animate.ts +333 -0
- package/{plugins/design/dev-server → apps/studio}/draw/brush.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/composition.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/geometry.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/index.ts +4 -1
- package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +1 -1
- package/apps/studio/draw/morph.ts +204 -0
- package/{plugins/design/dev-server → apps/studio}/draw/optimize.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/palette.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/primitives.ts +1 -1
- package/apps/studio/draw/serialize-animate.ts +324 -0
- package/{plugins/design/dev-server → apps/studio}/draw/serialize.ts +9 -3
- package/apps/studio/draw/test/animate.test.ts +172 -0
- package/apps/studio/draw/test/morph.test.ts +107 -0
- package/apps/studio/draw/test/serialize-animate.test.ts +142 -0
- package/{plugins/design/dev-server → apps/studio}/equal-spacing-detector.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/equal-spacing-handles.tsx +7 -29
- package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +16 -4
- package/{plugins/design/dev-server → apps/studio}/history.ts +20 -3
- package/{plugins/design/dev-server → apps/studio}/http.ts +226 -6
- package/{plugins/design/dev-server → apps/studio}/input-router.tsx +15 -1
- package/{plugins/design/dev-server → apps/studio}/inspect.ts +50 -4
- package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +2 -8
- package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
- package/{plugins/design/dev-server → apps/studio}/server.ts +23 -2
- package/{plugins/design/dev-server → apps/studio}/sync/agent.ts +231 -57
- package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +45 -0
- package/apps/studio/sync/cold-start.ts +158 -0
- package/{plugins/design/dev-server → apps/studio}/sync/connection-state.ts +58 -2
- package/{plugins/design/dev-server → apps/studio}/sync/index.ts +563 -221
- package/apps/studio/sync/journal.ts +190 -0
- package/apps/studio/sync/migrate-seed.ts +321 -0
- package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +22 -3
- package/{plugins/design/dev-server → apps/studio}/sync/status.ts +15 -4
- package/{plugins/design/dev-server → apps/studio}/test/activity.test.ts +1 -7
- package/apps/studio/test/annotate-write.test.ts +184 -0
- package/apps/studio/test/annotations-align.test.ts +88 -0
- package/apps/studio/test/annotations-bindings.test.ts +124 -0
- package/apps/studio/test/annotations-groups.test.ts +231 -0
- package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +63 -0
- package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +97 -0
- package/apps/studio/test/annotations-snap.test.ts +79 -0
- package/apps/studio/test/annotations-text-format.test.ts +396 -0
- package/apps/studio/test/asset-api.test.ts +149 -0
- package/apps/studio/test/canvas-create-api.test.ts +361 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +2 -0
- package/apps/studio/test/canvas-edit.test.ts +319 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +1 -1
- package/apps/studio/test/canvas-media-drop.test.ts +158 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +25 -1
- package/apps/studio/test/canvas-url.test.ts +99 -0
- package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +0 -2
- package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +2 -7
- package/{plugins/design/dev-server → apps/studio}/test/csp-canvas-shell.test.ts +5 -0
- package/apps/studio/test/csrf-write-guard.test.ts +52 -0
- package/apps/studio/test/figjam-v3-model.test.ts +342 -0
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +1 -0
- package/apps/studio/test/index-data-canvas-ds.test.ts +97 -0
- package/apps/studio/test/motion-sample.test.ts +35 -0
- package/{plugins/design/dev-server → apps/studio}/test/perf-harness.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/phase-3.6-smoke.test.ts +3 -1
- package/apps/studio/test/read-annotations.test.ts +409 -0
- package/{plugins/design/dev-server → apps/studio}/test/sanitize-annotation-svg.test.ts +108 -2
- package/{plugins/design/dev-server → apps/studio}/test/shared-doc-convergence.test.ts +1 -1
- package/apps/studio/test/shared-doc-migrate.test.ts +369 -0
- package/apps/studio/test/sync-agent.test.ts +575 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-codec.test.ts +65 -0
- package/apps/studio/test/sync-cold-start.test.ts +244 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +68 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +0 -8
- package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +0 -1
- package/apps/studio/test/sync-incident-replay.test.ts +211 -0
- package/apps/studio/test/sync-journal.test.ts +176 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +360 -5
- package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +66 -0
- package/apps/studio/test/tour-overlay.test.tsx +56 -0
- package/apps/studio/test/use-annotation-resize.test.ts +402 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-artboard-drag.test.ts +0 -1
- package/{plugins/design/dev-server → apps/studio}/test/use-collab.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +8 -2
- package/apps/studio/test/whats-new.test.ts +101 -0
- package/apps/studio/text-imports.d.ts +9 -0
- package/{plugins/design/dev-server → apps/studio}/tool-palette.tsx +15 -70
- package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -2
- package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +5 -3
- package/apps/studio/use-annotation-resize.tsx +912 -0
- package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +17 -5
- package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +3 -3
- package/apps/studio/use-canvas-media-drop.tsx +343 -0
- package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +21 -16
- package/{plugins/design/dev-server → apps/studio}/use-cursor-modifiers.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-keyboard-discipline.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/use-selection-set.tsx +18 -1
- package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +8 -2
- package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +1 -1
- package/apps/studio/whats-new.json +264 -0
- package/apps/studio/whats-new.schema.json +86 -0
- package/apps/studio/whats-new.ts +87 -0
- package/cli/bin/maude.mjs +10 -0
- package/cli/commands/design-link.test.mjs +84 -0
- package/cli/commands/design.mjs +16 -7
- package/cli/commands/design.test.mjs +40 -0
- package/cli/commands/help.mjs +8 -2
- package/cli/commands/hub.mjs +3 -3
- package/cli/commands/hub.test.mjs +1 -1
- package/cli/commands/scenario-report.mjs +2 -9
- package/cli/lib/design-link.mjs +51 -1
- package/cli/lib/flow-design-integration.test.mjs +1 -1
- package/cli/lib/gitignore-block.mjs +1 -0
- package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
- package/cli/lib/preflight.mjs +1 -1
- package/package.json +15 -15
- package/plugins/design/templates/_shell.html +28 -4
- package/plugins/design/templates/brief-board.tsx.template +61 -0
- package/plugins/design/templates/canvas.tsx.template +1 -1
- package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -1
- package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +1 -1
- package/plugins/flow/.claude-plugin/config.schema.json +16 -0
- package/plugins/design/dev-server/annotations-layer.tsx +0 -3402
- package/plugins/design/dev-server/client/app.jsx +0 -2985
- package/plugins/design/dev-server/client/index.html +0 -15
- package/plugins/design/dev-server/dist/client.bundle.js +0 -20692
- package/plugins/design/dev-server/dist/comment-mount.js +0 -1924
- package/plugins/design/dev-server/dist/styles.css +0 -2710
- package/plugins/design/dev-server/dom-selection.ts +0 -156
- package/plugins/design/dev-server/sync/migrate-seed.ts +0 -163
- package/plugins/design/dev-server/test/canvas-edit.test.ts +0 -139
- package/plugins/design/dev-server/test/shared-doc-migrate.test.ts +0 -159
- package/plugins/design/dev-server/test/sync-agent.test.ts +0 -305
- package/plugins/design/dev-server/test/use-annotation-resize.test.ts +0 -200
- package/plugins/design/dev-server/use-annotation-resize.tsx +0 -449
- /package/{plugins/design/dev-server → apps/studio}/activity.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_enumerate-artboards-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_html-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pdf-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pptx-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pw-launch.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_screenshot-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_svg-optimize.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_svg-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/asset-sweep.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/bootstrap-check.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/check-runtime-bundles.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/draw-build.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/handoff.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/prep.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/scenario-report.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/slug.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/smoke.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/svg-optimize.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/visual-sanity.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/boot-self-heal.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-build.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-header.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-meta.schema.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-pipeline.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/hmr.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/iframe-lazy.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/0-reset.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/1-tokens.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/2-layout.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/5-utilities.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/ai-activity.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/awareness-bridge.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/index.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/protocol.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/registry.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/room.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/config.schema.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/context.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/.min-sizes.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_decoding.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_encoding.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion_react.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/pixi-js.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom_client.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-dev-runtime.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-runtime.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_awareness.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_sync.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/yjs.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/brush.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/composition.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/geometry.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/optimize.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/palette.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/primitives.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/serialize.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/examples/README.md +0 -0
- /package/{plugins/design/dev-server → apps/studio}/examples/perf-100-artboards.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/_browser-bundles.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/canva-handoff-prompt.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/canva.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/html.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/zip.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/fs-watch.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/handoff.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/hmr-broadcast.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/locator.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/mem.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/runtime-bundle.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/server.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/echo-guard.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/fs-mirror.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/hubs-config.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/untrusted.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/_helpers.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/active-state.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotation-strokes-command.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/artboard-activity-overlay.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/binary-smoke.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/bundle-smoke.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-pipeline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-route.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-awareness-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/comment-mount.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/context-resolve-tokens.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/equal-spacing-detector.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/endpoint.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/html.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pdf.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/png.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx-deck.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/svg.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-20-annotations.svg +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-21-annotations.svg +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fs-watch.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/handoff.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/history-rollback.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/hmr-classify.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/input-router.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/marquee-overlay.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/move-artboards-command.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/participants-chrome.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/paths.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle-error-mapping.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/server-lifecycle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-foundation.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/system-endpoint.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/tool-palette.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/undo-stack.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-annotation-selection.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-cursor-modifiers.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-keyboard-discipline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-selection-set.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/ws-handshake.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/tsconfig.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/ws.ts +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# annotate.sh — thin wrapper around annotate.mjs (the AI annotation WRITE
|
|
3
|
+
# verb). Runs under Bun, not node: the verb imports the CANONICAL TypeScript
|
|
4
|
+
# annotation model (annotations-model.ts — serializer + sanitizer) so it can
|
|
5
|
+
# never emit a shape the canvas wouldn't, and node can't import TS.
|
|
6
|
+
# Reached via `maude design annotate` (DDR-062), never a raw bin path.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# annotate.sh <rel-path> [--ops <file|->] [--flow <file|->] [--near <id>]
|
|
10
|
+
# [--canvas-state <path>] [--root <repo>] [--dry-run]
|
|
11
|
+
# (see annotate.mjs --help for the full contract)
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
14
|
+
echo "annotate: bun is required (the verb imports the canonical TS annotation model). Install: https://bun.sh" >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
exec bun "$SCRIPT_DIR/annotate.mjs" "$@"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# canvas-edit.sh — AST-aware single-attribute edit on a canvas .tsx file.
|
|
3
|
-
# Wraps `
|
|
3
|
+
# Wraps `apps/studio/canvas-edit.ts` so /design:edit Step 3a can
|
|
4
4
|
# shell out without booting a Bun module loader inline. Phase 3.6 Task 5.
|
|
5
5
|
#
|
|
6
6
|
# Usage: canvas-edit.sh <canvas-abs-path> <data-cd-id> <attr> <value>
|
|
@@ -13,13 +13,23 @@
|
|
|
13
13
|
# draw-proof.sh --asset <mark.svg> --slug <name>
|
|
14
14
|
# [--root <repo>] [--out-dir <dir>] [--name <display label>]
|
|
15
15
|
# [--engine auto|agent-browser|playwright] [--timeout <secs>]
|
|
16
|
+
# [--motion] [--motion-selector <css>] [--motion-gap <ms>]
|
|
16
17
|
#
|
|
17
18
|
# Requires a running dev server (caller runs `maude design server-up` first);
|
|
18
19
|
# reads the port from <designRoot>/_server.json.
|
|
19
20
|
#
|
|
21
|
+
# --motion: after the still ladder, run the LIVE-MOTION proof — sample the
|
|
22
|
+
# animated element at two wall-clock times and assert it changed. A still
|
|
23
|
+
# screenshot can't prove animation (DDR-094 M1); a freeze-frame pass + an
|
|
24
|
+
# over-time NO-CHANGE is a HARD FAIL (dead mechanism, e.g. CSS d:path()).
|
|
25
|
+
# --motion-selector targets a specific element (default: auto-detect the
|
|
26
|
+
# parent of the first <animate>/<animateTransform>). --motion-gap sets the
|
|
27
|
+
# sample interval in ms (default 600).
|
|
28
|
+
#
|
|
20
29
|
# Stdout (last line): the screenshot output directory (capturable in $(...)).
|
|
21
30
|
# Stderr: progress / diagnostics.
|
|
22
|
-
# Exit: 0 success / 1 missing input or server / 2 bad args / 3 capture failed
|
|
31
|
+
# Exit: 0 success / 1 missing input or server / 2 bad args / 3 capture failed
|
|
32
|
+
# / 4 motion HARD FAIL (still ladder OK but no over-time change).
|
|
23
33
|
|
|
24
34
|
ASSET=""
|
|
25
35
|
SLUG=""
|
|
@@ -28,6 +38,9 @@ OUT_DIR=""
|
|
|
28
38
|
NAME=""
|
|
29
39
|
ENGINE="auto"
|
|
30
40
|
TIMEOUT=10
|
|
41
|
+
MOTION=0
|
|
42
|
+
MOTION_SELECTOR=""
|
|
43
|
+
MOTION_GAP=600
|
|
31
44
|
|
|
32
45
|
while [ $# -gt 0 ]; do
|
|
33
46
|
case "$1" in
|
|
@@ -38,8 +51,11 @@ while [ $# -gt 0 ]; do
|
|
|
38
51
|
--name) NAME="$2"; shift 2 ;;
|
|
39
52
|
--engine) ENGINE="$2"; shift 2 ;;
|
|
40
53
|
--timeout) TIMEOUT="$2"; shift 2 ;;
|
|
54
|
+
--motion) MOTION=1; shift ;;
|
|
55
|
+
--motion-selector) MOTION_SELECTOR="$2"; shift 2 ;;
|
|
56
|
+
--motion-gap) MOTION_GAP="$2"; shift 2 ;;
|
|
41
57
|
--help|-h)
|
|
42
|
-
sed -n '2,
|
|
58
|
+
sed -n '2,37p' "$0" | sed 's/^# \?//'
|
|
43
59
|
exit 0
|
|
44
60
|
;;
|
|
45
61
|
*)
|
|
@@ -125,5 +141,24 @@ fi
|
|
|
125
141
|
|
|
126
142
|
COUNT=$(find "$OUT_DIR" -maxdepth 1 -type f -name '*.png' 2>/dev/null | wc -l | tr -d ' ')
|
|
127
143
|
echo "→ captured $COUNT proof image(s) in $OUT_DIR" >&2
|
|
144
|
+
|
|
145
|
+
# ---------- live-motion proof (--motion) ----------
|
|
146
|
+
# The still ladder above is the freeze-frame; it cannot prove the mark ANIMATES.
|
|
147
|
+
# Sample the animated element at two wall-clock times and require a delta.
|
|
148
|
+
if [ "$MOTION" -eq 1 ]; then
|
|
149
|
+
echo "→ live-motion proof (over-time delta) …" >&2
|
|
150
|
+
MOTION_ARGS=(--url "$URL" --gap "$MOTION_GAP" --timeout "$TIMEOUT")
|
|
151
|
+
[ -n "$MOTION_SELECTOR" ] && MOTION_ARGS+=(--selector "$MOTION_SELECTOR")
|
|
152
|
+
node "$SCRIPT_DIR/_motion-sample-playwright.mjs" "${MOTION_ARGS[@]}" >&2
|
|
153
|
+
MRC=$?
|
|
154
|
+
case "$MRC" in
|
|
155
|
+
0) echo "→ motion proven (over-time delta confirmed)" >&2 ;;
|
|
156
|
+
4) echo "draw-proof.sh: MOTION HARD FAIL — still ladder OK but the mark does NOT animate over time (dead mechanism). See DDR-094 M1/M2." >&2
|
|
157
|
+
exit 4 ;;
|
|
158
|
+
*) echo "draw-proof.sh: motion sampler error (rc=$MRC) — could not verify animation" >&2
|
|
159
|
+
exit 3 ;;
|
|
160
|
+
esac
|
|
161
|
+
fi
|
|
162
|
+
|
|
128
163
|
# Last stdout line = the output dir, for $(maude design draw-proof ...) capture.
|
|
129
164
|
printf '%s\n' "$OUT_DIR"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
# Exit: 0 if all hard deps pass; 1 otherwise.
|
|
28
28
|
|
|
29
29
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
30
|
-
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR
|
|
30
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
31
31
|
PKG_ROOT="$(cd "$PLUGIN_ROOT/../.." && pwd)"
|
|
32
32
|
|
|
33
33
|
if [ -f "$PKG_ROOT/cli/lib/preflight.mjs" ]; then
|
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// read-annotations.mjs — headless, zero-dep SVG → JSON annotation reader.
|
|
3
|
+
//
|
|
4
|
+
// Phase 22. The browser-side parser `svgToStrokes` (annotations-layer.tsx) needs
|
|
5
|
+
// a DOMParser, which plain Node does not expose. This is its headless cousin: it
|
|
6
|
+
// reads a canvas's `<slug>.annotations.svg` and extracts every sticky / text /
|
|
7
|
+
// shape as structured JSON the `/design:new` ingest path folds into a generation
|
|
8
|
+
// brief. It is a SEPARATE implementation (not an import) of the same fixed
|
|
9
|
+
// vocabulary `strokesToSvg` serializes — `read-annotations.test.ts` rebuilds
|
|
10
|
+
// strokes through the CANONICAL serializer and asserts this reader recovers them,
|
|
11
|
+
// so any drift in the SVG shape (Phase 21 / 23 / 24 …) fails that test loud.
|
|
12
|
+
//
|
|
13
|
+
// Regex-over-known-vocabulary, mirroring the discipline of `sanitizeAnnotationSvg`
|
|
14
|
+
// (api.ts) and the defensive defaults of `svgToStrokes`. We never DOM-parse.
|
|
15
|
+
//
|
|
16
|
+
// Output: a JSON array of `{ tool, id, x, y, w, h, text, color }`. Strokes that
|
|
17
|
+
// carry words (sticky, text) populate `text`; position-only strokes (pen, rect,
|
|
18
|
+
// ellipse, polygon, arrow) emit `text: null` with their bounding box. With
|
|
19
|
+
// `--canvas-state <path>` each annotation also gets `artboard: <id|null>` by bbox
|
|
20
|
+
// overlap. Forward-compat: an unknown `data-tool` (Phase 23 image/link) passes
|
|
21
|
+
// through with any `href`/`url`/`title` it carries, so a later ingest picks up
|
|
22
|
+
// dropped reference media without a reader change.
|
|
23
|
+
//
|
|
24
|
+
// Reached via `maude design read-annotations "<rel-path>"` (DDR-062), never a raw
|
|
25
|
+
// bin path. A missing annotation file is NOT an error — a board with no notes is
|
|
26
|
+
// valid; the reader emits `[]` and exits 0.
|
|
27
|
+
//
|
|
28
|
+
// Usage:
|
|
29
|
+
// read-annotations.mjs <rel-path-relative-to-designRoot> [--root <repo>]
|
|
30
|
+
// [--canvas-state <path>] [--json]
|
|
31
|
+
|
|
32
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
33
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
34
|
+
import { pathToFileURL } from 'node:url';
|
|
35
|
+
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
// Argv
|
|
38
|
+
|
|
39
|
+
function parseArgv(argv) {
|
|
40
|
+
const out = { positional: [], root: null, canvasState: null, help: false, graph: false };
|
|
41
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
42
|
+
const a = argv[i];
|
|
43
|
+
if (a === '--help' || a === '-h') {
|
|
44
|
+
out.help = true;
|
|
45
|
+
} else if (a === '--json') {
|
|
46
|
+
/* default — accepted for symmetry with other verbs */
|
|
47
|
+
} else if (a === '--graph') {
|
|
48
|
+
out.graph = true;
|
|
49
|
+
} else if (a === '--root') {
|
|
50
|
+
i += 1;
|
|
51
|
+
out.root = argv[i];
|
|
52
|
+
} else if (a.startsWith('--root=')) {
|
|
53
|
+
out.root = a.slice('--root='.length);
|
|
54
|
+
} else if (a === '--canvas-state') {
|
|
55
|
+
i += 1;
|
|
56
|
+
out.canvasState = argv[i];
|
|
57
|
+
} else if (a.startsWith('--canvas-state=')) {
|
|
58
|
+
out.canvasState = a.slice('--canvas-state='.length);
|
|
59
|
+
} else {
|
|
60
|
+
out.positional.push(a);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const HELP = `read-annotations.mjs — headless SVG → JSON annotation reader (DDR-062 via \`maude design read-annotations\`)
|
|
67
|
+
|
|
68
|
+
Usage:
|
|
69
|
+
maude design read-annotations <rel-path> [--root <repo>] [--canvas-state <path>]
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
<rel-path> Canvas path relative to the design root (e.g. "ui/Foo.tsx").
|
|
73
|
+
--root <repo> Repo root. Default: $CLAUDE_PROJECT_DIR, then cwd.
|
|
74
|
+
--canvas-state <p> JSON of artboard rects ([{id,x,y,w,h}] or {artboards:[…]});
|
|
75
|
+
each annotation is tagged with the artboard it overlaps,
|
|
76
|
+
plus artboard-relative coords (rel) and a W3C-style
|
|
77
|
+
target block { source, selector, geometry }.
|
|
78
|
+
--graph Emit { annotations, graph } instead of the bare array:
|
|
79
|
+
graph.edges = bound arrows (from/to host ids), graph.nodes
|
|
80
|
+
= the strokes those arrows connect (with labels) — a
|
|
81
|
+
bound flow diagram reads back as a graph.
|
|
82
|
+
--json No-op (JSON is the only output form).
|
|
83
|
+
|
|
84
|
+
FigJam v3 additive fields per annotation: z (render order), groupIds (deepest →
|
|
85
|
+
shallowest), author ("ai" for annotate-verb strokes), and from/to host ids on
|
|
86
|
+
bound arrows.
|
|
87
|
+
|
|
88
|
+
Emits JSON to stdout. A missing annotation file emits [] (exit 0).`;
|
|
89
|
+
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
// Path + slug resolution — mirror the dev-server (api.ts fileSlug, context.ts
|
|
92
|
+
// designRoot). The annotation file is named by fileSlug(), so we recompute the
|
|
93
|
+
// SAME slug to find it.
|
|
94
|
+
|
|
95
|
+
function resolveDesignRoot(repoRoot) {
|
|
96
|
+
// config.json is ALWAYS at <repoRoot>/.design/config.json; its optional
|
|
97
|
+
// `designRoot` field (default ".design") is where canvases + annotation SVGs
|
|
98
|
+
// actually live. (context.ts:64,226 — designRoot defaults to ".design".)
|
|
99
|
+
let designRel = '.design';
|
|
100
|
+
try {
|
|
101
|
+
const cfg = JSON.parse(readFileSync(join(repoRoot, '.design', 'config.json'), 'utf8'));
|
|
102
|
+
if (typeof cfg.designRoot === 'string' && cfg.designRoot.trim()) {
|
|
103
|
+
designRel = cfg.designRoot.replace(/^\/+|\/+$/g, '');
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
/* no config — default .design */
|
|
107
|
+
}
|
|
108
|
+
return { designRel, designRoot: join(repoRoot, designRel) };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Byte-for-byte the producer's rule (api.ts:266 fileSlug). The reader's input is
|
|
112
|
+
// design-root-relative, but we still strip a leading designRel/ prefix so a
|
|
113
|
+
// repo-root-relative path ("./design/ui/Foo.tsx") resolves to the same slug.
|
|
114
|
+
function fileSlug(file, designRel) {
|
|
115
|
+
let p = String(file).replace(/^\/+|\/+$/g, '');
|
|
116
|
+
try {
|
|
117
|
+
p = decodeURIComponent(p);
|
|
118
|
+
} catch {
|
|
119
|
+
/* leave as-is */
|
|
120
|
+
}
|
|
121
|
+
const prefix = `${designRel.replace(/^\/+|\/+$/g, '')}/`;
|
|
122
|
+
if (p.startsWith(prefix)) p = p.slice(prefix.length);
|
|
123
|
+
return p
|
|
124
|
+
.replace(/\//g, '-')
|
|
125
|
+
.replace(/\s+/g, '_')
|
|
126
|
+
.replace(/\.(tsx|html)$/i, '')
|
|
127
|
+
.replace(/^\.+/, '')
|
|
128
|
+
.toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
132
|
+
// Tiny SVG helpers — attribute pluck, entity-decode, number coerce.
|
|
133
|
+
|
|
134
|
+
function attr(s, name) {
|
|
135
|
+
const re = new RegExp(
|
|
136
|
+
`(?:^|\\s)${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=\\s*"([^"]*)"`
|
|
137
|
+
);
|
|
138
|
+
const m = s.match(re);
|
|
139
|
+
return m ? m[1] : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function hasAttr(s, name) {
|
|
143
|
+
return attr(s, name) !== null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Reverse esc() (annotations-layer.tsx): & / " / < are the only
|
|
147
|
+
// entities the serializer writes, but we decode the common set defensively.
|
|
148
|
+
// Named entities first, & LAST so "&lt;" (a literal "<") survives.
|
|
149
|
+
function decodeEntities(s) {
|
|
150
|
+
return String(s)
|
|
151
|
+
.replace(/</g, '<')
|
|
152
|
+
.replace(/>/g, '>')
|
|
153
|
+
.replace(/"/g, '"')
|
|
154
|
+
.replace(/'/g, "'")
|
|
155
|
+
.replace(/'/g, "'")
|
|
156
|
+
.replace(/&/g, '&');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function num(v, d = 0) {
|
|
160
|
+
const n = Number.parseFloat(v);
|
|
161
|
+
return Number.isFinite(n) ? n : d;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function stripTags(s) {
|
|
165
|
+
return String(s).replace(/<[^>]*>/g, '');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
169
|
+
// Per-tool extraction.
|
|
170
|
+
|
|
171
|
+
function readText(attrs, inner) {
|
|
172
|
+
const anchorId = attr(attrs, 'data-anchor-id');
|
|
173
|
+
const standalone = !anchorId;
|
|
174
|
+
const o = {
|
|
175
|
+
tool: 'text',
|
|
176
|
+
id: attr(attrs, 'data-id') || '',
|
|
177
|
+
x: standalone ? num(attr(attrs, 'x')) : null,
|
|
178
|
+
y: standalone ? num(attr(attrs, 'y')) : null,
|
|
179
|
+
w: null,
|
|
180
|
+
h: null,
|
|
181
|
+
text: decodeEntities(inner).trim(),
|
|
182
|
+
color: attr(attrs, 'fill') || attr(attrs, 'stroke') || null,
|
|
183
|
+
};
|
|
184
|
+
if (anchorId) o.anchorId = anchorId;
|
|
185
|
+
return o;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// FigJam v3 — section container: geometry off the inner <rect>, label off
|
|
189
|
+
// data-label (the inner <text> is presentational chrome).
|
|
190
|
+
function readSection(attrs, inner) {
|
|
191
|
+
const rectAttrs = inner.match(/<rect\b([^>]*?)\/?>/)?.[1] ?? '';
|
|
192
|
+
return {
|
|
193
|
+
tool: 'section',
|
|
194
|
+
id: attr(attrs, 'data-id') || '',
|
|
195
|
+
x: num(attr(rectAttrs, 'x')),
|
|
196
|
+
y: num(attr(rectAttrs, 'y')),
|
|
197
|
+
w: num(attr(rectAttrs, 'width')),
|
|
198
|
+
h: num(attr(rectAttrs, 'height')),
|
|
199
|
+
text: decodeEntities(attr(attrs, 'data-label') || ''),
|
|
200
|
+
color: attr(attrs, 'fill') || null,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function readSticky(attrs, inner) {
|
|
205
|
+
// Geometry off the inner <rect>; paper tint off the group fill; body off the
|
|
206
|
+
// inner <text>. (annotations-layer.tsx strokeToSvgEl 'sticky' branch.)
|
|
207
|
+
const rectAttrs = inner.match(/<rect\b([^>]*?)\/?>/)?.[1] ?? '';
|
|
208
|
+
const body = inner.match(/<text\b[^>]*>([\s\S]*?)<\/text>/)?.[1] ?? '';
|
|
209
|
+
return {
|
|
210
|
+
tool: 'sticky',
|
|
211
|
+
id: attr(attrs, 'data-id') || '',
|
|
212
|
+
x: num(attr(rectAttrs, 'x')),
|
|
213
|
+
y: num(attr(rectAttrs, 'y')),
|
|
214
|
+
w: num(attr(rectAttrs, 'width')),
|
|
215
|
+
h: num(attr(rectAttrs, 'height')),
|
|
216
|
+
text: decodeEntities(body).trim(),
|
|
217
|
+
color: attr(attrs, 'fill') || null,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// FigJam v3 — a bound endpoint persists as `data-(start|end)-bind="<hostId>
|
|
222
|
+
// <nx> <ny>"`. Malformed / out-of-range values are rejected (mirror of the
|
|
223
|
+
// canonical parser's clamp).
|
|
224
|
+
function parseBind(raw) {
|
|
225
|
+
if (!raw) return null;
|
|
226
|
+
const parts = String(raw).trim().split(/\s+/);
|
|
227
|
+
if (parts.length !== 3) return null;
|
|
228
|
+
const nx = Number.parseFloat(parts[1]);
|
|
229
|
+
const ny = Number.parseFloat(parts[2]);
|
|
230
|
+
if (!parts[0] || !Number.isFinite(nx) || !Number.isFinite(ny)) return null;
|
|
231
|
+
if (nx < 0 || nx > 1 || ny < 0 || ny > 1) return null;
|
|
232
|
+
return { hostId: parts[0], nx, ny };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function readArrow(attrs, inner) {
|
|
236
|
+
const id = attr(attrs, 'data-id') || '';
|
|
237
|
+
const color = attr(attrs, 'stroke') || null;
|
|
238
|
+
let ends = null;
|
|
239
|
+
const lineAttrs = inner.match(/<line\b([^>]*?)\/?>/)?.[1];
|
|
240
|
+
if (lineAttrs) {
|
|
241
|
+
ends = {
|
|
242
|
+
x1: num(attr(lineAttrs, 'x1')),
|
|
243
|
+
y1: num(attr(lineAttrs, 'y1')),
|
|
244
|
+
x2: num(attr(lineAttrs, 'x2')),
|
|
245
|
+
y2: num(attr(lineAttrs, 'y2')),
|
|
246
|
+
};
|
|
247
|
+
} else {
|
|
248
|
+
// Curved / elbow arrow persists a <path>; first + last coordinate pairs are
|
|
249
|
+
// the endpoints (arrowEndpoints in annotations-layer.tsx).
|
|
250
|
+
const d = inner.match(/<path\b([^>]*?)\/?>/)?.[1];
|
|
251
|
+
const nums = d ? (attr(d, 'd') || '').match(/-?\d+(?:\.\d+)?/g) : null;
|
|
252
|
+
if (nums && nums.length >= 4) {
|
|
253
|
+
ends = {
|
|
254
|
+
x1: num(nums[0]),
|
|
255
|
+
y1: num(nums[1]),
|
|
256
|
+
x2: num(nums[nums.length - 2]),
|
|
257
|
+
y2: num(nums[nums.length - 1]),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!ends) return { tool: 'arrow', id, x: null, y: null, w: null, h: null, text: null, color };
|
|
262
|
+
return {
|
|
263
|
+
tool: 'arrow',
|
|
264
|
+
id,
|
|
265
|
+
x: Math.min(ends.x1, ends.x2),
|
|
266
|
+
y: Math.min(ends.y1, ends.y2),
|
|
267
|
+
w: Math.abs(ends.x2 - ends.x1),
|
|
268
|
+
h: Math.abs(ends.y2 - ends.y1),
|
|
269
|
+
text: null,
|
|
270
|
+
color,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function bboxFromNums(nums) {
|
|
275
|
+
let xMin = Number.POSITIVE_INFINITY;
|
|
276
|
+
let yMin = Number.POSITIVE_INFINITY;
|
|
277
|
+
let xMax = Number.NEGATIVE_INFINITY;
|
|
278
|
+
let yMax = Number.NEGATIVE_INFINITY;
|
|
279
|
+
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
280
|
+
const px = num(nums[i]);
|
|
281
|
+
const py = num(nums[i + 1]);
|
|
282
|
+
if (px < xMin) xMin = px;
|
|
283
|
+
if (px > xMax) xMax = px;
|
|
284
|
+
if (py < yMin) yMin = py;
|
|
285
|
+
if (py > yMax) yMax = py;
|
|
286
|
+
}
|
|
287
|
+
if (!Number.isFinite(xMin)) return null;
|
|
288
|
+
return { x: xMin, y: yMin, w: xMax - xMin, h: yMax - yMin };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function readPen(attrs) {
|
|
292
|
+
const id = attr(attrs, 'data-id') || '';
|
|
293
|
+
const color = attr(attrs, 'stroke') || null;
|
|
294
|
+
const bb = bboxFromNums((attr(attrs, 'd') || '').match(/-?\d+(?:\.\d+)?/g) || []);
|
|
295
|
+
if (!bb) return { tool: 'pen', id, x: null, y: null, w: null, h: null, text: null, color };
|
|
296
|
+
return { tool: 'pen', id, ...bb, text: null, color };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function readRect(attrs) {
|
|
300
|
+
return {
|
|
301
|
+
tool: 'rect',
|
|
302
|
+
id: attr(attrs, 'data-id') || '',
|
|
303
|
+
x: num(attr(attrs, 'x')),
|
|
304
|
+
y: num(attr(attrs, 'y')),
|
|
305
|
+
w: num(attr(attrs, 'width')),
|
|
306
|
+
h: num(attr(attrs, 'height')),
|
|
307
|
+
text: null,
|
|
308
|
+
color: attr(attrs, 'stroke') || null,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function readEllipse(attrs) {
|
|
313
|
+
const cx = num(attr(attrs, 'cx'));
|
|
314
|
+
const cy = num(attr(attrs, 'cy'));
|
|
315
|
+
const rx = num(attr(attrs, 'rx'));
|
|
316
|
+
const ry = num(attr(attrs, 'ry'));
|
|
317
|
+
return {
|
|
318
|
+
tool: 'ellipse',
|
|
319
|
+
id: attr(attrs, 'data-id') || '',
|
|
320
|
+
x: cx - rx,
|
|
321
|
+
y: cy - ry,
|
|
322
|
+
w: rx * 2,
|
|
323
|
+
h: ry * 2,
|
|
324
|
+
text: null,
|
|
325
|
+
color: attr(attrs, 'stroke') || null,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function readPolygon(attrs) {
|
|
330
|
+
const id = attr(attrs, 'data-id') || '';
|
|
331
|
+
const color = attr(attrs, 'stroke') || null;
|
|
332
|
+
const nums = (attr(attrs, 'points') || '').match(/-?\d+(?:\.\d+)?/g) || [];
|
|
333
|
+
const bb = bboxFromNums(nums);
|
|
334
|
+
if (!bb) return { tool: 'polygon', id, x: null, y: null, w: null, h: null, text: null, color };
|
|
335
|
+
return { tool: 'polygon', id, ...bb, text: null, color };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Forward-compat (Phase 23) — an unknown data-tool (image / link chip) passes
|
|
339
|
+
// through with whatever geometry + media attrs it carries.
|
|
340
|
+
function readUnknown(tool, attrs, inner) {
|
|
341
|
+
const text = inner
|
|
342
|
+
? decodeEntities(stripTags(inner)).trim() || null
|
|
343
|
+
: attr(attrs, 'data-text')
|
|
344
|
+
? decodeEntities(attr(attrs, 'data-text'))
|
|
345
|
+
: null;
|
|
346
|
+
const o = {
|
|
347
|
+
tool,
|
|
348
|
+
id: attr(attrs, 'data-id') || '',
|
|
349
|
+
x: hasAttr(attrs, 'x') ? num(attr(attrs, 'x')) : null,
|
|
350
|
+
y: hasAttr(attrs, 'y') ? num(attr(attrs, 'y')) : null,
|
|
351
|
+
w: hasAttr(attrs, 'width') ? num(attr(attrs, 'width')) : null,
|
|
352
|
+
h: hasAttr(attrs, 'height') ? num(attr(attrs, 'height')) : null,
|
|
353
|
+
text,
|
|
354
|
+
color: attr(attrs, 'fill') || attr(attrs, 'stroke') || null,
|
|
355
|
+
};
|
|
356
|
+
const href = attr(attrs, 'href') || attr(attrs, 'xlink:href');
|
|
357
|
+
const url = attr(attrs, 'data-url');
|
|
358
|
+
const title = attr(attrs, 'data-title');
|
|
359
|
+
if (href) o.href = href;
|
|
360
|
+
if (url) o.url = url;
|
|
361
|
+
if (title) o.title = decodeEntities(title);
|
|
362
|
+
return o;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
366
|
+
// Top-level element walk. The annotation vocabulary nests at most one level —
|
|
367
|
+
// sticky/arrow are <g> wrapping leaf primitives, text wraps only character data,
|
|
368
|
+
// and NO container nests inside the same container — so a non-greedy backref
|
|
369
|
+
// match recovers each top-level element in document order. (Task-2 drift guard
|
|
370
|
+
// keeps this invariant honest against the canonical serializer.)
|
|
371
|
+
// FigJam v3 — shared root attrs every tool can carry: group membership,
|
|
372
|
+
// provenance, render order (z = document position). Applied uniformly after
|
|
373
|
+
// the per-tool extraction; arrows additionally surface bound endpoints as
|
|
374
|
+
// `from`/`to` host ids so a bound diagram reads back as a GRAPH.
|
|
375
|
+
function withShared(o, attrs, z) {
|
|
376
|
+
o.z = z;
|
|
377
|
+
const g = attr(attrs, 'data-group-ids');
|
|
378
|
+
if (g) {
|
|
379
|
+
const ids = g.split(/\s+/).filter(Boolean);
|
|
380
|
+
if (ids.length) o.groupIds = ids;
|
|
381
|
+
}
|
|
382
|
+
if (attr(attrs, 'data-author') === 'ai') o.author = 'ai';
|
|
383
|
+
if (o.tool === 'arrow') {
|
|
384
|
+
const sb = parseBind(attr(attrs, 'data-start-bind'));
|
|
385
|
+
if (sb) o.from = sb.hostId;
|
|
386
|
+
const eb = parseBind(attr(attrs, 'data-end-bind'));
|
|
387
|
+
if (eb) o.to = eb.hostId;
|
|
388
|
+
}
|
|
389
|
+
return o;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function parseAnnotations(svg) {
|
|
393
|
+
const out = [];
|
|
394
|
+
if (!svg || !/<svg[\s>]/i.test(svg)) return out;
|
|
395
|
+
const re =
|
|
396
|
+
/<(g|text)\b([^>]*)>([\s\S]*?)<\/\1>|<(path|rect|ellipse|polygon|image)\b([^>]*?)\/?>/g;
|
|
397
|
+
let m = re.exec(svg);
|
|
398
|
+
while (m !== null) {
|
|
399
|
+
if (m[1]) {
|
|
400
|
+
const attrs = m[2];
|
|
401
|
+
const inner = m[3];
|
|
402
|
+
const tool = attr(attrs, 'data-tool');
|
|
403
|
+
if (tool === 'sticky') out.push(withShared(readSticky(attrs, inner), attrs, out.length));
|
|
404
|
+
else if (tool === 'section')
|
|
405
|
+
out.push(withShared(readSection(attrs, inner), attrs, out.length));
|
|
406
|
+
else if (tool === 'arrow') out.push(withShared(readArrow(attrs, inner), attrs, out.length));
|
|
407
|
+
else if (tool === 'text') out.push(withShared(readText(attrs, inner), attrs, out.length));
|
|
408
|
+
else if (tool) out.push(withShared(readUnknown(tool, attrs, inner), attrs, out.length));
|
|
409
|
+
} else {
|
|
410
|
+
const attrs = m[5];
|
|
411
|
+
const tool = attr(attrs, 'data-tool');
|
|
412
|
+
if (tool === 'pen') out.push(withShared(readPen(attrs), attrs, out.length));
|
|
413
|
+
else if (tool === 'rect') out.push(withShared(readRect(attrs), attrs, out.length));
|
|
414
|
+
else if (tool === 'ellipse') out.push(withShared(readEllipse(attrs), attrs, out.length));
|
|
415
|
+
else if (tool === 'polygon') out.push(withShared(readPolygon(attrs), attrs, out.length));
|
|
416
|
+
else if (tool) out.push(withShared(readUnknown(tool, attrs, ''), attrs, out.length));
|
|
417
|
+
}
|
|
418
|
+
m = re.exec(svg);
|
|
419
|
+
}
|
|
420
|
+
return out;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// FigJam v3 — derive the node/edge view of a bound diagram. Edges are arrows
|
|
424
|
+
// with at least one bound end; nodes are the strokes those arrows reference,
|
|
425
|
+
// labelled by their own text or by anchored text targeting them.
|
|
426
|
+
function buildGraph(annotations) {
|
|
427
|
+
const edges = [];
|
|
428
|
+
for (const a of annotations) {
|
|
429
|
+
if (a.tool === 'arrow' && (a.from || a.to)) {
|
|
430
|
+
edges.push({ id: a.id, from: a.from ?? null, to: a.to ?? null });
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const refIds = new Set();
|
|
434
|
+
for (const e of edges) {
|
|
435
|
+
if (e.from) refIds.add(e.from);
|
|
436
|
+
if (e.to) refIds.add(e.to);
|
|
437
|
+
}
|
|
438
|
+
const labelByAnchor = new Map();
|
|
439
|
+
for (const a of annotations) {
|
|
440
|
+
if (a.tool === 'text' && a.anchorId && a.text) labelByAnchor.set(a.anchorId, a.text);
|
|
441
|
+
}
|
|
442
|
+
const nodes = annotations
|
|
443
|
+
.filter((a) => refIds.has(a.id))
|
|
444
|
+
.map((a) => ({
|
|
445
|
+
id: a.id,
|
|
446
|
+
tool: a.tool,
|
|
447
|
+
label: a.text || labelByAnchor.get(a.id) || null,
|
|
448
|
+
x: a.x,
|
|
449
|
+
y: a.y,
|
|
450
|
+
w: a.w,
|
|
451
|
+
h: a.h,
|
|
452
|
+
artboard: a.artboard ?? null,
|
|
453
|
+
}));
|
|
454
|
+
return { nodes, edges };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
458
|
+
// --canvas-state artboard overlap tagging.
|
|
459
|
+
|
|
460
|
+
function loadArtboards(p) {
|
|
461
|
+
try {
|
|
462
|
+
const raw = JSON.parse(readFileSync(p, 'utf8'));
|
|
463
|
+
const arr = Array.isArray(raw)
|
|
464
|
+
? raw
|
|
465
|
+
: Array.isArray(raw.artboards)
|
|
466
|
+
? raw.artboards
|
|
467
|
+
: Array.isArray(raw.layout?.artboards)
|
|
468
|
+
? raw.layout.artboards
|
|
469
|
+
: [];
|
|
470
|
+
return arr.filter(
|
|
471
|
+
(r) => r && typeof r.id === 'string' && [r.x, r.y, r.w, r.h].every((n) => Number.isFinite(n))
|
|
472
|
+
);
|
|
473
|
+
} catch {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function findArtboard(ann, artboards) {
|
|
479
|
+
if (ann.x == null || ann.y == null) return null;
|
|
480
|
+
const ax2 = ann.x + (ann.w || 0);
|
|
481
|
+
const ay2 = ann.y + (ann.h || 0);
|
|
482
|
+
for (const r of artboards) {
|
|
483
|
+
const rx2 = r.x + r.w;
|
|
484
|
+
const ry2 = r.y + r.h;
|
|
485
|
+
if (ann.x <= rx2 && ax2 >= r.x && ann.y <= ry2 && ay2 >= r.y) return r;
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function tagArtboard(ann, artboards) {
|
|
491
|
+
return findArtboard(ann, artboards)?.id ?? null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* FigJam v3 — anchor an annotation to its artboard for AI consumers: the
|
|
496
|
+
* overlapping artboard id, artboard-RELATIVE coords (what survives an artboard
|
|
497
|
+
* move), and a W3C Web-Annotation-style target (anchor by stable id first,
|
|
498
|
+
* geometry as the refinement/fallback).
|
|
499
|
+
*/
|
|
500
|
+
function anchorToArtboard(ann, artboards) {
|
|
501
|
+
const r = findArtboard(ann, artboards);
|
|
502
|
+
if (!r) return { ...ann, artboard: null };
|
|
503
|
+
return {
|
|
504
|
+
...ann,
|
|
505
|
+
artboard: r.id,
|
|
506
|
+
rel: ann.x != null ? { x: ann.x - r.x, y: ann.y - r.y } : null,
|
|
507
|
+
target: {
|
|
508
|
+
source: r.id,
|
|
509
|
+
selector: { type: 'AnnotationIdSelector', value: ann.id },
|
|
510
|
+
geometry: ann.x != null ? { x: ann.x, y: ann.y, w: ann.w ?? 0, h: ann.h ?? 0 } : null,
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
516
|
+
// Main
|
|
517
|
+
|
|
518
|
+
function main() {
|
|
519
|
+
const args = parseArgv(process.argv.slice(2));
|
|
520
|
+
if (args.help) {
|
|
521
|
+
process.stdout.write(`${HELP}\n`);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const relPath = args.positional[0];
|
|
525
|
+
if (!relPath) {
|
|
526
|
+
process.stderr.write(`read-annotations: missing <rel-path>.\n\n${HELP}\n`);
|
|
527
|
+
process.exit(2);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const repoRoot = args.root
|
|
531
|
+
? resolve(args.root)
|
|
532
|
+
: process.env.CLAUDE_PROJECT_DIR
|
|
533
|
+
? resolve(process.env.CLAUDE_PROJECT_DIR)
|
|
534
|
+
: process.cwd();
|
|
535
|
+
|
|
536
|
+
const { designRel, designRoot } = resolveDesignRoot(repoRoot);
|
|
537
|
+
const slug = fileSlug(relPath, designRel);
|
|
538
|
+
const svgPath = join(designRoot, `${slug}.annotations.svg`);
|
|
539
|
+
|
|
540
|
+
let svg = '';
|
|
541
|
+
if (existsSync(svgPath)) {
|
|
542
|
+
try {
|
|
543
|
+
svg = readFileSync(svgPath, 'utf8');
|
|
544
|
+
} catch {
|
|
545
|
+
svg = '';
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
let annotations = parseAnnotations(svg);
|
|
550
|
+
|
|
551
|
+
if (args.canvasState) {
|
|
552
|
+
const csPath = isAbsolute(args.canvasState)
|
|
553
|
+
? args.canvasState
|
|
554
|
+
: resolve(process.cwd(), args.canvasState);
|
|
555
|
+
const artboards = loadArtboards(csPath);
|
|
556
|
+
if (artboards.length) {
|
|
557
|
+
annotations = annotations.map((a) => anchorToArtboard(a, artboards));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (args.graph) {
|
|
562
|
+
process.stdout.write(`${JSON.stringify({ annotations, graph: buildGraph(annotations) })}\n`);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
process.stdout.write(`${JSON.stringify(annotations)}\n`);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// FigJam v3 — the parsing core is importable (the `annotate` write verb reuses
|
|
569
|
+
// it for host-geometry lookups); main() runs only when invoked as a script.
|
|
570
|
+
export { buildGraph, fileSlug, loadArtboards, parseAnnotations, resolveDesignRoot, tagArtboard };
|
|
571
|
+
|
|
572
|
+
const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
573
|
+
if (isMain) main();
|