@1agh/maude 0.28.1 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +285 -5
- package/{plugins/design/dev-server → apps/studio}/annotations-layer.tsx +1206 -119
- package/{plugins/design/dev-server → apps/studio}/api.ts +486 -32
- package/{plugins/design/dev-server → apps/studio}/artboard-activity-overlay.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/artboard-marquee.tsx +1 -1
- package/apps/studio/bin/_motion-sample-playwright.mjs +129 -0
- package/{plugins/design/dev-server → apps/studio}/bin/canvas-edit.sh +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/draw-proof.sh +37 -2
- package/{plugins/design/dev-server → apps/studio}/bin/preflight.sh +1 -1
- package/apps/studio/bin/read-annotations.mjs +438 -0
- package/apps/studio/bin/read-annotations.sh +11 -0
- package/{plugins/design/dev-server → apps/studio}/bin/runtime-health.sh +1 -1
- package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
- package/{plugins/design/dev-server → apps/studio}/bin/server-up.sh +9 -3
- package/apps/studio/bin/to-lottie-verify.html +68 -0
- package/apps/studio/bin/to-lottie.sh +128 -0
- package/{plugins/design/dev-server → apps/studio}/build.ts +5 -3
- package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-comment-mount.tsx +1 -1
- package/apps/studio/canvas-create.ts +104 -0
- package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +46 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +60 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib-inline.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib-resolver.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/canvas-lib.tsx +31 -12
- package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +56 -41
- package/{plugins/design/dev-server → apps/studio}/client/app.jsx +1713 -530
- package/apps/studio/client/canvas-url.js +81 -0
- package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
- package/{plugins/design/dev-server → apps/studio}/client/index.html +2 -1
- package/apps/studio/client/styles/1-tokens-maude.css +296 -0
- package/apps/studio/client/styles/3-shell-maude.css +459 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/3-shell.css +129 -0
- package/apps/studio/client/styles/4-components-maude.css +74 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/4-components.css +357 -34
- package/apps/studio/client/styles/5-maude-overrides.css +123 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles/_index.css +9 -0
- package/{plugins/design/dev-server → apps/studio}/client/styles.css +1 -1
- package/apps/studio/client/tour/overlay.jsx +191 -0
- package/apps/studio/client/tour/usage-tour.js +35 -0
- package/apps/studio/client/whats-new-seen.js +52 -0
- package/apps/studio/client/whats-new.jsx +250 -0
- package/{plugins/design/dev-server → apps/studio}/commands/annotation-strokes-command.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/commands/equal-spacing-command.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/commands/move-artboards-command.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +25 -6
- package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +4 -7
- package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +1 -1
- package/apps/studio/dist/client.bundle.js +22825 -0
- package/apps/studio/dist/comment-mount.js +1960 -0
- package/apps/studio/dist/styles.css +5695 -0
- package/{plugins/design/dev-server → apps/studio}/dom-selection.ts +75 -4
- package/apps/studio/draw/animate.ts +333 -0
- package/{plugins/design/dev-server → apps/studio}/draw/brush.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/composition.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/geometry.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/index.ts +4 -1
- package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +1 -1
- package/apps/studio/draw/morph.ts +204 -0
- package/{plugins/design/dev-server → apps/studio}/draw/optimize.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/palette.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/draw/primitives.ts +1 -1
- package/apps/studio/draw/serialize-animate.ts +324 -0
- package/{plugins/design/dev-server → apps/studio}/draw/serialize.ts +9 -3
- package/apps/studio/draw/test/animate.test.ts +172 -0
- package/apps/studio/draw/test/morph.test.ts +107 -0
- package/apps/studio/draw/test/serialize-animate.test.ts +142 -0
- package/{plugins/design/dev-server → apps/studio}/equal-spacing-detector.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/equal-spacing-handles.tsx +7 -29
- package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +13 -1
- package/{plugins/design/dev-server → apps/studio}/http.ts +102 -1
- package/{plugins/design/dev-server → apps/studio}/input-router.tsx +8 -1
- package/{plugins/design/dev-server → apps/studio}/inspect.ts +20 -3
- package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
- package/{plugins/design/dev-server → apps/studio}/server.ts +18 -0
- package/{plugins/design/dev-server → apps/studio}/sync/index.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +63 -0
- package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +97 -0
- package/apps/studio/test/annotations-text-format.test.ts +396 -0
- package/apps/studio/test/asset-api.test.ts +149 -0
- package/apps/studio/test/canvas-create-api.test.ts +361 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +2 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +1 -1
- package/apps/studio/test/canvas-media-drop.test.ts +158 -0
- package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +17 -1
- package/apps/studio/test/canvas-url.test.ts +99 -0
- package/apps/studio/test/index-data-canvas-ds.test.ts +97 -0
- package/apps/studio/test/motion-sample.test.ts +35 -0
- package/{plugins/design/dev-server → apps/studio}/test/perf-harness.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/test/phase-3.6-smoke.test.ts +3 -1
- package/apps/studio/test/read-annotations.test.ts +409 -0
- package/{plugins/design/dev-server → apps/studio}/test/sanitize-annotation-svg.test.ts +108 -2
- package/{plugins/design/dev-server → apps/studio}/test/shared-doc-convergence.test.ts +1 -1
- package/apps/studio/test/tour-overlay.test.tsx +56 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-annotation-resize.test.ts +43 -0
- package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +4 -1
- package/apps/studio/test/whats-new.test.ts +101 -0
- package/apps/studio/text-imports.d.ts +9 -0
- package/{plugins/design/dev-server → apps/studio}/tool-palette.tsx +4 -69
- package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-annotation-resize.tsx +40 -7
- package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +1 -1
- package/apps/studio/use-canvas-media-drop.tsx +343 -0
- package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-cursor-modifiers.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-keyboard-discipline.tsx +5 -2
- package/{plugins/design/dev-server → apps/studio}/use-selection-set.tsx +9 -1
- package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
- package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +3 -1
- package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +1 -1
- package/apps/studio/whats-new.json +139 -0
- package/apps/studio/whats-new.schema.json +70 -0
- package/apps/studio/whats-new.ts +87 -0
- package/cli/bin/maude.mjs +10 -0
- package/cli/commands/design.mjs +15 -7
- package/cli/commands/design.test.mjs +36 -0
- package/cli/commands/help.mjs +8 -2
- package/cli/commands/hub.mjs +3 -3
- package/cli/commands/hub.test.mjs +1 -1
- package/cli/commands/scenario-report.mjs +2 -9
- package/cli/lib/flow-design-integration.test.mjs +1 -1
- package/cli/lib/gitignore-block.mjs +1 -0
- package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
- package/cli/lib/preflight.mjs +1 -1
- package/package.json +15 -15
- package/plugins/design/templates/brief-board.tsx.template +61 -0
- package/plugins/design/templates/canvas.tsx.template +1 -1
- package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -1
- package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +1 -1
- package/plugins/flow/.claude-plugin/config.schema.json +16 -0
- package/plugins/design/dev-server/dist/client.bundle.js +0 -20692
- package/plugins/design/dev-server/dist/comment-mount.js +0 -1924
- package/plugins/design/dev-server/dist/styles.css +0 -2710
- /package/{plugins/design/dev-server → apps/studio}/activity.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_enumerate-artboards-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_html-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pdf-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_png-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pptx-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_pw-launch.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_screenshot-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_svg-optimize.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/_svg-playwright.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/asset-sweep.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/bootstrap-check.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/check-runtime-bundles.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/draw-build.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/handoff.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/prep.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/scenario-report.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/slug.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/smoke.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/svg-optimize.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/bin/visual-sanity.sh +0 -0
- /package/{plugins/design/dev-server → apps/studio}/boot-self-heal.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-build.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-edit.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-header.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-meta.schema.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/canvas-pipeline.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/hmr.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/iframe-lazy.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/0-reset.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/1-tokens.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/2-layout.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/client/styles/5-utilities.css +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/ai-activity.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/awareness-bridge.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/index.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/protocol.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/registry.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/collab/room.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/config.schema.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/context.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/.min-sizes.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_decoding.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_encoding.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion_react.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/pixi-js.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom_client.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-dev-runtime.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-runtime.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_awareness.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_sync.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/dist/runtime/yjs.js +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/brush.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/composition.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/geometry.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/optimize.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/palette.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/primitives.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/draw/test/serialize.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/examples/README.md +0 -0
- /package/{plugins/design/dev-server → apps/studio}/examples/perf-100-artboards.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/_browser-bundles.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/canva-handoff-prompt.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/canva.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/html.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/exporters/zip.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/fs-watch.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/handoff.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/history.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/hmr-broadcast.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/locator.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/mem.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/runtime-bundle.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/server.mjs +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/agent.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/connection-state.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/echo-guard.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/fs-mirror.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/hubs-config.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/migrate-seed.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/status.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/sync/untrusted.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/_helpers.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/active-state.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/activity.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotation-strokes-command.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/artboard-activity-overlay.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/binary-smoke.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/bundle-smoke.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-edit.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-pipeline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/canvas-route.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-awareness-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/comment-mount.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/context-resolve-tokens.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/csp-canvas-shell.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/equal-spacing-detector.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/endpoint.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/html.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pdf.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/png.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx-deck.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/svg.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-20-annotations.svg +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-21-annotations.svg +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/fs-watch.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/handoff.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/history-rollback.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/hmr-classify.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/input-router.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/marquee-overlay.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/move-artboards-command.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/participants-chrome.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/paths.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle-error-mapping.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/server-lifecycle.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-foundation.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-migrate.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-agent.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-codec.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/system-endpoint.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/tool-palette.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/undo-stack.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-annotation-selection.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-artboard-drag.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-collab.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-cursor-modifiers.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-keyboard-discipline.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-selection-set.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +0 -0
- /package/{plugins/design/dev-server → apps/studio}/test/ws-handshake.test.ts +0 -0
- /package/{plugins/design/dev-server → apps/studio}/tsconfig.json +0 -0
- /package/{plugins/design/dev-server → apps/studio}/ws.ts +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// overlay.jsx — hand-rolled guided-tour engine (zero runtime dep, DDR-B).
|
|
2
|
+
//
|
|
3
|
+
// One <TourOverlay steps={[…]} open onClose onComplete/> drives both the
|
|
4
|
+
// per-feature spotlight tours (a What's New entry's `tour[]`) and the evergreen
|
|
5
|
+
// usage tour. A step = { target (CSS selector / [data-tour] anchor), title,
|
|
6
|
+
// body, placement? }. A step whose target can't be resolved still shows, just
|
|
7
|
+
// centered with no spotlight — the tour never dead-ends on a missing element.
|
|
8
|
+
//
|
|
9
|
+
// A11y: role="dialog" + aria-modal, focus moves to the primary button on each
|
|
10
|
+
// step and is trapped within the card, focus is restored on close, Esc skips,
|
|
11
|
+
// ←/→ navigate. prefers-reduced-motion disables the spotlight transition.
|
|
12
|
+
|
|
13
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
14
|
+
|
|
15
|
+
function getRect(target) {
|
|
16
|
+
if (typeof document === 'undefined' || !target) return null;
|
|
17
|
+
try {
|
|
18
|
+
const el = document.querySelector(target);
|
|
19
|
+
if (!el) return null;
|
|
20
|
+
const r = el.getBoundingClientRect();
|
|
21
|
+
if (r.width === 0 && r.height === 0) return null;
|
|
22
|
+
return r;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const CARD_W = 320;
|
|
29
|
+
const CARD_H_EST = 168;
|
|
30
|
+
|
|
31
|
+
function computeCardStyle(rect) {
|
|
32
|
+
if (typeof window === 'undefined' || !rect) {
|
|
33
|
+
return { top: '50%', left: '50%', transform: 'translate(-50%, -50%)', width: CARD_W };
|
|
34
|
+
}
|
|
35
|
+
const vw = window.innerWidth;
|
|
36
|
+
const vh = window.innerHeight;
|
|
37
|
+
const roomBelow = vh - rect.bottom;
|
|
38
|
+
const top =
|
|
39
|
+
roomBelow > CARD_H_EST + 24 ? rect.bottom + 12 : Math.max(12, rect.top - CARD_H_EST - 12);
|
|
40
|
+
const left = Math.min(Math.max(12, rect.left), vw - CARD_W - 12);
|
|
41
|
+
return { top, left, width: CARD_W };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function TourOverlay({ steps, open, onClose, onComplete }) {
|
|
45
|
+
const [i, setI] = useState(0);
|
|
46
|
+
const [rect, setRect] = useState(null);
|
|
47
|
+
const cardRef = useRef(null);
|
|
48
|
+
const prevFocus = useRef(null);
|
|
49
|
+
|
|
50
|
+
const measure = useCallback(() => {
|
|
51
|
+
const step = steps[i];
|
|
52
|
+
setRect(step ? getRect(step.target) : null);
|
|
53
|
+
}, [steps, i]);
|
|
54
|
+
|
|
55
|
+
// Reset to the first step whenever a tour (re)opens.
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (open) setI(0);
|
|
58
|
+
}, [open]);
|
|
59
|
+
|
|
60
|
+
useLayoutEffect(() => {
|
|
61
|
+
if (open) measure();
|
|
62
|
+
}, [open, measure]);
|
|
63
|
+
|
|
64
|
+
// Keep the spotlight glued to its target through scroll/resize; restore focus.
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!open) return;
|
|
67
|
+
prevFocus.current = document.activeElement;
|
|
68
|
+
const onMove = () => measure();
|
|
69
|
+
window.addEventListener('resize', onMove);
|
|
70
|
+
window.addEventListener('scroll', onMove, true);
|
|
71
|
+
return () => {
|
|
72
|
+
window.removeEventListener('resize', onMove);
|
|
73
|
+
window.removeEventListener('scroll', onMove, true);
|
|
74
|
+
try {
|
|
75
|
+
prevFocus.current?.focus?.();
|
|
76
|
+
} catch {}
|
|
77
|
+
};
|
|
78
|
+
}, [open, measure]);
|
|
79
|
+
|
|
80
|
+
const last = i >= steps.length - 1;
|
|
81
|
+
const next = useCallback(() => {
|
|
82
|
+
if (last) {
|
|
83
|
+
onComplete?.();
|
|
84
|
+
onClose?.();
|
|
85
|
+
} else {
|
|
86
|
+
setI((n) => Math.min(n + 1, steps.length - 1));
|
|
87
|
+
}
|
|
88
|
+
}, [last, onComplete, onClose, steps.length]);
|
|
89
|
+
const back = useCallback(() => setI((n) => Math.max(0, n - 1)), []);
|
|
90
|
+
|
|
91
|
+
// Keyboard: Esc skips, ←/→ navigate, Tab is trapped within the card buttons.
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (!open) return;
|
|
94
|
+
function onKey(e) {
|
|
95
|
+
if (e.key === 'Escape') {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
onClose?.();
|
|
98
|
+
} else if (e.key === 'ArrowRight') {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
next();
|
|
101
|
+
} else if (e.key === 'ArrowLeft') {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
back();
|
|
104
|
+
} else if (e.key === 'Tab') {
|
|
105
|
+
const card = cardRef.current;
|
|
106
|
+
if (!card) return;
|
|
107
|
+
const f = card.querySelectorAll('button');
|
|
108
|
+
if (!f.length) return;
|
|
109
|
+
const first = f[0];
|
|
110
|
+
const lastEl = f[f.length - 1];
|
|
111
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
lastEl.focus();
|
|
114
|
+
} else if (!e.shiftKey && document.activeElement === lastEl) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
first.focus();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
window.addEventListener('keydown', onKey, true);
|
|
121
|
+
return () => window.removeEventListener('keydown', onKey, true);
|
|
122
|
+
}, [open, next, back, onClose]);
|
|
123
|
+
|
|
124
|
+
// Move focus to the primary action on each step (accessible dialog behavior).
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!open) return;
|
|
127
|
+
const t = setTimeout(() => {
|
|
128
|
+
const primary = cardRef.current?.querySelector('[data-tour-primary]');
|
|
129
|
+
try {
|
|
130
|
+
primary?.focus();
|
|
131
|
+
} catch {}
|
|
132
|
+
}, 0);
|
|
133
|
+
return () => clearTimeout(t);
|
|
134
|
+
}, [open, i]);
|
|
135
|
+
|
|
136
|
+
if (!open || !steps.length) return null;
|
|
137
|
+
const step = steps[i];
|
|
138
|
+
const pad = 6;
|
|
139
|
+
const spot = rect
|
|
140
|
+
? {
|
|
141
|
+
top: rect.top - pad,
|
|
142
|
+
left: rect.left - pad,
|
|
143
|
+
width: rect.width + pad * 2,
|
|
144
|
+
height: rect.height + pad * 2,
|
|
145
|
+
}
|
|
146
|
+
: null;
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className="mdcc-tour" role="presentation">
|
|
150
|
+
{spot ? (
|
|
151
|
+
<div className="mdcc-tour__spot" style={spot} aria-hidden="true" />
|
|
152
|
+
) : (
|
|
153
|
+
<div className="mdcc-tour__scrim" aria-hidden="true" />
|
|
154
|
+
)}
|
|
155
|
+
<div
|
|
156
|
+
className="mdcc-tour__card"
|
|
157
|
+
ref={cardRef}
|
|
158
|
+
role="dialog"
|
|
159
|
+
aria-modal="true"
|
|
160
|
+
aria-labelledby="mdcc-tour-title"
|
|
161
|
+
aria-describedby="mdcc-tour-body"
|
|
162
|
+
style={computeCardStyle(rect)}
|
|
163
|
+
>
|
|
164
|
+
<div className="mdcc-tour__step">
|
|
165
|
+
{i + 1} / {steps.length}
|
|
166
|
+
</div>
|
|
167
|
+
<div className="mdcc-tour__title" id="mdcc-tour-title">
|
|
168
|
+
{step.title}
|
|
169
|
+
</div>
|
|
170
|
+
<div className="mdcc-tour__body" id="mdcc-tour-body">
|
|
171
|
+
{step.body}
|
|
172
|
+
</div>
|
|
173
|
+
<div className="mdcc-tour__actions">
|
|
174
|
+
<button type="button" className="mdcc-tour__skip" onClick={onClose}>
|
|
175
|
+
Skip
|
|
176
|
+
</button>
|
|
177
|
+
<div className="mdcc-tour__nav">
|
|
178
|
+
{i > 0 && (
|
|
179
|
+
<button type="button" className="mdcc-tour__back" onClick={back}>
|
|
180
|
+
Back
|
|
181
|
+
</button>
|
|
182
|
+
)}
|
|
183
|
+
<button type="button" className="mdcc-tour__next" data-tour-primary onClick={next}>
|
|
184
|
+
{last ? 'Done' : 'Next'}
|
|
185
|
+
</button>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// usage-tour.js — the evergreen "how the Maude UI works" walkthrough.
|
|
2
|
+
//
|
|
3
|
+
// Targets stable elements that exist at first load (no canvas open required):
|
|
4
|
+
// the sidebar, the empty viewport, the menus, the What's New badge, and Help.
|
|
5
|
+
// Steps whose target is missing are shown centered by the overlay, so this is
|
|
6
|
+
// resilient to chrome changes. Reached from Help → "Take the tour" and offered
|
|
7
|
+
// once on first run.
|
|
8
|
+
|
|
9
|
+
export const USAGE_TOUR = [
|
|
10
|
+
{
|
|
11
|
+
target: '.sidebar',
|
|
12
|
+
title: 'Your canvases live here',
|
|
13
|
+
body: 'Every mock in the project shows up in this tree. Click one to open it in a tab — or use “+ board” to spin up a blank brief-board, no command needed.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
target: '.viewport',
|
|
17
|
+
title: 'The canvas',
|
|
18
|
+
body: 'Open a canvas, then ⌘-hover to preview the element under your cursor and ⌘-click to select it. Right-click for Copy CSS / Fit / Reset.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
target: '.mb-menus',
|
|
22
|
+
title: 'Menus & tools',
|
|
23
|
+
body: 'View toggles panels (tree, comments, annotations); Selection and Tools act on the active canvas. Press ? for every shortcut.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
target: "[data-tour='whatsnew']",
|
|
27
|
+
title: 'What’s new',
|
|
28
|
+
body: 'New features land here. Click the ✦ badge any time to catch up on what shipped.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
target: "[data-tour='help']",
|
|
32
|
+
title: 'Help is one key away',
|
|
33
|
+
body: 'Press ? for the full keyboard cheat-sheet — and you can restart this tour from the Help menu whenever you like.',
|
|
34
|
+
},
|
|
35
|
+
];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// whats-new-seen.js — pure, browser-safe "what have I already seen?" logic.
|
|
2
|
+
//
|
|
3
|
+
// No React, no DOM, no node imports — so it's bundled into the client AND
|
|
4
|
+
// imported directly by bun:test (test/whats-new.test.ts). Keep it that way.
|
|
5
|
+
|
|
6
|
+
/** Parse a semver-ish string into a [major, minor, patch] triple, or null. */
|
|
7
|
+
export function parseVer(v) {
|
|
8
|
+
if (typeof v !== 'string') return null;
|
|
9
|
+
const m = v.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
10
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Is version `a` strictly newer than `b`?
|
|
15
|
+
* - Unparseable `a` (e.g. "dev") → not newer (false).
|
|
16
|
+
* - Unparseable `b` but parseable `a` → newer (true): a real release outranks
|
|
17
|
+
* a non-version "seen" marker, so the entry surfaces.
|
|
18
|
+
*/
|
|
19
|
+
export function verGt(a, b) {
|
|
20
|
+
const pa = parseVer(a);
|
|
21
|
+
const pb = parseVer(b);
|
|
22
|
+
if (!pa) return false;
|
|
23
|
+
if (!pb) return true;
|
|
24
|
+
for (let i = 0; i < 3; i++) {
|
|
25
|
+
if (pa[i] !== pb[i]) return pa[i] > pb[i];
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Entries the user hasn't seen yet, given the version they last acknowledged.
|
|
32
|
+
* A pending entry (version == null) is always unseen until acknowledged.
|
|
33
|
+
*/
|
|
34
|
+
export function computeUnseen(entries, seenVersion) {
|
|
35
|
+
if (!Array.isArray(entries)) return [];
|
|
36
|
+
return entries.filter((e) => e && (e.version == null || verGt(e.version, seenVersion)));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The version to persist as "seen" after the user reads the panel — the highest
|
|
41
|
+
* of the current feed version and every entry's version. Stamping this clears
|
|
42
|
+
* the unseen set (pending entries excepted — they have no version to outrank).
|
|
43
|
+
*/
|
|
44
|
+
export function bestSeenVersion(entries, currentVersion) {
|
|
45
|
+
let best = currentVersion;
|
|
46
|
+
if (Array.isArray(entries)) {
|
|
47
|
+
for (const e of entries) {
|
|
48
|
+
if (e && e.version && verGt(e.version, best)) best = e.version;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return best;
|
|
52
|
+
}
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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,7 +153,11 @@ 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
|
}
|
|
@@ -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,
|
|
@@ -453,6 +464,7 @@ export function CommentsOverlay(): React.ReactNode {
|
|
|
453
464
|
return (
|
|
454
465
|
<CommentThread
|
|
455
466
|
comment={focused}
|
|
467
|
+
sequence={indexById.get(focused.id) ?? 0}
|
|
456
468
|
onClose={() => {
|
|
457
469
|
setFocusedId(null);
|
|
458
470
|
// Drop the canvas halo when the thread closes — symmetric with
|
|
@@ -745,7 +757,7 @@ function CommentPin({
|
|
|
745
757
|
// Live screen-coord lookup mirrors SelectionHalos in canvas-shell.tsx.
|
|
746
758
|
// Falls back to stored bounds (a screen-coord capture at create time)
|
|
747
759
|
// when the target element is gone — better than vanishing entirely.
|
|
748
|
-
let pos = screenRectFor(comment.selector);
|
|
760
|
+
let pos = screenRectFor(comment.selector, comment.index);
|
|
749
761
|
if (!pos && comment.bounds) {
|
|
750
762
|
pos = {
|
|
751
763
|
x: comment.bounds.x,
|
|
@@ -925,12 +937,14 @@ function CommentComposer({
|
|
|
925
937
|
|
|
926
938
|
function CommentThread({
|
|
927
939
|
comment,
|
|
940
|
+
sequence,
|
|
928
941
|
onClose,
|
|
929
942
|
onPatch,
|
|
930
943
|
onDelete,
|
|
931
944
|
onReply,
|
|
932
945
|
}: {
|
|
933
946
|
comment: OverlayComment;
|
|
947
|
+
sequence: number;
|
|
934
948
|
onClose: () => void;
|
|
935
949
|
onPatch: (patch: Record<string, unknown>) => void;
|
|
936
950
|
onDelete: () => void;
|
|
@@ -1027,6 +1041,11 @@ function CommentThread({
|
|
|
1027
1041
|
>
|
|
1028
1042
|
<div className="cm-thread__head" id={headId}>
|
|
1029
1043
|
<div className="cm-thread__head-row">
|
|
1044
|
+
{/* Plan C P18 — pin/sequence badge in the popover header (parity with
|
|
1045
|
+
`.design/ui/Studio.tsx` thread popover). */}
|
|
1046
|
+
<span className="cm-thread__seq" aria-hidden="true">
|
|
1047
|
+
{sequence}
|
|
1048
|
+
</span>
|
|
1030
1049
|
<span className="cm-thread__author">{comment.author?.trim() || 'unknown'}</span>
|
|
1031
1050
|
<span className="cm-thread__time">{formatRelativeTime(comment.created)}</span>
|
|
1032
1051
|
<button
|
|
@@ -1159,7 +1178,7 @@ function computeThreadAnchor(comment: OverlayComment): { x: number; y: number }
|
|
|
1159
1178
|
// Resolve target's live screen rect; popover drops below the pin with small
|
|
1160
1179
|
// breathing room. Stored bounds (capture-time screen coords) are the
|
|
1161
1180
|
// last-resort fallback for orphaned pins.
|
|
1162
|
-
const rect = comment.selector ? screenRectFor(comment.selector) : null;
|
|
1181
|
+
const rect = comment.selector ? screenRectFor(comment.selector, comment.index) : null;
|
|
1163
1182
|
if (rect) {
|
|
1164
1183
|
// Pin sits at (rect.right - 12, rect.top - 12). Place popover at the same
|
|
1165
1184
|
// x for visual continuity, 16px below the top so it clears the pin.
|
|
@@ -1184,7 +1203,7 @@ function computeAnchor(state: ComposerState): { x: number; y: number } {
|
|
|
1184
1203
|
return { x: state.clientX, y: state.clientY + 8 };
|
|
1185
1204
|
}
|
|
1186
1205
|
if (state.selection.selector) {
|
|
1187
|
-
const rect = screenRectFor(state.selection.selector);
|
|
1206
|
+
const rect = screenRectFor(state.selection.selector, state.selection.index);
|
|
1188
1207
|
if (rect) {
|
|
1189
1208
|
return { x: rect.x, y: rect.y + rect.h + 8 };
|
|
1190
1209
|
}
|
|
@@ -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
|