@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
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file use-annotation-resize.tsx — Task 23 (Wave 2, G4)
|
|
3
|
-
* @scope plugins/design/dev-server/use-annotation-resize.tsx
|
|
4
|
-
* @purpose Screen-space corner / endpoint handles for the selected
|
|
5
|
-
* annotation. Per-tool resize math with FigJam resize modifiers:
|
|
6
|
-
* Shift = lock aspect ratio (45° angle snap for arrows), Alt =
|
|
7
|
-
* scale from center, Shift+Alt = both. Modifiers update live on
|
|
8
|
-
* keydown/keyup mid-drag (re-applied at the last pointer position).
|
|
9
|
-
*
|
|
10
|
-
* Handles are `position: fixed` DOM siblings of the canvas (the
|
|
11
|
-
* same pattern element selection uses) — they stay 8 × 8 CSS px
|
|
12
|
-
* at any zoom and never get caught in the SVG `vector-effect`
|
|
13
|
-
* gymnastics.
|
|
14
|
-
*
|
|
15
|
-
* Mounts the overlay element exactly once via `AnnotationResizeOverlay`.
|
|
16
|
-
* The hook does the math + persistence; the overlay component
|
|
17
|
-
* owns the rAF loop that follows pan/zoom + pointer drags.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { type ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
type ArrowStroke,
|
|
24
|
-
type EllipseStroke,
|
|
25
|
-
type PenStroke,
|
|
26
|
-
type PolygonStroke,
|
|
27
|
-
type RectStroke,
|
|
28
|
-
type StickyStroke,
|
|
29
|
-
type Stroke,
|
|
30
|
-
type StrokesStoreValue,
|
|
31
|
-
strokeBBox,
|
|
32
|
-
} from './annotations-layer.tsx';
|
|
33
|
-
import { useViewportControllerContext } from './canvas-lib.tsx';
|
|
34
|
-
import { useAnnotationSelection } from './use-annotation-selection.tsx';
|
|
35
|
-
|
|
36
|
-
const RESIZE_CSS = `
|
|
37
|
-
.dc-annot-resize-handle {
|
|
38
|
-
position: fixed;
|
|
39
|
-
width: 8px;
|
|
40
|
-
height: 8px;
|
|
41
|
-
background: var(--maude-hud-accent, #d63b1f);
|
|
42
|
-
border: 1px solid var(--bg-0, #ffffff);
|
|
43
|
-
border-radius: 1px;
|
|
44
|
-
box-shadow: 0 0 0 0.5px color-mix(in oklab, var(--fg-0, #1c1917) 30%, transparent);
|
|
45
|
-
z-index: 6;
|
|
46
|
-
pointer-events: auto;
|
|
47
|
-
touch-action: none;
|
|
48
|
-
}
|
|
49
|
-
/* Phase 24 — '!important' so the scale/move affordance beats use-tool-mode's
|
|
50
|
-
blanket '* { cursor: <tool> !important }' (move mode). Without it the move
|
|
51
|
-
glyph clobbered the resize cursors and the user saw no scale affordance over
|
|
52
|
-
a handle. Specificity already wins ('.class[attr]' > '*'); the '!important'
|
|
53
|
-
is what lets it through against the other '!important' rule. See DDR-067.
|
|
54
|
-
NOTE: keep this comment backtick-free — it lives inside the RESIZE_CSS
|
|
55
|
-
template literal and a stray backtick closes it (bun parse fail, §6). */
|
|
56
|
-
.dc-annot-resize-handle[data-corner="nw"], .dc-annot-resize-handle[data-corner="se"] { cursor: nwse-resize !important; }
|
|
57
|
-
.dc-annot-resize-handle[data-corner="ne"], .dc-annot-resize-handle[data-corner="sw"] { cursor: nesw-resize !important; }
|
|
58
|
-
.dc-annot-resize-handle[data-corner="ep1"], .dc-annot-resize-handle[data-corner="ep2"] { cursor: move !important; }
|
|
59
|
-
`.trim();
|
|
60
|
-
|
|
61
|
-
function ensureResizeStyles(): void {
|
|
62
|
-
if (typeof document === 'undefined') return;
|
|
63
|
-
if (document.getElementById('dc-annot-resize-css')) return;
|
|
64
|
-
const s = document.createElement('style');
|
|
65
|
-
s.id = 'dc-annot-resize-css';
|
|
66
|
-
s.textContent = RESIZE_CSS;
|
|
67
|
-
document.head.appendChild(s);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
type Corner = 'nw' | 'ne' | 'sw' | 'se' | 'ep1' | 'ep2';
|
|
71
|
-
|
|
72
|
-
/** Stroke types that expose resize handles. Text inherits its anchor bbox. */
|
|
73
|
-
function isResizable(
|
|
74
|
-
s: Stroke
|
|
75
|
-
): s is RectStroke | EllipseStroke | PolygonStroke | ArrowStroke | PenStroke | StickyStroke {
|
|
76
|
-
return (
|
|
77
|
-
s.tool === 'rect' ||
|
|
78
|
-
s.tool === 'ellipse' ||
|
|
79
|
-
s.tool === 'polygon' ||
|
|
80
|
-
s.tool === 'arrow' ||
|
|
81
|
-
s.tool === 'pen' ||
|
|
82
|
-
s.tool === 'sticky'
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Resize modifiers (FigJam-parity). Held during a handle drag:
|
|
88
|
-
* • `shift` — lock to the start aspect ratio (45° angle snap for arrows;
|
|
89
|
-
* always-1:1 stickies ignore it — they're square regardless).
|
|
90
|
-
* • `alt` — scale symmetrically around the stroke's center / midpoint.
|
|
91
|
-
* Both together combine (ratio-locked AND center-anchored). With neither held
|
|
92
|
-
* the result is byte-identical to the pre-modifier behaviour.
|
|
93
|
-
*/
|
|
94
|
-
export interface ResizeMods {
|
|
95
|
-
shift: boolean;
|
|
96
|
-
alt: boolean;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const NO_MODS: ResizeMods = { shift: false, alt: false };
|
|
100
|
-
|
|
101
|
-
const isWestCorner = (c: Corner): boolean => c === 'nw' || c === 'sw';
|
|
102
|
-
const isNorthCorner = (c: Corner): boolean => c === 'nw' || c === 'ne';
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Shared bbox resize for rect / polygon / sticky / ellipse. Returns the new
|
|
106
|
-
* axis-aligned box for the dragged `corner` moving to world (wx, wy):
|
|
107
|
-
* • normal — the diagonally-opposite corner is the fixed anchor.
|
|
108
|
-
* • Alt — the box's center is the fixed anchor (symmetric scale).
|
|
109
|
-
* • Shift — keep the start aspect ratio (the dominant axis drives scale).
|
|
110
|
-
* • square — force 1:1 regardless of Shift (sticky notes).
|
|
111
|
-
* The no-modifier branch is algebraically identical to the previous
|
|
112
|
-
* min/max corner math (verified against the resize round-trip tests).
|
|
113
|
-
*/
|
|
114
|
-
function bboxResize(
|
|
115
|
-
bbox: { x: number; y: number; w: number; h: number },
|
|
116
|
-
corner: Corner,
|
|
117
|
-
wx: number,
|
|
118
|
-
wy: number,
|
|
119
|
-
mods: ResizeMods,
|
|
120
|
-
square: boolean
|
|
121
|
-
): { x: number; y: number; w: number; h: number } {
|
|
122
|
-
const { x, y, w, h } = bbox;
|
|
123
|
-
const cx = x + w / 2;
|
|
124
|
-
const cy = y + h / 2;
|
|
125
|
-
const isW = isWestCorner(corner);
|
|
126
|
-
const isN = isNorthCorner(corner);
|
|
127
|
-
const anchorX = isW ? x + w : x;
|
|
128
|
-
const anchorY = isN ? y + h : y;
|
|
129
|
-
|
|
130
|
-
// Raw extents from the dragged corner — center-relative under Alt.
|
|
131
|
-
let nw = mods.alt ? 2 * Math.abs(wx - cx) : Math.abs(wx - anchorX);
|
|
132
|
-
let nh = mods.alt ? 2 * Math.abs(wy - cy) : Math.abs(wy - anchorY);
|
|
133
|
-
|
|
134
|
-
if (square) {
|
|
135
|
-
const side = Math.max(nw, nh, 1);
|
|
136
|
-
nw = side;
|
|
137
|
-
nh = side;
|
|
138
|
-
} else if (mods.shift && w > 0 && h > 0) {
|
|
139
|
-
const s = Math.max(nw / w, nh / h);
|
|
140
|
-
nw = w * s;
|
|
141
|
-
nh = h * s;
|
|
142
|
-
}
|
|
143
|
-
nw = Math.max(1, nw);
|
|
144
|
-
nh = Math.max(1, nh);
|
|
145
|
-
|
|
146
|
-
let nx: number;
|
|
147
|
-
let ny: number;
|
|
148
|
-
if (mods.alt) {
|
|
149
|
-
nx = cx - nw / 2;
|
|
150
|
-
ny = cy - nh / 2;
|
|
151
|
-
} else {
|
|
152
|
-
// Grow away from the anchor toward the cursor's side (handles flips).
|
|
153
|
-
nx = wx < anchorX ? anchorX - nw : anchorX;
|
|
154
|
-
ny = wy < anchorY ? anchorY - nh : anchorY;
|
|
155
|
-
}
|
|
156
|
-
return { x: nx, y: ny, w: nw, h: nh };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Per-tool resize math. Given a stroke + the moved corner + the new world
|
|
161
|
-
* coords for that corner, returns a patched stroke. `start` is the stroke at
|
|
162
|
-
* the moment the drag began (used as the source-of-truth for scaling math —
|
|
163
|
-
* avoids drift from rounding successive deltas). `mods` carries the live
|
|
164
|
-
* FigJam resize modifiers (Shift aspect-lock, Alt scale-from-center).
|
|
165
|
-
*/
|
|
166
|
-
export function resizeStroke(
|
|
167
|
-
start: Stroke,
|
|
168
|
-
corner: Corner,
|
|
169
|
-
wx: number,
|
|
170
|
-
wy: number,
|
|
171
|
-
mods: ResizeMods = NO_MODS
|
|
172
|
-
): Partial<Stroke> | null {
|
|
173
|
-
if (start.tool === 'rect' || start.tool === 'sticky' || start.tool === 'polygon') {
|
|
174
|
-
// Rect / polygon / sticky all resize via their shared x / y / w / h bbox.
|
|
175
|
-
// Text re-wraps inside the foreignObject automatically. Sticky stays 1:1.
|
|
176
|
-
const box = bboxResize(
|
|
177
|
-
{ x: start.x, y: start.y, w: start.w, h: start.h },
|
|
178
|
-
corner,
|
|
179
|
-
wx,
|
|
180
|
-
wy,
|
|
181
|
-
mods,
|
|
182
|
-
start.tool === 'sticky'
|
|
183
|
-
);
|
|
184
|
-
return box as Partial<RectStroke | StickyStroke | PolygonStroke>;
|
|
185
|
-
}
|
|
186
|
-
if (start.tool === 'ellipse') {
|
|
187
|
-
// Treat the four corners as the bbox of the ellipse, then derive cx/cy/rx/ry.
|
|
188
|
-
const box = bboxResize(
|
|
189
|
-
{ x: start.cx - start.rx, y: start.cy - start.ry, w: start.rx * 2, h: start.ry * 2 },
|
|
190
|
-
corner,
|
|
191
|
-
wx,
|
|
192
|
-
wy,
|
|
193
|
-
mods,
|
|
194
|
-
false
|
|
195
|
-
);
|
|
196
|
-
return {
|
|
197
|
-
cx: box.x + box.w / 2,
|
|
198
|
-
cy: box.y + box.h / 2,
|
|
199
|
-
rx: Math.max(1, box.w / 2),
|
|
200
|
-
ry: Math.max(1, box.h / 2),
|
|
201
|
-
} as Partial<EllipseStroke>;
|
|
202
|
-
}
|
|
203
|
-
if (start.tool === 'arrow') {
|
|
204
|
-
if (corner !== 'ep1' && corner !== 'ep2') return null;
|
|
205
|
-
const otherX = corner === 'ep1' ? start.x2 : start.x1;
|
|
206
|
-
const otherY = corner === 'ep1' ? start.y2 : start.y1;
|
|
207
|
-
const midX = (start.x1 + start.x2) / 2;
|
|
208
|
-
const midY = (start.y1 + start.y2) / 2;
|
|
209
|
-
// Alt pins the midpoint (both ends mirror); otherwise the far end is fixed.
|
|
210
|
-
const refX = mods.alt ? midX : otherX;
|
|
211
|
-
const refY = mods.alt ? midY : otherY;
|
|
212
|
-
let dragX = wx;
|
|
213
|
-
let dragY = wy;
|
|
214
|
-
if (mods.shift) {
|
|
215
|
-
// Snap the shaft angle (relative to the reference) to 45° increments.
|
|
216
|
-
const dx = wx - refX;
|
|
217
|
-
const dy = wy - refY;
|
|
218
|
-
const dist = Math.hypot(dx, dy);
|
|
219
|
-
const step = Math.PI / 4;
|
|
220
|
-
const ang = Math.round(Math.atan2(dy, dx) / step) * step;
|
|
221
|
-
dragX = refX + Math.cos(ang) * dist;
|
|
222
|
-
dragY = refY + Math.sin(ang) * dist;
|
|
223
|
-
}
|
|
224
|
-
if (mods.alt) {
|
|
225
|
-
const mirrorX = 2 * midX - dragX;
|
|
226
|
-
const mirrorY = 2 * midY - dragY;
|
|
227
|
-
return corner === 'ep1'
|
|
228
|
-
? ({ x1: dragX, y1: dragY, x2: mirrorX, y2: mirrorY } as Partial<ArrowStroke>)
|
|
229
|
-
: ({ x2: dragX, y2: dragY, x1: mirrorX, y1: mirrorY } as Partial<ArrowStroke>);
|
|
230
|
-
}
|
|
231
|
-
return corner === 'ep1'
|
|
232
|
-
? ({ x1: dragX, y1: dragY } as Partial<ArrowStroke>)
|
|
233
|
-
: ({ x2: dragX, y2: dragY } as Partial<ArrowStroke>);
|
|
234
|
-
}
|
|
235
|
-
if (start.tool === 'pen') {
|
|
236
|
-
// Scale all points around an anchor — the opposite corner (normal) or the
|
|
237
|
-
// bbox center (Alt). Shift forces a uniform scale (dominant axis wins).
|
|
238
|
-
// A 0-extent axis (single-point pen stroke) keeps scale 1 (no div-by-zero).
|
|
239
|
-
const bb = strokeBBox(start);
|
|
240
|
-
if (!bb) return null;
|
|
241
|
-
const cx = bb.x + bb.w / 2;
|
|
242
|
-
const cy = bb.y + bb.h / 2;
|
|
243
|
-
const isW = isWestCorner(corner);
|
|
244
|
-
const isN = isNorthCorner(corner);
|
|
245
|
-
let anchorX: number;
|
|
246
|
-
let anchorY: number;
|
|
247
|
-
let sx: number;
|
|
248
|
-
let sy: number;
|
|
249
|
-
if (mods.alt) {
|
|
250
|
-
anchorX = cx;
|
|
251
|
-
anchorY = cy;
|
|
252
|
-
sx = bb.w === 0 ? 1 : (wx - cx) / (isW ? -bb.w / 2 : bb.w / 2);
|
|
253
|
-
sy = bb.h === 0 ? 1 : (wy - cy) / (isN ? -bb.h / 2 : bb.h / 2);
|
|
254
|
-
} else {
|
|
255
|
-
anchorX = isW ? bb.x + bb.w : bb.x;
|
|
256
|
-
anchorY = isN ? bb.y + bb.h : bb.y;
|
|
257
|
-
const newLeft = isW ? wx : bb.x;
|
|
258
|
-
const newTop = isN ? wy : bb.y;
|
|
259
|
-
const newRight = isW ? bb.x + bb.w : wx;
|
|
260
|
-
const newBottom = isN ? bb.y + bb.h : wy;
|
|
261
|
-
sx = bb.w === 0 ? 1 : (newRight - newLeft) / bb.w;
|
|
262
|
-
sy = bb.h === 0 ? 1 : (newBottom - newTop) / bb.h;
|
|
263
|
-
}
|
|
264
|
-
if (mods.shift) {
|
|
265
|
-
const s = Math.max(Math.abs(sx), Math.abs(sy));
|
|
266
|
-
sx = (sx < 0 ? -1 : 1) * s;
|
|
267
|
-
sy = (sy < 0 ? -1 : 1) * s;
|
|
268
|
-
}
|
|
269
|
-
const scaled = start.points.map(
|
|
270
|
-
([px, py]) =>
|
|
271
|
-
[anchorX + (px - anchorX) * sx, anchorY + (py - anchorY) * sy] as [number, number]
|
|
272
|
-
);
|
|
273
|
-
return { points: scaled } as Partial<PenStroke>;
|
|
274
|
-
}
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Returns the four corners (or two endpoints, for arrow) of the selected
|
|
280
|
-
* stroke in world coordinates. Used by the overlay component to position
|
|
281
|
-
* the screen-space handle divs each rAF tick.
|
|
282
|
-
*/
|
|
283
|
-
function handlePositions(s: Stroke): Array<{ corner: Corner; x: number; y: number }> {
|
|
284
|
-
if (s.tool === 'arrow') {
|
|
285
|
-
return [
|
|
286
|
-
{ corner: 'ep1', x: s.x1, y: s.y1 },
|
|
287
|
-
{ corner: 'ep2', x: s.x2, y: s.y2 },
|
|
288
|
-
];
|
|
289
|
-
}
|
|
290
|
-
const bb = strokeBBox(s);
|
|
291
|
-
if (!bb) return [];
|
|
292
|
-
return [
|
|
293
|
-
{ corner: 'nw', x: bb.x, y: bb.y },
|
|
294
|
-
{ corner: 'ne', x: bb.x + bb.w, y: bb.y },
|
|
295
|
-
{ corner: 'sw', x: bb.x, y: bb.y + bb.h },
|
|
296
|
-
{ corner: 'se', x: bb.x + bb.w, y: bb.y + bb.h },
|
|
297
|
-
];
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export function AnnotationResizeOverlay({ store }: { store: StrokesStoreValue | null }): ReactNode {
|
|
301
|
-
ensureResizeStyles();
|
|
302
|
-
const annotSel = useAnnotationSelection();
|
|
303
|
-
const controller = useViewportControllerContext();
|
|
304
|
-
const vp = controller?.viewport ?? null;
|
|
305
|
-
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
306
|
-
const rafRef = useRef<number | null>(null);
|
|
307
|
-
const dragRef = useRef<{
|
|
308
|
-
pointerId: number;
|
|
309
|
-
startStroke: Stroke;
|
|
310
|
-
corner: Corner;
|
|
311
|
-
} | null>(null);
|
|
312
|
-
// Last pointer position (client coords) during a drag — lets a mid-drag
|
|
313
|
-
// Shift/Alt keydown re-apply the resize without waiting for a pointermove.
|
|
314
|
-
const lastPointRef = useRef<{ x: number; y: number } | null>(null);
|
|
315
|
-
|
|
316
|
-
const selectedId = annotSel.selectedIds.length === 1 ? (annotSel.selectedIds[0] ?? null) : null;
|
|
317
|
-
const selectedStroke: Stroke | null = useMemo(() => {
|
|
318
|
-
if (!selectedId || !store) return null;
|
|
319
|
-
return store.strokes.find((s) => s.id === selectedId) ?? null;
|
|
320
|
-
}, [selectedId, store]);
|
|
321
|
-
|
|
322
|
-
const screenToWorld = useCallback(
|
|
323
|
-
(cx: number, cy: number): [number, number] => {
|
|
324
|
-
const v = vp ?? { x: 0, y: 0, zoom: 1 };
|
|
325
|
-
const z = v.zoom || 1;
|
|
326
|
-
return [(cx - v.x) / z, (cy - v.y) / z];
|
|
327
|
-
},
|
|
328
|
-
[vp]
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
// rAF loop — repositions handles on every frame while a single resizable
|
|
332
|
-
// stroke is selected. Cheaper than wiring pan/zoom observers because the
|
|
333
|
-
// halo overlays already follow the same pattern.
|
|
334
|
-
useEffect(() => {
|
|
335
|
-
if (!selectedStroke || !isResizable(selectedStroke)) {
|
|
336
|
-
const c = containerRef.current;
|
|
337
|
-
if (c) {
|
|
338
|
-
for (const child of Array.from(c.children)) {
|
|
339
|
-
(child as HTMLElement).style.display = 'none';
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
const tick = () => {
|
|
345
|
-
rafRef.current = null;
|
|
346
|
-
const c = containerRef.current;
|
|
347
|
-
if (!c) return;
|
|
348
|
-
const positions = handlePositions(selectedStroke);
|
|
349
|
-
const v = vp ?? { x: 0, y: 0, zoom: 1 };
|
|
350
|
-
const z = v.zoom || 1;
|
|
351
|
-
// Ensure enough handle children exist (each corner = one absolutely-
|
|
352
|
-
// positioned div). 4 for rect/ellipse/pen; 2 for arrow.
|
|
353
|
-
while (c.children.length < positions.length) {
|
|
354
|
-
const handle = document.createElement('div');
|
|
355
|
-
handle.className = 'dc-annot-resize-handle';
|
|
356
|
-
c.appendChild(handle);
|
|
357
|
-
}
|
|
358
|
-
while (c.children.length > positions.length) {
|
|
359
|
-
c.lastChild && c.removeChild(c.lastChild);
|
|
360
|
-
}
|
|
361
|
-
for (let i = 0; i < positions.length; i++) {
|
|
362
|
-
const pos = positions[i];
|
|
363
|
-
const handle = c.children[i] as HTMLElement | undefined;
|
|
364
|
-
if (!pos || !handle) continue;
|
|
365
|
-
const sx = pos.x * z + v.x;
|
|
366
|
-
const sy = pos.y * z + v.y;
|
|
367
|
-
handle.style.display = 'block';
|
|
368
|
-
handle.style.left = `${Math.round(sx - 4)}px`;
|
|
369
|
-
handle.style.top = `${Math.round(sy - 4)}px`;
|
|
370
|
-
handle.dataset.corner = pos.corner;
|
|
371
|
-
}
|
|
372
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
373
|
-
};
|
|
374
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
375
|
-
return () => {
|
|
376
|
-
if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
|
|
377
|
-
};
|
|
378
|
-
}, [selectedStroke, vp]);
|
|
379
|
-
|
|
380
|
-
// Pointer handling — pointerdown on a handle starts a drag; pointermove
|
|
381
|
-
// patches the stroke via `store.updateStroke`; pointerup commits.
|
|
382
|
-
useEffect(() => {
|
|
383
|
-
const c = containerRef.current;
|
|
384
|
-
if (!c) return;
|
|
385
|
-
const onDown = (e: PointerEvent) => {
|
|
386
|
-
const t = e.target as HTMLElement | null;
|
|
387
|
-
if (!t?.classList.contains('dc-annot-resize-handle')) return;
|
|
388
|
-
if (!selectedStroke || !store) return;
|
|
389
|
-
const corner = t.dataset.corner as Corner | undefined;
|
|
390
|
-
if (!corner) return;
|
|
391
|
-
e.preventDefault();
|
|
392
|
-
e.stopPropagation();
|
|
393
|
-
dragRef.current = { pointerId: e.pointerId, startStroke: selectedStroke, corner };
|
|
394
|
-
try {
|
|
395
|
-
t.setPointerCapture(e.pointerId);
|
|
396
|
-
} catch {
|
|
397
|
-
/* some browsers reject capture on synthetic events */
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
const applyResize = (clientX: number, clientY: number, mods: ResizeMods) => {
|
|
401
|
-
const d = dragRef.current;
|
|
402
|
-
if (!d || !store) return;
|
|
403
|
-
const [wx, wy] = screenToWorld(clientX, clientY);
|
|
404
|
-
const patch = resizeStroke(d.startStroke, d.corner, wx, wy, mods);
|
|
405
|
-
if (patch) store.updateStroke(d.startStroke.id, patch);
|
|
406
|
-
};
|
|
407
|
-
const onMove = (e: PointerEvent) => {
|
|
408
|
-
const d = dragRef.current;
|
|
409
|
-
if (!d || e.pointerId !== d.pointerId) return;
|
|
410
|
-
lastPointRef.current = { x: e.clientX, y: e.clientY };
|
|
411
|
-
applyResize(e.clientX, e.clientY, { shift: e.shiftKey, alt: e.altKey });
|
|
412
|
-
};
|
|
413
|
-
// Holding/releasing Shift or Alt mid-drag re-runs the resize at the last
|
|
414
|
-
// known pointer position so the constraint flips live, FigJam-style.
|
|
415
|
-
const onKey = (e: KeyboardEvent) => {
|
|
416
|
-
if (!dragRef.current) return;
|
|
417
|
-
if (e.key !== 'Shift' && e.key !== 'Alt') return;
|
|
418
|
-
const p = lastPointRef.current;
|
|
419
|
-
if (!p) return;
|
|
420
|
-
e.preventDefault();
|
|
421
|
-
applyResize(p.x, p.y, { shift: e.shiftKey, alt: e.altKey });
|
|
422
|
-
};
|
|
423
|
-
const onUp = (e: PointerEvent) => {
|
|
424
|
-
const d = dragRef.current;
|
|
425
|
-
if (!d || e.pointerId !== d.pointerId) return;
|
|
426
|
-
dragRef.current = null;
|
|
427
|
-
lastPointRef.current = null;
|
|
428
|
-
};
|
|
429
|
-
c.addEventListener('pointerdown', onDown);
|
|
430
|
-
document.addEventListener('pointermove', onMove);
|
|
431
|
-
document.addEventListener('pointerup', onUp);
|
|
432
|
-
document.addEventListener('pointercancel', onUp);
|
|
433
|
-
document.addEventListener('keydown', onKey, true);
|
|
434
|
-
document.addEventListener('keyup', onKey, true);
|
|
435
|
-
return () => {
|
|
436
|
-
c.removeEventListener('pointerdown', onDown);
|
|
437
|
-
document.removeEventListener('pointermove', onMove);
|
|
438
|
-
document.removeEventListener('pointerup', onUp);
|
|
439
|
-
document.removeEventListener('pointercancel', onUp);
|
|
440
|
-
document.removeEventListener('keydown', onKey, true);
|
|
441
|
-
document.removeEventListener('keyup', onKey, true);
|
|
442
|
-
};
|
|
443
|
-
}, [selectedStroke, store, screenToWorld]);
|
|
444
|
-
|
|
445
|
-
// Only render when there's exactly one resizable stroke selected. Multi
|
|
446
|
-
// resize is undefined for v1 (no canonical UX); text inherits anchor bbox.
|
|
447
|
-
if (!selectedStroke || !isResizable(selectedStroke)) return null;
|
|
448
|
-
return <div ref={containerRef} aria-hidden="true" />;
|
|
449
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|