@1agh/maude 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -2
- package/apps/studio/annotations-align.ts +149 -0
- package/apps/studio/annotations-bindings.ts +197 -0
- package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +681 -151
- package/apps/studio/annotations-groups.ts +270 -0
- package/apps/studio/annotations-layer.tsx +4537 -0
- package/apps/studio/annotations-model.ts +2077 -0
- package/apps/studio/annotations-snap.ts +125 -0
- package/{plugins/design/dev-server → apps/studio}/api.ts +616 -70
- package/{plugins/design/dev-server → apps/studio}/artboard-activity-overlay.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/artboard-marquee.tsx +1 -1
- package/apps/studio/bin/_motion-sample-playwright.mjs +129 -0
- package/{plugins/design/dev-server → apps/studio}/bin/_png-playwright.mjs +1 -1
- package/apps/studio/bin/annotate.mjs +732 -0
- package/apps/studio/bin/annotate.sh +17 -0
- package/{plugins/design/dev-server → apps/studio}/bin/canvas-edit.sh +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/draw-proof.sh +37 -2
- package/{plugins/design/dev-server → apps/studio}/bin/preflight.sh +1 -1
- package/apps/studio/bin/read-annotations.mjs +573 -0
- package/apps/studio/bin/read-annotations.sh +11 -0
- package/{plugins/design/dev-server → apps/studio}/bin/runtime-health.sh +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
- package/{plugins/design/dev-server → apps/studio}/bin/server-up.sh +9 -3
- package/apps/studio/bin/to-lottie-verify.html +68 -0
- package/apps/studio/bin/to-lottie.sh +128 -0
- package/{plugins/design/dev-server → apps/studio}/build.ts +6 -4
- package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +79 -10
- package/{plugins/design/dev-server → apps/studio}/canvas-comment-mount.tsx +1 -1
- package/apps/studio/canvas-create.ts +104 -0
- package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +48 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-edit.ts +257 -7
- package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +165 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib-inline.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib-resolver.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib.tsx +31 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +360 -48
- package/apps/studio/client/app.jsx +6742 -0
- package/apps/studio/client/canvas-url.js +81 -0
- package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
- package/apps/studio/client/index.html +16 -0
- package/apps/studio/client/styles/1-tokens-maude.css +296 -0
- package/apps/studio/client/styles/3-shell-maude.css +1161 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/3-shell.css +129 -0
- package/apps/studio/client/styles/4-components-maude.css +74 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/4-components.css +396 -34
- package/apps/studio/client/styles/5-maude-overrides.css +137 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/_index.css +9 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles.css +1 -1
- package/apps/studio/client/tour/overlay.jsx +279 -0
- package/apps/studio/client/tour/usage-tour.js +48 -0
- package/apps/studio/client/whats-new-seen.js +52 -0
- package/apps/studio/client/whats-new.jsx +250 -0
- package/{plugins/design/dev-server → apps/studio}/commands/annotation-strokes-command.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/commands/equal-spacing-command.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/commands/move-artboards-command.ts +2 -2
- package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +32 -11
- package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +5 -4
- package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +18 -7
- package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +21 -21
- package/apps/studio/dist/client.bundle.js +25545 -0
- package/apps/studio/dist/comment-mount.js +2046 -0
- package/apps/studio/dist/styles.css +8018 -0
- package/apps/studio/dom-selection.ts +342 -0
- package/apps/studio/draw/animate.ts +333 -0
- package/{plugins/design/dev-server → apps/studio}/draw/brush.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/composition.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/geometry.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/index.ts +4 -1
- package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +1 -1
- package/apps/studio/draw/morph.ts +204 -0
- package/{plugins/design/dev-server → apps/studio}/draw/optimize.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/palette.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/primitives.ts +1 -1
- package/apps/studio/draw/serialize-animate.ts +324 -0
- package/{plugins/design/dev-server → apps/studio}/draw/serialize.ts +9 -3
- package/apps/studio/draw/test/animate.test.ts +172 -0
- package/apps/studio/draw/test/morph.test.ts +107 -0
- package/apps/studio/draw/test/serialize-animate.test.ts +142 -0
- package/{plugins/design/dev-server → apps/studio}/equal-spacing-detector.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/equal-spacing-handles.tsx +7 -29
- package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +16 -4
- package/{plugins/design/dev-server → apps/studio}/history.ts +20 -3
- package/{plugins/design/dev-server → apps/studio}/http.ts +226 -6
- package/{plugins/design/dev-server → apps/studio}/input-router.tsx +15 -1
- package/{plugins/design/dev-server → apps/studio}/inspect.ts +50 -4
- package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +2 -8
- package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
- package/{plugins/design/dev-server → apps/studio}/server.ts +23 -2
- package/{plugins/design/dev-server → apps/studio}/sync/agent.ts +231 -57
- package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +45 -0
- package/apps/studio/sync/cold-start.ts +158 -0
- package/{plugins/design/dev-server → apps/studio}/sync/connection-state.ts +58 -2
- package/{plugins/design/dev-server → apps/studio}/sync/index.ts +563 -221
- package/apps/studio/sync/journal.ts +190 -0
- package/apps/studio/sync/migrate-seed.ts +321 -0
- package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +22 -3
- package/{plugins/design/dev-server → apps/studio}/sync/status.ts +15 -4
- package/{plugins/design/dev-server → apps/studio}/test/activity.test.ts +1 -7
- package/apps/studio/test/annotate-write.test.ts +184 -0
- package/apps/studio/test/annotations-align.test.ts +88 -0
- package/apps/studio/test/annotations-bindings.test.ts +124 -0
- package/apps/studio/test/annotations-groups.test.ts +231 -0
- package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +63 -0
- package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +97 -0
- package/apps/studio/test/annotations-snap.test.ts +79 -0
- package/apps/studio/test/annotations-text-format.test.ts +396 -0
- package/apps/studio/test/asset-api.test.ts +149 -0
- package/apps/studio/test/canvas-create-api.test.ts +361 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +2 -0
- package/apps/studio/test/canvas-edit.test.ts +319 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +1 -1
- package/apps/studio/test/canvas-media-drop.test.ts +158 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +25 -1
- package/apps/studio/test/canvas-url.test.ts +99 -0
- package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +0 -2
- package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +2 -7
- package/{plugins/design/dev-server → apps/studio}/test/csp-canvas-shell.test.ts +5 -0
- package/apps/studio/test/csrf-write-guard.test.ts +52 -0
- package/apps/studio/test/figjam-v3-model.test.ts +342 -0
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +1 -0
- package/apps/studio/test/index-data-canvas-ds.test.ts +97 -0
- package/apps/studio/test/motion-sample.test.ts +35 -0
- package/{plugins/design/dev-server → apps/studio}/test/perf-harness.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/phase-3.6-smoke.test.ts +3 -1
- package/apps/studio/test/read-annotations.test.ts +409 -0
- package/{plugins/design/dev-server → apps/studio}/test/sanitize-annotation-svg.test.ts +108 -2
- package/{plugins/design/dev-server → apps/studio}/test/shared-doc-convergence.test.ts +1 -1
- package/apps/studio/test/shared-doc-migrate.test.ts +369 -0
- package/apps/studio/test/sync-agent.test.ts +575 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-codec.test.ts +65 -0
- package/apps/studio/test/sync-cold-start.test.ts +244 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +68 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +0 -8
- package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +0 -1
- package/apps/studio/test/sync-incident-replay.test.ts +211 -0
- package/apps/studio/test/sync-journal.test.ts +176 -0
- package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +360 -5
- package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +66 -0
- package/apps/studio/test/tour-overlay.test.tsx +56 -0
- package/apps/studio/test/use-annotation-resize.test.ts +402 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-artboard-drag.test.ts +0 -1
- package/{plugins/design/dev-server → apps/studio}/test/use-collab.test.ts +0 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +8 -2
- package/apps/studio/test/whats-new.test.ts +101 -0
- package/apps/studio/text-imports.d.ts +9 -0
- package/{plugins/design/dev-server → apps/studio}/tool-palette.tsx +15 -70
- package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -2
- package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +5 -3
- package/apps/studio/use-annotation-resize.tsx +912 -0
- package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +17 -5
- package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +3 -3
- package/apps/studio/use-canvas-media-drop.tsx +343 -0
- package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +21 -16
- package/{plugins/design/dev-server → apps/studio}/use-cursor-modifiers.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-keyboard-discipline.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/use-selection-set.tsx +18 -1
- package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +8 -2
- package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +1 -1
- package/apps/studio/whats-new.json +264 -0
- package/apps/studio/whats-new.schema.json +86 -0
- package/apps/studio/whats-new.ts +87 -0
- package/cli/bin/maude.mjs +10 -0
- package/cli/commands/design-link.test.mjs +84 -0
- package/cli/commands/design.mjs +16 -7
- package/cli/commands/design.test.mjs +40 -0
- package/cli/commands/help.mjs +8 -2
- package/cli/commands/hub.mjs +3 -3
- package/cli/commands/hub.test.mjs +1 -1
- package/cli/commands/scenario-report.mjs +2 -9
- package/cli/lib/design-link.mjs +51 -1
- package/cli/lib/flow-design-integration.test.mjs +1 -1
- package/cli/lib/gitignore-block.mjs +1 -0
- package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
- package/cli/lib/preflight.mjs +1 -1
- package/package.json +15 -15
- package/plugins/design/templates/_shell.html +28 -4
- package/plugins/design/templates/brief-board.tsx.template +61 -0
- package/plugins/design/templates/canvas.tsx.template +1 -1
- package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -1
- package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +1 -1
- package/plugins/flow/.claude-plugin/config.schema.json +16 -0
- package/plugins/design/dev-server/annotations-layer.tsx +0 -3402
- package/plugins/design/dev-server/client/app.jsx +0 -2985
- package/plugins/design/dev-server/client/index.html +0 -15
- package/plugins/design/dev-server/dist/client.bundle.js +0 -20692
- package/plugins/design/dev-server/dist/comment-mount.js +0 -1924
- package/plugins/design/dev-server/dist/styles.css +0 -2710
- package/plugins/design/dev-server/dom-selection.ts +0 -156
- package/plugins/design/dev-server/sync/migrate-seed.ts +0 -163
- package/plugins/design/dev-server/test/canvas-edit.test.ts +0 -139
- package/plugins/design/dev-server/test/shared-doc-migrate.test.ts +0 -159
- package/plugins/design/dev-server/test/sync-agent.test.ts +0 -305
- package/plugins/design/dev-server/test/use-annotation-resize.test.ts +0 -200
- package/plugins/design/dev-server/use-annotation-resize.tsx +0 -449
- /package/{plugins/design/dev-server → apps/studio}/activity.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_enumerate-artboards-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_html-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pdf-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pptx-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pw-launch.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_screenshot-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_svg-optimize.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_svg-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/asset-sweep.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/bootstrap-check.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/check-runtime-bundles.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/draw-build.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/handoff.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/prep.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/scenario-report.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/slug.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/smoke.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/svg-optimize.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/visual-sanity.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/boot-self-heal.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-build.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-header.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-meta.schema.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-pipeline.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/hmr.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/iframe-lazy.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/0-reset.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/1-tokens.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/2-layout.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/5-utilities.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/ai-activity.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/awareness-bridge.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/index.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/protocol.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/registry.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/room.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/config.schema.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/context.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/.min-sizes.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_decoding.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_encoding.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion_react.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/pixi-js.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom_client.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-dev-runtime.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-runtime.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_awareness.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_sync.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/yjs.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/brush.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/composition.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/geometry.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/optimize.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/palette.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/primitives.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/serialize.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/examples/README.md +0 -0
- /package/{plugins/design/dev-server → apps/studio}/examples/perf-100-artboards.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/_browser-bundles.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/canva-handoff-prompt.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/canva.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/html.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/zip.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/fs-watch.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/handoff.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/hmr-broadcast.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/locator.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/mem.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/runtime-bundle.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/server.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/echo-guard.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/fs-mirror.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/hubs-config.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/untrusted.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/_helpers.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/active-state.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotation-strokes-command.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/artboard-activity-overlay.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/binary-smoke.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/bundle-smoke.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-pipeline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-route.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-awareness-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/comment-mount.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/context-resolve-tokens.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/equal-spacing-detector.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/endpoint.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/html.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pdf.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/png.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx-deck.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/svg.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-20-annotations.svg +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-21-annotations.svg +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fs-watch.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/handoff.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/history-rollback.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/hmr-classify.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/input-router.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/marquee-overlay.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/move-artboards-command.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/participants-chrome.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/paths.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle-error-mapping.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/server-lifecycle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-foundation.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/system-endpoint.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/tool-palette.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/undo-stack.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-annotation-selection.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-cursor-modifiers.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-keyboard-discipline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-selection-set.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/ws-handshake.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/tsconfig.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/ws.ts +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// whats-new.jsx — Maude UI "What's New" surfaces.
|
|
2
|
+
//
|
|
3
|
+
// useWhatsNew(version) → headless handle (fetch + seen-state + open-state)
|
|
4
|
+
// <WhatsNewBadge> → menubar entry point (✦ + unseen-count dot)
|
|
5
|
+
// <WhatsNewToast> → first-run notice (bottom-left, mirrors ai-banner.tsx)
|
|
6
|
+
// <WhatsNewPanel> → list of entries (reuses the help-modal backdrop)
|
|
7
|
+
//
|
|
8
|
+
// Styling lives in client/styles/4-components.css (.mdcc-wn-*). Spotlight tours
|
|
9
|
+
// (entry.tour[]) land in Phase 3 — the panel/toast leave a hook for them.
|
|
10
|
+
|
|
11
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
12
|
+
import { bestSeenVersion, computeUnseen } from './whats-new-seen.js';
|
|
13
|
+
|
|
14
|
+
const WN_SEEN = 'mdcc-whatsnew-seen';
|
|
15
|
+
const WN_TOAST = 'mdcc-whatsnew-toast-dismissed';
|
|
16
|
+
|
|
17
|
+
// learnMore renders as <a href> in the privileged main origin. Only bind an
|
|
18
|
+
// http(s) URL — belt-and-suspenders next to React's built-in URL sanitizer and
|
|
19
|
+
// the schema's pattern (security review, defense-in-depth).
|
|
20
|
+
function isSafeHref(url) {
|
|
21
|
+
return typeof url === 'string' && /^https?:\/\//i.test(url);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readSeen(currentVersion) {
|
|
25
|
+
try {
|
|
26
|
+
const v = localStorage.getItem(WN_SEEN);
|
|
27
|
+
if (v) return v;
|
|
28
|
+
// First ever run: acknowledge the current version silently so a brand-new
|
|
29
|
+
// user isn't greeted with the entire backlog as "new". Only true upgrades
|
|
30
|
+
// (and pending dev entries) surface after this.
|
|
31
|
+
localStorage.setItem(WN_SEEN, currentVersion);
|
|
32
|
+
} catch {}
|
|
33
|
+
return currentVersion;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readToastDismissed() {
|
|
37
|
+
try {
|
|
38
|
+
return localStorage.getItem(WN_TOAST);
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function useWhatsNew(currentVersion) {
|
|
45
|
+
const [entries, setEntries] = useState([]);
|
|
46
|
+
const [feedVersion, setFeedVersion] = useState(currentVersion);
|
|
47
|
+
const [seenVersion, setSeenVersion] = useState(() => readSeen(currentVersion));
|
|
48
|
+
const [toastDismissed, setToastDismissed] = useState(() => readToastDismissed());
|
|
49
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
let cancelled = false;
|
|
53
|
+
fetch('/_api/whats-new')
|
|
54
|
+
.then((r) => r.json())
|
|
55
|
+
.then((j) => {
|
|
56
|
+
if (cancelled) return;
|
|
57
|
+
setEntries(Array.isArray(j?.entries) ? j.entries : []);
|
|
58
|
+
if (typeof j?.version === 'string') setFeedVersion(j.version);
|
|
59
|
+
})
|
|
60
|
+
.catch(() => {
|
|
61
|
+
/* offline / restart — no banner */
|
|
62
|
+
});
|
|
63
|
+
return () => {
|
|
64
|
+
cancelled = true;
|
|
65
|
+
};
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const unseen = computeUnseen(entries, seenVersion);
|
|
69
|
+
const showToast = unseen.length > 0 && toastDismissed !== feedVersion && !panelOpen;
|
|
70
|
+
const toastEntry = unseen[0] ?? null;
|
|
71
|
+
|
|
72
|
+
const markAllSeen = useCallback(() => {
|
|
73
|
+
const next = bestSeenVersion(entries, feedVersion);
|
|
74
|
+
setSeenVersion(next);
|
|
75
|
+
setToastDismissed(feedVersion);
|
|
76
|
+
try {
|
|
77
|
+
localStorage.setItem(WN_SEEN, next);
|
|
78
|
+
localStorage.setItem(WN_TOAST, feedVersion);
|
|
79
|
+
} catch {}
|
|
80
|
+
}, [entries, feedVersion]);
|
|
81
|
+
|
|
82
|
+
const dismissToast = useCallback(() => {
|
|
83
|
+
setToastDismissed(feedVersion);
|
|
84
|
+
try {
|
|
85
|
+
localStorage.setItem(WN_TOAST, feedVersion);
|
|
86
|
+
} catch {}
|
|
87
|
+
}, [feedVersion]);
|
|
88
|
+
|
|
89
|
+
const openPanel = useCallback(() => setPanelOpen(true), []);
|
|
90
|
+
const closePanel = useCallback(() => {
|
|
91
|
+
setPanelOpen(false);
|
|
92
|
+
markAllSeen();
|
|
93
|
+
}, [markAllSeen]);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
entries,
|
|
97
|
+
unseen,
|
|
98
|
+
feedVersion,
|
|
99
|
+
panelOpen,
|
|
100
|
+
openPanel,
|
|
101
|
+
closePanel,
|
|
102
|
+
showToast,
|
|
103
|
+
toastEntry,
|
|
104
|
+
dismissToast,
|
|
105
|
+
markAllSeen,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function WhatsNewBadge({ count = 0, onOpen }) {
|
|
110
|
+
return (
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
className="mdcc-wn-badge"
|
|
114
|
+
data-tour="whatsnew"
|
|
115
|
+
onClick={onOpen}
|
|
116
|
+
title="What's new"
|
|
117
|
+
aria-label={count > 0 ? `What's new — ${count} new` : "What's new"}
|
|
118
|
+
>
|
|
119
|
+
<span aria-hidden="true">✦</span>
|
|
120
|
+
<span className="mdcc-wn-badge__label">New</span>
|
|
121
|
+
{count > 0 && (
|
|
122
|
+
<span className="mdcc-wn-badge__dot" aria-hidden="true">
|
|
123
|
+
{count}
|
|
124
|
+
</span>
|
|
125
|
+
)}
|
|
126
|
+
</button>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function WhatsNewToast({ wn }) {
|
|
131
|
+
if (!wn.showToast || !wn.toastEntry) return null;
|
|
132
|
+
const e = wn.toastEntry;
|
|
133
|
+
const total = wn.unseen.length;
|
|
134
|
+
// Plan C P6 — restyled to the maude `.st-toast` family (was `mdcc-wn-toast`),
|
|
135
|
+
// matching `.design/ui/Studio.tsx` AB-D. Behavior unchanged.
|
|
136
|
+
return (
|
|
137
|
+
<div className="st-toast" role="status" aria-live="polite">
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
className="st-toast-close"
|
|
141
|
+
aria-label="Dismiss"
|
|
142
|
+
onClick={wn.dismissToast}
|
|
143
|
+
>
|
|
144
|
+
×
|
|
145
|
+
</button>
|
|
146
|
+
<div className="st-toast-hd">
|
|
147
|
+
<span aria-hidden="true">✦</span>
|
|
148
|
+
What's new{e.version ? ` · v${e.version}` : ''}
|
|
149
|
+
</div>
|
|
150
|
+
<div className="st-toast-title">{e.title}</div>
|
|
151
|
+
<div className="st-toast-txt">{e.summary}</div>
|
|
152
|
+
<div className="st-toast-actions">
|
|
153
|
+
<button type="button" className="btn btn--primary btn--sm" onClick={wn.openPanel}>
|
|
154
|
+
{total > 1 ? `See all (${total})` : 'Details'}
|
|
155
|
+
</button>
|
|
156
|
+
<button type="button" className="btn btn--ghost btn--sm" onClick={wn.dismissToast}>
|
|
157
|
+
Dismiss
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function WhatsNewPanel({ wn, onStartTour }) {
|
|
165
|
+
const { panelOpen, closePanel } = wn;
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (!panelOpen) return;
|
|
168
|
+
function onKey(e) {
|
|
169
|
+
if (e.key === 'Escape') closePanel();
|
|
170
|
+
}
|
|
171
|
+
window.addEventListener('keydown', onKey);
|
|
172
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
173
|
+
}, [panelOpen, closePanel]);
|
|
174
|
+
|
|
175
|
+
if (!panelOpen) return null;
|
|
176
|
+
const unseenIds = new Set(wn.unseen.map((e) => e.id));
|
|
177
|
+
return (
|
|
178
|
+
<div
|
|
179
|
+
className="help-modal-backdrop"
|
|
180
|
+
role="presentation"
|
|
181
|
+
onMouseDown={(e) => {
|
|
182
|
+
if (e.target === e.currentTarget) closePanel();
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
<div
|
|
186
|
+
className="mdcc-wn-panel"
|
|
187
|
+
role="dialog"
|
|
188
|
+
aria-modal="true"
|
|
189
|
+
aria-labelledby="mdcc-wn-title"
|
|
190
|
+
>
|
|
191
|
+
<header className="help-modal-hd">
|
|
192
|
+
<span className="title" id="mdcc-wn-title">
|
|
193
|
+
✦ What’s new in maude
|
|
194
|
+
</span>
|
|
195
|
+
<button
|
|
196
|
+
type="button"
|
|
197
|
+
className="help-modal-close"
|
|
198
|
+
aria-label="Close (Esc)"
|
|
199
|
+
onClick={closePanel}
|
|
200
|
+
>
|
|
201
|
+
×
|
|
202
|
+
</button>
|
|
203
|
+
</header>
|
|
204
|
+
<div className="mdcc-wn-panel__body">
|
|
205
|
+
{wn.entries.length === 0 ? (
|
|
206
|
+
<p className="mdcc-wn-empty">Nothing new yet.</p>
|
|
207
|
+
) : (
|
|
208
|
+
<ul className="mdcc-wn-list">
|
|
209
|
+
{wn.entries.map((e) => (
|
|
210
|
+
<li
|
|
211
|
+
key={e.id}
|
|
212
|
+
className={'mdcc-wn-item' + (unseenIds.has(e.id) ? ' is-unseen' : '')}
|
|
213
|
+
>
|
|
214
|
+
<div className="mdcc-wn-item__hd">
|
|
215
|
+
<span className={'mdcc-wn-kind mdcc-wn-kind--' + e.kind}>{e.kind}</span>
|
|
216
|
+
<span className="mdcc-wn-item__title">{e.title}</span>
|
|
217
|
+
<span className="mdcc-wn-item__ver">{e.version ? `v${e.version}` : 'next'}</span>
|
|
218
|
+
</div>
|
|
219
|
+
<p className="mdcc-wn-item__summary">{e.summary}</p>
|
|
220
|
+
{isSafeHref(e.learnMore) && (
|
|
221
|
+
<a
|
|
222
|
+
className="mdcc-wn-item__more"
|
|
223
|
+
href={e.learnMore}
|
|
224
|
+
target="_blank"
|
|
225
|
+
rel="noreferrer"
|
|
226
|
+
>
|
|
227
|
+
Learn more →
|
|
228
|
+
</a>
|
|
229
|
+
)}
|
|
230
|
+
{onStartTour && Array.isArray(e.tour) && e.tour.length > 0 && (
|
|
231
|
+
<button
|
|
232
|
+
type="button"
|
|
233
|
+
className="mdcc-wn-item__tour"
|
|
234
|
+
onClick={() => {
|
|
235
|
+
closePanel();
|
|
236
|
+
onStartTour(e.tour);
|
|
237
|
+
}}
|
|
238
|
+
>
|
|
239
|
+
▶ Take tour
|
|
240
|
+
</button>
|
|
241
|
+
)}
|
|
242
|
+
</li>
|
|
243
|
+
))}
|
|
244
|
+
</ul>
|
|
245
|
+
)}
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file commands/annotation-strokes-command.ts — undo entry for annotation
|
|
3
3
|
* stroke add / erase / batch-translate.
|
|
4
|
-
* @scope
|
|
4
|
+
* @scope apps/studio/commands/annotation-strokes-command.ts
|
|
5
5
|
* @purpose Records a `Stroke[]` pair (before/after) and routes both
|
|
6
6
|
* directions through a single `putFn`. The server endpoint is
|
|
7
7
|
* `PUT /_api/annotations` which replaces the entire SVG; we
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import type { Stroke } from '../annotations-layer.tsx';
|
|
23
|
-
import type { CommandRecord,
|
|
23
|
+
import type { CommandRecord, EditCommand } from '../undo-stack.ts';
|
|
24
24
|
import { registerCommand } from '../undo-stack.ts';
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file commands/equal-spacing-command.ts — undo label helpers for
|
|
3
3
|
* equal-spacing / align gestures.
|
|
4
|
-
* @scope
|
|
4
|
+
* @scope apps/studio/commands/equal-spacing-command.ts
|
|
5
5
|
* @purpose Distribute + align both call `dragBus.commitPositions(moved)`,
|
|
6
6
|
* which wraps the move in a generic MoveArtboardsCommand. Per
|
|
7
7
|
* DDR-050 the underlying command type is shared (the inverse
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file commands/move-artboards-command.ts — undo entry for artboard moves
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/commands/move-artboards-command.ts
|
|
4
4
|
* @purpose Reversible record of an artboard-layout PATCH. Pairs the full
|
|
5
5
|
* `before` and `after` layout snapshots with an injected
|
|
6
6
|
* `patchFn` (in production, `applyArtboardLayout`; in tests, a
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* stale layout.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import type { CommandRecord,
|
|
20
|
+
import type { CommandRecord, EditCommand } from '../undo-stack.ts';
|
|
21
21
|
import { registerCommand } from '../undo-stack.ts';
|
|
22
22
|
|
|
23
23
|
export interface ArtboardLayoutEntry {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file comments-overlay.tsx — FigJam-style in-place comments overlay
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/comments-overlay.tsx
|
|
4
4
|
* @purpose Renders DS-styled comment pins (Phase 6 Task 2), the in-place
|
|
5
5
|
* composer bubble (Task 3), and the thread popover (Task 4)
|
|
6
6
|
* inside the canvas iframe. Sibling to `annotations-layer` —
|
|
@@ -81,6 +81,9 @@ export interface OverlayComment {
|
|
|
81
81
|
id: string;
|
|
82
82
|
file: string;
|
|
83
83
|
selector: string;
|
|
84
|
+
/** Occurrence index among `querySelectorAll(selector)` — disambiguates a
|
|
85
|
+
* component repeated within one artboard. Absent (old comments) → first. */
|
|
86
|
+
index?: number;
|
|
84
87
|
bounds: OverlayBounds | null;
|
|
85
88
|
text: string;
|
|
86
89
|
status: 'open' | 'resolved';
|
|
@@ -138,7 +141,10 @@ function deriveFile(): string | null {
|
|
|
138
141
|
// halo chrome at z-index 5) instead of being portaled into `.dc-world` where
|
|
139
142
|
// it would lose the stacking battle.
|
|
140
143
|
|
|
141
|
-
function screenRectFor(
|
|
144
|
+
function screenRectFor(
|
|
145
|
+
selector: string,
|
|
146
|
+
index?: number
|
|
147
|
+
): {
|
|
142
148
|
x: number;
|
|
143
149
|
y: number;
|
|
144
150
|
w: number;
|
|
@@ -147,11 +153,15 @@ function screenRectFor(selector: string): {
|
|
|
147
153
|
if (!selector) return null;
|
|
148
154
|
let el: HTMLElement | null = null;
|
|
149
155
|
try {
|
|
150
|
-
|
|
156
|
+
// index disambiguates a component repeated within one artboard (querySelector
|
|
157
|
+
// alone would always grab the first match). Absent/0 → first.
|
|
158
|
+
const all = document.querySelectorAll(selector);
|
|
159
|
+
const i = index && index > 0 && index < all.length ? index : 0;
|
|
160
|
+
el = (all[i] ?? all[0] ?? null) as HTMLElement | null;
|
|
151
161
|
} catch {
|
|
152
162
|
return null;
|
|
153
163
|
}
|
|
154
|
-
if (!el
|
|
164
|
+
if (!el?.isConnected) return null;
|
|
155
165
|
const r = el.getBoundingClientRect();
|
|
156
166
|
if (r.width === 0 && r.height === 0) return null;
|
|
157
167
|
return { x: r.left, y: r.top, w: r.width, h: r.height };
|
|
@@ -194,7 +204,7 @@ export function CommentsOverlay(): React.ReactNode {
|
|
|
194
204
|
const mirrorSelection = useCallback(
|
|
195
205
|
(comment: OverlayComment | undefined) => {
|
|
196
206
|
if (!selSet) return;
|
|
197
|
-
if (!comment
|
|
207
|
+
if (!comment?.selector) {
|
|
198
208
|
selSet.clear();
|
|
199
209
|
return;
|
|
200
210
|
}
|
|
@@ -288,7 +298,7 @@ export function CommentsOverlay(): React.ReactNode {
|
|
|
288
298
|
const detail = (
|
|
289
299
|
e as CustomEvent<{ selection?: ComposeSelection; clientX?: number; clientY?: number }>
|
|
290
300
|
).detail;
|
|
291
|
-
if (!detail
|
|
301
|
+
if (!detail?.selection) return;
|
|
292
302
|
setComposer({
|
|
293
303
|
selection: detail.selection,
|
|
294
304
|
clientX: typeof detail.clientX === 'number' ? detail.clientX : 0,
|
|
@@ -316,6 +326,7 @@ export function CommentsOverlay(): React.ReactNode {
|
|
|
316
326
|
const payload = {
|
|
317
327
|
file: sel.file,
|
|
318
328
|
selector: sel.selector,
|
|
329
|
+
index: sel.index,
|
|
319
330
|
dom_path: sel.dom_path,
|
|
320
331
|
tag: sel.tag,
|
|
321
332
|
classes: sel.classes,
|
|
@@ -372,7 +383,9 @@ export function CommentsOverlay(): React.ReactNode {
|
|
|
372
383
|
const indexById = useMemo(() => {
|
|
373
384
|
const m = new Map<string, number>();
|
|
374
385
|
const all = comments.slice().sort((a, b) => a.created.localeCompare(b.created));
|
|
375
|
-
all.forEach((c, i) =>
|
|
386
|
+
all.forEach((c, i) => {
|
|
387
|
+
m.set(c.id, i + 1);
|
|
388
|
+
});
|
|
376
389
|
return m;
|
|
377
390
|
}, [comments]);
|
|
378
391
|
|
|
@@ -453,6 +466,7 @@ export function CommentsOverlay(): React.ReactNode {
|
|
|
453
466
|
return (
|
|
454
467
|
<CommentThread
|
|
455
468
|
comment={focused}
|
|
469
|
+
sequence={indexById.get(focused.id) ?? 0}
|
|
456
470
|
onClose={() => {
|
|
457
471
|
setFocusedId(null);
|
|
458
472
|
// Drop the canvas halo when the thread closes — symmetric with
|
|
@@ -745,7 +759,7 @@ function CommentPin({
|
|
|
745
759
|
// Live screen-coord lookup mirrors SelectionHalos in canvas-shell.tsx.
|
|
746
760
|
// Falls back to stored bounds (a screen-coord capture at create time)
|
|
747
761
|
// when the target element is gone — better than vanishing entirely.
|
|
748
|
-
let pos = screenRectFor(comment.selector);
|
|
762
|
+
let pos = screenRectFor(comment.selector, comment.index);
|
|
749
763
|
if (!pos && comment.bounds) {
|
|
750
764
|
pos = {
|
|
751
765
|
x: comment.bounds.x,
|
|
@@ -772,7 +786,7 @@ function CommentPin({
|
|
|
772
786
|
return () => {
|
|
773
787
|
if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
|
|
774
788
|
};
|
|
775
|
-
}, [comment.selector, comment.bounds]);
|
|
789
|
+
}, [comment.selector, comment.bounds, comment.index]);
|
|
776
790
|
|
|
777
791
|
const author = comment.author?.trim() || 'unknown';
|
|
778
792
|
const label = `Comment ${sequence} by ${author}`;
|
|
@@ -925,12 +939,14 @@ function CommentComposer({
|
|
|
925
939
|
|
|
926
940
|
function CommentThread({
|
|
927
941
|
comment,
|
|
942
|
+
sequence,
|
|
928
943
|
onClose,
|
|
929
944
|
onPatch,
|
|
930
945
|
onDelete,
|
|
931
946
|
onReply,
|
|
932
947
|
}: {
|
|
933
948
|
comment: OverlayComment;
|
|
949
|
+
sequence: number;
|
|
934
950
|
onClose: () => void;
|
|
935
951
|
onPatch: (patch: Record<string, unknown>) => void;
|
|
936
952
|
onDelete: () => void;
|
|
@@ -1027,6 +1043,11 @@ function CommentThread({
|
|
|
1027
1043
|
>
|
|
1028
1044
|
<div className="cm-thread__head" id={headId}>
|
|
1029
1045
|
<div className="cm-thread__head-row">
|
|
1046
|
+
{/* Plan C P18 — pin/sequence badge in the popover header (parity with
|
|
1047
|
+
`.design/ui/Studio.tsx` thread popover). */}
|
|
1048
|
+
<span className="cm-thread__seq" aria-hidden="true">
|
|
1049
|
+
{sequence}
|
|
1050
|
+
</span>
|
|
1030
1051
|
<span className="cm-thread__author">{comment.author?.trim() || 'unknown'}</span>
|
|
1031
1052
|
<span className="cm-thread__time">{formatRelativeTime(comment.created)}</span>
|
|
1032
1053
|
<button
|
|
@@ -1159,7 +1180,7 @@ function computeThreadAnchor(comment: OverlayComment): { x: number; y: number }
|
|
|
1159
1180
|
// Resolve target's live screen rect; popover drops below the pin with small
|
|
1160
1181
|
// breathing room. Stored bounds (capture-time screen coords) are the
|
|
1161
1182
|
// last-resort fallback for orphaned pins.
|
|
1162
|
-
const rect = comment.selector ? screenRectFor(comment.selector) : null;
|
|
1183
|
+
const rect = comment.selector ? screenRectFor(comment.selector, comment.index) : null;
|
|
1163
1184
|
if (rect) {
|
|
1164
1185
|
// Pin sits at (rect.right - 12, rect.top - 12). Place popover at the same
|
|
1165
1186
|
// x for visual continuity, 16px below the top so it clears the pin.
|
|
@@ -1184,7 +1205,7 @@ function computeAnchor(state: ComposerState): { x: number; y: number } {
|
|
|
1184
1205
|
return { x: state.clientX, y: state.clientY + 8 };
|
|
1185
1206
|
}
|
|
1186
1207
|
if (state.selection.selector) {
|
|
1187
|
-
const rect = screenRectFor(state.selection.selector);
|
|
1208
|
+
const rect = screenRectFor(state.selection.selector, state.selection.index);
|
|
1188
1209
|
if (rect) {
|
|
1189
1210
|
return { x: rect.x, y: rect.y + rect.h + 8 };
|
|
1190
1211
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file context-menu.tsx — Phase 4.1 right-click context menu
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/context-menu.tsx
|
|
4
4
|
* @purpose Floating, context-aware right-click menu. The router calls
|
|
5
5
|
* `openContextMenu({ clientX, clientY, target })`; the menu
|
|
6
6
|
* resolves the section list via the ContextRegistry and renders
|
|
@@ -237,7 +237,9 @@ const MENU_CSS = `
|
|
|
237
237
|
.dc-context-menu .dc-menu-flyout.is-flip { left: auto; right: calc(100% + 3px); }
|
|
238
238
|
`.trim();
|
|
239
239
|
|
|
240
|
-
|
|
240
|
+
// Exported (FigJam v3) — the annotation layer's right-click menu reuses the
|
|
241
|
+
// same .dc-context-menu visual language without duplicating the stylesheet.
|
|
242
|
+
export function ensureMenuStyles(): void {
|
|
241
243
|
if (typeof document === 'undefined') return;
|
|
242
244
|
if (document.getElementById('dc-context-menu-css')) return;
|
|
243
245
|
const s = document.createElement('style');
|
|
@@ -342,7 +344,7 @@ function ContextMenuView({
|
|
|
342
344
|
el.querySelectorAll<HTMLButtonElement>('button.dc-menu-item:not([disabled])')
|
|
343
345
|
);
|
|
344
346
|
if (items.length === 0) return;
|
|
345
|
-
const idx = items.
|
|
347
|
+
const idx = items.indexOf(document.activeElement);
|
|
346
348
|
const nextIdx =
|
|
347
349
|
e.key === 'ArrowDown'
|
|
348
350
|
? (idx + 1) % items.length
|
|
@@ -368,7 +370,6 @@ function ContextMenuView({
|
|
|
368
370
|
{sections.map((section, si) => {
|
|
369
371
|
const sectionKey = section.map((i) => i.id).join('|') || `s${si}`;
|
|
370
372
|
return (
|
|
371
|
-
// biome-ignore lint/a11y/useSemanticElements: ARIA group within role="menu"; no native equivalent.
|
|
372
373
|
<div key={sectionKey} role="group">
|
|
373
374
|
{si > 0 ? <div className="dc-menu-sep" aria-hidden="true" /> : null}
|
|
374
375
|
{section.map((item) => (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file contextual-toolbar.tsx — T30 (Wave 3)
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/contextual-toolbar.tsx
|
|
4
4
|
* @purpose Selection-anchored floating chrome for ELEMENT selections
|
|
5
5
|
* (cd-id entries in the selection set). Mirrors the
|
|
6
6
|
* MultiArtboardToolbar pattern but scopes to user content inside
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { useEffect, useRef } from 'react';
|
|
32
32
|
|
|
33
|
+
import { resolveSelectionEl } from './dom-selection.ts';
|
|
33
34
|
import { useSelectionSet } from './use-selection-set.tsx';
|
|
34
35
|
|
|
35
36
|
const CTX_TOOLBAR_CSS = `
|
|
@@ -159,9 +160,7 @@ export function ContextualToolbar() {
|
|
|
159
160
|
let yMax = Number.NEGATIVE_INFINITY;
|
|
160
161
|
let any = false;
|
|
161
162
|
for (const sel of elementSelections) {
|
|
162
|
-
const node = sel
|
|
163
|
-
? document.querySelector(`[data-cd-id="${sel.id}"]`)
|
|
164
|
-
: document.querySelector(sel.selector);
|
|
163
|
+
const node = resolveSelectionEl(document, sel);
|
|
165
164
|
if (!node) continue;
|
|
166
165
|
const r = (node as HTMLElement).getBoundingClientRect();
|
|
167
166
|
if (r.width === 0 && r.height === 0) continue;
|
|
@@ -208,6 +207,20 @@ export function ContextualToolbar() {
|
|
|
208
207
|
return (
|
|
209
208
|
<div ref={ref} className="dc-elem-ctx-tb" role="toolbar" aria-label="Element actions">
|
|
210
209
|
<span className="dc-elem-ctx-count">{count === 1 ? '1 element' : `${count} elements`}</span>
|
|
210
|
+
<button
|
|
211
|
+
type="button"
|
|
212
|
+
title="Inspect — open the right panel (Inspect / Layers / CSS)"
|
|
213
|
+
onClick={() => {
|
|
214
|
+
// Phase 12 — open the shell's Inspector on the current selection.
|
|
215
|
+
try {
|
|
216
|
+
window.parent.postMessage({ dgn: 'open-inspector' }, '*');
|
|
217
|
+
} catch {
|
|
218
|
+
/* detached / cross-origin */
|
|
219
|
+
}
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
Inspect
|
|
223
|
+
</button>
|
|
211
224
|
<button
|
|
212
225
|
type="button"
|
|
213
226
|
title="Copy CSS selector"
|
|
@@ -228,9 +241,7 @@ export function ContextualToolbar() {
|
|
|
228
241
|
title="Add comment on this element"
|
|
229
242
|
onClick={() => {
|
|
230
243
|
if (!primary) return;
|
|
231
|
-
const node = primary
|
|
232
|
-
? document.querySelector(`[data-cd-id="${primary.id}"]`)
|
|
233
|
-
: document.querySelector(primary.selector);
|
|
244
|
+
const node = resolveSelectionEl(document, primary);
|
|
234
245
|
openComposerForSelection(primary, node);
|
|
235
246
|
}}
|
|
236
247
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file cursors-overlay.tsx — foreign peer cursors rendered on the canvas
|
|
3
|
-
* @scope
|
|
3
|
+
* @scope apps/studio/cursors-overlay.tsx
|
|
4
4
|
* @purpose Renders one colored SVG arrow + name label per foreign peer at
|
|
5
5
|
* their published world coords, transformed through the local
|
|
6
6
|
* viewport so the cursor anchors to canvas content (not to my
|
|
@@ -43,24 +43,28 @@ const CURSOR_CSS = `
|
|
|
43
43
|
}
|
|
44
44
|
.dc-cursor-arrow {
|
|
45
45
|
display: block;
|
|
46
|
-
filter: drop-shadow(0 1px
|
|
46
|
+
filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.4));
|
|
47
47
|
}
|
|
48
|
+
/* DS colors-presence .cur-label recipe — hue pill, mono 10px, dark
|
|
49
|
+
* accent-fg text (never white-on-hue, which washed out on light hues).
|
|
50
|
+
* NOTE: keep this comment backtick-free — it lives inside the CURSOR_CSS
|
|
51
|
+
* template literal and a stray backtick closes it. */
|
|
48
52
|
.dc-cursor-label {
|
|
49
53
|
position: absolute;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
/* specimen geometry: the pill hangs below-right of the 15px triangle */
|
|
55
|
+
top: 14px;
|
|
56
|
+
left: 10px;
|
|
57
|
+
padding: 1px 8px;
|
|
58
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
54
59
|
font-weight: 500;
|
|
55
|
-
font-size:
|
|
56
|
-
line-height: 1.
|
|
57
|
-
color: var(--maude-hud-accent-fg,
|
|
58
|
-
border-radius:
|
|
60
|
+
font-size: 10px;
|
|
61
|
+
line-height: 1.4;
|
|
62
|
+
color: var(--maude-hud-accent-fg, oklch(0.180 0.030 268));
|
|
63
|
+
border-radius: 999px;
|
|
59
64
|
white-space: nowrap;
|
|
60
65
|
max-width: 180px;
|
|
61
66
|
overflow: hidden;
|
|
62
67
|
text-overflow: ellipsis;
|
|
63
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
64
68
|
}
|
|
65
69
|
.dc-peer-selection {
|
|
66
70
|
position: absolute;
|
|
@@ -117,20 +121,16 @@ const Cursor = memo(function Cursor({ peer, viewport }: CursorProps): JSX.Elemen
|
|
|
117
121
|
const screenY = peer.cursor.y * viewport.zoom + viewport.y;
|
|
118
122
|
return (
|
|
119
123
|
<div className="dc-cursor" style={{ transform: `translate(${screenX}px, ${screenY}px)` }}>
|
|
124
|
+
{/* DS colors-presence Pointer — one plain triangle glyph, tinted by its
|
|
125
|
+
owner (the specimen's exact 24-grid path, no tail/notch). */}
|
|
120
126
|
<svg
|
|
121
127
|
className="dc-cursor-arrow"
|
|
122
|
-
width="
|
|
123
|
-
height="
|
|
124
|
-
viewBox="0 0
|
|
128
|
+
width="15"
|
|
129
|
+
height="15"
|
|
130
|
+
viewBox="0 0 24 24"
|
|
125
131
|
aria-hidden="true"
|
|
126
132
|
>
|
|
127
|
-
<path
|
|
128
|
-
d="M 2 2 L 2 18 L 6 14 L 9 21 L 12 20 L 9 13 L 14 13 Z"
|
|
129
|
-
fill={peer.color}
|
|
130
|
-
stroke="#fff"
|
|
131
|
-
strokeWidth="1.2"
|
|
132
|
-
strokeLinejoin="round"
|
|
133
|
-
/>
|
|
133
|
+
<path d="M4 3 L20 11.5 L12.5 13 L10 21 Z" fill={peer.color} />
|
|
134
134
|
</svg>
|
|
135
135
|
<div className="dc-cursor-label" style={{ background: peer.color }}>
|
|
136
136
|
{peer.name}
|