@1agh/maude 1.0.11 → 1.3.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 -1
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/index.ts +43 -7
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/annotations-layer.tsx +191 -116
- package/apps/studio/annotations-model.ts +39 -0
- package/apps/studio/annotations-sync.ts +50 -0
- package/apps/studio/api.ts +821 -55
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/bin/annotate.mjs +3 -1
- package/apps/studio/bin/server-up.sh +17 -2
- package/apps/studio/canvas-edit.ts +391 -7
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/canvas-lib.tsx +82 -18
- package/apps/studio/canvas-notice-message.ts +16 -0
- package/apps/studio/canvas-notifications.tsx +19 -0
- package/apps/studio/canvas-shell.tsx +57 -6
- package/apps/studio/client/app.jsx +943 -248
- package/apps/studio/client/apply-edit-request.ts +55 -0
- package/apps/studio/client/export-center.jsx +125 -67
- package/apps/studio/client/github.js +38 -1
- package/apps/studio/client/index-loader.ts +76 -0
- package/apps/studio/client/panels/CloudBar.jsx +52 -6
- package/apps/studio/client/panels/GitPanel.jsx +103 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
- package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
- package/apps/studio/client/panels/SyncPanel.jsx +238 -4
- package/apps/studio/client/panels/TeamProjects.jsx +376 -0
- package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
- package/apps/studio/client/photo-knobs.jsx +3 -0
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/share-dialog.jsx +91 -0
- package/apps/studio/client/share-link.js +94 -0
- package/apps/studio/client/styles/3-shell-maude.css +69 -18
- package/apps/studio/client/styles/4-components.css +44 -1
- package/apps/studio/client/tree-row-menu.jsx +48 -3
- package/apps/studio/client/whats-new.jsx +26 -32
- package/apps/studio/cloud/endpoints.ts +132 -0
- package/apps/studio/collab/index.ts +4 -0
- package/apps/studio/collab/persistence.ts +137 -17
- package/apps/studio/collab/registry.ts +19 -21
- package/apps/studio/collab/room.ts +13 -2
- package/apps/studio/context.ts +59 -0
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1911 -1879
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/sonner.js +1 -0
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/export-dialog.tsx +54 -0
- package/apps/studio/exporters/degraded.ts +19 -3
- package/apps/studio/exporters/ghostscript.ts +194 -0
- package/apps/studio/exporters/pdf-fonts.ts +261 -0
- package/apps/studio/exporters/pdf.ts +152 -3
- package/apps/studio/exporters/remote.ts +36 -1
- package/apps/studio/fs-watch.ts +45 -1
- package/apps/studio/http.ts +296 -19
- package/apps/studio/inspect.ts +19 -0
- package/apps/studio/managed-projects.ts +150 -0
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/notifications.tsx +292 -0
- package/apps/studio/runtime-bundle.ts +2 -0
- package/apps/studio/server.ts +69 -6
- package/apps/studio/sync/accepted-cold-start.ts +225 -0
- package/apps/studio/sync/accepted-link.ts +320 -0
- package/apps/studio/sync/action-stage.ts +343 -0
- package/apps/studio/sync/agent.ts +104 -45
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/cell-file-events.ts +3 -0
- package/apps/studio/sync/codec.ts +192 -34
- package/apps/studio/sync/cold-start-apply.ts +7 -0
- package/apps/studio/sync/cold-start.ts +118 -22
- package/apps/studio/sync/comment-identity.ts +68 -0
- package/apps/studio/sync/connection-state.ts +15 -1
- package/apps/studio/sync/ctl-provider.ts +15 -2
- package/apps/studio/sync/document-discovery.ts +81 -0
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-membership.ts +10 -2
- package/apps/studio/sync/file-plane.ts +926 -47
- package/apps/studio/sync/index.ts +1786 -36
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +150 -22
- package/apps/studio/sync/poke.ts +4 -2
- package/apps/studio/sync/presentation.ts +311 -5
- package/apps/studio/sync/projection.ts +820 -23
- package/apps/studio/sync/repeated-module.ts +73 -0
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/revision-barrier.ts +129 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/seed-repair.ts +46 -0
- package/apps/studio/sync/source-merge.ts +100 -0
- package/apps/studio/sync/source-ops.ts +289 -0
- package/apps/studio/sync/source-recovery.ts +70 -0
- package/apps/studio/sync/source-validation.ts +56 -0
- package/apps/studio/sync/status.ts +201 -6
- package/apps/studio/sync/transaction-client.ts +460 -0
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/sync/writer-registry.ts +236 -0
- package/apps/studio/text-caret.ts +35 -0
- package/apps/studio/undo-hud.tsx +9 -87
- package/apps/studio/use-canvas-media-drop.tsx +4 -39
- package/apps/studio/whats-new.json +173 -0
- package/cli/bin/maude.mjs +2 -0
- package/cli/commands/codex.mjs +191 -0
- package/cli/commands/harness.mjs +1051 -0
- package/cli/commands/help.mjs +13 -0
- package/cli/lib/design-link.mjs +190 -2
- package/cli/lib/harness/capabilities.mjs +302 -0
- package/cli/lib/harness/codex-runtime.mjs +892 -0
- package/cli/lib/harness/compatibility.mjs +27 -0
- package/cli/lib/harness/discover-claude.mjs +1393 -0
- package/cli/lib/harness/managed-state.mjs +1296 -0
- package/cli/lib/harness/model.mjs +86 -0
- package/cli/lib/harness/secrets.mjs +390 -0
- package/cli/lib/harness/targets/codex.mjs +1141 -0
- package/cli/lib/harness/targets/opencode.mjs +807 -0
- package/cli/lib/harness/transaction.mjs +1099 -0
- package/cli/lib/hubs-config.mjs +35 -8
- package/cli/templates/harness/opencode/maude-projector.ts +58 -0
- package/docs/harness-capability-matrix.md +125 -0
- package/docs/harness-environment-projection.md +95 -0
- package/package.json +33 -9
- package/plugins/design/dependencies.json +3 -3
- package/plugins/design/templates/_shell.html +47 -0
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/plugins/flow/dependencies.json +3 -3
- package/apps/studio/.ai/cache/_stats.json +0 -7
- package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
- package/apps/studio/bin/_curl-local.test.mjs +0 -280
- package/apps/studio/bin/_pw-launch.test.mjs +0 -90
- package/apps/studio/bin/_smart-frames.test.mjs +0 -195
- package/apps/studio/bin/_transcribe.test.mjs +0 -80
- package/apps/studio/bun.lock +0 -928
- package/apps/studio/draw/test/animate.test.ts +0 -172
- package/apps/studio/draw/test/brush.test.ts +0 -213
- package/apps/studio/draw/test/composition.test.ts +0 -141
- package/apps/studio/draw/test/geometry.test.ts +0 -199
- package/apps/studio/draw/test/gradient.test.ts +0 -167
- package/apps/studio/draw/test/layout.test.ts +0 -120
- package/apps/studio/draw/test/morph.test.ts +0 -107
- package/apps/studio/draw/test/optimize.test.ts +0 -40
- package/apps/studio/draw/test/palette.test.ts +0 -123
- package/apps/studio/draw/test/primitives.test.ts +0 -129
- package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
- package/apps/studio/draw/test/serialize.test.ts +0 -105
- package/apps/studio/figma/assets.test.ts +0 -464
- package/apps/studio/figma/client.test.ts +0 -395
- package/apps/studio/figma/codegen-client.test.ts +0 -276
- package/apps/studio/figma/codegen-fonts.test.ts +0 -103
- package/apps/studio/figma/codegen-values.test.ts +0 -179
- package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
- package/apps/studio/figma/fig-decode.test.ts +0 -788
- package/apps/studio/figma/fig-differential.test.ts +0 -182
- package/apps/studio/figma/fig-translator.test.ts +0 -192
- package/apps/studio/figma/fig-vector.test.ts +0 -113
- package/apps/studio/figma/from-codegen.test.ts +0 -408
- package/apps/studio/figma/sanitize.test.ts +0 -325
- package/apps/studio/figma/tailwind-map.test.ts +0 -142
- package/apps/studio/figma/to-artboard.test.ts +0 -808
- package/apps/studio/figma/to-render.test.ts +0 -180
- package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
- package/apps/studio/figma/to-strokes.test.ts +0 -705
- package/apps/studio/figma/to-tokens.test.ts +0 -321
- package/apps/studio/figma/url.test.ts +0 -167
- package/apps/studio/footage/schema.test.ts +0 -287
- package/apps/studio/generation/adapters/groq.test.ts +0 -80
- package/apps/studio/generation/audio-library.test.ts +0 -95
- package/apps/studio/generation/captions.test.ts +0 -78
- package/apps/studio/generation/elevenlabs.test.ts +0 -223
- package/apps/studio/generation/gemini.test.ts +0 -350
- package/apps/studio/generation/jobs.test.ts +0 -128
- package/apps/studio/generation/keys.test.ts +0 -75
- package/apps/studio/generation/prefs.test.ts +0 -95
- package/apps/studio/generation/types.test.ts +0 -123
- package/apps/studio/generation/whisper-models.test.ts +0 -179
- package/apps/studio/test/_helpers.ts +0 -108
- package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
- package/apps/studio/test/acp-activity.test.ts +0 -154
- package/apps/studio/test/acp-ai-activity.test.ts +0 -182
- package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
- package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
- package/apps/studio/test/acp-branch-guard.test.ts +0 -123
- package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
- package/apps/studio/test/acp-bridge.test.ts +0 -545
- package/apps/studio/test/acp-capabilities.test.ts +0 -123
- package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
- package/apps/studio/test/acp-commands.test.ts +0 -119
- package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
- package/apps/studio/test/acp-elicitation.test.ts +0 -251
- package/apps/studio/test/acp-env.test.ts +0 -95
- package/apps/studio/test/acp-mode-banner.test.ts +0 -45
- package/apps/studio/test/acp-origin-gate.test.ts +0 -160
- package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
- package/apps/studio/test/acp-permission.test.ts +0 -278
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
- package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
- package/apps/studio/test/acp-session-plugins.test.ts +0 -209
- package/apps/studio/test/acp-toolgroup.test.ts +0 -76
- package/apps/studio/test/acp-transcript-view.test.ts +0 -71
- package/apps/studio/test/acp-transcript.test.ts +0 -238
- package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
- package/apps/studio/test/acp-usage.test.ts +0 -143
- package/apps/studio/test/acp-write-gate.test.ts +0 -323
- package/apps/studio/test/acp-write-scope.test.ts +0 -533
- package/apps/studio/test/active-state.test.ts +0 -186
- package/apps/studio/test/activity.test.ts +0 -189
- package/apps/studio/test/ai-activity.test.ts +0 -113
- package/apps/studio/test/annotate-write.test.ts +0 -1074
- package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
- package/apps/studio/test/annotations-align.test.ts +0 -88
- package/apps/studio/test/annotations-api.test.ts +0 -145
- package/apps/studio/test/annotations-bindings.test.ts +0 -262
- package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
- package/apps/studio/test/annotations-groups.test.ts +0 -231
- package/apps/studio/test/annotations-layer.test.ts +0 -1011
- package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
- package/apps/studio/test/annotations-snap.test.ts +0 -135
- package/apps/studio/test/annotations-text-format.test.ts +0 -396
- package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
- package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
- package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
- package/apps/studio/test/artboard-kinds.test.tsx +0 -83
- package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
- package/apps/studio/test/asset-api.test.ts +0 -149
- package/apps/studio/test/assets-s3.test.ts +0 -249
- package/apps/studio/test/binary-smoke.test.ts +0 -47
- package/apps/studio/test/boot-self-heal.test.ts +0 -87
- package/apps/studio/test/browse-posture.test.tsx +0 -107
- package/apps/studio/test/bundle-smoke.test.ts +0 -46
- package/apps/studio/test/camera-reveal.test.tsx +0 -173
- package/apps/studio/test/canvas-artifacts.test.ts +0 -125
- package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
- package/apps/studio/test/canvas-build.test.ts +0 -141
- package/apps/studio/test/canvas-create-api.test.ts +0 -676
- package/apps/studio/test/canvas-cursors.test.ts +0 -164
- package/apps/studio/test/canvas-edit.test.ts +0 -470
- package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
- package/apps/studio/test/canvas-header.test.ts +0 -127
- package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
- package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
- package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
- package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
- package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
- package/apps/studio/test/canvas-list-watch.test.ts +0 -388
- package/apps/studio/test/canvas-media-drop.test.ts +0 -187
- package/apps/studio/test/canvas-meta-api.test.ts +0 -707
- package/apps/studio/test/canvas-move-api.test.ts +0 -587
- package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
- package/apps/studio/test/canvas-pipeline.test.ts +0 -180
- package/apps/studio/test/canvas-rects.test.ts +0 -198
- package/apps/studio/test/canvas-reorder.test.ts +0 -211
- package/apps/studio/test/canvas-route.test.ts +0 -179
- package/apps/studio/test/canvas-shell-base.test.ts +0 -107
- package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
- package/apps/studio/test/canvas-url.test.ts +0 -138
- package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
- package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
- package/apps/studio/test/capture-core.test.ts +0 -32
- package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
- package/apps/studio/test/chat-attachments.test.ts +0 -117
- package/apps/studio/test/chat-context.test.ts +0 -129
- package/apps/studio/test/chat-markdown.test.tsx +0 -58
- package/apps/studio/test/clip-addressing.test.ts +0 -738
- package/apps/studio/test/clip-ops.test.ts +0 -922
- package/apps/studio/test/cloud-build.test.ts +0 -95
- package/apps/studio/test/cloud-connect-note.test.ts +0 -135
- package/apps/studio/test/cloud-endpoints.test.ts +0 -590
- package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
- package/apps/studio/test/cloud-history-posture.test.ts +0 -230
- package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
- package/apps/studio/test/cloud-renew.test.ts +0 -205
- package/apps/studio/test/cloud-session-role.test.ts +0 -161
- package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
- package/apps/studio/test/cold-start-apply.test.ts +0 -303
- package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
- package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
- package/apps/studio/test/collab-bridge.test.ts +0 -79
- package/apps/studio/test/collab-loopback.test.ts +0 -63
- package/apps/studio/test/collab-origin-gate.test.ts +0 -323
- package/apps/studio/test/collab-protocol.test.ts +0 -146
- package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
- package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
- package/apps/studio/test/collab-room.test.ts +0 -188
- package/apps/studio/test/collab-session-survival.test.tsx +0 -176
- package/apps/studio/test/collab-stress.test.ts +0 -129
- package/apps/studio/test/comment-mount.test.ts +0 -97
- package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
- package/apps/studio/test/comments-api.test.ts +0 -277
- package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
- package/apps/studio/test/comments-overlay.test.ts +0 -117
- package/apps/studio/test/compile-entry.test.ts +0 -133
- package/apps/studio/test/component-map.test.ts +0 -48
- package/apps/studio/test/config-projection.test.ts +0 -104
- package/apps/studio/test/config-reload.test.ts +0 -230
- package/apps/studio/test/config-version.test.ts +0 -88
- package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
- package/apps/studio/test/convert-to-absolute.test.ts +0 -333
- package/apps/studio/test/credential-grammar.test.ts +0 -107
- package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
- package/apps/studio/test/csrf-write-guard.test.ts +0 -141
- package/apps/studio/test/debug-bundle.test.ts +0 -105
- package/apps/studio/test/design-setup-readiness.test.ts +0 -155
- package/apps/studio/test/detach-component.test.ts +0 -94
- package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
- package/apps/studio/test/dom-selection.test.ts +0 -198
- package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
- package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
- package/apps/studio/test/edit-persistence.test.ts +0 -91
- package/apps/studio/test/edit-scope-api.test.ts +0 -119
- package/apps/studio/test/edit-source-command.test.ts +0 -142
- package/apps/studio/test/edit-suppress.test.ts +0 -170
- package/apps/studio/test/editing-presence.test.ts +0 -103
- package/apps/studio/test/element-resize.test.ts +0 -136
- package/apps/studio/test/element-structural-api.test.ts +0 -434
- package/apps/studio/test/element-structural-edit.test.ts +0 -750
- package/apps/studio/test/ensure-browser.test.ts +0 -92
- package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
- package/apps/studio/test/export-assemble.test.ts +0 -53
- package/apps/studio/test/export-browser-lane.test.ts +0 -126
- package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
- package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
- package/apps/studio/test/export-center.test.tsx +0 -287
- package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
- package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
- package/apps/studio/test/export-lane.test.ts +0 -245
- package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
- package/apps/studio/test/exporters/canva.test.ts +0 -63
- package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
- package/apps/studio/test/exporters/endpoint.test.ts +0 -121
- package/apps/studio/test/exporters/history.test.ts +0 -107
- package/apps/studio/test/exporters/html.test.ts +0 -26
- package/apps/studio/test/exporters/jobs.test.ts +0 -342
- package/apps/studio/test/exporters/pdf.test.ts +0 -85
- package/apps/studio/test/exporters/png.test.ts +0 -103
- package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
- package/apps/studio/test/exporters/pptx.test.ts +0 -31
- package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
- package/apps/studio/test/exporters/runtime.test.ts +0 -59
- package/apps/studio/test/exporters/scope.test.ts +0 -292
- package/apps/studio/test/exporters/svg.test.ts +0 -29
- package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
- package/apps/studio/test/exporters/zip.test.ts +0 -104
- package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
- package/apps/studio/test/figjam-v3-model.test.ts +0 -342
- package/apps/studio/test/figma-explode.test.ts +0 -438
- package/apps/studio/test/figma-provenance.test.ts +0 -108
- package/apps/studio/test/figma-routes.test.ts +0 -294
- package/apps/studio/test/file-lock.test.ts +0 -84
- package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
- package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
- package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
- package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
- package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
- package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
- package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
- package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
- package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
- package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
- package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
- package/apps/studio/test/footage-store.test.ts +0 -100
- package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
- package/apps/studio/test/fs-watch.test.ts +0 -41
- package/apps/studio/test/gemma-models.test.ts +0 -245
- package/apps/studio/test/generate-route.test.ts +0 -106
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +0 -341
- package/apps/studio/test/git-cloud-posture.test.ts +0 -59
- package/apps/studio/test/git-grouping.test.ts +0 -106
- package/apps/studio/test/git-lifecycle.test.ts +0 -100
- package/apps/studio/test/git-log-format.test.ts +0 -95
- package/apps/studio/test/git-safe-rel.test.ts +0 -132
- package/apps/studio/test/git-system-engine.test.ts +0 -135
- package/apps/studio/test/git-watch.test.ts +0 -97
- package/apps/studio/test/github-api.test.ts +0 -577
- package/apps/studio/test/grid-track-handles.test.ts +0 -160
- package/apps/studio/test/handoff-static-frames.test.ts +0 -215
- package/apps/studio/test/handoff.test.ts +0 -329
- package/apps/studio/test/history-rollback.test.ts +0 -88
- package/apps/studio/test/hmr-broadcast.test.ts +0 -236
- package/apps/studio/test/hmr-classify.test.ts +0 -87
- package/apps/studio/test/hub-link.test.ts +0 -80
- package/apps/studio/test/identity-cache.test.ts +0 -83
- package/apps/studio/test/import-asset-browser.test.ts +0 -64
- package/apps/studio/test/import-asset-route.test.ts +0 -71
- package/apps/studio/test/import-asset.test.ts +0 -427
- package/apps/studio/test/import-brand-browser.test.ts +0 -88
- package/apps/studio/test/import-brand-route.test.ts +0 -120
- package/apps/studio/test/import-brand.test.ts +0 -206
- package/apps/studio/test/import-figma.test.ts +0 -667
- package/apps/studio/test/import-tokens.test.ts +0 -722
- package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
- package/apps/studio/test/input-router.test.ts +0 -757
- package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
- package/apps/studio/test/inspect-selections.test.ts +0 -219
- package/apps/studio/test/knob-props-authored.test.ts +0 -87
- package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
- package/apps/studio/test/locator.test.ts +0 -214
- package/apps/studio/test/marquee-overlay.test.ts +0 -94
- package/apps/studio/test/media-commit-chain.test.ts +0 -210
- package/apps/studio/test/mode-toggle.test.tsx +0 -226
- package/apps/studio/test/motion-sample.test.ts +0 -35
- package/apps/studio/test/move-artboards-command.test.ts +0 -108
- package/apps/studio/test/participants-chrome.test.ts +0 -65
- package/apps/studio/test/paths.test.ts +0 -88
- package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
- package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
- package/apps/studio/test/perf-harness.ts +0 -193
- package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
- package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
- package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
- package/apps/studio/test/photo-edit-api.test.ts +0 -201
- package/apps/studio/test/photo-filters.test.ts +0 -149
- package/apps/studio/test/photo-pipeline.test.ts +0 -106
- package/apps/studio/test/photo-taxonomy.test.ts +0 -96
- package/apps/studio/test/print-marks.test.ts +0 -113
- package/apps/studio/test/print-units.test.ts +0 -173
- package/apps/studio/test/read-annotations.test.ts +0 -727
- package/apps/studio/test/read-only-gate.test.ts +0 -188
- package/apps/studio/test/read-only-role.test.ts +0 -95
- package/apps/studio/test/readiness.test.ts +0 -337
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
- package/apps/studio/test/reorder-api.test.ts +0 -210
- package/apps/studio/test/repo-concurrency.test.ts +0 -146
- package/apps/studio/test/repo-lock.test.ts +0 -350
- package/apps/studio/test/report-proxy.test.ts +0 -125
- package/apps/studio/test/ripple.test.ts +0 -197
- package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
- package/apps/studio/test/runtime-bundle.test.ts +0 -69
- package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
- package/apps/studio/test/scaffold-design.test.ts +0 -122
- package/apps/studio/test/server-lifecycle.test.ts +0 -38
- package/apps/studio/test/session-runtime-state.test.ts +0 -226
- package/apps/studio/test/session-scope.test.ts +0 -100
- package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
- package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
- package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
- package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
- package/apps/studio/test/shared-doc-projection.test.ts +0 -238
- package/apps/studio/test/shell-importmap.test.ts +0 -56
- package/apps/studio/test/sizing-mode.test.ts +0 -102
- package/apps/studio/test/slash-commands.test.ts +0 -117
- package/apps/studio/test/snap-distance-pill.test.ts +0 -68
- package/apps/studio/test/spacing-handles.test.ts +0 -138
- package/apps/studio/test/specimen-select.test.ts +0 -88
- package/apps/studio/test/sync-agent.test.ts +0 -660
- package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
- package/apps/studio/test/sync-asset-push.test.ts +0 -931
- package/apps/studio/test/sync-atomic-write.test.ts +0 -62
- package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
- package/apps/studio/test/sync-autocommit.test.ts +0 -537
- package/apps/studio/test/sync-canvas-path.test.ts +0 -200
- package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
- package/apps/studio/test/sync-codec.test.ts +0 -230
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
- package/apps/studio/test/sync-cold-start.test.ts +0 -304
- package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
- package/apps/studio/test/sync-connection-state.test.ts +0 -337
- package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
- package/apps/studio/test/sync-decide-file.test.ts +0 -420
- package/apps/studio/test/sync-doc-name.test.ts +0 -281
- package/apps/studio/test/sync-echo-guard.test.ts +0 -96
- package/apps/studio/test/sync-file-ledger.test.ts +0 -334
- package/apps/studio/test/sync-file-membership.test.ts +0 -347
- package/apps/studio/test/sync-file-plane.test.ts +0 -976
- package/apps/studio/test/sync-file-pull.test.ts +0 -333
- package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
- package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
- package/apps/studio/test/sync-hardening.test.ts +0 -586
- package/apps/studio/test/sync-hub-listing.test.ts +0 -46
- package/apps/studio/test/sync-hubs-config.test.ts +0 -133
- package/apps/studio/test/sync-incident-replay.test.ts +0 -211
- package/apps/studio/test/sync-journal.test.ts +0 -176
- package/apps/studio/test/sync-meta-codec.test.ts +0 -199
- package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
- package/apps/studio/test/sync-move-retirement.test.ts +0 -231
- package/apps/studio/test/sync-panel-surface.test.ts +0 -153
- package/apps/studio/test/sync-path-pull.test.ts +0 -531
- package/apps/studio/test/sync-presentation.test.ts +0 -262
- package/apps/studio/test/sync-pull-budget.test.ts +0 -169
- package/apps/studio/test/sync-remote-docs.test.ts +0 -333
- package/apps/studio/test/sync-resync-routes.test.ts +0 -168
- package/apps/studio/test/sync-runtime.test.ts +0 -1675
- package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
- package/apps/studio/test/sync-settings-routes.test.ts +0 -195
- package/apps/studio/test/sync-settings.test.ts +0 -151
- package/apps/studio/test/sync-status.test.ts +0 -261
- package/apps/studio/test/sync-supervisor.test.ts +0 -262
- package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
- package/apps/studio/test/sync-trash.test.ts +0 -132
- package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
- package/apps/studio/test/sync-untrusted.test.ts +0 -103
- package/apps/studio/test/system-endpoint.test.ts +0 -144
- package/apps/studio/test/text-editability-stamp.test.ts +0 -131
- package/apps/studio/test/timeline-comp-target.test.ts +0 -139
- package/apps/studio/test/timeline-media-cache.test.ts +0 -41
- package/apps/studio/test/timeline-parse.test.ts +0 -184
- package/apps/studio/test/timeline-scale.test.ts +0 -65
- package/apps/studio/test/timeline-snap.test.ts +0 -85
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
- package/apps/studio/test/tool-palette.test.tsx +0 -55
- package/apps/studio/test/tour-overlay.test.tsx +0 -90
- package/apps/studio/test/trusted-request-host.test.ts +0 -66
- package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
- package/apps/studio/test/undo-stack.test.ts +0 -244
- package/apps/studio/test/use-agent-presence.test.tsx +0 -117
- package/apps/studio/test/use-annotation-resize.test.ts +0 -402
- package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
- package/apps/studio/test/use-artboard-drag.test.ts +0 -324
- package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
- package/apps/studio/test/use-collab.test.ts +0 -0
- package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
- package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
- package/apps/studio/test/use-selection-set.test.tsx +0 -166
- package/apps/studio/test/use-snap-guides.test.ts +0 -271
- package/apps/studio/test/use-tool-mode.test.tsx +0 -186
- package/apps/studio/test/use-undo-stack.test.tsx +0 -223
- package/apps/studio/test/video-asset.test.ts +0 -163
- package/apps/studio/test/video-comp-fixture.test.ts +0 -50
- package/apps/studio/test/video-comp.test.ts +0 -270
- package/apps/studio/test/video-encode-lib.test.ts +0 -63
- package/apps/studio/test/video-render-bridge.test.ts +0 -171
- package/apps/studio/test/whats-new.test.ts +0 -113
- package/apps/studio/test/whisper-setup.test.ts +0 -97
- package/apps/studio/test/workspace-containment.test.ts +0 -382
- package/apps/studio/test/workspace-signin.test.ts +0 -292
- package/apps/studio/test/ws-handshake.test.ts +0 -33
- package/cli/commands/cache.test.mjs +0 -166
- package/cli/commands/design-link.test.mjs +0 -419
- package/cli/commands/design.test.mjs +0 -240
- package/cli/commands/doctor.test.mjs +0 -237
- package/cli/commands/hub-workspace.test.mjs +0 -275
- package/cli/commands/hub.test.mjs +0 -225
- package/cli/commands/kg.test.mjs +0 -142
- package/cli/commands/preflight.test.mjs +0 -46
- package/cli/lib/argv.test.mjs +0 -46
- package/cli/lib/cache.test.mjs +0 -303
- package/cli/lib/cell-plan.test.mjs +0 -225
- package/cli/lib/config-lint.test.mjs +0 -117
- package/cli/lib/ddr-to-kgai.test.mjs +0 -118
- package/cli/lib/design-ownership.test.mjs +0 -329
- package/cli/lib/fetch-asset.test.mjs +0 -213
- package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
- package/cli/lib/figma-import-controls.test.mjs +0 -70
- package/cli/lib/flow-design-integration.test.mjs +0 -165
- package/cli/lib/gitignore-block.test.mjs +0 -136
- package/cli/lib/gitignore-drift.test.mjs +0 -156
- package/cli/lib/hubs-config.test.mjs +0 -165
- package/cli/lib/pkg-root.test.mjs +0 -111
- package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
- package/cli/lib/plugin-name-namespace.test.mjs +0 -71
- package/cli/lib/reconstruct-toolset.test.mjs +0 -194
- package/cli/lib/stack-detect.test.mjs +0 -121
- package/cli/lib/update-check.test.mjs +0 -32
- package/cli/lib/workspace-plan.test.mjs +0 -699
|
@@ -1,757 +0,0 @@
|
|
|
1
|
-
// input-router — Phase 4.1 Task 1. Pure classifier table tests.
|
|
2
|
-
|
|
3
|
-
import { describe, expect, test } from 'bun:test';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
type ClassifyInput,
|
|
7
|
-
classify,
|
|
8
|
-
crossedDragThreshold,
|
|
9
|
-
DRAG_THRESHOLD_PX,
|
|
10
|
-
isArtboardDragChrome,
|
|
11
|
-
isEditableTarget,
|
|
12
|
-
isOverlayTarget,
|
|
13
|
-
type Tool,
|
|
14
|
-
yieldsClickToArtboardChrome,
|
|
15
|
-
yieldsToArtboardDrag,
|
|
16
|
-
} from '../input-router.tsx';
|
|
17
|
-
|
|
18
|
-
const base = (over: Partial<ClassifyInput>): ClassifyInput => ({
|
|
19
|
-
type: 'pointermove',
|
|
20
|
-
activeTool: 'move' as Tool,
|
|
21
|
-
...over,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('input-router / pointermove — move (select) tool', () => {
|
|
25
|
-
// feature-4 (browse/move split, DDR-187) — the Move tool is now a SELECT
|
|
26
|
-
// tool: a bare hover paints a TOP-level preview halo (Figma's hover outline),
|
|
27
|
-
// Cmd-hover previews the deepest element.
|
|
28
|
-
test('bare hover → hover preview with deep=false (top-level outline)', () => {
|
|
29
|
-
const action = classify(base({ type: 'pointermove', clientX: 10, clientY: 20 }));
|
|
30
|
-
expect(action).toMatchObject({ kind: 'hover', deep: false });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('cmd-hover → hover preview with deep=true', () => {
|
|
34
|
-
const action = classify(base({ type: 'pointermove', metaKey: true, clientX: 5, clientY: 6 }));
|
|
35
|
-
expect(action).toMatchObject({ kind: 'hover', deep: true });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('ctrl-hover (windows/linux) → hover with deep=true', () => {
|
|
39
|
-
const action = classify(base({ type: 'pointermove', ctrlKey: true }));
|
|
40
|
-
expect(action).toMatchObject({ kind: 'hover', deep: true });
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('hover in hand tool → no-op', () => {
|
|
44
|
-
const action = classify(base({ type: 'pointermove', activeTool: 'hand' }));
|
|
45
|
-
expect(action.kind).toBe('no-op');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('hover in comment tool → hover with deep=true (preview deepest)', () => {
|
|
49
|
-
const action = classify(
|
|
50
|
-
base({ type: 'pointermove', activeTool: 'comment', clientX: 1, clientY: 2 })
|
|
51
|
-
);
|
|
52
|
-
expect(action).toEqual({ kind: 'hover', deep: true, clientX: 1, clientY: 2 });
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('input-router / pointerdown — move (select) tool ladder', () => {
|
|
57
|
-
// feature-4 (browse/move split, DDR-187) — the full Figma select ladder.
|
|
58
|
-
test('bare left-click → select replace, deep=false (TOP-level object)', () => {
|
|
59
|
-
const action = classify(base({ type: 'pointerdown', button: 0, clientX: 100, clientY: 200 }));
|
|
60
|
-
expect(action).toEqual({
|
|
61
|
-
kind: 'select',
|
|
62
|
-
mode: 'replace',
|
|
63
|
-
deep: false,
|
|
64
|
-
clientX: 100,
|
|
65
|
-
clientY: 200,
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('shift+left-click (no cmd) → select add, deep=false (add TOP-level)', () => {
|
|
70
|
-
const action = classify(base({ type: 'pointerdown', button: 0, shiftKey: true }));
|
|
71
|
-
expect(action).toMatchObject({ kind: 'select', mode: 'add', deep: false });
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('cmd+left-click → select replace, deep=true (nested single)', () => {
|
|
75
|
-
const action = classify(
|
|
76
|
-
base({ type: 'pointerdown', button: 0, metaKey: true, clientX: 11, clientY: 22 })
|
|
77
|
-
);
|
|
78
|
-
expect(action).toEqual({
|
|
79
|
-
kind: 'select',
|
|
80
|
-
mode: 'replace',
|
|
81
|
-
deep: true,
|
|
82
|
-
clientX: 11,
|
|
83
|
-
clientY: 22,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('cmd+shift+left-click → select add, deep=true (multi nested)', () => {
|
|
88
|
-
const action = classify(
|
|
89
|
-
base({ type: 'pointerdown', button: 0, metaKey: true, shiftKey: true })
|
|
90
|
-
);
|
|
91
|
-
expect(action).toMatchObject({ kind: 'select', mode: 'add', deep: true });
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('ctrl+left-click (linux/windows cmd-equivalent) → select replace deep', () => {
|
|
95
|
-
const action = classify(base({ type: 'pointerdown', button: 0, ctrlKey: true }));
|
|
96
|
-
expect(action).toMatchObject({ kind: 'select', mode: 'replace', deep: true });
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('input-router / pointerdown — non-left buttons', () => {
|
|
101
|
-
test('middle-button → no-op (viewport-controller owns it)', () => {
|
|
102
|
-
const action = classify(base({ type: 'pointerdown', button: 1 }));
|
|
103
|
-
expect(action.kind).toBe('no-op');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('right-button → context-menu', () => {
|
|
107
|
-
const action = classify(base({ type: 'pointerdown', button: 2, clientX: 50, clientY: 60 }));
|
|
108
|
-
expect(action).toEqual({ kind: 'context-menu', clientX: 50, clientY: 60 });
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('space-held + left-click → no-op (viewport-controller pans)', () => {
|
|
112
|
-
const action = classify(base({ type: 'pointerdown', button: 0, spaceHeld: true }));
|
|
113
|
-
expect(action.kind).toBe('no-op');
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('input-router / pointerdown — tool-aware', () => {
|
|
118
|
-
test('comment tool + bare click → drop-comment', () => {
|
|
119
|
-
const action = classify(
|
|
120
|
-
base({ type: 'pointerdown', button: 0, activeTool: 'comment', clientX: 12, clientY: 34 })
|
|
121
|
-
);
|
|
122
|
-
expect(action).toEqual({ kind: 'drop-comment', clientX: 12, clientY: 34 });
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test('comment tool + shift+click → drop-comment (modifier ignored for now)', () => {
|
|
126
|
-
const action = classify(
|
|
127
|
-
base({ type: 'pointerdown', button: 0, activeTool: 'comment', shiftKey: true })
|
|
128
|
-
);
|
|
129
|
-
expect(action.kind).toBe('drop-comment');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('hand tool + bare click → no-op (viewport-controller claims the drag)', () => {
|
|
133
|
-
const action = classify(base({ type: 'pointerdown', button: 0, activeTool: 'hand' }));
|
|
134
|
-
expect(action.kind).toBe('no-op');
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('hand tool + cmd+click → still no-op (no select in hand mode)', () => {
|
|
138
|
-
const action = classify(
|
|
139
|
-
base({ type: 'pointerdown', button: 0, activeTool: 'hand', metaKey: true })
|
|
140
|
-
);
|
|
141
|
-
expect(action.kind).toBe('no-op');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
146
|
-
// feature-4 (browse/move split, DDR-187) — POSTURE INVARIANTS. The browse tool
|
|
147
|
-
// is the boot default and must be a PURE native pass-through: it claims NOTHING,
|
|
148
|
-
// so a freshly-opened mock stays alive (buttons click, links follow). This
|
|
149
|
-
// promotes the old `input-router.tsx` comment-invariant ("bare clicks pass
|
|
150
|
-
// through") into an enforced gate. If any of these flip to a claim, an existing
|
|
151
|
-
// canvas silently loses its native interactivity on boot.
|
|
152
|
-
describe('input-router / browse tool — pure pass-through (boot default)', () => {
|
|
153
|
-
const browse = (over: Partial<ClassifyInput>): ClassifyInput =>
|
|
154
|
-
base({ activeTool: 'browse', ...over });
|
|
155
|
-
|
|
156
|
-
test('bare left-click → no-op (native button/link/input fires)', () => {
|
|
157
|
-
expect(classify(browse({ type: 'pointerdown', button: 0, clientX: 5, clientY: 6 })).kind).toBe(
|
|
158
|
-
'no-op'
|
|
159
|
-
);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test('cmd+left-click → select deep (the escape hatch; consumer flips tool to move)', () => {
|
|
163
|
-
// User steer 2026-07-19 — "I clicked to edit" shouldn't require V first.
|
|
164
|
-
expect(classify(browse({ type: 'pointerdown', button: 0, metaKey: true }))).toMatchObject({
|
|
165
|
-
kind: 'select',
|
|
166
|
-
mode: 'replace',
|
|
167
|
-
deep: true,
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('shift+left-click → no-op (Shift alone is NOT the escape hatch)', () => {
|
|
172
|
-
expect(classify(browse({ type: 'pointerdown', button: 0, shiftKey: true })).kind).toBe('no-op');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('bare hover → no-op; Cmd-hover previews deep (escape-hatch affordance)', () => {
|
|
176
|
-
expect(classify(browse({ type: 'pointermove', clientX: 1, clientY: 2 })).kind).toBe('no-op');
|
|
177
|
-
expect(classify(browse({ type: 'pointermove', metaKey: true }))).toMatchObject({
|
|
178
|
-
kind: 'hover',
|
|
179
|
-
deep: true,
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test('right-click → still context-menu (chrome, not canvas content)', () => {
|
|
184
|
-
expect(classify(browse({ type: 'pointerdown', button: 2, clientX: 3, clientY: 4 })).kind).toBe(
|
|
185
|
-
'context-menu'
|
|
186
|
-
);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test('V keydown → tool move (the router stays live in browse so V switches)', () => {
|
|
190
|
-
expect(classify(browse({ type: 'keydown', key: 'v' }))).toEqual({
|
|
191
|
-
kind: 'tool',
|
|
192
|
-
tool: 'move',
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// The select action must NEVER be produced from a non-select tool on a bare
|
|
198
|
-
// gesture. Guards the "select leaked into comment/draw/hand/browse" regression.
|
|
199
|
-
describe('input-router / select never leaks into non-select tools', () => {
|
|
200
|
-
const nonSelectBare: Tool[] = ['browse', 'hand', 'comment', 'pen', 'shape', 'sticky', 'eraser'];
|
|
201
|
-
for (const activeTool of nonSelectBare) {
|
|
202
|
-
test(`bare left-click in ${activeTool} → not a select`, () => {
|
|
203
|
-
const action = classify(base({ type: 'pointerdown', button: 0, activeTool }));
|
|
204
|
-
expect(action.kind).not.toBe('select');
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe('input-router / contextmenu event', () => {
|
|
210
|
-
test('always opens menu + carries cursor coords', () => {
|
|
211
|
-
const action = classify(base({ type: 'contextmenu', clientX: 77, clientY: 88 }));
|
|
212
|
-
expect(action).toEqual({ kind: 'context-menu', clientX: 77, clientY: 88 });
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('input-router / keydown — tool letters', () => {
|
|
217
|
-
test('V → tool move', () => {
|
|
218
|
-
const action = classify(base({ type: 'keydown', key: 'v' }));
|
|
219
|
-
expect(action).toEqual({ kind: 'tool', tool: 'move' });
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test('uppercase V (Shift held during letter)', () => {
|
|
223
|
-
const action = classify(base({ type: 'keydown', key: 'V' }));
|
|
224
|
-
expect(action).toEqual({ kind: 'tool', tool: 'move' });
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test('H → tool hand', () => {
|
|
228
|
-
expect(classify(base({ type: 'keydown', key: 'h' }))).toEqual({
|
|
229
|
-
kind: 'tool',
|
|
230
|
-
tool: 'hand',
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('C → tool comment', () => {
|
|
235
|
-
expect(classify(base({ type: 'keydown', key: 'c' }))).toEqual({
|
|
236
|
-
kind: 'tool',
|
|
237
|
-
tool: 'comment',
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
test('Escape → escape action', () => {
|
|
242
|
-
expect(classify(base({ type: 'keydown', key: 'Escape' }))).toEqual({
|
|
243
|
-
kind: 'escape',
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
test('Cmd+C with modifier → no-op (browser copy / viewport-controller)', () => {
|
|
248
|
-
const action = classify(base({ type: 'keydown', key: 'c', metaKey: true }));
|
|
249
|
-
expect(action.kind).toBe('no-op');
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
test('Cmd+V with modifier → no-op', () => {
|
|
253
|
-
const action = classify(base({ type: 'keydown', key: 'v', metaKey: true }));
|
|
254
|
-
expect(action.kind).toBe('no-op');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test('Cmd+Escape → escape (cancels regardless of modifiers)', () => {
|
|
258
|
-
const action = classify(base({ type: 'keydown', key: 'Escape', metaKey: true }));
|
|
259
|
-
expect(action.kind).toBe('escape');
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test('V in input field → no-op', () => {
|
|
263
|
-
const action = classify(base({ type: 'keydown', key: 'v', isEditable: true }));
|
|
264
|
-
expect(action.kind).toBe('no-op');
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test('Other letters → no-op', () => {
|
|
268
|
-
expect(classify(base({ type: 'keydown', key: 'x' })).kind).toBe('no-op');
|
|
269
|
-
expect(classify(base({ type: 'keydown', key: 'Tab' })).kind).toBe('no-op');
|
|
270
|
-
expect(classify(base({ type: 'keydown', key: 'Enter' })).kind).toBe('no-op');
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
describe('input-router / keydown — Phase 5 draw tools', () => {
|
|
275
|
-
test('B → tool pen', () => {
|
|
276
|
-
expect(classify(base({ type: 'keydown', key: 'b' }))).toEqual({
|
|
277
|
-
kind: 'tool',
|
|
278
|
-
tool: 'pen',
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
test('R → tool shape (Phase 24 — single Shape tool)', () => {
|
|
283
|
-
expect(classify(base({ type: 'keydown', key: 'r' }))).toEqual({
|
|
284
|
-
kind: 'tool',
|
|
285
|
-
tool: 'shape',
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
test('A → tool arrow', () => {
|
|
290
|
-
expect(classify(base({ type: 'keydown', key: 'a' }))).toEqual({
|
|
291
|
-
kind: 'tool',
|
|
292
|
-
tool: 'arrow',
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
test('E → tool eraser', () => {
|
|
297
|
-
expect(classify(base({ type: 'keydown', key: 'e' }))).toEqual({
|
|
298
|
-
kind: 'tool',
|
|
299
|
-
tool: 'eraser',
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
test('O → tool shape (Phase 24 — legacy ellipse key now arms Shape)', () => {
|
|
304
|
-
expect(classify(base({ type: 'keydown', key: 'o' }))).toEqual({
|
|
305
|
-
kind: 'tool',
|
|
306
|
-
tool: 'shape',
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
test('shape tool owns bare pointer events (SVG overlay claims)', () => {
|
|
311
|
-
expect(classify(base({ type: 'pointerdown', activeTool: 'shape', button: 0 })).kind).toBe(
|
|
312
|
-
'no-op'
|
|
313
|
-
);
|
|
314
|
-
expect(classify(base({ type: 'pointermove', activeTool: 'shape' })).kind).toBe('no-op');
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
test('N → tool sticky (Phase 21)', () => {
|
|
318
|
-
expect(classify(base({ type: 'keydown', key: 'n' }))).toEqual({
|
|
319
|
-
kind: 'tool',
|
|
320
|
-
tool: 'sticky',
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
test('T → tool text (Phase 21)', () => {
|
|
325
|
-
expect(classify(base({ type: 'keydown', key: 't' }))).toEqual({
|
|
326
|
-
kind: 'tool',
|
|
327
|
-
tool: 'text',
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test('uppercase B (shift held) — still maps to pen (lowercased)', () => {
|
|
332
|
-
expect(classify(base({ type: 'keydown', key: 'B', shiftKey: true }))).toEqual({
|
|
333
|
-
kind: 'tool',
|
|
334
|
-
tool: 'pen',
|
|
335
|
-
});
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
test('Cmd+B (modifier-held) → no-op so the browser keeps it', () => {
|
|
339
|
-
expect(classify(base({ type: 'keydown', key: 'b', metaKey: true })).kind).toBe('no-op');
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe('input-router / pointer events — Phase 5 annotation tools', () => {
|
|
344
|
-
test('pointermove in pen tool → no-op (SVG overlay owns it)', () => {
|
|
345
|
-
expect(
|
|
346
|
-
classify(base({ type: 'pointermove', activeTool: 'pen', clientX: 1, clientY: 2 })).kind
|
|
347
|
-
).toBe('no-op');
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
test('pointermove in eraser tool → no-op', () => {
|
|
351
|
-
expect(classify(base({ type: 'pointermove', activeTool: 'eraser' })).kind).toBe('no-op');
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
test('bare left-click in rect tool → no-op (SVG overlay claims)', () => {
|
|
355
|
-
expect(classify(base({ type: 'pointerdown', activeTool: 'rect', button: 0 })).kind).toBe(
|
|
356
|
-
'no-op'
|
|
357
|
-
);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
test('bare left-click in ellipse tool → no-op (Phase 5.1)', () => {
|
|
361
|
-
expect(classify(base({ type: 'pointerdown', activeTool: 'ellipse', button: 0 })).kind).toBe(
|
|
362
|
-
'no-op'
|
|
363
|
-
);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
test('pointermove in ellipse tool → no-op (SVG overlay owns it)', () => {
|
|
367
|
-
expect(classify(base({ type: 'pointermove', activeTool: 'ellipse' })).kind).toBe('no-op');
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
test('sticky + text tools own bare pointer events (Phase 21 — SVG overlay claims)', () => {
|
|
371
|
-
for (const activeTool of ['sticky', 'text'] as const) {
|
|
372
|
-
expect(classify(base({ type: 'pointerdown', activeTool, button: 0 })).kind).toBe('no-op');
|
|
373
|
-
expect(classify(base({ type: 'pointermove', activeTool })).kind).toBe('no-op');
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
test('cmd+left-click in arrow tool → select replace (escape hatch to move)', () => {
|
|
378
|
-
expect(
|
|
379
|
-
classify(
|
|
380
|
-
base({
|
|
381
|
-
type: 'pointerdown',
|
|
382
|
-
activeTool: 'arrow',
|
|
383
|
-
button: 0,
|
|
384
|
-
metaKey: true,
|
|
385
|
-
clientX: 4,
|
|
386
|
-
clientY: 5,
|
|
387
|
-
})
|
|
388
|
-
)
|
|
389
|
-
).toEqual({
|
|
390
|
-
kind: 'select',
|
|
391
|
-
mode: 'replace',
|
|
392
|
-
deep: true,
|
|
393
|
-
clientX: 4,
|
|
394
|
-
clientY: 5,
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
test('right-click in pen tool → context-menu (unchanged)', () => {
|
|
399
|
-
expect(classify(base({ type: 'pointerdown', activeTool: 'pen', button: 2 })).kind).toBe(
|
|
400
|
-
'context-menu'
|
|
401
|
-
);
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
describe('input-router / isEditableTarget', () => {
|
|
406
|
-
test('null target → false', () => {
|
|
407
|
-
expect(isEditableTarget(null)).toBe(false);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
test('plain div → false', () => {
|
|
411
|
-
const el = { tagName: 'DIV', isContentEditable: false } as HTMLElement;
|
|
412
|
-
expect(isEditableTarget(el)).toBe(false);
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
test('INPUT → true', () => {
|
|
416
|
-
const el = { tagName: 'INPUT', isContentEditable: false } as HTMLElement;
|
|
417
|
-
expect(isEditableTarget(el)).toBe(true);
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
test('TEXTAREA → true', () => {
|
|
421
|
-
const el = { tagName: 'TEXTAREA', isContentEditable: false } as HTMLElement;
|
|
422
|
-
expect(isEditableTarget(el)).toBe(true);
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
test('contentEditable=true → true', () => {
|
|
426
|
-
const el = { tagName: 'DIV', isContentEditable: true } as HTMLElement;
|
|
427
|
-
expect(isEditableTarget(el)).toBe(true);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
// Dogfood fix — the reported bug: typing "R" while editing in-canvas
|
|
431
|
-
// element text (canvas-shell.tsx's contenteditable="plaintext-only") fired
|
|
432
|
-
// the Rectangle tool shortcut. `.isContentEditable` SHOULD reflect
|
|
433
|
-
// "plaintext-only" per spec, but engine/version inconsistencies mean it
|
|
434
|
-
// can't be the only check — these lock in the raw-attribute fallback that
|
|
435
|
-
// now makes isEditableTarget resilient regardless.
|
|
436
|
-
test('contenteditable="plaintext-only" (isContentEditable false or unreliable) → true', () => {
|
|
437
|
-
const el = {
|
|
438
|
-
tagName: 'DIV',
|
|
439
|
-
isContentEditable: false,
|
|
440
|
-
getAttribute: (name: string) => (name === 'contenteditable' ? 'plaintext-only' : null),
|
|
441
|
-
} as unknown as HTMLElement;
|
|
442
|
-
expect(isEditableTarget(el)).toBe(true);
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
test('bare contenteditable="" attribute → true', () => {
|
|
446
|
-
const el = {
|
|
447
|
-
tagName: 'DIV',
|
|
448
|
-
isContentEditable: false,
|
|
449
|
-
getAttribute: (name: string) => (name === 'contenteditable' ? '' : null),
|
|
450
|
-
} as unknown as HTMLElement;
|
|
451
|
-
expect(isEditableTarget(el)).toBe(true);
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
test('plain div WITH a getAttribute method (real-element shape) still → false', () => {
|
|
455
|
-
const el = {
|
|
456
|
-
tagName: 'DIV',
|
|
457
|
-
isContentEditable: false,
|
|
458
|
-
getAttribute: () => null,
|
|
459
|
-
} as unknown as HTMLElement;
|
|
460
|
-
expect(isEditableTarget(el)).toBe(false);
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
// issue-71 — a bare (no-modifier) pointerdown/mousedown on an artboard's own
|
|
465
|
-
// drag chrome (the label strip + border, OUTSIDE `.dc-artboard-body`) must
|
|
466
|
-
// still be identified so useInputRouter can skip stopImmediatePropagation and
|
|
467
|
-
// let use-artboard-drag.tsx's own bubble-phase onPointerDown arm its
|
|
468
|
-
// pending→dragging state machine — otherwise no artboard could ever be
|
|
469
|
-
// repositioned by dragging its header (the router's capture-phase claim
|
|
470
|
-
// killed the event before it reached that listener).
|
|
471
|
-
describe('input-router / isArtboardDragChrome', () => {
|
|
472
|
-
// `closest(sel)` here mimics Element.closest: return a truthy stub for any
|
|
473
|
-
// selector this fake node "matches" (is, or is a descendant of), null else.
|
|
474
|
-
const fakeTarget = (memberOf: string[]): EventTarget =>
|
|
475
|
-
({
|
|
476
|
-
closest: (sel: string) => (memberOf.includes(sel) ? {} : null),
|
|
477
|
-
}) as unknown as EventTarget;
|
|
478
|
-
|
|
479
|
-
test('null target → false', () => {
|
|
480
|
-
expect(isArtboardDragChrome(null)).toBe(false);
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
test('target with no closest() (non-Element) → false', () => {
|
|
484
|
-
expect(isArtboardDragChrome({} as EventTarget)).toBe(false);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
test('outside any artboard → false', () => {
|
|
488
|
-
expect(isArtboardDragChrome(fakeTarget([]))).toBe(false);
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
test('the label strip / border chrome (inside an artboard, not its body) → true', () => {
|
|
492
|
-
expect(isArtboardDragChrome(fakeTarget(['[data-dc-screen]']))).toBe(true);
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
test('inside .dc-artboard-body (the canvas content, not chrome) → false', () => {
|
|
496
|
-
expect(isArtboardDragChrome(fakeTarget(['[data-dc-screen]', '.dc-artboard-body']))).toBe(false);
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
// issue-90 — once comment mode is armed, a bare click anywhere on the shell's
|
|
501
|
-
// floating chrome (tool palette, zoom pill, minimap, right-click context
|
|
502
|
-
// menu) was claimed as `drop-comment` and dropped a stray pin at the
|
|
503
|
-
// chrome's own coordinates instead of reaching the chrome's own React
|
|
504
|
-
// onClick — the palette became unusable the moment the user picked the
|
|
505
|
-
// Comment tool. `classifyContextKind` (canvas-shell.tsx) already treats
|
|
506
|
-
// these same selectors as non-canvas `'overlay'` chrome; `isOverlayTarget`
|
|
507
|
-
// was the one claim gate that didn't.
|
|
508
|
-
describe('input-router / isOverlayTarget — shell chrome exemptions (issue-90)', () => {
|
|
509
|
-
// isOverlayTarget makes ONE `closest(a, b, c, ...)` call against the full
|
|
510
|
-
// comma-joined selector list, unlike isArtboardDragChrome's fakeTarget
|
|
511
|
-
// above (which mimics one call per bare selector) — so the stub instead
|
|
512
|
-
// checks whether any of the target's own classes appears among the
|
|
513
|
-
// selector's comma-separated parts, same as a real `Element.closest` would
|
|
514
|
-
// resolve for a node carrying that class.
|
|
515
|
-
const fakeTarget = (memberOf: string[]): EventTarget =>
|
|
516
|
-
({
|
|
517
|
-
closest: (sel: string) => (memberOf.some((cls) => sel.includes(cls)) ? {} : null),
|
|
518
|
-
}) as unknown as EventTarget;
|
|
519
|
-
|
|
520
|
-
test('null target → false', () => {
|
|
521
|
-
expect(isOverlayTarget(null)).toBe(false);
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
test('a click on the tool palette is exempt', () => {
|
|
525
|
-
expect(isOverlayTarget(fakeTarget(['.dc-tool-palette']))).toBe(true);
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test('a click on the zoom toolbar is exempt', () => {
|
|
529
|
-
expect(isOverlayTarget(fakeTarget(['.dc-zoom-tb']))).toBe(true);
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
test('a click on the minimap is exempt', () => {
|
|
533
|
-
expect(isOverlayTarget(fakeTarget(['.dc-mm']))).toBe(true);
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
test('a click on the right-click context menu is exempt', () => {
|
|
537
|
-
expect(isOverlayTarget(fakeTarget(['.dc-context-menu']))).toBe(true);
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
test('a click on ordinary canvas content is NOT exempt', () => {
|
|
541
|
-
expect(isOverlayTarget(fakeTarget([]))).toBe(false);
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
test('the pre-existing comment-overlay exemptions still hold', () => {
|
|
545
|
-
expect(isOverlayTarget(fakeTarget(['.cm-composer']))).toBe(true);
|
|
546
|
-
expect(isOverlayTarget(fakeTarget(['.cm-thread']))).toBe(true);
|
|
547
|
-
expect(isOverlayTarget(fakeTarget(['.cm-mention-popup']))).toBe(true);
|
|
548
|
-
expect(isOverlayTarget(fakeTarget(['.cm-pin']))).toBe(true);
|
|
549
|
-
expect(isOverlayTarget(fakeTarget(['[data-mediaref-player]']))).toBe(true);
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
// Security review finding (issue-90) — `.dc-world` is where the ACTIVE
|
|
553
|
-
// CANVAS's own untrusted, AI/user-authored JSX renders. None of the real
|
|
554
|
-
// chrome ever lives there, so an element inside `.dc-world` that ALSO
|
|
555
|
-
// carries one of these class names is necessarily a same-name imposter —
|
|
556
|
-
// untrusted canvas content spoofing the shell's own chrome to make the
|
|
557
|
-
// router yield (skip preventDefault) on it instead of claiming the
|
|
558
|
-
// gesture. Must be rejected regardless of which chrome class it copies.
|
|
559
|
-
test('an element inside .dc-world carrying a spoofed chrome class is NOT exempt', () => {
|
|
560
|
-
expect(isOverlayTarget(fakeTarget(['.dc-world', '.dc-tool-palette']))).toBe(false);
|
|
561
|
-
expect(isOverlayTarget(fakeTarget(['.dc-world', '.dc-zoom-tb']))).toBe(false);
|
|
562
|
-
expect(isOverlayTarget(fakeTarget(['.dc-world', '.dc-mm']))).toBe(false);
|
|
563
|
-
expect(isOverlayTarget(fakeTarget(['.dc-world', '.dc-context-menu']))).toBe(false);
|
|
564
|
-
expect(isOverlayTarget(fakeTarget(['.dc-world', '.cm-composer']))).toBe(false);
|
|
565
|
-
});
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
// issue-71 — the actual regression: before this fix, useInputRouter's
|
|
569
|
-
// pointerdown/mousedown handlers called `stopImmediatePropagation()` on EVERY
|
|
570
|
-
// claimed action, including a bare click on an artboard's own drag chrome —
|
|
571
|
-
// which killed the event before use-artboard-drag.tsx's bubble-phase
|
|
572
|
-
// onPointerDown (bound to that same artboard) ever saw it, so dragging an
|
|
573
|
-
// artboard by its header did nothing in either the Move or the (former)
|
|
574
|
-
// always-select tool state. `yieldsToArtboardDrag` is the exact decision the
|
|
575
|
-
// handlers now gate `stopImmediatePropagation` on.
|
|
576
|
-
describe('input-router / yieldsToArtboardDrag', () => {
|
|
577
|
-
const chrome = (): EventTarget =>
|
|
578
|
-
({
|
|
579
|
-
closest: (sel: string) => (sel === '[data-dc-screen]' ? {} : null),
|
|
580
|
-
}) as unknown as EventTarget;
|
|
581
|
-
const body = (): EventTarget => ({ closest: () => ({}) }) as unknown as EventTarget; // matches every selector, incl. .dc-artboard-body
|
|
582
|
-
|
|
583
|
-
test('bare click on artboard chrome (the reported bug) → true (let the drag hook see it)', () => {
|
|
584
|
-
const action = classify({ type: 'pointerdown', activeTool: 'move', button: 0 });
|
|
585
|
-
expect(yieldsToArtboardDrag(action, chrome(), { metaKey: false, ctrlKey: false })).toBe(true);
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
test('shift+click on artboard chrome (multi-select-and-drag) → true', () => {
|
|
589
|
-
const action = classify({
|
|
590
|
-
type: 'pointerdown',
|
|
591
|
-
activeTool: 'move',
|
|
592
|
-
button: 0,
|
|
593
|
-
shiftKey: true,
|
|
594
|
-
});
|
|
595
|
-
expect(yieldsToArtboardDrag(action, chrome(), { metaKey: false, ctrlKey: false })).toBe(true);
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
test('cmd+click on artboard chrome (deep-select, not a drag) → false', () => {
|
|
599
|
-
const action = classify({
|
|
600
|
-
type: 'pointerdown',
|
|
601
|
-
activeTool: 'move',
|
|
602
|
-
button: 0,
|
|
603
|
-
metaKey: true,
|
|
604
|
-
});
|
|
605
|
-
expect(yieldsToArtboardDrag(action, chrome(), { metaKey: true, ctrlKey: false })).toBe(false);
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
test('bare click on .dc-artboard-body content (not chrome) → false', () => {
|
|
609
|
-
const action = classify({ type: 'pointerdown', activeTool: 'move', button: 0 });
|
|
610
|
-
expect(yieldsToArtboardDrag(action, body(), { metaKey: false, ctrlKey: false })).toBe(false);
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
test('bare click outside any artboard → false (nothing to yield to)', () => {
|
|
614
|
-
const action = classify({ type: 'pointerdown', activeTool: 'move', button: 0 });
|
|
615
|
-
const outside = { closest: () => null } as unknown as EventTarget;
|
|
616
|
-
expect(yieldsToArtboardDrag(action, outside, { metaKey: false, ctrlKey: false })).toBe(false);
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
test('right-click (context-menu) on chrome → false — not a select at all', () => {
|
|
620
|
-
const action = classify({ type: 'pointerdown', activeTool: 'move', button: 2 });
|
|
621
|
-
expect(yieldsToArtboardDrag(action, chrome(), { metaKey: false, ctrlKey: false })).toBe(false);
|
|
622
|
-
});
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
// issue-78 — the actual regression: the router's capture-phase `click`
|
|
626
|
-
// listener claimed every bare click while in the Move tool (and every
|
|
627
|
-
// Cmd/Ctrl+click from Browse/annotation tools) and called
|
|
628
|
-
// `stopImmediatePropagation()` unconditionally, unlike its pointerdown/
|
|
629
|
-
// mousedown siblings above. That killed the native click on any artboard-
|
|
630
|
-
// chrome control BEFORE React's delegated onClick ever ran — including the
|
|
631
|
-
// video-timeline badge (`.dc-artboard-video-badge`, DDR-148), which sits
|
|
632
|
-
// outside `.dc-artboard-body` as chrome. Clicking it did nothing.
|
|
633
|
-
// `yieldsClickToArtboardChrome` is the decision the click handler now gates
|
|
634
|
-
// its suppression on, mirroring `yieldsToArtboardDrag` above.
|
|
635
|
-
describe('input-router / yieldsClickToArtboardChrome', () => {
|
|
636
|
-
const chrome = (): EventTarget =>
|
|
637
|
-
({
|
|
638
|
-
closest: (sel: string) => (sel === '[data-dc-screen]' ? {} : null),
|
|
639
|
-
}) as unknown as EventTarget;
|
|
640
|
-
const body = (): EventTarget => ({ closest: () => ({}) }) as unknown as EventTarget; // matches every selector, incl. .dc-artboard-body
|
|
641
|
-
|
|
642
|
-
test('bare click on the video-timeline badge in Move tool (the reported bug) → true', () => {
|
|
643
|
-
expect(
|
|
644
|
-
yieldsClickToArtboardChrome('select', chrome(), { metaKey: false, ctrlKey: false })
|
|
645
|
-
).toBe(true);
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
test('cmd+click on artboard chrome (deep-select, not a chrome control) → false', () => {
|
|
649
|
-
expect(yieldsClickToArtboardChrome('select', chrome(), { metaKey: true, ctrlKey: false })).toBe(
|
|
650
|
-
false
|
|
651
|
-
);
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
test('bare click on .dc-artboard-body content (not chrome) → false', () => {
|
|
655
|
-
expect(yieldsClickToArtboardChrome('select', body(), { metaKey: false, ctrlKey: false })).toBe(
|
|
656
|
-
false
|
|
657
|
-
);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
test('bare click outside any artboard → false (nothing to yield to)', () => {
|
|
661
|
-
const outside = { closest: () => null } as unknown as EventTarget;
|
|
662
|
-
expect(yieldsClickToArtboardChrome('select', outside, { metaKey: false, ctrlKey: false })).toBe(
|
|
663
|
-
false
|
|
664
|
-
);
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
test('non-select routed kind (e.g. context-menu) on chrome → false', () => {
|
|
668
|
-
expect(
|
|
669
|
-
yieldsClickToArtboardChrome('context-menu', chrome(), { metaKey: false, ctrlKey: false })
|
|
670
|
-
).toBe(false);
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
test('null routed kind on chrome → false', () => {
|
|
674
|
-
expect(yieldsClickToArtboardChrome(null, chrome(), { metaKey: false, ctrlKey: false })).toBe(
|
|
675
|
-
false
|
|
676
|
-
);
|
|
677
|
-
});
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
// T25 — Drag-vs-click threshold. Centralized here so every drag-class gesture
|
|
681
|
-
// (artboard drag, artboard marquee, element marquee, annotation pen/rect/etc.)
|
|
682
|
-
// reads the same value. Microsoft Win32 SM_CXDRAG default + d3-drag convention.
|
|
683
|
-
describe('input-router / crossedDragThreshold', () => {
|
|
684
|
-
test('constant is 4', () => {
|
|
685
|
-
expect(DRAG_THRESHOLD_PX).toBe(4);
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
test('no movement → false', () => {
|
|
689
|
-
expect(crossedDragThreshold(100, 200, 100, 200)).toBe(false);
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
test('3 px move (sub-threshold) → false', () => {
|
|
693
|
-
expect(crossedDragThreshold(0, 0, 3, 0)).toBe(false);
|
|
694
|
-
expect(crossedDragThreshold(0, 0, 2, 2)).toBe(false); // hypot ≈ 2.83
|
|
695
|
-
});
|
|
696
|
-
|
|
697
|
-
test('exactly 4 px → true (boundary inclusive)', () => {
|
|
698
|
-
expect(crossedDragThreshold(0, 0, 4, 0)).toBe(true);
|
|
699
|
-
expect(crossedDragThreshold(0, 0, 0, 4)).toBe(true);
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
test('5 px diagonal → true', () => {
|
|
703
|
-
// 3-4-5 right triangle: hypot = 5
|
|
704
|
-
expect(crossedDragThreshold(10, 20, 13, 24)).toBe(true);
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
test('negative deltas — hypot is direction-agnostic', () => {
|
|
708
|
-
expect(crossedDragThreshold(50, 50, 47, 46)).toBe(true); // hypot = 5
|
|
709
|
-
expect(crossedDragThreshold(50, 50, 48, 49)).toBe(false); // hypot ≈ 2.24
|
|
710
|
-
});
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
describe('input-router / keydown — undo + redo (Phase 20)', () => {
|
|
714
|
-
test('Cmd+Z → undo', () => {
|
|
715
|
-
expect(classify(base({ type: 'keydown', key: 'z', metaKey: true })).kind).toBe('undo');
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
test('Cmd+Shift+Z → redo', () => {
|
|
719
|
-
expect(classify(base({ type: 'keydown', key: 'z', metaKey: true, shiftKey: true })).kind).toBe(
|
|
720
|
-
'redo'
|
|
721
|
-
);
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
test('Ctrl+Z → undo (Windows / Linux mac-less)', () => {
|
|
725
|
-
expect(classify(base({ type: 'keydown', key: 'z', ctrlKey: true })).kind).toBe('undo');
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
test('Ctrl+Y → redo (Windows convention)', () => {
|
|
729
|
-
expect(classify(base({ type: 'keydown', key: 'y', ctrlKey: true })).kind).toBe('redo');
|
|
730
|
-
});
|
|
731
|
-
|
|
732
|
-
test('Cmd+Y → redo (mac users with Windows muscle-memory)', () => {
|
|
733
|
-
expect(classify(base({ type: 'keydown', key: 'y', metaKey: true })).kind).toBe('redo');
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
test('Cmd+Z inside editable → no-op (browser native text undo wins)', () => {
|
|
737
|
-
expect(
|
|
738
|
-
classify(base({ type: 'keydown', key: 'z', metaKey: true, isEditable: true })).kind
|
|
739
|
-
).toBe('no-op');
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
test('Cmd+Opt+Z → no-op (Alt is reserved for browser text gestures)', () => {
|
|
743
|
-
expect(classify(base({ type: 'keydown', key: 'z', metaKey: true, altKey: true })).kind).toBe(
|
|
744
|
-
'no-op'
|
|
745
|
-
);
|
|
746
|
-
});
|
|
747
|
-
|
|
748
|
-
test('bare Z → no-op (Z is not a tool letter; needs Cmd)', () => {
|
|
749
|
-
expect(classify(base({ type: 'keydown', key: 'z' })).kind).toBe('no-op');
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
test('Cmd+Shift+Y → no-op (only Cmd+Y; we do not over-claim)', () => {
|
|
753
|
-
expect(classify(base({ type: 'keydown', key: 'y', metaKey: true, shiftKey: true })).kind).toBe(
|
|
754
|
-
'no-op'
|
|
755
|
-
);
|
|
756
|
-
});
|
|
757
|
-
});
|