@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,56 @@
|
|
|
1
|
+
// tour-overlay — Phase 3. Render contract for the hand-rolled tour engine.
|
|
2
|
+
//
|
|
3
|
+
// renderToStaticMarkup doesn't run effects, so the rect stays null on the
|
|
4
|
+
// server render → the overlay falls back to a centered card + scrim. We assert
|
|
5
|
+
// the a11y dialog contract + step/nav wiring; keyboard/focus-trap behavior is a
|
|
6
|
+
// live-only concern (verified via agent-browser).
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from 'bun:test';
|
|
9
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
10
|
+
import { TourOverlay } from '../client/tour/overlay.jsx';
|
|
11
|
+
|
|
12
|
+
const STEP = { target: '.x', title: 'Welcome', body: 'Body copy here' };
|
|
13
|
+
|
|
14
|
+
describe('TourOverlay', () => {
|
|
15
|
+
test('renders an accessible dialog with the first step', () => {
|
|
16
|
+
const html = renderToStaticMarkup(
|
|
17
|
+
<TourOverlay open steps={[STEP]} onClose={() => {}} onComplete={() => {}} />
|
|
18
|
+
);
|
|
19
|
+
expect(html).toContain('mdcc-tour');
|
|
20
|
+
expect(html).toContain('role="dialog"');
|
|
21
|
+
expect(html).toContain('aria-modal="true"');
|
|
22
|
+
expect(html).toContain('Welcome');
|
|
23
|
+
expect(html).toContain('Body copy here');
|
|
24
|
+
expect(html).toContain('1 / 1');
|
|
25
|
+
expect(html).toContain('mdcc-tour__scrim'); // no rect on the server → scrim, not spotlight
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('single step shows Done (not Next) and no Back', () => {
|
|
29
|
+
const html = renderToStaticMarkup(
|
|
30
|
+
<TourOverlay open steps={[STEP]} onClose={() => {}} onComplete={() => {}} />
|
|
31
|
+
);
|
|
32
|
+
expect(html).toContain('Done');
|
|
33
|
+
expect(html).not.toContain('mdcc-tour__back');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('multi-step first page shows Next, the counter, and no Back yet', () => {
|
|
37
|
+
const steps = [STEP, { ...STEP, title: 'Two' }, { ...STEP, title: 'Three' }];
|
|
38
|
+
const html = renderToStaticMarkup(
|
|
39
|
+
<TourOverlay open steps={steps} onClose={() => {}} onComplete={() => {}} />
|
|
40
|
+
);
|
|
41
|
+
expect(html).toContain('1 / 3');
|
|
42
|
+
expect(html).toContain('Next');
|
|
43
|
+
expect(html).not.toContain('mdcc-tour__back'); // i === 0
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('closed or empty renders nothing', () => {
|
|
47
|
+
expect(
|
|
48
|
+
renderToStaticMarkup(
|
|
49
|
+
<TourOverlay open={false} steps={[STEP]} onClose={() => {}} onComplete={() => {}} />
|
|
50
|
+
)
|
|
51
|
+
).toBe('');
|
|
52
|
+
expect(
|
|
53
|
+
renderToStaticMarkup(<TourOverlay open steps={[]} onClose={() => {}} onComplete={() => {}} />)
|
|
54
|
+
).toBe('');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// use-annotation-resize — FigJam resize-modifier math (pure `resizeStroke`).
|
|
2
|
+
//
|
|
3
|
+
// Covers the no-modifier baseline (must stay algebraically identical to the
|
|
4
|
+
// pre-modifier corner math) plus Shift (aspect-lock / 45° snap), Alt
|
|
5
|
+
// (scale-from-center / mirror-from-midpoint), and Shift+Alt combined, across
|
|
6
|
+
// every resizable stroke kind. Pure function — no DOM render needed.
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from 'bun:test';
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ArrowStroke,
|
|
12
|
+
EllipseStroke,
|
|
13
|
+
ImageStroke,
|
|
14
|
+
LinkStroke,
|
|
15
|
+
PenStroke,
|
|
16
|
+
PolygonStroke,
|
|
17
|
+
RectStroke,
|
|
18
|
+
StickyStroke,
|
|
19
|
+
} from '../annotations-layer.tsx';
|
|
20
|
+
import { resizeStroke } from '../use-annotation-resize.tsx';
|
|
21
|
+
|
|
22
|
+
const rect: RectStroke = {
|
|
23
|
+
id: 'r1',
|
|
24
|
+
tool: 'rect',
|
|
25
|
+
color: '#000',
|
|
26
|
+
width: 2,
|
|
27
|
+
x: 0,
|
|
28
|
+
y: 0,
|
|
29
|
+
w: 100,
|
|
30
|
+
h: 50,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('resizeStroke / rect — no modifiers (baseline)', () => {
|
|
34
|
+
test('se corner anchors the nw corner (back-compat)', () => {
|
|
35
|
+
expect(resizeStroke(rect, 'se', 120, 80)).toEqual({ x: 0, y: 0, w: 120, h: 80 });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('nw corner anchors the se corner', () => {
|
|
39
|
+
expect(resizeStroke(rect, 'nw', -20, -10)).toEqual({ x: -20, y: -10, w: 120, h: 60 });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('dragging past the anchor flips the box (min/max parity)', () => {
|
|
43
|
+
// se dragged to the left of x=0 → box lives to the left of the anchor.
|
|
44
|
+
expect(resizeStroke(rect, 'se', -40, 80)).toEqual({ x: -40, y: 0, w: 40, h: 80 });
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('resizeStroke / rect — Shift (aspect lock)', () => {
|
|
49
|
+
test('keeps the 2:1 start ratio, dominant axis drives scale', () => {
|
|
50
|
+
// raw 120×200 → scale = max(1.2, 4) = 4 → 400×200 (ratio preserved).
|
|
51
|
+
expect(resizeStroke(rect, 'se', 120, 200, { shift: true, alt: false })).toEqual({
|
|
52
|
+
x: 0,
|
|
53
|
+
y: 0,
|
|
54
|
+
w: 400,
|
|
55
|
+
h: 200,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('resizeStroke / rect — Alt (scale from center)', () => {
|
|
61
|
+
test('center stays fixed; both sides grow symmetrically', () => {
|
|
62
|
+
const out = resizeStroke(rect, 'se', 120, 80, { shift: false, alt: true });
|
|
63
|
+
expect(out).toEqual({ x: -20, y: -30, w: 140, h: 110 });
|
|
64
|
+
// center invariant: (-20 + 140/2, -30 + 110/2) === (50, 25) === start center.
|
|
65
|
+
expect((out as RectStroke).x + (out as RectStroke).w / 2).toBe(50);
|
|
66
|
+
expect((out as RectStroke).y + (out as RectStroke).h / 2).toBe(25);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('resizeStroke / rect — Shift+Alt (ratio + center)', () => {
|
|
71
|
+
test('center-anchored and ratio-locked at once', () => {
|
|
72
|
+
expect(resizeStroke(rect, 'se', 120, 200, { shift: true, alt: true })).toEqual({
|
|
73
|
+
x: -300,
|
|
74
|
+
y: -150,
|
|
75
|
+
w: 700,
|
|
76
|
+
h: 350,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('resizeStroke / polygon — routes through the shared bbox math', () => {
|
|
82
|
+
const poly: PolygonStroke = {
|
|
83
|
+
id: 'p1',
|
|
84
|
+
tool: 'polygon',
|
|
85
|
+
shape: 'diamond',
|
|
86
|
+
color: '#000',
|
|
87
|
+
width: 2,
|
|
88
|
+
x: 0,
|
|
89
|
+
y: 0,
|
|
90
|
+
w: 100,
|
|
91
|
+
h: 50,
|
|
92
|
+
};
|
|
93
|
+
test('Shift aspect-locks the polygon like a rect', () => {
|
|
94
|
+
expect(resizeStroke(poly, 'se', 120, 200, { shift: true, alt: false })).toEqual({
|
|
95
|
+
x: 0,
|
|
96
|
+
y: 0,
|
|
97
|
+
w: 400,
|
|
98
|
+
h: 200,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('resizeStroke / sticky — always 1:1 square', () => {
|
|
104
|
+
const sticky: StickyStroke = {
|
|
105
|
+
id: 's1',
|
|
106
|
+
tool: 'sticky',
|
|
107
|
+
color: '#fce8a6',
|
|
108
|
+
x: 0,
|
|
109
|
+
y: 0,
|
|
110
|
+
w: 100,
|
|
111
|
+
h: 100,
|
|
112
|
+
text: '',
|
|
113
|
+
fontSize: 16,
|
|
114
|
+
};
|
|
115
|
+
test('no modifiers — larger axis becomes the side', () => {
|
|
116
|
+
expect(resizeStroke(sticky, 'se', 150, 120)).toEqual({ x: 0, y: 0, w: 150, h: 150 });
|
|
117
|
+
});
|
|
118
|
+
test('Alt — square grows from the center', () => {
|
|
119
|
+
expect(resizeStroke(sticky, 'se', 150, 120, { shift: false, alt: true })).toEqual({
|
|
120
|
+
x: -50,
|
|
121
|
+
y: -50,
|
|
122
|
+
w: 200,
|
|
123
|
+
h: 200,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('resizeStroke / image — Phase 23 (aspect-lock by default, Shift frees)', () => {
|
|
129
|
+
const image: ImageStroke = {
|
|
130
|
+
id: 'im1',
|
|
131
|
+
tool: 'image',
|
|
132
|
+
x: 0,
|
|
133
|
+
y: 0,
|
|
134
|
+
w: 100,
|
|
135
|
+
h: 50, // 2:1 intrinsic aspect
|
|
136
|
+
href: 'assets/abcd1234.png',
|
|
137
|
+
};
|
|
138
|
+
test('no modifiers keeps the intrinsic aspect (the inverse of shapes)', () => {
|
|
139
|
+
// se → 120,80 would be 2.4:1 free, but the lock keeps 2:1 (dominant axis).
|
|
140
|
+
expect(resizeStroke(image, 'se', 120, 80)).toEqual({ x: 0, y: 0, w: 160, h: 80 });
|
|
141
|
+
});
|
|
142
|
+
test('Shift held inverts to a free (unconstrained) resize', () => {
|
|
143
|
+
expect(resizeStroke(image, 'se', 120, 80, { shift: true, alt: false })).toEqual({
|
|
144
|
+
x: 0,
|
|
145
|
+
y: 0,
|
|
146
|
+
w: 120,
|
|
147
|
+
h: 80,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('resizeStroke / link — Phase 23 (free-resizes like a rect)', () => {
|
|
153
|
+
const link: LinkStroke = {
|
|
154
|
+
id: 'lk1',
|
|
155
|
+
tool: 'link',
|
|
156
|
+
x: 0,
|
|
157
|
+
y: 0,
|
|
158
|
+
w: 200,
|
|
159
|
+
h: 80,
|
|
160
|
+
url: 'https://example.com',
|
|
161
|
+
title: 'Example',
|
|
162
|
+
domain: 'example.com',
|
|
163
|
+
};
|
|
164
|
+
test('no modifiers free-resizes (no forced aspect)', () => {
|
|
165
|
+
expect(resizeStroke(link, 'se', 300, 120)).toEqual({ x: 0, y: 0, w: 300, h: 120 });
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('resizeStroke / ellipse — Alt keeps the center fixed', () => {
|
|
170
|
+
const ell: EllipseStroke = {
|
|
171
|
+
id: 'e1',
|
|
172
|
+
tool: 'ellipse',
|
|
173
|
+
color: '#000',
|
|
174
|
+
width: 2,
|
|
175
|
+
cx: 50,
|
|
176
|
+
cy: 50,
|
|
177
|
+
rx: 40,
|
|
178
|
+
ry: 20,
|
|
179
|
+
};
|
|
180
|
+
test('center invariant under symmetric scale', () => {
|
|
181
|
+
expect(resizeStroke(ell, 'se', 120, 90, { shift: false, alt: true })).toEqual({
|
|
182
|
+
cx: 50,
|
|
183
|
+
cy: 50,
|
|
184
|
+
rx: 70,
|
|
185
|
+
ry: 40,
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('resizeStroke / arrow', () => {
|
|
191
|
+
const arrow: ArrowStroke = {
|
|
192
|
+
id: 'a1',
|
|
193
|
+
tool: 'arrow',
|
|
194
|
+
color: '#000',
|
|
195
|
+
width: 2,
|
|
196
|
+
x1: 0,
|
|
197
|
+
y1: 0,
|
|
198
|
+
x2: 100,
|
|
199
|
+
y2: 0,
|
|
200
|
+
};
|
|
201
|
+
test('no modifiers — only the dragged endpoint moves', () => {
|
|
202
|
+
expect(resizeStroke(arrow, 'ep2', 100, 40)).toEqual({ x2: 100, y2: 40 });
|
|
203
|
+
});
|
|
204
|
+
test('Alt — the midpoint is pinned; the far end mirrors', () => {
|
|
205
|
+
expect(resizeStroke(arrow, 'ep2', 100, 40, { shift: false, alt: true })).toEqual({
|
|
206
|
+
x2: 100,
|
|
207
|
+
y2: 40,
|
|
208
|
+
x1: 0,
|
|
209
|
+
y1: -40,
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
test('Shift — shaft angle snaps to the nearest 45°', () => {
|
|
213
|
+
const out = resizeStroke(arrow, 'ep2', 50, 60, { shift: true, alt: false }) as ArrowStroke;
|
|
214
|
+
// Pointer at atan2(60,50)≈49° snaps to 45° → dx === dy along the shaft.
|
|
215
|
+
expect(out.x2).toBeCloseTo(out.y2 ?? Number.NaN, 6);
|
|
216
|
+
expect(out.x2).toBeGreaterThan(0);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('resizeStroke / pen — point scaling', () => {
|
|
221
|
+
const pen: PenStroke = {
|
|
222
|
+
id: 'pen1',
|
|
223
|
+
tool: 'pen',
|
|
224
|
+
color: '#000',
|
|
225
|
+
width: 2,
|
|
226
|
+
points: [
|
|
227
|
+
[0, 0],
|
|
228
|
+
[100, 0],
|
|
229
|
+
[100, 100],
|
|
230
|
+
[0, 100],
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
test('no modifiers — scales around the opposite corner', () => {
|
|
234
|
+
const out = resizeStroke(pen, 'se', 200, 200) as PenStroke;
|
|
235
|
+
expect(out.points[0]).toEqual([0, 0]);
|
|
236
|
+
expect(out.points[2]).toEqual([200, 200]);
|
|
237
|
+
});
|
|
238
|
+
test('Alt — scales around the bbox center', () => {
|
|
239
|
+
const out = resizeStroke(pen, 'se', 150, 150, { shift: false, alt: true }) as PenStroke;
|
|
240
|
+
expect(out.points[0]).toEqual([-50, -50]);
|
|
241
|
+
expect(out.points[2]).toEqual([150, 150]);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe('resizeStroke / rotation — Wave G corner rotate zones', () => {
|
|
246
|
+
// rect center = (50, 25). Angles below are measured around it.
|
|
247
|
+
test('relative rotation: grab angle is the zero reference (no jump)', () => {
|
|
248
|
+
// Grab at angle 0° (due east of center), drag to 45° → rotation 45.
|
|
249
|
+
const rotRef = { angle0: 0, rot0: 0 };
|
|
250
|
+
const out = resizeStroke(
|
|
251
|
+
rect,
|
|
252
|
+
'rot-se',
|
|
253
|
+
50 + 70,
|
|
254
|
+
25 + 70,
|
|
255
|
+
{ shift: false, alt: false },
|
|
256
|
+
rotRef
|
|
257
|
+
);
|
|
258
|
+
expect(out).toEqual({ rotation: 45 });
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('starting rotation accumulates with the drag delta', () => {
|
|
262
|
+
const rotated: RectStroke = { ...rect, rotation: 30 };
|
|
263
|
+
const rotRef = { angle0: 0, rot0: 30 };
|
|
264
|
+
const out = resizeStroke(
|
|
265
|
+
rotated,
|
|
266
|
+
'rot-ne',
|
|
267
|
+
50 + 70,
|
|
268
|
+
25 + 70,
|
|
269
|
+
{ shift: false, alt: false },
|
|
270
|
+
rotRef
|
|
271
|
+
);
|
|
272
|
+
expect(out).toEqual({ rotation: 75 });
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('magnetic cardinals: within 2° of 0 lock on and serialize as undefined', () => {
|
|
276
|
+
const rotated: RectStroke = { ...rect, rotation: 44 };
|
|
277
|
+
// Drag back by 42.5° → raw −1.5° + rot0 44 = ... use angle0 50, current 7.5 → deg = 44 − 42.5 = 1.5 → locks to 0.
|
|
278
|
+
const cur = (Math.PI / 180) * 7.5;
|
|
279
|
+
const out = resizeStroke(
|
|
280
|
+
rotated,
|
|
281
|
+
'rot-nw',
|
|
282
|
+
50 + 70 * Math.cos(cur),
|
|
283
|
+
25 + 70 * Math.sin(cur),
|
|
284
|
+
{ shift: false, alt: false },
|
|
285
|
+
{ angle0: 50, rot0: 44 }
|
|
286
|
+
);
|
|
287
|
+
expect(out).toEqual({ rotation: undefined });
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('Shift snaps to 15° steps', () => {
|
|
291
|
+
// delta = 40° → snaps to 45.
|
|
292
|
+
const cur = (Math.PI / 180) * 40;
|
|
293
|
+
const out = resizeStroke(
|
|
294
|
+
rect,
|
|
295
|
+
'rot-sw',
|
|
296
|
+
50 + 70 * Math.cos(cur),
|
|
297
|
+
25 + 70 * Math.sin(cur),
|
|
298
|
+
{ shift: true, alt: false },
|
|
299
|
+
{ angle0: 0, rot0: 0 }
|
|
300
|
+
);
|
|
301
|
+
expect(out).toEqual({ rotation: 45 });
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('non-rotatable strokes refuse the rotate corners', () => {
|
|
305
|
+
const arrow: ArrowStroke = {
|
|
306
|
+
id: 'a1',
|
|
307
|
+
tool: 'arrow',
|
|
308
|
+
color: '#000',
|
|
309
|
+
width: 2,
|
|
310
|
+
x1: 0,
|
|
311
|
+
y1: 0,
|
|
312
|
+
x2: 100,
|
|
313
|
+
y2: 0,
|
|
314
|
+
};
|
|
315
|
+
expect(
|
|
316
|
+
resizeStroke(arrow, 'rot-se', 50, 50, { shift: false, alt: false }, { angle0: 0, rot0: 0 })
|
|
317
|
+
).toBeNull();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('polygon rotates (diamond/triangle are rotatable hosts)', () => {
|
|
321
|
+
const poly: PolygonStroke = {
|
|
322
|
+
id: 'p1',
|
|
323
|
+
tool: 'polygon',
|
|
324
|
+
shape: 'diamond',
|
|
325
|
+
color: '#000',
|
|
326
|
+
width: 2,
|
|
327
|
+
x: 0,
|
|
328
|
+
y: 0,
|
|
329
|
+
w: 100,
|
|
330
|
+
h: 50,
|
|
331
|
+
};
|
|
332
|
+
const out = resizeStroke(
|
|
333
|
+
poly,
|
|
334
|
+
'rot-se',
|
|
335
|
+
50 + 70,
|
|
336
|
+
25 + 70,
|
|
337
|
+
{ shift: false, alt: false },
|
|
338
|
+
{ angle0: 0, rot0: 0 }
|
|
339
|
+
);
|
|
340
|
+
expect(out).toEqual({ rotation: 45 });
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe('resizeStroke / rotated strokes — Wave H anchor compensation', () => {
|
|
345
|
+
// rect 100×50 at origin, rotated 90° around its center (50, 25).
|
|
346
|
+
const rotated: RectStroke = { ...rect, rotation: 90 };
|
|
347
|
+
const rotP = (px: number, py: number, cx: number, cy: number, deg: number): [number, number] => {
|
|
348
|
+
const r = (deg * Math.PI) / 180;
|
|
349
|
+
const dx = px - cx;
|
|
350
|
+
const dy = py - cy;
|
|
351
|
+
return [cx + dx * Math.cos(r) - dy * Math.sin(r), cy + dx * Math.sin(r) + dy * Math.cos(r)];
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
test('se-corner drag keeps the nw corner fixed in WORLD space', () => {
|
|
355
|
+
// Cursor in the LOCAL frame (applyResize inverse-rotates before calling).
|
|
356
|
+
const out = resizeStroke(rotated, 'se', 140, 90) as RectStroke;
|
|
357
|
+
expect(out.w).toBeCloseTo(140, 6);
|
|
358
|
+
expect(out.h).toBeCloseTo(90, 6);
|
|
359
|
+
const before = rotP(0, 0, 50, 25, 90);
|
|
360
|
+
const after = rotP(out.x, out.y, out.x + out.w / 2, out.y + out.h / 2, 90);
|
|
361
|
+
expect(after[0]).toBeCloseTo(before[0], 6);
|
|
362
|
+
expect(after[1]).toBeCloseTo(before[1], 6);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test('e-edge drag keeps the west edge midpoint fixed in WORLD space', () => {
|
|
366
|
+
const out = resizeStroke(rotated, 'e', 130, 25) as RectStroke;
|
|
367
|
+
expect(out.w).toBeCloseTo(130, 6);
|
|
368
|
+
const before = rotP(0, 25, 50, 25, 90);
|
|
369
|
+
const after = rotP(out.x, out.y + out.h / 2, out.x + out.w / 2, out.y + out.h / 2, 90);
|
|
370
|
+
expect(after[0]).toBeCloseTo(before[0], 6);
|
|
371
|
+
expect(after[1]).toBeCloseTo(before[1], 6);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('Alt resize keeps the center (no compensation path)', () => {
|
|
375
|
+
const out = resizeStroke(rotated, 'se', 130, 80, { shift: false, alt: true }) as RectStroke;
|
|
376
|
+
expect(out.x + out.w / 2).toBeCloseTo(50, 6);
|
|
377
|
+
expect(out.y + out.h / 2).toBeCloseTo(25, 6);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test('unrotated strokes are byte-stable (no shift applied)', () => {
|
|
381
|
+
expect(resizeStroke(rect, 'se', 120, 80)).toEqual({ x: 0, y: 0, w: 120, h: 80 });
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('rotated ellipse: se drag keeps the nw bbox corner fixed in WORLD space', () => {
|
|
385
|
+
const ell: EllipseStroke = {
|
|
386
|
+
id: 'e1',
|
|
387
|
+
tool: 'ellipse',
|
|
388
|
+
color: '#000',
|
|
389
|
+
width: 2,
|
|
390
|
+
cx: 50,
|
|
391
|
+
cy: 25,
|
|
392
|
+
rx: 50,
|
|
393
|
+
ry: 25,
|
|
394
|
+
rotation: 45,
|
|
395
|
+
};
|
|
396
|
+
const out = resizeStroke(ell, 'se', 140, 90) as EllipseStroke;
|
|
397
|
+
const before = rotP(0, 0, 50, 25, 45);
|
|
398
|
+
const after = rotP(out.cx - out.rx, out.cy - out.ry, out.cx, out.cy, 45);
|
|
399
|
+
expect(after[0]).toBeCloseTo(before[0], 6);
|
|
400
|
+
expect(after[1]).toBeCloseTo(before[1], 6);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// use-tool-mode — Phase 4.1 Task 2. Provider transitions + cursor sync.
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { describe, expect, test } from 'bun:test';
|
|
4
4
|
|
|
5
5
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
6
6
|
|
|
@@ -12,14 +12,16 @@ import {
|
|
|
12
12
|
} from '../use-tool-mode.tsx';
|
|
13
13
|
|
|
14
14
|
describe('use-tool-mode / static', () => {
|
|
15
|
-
test('DEFAULT_TOOLS exposes V/H/C + draw set B/R/N/A/T/E (
|
|
15
|
+
test('DEFAULT_TOOLS exposes V/H/C + draw set B/I/R/N/⇧S/A/T/E (single Shape tool + highlighter + section)', () => {
|
|
16
16
|
expect(DEFAULT_TOOLS.map((t) => t.id)).toEqual([
|
|
17
17
|
'move',
|
|
18
18
|
'hand',
|
|
19
19
|
'comment',
|
|
20
20
|
'pen',
|
|
21
|
+
'highlighter',
|
|
21
22
|
'shape',
|
|
22
23
|
'sticky',
|
|
24
|
+
'section',
|
|
23
25
|
'arrow',
|
|
24
26
|
'text',
|
|
25
27
|
'eraser',
|
|
@@ -29,8 +31,10 @@ describe('use-tool-mode / static', () => {
|
|
|
29
31
|
'H',
|
|
30
32
|
'C',
|
|
31
33
|
'B',
|
|
34
|
+
'I',
|
|
32
35
|
'R',
|
|
33
36
|
'N',
|
|
37
|
+
'⇧S',
|
|
34
38
|
'A',
|
|
35
39
|
'T',
|
|
36
40
|
'E',
|
|
@@ -50,8 +54,10 @@ describe('use-tool-mode / static', () => {
|
|
|
50
54
|
'hand',
|
|
51
55
|
'comment',
|
|
52
56
|
'pen',
|
|
57
|
+
'highlighter',
|
|
53
58
|
'shape',
|
|
54
59
|
'sticky',
|
|
60
|
+
'section',
|
|
55
61
|
'arrow',
|
|
56
62
|
'text',
|
|
57
63
|
'eraser',
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { bestSeenVersion, computeUnseen, parseVer, verGt } from '../client/whats-new-seen.js';
|
|
6
|
+
import { loadWhatsNew, resolveMaudeVersion } from '../whats-new.ts';
|
|
7
|
+
|
|
8
|
+
describe('whats-new feed loader', () => {
|
|
9
|
+
test('loads the committed feed with a version + non-empty entries', () => {
|
|
10
|
+
const feed = loadWhatsNew({ fresh: true });
|
|
11
|
+
expect(typeof feed.version).toBe('string');
|
|
12
|
+
expect(Array.isArray(feed.entries)).toBe(true);
|
|
13
|
+
expect(feed.entries.length).toBeGreaterThan(0);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('every entry has the required shape', () => {
|
|
17
|
+
for (const e of loadWhatsNew({ fresh: true }).entries) {
|
|
18
|
+
expect(typeof e.id).toBe('string');
|
|
19
|
+
expect(e.id).toMatch(/^[a-z0-9][a-z0-9-]*$/);
|
|
20
|
+
expect(['feature', 'improvement', 'usage', 'fix']).toContain(e.kind);
|
|
21
|
+
expect(e.title.length).toBeGreaterThan(0);
|
|
22
|
+
expect(e.summary.length).toBeGreaterThan(0);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('resolveMaudeVersion reads the design plugin manifest', () => {
|
|
27
|
+
expect(resolveMaudeVersion()).toMatch(/^\d+\.\d+\.\d+/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('a missing feed dir yields empty entries and never throws', () => {
|
|
31
|
+
const feed = loadWhatsNew({ root: join(import.meta.dir, '__does-not-exist__'), fresh: true });
|
|
32
|
+
expect(feed.entries).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Security regression (ethical-hacker creativity finding): the feed MUST
|
|
36
|
+
// resolve from the maude package root, NEVER a served project's tree. A
|
|
37
|
+
// hostile served project dropping its own whats-new.json must not inject
|
|
38
|
+
// entries into the privileged main-origin UI. Pin the boundary so a future
|
|
39
|
+
// "project-overridable feed" change can't silently erode it.
|
|
40
|
+
test('default resolution ignores a feed planted in an arbitrary (served-project) dir', () => {
|
|
41
|
+
const hostile = mkdtempSync(join(tmpdir(), 'mdcc-served-'));
|
|
42
|
+
writeFileSync(
|
|
43
|
+
join(hostile, 'whats-new.json'),
|
|
44
|
+
JSON.stringify({
|
|
45
|
+
entries: [{ id: 'evil', kind: 'feature', title: 'pwn', summary: 'x' }],
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
const feed = loadWhatsNew({ fresh: true }); // no root → package root only
|
|
49
|
+
expect(feed.entries.some((e) => e.id === 'evil')).toBe(false);
|
|
50
|
+
expect(feed.entries.length).toBeGreaterThan(0); // still reads the real package feed
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Defense-in-depth contract: a rendered learnMore is always an http(s) URL.
|
|
54
|
+
test('every committed entry learnMore is http(s) (no javascript:/data: scheme)', () => {
|
|
55
|
+
for (const e of loadWhatsNew({ fresh: true }).entries) {
|
|
56
|
+
if (e.learnMore != null) expect(e.learnMore).toMatch(/^https?:\/\//);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('seen logic (browser-safe, shared with the client)', () => {
|
|
62
|
+
test('parseVer extracts a semver triple', () => {
|
|
63
|
+
expect(parseVer('0.28.1')).toEqual([0, 28, 1]);
|
|
64
|
+
expect(parseVer('1.2.3-beta.4')).toEqual([1, 2, 3]);
|
|
65
|
+
expect(parseVer('dev')).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('verGt compares semver, treating unparseable seen as oldest', () => {
|
|
69
|
+
expect(verGt('0.29.0', '0.28.1')).toBe(true);
|
|
70
|
+
expect(verGt('0.28.1', '0.29.0')).toBe(false);
|
|
71
|
+
expect(verGt('1.0.0', '0.99.99')).toBe(true);
|
|
72
|
+
expect(verGt('0.28.1', '0.28.1')).toBe(false);
|
|
73
|
+
expect(verGt('0.28.1', 'dev')).toBe(true);
|
|
74
|
+
expect(verGt('dev', '0.28.1')).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('pending (null version) entries are always unseen', () => {
|
|
78
|
+
const u = computeUnseen(
|
|
79
|
+
[{ id: 'a', version: null, kind: 'feature', title: 't', summary: 's' }],
|
|
80
|
+
'9.9.9'
|
|
81
|
+
);
|
|
82
|
+
expect(u.map((e) => e.id)).toEqual(['a']);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('computeUnseen returns only entries newer than the seen version', () => {
|
|
86
|
+
const entries = [
|
|
87
|
+
{ id: 'old', version: '0.27.0', kind: 'feature', title: 't', summary: 's' },
|
|
88
|
+
{ id: 'new', version: '0.29.0', kind: 'feature', title: 't', summary: 's' },
|
|
89
|
+
];
|
|
90
|
+
expect(computeUnseen(entries, '0.28.0').map((e) => e.id)).toEqual(['new']);
|
|
91
|
+
expect(computeUnseen(entries, '0.29.0')).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('bestSeenVersion picks the highest of feed + entry versions', () => {
|
|
95
|
+
expect(bestSeenVersion([{ version: '0.29.0' }, { version: '0.27.0' }], '0.28.1')).toBe(
|
|
96
|
+
'0.29.0'
|
|
97
|
+
);
|
|
98
|
+
expect(bestSeenVersion([{ version: '0.20.0' }], '0.28.1')).toBe('0.28.1');
|
|
99
|
+
expect(bestSeenVersion([{ version: null }], '0.28.1')).toBe('0.28.1');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Ambient declarations for Bun text imports — `import x from './f.ext' with { type: 'text' }`.
|
|
2
|
+
// The dev-server runs under Bun (which honors the import attribute) and ships as
|
|
3
|
+
// a `bun --compile` binary (which inlines the text at build time). tsc only needs
|
|
4
|
+
// to know the module resolves to a string default export.
|
|
5
|
+
|
|
6
|
+
declare module '*.tsx.template' {
|
|
7
|
+
const content: string;
|
|
8
|
+
export default content;
|
|
9
|
+
}
|