@1agh/maude 0.28.0 → 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 +286 -6
- package/{plugins/design/dev-server → apps/studio}/annotations-layer.tsx +1209 -122
- 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/scenario-report.mjs +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
- package/apps/studio/bin/server-up.sh +234 -0
- 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}/bin/visual-sanity.sh +19 -3
- 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 +2 -2
- package/apps/studio/canvas-create.ts +104 -0
- package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +47 -13
- 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 +41 -33
- package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +57 -42
- 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}/collab/index.ts +4 -4
- package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +1 -1
- 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 +2 -2
- 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/runtime/lib0_decoding.js +1 -0
- package/apps/studio/dist/runtime/lib0_encoding.js +1 -0
- package/apps/studio/dist/runtime/motion.js +4 -0
- package/apps/studio/dist/runtime/motion_react.js +12 -0
- package/apps/studio/dist/runtime/pixi-js.js +3397 -0
- package/apps/studio/dist/runtime/react-dom.js +1 -0
- package/apps/studio/dist/runtime/react-dom_client.js +9 -0
- package/apps/studio/dist/runtime/react.js +1 -0
- package/apps/studio/dist/runtime/react_jsx-dev-runtime.js +1 -0
- package/apps/studio/dist/runtime/react_jsx-runtime.js +1 -0
- package/apps/studio/dist/runtime/y-protocols_awareness.js +1 -0
- package/apps/studio/dist/runtime/y-protocols_sync.js +1 -0
- package/apps/studio/dist/runtime/yjs.js +5 -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 +4 -4
- 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 +9 -6
- package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +2 -2
- 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/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +1 -1
- 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}/draw/test/serialize.test.ts +1 -1
- 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}/examples/perf-100-artboards.tsx +1 -2
- package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +14 -2
- package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/http.ts +103 -2
- 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 +6 -3
- 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 +19 -1
- package/{plugins/design/dev-server → apps/studio}/sync/index.ts +6 -6
- package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +69 -6
- package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +98 -1
- package/apps/studio/test/annotations-text-format.test.ts +396 -0
- package/apps/studio/test/asset-api.test.ts +149 -0
- package/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +1 -1
- package/apps/studio/test/canvas-create-api.test.ts +361 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +3 -1
- package/{plugins/design/dev-server → apps/studio}/test/canvas-edit.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +2 -3
- package/apps/studio/test/canvas-media-drop.test.ts +158 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +18 -3
- package/apps/studio/test/canvas-url.test.ts +99 -0
- package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +1 -1
- 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/runtime-bundle.test.ts +1 -1
- 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 +2 -2
- package/{plugins/design/dev-server → apps/studio}/test/shared-doc-migrate.test.ts +2 -3
- package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/sync-agent.test.ts +2 -3
- package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +2 -3
- package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +4 -5
- package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +1 -2
- package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +3 -8
- package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +1 -2
- package/apps/studio/test/tour-overlay.test.tsx +56 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +4 -1
- package/{plugins/design/dev-server → apps/studio}/test/use-annotation-resize.test.ts +43 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +1 -1
- 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 +2 -2
- package/{plugins/design/dev-server → apps/studio}/use-annotation-resize.tsx +41 -12
- package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +3 -7
- package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +2 -2
- package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +2 -2
- package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +2 -2
- package/apps/studio/use-canvas-media-drop.tsx +343 -0
- package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +4 -5
- 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 +10 -2
- package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +4 -2
- package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +5 -6
- 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 +84 -10
- package/cli/commands/design.test.mjs +133 -1
- 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/cache.mjs +1 -1
- package/cli/lib/config-lint.mjs +1 -1
- package/cli/lib/copy-tree.mjs +1 -1
- package/cli/lib/design-link.mjs +1 -1
- package/cli/lib/flow-design-integration.test.mjs +2 -2
- package/cli/lib/gitignore-block.mjs +1 -0
- package/cli/lib/gitignore-block.test.mjs +2 -2
- package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
- package/cli/lib/preflight.mjs +1 -1
- package/cli/lib/stack-detect.mjs +1 -1
- package/package.json +16 -16
- package/plugins/design/templates/_shell.html +3 -3
- 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/SUB-AGENT-PROMPTS.md +18 -3
- 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/bin/server-up.sh +0 -135
- 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/runtime/lib0_decoding.js +0 -620
- package/plugins/design/dev-server/dist/runtime/lib0_encoding.js +0 -604
- package/plugins/design/dev-server/dist/runtime/motion.js +0 -4944
- package/plugins/design/dev-server/dist/runtime/motion_react.js +0 -10056
- package/plugins/design/dev-server/dist/runtime/pixi-js.js +0 -53104
- package/plugins/design/dev-server/dist/runtime/react-dom.js +0 -231
- package/plugins/design/dev-server/dist/runtime/react-dom_client.js +0 -10357
- package/plugins/design/dev-server/dist/runtime/react.js +0 -535
- package/plugins/design/dev-server/dist/runtime/react_jsx-dev-runtime.js +0 -60
- package/plugins/design/dev-server/dist/runtime/react_jsx-runtime.js +0 -85
- package/plugins/design/dev-server/dist/runtime/y-protocols_awareness.js +0 -376
- package/plugins/design/dev-server/dist/runtime/y-protocols_sync.js +0 -100
- package/plugins/design/dev-server/dist/runtime/yjs.js +0 -6687
- 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/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}/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 +1 -1
- package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.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}/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 +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.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 +1 -1
- package/{plugins/design/dev-server → apps/studio}/examples/README.md +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 +1 -1
- package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +1 -1
- 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 +1 -1
- 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/migrate-seed.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +3 -3
- 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/annotation-strokes-command.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +2 -2
- 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/bundle-smoke.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.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-awareness-bridge.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.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/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/endpoint.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/svg.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/handoff.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/history-rollback.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 +2 -2
- package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +1 -1
- 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/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/sync-codec.test.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +1 -1
- 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 +2 -2
- 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 +3 -3
- package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +2 -2
- 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 +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +2 -2
- /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 use-annotation-selection.tsx — Phase 5.1 annotation selection store
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-annotation-selection.tsx
|
|
4
4
|
* @purpose Parallel to `use-selection-set`, but holds annotation stroke IDs
|
|
5
5
|
* instead of DOM-element selections. Annotation IDs are local
|
|
6
6
|
* identifiers (`s_*`) generated by `rid()` — they never live in
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* selection resets (the strokes themselves persist via `.annotations.svg`).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { type ReactNode,
|
|
13
|
+
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from 'react';
|
|
14
14
|
|
|
15
15
|
export interface AnnotationSelectionValue {
|
|
16
16
|
selectedIds: string[];
|
|
@@ -34,11 +34,7 @@ function dedupe(ids: string[]): string[] {
|
|
|
34
34
|
return out;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function AnnotationSelectionProvider({
|
|
38
|
-
children,
|
|
39
|
-
}: {
|
|
40
|
-
children: ReactNode;
|
|
41
|
-
}) {
|
|
37
|
+
export function AnnotationSelectionProvider({ children }: { children: ReactNode }) {
|
|
42
38
|
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
43
39
|
|
|
44
40
|
const replace = useCallback((id: string | string[]) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-annotations-visibility.tsx — Phase 5.1 visibility store
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-annotations-visibility.tsx
|
|
4
4
|
* @purpose Tiny boolean store shared by AnnotationsLayer (render gate),
|
|
5
5
|
* ToolPalette (presentation toggle button), and the menubar
|
|
6
6
|
* bridge (`view-annotations` postMessage handler in
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* siblings under CanvasRouter all consume the same value.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { type ReactNode,
|
|
11
|
+
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from 'react';
|
|
12
12
|
|
|
13
13
|
export interface AnnotationsVisibilityValue {
|
|
14
14
|
visible: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-artboard-drag.tsx — Phase 4.2 artboard drag controller
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-artboard-drag.tsx
|
|
4
4
|
* @purpose Owns the pointerdown → pointermove → pointerup state machine
|
|
5
5
|
* for dragging artboards on the infinite canvas. The pure
|
|
6
6
|
* `dragReducer` + `commitFromState` are unit-testable without a
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
|
|
39
39
|
import { DRAG_THRESHOLD_PX as INPUT_DRAG_THRESHOLD_PX } from './input-router.tsx';
|
|
40
40
|
import type { Selection } from './use-selection-set.tsx';
|
|
41
|
-
import { type Rect, type SnapResult
|
|
41
|
+
import { computeSnap, type Rect, type SnapResult } from './use-snap-guides.tsx';
|
|
42
42
|
|
|
43
43
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
44
44
|
// Constants
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-canvas-activity.tsx — Phase 13 / DDR-029 activity context.
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-canvas-activity.tsx
|
|
4
4
|
* @purpose Iframe-runtime React context fed by the server's `activity` WS
|
|
5
5
|
* messages. `DCArtboard` reads it to render the "agent works here"
|
|
6
6
|
* overlay on the artboards being edited right now.
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
-
type ReactNode,
|
|
18
17
|
createContext,
|
|
18
|
+
type ReactNode,
|
|
19
19
|
useContext,
|
|
20
20
|
useEffect,
|
|
21
21
|
useMemo,
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file use-canvas-media-drop.tsx — Phase 23 canvas media intake
|
|
3
|
+
* @scope apps/studio/use-canvas-media-drop.tsx
|
|
4
|
+
* @purpose OS-level drag-and-drop + clipboard paste of images and URLs onto
|
|
5
|
+
* the canvas. Routes each gesture to a create callback owned by
|
|
6
|
+
* AnnotationsLayer (which holds the commit/undo sink + screenToWorld):
|
|
7
|
+
* • image file → `onImage(file, world)` → optimistic stroke + upload
|
|
8
|
+
* • http(s) URL → `onLink(url, title, world)` → client-only link chip
|
|
9
|
+
*
|
|
10
|
+
* The classification + URL helpers are PURE + exported so the unit
|
|
11
|
+
* tests cover the dispatch matrix without a real DataTransfer /
|
|
12
|
+
* ClipboardEvent. The hook itself only wires the DOM events and the
|
|
13
|
+
* drag-affordance class.
|
|
14
|
+
*
|
|
15
|
+
* Security: link URLs are gated to http(s) (no javascript:/data:);
|
|
16
|
+
* image bytes go through POST /_api/asset (magic-byte sniff + caps,
|
|
17
|
+
* DDR Task 9). Nothing here trusts the dropped content.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { useEffect } from 'react';
|
|
21
|
+
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
// Pure helpers (exported for unit tests)
|
|
24
|
+
|
|
25
|
+
/** A normalized snapshot of a DataTransfer / ClipboardData payload. */
|
|
26
|
+
export interface MediaPayload {
|
|
27
|
+
files: readonly File[];
|
|
28
|
+
/** text/uri-list (DnD) */
|
|
29
|
+
uriList: string;
|
|
30
|
+
/** text/html (carries the anchor text for a dragged/pasted link) */
|
|
31
|
+
html: string;
|
|
32
|
+
/** text/plain */
|
|
33
|
+
plain: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type MediaIntent =
|
|
37
|
+
| { kind: 'image'; file: File }
|
|
38
|
+
| { kind: 'link'; url: string; title: string };
|
|
39
|
+
|
|
40
|
+
/** True only for an absolute http(s) URL — the one scheme a link chip accepts. */
|
|
41
|
+
export function isHttpUrl(value: string): boolean {
|
|
42
|
+
try {
|
|
43
|
+
const u = new URL(value.trim());
|
|
44
|
+
return u.protocol === 'http:' || u.protocol === 'https:';
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Normalize a single token into a safe http(s) URL, or null. You should NOT have
|
|
52
|
+
* to type a scheme to paste a link:
|
|
53
|
+
* - an explicit scheme is kept ONLY if it's http(s) — `javascript:` / `data:`
|
|
54
|
+
* / `file:` / `mailto:` all return null (never reach window.open);
|
|
55
|
+
* - a scheme-less but domain-shaped token (`example.com`, `sub.example.io/x`)
|
|
56
|
+
* gets `https://` prepended. The TLD must be alphabetic (≥2) so a bare
|
|
57
|
+
* decimal like `3.5` or `v1.2` is NOT mistaken for a domain.
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeUrl(raw: string): string | null {
|
|
60
|
+
const s = raw.trim();
|
|
61
|
+
if (!s) return null;
|
|
62
|
+
// Distinguish a real scheme (`http:`, `javascript:`) from a `host:port` colon.
|
|
63
|
+
// A scheme: the part before the FIRST colon is a valid scheme name (no `/`) AND
|
|
64
|
+
// the char after the colon is NOT a digit (a digit there is a port → scheme-less).
|
|
65
|
+
const colon = s.indexOf(':');
|
|
66
|
+
const hasScheme =
|
|
67
|
+
colon > 0 &&
|
|
68
|
+
/^[a-zA-Z][a-zA-Z0-9+.-]*$/.test(s.slice(0, colon)) &&
|
|
69
|
+
!/^\d/.test(s.slice(colon + 1));
|
|
70
|
+
if (hasScheme) return isHttpUrl(s) ? s : null;
|
|
71
|
+
// Scheme-less but domain-shaped (alphabetic TLD ≥2) → assume https.
|
|
72
|
+
if (/^([a-z0-9-]+\.)+[a-z]{2,}(:\d+)?(\/\S*)?$/i.test(s)) {
|
|
73
|
+
const u = `https://${s}`;
|
|
74
|
+
return isHttpUrl(u) ? u : null;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* First link URL in a blob of text, normalized to http(s). Handles a uri-list
|
|
81
|
+
* (newline-separated, `#` comment lines), a bare-domain paste, and an explicit
|
|
82
|
+
* inline URL inside prose. Returns null when none.
|
|
83
|
+
*/
|
|
84
|
+
export function firstHttpUrl(text: string): string | null {
|
|
85
|
+
if (!text) return null;
|
|
86
|
+
for (const raw of text.split(/[\r\n]+/)) {
|
|
87
|
+
const line = raw.trim();
|
|
88
|
+
if (!line || line.startsWith('#')) continue;
|
|
89
|
+
const u = normalizeUrl(line);
|
|
90
|
+
if (u) return u;
|
|
91
|
+
}
|
|
92
|
+
// Fall back to an explicit inline URL token inside prose (a scheme is required
|
|
93
|
+
// here — bare-domain detection only fires on a whole standalone line).
|
|
94
|
+
const m = text.match(/https?:\/\/[^\s"'<>]+/i);
|
|
95
|
+
return m ? normalizeUrl(m[0]) : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Hostname without a leading `www.` — the chip's domain label. */
|
|
99
|
+
export function linkDomain(url: string): string {
|
|
100
|
+
try {
|
|
101
|
+
return new URL(url).hostname.replace(/^www\./, '');
|
|
102
|
+
} catch {
|
|
103
|
+
return url;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A readable title fallback when no anchor text is available. */
|
|
108
|
+
export function prettifyUrl(url: string): string {
|
|
109
|
+
try {
|
|
110
|
+
const u = new URL(url);
|
|
111
|
+
const host = u.hostname.replace(/^www\./, '');
|
|
112
|
+
const path = u.pathname.replace(/\/+$/, '');
|
|
113
|
+
return path && path !== '/' ? `${host}${path}` : host;
|
|
114
|
+
} catch {
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Anchor text from a `text/html` DnD/clipboard payload, else null. */
|
|
120
|
+
export function anchorTextFromHtml(html: string): string | null {
|
|
121
|
+
if (!html) return null;
|
|
122
|
+
const m = html.match(/<a\b[^>]*>([\s\S]*?)<\/a>/i);
|
|
123
|
+
if (!m?.[1]) return null;
|
|
124
|
+
const text = m[1]
|
|
125
|
+
.replace(/<[^>]*>/g, '')
|
|
126
|
+
.replace(/\s+/g, ' ')
|
|
127
|
+
.trim();
|
|
128
|
+
return text || null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Decide what a drop / paste is: an image file wins over a URL (a dragged image
|
|
133
|
+
* carries both an image file AND a uri-list pointing at it). Returns null for a
|
|
134
|
+
* plain-text / non-media payload so the caller leaves the event untouched.
|
|
135
|
+
*/
|
|
136
|
+
export function classifyMediaPayload(p: MediaPayload): MediaIntent | null {
|
|
137
|
+
const imageFile = p.files.find((f) => typeof f.type === 'string' && f.type.startsWith('image/'));
|
|
138
|
+
if (imageFile) return { kind: 'image', file: imageFile };
|
|
139
|
+
const url = firstHttpUrl(p.uriList) ?? firstHttpUrl(p.plain);
|
|
140
|
+
if (url) {
|
|
141
|
+
const title = anchorTextFromHtml(p.html) ?? prettifyUrl(url);
|
|
142
|
+
return { kind: 'link', url, title };
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
// Upload
|
|
149
|
+
|
|
150
|
+
/** POST raw image bytes to the capped asset route. Returns the assets/ path. */
|
|
151
|
+
export async function uploadAsset(file: Blob): Promise<{ path: string } | { error: string }> {
|
|
152
|
+
try {
|
|
153
|
+
const res = await fetch('/_api/asset', {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': file.type || 'application/octet-stream' },
|
|
156
|
+
body: file,
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
let msg = `upload failed (${res.status})`;
|
|
160
|
+
try {
|
|
161
|
+
const j = (await res.json()) as { error?: string };
|
|
162
|
+
if (j?.error) msg = j.error;
|
|
163
|
+
} catch {
|
|
164
|
+
/* non-JSON error body */
|
|
165
|
+
}
|
|
166
|
+
return { error: msg };
|
|
167
|
+
}
|
|
168
|
+
const j = (await res.json()) as { path?: string };
|
|
169
|
+
return typeof j?.path === 'string' ? { path: j.path } : { error: 'malformed upload response' };
|
|
170
|
+
} catch (e) {
|
|
171
|
+
return { error: e instanceof Error ? e.message : 'network error' };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
176
|
+
// Transient toast (no React state — self-contained DOM, mirrors ensure*Styles)
|
|
177
|
+
|
|
178
|
+
const TOAST_CSS = `
|
|
179
|
+
.dc-media-toast {
|
|
180
|
+
position: fixed;
|
|
181
|
+
left: 50%;
|
|
182
|
+
bottom: 108px;
|
|
183
|
+
transform: translateX(-50%);
|
|
184
|
+
z-index: 9;
|
|
185
|
+
max-width: 360px;
|
|
186
|
+
padding: 8px 14px;
|
|
187
|
+
border-radius: 8px;
|
|
188
|
+
background: #26262b;
|
|
189
|
+
color: #fff;
|
|
190
|
+
font-family: var(--maude-chrome-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
191
|
+
font-size: 12px;
|
|
192
|
+
box-shadow: 0 8px 28px rgba(0,0,0,0.34);
|
|
193
|
+
opacity: 0;
|
|
194
|
+
transition: opacity 140ms ease;
|
|
195
|
+
pointer-events: none;
|
|
196
|
+
}
|
|
197
|
+
.dc-media-toast[data-show="1"] { opacity: 1; }
|
|
198
|
+
/* Drop affordance — a focus-accent inset frame while a media drag is over the
|
|
199
|
+
canvas, so the surface reads as a drop target. */
|
|
200
|
+
.dc-media-dragover::after {
|
|
201
|
+
content: "";
|
|
202
|
+
position: fixed;
|
|
203
|
+
inset: 6px;
|
|
204
|
+
z-index: 8;
|
|
205
|
+
border: 2px dashed var(--maude-hud-accent, #d63b1f);
|
|
206
|
+
border-radius: 12px;
|
|
207
|
+
pointer-events: none;
|
|
208
|
+
}
|
|
209
|
+
@media (prefers-reduced-motion: reduce) {
|
|
210
|
+
.dc-media-toast { transition: none; }
|
|
211
|
+
}
|
|
212
|
+
`.trim();
|
|
213
|
+
|
|
214
|
+
function ensureMediaStyles(): void {
|
|
215
|
+
if (typeof document === 'undefined') return;
|
|
216
|
+
if (document.getElementById('dc-media-css')) return;
|
|
217
|
+
const s = document.createElement('style');
|
|
218
|
+
s.id = 'dc-media-css';
|
|
219
|
+
s.textContent = TOAST_CSS;
|
|
220
|
+
document.head.appendChild(s);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Show a brief auto-dismissing toast in the canvas (e.g. an upload failure). */
|
|
224
|
+
export function showCanvasToast(message: string): void {
|
|
225
|
+
if (typeof document === 'undefined') return;
|
|
226
|
+
ensureMediaStyles();
|
|
227
|
+
const el = document.createElement('div');
|
|
228
|
+
el.className = 'dc-media-toast';
|
|
229
|
+
el.setAttribute('role', 'status');
|
|
230
|
+
el.textContent = message;
|
|
231
|
+
document.body.appendChild(el);
|
|
232
|
+
requestAnimationFrame(() => el.setAttribute('data-show', '1'));
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
el.setAttribute('data-show', '0');
|
|
235
|
+
setTimeout(() => el.remove(), 200);
|
|
236
|
+
}, 2600);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
240
|
+
// Hook
|
|
241
|
+
|
|
242
|
+
export interface MediaDropCallbacks {
|
|
243
|
+
onImage: (file: File, world: [number, number]) => void;
|
|
244
|
+
onLink: (url: string, title: string, world: [number, number]) => void;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function useCanvasMediaDrop(opts: {
|
|
248
|
+
enabled: boolean;
|
|
249
|
+
screenToWorld: (cx: number, cy: number) => [number, number];
|
|
250
|
+
callbacks: MediaDropCallbacks;
|
|
251
|
+
}): void {
|
|
252
|
+
const { enabled, screenToWorld, callbacks } = opts;
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (!enabled || typeof document === 'undefined') return;
|
|
255
|
+
ensureMediaStyles();
|
|
256
|
+
const body = document.body;
|
|
257
|
+
|
|
258
|
+
const hasMedia = (dt: DataTransfer | null): boolean => {
|
|
259
|
+
if (!dt) return false;
|
|
260
|
+
const types = Array.from(dt.types ?? []);
|
|
261
|
+
return types.includes('Files') || types.includes('text/uri-list');
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const payloadFromTransfer = (dt: DataTransfer): MediaPayload => ({
|
|
265
|
+
files: dt.files ? Array.from(dt.files) : [],
|
|
266
|
+
uriList: safeGet(dt, 'text/uri-list'),
|
|
267
|
+
html: safeGet(dt, 'text/html'),
|
|
268
|
+
plain: safeGet(dt, 'text/plain'),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const onDragOver = (e: DragEvent) => {
|
|
272
|
+
if (!hasMedia(e.dataTransfer)) return;
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
|
|
275
|
+
body.classList.add('dc-media-dragover');
|
|
276
|
+
};
|
|
277
|
+
const onDragLeave = (e: DragEvent) => {
|
|
278
|
+
// Only clear when the pointer actually leaves the window (related target
|
|
279
|
+
// null), not when crossing between child elements.
|
|
280
|
+
if (e.relatedTarget == null) body.classList.remove('dc-media-dragover');
|
|
281
|
+
};
|
|
282
|
+
const onDrop = (e: DragEvent) => {
|
|
283
|
+
body.classList.remove('dc-media-dragover');
|
|
284
|
+
if (!e.dataTransfer) return;
|
|
285
|
+
const intent = classifyMediaPayload(payloadFromTransfer(e.dataTransfer));
|
|
286
|
+
if (!intent) return; // not media — leave the event for other handlers
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
const world = screenToWorld(e.clientX, e.clientY);
|
|
289
|
+
dispatchIntent(intent, world);
|
|
290
|
+
};
|
|
291
|
+
const onPaste = (e: ClipboardEvent) => {
|
|
292
|
+
const cd = e.clipboardData;
|
|
293
|
+
if (!cd) return;
|
|
294
|
+
const files: File[] = [];
|
|
295
|
+
if (cd.files) for (const f of Array.from(cd.files)) files.push(f);
|
|
296
|
+
// Some browsers expose pasted images only via items, not files.
|
|
297
|
+
if (cd.items) {
|
|
298
|
+
for (const it of Array.from(cd.items)) {
|
|
299
|
+
if (it.kind === 'file' && it.type.startsWith('image/')) {
|
|
300
|
+
const f = it.getAsFile();
|
|
301
|
+
if (f && !files.includes(f)) files.push(f);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const intent = classifyMediaPayload({
|
|
306
|
+
files,
|
|
307
|
+
uriList: safeGet(cd, 'text/uri-list'),
|
|
308
|
+
html: safeGet(cd, 'text/html'),
|
|
309
|
+
plain: safeGet(cd, 'text/plain'),
|
|
310
|
+
});
|
|
311
|
+
if (!intent) return; // plain-text paste — let the existing paste flow run
|
|
312
|
+
e.preventDefault();
|
|
313
|
+
// Paste has no pointer position — drop at the visible viewport centre.
|
|
314
|
+
const world = screenToWorld(window.innerWidth / 2, window.innerHeight / 2);
|
|
315
|
+
dispatchIntent(intent, world);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const dispatchIntent = (intent: MediaIntent, world: [number, number]) => {
|
|
319
|
+
if (intent.kind === 'image') callbacks.onImage(intent.file, world);
|
|
320
|
+
else if (isHttpUrl(intent.url)) callbacks.onLink(intent.url, intent.title, world);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
document.addEventListener('dragover', onDragOver);
|
|
324
|
+
document.addEventListener('dragleave', onDragLeave);
|
|
325
|
+
document.addEventListener('drop', onDrop);
|
|
326
|
+
document.addEventListener('paste', onPaste);
|
|
327
|
+
return () => {
|
|
328
|
+
document.removeEventListener('dragover', onDragOver);
|
|
329
|
+
document.removeEventListener('dragleave', onDragLeave);
|
|
330
|
+
document.removeEventListener('drop', onDrop);
|
|
331
|
+
document.removeEventListener('paste', onPaste);
|
|
332
|
+
body.classList.remove('dc-media-dragover');
|
|
333
|
+
};
|
|
334
|
+
}, [enabled, screenToWorld, callbacks]);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function safeGet(dt: DataTransfer, type: string): string {
|
|
338
|
+
try {
|
|
339
|
+
return dt.getData(type) ?? '';
|
|
340
|
+
} catch {
|
|
341
|
+
return '';
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-collab.tsx — client-side Yjs collab provider for canvas iframes
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-collab.tsx
|
|
4
4
|
* @purpose Mounts a single Y.Doc + Awareness per canvas iframe. Opens a
|
|
5
5
|
* WebSocket to `/_ws/collab/:slug`, speaks the y-websocket binary
|
|
6
6
|
* protocol, exposes hooks for the cursor overlay + Task 3 comments
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* <CollabProvider> never resolve these specifiers and pay zero bundle cost.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import * as decoding from 'lib0/decoding';
|
|
20
|
+
import * as encoding from 'lib0/encoding';
|
|
19
21
|
import {
|
|
20
|
-
type ReactNode,
|
|
21
22
|
createContext,
|
|
23
|
+
type ReactNode,
|
|
22
24
|
useCallback,
|
|
23
25
|
useContext,
|
|
24
26
|
useEffect,
|
|
@@ -26,9 +28,6 @@ import {
|
|
|
26
28
|
useRef,
|
|
27
29
|
useState,
|
|
28
30
|
} from 'react';
|
|
29
|
-
|
|
30
|
-
import * as decoding from 'lib0/decoding';
|
|
31
|
-
import * as encoding from 'lib0/encoding';
|
|
32
31
|
import { Awareness, applyAwarenessUpdate, encodeAwarenessUpdate } from 'y-protocols/awareness';
|
|
33
32
|
import { readSyncMessage, writeSyncStep1, writeUpdate } from 'y-protocols/sync';
|
|
34
33
|
import * as Y from 'yjs';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-cursor-modifiers.tsx — T28 (Wave 3)
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-cursor-modifiers.tsx
|
|
4
4
|
* @purpose Modifier-aware cursor state machine. Tracks Alt / Shift / Meta
|
|
5
5
|
* modifier state globally and reflects it as `data-mod-*`
|
|
6
6
|
* attributes on `.dc-canvas`. CSS rules consume those attrs to
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-keyboard-discipline.tsx — T29 (Wave 3)
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-keyboard-discipline.tsx
|
|
4
4
|
* @purpose Keyboard nudge + Cmd+A select-all-in-artboard. Bail when focus
|
|
5
5
|
* is inside an input / textarea / contenteditable so typing
|
|
6
6
|
* into the dev-server's own inputs (file tree filter, etc.)
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
import { useEffect } from 'react';
|
|
30
30
|
|
|
31
31
|
import { useArtboardsContext, useDragStateContext } from './canvas-lib.tsx';
|
|
32
|
+
import { scopedCdSelector, selectorIndex } from './dom-selection.ts';
|
|
32
33
|
import { isEditableTarget } from './input-router.tsx';
|
|
33
34
|
import { type Selection, useSelectionSet } from './use-selection-set.tsx';
|
|
34
35
|
|
|
@@ -85,10 +86,12 @@ export function useKeyboardDiscipline(): void {
|
|
|
85
86
|
for (const el of stamped) {
|
|
86
87
|
const cdId = el.getAttribute('data-cd-id');
|
|
87
88
|
if (!cdId) continue;
|
|
89
|
+
const kdSelector = scopedCdSelector(cdId, id);
|
|
88
90
|
hits.push({
|
|
89
91
|
id: cdId,
|
|
90
|
-
selector:
|
|
92
|
+
selector: kdSelector,
|
|
91
93
|
artboardId: id,
|
|
94
|
+
index: selectorIndex(document, kdSelector, el),
|
|
92
95
|
tag: el.tagName.toLowerCase(),
|
|
93
96
|
});
|
|
94
97
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-selection-set.tsx — Phase 4.1 multi-selection store
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-selection-set.tsx
|
|
4
4
|
* @purpose Multi-element selection state for canvas-shell. The canvas
|
|
5
5
|
* input router calls `replace()` / `add()` / `clear()`;
|
|
6
6
|
* the provider debounces and posts up to the dev-server shell
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
|
-
type ReactNode,
|
|
21
20
|
createContext,
|
|
21
|
+
type ReactNode,
|
|
22
22
|
useCallback,
|
|
23
23
|
useContext,
|
|
24
24
|
useEffect,
|
|
@@ -46,6 +46,14 @@ export interface Selection {
|
|
|
46
46
|
selector: string;
|
|
47
47
|
/** Artboard host (`data-dc-screen`) — for scoping multi-edits in future. */
|
|
48
48
|
artboardId?: string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Occurrence index of this element among `querySelectorAll(selector)`.
|
|
51
|
+
* data-cd-id is stamped per SOURCE element, so a component rendered N times
|
|
52
|
+
* (a list row, or a reusable used twice) yields N DOM nodes with the SAME
|
|
53
|
+
* id+artboard selector. The index disambiguates which instance — resolvers
|
|
54
|
+
* use `querySelectorAll(selector)[index]`. Absent/0 → first match.
|
|
55
|
+
*/
|
|
56
|
+
index?: number;
|
|
49
57
|
/** Snapshot fields filled by the router from `resolveHoverTarget`. */
|
|
50
58
|
tag?: string;
|
|
51
59
|
classes?: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-snap-guides.tsx — Phase 4.2 snap math + guide-line shapes
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-snap-guides.tsx
|
|
4
4
|
* @purpose Pure function `computeSnap` that returns a snapped (x, y) for a
|
|
5
5
|
* proposed artboard rect plus the guide lines that should render
|
|
6
6
|
* as visual feedback. No React state; the drag controller calls
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-tool-mode.tsx — Phase 4.1 tool-mode store
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-tool-mode.tsx
|
|
4
4
|
* @purpose Context + hook for the active canvas tool. Wired into
|
|
5
5
|
* DesignCanvas. Phase 5 will
|
|
6
6
|
* register additional tools (pen, circle, arrow, eraser) via
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
|
-
type ReactNode,
|
|
16
15
|
createContext,
|
|
16
|
+
type ReactNode,
|
|
17
17
|
useCallback,
|
|
18
18
|
useContext,
|
|
19
19
|
useEffect,
|
|
@@ -52,6 +52,8 @@ export const DEFAULT_TOOLS: readonly ToolDescriptor[] = Object.freeze([
|
|
|
52
52
|
{ id: 'hand', label: 'Hand', shortcut: 'H', cursor: TOOL_CURSORS.hand },
|
|
53
53
|
{ id: 'comment', label: 'Comment', shortcut: 'C', cursor: TOOL_CURSORS.comment },
|
|
54
54
|
{ id: 'pen', label: 'Pen', shortcut: 'B', cursor: TOOL_CURSORS.pen },
|
|
55
|
+
// Annotation polish (item 8) — highlighter sits next to the pen.
|
|
56
|
+
{ id: 'highlighter', label: 'Highlighter', shortcut: 'I', cursor: TOOL_CURSORS.highlighter },
|
|
55
57
|
// Phase 24 — one Shape tool replaces the separate Rect (R) + Ellipse (O)
|
|
56
58
|
// buttons; the primitive is chosen from the palette popover.
|
|
57
59
|
{ id: 'shape', label: 'Shape', shortcut: 'R', cursor: TOOL_CURSORS.shape },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file use-undo-stack.tsx — React Context + Provider for the undo stack
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/use-undo-stack.tsx
|
|
4
4
|
* @purpose Wraps the pure `undoReducer` (undo-stack.ts) in React state,
|
|
5
5
|
* owns the async runner that awaits side-effects before applying
|
|
6
6
|
* the next state transition, exposes the `lastLabel` HUD signal,
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import {
|
|
21
|
-
type ReactNode,
|
|
22
21
|
createContext,
|
|
22
|
+
type ReactNode,
|
|
23
23
|
useCallback,
|
|
24
24
|
useContext,
|
|
25
25
|
useEffect,
|
|
@@ -31,13 +31,13 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
type CommandRecord,
|
|
33
33
|
type CommandSinks,
|
|
34
|
-
type EditCommand,
|
|
35
|
-
type UndoStackState,
|
|
36
34
|
canRedo as canRedoOf,
|
|
37
35
|
canUndo as canUndoOf,
|
|
36
|
+
type EditCommand,
|
|
38
37
|
loadStackState,
|
|
39
38
|
rebuildCommand,
|
|
40
39
|
saveStackState,
|
|
40
|
+
type UndoStackState,
|
|
41
41
|
undoReducer,
|
|
42
42
|
} from './undo-stack.ts';
|
|
43
43
|
|
|
@@ -349,7 +349,6 @@ export function useUndoSinks(): UndoSinksValue {
|
|
|
349
349
|
return useContext(UndoSinksContext) ?? NOOP_SINKS;
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
export { UndoStackContext, UndoSinksContext };
|
|
353
|
-
|
|
354
352
|
// Re-export the EditCommand type for ergonomic single-import in consumers.
|
|
355
353
|
export type { CommandRecord, EditCommand } from './undo-stack.ts';
|
|
354
|
+
export { UndoSinksContext, UndoStackContext };
|