@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file dom-selection.ts — selection-from-DOM helpers (leaf module)
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/dom-selection.ts
|
|
4
4
|
* @purpose Pure DOM → Selection builders shared by the canvas chrome
|
|
5
5
|
* (canvas-shell.tsx) and the shell-owned comment mount layer
|
|
6
6
|
* (canvas-comment-mount.tsx). Lives in its own leaf module — no
|
|
@@ -114,6 +114,67 @@ export function cssEscape(s: string): string {
|
|
|
114
114
|
* defaults to `deriveFile()`; the comment mount layer passes it explicitly
|
|
115
115
|
* so all three consumers (router, overlay, mount) agree on the same key.
|
|
116
116
|
*/
|
|
117
|
+
/**
|
|
118
|
+
* The artboard-scoped data-cd-id selector. A component shared across artboards
|
|
119
|
+
* carries the SAME data-cd-id in each, so a bare `[data-cd-id="…"]` resolves
|
|
120
|
+
* (via querySelector) to the FIRST artboard. Prefixing the hit's artboard makes
|
|
121
|
+
* the anchor per-instance. Shared by EVERY selector builder + resolver so they
|
|
122
|
+
* can't drift (the original fix only patched one of ~8 sites).
|
|
123
|
+
*/
|
|
124
|
+
export function scopedCdSelector(cdId: string, artboardId?: string | null): string {
|
|
125
|
+
return artboardId
|
|
126
|
+
? `[data-dc-screen="${artboardId}"] [data-cd-id="${cdId}"]`
|
|
127
|
+
: `[data-cd-id="${cdId}"]`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Occurrence index of `el` among `doc.querySelectorAll(selector)`. Even with an
|
|
132
|
+
* artboard-scoped data-cd-id selector, a component repeated WITHIN one artboard
|
|
133
|
+
* (a list row, or a reusable used twice) produces several matches — the index
|
|
134
|
+
* is the only thing that makes the anchor truly unique per DOM instance.
|
|
135
|
+
*/
|
|
136
|
+
export function selectorIndex(doc: Document, selector: string, el: Element | null): number {
|
|
137
|
+
if (!el) return 0;
|
|
138
|
+
try {
|
|
139
|
+
const all = doc.querySelectorAll(selector);
|
|
140
|
+
for (let i = 0; i < all.length; i++) {
|
|
141
|
+
if (all[i] === el) return i;
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
/* malformed selector */
|
|
145
|
+
}
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Resolve a stored Selection to its live element, artboard-scoped. Prefers the
|
|
151
|
+
* id+artboardId scoped selector (the robust path), then the stored `selector`
|
|
152
|
+
* (already scoped for recent selections; a legacy fallback for old comments).
|
|
153
|
+
* Every halo / pin / toolbar / spacing-handle resolver routes through this so a
|
|
154
|
+
* shared component anchors to the instance the user actually clicked.
|
|
155
|
+
*/
|
|
156
|
+
export function resolveSelectionEl(
|
|
157
|
+
doc: Document,
|
|
158
|
+
sel: { id?: string | null; selector?: string | null; artboardId?: string | null; index?: number }
|
|
159
|
+
): Element | null {
|
|
160
|
+
const at = (selector: string): Element | null => {
|
|
161
|
+
try {
|
|
162
|
+
const all = doc.querySelectorAll(selector);
|
|
163
|
+
if (!all.length) return null;
|
|
164
|
+
const i = sel.index && sel.index > 0 && sel.index < all.length ? sel.index : 0;
|
|
165
|
+
return all[i] ?? all[0];
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
if (sel.id) {
|
|
171
|
+
const el = at(scopedCdSelector(sel.id, sel.artboardId));
|
|
172
|
+
if (el) return el;
|
|
173
|
+
}
|
|
174
|
+
if (sel.selector) return at(sel.selector);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
117
178
|
export function hoverTargetToSelection(target: HoverTarget, file?: string): Selection {
|
|
118
179
|
const el = target.el;
|
|
119
180
|
const rect =
|
|
@@ -125,20 +186,30 @@ export function hoverTargetToSelection(target: HoverTarget, file?: string): Sele
|
|
|
125
186
|
// anchor exists.
|
|
126
187
|
const cdId = target.cdId;
|
|
127
188
|
// Selector resolution order:
|
|
128
|
-
// 1. data-cd-id anchor — stable pipeline-stamped id (preferred).
|
|
189
|
+
// 1. data-cd-id anchor — stable pipeline-stamped id (preferred). SCOPED by
|
|
190
|
+
// the hit's artboard (`[data-dc-screen=…] [data-cd-id=…]`) — a component
|
|
191
|
+
// shared across artboards carries the SAME data-cd-id in each, so an
|
|
192
|
+
// unscoped `[data-cd-id]` selector resolves (via querySelector) to the
|
|
193
|
+
// FIRST artboard's instance and the pin/select lands on the wrong board.
|
|
194
|
+
// Prefixing the artboard makes the anchor per-instance.
|
|
129
195
|
// 2. data-dc-screen — chrome click promoted to whole-artboard select
|
|
130
196
|
// (T24.5 G8 multi-artboard gesture).
|
|
131
197
|
// 3. cssPath of the hit — last-resort path string.
|
|
132
198
|
const selector = cdId
|
|
133
|
-
?
|
|
134
|
-
:
|
|
199
|
+
? scopedCdSelector(cdId, target.artboardId)
|
|
200
|
+
: target.artboardId
|
|
135
201
|
? `[data-dc-screen="${target.artboardId}"]`
|
|
136
202
|
: cssPath(el);
|
|
203
|
+
// Disambiguate repeated instances within the same artboard (list rows, a
|
|
204
|
+
// reusable used twice) — the index is which `querySelectorAll(selector)`
|
|
205
|
+
// match this element is. cssPath is already unique, so 0 there.
|
|
206
|
+
const index = cdId && typeof document !== 'undefined' ? selectorIndex(document, selector, el) : 0;
|
|
137
207
|
return {
|
|
138
208
|
file: file ?? deriveFile(),
|
|
139
209
|
id: cdId ?? undefined,
|
|
140
210
|
selector,
|
|
141
211
|
artboardId: target.artboardId,
|
|
212
|
+
index,
|
|
142
213
|
tag: el?.tagName.toLowerCase() ?? '',
|
|
143
214
|
classes: realClasses(el),
|
|
144
215
|
text: shortText(el, 240),
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file draw/animate.ts — draw animation IR (keyframe timeline)
|
|
3
|
+
* @scope apps/studio/draw/animate.ts
|
|
4
|
+
* @purpose The cross-platform ANIMATION source of truth. Where `primitives.ts`
|
|
5
|
+
* names *shape*, this module names *time*: a keyframe `Timeline` of
|
|
6
|
+
* property `Track`s on any node. It extends the DDR-067 single-source
|
|
7
|
+
* invariant from static geometry to motion — one IR feeds the web
|
|
8
|
+
* authoring serializers (SMIL + motion/JSX, `serialize.ts`) AND the
|
|
9
|
+
* production `toLottie()` emitter (DDR-094).
|
|
10
|
+
*
|
|
11
|
+
* Vocabulary:
|
|
12
|
+
* Keyframe — { t, value, ease? } at an ABSOLUTE time (seconds).
|
|
13
|
+
* Track — keyframes on one property of one node (`d` morph as a
|
|
14
|
+
* vertex array, `transform`, `opacity`, gradient stops).
|
|
15
|
+
* Timeline — { dur, begin?, repeat?, tracks, stagger? }.
|
|
16
|
+
*
|
|
17
|
+
* Choreography ("animate sequences of anything, scalable") is built
|
|
18
|
+
* from pure combinators — `sequence` (in series), `parallel` (at
|
|
19
|
+
* once), `stagger` (offset each track by index·delay). Easing is
|
|
20
|
+
* per-segment CSS `cubic-bezier`, evaluated with a Newton-Raphson
|
|
21
|
+
* solver so authoring tokens (incl. overshoot, `y>1`) round-trip
|
|
22
|
+
* exactly into SMIL `keySplines` / Lottie `i`/`o` handles.
|
|
23
|
+
*
|
|
24
|
+
* DEPENDENCY RULE (DDR-067): React-free root. Imports only the pure
|
|
25
|
+
* `Point` type from `primitives.ts`; nothing from react or a `.tsx`.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { Point } from './primitives.ts';
|
|
29
|
+
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
|
+
// Easing — CSS cubic-bezier(x1,y1,x2,y2) timing function
|
|
32
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The four control-point handles of a CSS `cubic-bezier(x1,y1,x2,y2)` easing.
|
|
36
|
+
* `x` components are clamped to [0,1] (CSS constraint); `y` may exceed [0,1]
|
|
37
|
+
* to express overshoot/anticipation (`--ease-out (0.34,1.42,…)` "snap").
|
|
38
|
+
*/
|
|
39
|
+
export type CubicBezierHandles = readonly [x1: number, y1: number, x2: number, y2: number];
|
|
40
|
+
|
|
41
|
+
/** `linear` — the identity timing function. */
|
|
42
|
+
export const LINEAR: CubicBezierHandles = [0, 0, 1, 1];
|
|
43
|
+
|
|
44
|
+
/** One axis of a cubic Bézier with P0=0, P3=1: B(t) = 3(1-t)²t·c1 + 3(1-t)t²·c2 + t³. */
|
|
45
|
+
function bezierAxis(t: number, c1: number, c2: number): number {
|
|
46
|
+
const mt = 1 - t;
|
|
47
|
+
return 3 * mt * mt * t * c1 + 3 * mt * t * t * c2 + t * t * t;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** dB/dt for the same axis — used by the Newton-Raphson x→t solve. */
|
|
51
|
+
function bezierAxisDeriv(t: number, c1: number, c2: number): number {
|
|
52
|
+
const mt = 1 - t;
|
|
53
|
+
return 3 * mt * mt * c1 + 6 * mt * t * (c2 - c1) + 3 * t * t * (1 - c2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Evaluate a CSS cubic-bezier easing: given progress `x` ∈ [0,1] along the
|
|
58
|
+
* timeline, return the eased output `y`. Solves `bezierX(t) = x` for the curve
|
|
59
|
+
* parameter `t` (Newton-Raphson, bisection fallback), then returns `bezierY(t)`.
|
|
60
|
+
* `linear` short-circuits. Output `y` is NOT clamped — overshoot is preserved.
|
|
61
|
+
*/
|
|
62
|
+
export function easeBezier(handles: CubicBezierHandles, x: number): number {
|
|
63
|
+
const [x1, y1, x2, y2] = handles;
|
|
64
|
+
if (x <= 0) return 0;
|
|
65
|
+
if (x >= 1) return 1;
|
|
66
|
+
// Linear fast-path (both handles on the diagonal).
|
|
67
|
+
if (x1 === y1 && x2 === y2) return x;
|
|
68
|
+
|
|
69
|
+
// Solve bezierX(t) = x for t.
|
|
70
|
+
let t = x;
|
|
71
|
+
for (let i = 0; i < 8; i++) {
|
|
72
|
+
const xe = bezierAxis(t, x1, x2) - x;
|
|
73
|
+
if (Math.abs(xe) < 1e-7) break;
|
|
74
|
+
const dx = bezierAxisDeriv(t, x1, x2);
|
|
75
|
+
if (Math.abs(dx) < 1e-7) break;
|
|
76
|
+
t -= xe / dx;
|
|
77
|
+
}
|
|
78
|
+
// Bisection fallback if Newton left the [0,1] domain or stalled.
|
|
79
|
+
if (t < 0 || t > 1 || Number.isNaN(t)) {
|
|
80
|
+
let lo = 0;
|
|
81
|
+
let hi = 1;
|
|
82
|
+
t = x;
|
|
83
|
+
for (let i = 0; i < 32; i++) {
|
|
84
|
+
t = (lo + hi) / 2;
|
|
85
|
+
const xe = bezierAxis(t, x1, x2);
|
|
86
|
+
if (Math.abs(xe - x) < 1e-7) break;
|
|
87
|
+
if (xe < x) lo = t;
|
|
88
|
+
else hi = t;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return bezierAxis(t, y1, y2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
95
|
+
// IR types
|
|
96
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* A value a track can interpolate:
|
|
100
|
+
* • `number` — opacity, scalar scale/rotate, a single stop offset.
|
|
101
|
+
* • `number[]` — element-wise (gradient stop offsets/opacities, a transform
|
|
102
|
+
* decomposed into [tx,ty,scale,rotate], …).
|
|
103
|
+
* • `Point[]` — a `d` shape morph as a vertex array (the cross-renderer
|
|
104
|
+
* interpolable form; see {@link lerpVertices}).
|
|
105
|
+
* • `string` — a discrete value (a `transform` string, a color); NOT
|
|
106
|
+
* numerically interpolated — held until the next keyframe.
|
|
107
|
+
*/
|
|
108
|
+
export type TrackValue = number | number[] | Point[] | string;
|
|
109
|
+
|
|
110
|
+
/** A single keyframe at an ABSOLUTE time `t` (seconds from the track's begin). */
|
|
111
|
+
export interface Keyframe<V extends TrackValue = TrackValue> {
|
|
112
|
+
/** Absolute time in seconds. */
|
|
113
|
+
t: number;
|
|
114
|
+
value: V;
|
|
115
|
+
/**
|
|
116
|
+
* Easing of the segment STARTING at this keyframe (→ the next one), mirroring
|
|
117
|
+
* SMIL `keySplines` (one spline per interval) and Lottie out/in tangents.
|
|
118
|
+
* Omit for `linear`. Ignored on the final keyframe.
|
|
119
|
+
*/
|
|
120
|
+
ease?: CubicBezierHandles;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Animatable property names the serializers understand. Open for extension. */
|
|
124
|
+
export type TrackProperty =
|
|
125
|
+
| 'd'
|
|
126
|
+
| 'transform'
|
|
127
|
+
| 'opacity'
|
|
128
|
+
| 'fill'
|
|
129
|
+
| 'stroke'
|
|
130
|
+
| 'gradientStops'
|
|
131
|
+
| (string & {});
|
|
132
|
+
|
|
133
|
+
/** Keyframes on one property of one node. */
|
|
134
|
+
export interface Track<V extends TrackValue = TrackValue> {
|
|
135
|
+
/** The animated property (`d`, `opacity`, `transform`, …). */
|
|
136
|
+
property: TrackProperty;
|
|
137
|
+
/** `id` of the node this track drives (the serializer targets it). */
|
|
138
|
+
target?: string;
|
|
139
|
+
/** Keyframes, kept sorted ascending by `t` (constructors enforce this). */
|
|
140
|
+
keyframes: Keyframe<V>[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Repeat policy. `'indefinite'` ⇒ SMIL `indefinite` / Lottie loop. */
|
|
144
|
+
export type Repeat = number | 'indefinite';
|
|
145
|
+
|
|
146
|
+
/** A timeline: a bundle of property tracks sharing a clock. */
|
|
147
|
+
export interface Timeline {
|
|
148
|
+
/** Total wall-clock duration in seconds (the choreography envelope). */
|
|
149
|
+
dur: number;
|
|
150
|
+
/** Start offset in seconds (default 0). */
|
|
151
|
+
begin?: number;
|
|
152
|
+
/** Repeat count or `'indefinite'`. */
|
|
153
|
+
repeat?: Repeat;
|
|
154
|
+
tracks: Track[];
|
|
155
|
+
/**
|
|
156
|
+
* If set, each track's keyframes are offset by `index · stagger` seconds at
|
|
157
|
+
* resolve time (see {@link resolveStagger}). A declarative shorthand for
|
|
158
|
+
* {@link stagger}; resolved (baked into keyframe `t`) before serialization.
|
|
159
|
+
*/
|
|
160
|
+
stagger?: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
164
|
+
// Constructors
|
|
165
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
/** Build a track, sorting keyframes by time (defensive — callers may be loose). */
|
|
168
|
+
export function track<V extends TrackValue>(
|
|
169
|
+
property: TrackProperty,
|
|
170
|
+
keyframes: Keyframe<V>[],
|
|
171
|
+
target?: string
|
|
172
|
+
): Track<V> {
|
|
173
|
+
const sorted = [...keyframes].sort((a, b) => a.t - b.t);
|
|
174
|
+
const out: Track<V> = { property, keyframes: sorted };
|
|
175
|
+
if (target !== undefined) out.target = target;
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Build a timeline; `dur` defaults to the latest keyframe time across tracks. */
|
|
180
|
+
export function timeline(o: {
|
|
181
|
+
tracks: Track[];
|
|
182
|
+
dur?: number;
|
|
183
|
+
begin?: number;
|
|
184
|
+
repeat?: Repeat;
|
|
185
|
+
stagger?: number;
|
|
186
|
+
}): Timeline {
|
|
187
|
+
const dur = o.dur ?? trackSpan(o.tracks);
|
|
188
|
+
const out: Timeline = { dur, tracks: o.tracks };
|
|
189
|
+
if (o.begin !== undefined) out.begin = o.begin;
|
|
190
|
+
if (o.repeat !== undefined) out.repeat = o.repeat;
|
|
191
|
+
if (o.stagger !== undefined) out.stagger = o.stagger;
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The latest keyframe time across a set of tracks (0 if none). */
|
|
196
|
+
export function trackSpan(tracks: Track[]): number {
|
|
197
|
+
let max = 0;
|
|
198
|
+
for (const tr of tracks) {
|
|
199
|
+
const last = tr.keyframes[tr.keyframes.length - 1];
|
|
200
|
+
if (last && last.t > max) max = last.t;
|
|
201
|
+
}
|
|
202
|
+
return max;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
206
|
+
// Interpolation
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Vertex-array interpolation for `d` morphs. REQUIRES equal length — the
|
|
211
|
+
* cross-renderer interpolability constraint (an identical command template /
|
|
212
|
+
* fixed vertex count). Throws on mismatch so a bad morph fails loud at author
|
|
213
|
+
* time rather than producing a degenerate tween.
|
|
214
|
+
*/
|
|
215
|
+
export function lerpVertices(a: Point[], b: Point[], u: number): Point[] {
|
|
216
|
+
if (a.length !== b.length) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`lerpVertices: vertex count mismatch (${a.length} vs ${b.length}); ` +
|
|
219
|
+
'morph endpoints must share a fixed vertex count'
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
return a.map((p, i) => ({ x: p.x + (b[i].x - p.x) * u, y: p.y + (b[i].y - p.y) * u }));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Interpolate two same-shaped {@link TrackValue}s by `u` ∈ [0,1]. */
|
|
226
|
+
export function lerpValue(a: TrackValue, b: TrackValue, u: number): TrackValue {
|
|
227
|
+
if (typeof a === 'number' && typeof b === 'number') return a + (b - a) * u;
|
|
228
|
+
if (typeof a === 'string' || typeof b === 'string') return u < 1 ? a : b; // discrete hold
|
|
229
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
230
|
+
if (a.length !== b.length) {
|
|
231
|
+
throw new Error(`lerpValue: array length mismatch (${a.length} vs ${b.length})`);
|
|
232
|
+
}
|
|
233
|
+
if (a.length === 0) return [];
|
|
234
|
+
// Point[] (vertex array) vs number[].
|
|
235
|
+
if (typeof a[0] === 'object') return lerpVertices(a as Point[], b as Point[], u);
|
|
236
|
+
return (a as number[]).map((n, i) => n + ((b as number[])[i] - n) * u);
|
|
237
|
+
}
|
|
238
|
+
throw new Error('lerpValue: incompatible value kinds');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Sample a track at absolute time `tSec`. Clamps outside the keyframe span
|
|
243
|
+
* (hold first / hold last). Applies the per-segment easing of the leading
|
|
244
|
+
* keyframe, then interpolates. Returns `undefined` for an empty track.
|
|
245
|
+
*/
|
|
246
|
+
export function sampleTrack(tr: Track, tSec: number): TrackValue | undefined {
|
|
247
|
+
const kfs = tr.keyframes;
|
|
248
|
+
if (kfs.length === 0) return undefined;
|
|
249
|
+
if (kfs.length === 1 || tSec <= kfs[0].t) return kfs[0].value;
|
|
250
|
+
const last = kfs[kfs.length - 1];
|
|
251
|
+
if (tSec >= last.t) return last.value;
|
|
252
|
+
|
|
253
|
+
let i = 0;
|
|
254
|
+
while (i < kfs.length - 1 && kfs[i + 1].t <= tSec) i++;
|
|
255
|
+
const a = kfs[i];
|
|
256
|
+
const b = kfs[i + 1];
|
|
257
|
+
const span = b.t - a.t;
|
|
258
|
+
const x = span > 0 ? (tSec - a.t) / span : 0;
|
|
259
|
+
const u = a.ease ? easeBezier(a.ease, x) : x;
|
|
260
|
+
return lerpValue(a.value, b.value, u);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
264
|
+
// Choreography combinators (pure)
|
|
265
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
/** Shift every keyframe in a track by `dt` seconds (immutable). */
|
|
268
|
+
export function shiftTrack(tr: Track, dt: number): Track {
|
|
269
|
+
if (dt === 0) return tr;
|
|
270
|
+
const out: Track = {
|
|
271
|
+
property: tr.property,
|
|
272
|
+
keyframes: tr.keyframes.map((k) => ({ ...k, t: k.t + dt })),
|
|
273
|
+
};
|
|
274
|
+
if (tr.target !== undefined) out.target = tr.target;
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Bake a timeline's declarative `stagger` field into its tracks (keyframe `t`
|
|
280
|
+
* += index·stagger), clearing the field and growing `dur` to fit. A no-op when
|
|
281
|
+
* `stagger` is unset/zero. Idempotent on an already-resolved timeline.
|
|
282
|
+
*/
|
|
283
|
+
export function resolveStagger(tl: Timeline): Timeline {
|
|
284
|
+
if (!tl.stagger) return tl;
|
|
285
|
+
const tracks = tl.tracks.map((tr, i) => shiftTrack(tr, i * (tl.stagger as number)));
|
|
286
|
+
const out: Timeline = { dur: Math.max(tl.dur, trackSpan(tracks)), tracks };
|
|
287
|
+
if (tl.begin !== undefined) out.begin = tl.begin;
|
|
288
|
+
if (tl.repeat !== undefined) out.repeat = tl.repeat;
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Stagger a list of tracks: track `i` is offset by `i · delay` seconds. The
|
|
294
|
+
* imperative form of {@link Timeline.stagger}. Returns the offset tracks (sort
|
|
295
|
+
* order preserved).
|
|
296
|
+
*/
|
|
297
|
+
export function stagger(tracks: Track[], delay: number): Track[] {
|
|
298
|
+
return tracks.map((tr, i) => shiftTrack(tr, i * delay));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Compose timelines IN PARALLEL — all start together; the result's tracks are
|
|
303
|
+
* the union (each first resolved for its own `stagger`/`begin`). `dur` is the
|
|
304
|
+
* max. The shape for "these things animate at once".
|
|
305
|
+
*/
|
|
306
|
+
export function parallel(...timelines: Timeline[]): Timeline {
|
|
307
|
+
const tracks: Track[] = [];
|
|
308
|
+
let dur = 0;
|
|
309
|
+
for (const raw of timelines) {
|
|
310
|
+
const tl = resolveStagger(raw);
|
|
311
|
+
const b = tl.begin ?? 0;
|
|
312
|
+
for (const tr of tl.tracks) tracks.push(shiftTrack(tr, b));
|
|
313
|
+
dur = Math.max(dur, b + tl.dur);
|
|
314
|
+
}
|
|
315
|
+
return { dur, tracks };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Compose timelines IN SERIES — each starts where the previous ended (its own
|
|
320
|
+
* `begin` adds extra gap). The shape for "first this, then that". Durations
|
|
321
|
+
* sum (plus gaps); tracks are shifted onto the shared clock.
|
|
322
|
+
*/
|
|
323
|
+
export function sequence(...timelines: Timeline[]): Timeline {
|
|
324
|
+
const tracks: Track[] = [];
|
|
325
|
+
let cursor = 0;
|
|
326
|
+
for (const raw of timelines) {
|
|
327
|
+
const tl = resolveStagger(raw);
|
|
328
|
+
const start = cursor + (tl.begin ?? 0);
|
|
329
|
+
for (const tr of tl.tracks) tracks.push(shiftTrack(tr, start));
|
|
330
|
+
cursor = start + tl.dur;
|
|
331
|
+
}
|
|
332
|
+
return { dur: cursor, tracks };
|
|
333
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file draw/brush.ts — Phase 25.2 brush layer
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/draw/brush.ts
|
|
4
4
|
* @purpose "Brush" expression for a pure-vector engine, three levels:
|
|
5
5
|
* L1 roughenFilter() — feTurbulence → feDisplacementMap edge
|
|
6
6
|
* texture (dry-brush / ink / charcoal roughness) you apply to
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file draw/composition.ts — Phase 25.1 compositional scaffolding layer
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/draw/composition.ts
|
|
4
4
|
* @purpose The missing answer to "blob soup": deterministic COMPOSITION.
|
|
5
5
|
* Random placement is empirically catastrophic — only ~3–12% of
|
|
6
6
|
* randomly-placed multi-element layouts even avoid overlap (Shiripour
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file draw/geometry.ts — Phase 25 geometry-engine math layer
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/draw/geometry.ts
|
|
4
4
|
* @purpose Pure, deterministic geometry the LLM must NOT free-hand:
|
|
5
5
|
* • PCHIP monotone-cubic interpolation (overshoot-free curves —
|
|
6
6
|
* the whole point vs LLM-guessed Béziers, which wobble);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file draw/index.ts — Phase 25 geometry-engine public surface
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/draw/index.ts
|
|
4
4
|
* @purpose Single import point for the draw engine. The draw-agent and the
|
|
5
5
|
* `draw-proof` / `svg-optimize` bin helpers import from here:
|
|
6
6
|
*
|
|
@@ -18,11 +18,14 @@
|
|
|
18
18
|
* React-free (DDR-067) — nothing here imports react / a `.tsx`.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
export * from './animate.ts';
|
|
21
22
|
export * from './brush.ts';
|
|
22
23
|
export * from './composition.ts';
|
|
23
24
|
export * from './geometry.ts';
|
|
24
25
|
export * from './layout.ts';
|
|
26
|
+
export * from './morph.ts';
|
|
25
27
|
export * from './optimize.ts';
|
|
26
28
|
export * from './palette.ts';
|
|
27
29
|
export * from './primitives.ts';
|
|
28
30
|
export * from './serialize.ts';
|
|
31
|
+
export * from './serialize-animate.ts';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file draw/layout.ts — Phase 25 geometry-engine layout / diagram layer
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/draw/layout.ts
|
|
4
4
|
* @purpose Composition math:
|
|
5
5
|
* • a deterministic constraint-based label placer (pick the
|
|
6
6
|
* candidate slot around each anchor that minimizes overlap —
|