@1agh/maude 0.28.1 → 0.29.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 +2 -2
- package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +285 -5
- package/{plugins/design/dev-server → apps/studio}/annotations-layer.tsx +1206 -119
- package/{plugins/design/dev-server → apps/studio}/api.ts +486 -32
- 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/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 +438 -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 +5 -3
- package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +1 -1
- 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 +46 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +60 -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 +56 -41
- package/{plugins/design/dev-server → apps/studio}/client/app.jsx +1713 -530
- package/apps/studio/client/canvas-url.js +81 -0
- package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
- package/{plugins/design/dev-server → apps/studio}/client/index.html +2 -1
- package/apps/studio/client/styles/1-tokens-maude.css +296 -0
- package/apps/studio/client/styles/3-shell-maude.css +459 -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 +357 -34
- package/apps/studio/client/styles/5-maude-overrides.css +123 -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 +191 -0
- package/apps/studio/client/tour/usage-tour.js +35 -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 +1 -1
- 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 +1 -1
- package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +25 -6
- package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +4 -7
- package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +1 -1
- package/apps/studio/dist/client.bundle.js +22825 -0
- package/apps/studio/dist/comment-mount.js +1960 -0
- package/apps/studio/dist/styles.css +5695 -0
- package/{plugins/design/dev-server → apps/studio}/dom-selection.ts +75 -4
- 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 +13 -1
- package/{plugins/design/dev-server → apps/studio}/http.ts +102 -1
- package/{plugins/design/dev-server → apps/studio}/input-router.tsx +8 -1
- package/{plugins/design/dev-server → apps/studio}/inspect.ts +20 -3
- package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
- package/{plugins/design/dev-server → apps/studio}/server.ts +18 -0
- package/{plugins/design/dev-server → apps/studio}/sync/index.ts +1 -1
- 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-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/{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 +17 -1
- package/apps/studio/test/canvas-url.test.ts +99 -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/tour-overlay.test.tsx +56 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-annotation-resize.test.ts +43 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +4 -1
- 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 +4 -69
- package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-annotation-resize.tsx +40 -7
- package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +1 -1
- 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 +1 -1
- package/apps/studio/use-canvas-media-drop.tsx +343 -0
- package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +1 -1
- 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 +9 -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 +3 -1
- package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +1 -1
- package/apps/studio/whats-new.json +139 -0
- package/apps/studio/whats-new.schema.json +70 -0
- package/apps/studio/whats-new.ts +87 -0
- package/cli/bin/maude.mjs +10 -0
- package/cli/commands/design.mjs +15 -7
- package/cli/commands/design.test.mjs +36 -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/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/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/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 → 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/_png-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-edit.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}/history.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/agent.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/connection-state.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/migrate-seed.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/status.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/activity.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-edit.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-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-room.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/csp-canvas-shell.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-migrate.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-agent.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-codec.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.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-hardening.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.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-runtime.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-status.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-artboard-drag.test.ts +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-collab.test.ts +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
|
@@ -213,6 +213,55 @@ capture() {
|
|
|
213
213
|
fi
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
# ---------- port-bounce recovery (P2.1 / DDR-094) ----------
|
|
217
|
+
# The dev server can respawn on a DIFFERENT port (the prior port got taken, a
|
|
218
|
+
# Tailscale/mDNS hiccup bounced it, a stale _server.json was cleared). A capture
|
|
219
|
+
# launched against the OLD port then fails with ERR_CONNECTION_REFUSED. The
|
|
220
|
+
# running server keeps _server.json current, so on any capture failure we re-read
|
|
221
|
+
# the LIVE port and, if it changed, rewrite $URL and retry once.
|
|
222
|
+
RELIVE_REPO="${ROOT:-${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
|
|
223
|
+
RELIVE_STATE="$RELIVE_REPO/.design/_server.json"
|
|
224
|
+
RELIVED=0
|
|
225
|
+
|
|
226
|
+
read_state_port() {
|
|
227
|
+
[ -f "$RELIVE_STATE" ] || return 1
|
|
228
|
+
if command -v jq >/dev/null 2>&1; then
|
|
229
|
+
jq -r .port "$RELIVE_STATE" 2>/dev/null
|
|
230
|
+
else
|
|
231
|
+
sed -nE 's/.*"port"[[:space:]]*:[[:space:]]*([0-9]+).*/\1/p' "$RELIVE_STATE" 2>/dev/null | head -n1
|
|
232
|
+
fi
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# Re-read the live port; if it differs from the one in $URL, rewrite $URL and
|
|
236
|
+
# return 0 (a retry is warranted). Returns 1 when nothing changed (don't retry).
|
|
237
|
+
# Only fires ONCE per run (RELIVED guard) so a genuinely-down server still fails.
|
|
238
|
+
relive_url() {
|
|
239
|
+
[ "$RELIVED" -eq 0 ] || return 1
|
|
240
|
+
local cur new
|
|
241
|
+
cur=$(printf '%s' "$URL" | sed -nE 's#https?://[^:/]+:([0-9]+).*#\1#p')
|
|
242
|
+
new=$(read_state_port)
|
|
243
|
+
[ -n "$new" ] || return 1
|
|
244
|
+
if [ -n "$cur" ] && [ "$cur" != "$new" ]; then
|
|
245
|
+
URL=$(printf '%s' "$URL" | sed -E "s#(://[^:/]+:)[0-9]+#\1${new}#")
|
|
246
|
+
RELIVED=1
|
|
247
|
+
echo "→ dev-server bounced ports ($cur → $new); re-reading _server.json and retrying at $URL" >&2
|
|
248
|
+
return 0
|
|
249
|
+
fi
|
|
250
|
+
return 1
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# capture(), but on failure re-read the live port and retry once if it bounced.
|
|
254
|
+
capture_resilient() {
|
|
255
|
+
local css="$1"
|
|
256
|
+
local out="$2"
|
|
257
|
+
capture "$css" "$out" && return 0
|
|
258
|
+
if relive_url; then
|
|
259
|
+
navigate_once
|
|
260
|
+
capture "$css" "$out" && return 0
|
|
261
|
+
fi
|
|
262
|
+
return 1
|
|
263
|
+
}
|
|
264
|
+
|
|
216
265
|
# ---------- --all-screens loop ----------
|
|
217
266
|
if [ $ALL_SCREENS -eq 1 ]; then
|
|
218
267
|
mkdir -p "$OUT_DIR"
|
|
@@ -227,6 +276,17 @@ if [ $ALL_SCREENS -eq 1 ]; then
|
|
|
227
276
|
# Strip surrounding quotes, then split on comma.
|
|
228
277
|
IDS=$(printf '%s' "$raw" | sed 's/^"//; s/"$//' | tr ',' '\n')
|
|
229
278
|
fi
|
|
279
|
+
# No screens enumerated may mean the page never loaded (port bounce) — try a
|
|
280
|
+
# live-port re-read + one re-navigate before giving up.
|
|
281
|
+
if [ -z "$IDS" ] && relive_url; then
|
|
282
|
+
navigate_once
|
|
283
|
+
if [ "$ENGINE" = "agent-browser" ]; then
|
|
284
|
+
raw=$(agent-browser eval \
|
|
285
|
+
"Array.from(document.querySelectorAll('[data-dc-screen],[data-dc-slot]')).map(e => e.getAttribute('data-dc-screen') || e.getAttribute('data-dc-slot')).filter(Boolean).join(',')" \
|
|
286
|
+
2>/dev/null)
|
|
287
|
+
IDS=$(printf '%s' "$raw" | sed 's/^"//; s/"$//' | tr ',' '\n')
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
230
290
|
if [ -z "$IDS" ]; then
|
|
231
291
|
echo "✗ no [data-dc-screen]/[data-dc-slot] elements found (or eval unavailable on this engine)" >&2
|
|
232
292
|
exit 3
|
|
@@ -241,7 +301,7 @@ if [ $ALL_SCREENS -eq 1 ]; then
|
|
|
241
301
|
agent-browser eval "document.querySelector('[data-dc-screen=\"$ID\"], [data-dc-slot=\"$ID\"]').scrollIntoView({block:'center'})" >/dev/null 2>&1
|
|
242
302
|
sleep 0.6
|
|
243
303
|
fi
|
|
244
|
-
if
|
|
304
|
+
if capture_resilient "[data-dc-screen=\"$ID\"], [data-dc-slot=\"$ID\"]" "$OUT_FILE"; then
|
|
245
305
|
echo "$OUT_FILE"
|
|
246
306
|
else
|
|
247
307
|
echo "✗ failed: $ID" >&2
|
|
@@ -254,7 +314,7 @@ fi
|
|
|
254
314
|
|
|
255
315
|
# ---------- single-shot ----------
|
|
256
316
|
navigate_once
|
|
257
|
-
if
|
|
317
|
+
if capture_resilient "$CSS_SEL" "$OUT"; then
|
|
258
318
|
echo "$OUT"
|
|
259
319
|
exit 0
|
|
260
320
|
fi
|
|
@@ -55,7 +55,12 @@ fi
|
|
|
55
55
|
# `bash server-up.sh` falls back to the postinstall side-channel.
|
|
56
56
|
# bun + server.ts → dev tree / fallback (DDR-020 made bun authoritative for source).
|
|
57
57
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
58
|
-
PLUGIN_ROOT
|
|
58
|
+
# PLUGIN_ROOT is a 2-levels-under-pkgRoot anchor: plugins/design when `maude
|
|
59
|
+
# design <verb>` sets CLAUDE_PLUGIN_ROOT, else apps/studio ($SCRIPT_DIR/.. — the
|
|
60
|
+
# studio root, also 2-deep) for a direct `bash server-up.sh`. Both keep
|
|
61
|
+
# `$PLUGIN_ROOT/../../cli` = <pkgRoot>/cli. The dev-server tree itself is always
|
|
62
|
+
# $SCRIPT_DIR/.. (apps/studio) — DDR-095 moved it out of plugins/design.
|
|
63
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
59
64
|
SERVER_TS="$PLUGIN_ROOT/dev-server/server.ts"
|
|
60
65
|
SERVER_MJS="$PLUGIN_ROOT/dev-server/server.mjs"
|
|
61
66
|
if [ ! -f "$SERVER_TS" ]; then SERVER_TS="$SCRIPT_DIR/../server.ts"; fi
|
|
@@ -66,7 +71,8 @@ if [ ! -f "$SERVER_MJS" ]; then SERVER_MJS="$SCRIPT_DIR/../server.mjs"; fi
|
|
|
66
71
|
# `bash server-up.sh` on a production install). MAUDE_FORCE_SOURCE=1 forces source.
|
|
67
72
|
SERVER_BIN="${MAUDE_DEV_SERVER_BIN:-}"
|
|
68
73
|
if [ -z "$SERVER_BIN" ] && [ "${MAUDE_FORCE_SOURCE:-0}" != "1" ]; then
|
|
69
|
-
# PLUGIN_ROOT
|
|
74
|
+
# PLUGIN_ROOT is 2-deep under pkgRoot (plugins/design or apps/studio), so the
|
|
75
|
+
# side-channel is ../../cli either way.
|
|
70
76
|
# `head -n1` (parity with the Node readers' `.trim()`) tolerates a trailing newline.
|
|
71
77
|
SC="$PLUGIN_ROOT/../../cli/.platform-binary-path"
|
|
72
78
|
if [ -f "$SC" ]; then
|
|
@@ -154,7 +160,7 @@ fi
|
|
|
154
160
|
# Step 1.5 — dependency preflight (source `bun server.ts` path ONLY; runs on cold
|
|
155
161
|
# start). The compiled binary embeds its deps, so this is skipped when RUNTIME=binary
|
|
156
162
|
# — it only guards the source fallback. The dev-server's runtime deps live in a
|
|
157
|
-
# NESTED package.json (
|
|
163
|
+
# NESTED package.json (apps/studio/) that a global `@1agh/maude` npm
|
|
158
164
|
# install or a fresh `git worktree` does NOT populate; a missing `yjs` (imported at
|
|
159
165
|
# boot by sync/index.ts) crashes `bun server.ts` AFTER spawn → without this guard we
|
|
160
166
|
# poll the full ${TIMEOUT}s and report a generic "start timeout", burying the cause
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!-- to-lottie-verify.html — self-verify harness for `maude design to-lottie --verify`.
|
|
3
|
+
Renders ANY Lottie JSON through the REAL lottie-web runtime (NOT cairo — cairo
|
|
4
|
+
can't render masks; lottie-web must — DDR-094 rule 5). Generalized from the POC
|
|
5
|
+
verify.html: the Lottie source + the seek frame are query params.
|
|
6
|
+
|
|
7
|
+
Serve this dir (file:// blocks fetch): python3 -m http.server 8200
|
|
8
|
+
Usage: to-lottie-verify.html?src=<file.json>&f=<frame> (omit ?f to autoplay)
|
|
9
|
+
&bg=<css color> — backdrop (default dark) · &w=&h= — stage size px -->
|
|
10
|
+
<html>
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="utf8" />
|
|
13
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js"></script>
|
|
14
|
+
<style>
|
|
15
|
+
html,
|
|
16
|
+
body {
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
#stage {
|
|
20
|
+
margin: 20px auto;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="stage"></div>
|
|
26
|
+
<script>
|
|
27
|
+
const p = new URLSearchParams(location.search);
|
|
28
|
+
const src = p.get('src');
|
|
29
|
+
const f = p.get('f');
|
|
30
|
+
const bg = p.get('bg') || '#0a0a14';
|
|
31
|
+
const w = p.get('w') || '400';
|
|
32
|
+
const h = p.get('h') || '400';
|
|
33
|
+
document.body.style.background = bg;
|
|
34
|
+
const stage = document.getElementById('stage');
|
|
35
|
+
stage.style.width = `${w}px`;
|
|
36
|
+
stage.style.height = `${h}px`;
|
|
37
|
+
|
|
38
|
+
if (!src) {
|
|
39
|
+
document.title = 'error-no-src';
|
|
40
|
+
} else {
|
|
41
|
+
fetch(`./${src}`)
|
|
42
|
+
.then((r) => {
|
|
43
|
+
if (!r.ok) throw new Error(`fetch ${src} → ${r.status}`);
|
|
44
|
+
return r.json();
|
|
45
|
+
})
|
|
46
|
+
.then((data) => {
|
|
47
|
+
const anim = lottie.loadAnimation({
|
|
48
|
+
container: stage,
|
|
49
|
+
renderer: 'svg',
|
|
50
|
+
loop: true,
|
|
51
|
+
autoplay: f === null,
|
|
52
|
+
animationData: data,
|
|
53
|
+
});
|
|
54
|
+
anim.addEventListener('DOMLoaded', () => {
|
|
55
|
+
if (f !== null) anim.goToAndStop(parseInt(f, 10), true);
|
|
56
|
+
document.title = `ready${f !== null ? `-${f}` : ''}`;
|
|
57
|
+
});
|
|
58
|
+
anim.addEventListener('error', () => {
|
|
59
|
+
document.title = 'error-render';
|
|
60
|
+
});
|
|
61
|
+
})
|
|
62
|
+
.catch((e) => {
|
|
63
|
+
document.title = `error-${String(e.message || e).slice(0, 40)}`;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# to-lottie.sh — emit ONE Lottie from CODE (the maude motion handoff, DDR-094).
|
|
3
|
+
#
|
|
4
|
+
# This is the production-delivery counterpart to draw-build.sh: where draw-build
|
|
5
|
+
# runs an agent-authored *engine* script to emit static SVG/JSX, to-lottie runs an
|
|
6
|
+
# agent-authored *python-lottie generator* to emit ONE `.lottie`/`.json` that
|
|
7
|
+
# renders 1:1 on web (lottie-web / dotlottie-react) AND mobile
|
|
8
|
+
# (lottie-react-native) — same renderer family, 1:1 by construction. It is an
|
|
9
|
+
# EMITTER from the keyframe data, NOT a converter of rendered SVG/SMIL (no reliable
|
|
10
|
+
# one exists — DDR-094 research). The generator encodes the 8 conversion rules; see
|
|
11
|
+
# `plugins/design/commands/to-lottie.md` + `_draw-motion-rules.md`.
|
|
12
|
+
#
|
|
13
|
+
# Reached via `maude design to-lottie` (DDR-062), never a raw bin path.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# to-lottie.sh --script <gen.py> --out <mark.json>
|
|
17
|
+
# [--verify] [--frames "0,33,66"] [--root <repo>] [--timeout 12]
|
|
18
|
+
#
|
|
19
|
+
# The generator (gen.py) builds the Lottie via python-lottie and writes it to the
|
|
20
|
+
# path in $MAUDE_LOTTIE_OUT (= --out). python-lottie owns format correctness
|
|
21
|
+
# (beziers, keyframes, gradients, masks, parenting); the agent encodes the
|
|
22
|
+
# conversion rules (per-segment easing incl. overshoot, arc parsing via
|
|
23
|
+
# parse_svg_file, layers[0]=top + reverse, masks, baked sub-loops, coord offset,
|
|
24
|
+
# gradient opacity stops).
|
|
25
|
+
#
|
|
26
|
+
# --verify renders frames through HEADLESS lottie-web (cairo can't render masks —
|
|
27
|
+
# lottie-web must) using the shipped harness + the screenshot helper, so you can
|
|
28
|
+
# read the PNGs and compare to the web reference.
|
|
29
|
+
#
|
|
30
|
+
# Stdout (last line): the output .json path (capturable in $(...)).
|
|
31
|
+
# Exit: 0 ok / 1 missing input or runtime / 2 bad args / 3 generate failed
|
|
32
|
+
# / 4 verify render failed.
|
|
33
|
+
|
|
34
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
35
|
+
GEN=""
|
|
36
|
+
OUT=""
|
|
37
|
+
VERIFY=0
|
|
38
|
+
FRAMES="0,33,66"
|
|
39
|
+
REPO=""
|
|
40
|
+
TIMEOUT=12
|
|
41
|
+
|
|
42
|
+
while [ $# -gt 0 ]; do
|
|
43
|
+
case "$1" in
|
|
44
|
+
--script) GEN="$2"; shift 2 ;;
|
|
45
|
+
--out) OUT="$2"; shift 2 ;;
|
|
46
|
+
--verify) VERIFY=1; shift ;;
|
|
47
|
+
--frames) FRAMES="$2"; shift 2 ;;
|
|
48
|
+
--root) REPO="$2"; shift 2 ;;
|
|
49
|
+
--timeout) TIMEOUT="$2"; shift 2 ;;
|
|
50
|
+
--help|-h) sed -n '2,33p' "$0" | sed 's/^# \?//'; exit 0 ;;
|
|
51
|
+
*) echo "to-lottie.sh: unknown arg '$1' (try --help)" >&2; exit 2 ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
[ -n "$GEN" ] || { echo "to-lottie.sh: --script <gen.py> required" >&2; exit 2; }
|
|
56
|
+
[ -n "$OUT" ] || { echo "to-lottie.sh: --out <mark.json> required" >&2; exit 2; }
|
|
57
|
+
[ -f "$GEN" ] || { echo "to-lottie.sh: generator not found: $GEN" >&2; exit 1; }
|
|
58
|
+
command -v python3 >/dev/null 2>&1 || { echo "to-lottie.sh: python3 is required." >&2; exit 1; }
|
|
59
|
+
|
|
60
|
+
# ---------- python-lottie venv (cached, idempotent) ----------
|
|
61
|
+
VENV="${MAUDE_LOTTIE_VENV:-${XDG_CACHE_HOME:-$HOME/.cache}/maude/lottie-venv}"
|
|
62
|
+
PY="$VENV/bin/python"
|
|
63
|
+
if [ ! -x "$PY" ]; then
|
|
64
|
+
echo "→ bootstrapping python-lottie venv at $VENV (one-off) …" >&2
|
|
65
|
+
python3 -m venv "$VENV" || { echo "to-lottie.sh: venv create failed" >&2; exit 1; }
|
|
66
|
+
"$VENV/bin/pip" install --quiet --disable-pip-version-check lottie cairosvg pillow \
|
|
67
|
+
|| { echo "to-lottie.sh: pip install lottie failed (needs network on first run)" >&2; exit 1; }
|
|
68
|
+
fi
|
|
69
|
+
# Sanity: the `lottie` module must import.
|
|
70
|
+
"$PY" -c 'import lottie' 2>/dev/null || {
|
|
71
|
+
echo "→ (re)installing python-lottie into $VENV …" >&2
|
|
72
|
+
"$VENV/bin/pip" install --quiet --disable-pip-version-check lottie cairosvg pillow \
|
|
73
|
+
|| { echo "to-lottie.sh: python-lottie unavailable" >&2; exit 1; }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# ---------- generate ----------
|
|
77
|
+
mkdir -p "$(dirname "$OUT")"
|
|
78
|
+
echo "→ generating Lottie: $GEN → $OUT" >&2
|
|
79
|
+
MAUDE_LOTTIE_OUT="$OUT" "$PY" "$GEN" >&2
|
|
80
|
+
GRC=$?
|
|
81
|
+
if [ "$GRC" -ne 0 ] || [ ! -s "$OUT" ]; then
|
|
82
|
+
echo "to-lottie.sh: generator failed (rc=$GRC) or wrote no output to $OUT" >&2
|
|
83
|
+
exit 3
|
|
84
|
+
fi
|
|
85
|
+
# Validity gate: the output must be parseable JSON with the Lottie shape keys.
|
|
86
|
+
"$PY" - "$OUT" <<'PYEOF' >&2 || { echo "to-lottie.sh: output is not a valid Lottie JSON" >&2; exit 3; }
|
|
87
|
+
import json, sys
|
|
88
|
+
d = json.load(open(sys.argv[1]))
|
|
89
|
+
assert isinstance(d, dict) and 'layers' in d and 'op' in d, 'missing Lottie keys (layers/op)'
|
|
90
|
+
print(f" ✓ valid Lottie: {len(d.get('layers', []))} layers, {d.get('op')} frames @ {d.get('fr')}fps")
|
|
91
|
+
PYEOF
|
|
92
|
+
|
|
93
|
+
# ---------- self-verify through headless lottie-web ----------
|
|
94
|
+
if [ "$VERIFY" -eq 1 ]; then
|
|
95
|
+
HARNESS="$SCRIPT_DIR/to-lottie-verify.html"
|
|
96
|
+
[ -f "$HARNESS" ] || { echo "to-lottie.sh: verify harness missing at $HARNESS — reinstall maude." >&2; exit 1; }
|
|
97
|
+
OUT_DIR_ABS="$(cd "$(dirname "$OUT")" && pwd)"
|
|
98
|
+
OUT_BASE="$(basename "$OUT")"
|
|
99
|
+
cp "$HARNESS" "$OUT_DIR_ABS/_to-lottie-verify.html"
|
|
100
|
+
FRAMES_DIR="$OUT_DIR_ABS/_verify-frames"
|
|
101
|
+
mkdir -p "$FRAMES_DIR"
|
|
102
|
+
|
|
103
|
+
# Serve the dir (file:// blocks fetch of the JSON). Pick a free-ish port.
|
|
104
|
+
PORT=$(( ( RANDOM % 2000 ) + 8200 ))
|
|
105
|
+
( cd "$OUT_DIR_ABS" && "$PY" -m http.server "$PORT" >/dev/null 2>&1 ) &
|
|
106
|
+
SRV_PID=$!
|
|
107
|
+
trap 'kill "$SRV_PID" 2>/dev/null; rm -f "$OUT_DIR_ABS/_to-lottie-verify.html"' EXIT
|
|
108
|
+
sleep 1
|
|
109
|
+
|
|
110
|
+
FAIL=0
|
|
111
|
+
IFS=',' read -r -a FRAME_ARR <<< "$FRAMES"
|
|
112
|
+
for F in "${FRAME_ARR[@]}"; do
|
|
113
|
+
F_TRIM="$(printf '%s' "$F" | tr -d '[:space:]')"
|
|
114
|
+
URL="http://localhost:$PORT/_to-lottie-verify.html?src=$OUT_BASE&f=$F_TRIM"
|
|
115
|
+
PNG="$FRAMES_DIR/frame-$F_TRIM.png"
|
|
116
|
+
echo "→ verify frame $F_TRIM ($URL)" >&2
|
|
117
|
+
bash "$SCRIPT_DIR/screenshot.sh" --url "$URL" --full --out "$PNG" --timeout "$TIMEOUT" >&2 \
|
|
118
|
+
|| { echo "✗ verify frame $F_TRIM failed" >&2; FAIL=$((FAIL + 1)); }
|
|
119
|
+
done
|
|
120
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
121
|
+
echo "to-lottie.sh: $FAIL verify frame(s) failed to render" >&2
|
|
122
|
+
exit 4
|
|
123
|
+
fi
|
|
124
|
+
echo "→ verify frames in $FRAMES_DIR — READ them and compare to the web reference (masks render on lottie-web, NOT cairo)" >&2
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Last stdout line = the output path, for $(maude design to-lottie ...) capture.
|
|
128
|
+
printf '%s\n' "$OUT"
|
|
@@ -66,8 +66,8 @@ function ensureDist() {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
async function readPluginVersion(): Promise<{ version: string }> {
|
|
69
|
-
//
|
|
70
|
-
const manifest = join(ROOT, '..', '.claude-plugin', 'plugin.json');
|
|
69
|
+
// apps/studio/ → ../../plugins/design/.claude-plugin/plugin.json (DDR-095)
|
|
70
|
+
const manifest = join(ROOT, '..', '..', 'plugins', 'design', '.claude-plugin', 'plugin.json');
|
|
71
71
|
try {
|
|
72
72
|
const parsed = JSON.parse(await Bun.file(manifest).text()) as { version?: unknown };
|
|
73
73
|
if (typeof parsed.version === 'string') return { version: parsed.version };
|
|
@@ -257,7 +257,9 @@ async function ensureBindingForTarget(oxcSlug: string): Promise<void> {
|
|
|
257
257
|
let oxcVersion = '0.131.0';
|
|
258
258
|
const candidates = [
|
|
259
259
|
join(ROOT, 'node_modules', 'oxc-parser', 'package.json'),
|
|
260
|
-
|
|
260
|
+
// apps/studio → ../../ = repo root → node_modules (DDR-095: was ../../../ at
|
|
261
|
+
// the old plugins/design/dev-server depth).
|
|
262
|
+
join(ROOT, '..', '..', 'node_modules', 'oxc-parser', 'package.json'),
|
|
261
263
|
];
|
|
262
264
|
for (const c of candidates) {
|
|
263
265
|
if (existsSync(c)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file canvas-arrowheads.ts — Phase 24 arrow shaft + head geometry
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/canvas-arrowheads.ts
|
|
4
4
|
* @purpose Single source of truth for arrow rendering. An ArrowStroke is
|
|
5
5
|
* reduced to an ordered list of `SvgPrimitive`s (the shaft, then
|
|
6
6
|
* the start head, then the end head). The serializer formats each
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file canvas-comment-mount.tsx — shell-owned comment layer + mountCanvas
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/canvas-comment-mount.tsx
|
|
4
4
|
* @purpose The canvas mount harness (`_shell.html`) calls `mountCanvas`
|
|
5
5
|
* instead of rendering the canvas default-export raw. mountCanvas
|
|
6
6
|
* wraps ANY default export — a `DesignCanvas` UI canvas OR a bare
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// canvas-create.ts — Phase 22 UI add-on. Pure helpers behind POST /_api/canvas
|
|
2
|
+
// (api.ts createCanvas), which lets the browser file-tree create a blank brief
|
|
3
|
+
// board directly — no slash command, no model cost. Generation (ingesting a
|
|
4
|
+
// board's notes into artboards) stays with Claude via `/design:new`; this only
|
|
5
|
+
// stamps out the empty annotation surface.
|
|
6
|
+
//
|
|
7
|
+
// SINGLE SOURCE — the board envelope is the SAME `brief-board.tsx.template`
|
|
8
|
+
// `/design:new --blank` substitutes. We text-import it so Bun.build inlines the
|
|
9
|
+
// content into the `bun --compile` standalone binary (verified: works at dev
|
|
10
|
+
// runtime AND embeds at build time), sidestepping DDR-045 — no runtime disk read
|
|
11
|
+
// of a file that lives outside `apps/studio/`. The template stayed under the
|
|
12
|
+
// design plugin (plugins/design/templates) when the studio moved (DDR-095).
|
|
13
|
+
|
|
14
|
+
import briefBoardTemplate from '../../plugins/design/templates/brief-board.tsx.template' with {
|
|
15
|
+
type: 'text',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const BRIEF_BOARD_TEMPLATE: string = briefBoardTemplate;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The ONE security boundary for the create endpoint. A canvas name is the only
|
|
22
|
+
* user-controlled value, and it is interpolated into BOTH a real nested
|
|
23
|
+
* filesystem path (`<group>/<name>.tsx`) AND the generated `.tsx` (JSX text) +
|
|
24
|
+
* `.meta.json` (JSON string). This strict allowlist does triple duty:
|
|
25
|
+
* • path safety — no `/`, `\`, `..`, or leading dot ⇒ no traversal;
|
|
26
|
+
* • template safety — no `<`, `>`, `{`, `}` ⇒ can't break out of JSX text;
|
|
27
|
+
* • JSON safety — no `"` or `\` ⇒ can't break out of a meta string.
|
|
28
|
+
* Unicode letters/numbers are allowed (so "Onboarding Brief" / accented names
|
|
29
|
+
* work); the first char must be a letter/number (excludes leading space/dash/dot).
|
|
30
|
+
*/
|
|
31
|
+
const NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N} _-]{0,59}$/u;
|
|
32
|
+
|
|
33
|
+
export interface NameValidation {
|
|
34
|
+
ok: boolean;
|
|
35
|
+
name?: string;
|
|
36
|
+
componentName?: string;
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function validateCanvasName(raw: unknown): NameValidation {
|
|
41
|
+
if (typeof raw !== 'string') return { ok: false, error: 'name must be a string' };
|
|
42
|
+
// NFC-normalize so a decomposed (NFD) name and its composed sibling can't hash
|
|
43
|
+
// to two JS strings that collide to ONE on-disk path on a normalizing filesystem
|
|
44
|
+
// (APFS/HFS+) — which would let the 409 existence guard be bypassed and an
|
|
45
|
+
// existing board silently overwritten (Phase 22 security review, low). Collapse
|
|
46
|
+
// runs of SPACES (the one allowed whitespace) to a single space so the JS slug
|
|
47
|
+
// (`/\s+/→_`) and slug.sh (`tr ' ' '_'`) can't diverge on a double-spaced name
|
|
48
|
+
// (review #4); tabs/newlines stay un-collapsed and are rejected by NAME_RE.
|
|
49
|
+
const name = raw.normalize('NFC').trim().replace(/ +/g, ' ');
|
|
50
|
+
if (!name) return { ok: false, error: 'name is required' };
|
|
51
|
+
if (name.length > 60) return { ok: false, error: 'name must be 60 characters or fewer' };
|
|
52
|
+
if (!NAME_RE.test(name)) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
error: 'name may contain only letters, numbers, spaces, hyphens and underscores',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Defense in depth — the regex already forbids these, but assert no path bits
|
|
59
|
+
// survive (a regex edit must never silently re-open traversal).
|
|
60
|
+
if (name.includes('..') || name.includes('/') || name.includes('\\') || name.startsWith('.')) {
|
|
61
|
+
return { ok: false, error: 'name may not contain path separators or leading dots' };
|
|
62
|
+
}
|
|
63
|
+
return { ok: true, name, componentName: componentNameFrom(name) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** PascalCase a name into a valid JS identifier for the component default export.
|
|
67
|
+
* Non-identifier characters split words; an empty / digit-leading result gets a
|
|
68
|
+
* `Board` prefix so the emitted `export default function <id>()` always parses. */
|
|
69
|
+
export function componentNameFrom(name: string): string {
|
|
70
|
+
// Split on NON-(letter|number) so Unicode letters survive (the validator
|
|
71
|
+
// accepts them) — `Přihlášení` → `Přihlášení`, a valid JS identifier (verified
|
|
72
|
+
// it parses), not the ASCII-mangled `PIhlEn` (review #1). Empty (all-symbol) →
|
|
73
|
+
// `BriefBoard`; a digit-leading result (incl. Unicode digits) → `Board…` so the
|
|
74
|
+
// emitted `export default function <id>()` always parses.
|
|
75
|
+
const pascal = name
|
|
76
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.map((w) => w[0].toUpperCase() + w.slice(1))
|
|
79
|
+
.join('');
|
|
80
|
+
if (!pascal) return 'BriefBoard';
|
|
81
|
+
if (/^\p{Nd}/u.test(pascal)) return `Board${pascal}`;
|
|
82
|
+
return pascal;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface BriefBoardOpts {
|
|
86
|
+
name: string;
|
|
87
|
+
componentName: string;
|
|
88
|
+
dsName: string;
|
|
89
|
+
platform: string;
|
|
90
|
+
seedHint: string;
|
|
91
|
+
historyDir: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Substitute the brief-board template placeholders. Safe because `name`
|
|
95
|
+
* (the only user value reaching here) is validated by `validateCanvasName`,
|
|
96
|
+
* and every other field is server-derived (config / fixed strings). */
|
|
97
|
+
export function renderBriefBoard(opts: BriefBoardOpts): string {
|
|
98
|
+
return BRIEF_BOARD_TEMPLATE.replaceAll('{{NAME}}', opts.name)
|
|
99
|
+
.replaceAll('{{COMPONENT_NAME}}', opts.componentName)
|
|
100
|
+
.replaceAll('{{DS_NAME}}', opts.dsName)
|
|
101
|
+
.replaceAll('{{PLATFORM}}', opts.platform)
|
|
102
|
+
.replaceAll('{{SEED_HINT}}', opts.seedHint)
|
|
103
|
+
.replaceAll('{{HISTORY_DIR}}', opts.historyDir);
|
|
104
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file canvas-cursors.ts — Phase 24 custom tool cursors (Kenney CC0)
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/canvas-cursors.ts
|
|
4
4
|
* @purpose FigJam/Figma-style custom cursors for EVERY canvas tool. The
|
|
5
5
|
* native crosshair / text / arrow cursors are tiny + thin and
|
|
6
6
|
* vanish on busy canvases; these are 32×32 SVG cursors that read on
|
|
@@ -46,16 +46,29 @@ const HALO = "stroke='#ffffff' stroke-linejoin='round' stroke-linecap='round'";
|
|
|
46
46
|
// Kenney's outline band (`b`) is painted WHITE (the halo) and its inner fill
|
|
47
47
|
// (`w`) is painted dark INK. Reads crisply on a LIGHT canvas (solid dark glyph)
|
|
48
48
|
// AND a dark one (white halo edge).
|
|
49
|
-
function kenney(b: string, w: string): string {
|
|
50
|
-
|
|
49
|
+
function kenney(b: string, w: string, transform?: string): string {
|
|
50
|
+
// Optional `<g transform>` for the drawing tools whose Kenney glyph needs
|
|
51
|
+
// re-orienting so the writing tip lands at the hotspot (user steer
|
|
52
|
+
// 2026-06-04: pen / highlighter "kreslí pod koncem, ne na špičce" → flipped so
|
|
53
|
+
// the nib points down-LEFT, top-right→bottom-left).
|
|
54
|
+
const open = transform ? `<g transform='${transform}'>` : '';
|
|
55
|
+
const close = transform ? '</g>' : '';
|
|
56
|
+
return `<svg ${W}>${open}<path fill='#ffffff' d='${b}'/><path fill='${INK}' d='${w}'/>${close}</svg>`;
|
|
51
57
|
}
|
|
52
58
|
|
|
59
|
+
// Vertical mirror about the 32-viewBox horizontal centre: (x,y) → (x, 32−y).
|
|
60
|
+
// Applied to pen/highlighter so the glyph lies along the top-right→bottom-left
|
|
61
|
+
// diagonal with the nib at bottom-left.
|
|
62
|
+
const FLIP_V = 'translate(0,32) scale(1,-1)';
|
|
63
|
+
|
|
64
|
+
// move → Kenney `pointer_b` (user steer 2026-06-04 — the plain variant, not the
|
|
65
|
+
// shaded one). Hotspot at the arrow tip (top).
|
|
53
66
|
const MOVE = svgCursor(
|
|
54
67
|
kenney(
|
|
55
|
-
'
|
|
56
|
-
'
|
|
68
|
+
'M10.25 8.25 L10.05 8.65 10 9 10 23.1 10.05 23.4 10.25 23.75 10.8 24 11.45 23.85 11.6 23.7 11.75 23.45 14.65 19.8 14.85 19.65 14.95 19.5 15.4 19.2 15.95 19.1 21.05 19.1 21.2 19.1 21.35 19.05 21.65 18.9 21.9 18.6 22 18.3 22 17.9 21.9 17.65 21.75 17.35 21.65 17.3 21.55 17.15 11.5 8.25 11.25 8.05 10.95 8 10.6 8.05 10.25 8.25 M9.15 6.6 L9.7 6.25 10 6.15 10.95 6 11.9 6.15 13 6.9 22.9 15.65 23 15.8 23.2 15.95 23.75 16.75 24 17.9 24 18.3 23.9 18.95 23.8 19.25 23.6 19.7 23.1 20.35 22.75 20.6 22.1 20.9 22.05 20.95 22 20.95 21.2 21.1 21.05 21.1 16.2 21.1 16.1 21.25 16.05 21.25 13.4 24.6 13.05 25.15 12.9 25.3 12.55 25.55 Q11.6 26.15 10.55 26 L10.5 26 Q9.65 25.85 9 25.3 L8.45 24.65 8.4 24.5 8.15 24.05 8 23.1 8 9 Q8 8.45 8.15 8.05 L8.25 7.75 8.45 7.35 9.15 6.6',
|
|
69
|
+
'M10.25 8.25 L10.6 8.05 10.95 8 11.25 8.05 11.5 8.25 21.55 17.15 21.65 17.3 21.75 17.35 21.9 17.65 22 17.9 22 18.3 21.9 18.6 21.65 18.9 21.35 19.05 21.2 19.1 21.05 19.1 15.95 19.1 15.4 19.2 14.95 19.5 14.85 19.65 14.65 19.8 11.75 23.45 11.6 23.7 11.45 23.85 10.8 24 10.25 23.75 10.05 23.4 10 23.1 10 9 10.05 8.65 10.25 8.25'
|
|
57
70
|
),
|
|
58
|
-
|
|
71
|
+
8,
|
|
59
72
|
5,
|
|
60
73
|
'default'
|
|
61
74
|
);
|
|
@@ -68,22 +81,40 @@ const HAND = svgCursor(
|
|
|
68
81
|
12,
|
|
69
82
|
'grab'
|
|
70
83
|
);
|
|
84
|
+
// comment → Kenney `message_round` (user steer 2026-06-04). Hotspot at the
|
|
85
|
+
// tail tip (bottom-centre), where the comment pin is dropped.
|
|
71
86
|
const COMMENT = svgCursor(
|
|
72
87
|
kenney(
|
|
73
|
-
'
|
|
74
|
-
'
|
|
88
|
+
'M28 14 Q28 9.85 24.45 6.9 20.95 4 16 4 11.05 4 7.5 6.9 L7.45 7 Q4 9.9 4 14 4 18.15 7.55 21.05 L7.5 21.05 Q9.2 22.45 11.2 23.2 L15.3 27.3 16 27.6 16.7 27.3 20.85 23.2 Q22.7 22.45 24.3 21.15 L24.45 21.05 Q28 18.15 28 14 M25.75 5.35 Q30.05 8.95 30 14 30.05 19.05 25.75 22.6 L25.4 22.85 Q23.8 24.1 22 24.9 L18.1 28.75 18.15 28.75 Q17.2 29.6 16 29.6 14.8 29.6 13.85 28.75 L13.9 28.75 10 24.9 6.6 22.85 6.25 22.6 Q1.95 19.05 2 14 1.95 8.95 6.25 5.35 10.3 2 16 2 21.7 2 25.75 5.35',
|
|
89
|
+
'M28 14 Q28 18.15 24.45 21.05 L24.3 21.15 Q22.7 22.45 20.85 23.2 L16.7 27.3 16 27.6 15.3 27.3 11.2 23.2 Q9.2 22.45 7.5 21.05 L7.55 21.05 Q4 18.15 4 14 4 9.9 7.45 7 L7.5 6.9 Q11.05 4 16 4 20.95 4 24.45 6.9 28 9.85 28 14'
|
|
75
90
|
),
|
|
76
91
|
12,
|
|
77
|
-
|
|
92
|
+
21,
|
|
78
93
|
'crosshair'
|
|
79
94
|
);
|
|
95
|
+
// pen → Kenney `drawing_pen`, mirrored vertically (FLIP_V) so the nib points
|
|
96
|
+
// down-LEFT (top-right→bottom-left diagonal). Source gradient-shade path dropped.
|
|
97
|
+
// Hotspot at the nib, bottom-left.
|
|
80
98
|
const PEN = svgCursor(
|
|
99
|
+
kenney(
|
|
100
|
+
'M4 13.1 L4 6 Q4 5.15 4.6 4.6 5.15 4 6 4 L13.1 4 Q13.95 4 14.55 4.6 L28.65 18.75 Q29.8 19.9 29.75 21.45 L29.75 21.6 29.75 21.7 Q29.75 23.2 28.7 24.35 L24.45 28.65 24.35 28.75 Q23.15 29.85 21.65 29.8 L21.55 29.8 21.4 29.8 Q20.2 29.8 19.25 29.15 L18 29.35 Q16.6 29.4 15.25 28 L15.2 27.95 8.2 20.95 Q7.6 20.35 7.6 19.55 7.6 18.7 8.2 18.1 L4.6 14.5 Q4 13.9 4 13.1 M19.5 26.65 L20.1 27.25 Q20.7 27.85 21.5 27.8 22.35 27.85 23 27.25 L27.25 22.95 Q27.8 22.35 27.75 21.55 27.8 20.75 27.2 20.15 L13.1 6 6 6 6 13.1 12.35 19.45 9.6 19.5 16.65 26.55 Q18.05 28.05 19.5 26.65 M8.05 12.3 L8 8 12.25 8.05 13.8 9.6 9.6 13.85 8.05 12.3 M15.25 11.05 L23 18.8 18.8 23.05 11.05 15.3 15.25 11.05',
|
|
101
|
+
'M15.25 11.05 L11.05 15.3 18.8 23.05 23 18.8 15.25 11.05 M12.35 19.45 L6 13.1 6 6 13.1 6 27.2 20.15 Q27.8 20.75 27.75 21.55 27.8 22.35 27.25 22.95 L23 27.25 Q22.35 27.85 21.5 27.8 20.7 27.85 20.1 27.25 L19.5 26.65 12.35 19.45 M8.05 12.3 L9.6 13.85 13.8 9.6 12.25 8.05 8 8 8.05 12.3',
|
|
102
|
+
FLIP_V
|
|
103
|
+
),
|
|
104
|
+
6,
|
|
105
|
+
18,
|
|
106
|
+
'crosshair'
|
|
107
|
+
);
|
|
108
|
+
// highlighter → Kenney `drawing_pencil`, also mirrored vertically (distinct from
|
|
109
|
+
// the pen glyph). Hotspot at the lead tip, bottom-left.
|
|
110
|
+
const HIGHLIGHTER = svgCursor(
|
|
81
111
|
kenney(
|
|
82
112
|
'M5.15 10.85 Q4 9.65 4 8.1 L4 7.95 4 7.85 Q3.95 6.2 5.1 5.15 6.25 4 7.85 4 L7.95 4 8.1 4 Q9.65 4 10.85 5.15 L11 5.3 14.3 5.25 Q15.15 5.25 15.75 5.85 L29.9 20 Q31 21.15 31 22.75 L31 22.85 31 23 Q31.05 24.5 29.9 25.7 L29.55 25.95 25.65 29.9 25.65 29.95 Q24.45 31.1 22.75 31.1 21.2 31.15 20 29.95 L5.85 15.8 Q5.25 15.2 5.25 14.35 L5.25 10.95 5.15 10.85 M19.95 24.2 L18.55 22.8 22.8 18.55 24.2 19.95 19.95 24.2 M28.45 21.4 L14.3 7.25 10.15 7.3 9.4 6.55 Q8.8 5.95 7.95 6 7.1 5.95 6.5 6.55 5.95 7.1 6 7.95 5.95 8.8 6.55 9.4 L7.25 10.1 7.25 14.35 21.4 28.5 Q22 29.1 22.75 29.1 23.6 29.1 24.2 28.5 L28.45 24.25 Q29.05 23.65 29 22.85 29.05 22 28.45 21.4 M9.25 13.5 L9.25 9.3 13.5 9.25 14.3 10.05 10.05 14.3 9.25 13.5 M17.15 21.4 L11.5 15.75 15.75 11.5 21.4 17.15 17.15 21.4',
|
|
83
|
-
'M17.15 21.4 L21.4 17.15 15.75 11.5 11.5 15.75 17.15 21.4 M28.45 21.4 Q29.05 22 29 22.85 29.05 23.65 28.45 24.25 L24.2 28.5 Q23.6 29.1 22.75 29.1 22 29.1 21.4 28.5 L7.25 14.35 7.25 10.1 6.55 9.4 Q5.95 8.8 6 7.95 5.95 7.1 6.5 6.55 7.1 5.95 7.95 6 8.8 5.95 9.4 6.55 L10.15 7.3 14.3 7.25 28.45 21.4 M19.95 24.2 L24.2 19.95 22.8 18.55 18.55 22.8 19.95 24.2 M9.25 13.5 L10.05 14.3 14.3 10.05 13.5 9.25 9.25 9.3 9.25 13.5'
|
|
113
|
+
'M17.15 21.4 L21.4 17.15 15.75 11.5 11.5 15.75 17.15 21.4 M28.45 21.4 Q29.05 22 29 22.85 29.05 23.65 28.45 24.25 L24.2 28.5 Q23.6 29.1 22.75 29.1 22 29.1 21.4 28.5 L7.25 14.35 7.25 10.1 6.55 9.4 Q5.95 8.8 6 7.95 5.95 7.1 6.5 6.55 7.1 5.95 7.95 6 8.8 5.95 9.4 6.55 L10.15 7.3 14.3 7.25 28.45 21.4 M19.95 24.2 L24.2 19.95 22.8 18.55 18.55 22.8 19.95 24.2 M9.25 13.5 L10.05 14.3 14.3 10.05 13.5 9.25 9.25 9.3 9.25 13.5',
|
|
114
|
+
FLIP_V
|
|
84
115
|
),
|
|
85
|
-
|
|
86
|
-
|
|
116
|
+
6,
|
|
117
|
+
18,
|
|
87
118
|
'crosshair'
|
|
88
119
|
);
|
|
89
120
|
const CROSSHAIR = svgCursor(
|
|
@@ -140,6 +171,9 @@ export const TOOL_CURSORS: Record<Tool, string> = Object.freeze({
|
|
|
140
171
|
hand: HAND,
|
|
141
172
|
comment: COMMENT,
|
|
142
173
|
pen: PEN,
|
|
174
|
+
// Highlighter = Kenney `drawing_pencil` rot180; pen = `drawing_pen` rot180 —
|
|
175
|
+
// distinct glyphs, both from the Kenney pack (user steer 2026-06-04).
|
|
176
|
+
highlighter: HIGHLIGHTER,
|
|
143
177
|
shape: CROSSHAIR,
|
|
144
178
|
rect: CROSSHAIR,
|
|
145
179
|
ellipse: CROSSHAIR,
|