@agent-native/core 0.92.7 → 0.92.8
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
- package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
- package/corpus/core/docs/content/pr-visual-recap.mdx +3 -3
- package/corpus/core/docs/content/pure-agent-apps.mdx +22 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/builtin.ts +1 -1
- package/corpus/core/src/agent/model-config.ts +30 -30
- package/corpus/core/src/agent/production-agent.ts +129 -7
- package/corpus/core/src/cli/design-connect.ts +438 -83
- package/corpus/core/src/cli/recap.ts +2 -2
- package/corpus/core/src/client/AgentPanel.tsx +8 -1
- package/corpus/core/src/client/agent-chat-adapter.ts +12 -7
- package/corpus/core/src/client/composer/TiptapComposer.tsx +1 -1
- package/corpus/core/src/client/index.ts +4 -0
- package/corpus/core/src/client/org/TeamPage.tsx +41 -26
- package/corpus/core/src/client/use-action.ts +113 -1
- package/corpus/core/src/db/client.ts +12 -2
- package/corpus/core/src/db/create-get-db.ts +4 -1
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
- package/corpus/core/src/server/transcribe-voice.ts +1 -1
- package/corpus/core/src/usage/store.ts +13 -4
- package/corpus/core/src/vite/client.ts +38 -1
- package/corpus/templates/analytics/AGENTS.md +3 -0
- package/corpus/templates/analytics/actions/get-data-program.ts +78 -0
- package/corpus/templates/analytics/actions/list-data-programs.ts +49 -0
- package/corpus/templates/analytics/app/global.css +11 -0
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +163 -17
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/analytics/app/i18n-data.ts +20 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +96 -3
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +61 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +40 -2
- package/corpus/templates/analytics/changelog/2026-07-09-analytics-no-longer-forgets-successful-data-queries-when-a-l.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-keep-consistent-spacing-when-they-stack-ver.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-charts-tables-and-extensions-can-be-selected-and-d.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-long-running-analytics-requests-now-recover-automatically-wh.md +6 -0
- package/corpus/templates/analytics/server/db/index.ts +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +1 -7
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +102 -15
- package/corpus/templates/content/app/hooks/use-content-database.ts +52 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +50 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/content/app/i18n-data.ts +28 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-warnings-no-longer-flash-during-normal-synce.md +6 -0
- package/corpus/templates/content/changelog/2026-07-09-property-menus-and-delete-confirmations-now-open-visibly-and.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +130 -28
- package/corpus/templates/design/actions/add-breakpoint.ts +48 -68
- package/corpus/templates/design/actions/add-localhost-screens.ts +496 -61
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +2 -18
- package/corpus/templates/design/actions/apply-design-token-edit.ts +51 -82
- package/corpus/templates/design/actions/apply-tweaks.ts +63 -41
- package/corpus/templates/design/actions/capture-design-state.ts +2 -2
- package/corpus/templates/design/actions/connect-localhost.ts +58 -12
- package/corpus/templates/design/actions/create-component.ts +2 -18
- package/corpus/templates/design/actions/create-design-branch.ts +38 -18
- package/corpus/templates/design/actions/create-fusion-app.ts +15 -11
- package/corpus/templates/design/actions/delete-file.ts +19 -34
- package/corpus/templates/design/actions/deploy-design-preview.ts +59 -27
- package/corpus/templates/design/actions/deploy-fusion-app.ts +22 -15
- package/corpus/templates/design/actions/export-pdf.ts +2 -1
- package/corpus/templates/design/actions/export-zip.ts +9 -3
- package/corpus/templates/design/actions/generate-design.ts +107 -70
- package/corpus/templates/design/actions/get-component-details.ts +2 -18
- package/corpus/templates/design/actions/get-design-branch-diff.ts +2 -3
- package/corpus/templates/design/actions/get-design-surface-index.ts +2 -18
- package/corpus/templates/design/actions/get-design.ts +2 -1
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +7 -1
- package/corpus/templates/design/actions/import-design-tokens.ts +114 -76
- package/corpus/templates/design/actions/index-components.ts +2 -18
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +23 -28
- package/corpus/templates/design/actions/list-local-files.ts +9 -2
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +82 -98
- package/corpus/templates/design/actions/open-component-source.ts +2 -18
- package/corpus/templates/design/actions/open-visual-edit.ts +19 -1
- package/corpus/templates/design/actions/present-design-variants.ts +70 -70
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +4 -14
- package/corpus/templates/design/actions/read-local-file.ts +9 -2
- package/corpus/templates/design/actions/remove-breakpoint.ts +40 -61
- package/corpus/templates/design/actions/rename-screen.ts +425 -0
- package/corpus/templates/design/actions/request-localhost-write-consent.ts +12 -2
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +9 -2
- package/corpus/templates/design/actions/sync-fusion-app.ts +56 -39
- package/corpus/templates/design/actions/update-design.ts +376 -61
- package/corpus/templates/design/actions/update-file.ts +455 -188
- package/corpus/templates/design/actions/write-local-file.ts +10 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +685 -608
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +43 -15
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +576 -2015
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +62 -14
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +263 -25
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +34 -0
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +40 -0
- package/corpus/templates/design/app/components/design/design-canvas/creation.ts +47 -0
- package/corpus/templates/design/app/components/design/design-canvas/element-payload.ts +27 -0
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +62 -0
- package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +137 -0
- package/corpus/templates/design/app/components/design/design-canvas/file-drop.ts +10 -0
- package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +23 -0
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +23 -0
- package/corpus/templates/design/app/components/design/design-canvas/iframe-pan.ts +177 -0
- package/corpus/templates/design/app/components/design/design-canvas/motion-types.ts +7 -0
- package/corpus/templates/design/app/components/design/design-canvas/pending-text-edit.ts +62 -0
- package/corpus/templates/design/app/components/design/index.ts +2 -2
- package/corpus/templates/design/app/components/design/multi-screen/canvas-tools.ts +100 -0
- package/corpus/templates/design/app/components/design/multi-screen/coordinate-transforms.ts +81 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +23 -13
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +238 -19
- package/corpus/templates/design/app/components/design/multi-screen/draft-primitives.ts +424 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +341 -0
- package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +52 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +62 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +283 -0
- package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +272 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +5 -1
- package/corpus/templates/design/app/components/design/multi-screen/wheel-gesture-state.ts +12 -0
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +325 -84
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +15 -1
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +119 -47
- package/corpus/templates/design/app/i18n/zh-TW.ts +9 -0
- package/corpus/templates/design/app/i18n-data.ts +223 -0
- package/corpus/templates/design/app/lib/design-save-outbox.ts +379 -0
- package/corpus/templates/design/app/lib/png-clipboard.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/canvas-primitives.ts +229 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +918 -0
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +43 -0
- package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +336 -0
- package/corpus/templates/design/app/pages/design-editor/data-operations.ts +270 -0
- package/corpus/templates/design/app/pages/design-editor/dom-guards.ts +24 -0
- package/corpus/templates/design/app/pages/design-editor/dom-utils.ts +11 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +20 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +408 -0
- package/corpus/templates/design/app/pages/design-editor/export-capture.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/geometry-persistence.ts +93 -0
- package/corpus/templates/design/app/pages/design-editor/history.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +185 -0
- package/corpus/templates/design/app/pages/design-editor/motion-state.ts +309 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +438 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +510 -0
- package/corpus/templates/design/app/pages/design-editor/portable-style.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +337 -0
- package/corpus/templates/design/app/pages/design-editor/style-utils.ts +5 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +92 -0
- package/corpus/templates/design/app/pages/design-editor/tweak-save.ts +87 -0
- package/corpus/templates/design/app/pages/design-editor/types.ts +36 -0
- package/corpus/templates/design/app/routes/design.$id.tsx +11 -1
- package/corpus/templates/design/changelog/2026-07-09-design-edits-canvas-positions-tools-and-tab-context-now-pers.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-figma-style-shortcuts-drawing-annotations-and-cross-screen-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hand-and-scale-tools-now-keep-their-active-toolbar-identity-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-large-multi-screen-designs-now-stay-responsive-during-long-c.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-drags-now-land-exactly-where-the-insertion-indicator-s.md +6 -0
- package/corpus/templates/design/server/db/index.ts +2 -52
- package/corpus/templates/design/server/db/schema.ts +16 -0
- package/corpus/templates/design/server/lib/design-data-access.ts +70 -0
- package/corpus/templates/design/server/lib/design-data-mutation.ts +249 -0
- package/corpus/templates/design/server/lib/fusion-screens.ts +89 -76
- package/corpus/templates/design/server/lib/import-design-files.ts +76 -35
- package/corpus/templates/design/server/lib/verify-write-grant.ts +7 -2
- package/corpus/templates/design/server/plugins/db.ts +19 -0
- package/corpus/templates/design/server/source-workspace.ts +12 -13
- package/corpus/templates/design/shared/resolve-tweaks.ts +15 -0
- package/corpus/templates/design/shared/screen-rename.ts +42 -0
- package/corpus/templates/design/shared/source-mode.ts +84 -4
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builtin.js +1 -1
- package/dist/agent/engine/builtin.js.map +1 -1
- package/dist/agent/model-config.d.ts +9 -9
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +30 -30
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts +60 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +106 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +30 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +359 -75
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/recap.js +2 -2
- package/dist/cli/recap.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +10 -5
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/TiptapComposer.js +1 -1
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +6 -5
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/use-action.d.ts +32 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +68 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +7 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +2 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.js +1 -1
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +13 -4
- package/dist/usage/store.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +36 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ar-SA/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ar-SA/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/de-DE/a2a-protocol.mdx +12 -0
- package/docs/content/locales/de-DE/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/es-ES/a2a-protocol.mdx +12 -0
- package/docs/content/locales/es-ES/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/fr-FR/a2a-protocol.mdx +12 -0
- package/docs/content/locales/fr-FR/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/hi-IN/a2a-protocol.mdx +12 -0
- package/docs/content/locales/hi-IN/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/hi-IN/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/ja-JP/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ja-JP/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/ja-JP/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/ko-KR/a2a-protocol.mdx +12 -0
- package/docs/content/locales/ko-KR/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/ko-KR/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/pt-BR/a2a-protocol.mdx +12 -0
- package/docs/content/locales/pt-BR/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/zh-CN/a2a-protocol.mdx +12 -0
- package/docs/content/locales/zh-CN/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -0
- package/docs/content/locales/zh-TW/a2a-protocol.mdx +12 -0
- package/docs/content/locales/zh-TW/pr-visual-recap.mdx +3 -3
- package/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -0
- package/docs/content/pr-visual-recap.mdx +3 -3
- package/docs/content/pure-agent-apps.mdx +22 -0
- package/package.json +2 -2
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export function liveEditEndpointUrl(
|
|
2
|
+
bridgeUrl: string,
|
|
3
|
+
previewUrl: string,
|
|
4
|
+
options: {
|
|
5
|
+
previewToken: string;
|
|
6
|
+
includeEditorBridge?: boolean;
|
|
7
|
+
bridgeKey?: string;
|
|
8
|
+
},
|
|
9
|
+
): string {
|
|
10
|
+
const endpoint = new URL("/live-edit", bridgeUrl);
|
|
11
|
+
endpoint.searchParams.set("url", previewUrl);
|
|
12
|
+
endpoint.searchParams.set("previewToken", options.previewToken);
|
|
13
|
+
if (options?.includeEditorBridge === false) {
|
|
14
|
+
endpoint.searchParams.set("bridge", "0");
|
|
15
|
+
} else if (options?.bridgeKey) {
|
|
16
|
+
endpoint.searchParams.set("bridgeKey", options.bridgeKey);
|
|
17
|
+
}
|
|
18
|
+
return endpoint.toString();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolveLiveEditPreviewUrl(args: {
|
|
22
|
+
sourceType: string | undefined;
|
|
23
|
+
bridgeUrl: string | undefined;
|
|
24
|
+
previewToken: string | undefined;
|
|
25
|
+
previewUrl: string | null | undefined;
|
|
26
|
+
bridgeKey: string;
|
|
27
|
+
registeredBridgeKey: string | null;
|
|
28
|
+
}): string | null {
|
|
29
|
+
if (
|
|
30
|
+
args.sourceType !== "localhost" ||
|
|
31
|
+
!args.bridgeUrl ||
|
|
32
|
+
!args.previewToken ||
|
|
33
|
+
!args.previewUrl
|
|
34
|
+
) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// Every localhost mode registers a keyed injected script before rendering:
|
|
38
|
+
// editable modes include editor chrome, while Interact/read-only modes use
|
|
39
|
+
// the gesture-only bridge. Waiting for the matching key prevents a raw URL
|
|
40
|
+
// load followed by a second proxied load (the flash/state-loss regression).
|
|
41
|
+
if (args.registeredBridgeKey !== args.bridgeKey) return null;
|
|
42
|
+
return liveEditEndpointUrl(args.bridgeUrl, args.previewUrl, {
|
|
43
|
+
previewToken: args.previewToken,
|
|
44
|
+
bridgeKey: args.bridgeKey,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function shouldUseIframeLoadReadyFallback(
|
|
49
|
+
usesLiveEditEditorBridge: boolean,
|
|
50
|
+
): boolean {
|
|
51
|
+
return !usesLiveEditEditorBridge;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function shouldFetchExternalSourceSnapshot(args: {
|
|
55
|
+
sourceType: string | undefined;
|
|
56
|
+
bridgeUrl: string | undefined;
|
|
57
|
+
previewToken: string | undefined;
|
|
58
|
+
previewUrl: string | null | undefined;
|
|
59
|
+
hasSnapshotConsumer: boolean;
|
|
60
|
+
}): boolean {
|
|
61
|
+
return Boolean(
|
|
62
|
+
args.sourceType === "localhost" &&
|
|
63
|
+
args.bridgeUrl &&
|
|
64
|
+
args.previewToken &&
|
|
65
|
+
args.previewUrl &&
|
|
66
|
+
args.hasSnapshotConsumer,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const SCRIPT_TAG_RE = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
|
|
71
|
+
const SCRIPT_SRC_RE = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
|
|
72
|
+
|
|
73
|
+
function isKnownDevRuntimeScriptSource(value: string): boolean {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = new URL(value, "http://localhost");
|
|
76
|
+
return (
|
|
77
|
+
parsed.pathname === "/@vite/client" ||
|
|
78
|
+
parsed.pathname === "/@react-refresh" ||
|
|
79
|
+
parsed.pathname.includes("/__x00__react-refresh")
|
|
80
|
+
);
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Remove development-server HTML transforms before treating a live snapshot as
|
|
88
|
+
* writable source. Vite injects its HMR client and React Refresh preamble into
|
|
89
|
+
* the served document; persisting either back to index.html corrupts the real
|
|
90
|
+
* source and can duplicate refresh runtimes on every Apply-to-source cycle.
|
|
91
|
+
* User-authored module scripts and inline application scripts are preserved.
|
|
92
|
+
*/
|
|
93
|
+
export function sanitizeLocalhostSourceSnapshotHtml(html: string): string {
|
|
94
|
+
return html.replace(
|
|
95
|
+
SCRIPT_TAG_RE,
|
|
96
|
+
(fullScript, rawAttributes: string, inlineBody: string) => {
|
|
97
|
+
const srcMatch = rawAttributes.match(SCRIPT_SRC_RE);
|
|
98
|
+
const src = srcMatch?.[1] ?? srcMatch?.[2] ?? srcMatch?.[3] ?? "";
|
|
99
|
+
if (src && isKnownDevRuntimeScriptSource(src)) return "";
|
|
100
|
+
|
|
101
|
+
const knownRefreshPreamble =
|
|
102
|
+
inlineBody.includes("/@react-refresh") ||
|
|
103
|
+
inlineBody.includes("RefreshRuntime.injectIntoGlobalHook") ||
|
|
104
|
+
inlineBody.includes("__vite_plugin_react_preamble_installed__");
|
|
105
|
+
const knownViteClientInjection =
|
|
106
|
+
inlineBody.includes("/@vite/client") &&
|
|
107
|
+
(inlineBody.includes("import") || inlineBody.includes("__vite"));
|
|
108
|
+
return knownRefreshPreamble || knownViteClientInjection ? "" : fullScript;
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const EXTERNAL_PREVIEW_IFRAME_SANDBOX =
|
|
114
|
+
"allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin";
|
|
115
|
+
const EDITABLE_INLINE_IFRAME_SANDBOX =
|
|
116
|
+
"allow-scripts allow-popups allow-popups-to-escape-sandbox allow-same-origin";
|
|
117
|
+
const READ_ONLY_INLINE_IFRAME_SANDBOX =
|
|
118
|
+
"allow-scripts allow-popups allow-popups-to-escape-sandbox";
|
|
119
|
+
|
|
120
|
+
export function getDesignCanvasIframeSandbox(args: {
|
|
121
|
+
externalPreview: boolean;
|
|
122
|
+
readOnly: boolean;
|
|
123
|
+
}): string {
|
|
124
|
+
if (args.externalPreview) return EXTERNAL_PREVIEW_IFRAME_SANDBOX;
|
|
125
|
+
return args.readOnly
|
|
126
|
+
? READ_ONLY_INLINE_IFRAME_SANDBOX
|
|
127
|
+
: EDITABLE_INLINE_IFRAME_SANDBOX;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const SNAPSHOT_RETRY_BASE_DELAY_MS = 1500;
|
|
131
|
+
const SNAPSHOT_RETRY_MAX_DELAY_MS = 15000;
|
|
132
|
+
|
|
133
|
+
export function getSnapshotRetryDelayMs(attempt: number): number {
|
|
134
|
+
const safeAttempt = Number.isFinite(attempt) ? Math.max(0, attempt) : 0;
|
|
135
|
+
const delay = SNAPSHOT_RETRY_BASE_DELAY_MS * 2 ** safeAttempt;
|
|
136
|
+
return Math.min(SNAPSHOT_RETRY_MAX_DELAY_MS, delay);
|
|
137
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function isOsFileDragEvent(event: {
|
|
2
|
+
dataTransfer: { types: readonly string[] | DOMStringList } | null;
|
|
3
|
+
}): boolean {
|
|
4
|
+
const types = event.dataTransfer?.types;
|
|
5
|
+
if (!types) return false;
|
|
6
|
+
for (let index = 0; index < types.length; index += 1) {
|
|
7
|
+
if (types[index] === "Files") return true;
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { hitTestBridgeScript } from "../../../../.generated/bridge/hit-test.generated";
|
|
2
|
+
|
|
3
|
+
export const LIGHTWEIGHT_HIT_TEST_BRIDGE_SCRIPT = `
|
|
4
|
+
<script data-agent-native-hit-test-bridge>
|
|
5
|
+
${hitTestBridgeScript}
|
|
6
|
+
</script>
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export function appendHitTestResponder(html: string): string {
|
|
10
|
+
if (html.includes("</body>")) {
|
|
11
|
+
return html.replace(
|
|
12
|
+
"</body>", // i18n-ignore generated iframe HTML marker
|
|
13
|
+
LIGHTWEIGHT_HIT_TEST_BRIDGE_SCRIPT + "</body>", // i18n-ignore generated iframe HTML injection
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
if (html.includes("</html>")) {
|
|
17
|
+
return html.replace(
|
|
18
|
+
"</html>", // i18n-ignore generated iframe HTML marker
|
|
19
|
+
LIGHTWEIGHT_HIT_TEST_BRIDGE_SCRIPT + "</html>", // i18n-ignore generated iframe HTML injection
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return html + LIGHTWEIGHT_HIT_TEST_BRIDGE_SCRIPT;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ElementInfo } from "../types";
|
|
2
|
+
|
|
3
|
+
export interface IframeHotkeyPayload {
|
|
4
|
+
key: string;
|
|
5
|
+
code: string;
|
|
6
|
+
metaKey: boolean;
|
|
7
|
+
ctrlKey: boolean;
|
|
8
|
+
shiftKey: boolean;
|
|
9
|
+
altKey: boolean;
|
|
10
|
+
repeat: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IframeFigmaClipboardPastePayload {
|
|
14
|
+
content: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IframeContextMenuPayload {
|
|
18
|
+
clientX: number;
|
|
19
|
+
clientY: number;
|
|
20
|
+
viewportClientX?: number;
|
|
21
|
+
viewportClientY?: number;
|
|
22
|
+
info?: ElementInfo | null;
|
|
23
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const MAX_IFRAME_PAN_COORDINATE = 100_000;
|
|
2
|
+
const MAX_MOUSE_BUTTONS_MASK = 31;
|
|
3
|
+
|
|
4
|
+
export type EmbeddedCanvasPanPhase = "start" | "move" | "end" | "cancel";
|
|
5
|
+
|
|
6
|
+
export interface EmbeddedCanvasPanSession {
|
|
7
|
+
pointerId: number;
|
|
8
|
+
button: 0 | 1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface EmbeddedCanvasPanMessage {
|
|
12
|
+
type: "embedded-canvas-pan";
|
|
13
|
+
phase: EmbeddedCanvasPanPhase;
|
|
14
|
+
pointerId: number;
|
|
15
|
+
button: 0 | 1;
|
|
16
|
+
buttons: number;
|
|
17
|
+
clientX: number;
|
|
18
|
+
clientY: number;
|
|
19
|
+
ctrlKey: boolean;
|
|
20
|
+
metaKey: boolean;
|
|
21
|
+
shiftKey: boolean;
|
|
22
|
+
altKey: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ForwardEmbeddedCanvasPanResult {
|
|
26
|
+
handled: boolean;
|
|
27
|
+
session: EmbeddedCanvasPanSession | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function finiteNumber(value: unknown): number | null {
|
|
31
|
+
const number = Number(value);
|
|
32
|
+
return Number.isFinite(number) ? number : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function clamp(value: number, min: number, max: number): number {
|
|
36
|
+
return Math.max(min, Math.min(max, value));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseEmbeddedCanvasPanMessage(
|
|
40
|
+
value: unknown,
|
|
41
|
+
): EmbeddedCanvasPanMessage | null {
|
|
42
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
43
|
+
const candidate = value as Record<string, unknown>;
|
|
44
|
+
if (candidate.type !== "embedded-canvas-pan") return null;
|
|
45
|
+
const phase = candidate.phase;
|
|
46
|
+
if (
|
|
47
|
+
phase !== "start" &&
|
|
48
|
+
phase !== "move" &&
|
|
49
|
+
phase !== "end" &&
|
|
50
|
+
phase !== "cancel"
|
|
51
|
+
) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const pointerId = finiteNumber(candidate.pointerId);
|
|
55
|
+
const rawButton = finiteNumber(candidate.button);
|
|
56
|
+
const buttons = finiteNumber(candidate.buttons);
|
|
57
|
+
const clientX = finiteNumber(candidate.clientX);
|
|
58
|
+
const clientY = finiteNumber(candidate.clientY);
|
|
59
|
+
if (
|
|
60
|
+
pointerId === null ||
|
|
61
|
+
!Number.isInteger(pointerId) ||
|
|
62
|
+
pointerId < 0 ||
|
|
63
|
+
pointerId > 0x7fffffff ||
|
|
64
|
+
(rawButton !== 0 && rawButton !== 1) ||
|
|
65
|
+
buttons === null ||
|
|
66
|
+
clientX === null ||
|
|
67
|
+
clientY === null
|
|
68
|
+
) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
type: "embedded-canvas-pan",
|
|
73
|
+
phase,
|
|
74
|
+
pointerId,
|
|
75
|
+
button: rawButton,
|
|
76
|
+
buttons: clamp(Math.trunc(buttons), 0, MAX_MOUSE_BUTTONS_MASK),
|
|
77
|
+
clientX: clamp(
|
|
78
|
+
clientX,
|
|
79
|
+
-MAX_IFRAME_PAN_COORDINATE,
|
|
80
|
+
MAX_IFRAME_PAN_COORDINATE,
|
|
81
|
+
),
|
|
82
|
+
clientY: clamp(
|
|
83
|
+
clientY,
|
|
84
|
+
-MAX_IFRAME_PAN_COORDINATE,
|
|
85
|
+
MAX_IFRAME_PAN_COORDINATE,
|
|
86
|
+
),
|
|
87
|
+
ctrlKey: Boolean(candidate.ctrlKey),
|
|
88
|
+
metaKey: Boolean(candidate.metaKey),
|
|
89
|
+
shiftKey: Boolean(candidate.shiftKey),
|
|
90
|
+
altKey: Boolean(candidate.altKey),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Replays a trusted iframe bridge pan message through the parent document's
|
|
96
|
+
* existing mouse-drag path. The synthetic mousedown bubbles from the iframe
|
|
97
|
+
* element, so single-screen DesignCanvas and overview MultiScreenCanvas keep
|
|
98
|
+
* one authoritative pan implementation; move/end events go to `window`, where
|
|
99
|
+
* both implementations already install their lifetime drag listeners.
|
|
100
|
+
*
|
|
101
|
+
* The caller owns `session` and must only call this after validating the
|
|
102
|
+
* MessageEvent's source window + origin. Session matching rejects injected or
|
|
103
|
+
* reordered move/end packets before they can disturb another active gesture.
|
|
104
|
+
*/
|
|
105
|
+
export function forwardEmbeddedCanvasPanMessage({
|
|
106
|
+
data,
|
|
107
|
+
iframe,
|
|
108
|
+
hostWindow,
|
|
109
|
+
session,
|
|
110
|
+
}: {
|
|
111
|
+
data: unknown;
|
|
112
|
+
iframe: HTMLIFrameElement;
|
|
113
|
+
hostWindow: Window;
|
|
114
|
+
session: EmbeddedCanvasPanSession | null;
|
|
115
|
+
}): ForwardEmbeddedCanvasPanResult {
|
|
116
|
+
const message = parseEmbeddedCanvasPanMessage(data);
|
|
117
|
+
if (!message) return { handled: false, session };
|
|
118
|
+
|
|
119
|
+
if (message.phase === "start") {
|
|
120
|
+
if (session) return { handled: false, session };
|
|
121
|
+
} else if (
|
|
122
|
+
!session ||
|
|
123
|
+
session.pointerId !== message.pointerId ||
|
|
124
|
+
session.button !== message.button
|
|
125
|
+
) {
|
|
126
|
+
return { handled: false, session };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const frameRect = iframe.getBoundingClientRect();
|
|
130
|
+
const scaleX =
|
|
131
|
+
iframe.clientWidth > 0 && Number.isFinite(frameRect.width)
|
|
132
|
+
? frameRect.width / iframe.clientWidth
|
|
133
|
+
: 1;
|
|
134
|
+
const scaleY =
|
|
135
|
+
iframe.clientHeight > 0 && Number.isFinite(frameRect.height)
|
|
136
|
+
? frameRect.height / iframe.clientHeight
|
|
137
|
+
: 1;
|
|
138
|
+
const clientX = frameRect.left + message.clientX * scaleX;
|
|
139
|
+
const clientY = frameRect.top + message.clientY * scaleY;
|
|
140
|
+
const eventType =
|
|
141
|
+
message.phase === "start"
|
|
142
|
+
? "mousedown"
|
|
143
|
+
: message.phase === "move"
|
|
144
|
+
? "mousemove"
|
|
145
|
+
: "mouseup";
|
|
146
|
+
const HostMouseEvent = (hostWindow as Window & typeof globalThis).MouseEvent;
|
|
147
|
+
const forwarded = new HostMouseEvent(eventType, {
|
|
148
|
+
bubbles: true,
|
|
149
|
+
cancelable: true,
|
|
150
|
+
view: hostWindow,
|
|
151
|
+
button: message.button,
|
|
152
|
+
buttons:
|
|
153
|
+
message.phase === "end" || message.phase === "cancel"
|
|
154
|
+
? 0
|
|
155
|
+
: message.buttons,
|
|
156
|
+
clientX,
|
|
157
|
+
clientY,
|
|
158
|
+
ctrlKey: message.ctrlKey,
|
|
159
|
+
metaKey: message.metaKey,
|
|
160
|
+
shiftKey: message.shiftKey,
|
|
161
|
+
altKey: message.altKey,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (message.phase === "start") {
|
|
165
|
+
iframe.dispatchEvent(forwarded);
|
|
166
|
+
return {
|
|
167
|
+
handled: true,
|
|
168
|
+
session: { pointerId: message.pointerId, button: message.button },
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
hostWindow.dispatchEvent(forwarded);
|
|
173
|
+
return {
|
|
174
|
+
handled: true,
|
|
175
|
+
session: message.phase === "move" ? session : null,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type PendingTextEditKeyAction =
|
|
2
|
+
| { action: "buffer"; char: string }
|
|
3
|
+
| { action: "drop-last" }
|
|
4
|
+
| { action: "swallow" }
|
|
5
|
+
| { action: "clear-and-swallow" }
|
|
6
|
+
| { action: "pass" };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run text-edit activation immediately unless it was requested from inside a
|
|
10
|
+
* pointer gesture's mouseup handler. Browsers dispatch the trailing `click`
|
|
11
|
+
* after mouseup; focusing the new editable before that click makes the click
|
|
12
|
+
* immediately blur it again. Deferring one task preserves the already-armed
|
|
13
|
+
* keystroke buffer while letting that trailing click finish first.
|
|
14
|
+
*/
|
|
15
|
+
export function schedulePendingTextEditActivation(
|
|
16
|
+
activate: () => void,
|
|
17
|
+
options: {
|
|
18
|
+
afterPointerGesture?: boolean;
|
|
19
|
+
schedule?: (callback: () => void, delayMs: number) => void;
|
|
20
|
+
} = {},
|
|
21
|
+
): void {
|
|
22
|
+
if (!options.afterPointerGesture) {
|
|
23
|
+
activate();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const schedule =
|
|
27
|
+
options.schedule ??
|
|
28
|
+
((callback: () => void, delayMs: number) =>
|
|
29
|
+
window.setTimeout(callback, delayMs));
|
|
30
|
+
schedule(activate, POINTER_TEXT_EDIT_ACTIVATION_DELAY_MS);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function routePendingTextEditKey(event: {
|
|
34
|
+
key: string;
|
|
35
|
+
metaKey?: boolean;
|
|
36
|
+
ctrlKey?: boolean;
|
|
37
|
+
altKey?: boolean;
|
|
38
|
+
isComposing?: boolean;
|
|
39
|
+
}): PendingTextEditKeyAction {
|
|
40
|
+
if (event.isComposing) return { action: "pass" };
|
|
41
|
+
if (event.metaKey || event.ctrlKey) return { action: "pass" };
|
|
42
|
+
if (event.key === "Escape") return { action: "clear-and-swallow" };
|
|
43
|
+
if (event.key === "Backspace") return { action: "drop-last" };
|
|
44
|
+
if (
|
|
45
|
+
event.key === "Delete" ||
|
|
46
|
+
event.key === "Enter" ||
|
|
47
|
+
event.key === "Tab" ||
|
|
48
|
+
event.key.startsWith("Arrow")
|
|
49
|
+
) {
|
|
50
|
+
return { action: "swallow" };
|
|
51
|
+
}
|
|
52
|
+
if (event.key.length === 1) return { action: "buffer", char: event.key };
|
|
53
|
+
return { action: "pass" };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const PENDING_TEXT_EDIT_TIMEOUT_MS = 3000;
|
|
57
|
+
|
|
58
|
+
// Overview creation patches the target iframe document after persistence.
|
|
59
|
+
// Waiting through that bounded replacement avoids opening an edit session in
|
|
60
|
+
// the outgoing DOM (visible caret blink, then lost focus). Keystrokes remain
|
|
61
|
+
// lossless because DesignCanvas arms its pending buffer before scheduling.
|
|
62
|
+
export const POINTER_TEXT_EDIT_ACTIVATION_DELAY_MS = 300;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { DesignCanvas } from "./DesignCanvas";
|
|
2
|
+
export type { MotionTrackWire } from "./design-canvas/motion-types";
|
|
2
3
|
export type {
|
|
3
|
-
MotionTrackWire,
|
|
4
4
|
IframeHotkeyPayload,
|
|
5
5
|
IframeContextMenuPayload,
|
|
6
|
-
} from "./
|
|
6
|
+
} from "./design-canvas/iframe-events";
|
|
7
7
|
export { DeviceFrame } from "./DeviceFrame";
|
|
8
8
|
export { DrawOverlay } from "./DrawOverlay";
|
|
9
9
|
export { EditPanel } from "./EditPanel";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { MultiScreenCanvasTool, DraftCreationTool } from "./types";
|
|
2
|
+
|
|
3
|
+
export function normalizeCanvasTool(
|
|
4
|
+
tool: MultiScreenCanvasTool,
|
|
5
|
+
): MultiScreenCanvasTool {
|
|
6
|
+
return tool === "rectangle" ? "rect" : tool;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getDraftCreationTool(
|
|
10
|
+
tool: MultiScreenCanvasTool,
|
|
11
|
+
): DraftCreationTool | null {
|
|
12
|
+
if (
|
|
13
|
+
tool === "frame" ||
|
|
14
|
+
tool === "rect" ||
|
|
15
|
+
tool === "line" ||
|
|
16
|
+
tool === "arrow" ||
|
|
17
|
+
tool === "ellipse" ||
|
|
18
|
+
tool === "polygon" ||
|
|
19
|
+
tool === "star" ||
|
|
20
|
+
tool === "text" ||
|
|
21
|
+
tool === "pen"
|
|
22
|
+
) {
|
|
23
|
+
return tool;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isDirectScreenHoverTarget(
|
|
29
|
+
target: EventTarget | null,
|
|
30
|
+
currentTarget: HTMLElement,
|
|
31
|
+
) {
|
|
32
|
+
if (target === currentTarget) return true;
|
|
33
|
+
const element =
|
|
34
|
+
target && typeof (target as Element).closest === "function"
|
|
35
|
+
? (target as Element)
|
|
36
|
+
: null;
|
|
37
|
+
return !!element && !element.closest("[data-screen-content]");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** True only for native OS file drags, not internal HTML drag operations. */
|
|
41
|
+
export function isOsFileDrag(event: {
|
|
42
|
+
dataTransfer: { types: readonly string[] | DOMStringList } | null;
|
|
43
|
+
}): boolean {
|
|
44
|
+
const types = event.dataTransfer?.types;
|
|
45
|
+
if (!types) return false;
|
|
46
|
+
for (let index = 0; index < types.length; index += 1) {
|
|
47
|
+
if (types[index] === "Files") return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function shouldBoardSurfaceCapturePointerEvents(args: {
|
|
53
|
+
tool: string;
|
|
54
|
+
gestureActive?: boolean;
|
|
55
|
+
}) {
|
|
56
|
+
if (args.gestureActive) return false;
|
|
57
|
+
const tool = normalizeCanvasTool(args.tool as MultiScreenCanvasTool);
|
|
58
|
+
return (
|
|
59
|
+
!getDraftCreationTool(tool) &&
|
|
60
|
+
tool !== "hand" &&
|
|
61
|
+
tool !== "comment" &&
|
|
62
|
+
tool !== "draw"
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function shouldBeginCanvasPan(args: { button: number; tool: string }) {
|
|
67
|
+
return (
|
|
68
|
+
args.button === 1 ||
|
|
69
|
+
(args.button === 0 &&
|
|
70
|
+
normalizeCanvasTool(args.tool as MultiScreenCanvasTool) === "hand")
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function shouldShowFrameFullViewButton(args: {
|
|
75
|
+
emphasized: boolean;
|
|
76
|
+
showFullView?: boolean;
|
|
77
|
+
childHoverActive?: boolean;
|
|
78
|
+
}) {
|
|
79
|
+
return args.emphasized || !!args.showFullView || !!args.childHoverActive;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function shouldShowBreakpointMenuAffordance(args: {
|
|
83
|
+
canEdit: boolean;
|
|
84
|
+
hasRemoveOrChangeWidth: boolean;
|
|
85
|
+
isActive: boolean;
|
|
86
|
+
menuOpen: boolean;
|
|
87
|
+
}): boolean {
|
|
88
|
+
return (
|
|
89
|
+
args.canEdit &&
|
|
90
|
+
args.hasRemoveOrChangeWidth &&
|
|
91
|
+
(args.isActive || args.menuOpen)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function shouldClearSelectionOnEmptyCanvasClick(gesture: {
|
|
96
|
+
hasMoved: boolean;
|
|
97
|
+
additive: boolean;
|
|
98
|
+
}): boolean {
|
|
99
|
+
return !gesture.hasMoved && !gesture.additive;
|
|
100
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { rotatePoint } from "@shared/canvas-math";
|
|
2
|
+
|
|
3
|
+
import type { FrameGeometry, Point } from "./types";
|
|
4
|
+
|
|
5
|
+
export interface ScreenViewport {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function frameCenter(frame: FrameGeometry): Point {
|
|
11
|
+
return {
|
|
12
|
+
x: frame.x + frame.width / 2,
|
|
13
|
+
y: frame.y + frame.height / 2,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Maps a point from an iframe/screen's local pixel space into board space. */
|
|
18
|
+
export function screenLocalPointToBoardPoint(
|
|
19
|
+
point: Point,
|
|
20
|
+
frame: FrameGeometry,
|
|
21
|
+
viewport: ScreenViewport,
|
|
22
|
+
): Point {
|
|
23
|
+
const unrotated = {
|
|
24
|
+
x: frame.x + point.x * (frame.width / Math.max(1, viewport.width)),
|
|
25
|
+
y: frame.y + point.y * (frame.height / Math.max(1, viewport.height)),
|
|
26
|
+
};
|
|
27
|
+
return rotatePoint(unrotated, frameCenter(frame), frame.rotation ?? 0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Maps a board-space point into an iframe/screen's local pixel space. */
|
|
31
|
+
export function boardPointToScreenLocalPoint(
|
|
32
|
+
point: Point,
|
|
33
|
+
frame: FrameGeometry,
|
|
34
|
+
viewport: ScreenViewport,
|
|
35
|
+
): Point {
|
|
36
|
+
const unrotated = rotatePoint(
|
|
37
|
+
point,
|
|
38
|
+
frameCenter(frame),
|
|
39
|
+
-(frame.rotation ?? 0),
|
|
40
|
+
);
|
|
41
|
+
return {
|
|
42
|
+
x: (unrotated.x - frame.x) * (viewport.width / Math.max(1, frame.width)),
|
|
43
|
+
y: (unrotated.y - frame.y) * (viewport.height / Math.max(1, frame.height)),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Maps an axis-aligned screen-local rect into the board. The returned geometry
|
|
49
|
+
* keeps width/height in the screen's local axes and carries the screen's
|
|
50
|
+
* rotation, matching the CSS box needed to render/hit-test it on the board.
|
|
51
|
+
*/
|
|
52
|
+
export function screenLocalRectToBoardGeometry(
|
|
53
|
+
rect: { left: number; top: number; width: number; height: number },
|
|
54
|
+
frame: FrameGeometry,
|
|
55
|
+
viewport: ScreenViewport,
|
|
56
|
+
minSize = 1,
|
|
57
|
+
): FrameGeometry {
|
|
58
|
+
const scaleX = frame.width / Math.max(1, viewport.width);
|
|
59
|
+
const scaleY = frame.height / Math.max(1, viewport.height);
|
|
60
|
+
const width = Math.max(minSize, rect.width * scaleX);
|
|
61
|
+
const height = Math.max(minSize, rect.height * scaleY);
|
|
62
|
+
const unrotatedCenter = {
|
|
63
|
+
// Preserve the historical local-left/local-top mapping even when a tiny
|
|
64
|
+
// rect's rendered board size is clamped up to minSize.
|
|
65
|
+
x: frame.x + rect.left * scaleX + width / 2,
|
|
66
|
+
y: frame.y + rect.top * scaleY + height / 2,
|
|
67
|
+
};
|
|
68
|
+
const center = rotatePoint(
|
|
69
|
+
unrotatedCenter,
|
|
70
|
+
frameCenter(frame),
|
|
71
|
+
frame.rotation ?? 0,
|
|
72
|
+
);
|
|
73
|
+
const rotation = frame.rotation ?? 0;
|
|
74
|
+
return {
|
|
75
|
+
x: center.x - width / 2,
|
|
76
|
+
y: center.y - height / 2,
|
|
77
|
+
width,
|
|
78
|
+
height,
|
|
79
|
+
...(rotation ? { rotation } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|