@agent-native/core 0.182.1 → 0.182.2-nightly-20260917225811
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/templates/clips/app/root.tsx +59 -10
- package/corpus/templates/clips/changelog/2026-09-17-desktop-popover-closes-when-clicking-away.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-17-self-hosted-extension-sign-in.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +45 -25
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +43 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +55 -6
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +107 -19
- package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +1 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +54 -22
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +174 -14
- package/corpus/templates/design/actions/apply-visual-edit.ts +3 -1
- package/corpus/templates/design/actions/create-component.ts +173 -12
- package/corpus/templates/design/actions/create-design-from-template.ts +50 -35
- package/corpus/templates/design/actions/create-design-version.ts +21 -0
- package/corpus/templates/design/actions/create-file.ts +35 -33
- package/corpus/templates/design/actions/delete-design.ts +29 -28
- package/corpus/templates/design/actions/delete-file.ts +154 -23
- package/corpus/templates/design/actions/generate-design.ts +28 -16
- package/corpus/templates/design/actions/get-component-details.ts +272 -33
- package/corpus/templates/design/actions/get-design-version.ts +9 -1
- package/corpus/templates/design/actions/go-to-main-component.ts +37 -35
- package/corpus/templates/design/actions/index-components.ts +241 -126
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +12 -9
- package/corpus/templates/design/actions/present-design-variants.ts +68 -72
- package/corpus/templates/design/actions/rename-component.ts +294 -0
- package/corpus/templates/design/actions/rename-screen.ts +10 -10
- package/corpus/templates/design/actions/update-file.ts +327 -330
- package/corpus/templates/design/actions/update-screen-source.ts +2 -43
- package/corpus/templates/design/app/components/design/EditPanel.tsx +17 -1
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +228 -24
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +17 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +151 -32
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +4 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +8 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +55 -33
- package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +2 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +4 -2
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +273 -60
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +9 -2
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +57 -9
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +13 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +14 -0
- package/corpus/templates/design/app/components/design/types.ts +9 -1
- package/corpus/templates/design/app/hooks/use-design-versions.ts +3 -2
- package/corpus/templates/design/app/i18n-breakpoints.ts +0 -110
- package/corpus/templates/design/app/i18n-data.ts +0 -10
- package/corpus/templates/design/app/pages/DesignEditor.tsx +669 -161
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +20 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +21 -3
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-component.ts +44 -3
- package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +69 -47
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +157 -52
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +76 -1
- package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +152 -44
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +131 -2
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +5 -1
- package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +26 -1
- package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +1 -0
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +58 -8
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +63 -2
- package/corpus/templates/design/app/pages/design-editor/history.ts +3 -0
- package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +74 -22
- package/corpus/templates/design/app/pages/design-editor/source-publication.ts +14 -2
- package/corpus/templates/design/changelog/2026-09-16-design-components-retain-linked-identities-when-edited-and-d.md +6 -0
- package/corpus/templates/design/changelog/2026-09-16-fix-design-nested-layer-dragging-and-screen-duplication-pari.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-design-drag-and-drop-keeps-duplicates-cross-screen-moves-and.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-design-preserves-reusable-component-identity-and-props-acros.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-open-vector-paths-no-longer-show-fills-and-interact-dimensio.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-screen-deletion-is-immediate-and-undoable-with-history-that-.md +6 -0
- package/corpus/templates/design/e2e/fixtures/design-component-react/index.html +98 -0
- package/corpus/templates/design/e2e/fixtures/design-component-react/inline-component.html +65 -0
- package/corpus/templates/design/e2e/helpers.ts +6 -1
- package/corpus/templates/design/server/lib/design-data-mutation.ts +9 -5
- package/corpus/templates/design/server/lib/design-snapshot.ts +28 -16
- package/corpus/templates/design/server/lib/design-versions.ts +208 -130
- package/corpus/templates/design/server/lib/fusion-screens.ts +46 -20
- package/corpus/templates/design/server/lib/import-design-files.ts +5 -1
- package/corpus/templates/design/server/lib/localhost-connection.ts +54 -0
- package/corpus/templates/design/server/source-workspace.ts +412 -356
- package/corpus/templates/design/shared/component-library.ts +36 -6
- package/corpus/templates/design/shared/component-model.ts +12 -2
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +231 -5
- package/corpus/templates/design/shared/source-mode.ts +22 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/AssistantChat.d.ts +4 -1
- package/dist/client/AssistantChat.js +26 -3
- package/dist/client/chat/agentkit-protocol.js +148 -34
- package/dist/client/chat/message-components.js +7 -5
- package/dist/client/chat/tool-call-display.js +4 -3
- package/dist/client/conversation/AgentConversation.js +4 -2
- package/dist/client/tool-display.d.ts +9 -0
- package/dist/client/tool-display.js +15 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.js +2 -0
- package/dist/localization/core-messages/de-DE.js +2 -0
- package/dist/localization/core-messages/en-US.d.ts +2 -0
- package/dist/localization/core-messages/en-US.js +2 -0
- package/dist/localization/core-messages/es-ES.js +2 -0
- package/dist/localization/core-messages/fr-FR.js +2 -0
- package/dist/localization/core-messages/hi-IN.js +2 -0
- package/dist/localization/core-messages/ja-JP.js +2 -0
- package/dist/localization/core-messages/ko-KR.js +2 -0
- package/dist/localization/core-messages/pt-BR.js +2 -0
- package/dist/localization/core-messages/zh-CN.js +2 -0
- package/dist/localization/core-messages/zh-TW.js +2 -0
- package/dist/localization/core-messages.d.ts +2 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/package.json +3 -3
- package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +0 -66
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "../server/lib/design-data-mutation.js";
|
|
11
11
|
import { snapshotDesignBeforeAgentEdit } from "../server/lib/design-versions.js";
|
|
12
12
|
import {
|
|
13
|
-
|
|
13
|
+
fetchLocalhostSnapshot,
|
|
14
14
|
resolveLocalhostConnectionScope,
|
|
15
15
|
} from "../server/lib/localhost-connection.js";
|
|
16
16
|
import {
|
|
@@ -113,47 +113,6 @@ export function screenSourceMetadataForStatic(
|
|
|
113
113
|
return next;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
async function fetchFallbackSnapshot(args: {
|
|
117
|
-
bridgeUrl: string;
|
|
118
|
-
previewToken: string | null;
|
|
119
|
-
url: string;
|
|
120
|
-
}): Promise<string> {
|
|
121
|
-
if (!args.previewToken) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
"This URL-backed screen has no preview token. Reload the frame or reconnect the localhost app before switching it to static HTML.",
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
const endpoint = new URL("/snapshot", args.bridgeUrl);
|
|
127
|
-
endpoint.searchParams.set("url", args.url);
|
|
128
|
-
let response: Response;
|
|
129
|
-
try {
|
|
130
|
-
response = await fetch(endpoint, {
|
|
131
|
-
headers: {
|
|
132
|
-
accept: "application/json",
|
|
133
|
-
"x-design-preview-token": args.previewToken,
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
} catch (error) {
|
|
137
|
-
throw new Error(
|
|
138
|
-
`Could not reach the localhost bridge for snapshot (${error instanceof Error ? error.message : String(error)}).`,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
if (!response.ok) {
|
|
142
|
-
const errorText = await response.text().catch(() => response.statusText);
|
|
143
|
-
throw localhostBridgeRequestError("snapshot", response.status, errorText);
|
|
144
|
-
}
|
|
145
|
-
let payload: { html?: unknown } | null;
|
|
146
|
-
try {
|
|
147
|
-
payload = (await response.json()) as { html?: unknown } | null;
|
|
148
|
-
} catch {
|
|
149
|
-
throw new Error("The localhost bridge returned invalid snapshot JSON.");
|
|
150
|
-
}
|
|
151
|
-
if (!payload || typeof payload.html !== "string") {
|
|
152
|
-
throw new Error("The localhost bridge returned no HTML snapshot.");
|
|
153
|
-
}
|
|
154
|
-
return payload.html;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
116
|
export default defineAction({
|
|
158
117
|
description:
|
|
159
118
|
"Change one Design screen between static HTML and a live localhost URL. " +
|
|
@@ -348,7 +307,7 @@ export default defineAction({
|
|
|
348
307
|
}
|
|
349
308
|
const rawSnapshot =
|
|
350
309
|
snapshotHtml ??
|
|
351
|
-
(await
|
|
310
|
+
(await fetchLocalhostSnapshot({
|
|
352
311
|
bridgeUrl: connection.bridgeUrl,
|
|
353
312
|
previewToken: connection.previewToken,
|
|
354
313
|
url: currentUrl,
|
|
@@ -72,7 +72,10 @@ import {
|
|
|
72
72
|
truncateOpeningTag,
|
|
73
73
|
vscodeDeepLink,
|
|
74
74
|
} from "./edit-panel/code-inspect-helpers";
|
|
75
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
ComponentSection,
|
|
77
|
+
type RuntimeComponentDetails,
|
|
78
|
+
} from "./edit-panel/component-section";
|
|
76
79
|
import {
|
|
77
80
|
type DocumentColorSourceFile,
|
|
78
81
|
type SelectionColorValue,
|
|
@@ -188,6 +191,7 @@ import {
|
|
|
188
191
|
} from "./inspector";
|
|
189
192
|
import { IconText } from "./inspector/design-icons";
|
|
190
193
|
import { type GlslShaderPanelContext } from "./inspector/GlslShaderPanel";
|
|
194
|
+
import type { LocalhostWriteConsentPayload } from "./LocalhostWriteConsentDialog";
|
|
191
195
|
import { getActiveScreenIframeId } from "./multi-screen/iframe-targeting";
|
|
192
196
|
import type { ScreenHeightMode } from "./multi-screen/screen-height";
|
|
193
197
|
import {
|
|
@@ -452,6 +456,14 @@ interface EditPanelProps {
|
|
|
452
456
|
* and an Edit component action.
|
|
453
457
|
*/
|
|
454
458
|
componentNodeId?: string;
|
|
459
|
+
/** Runtime component metadata for URL-backed React selections. */
|
|
460
|
+
componentRuntime?: RuntimeComponentDetails;
|
|
461
|
+
/** Request the existing localhost write-consent dialog before source writes. */
|
|
462
|
+
requestLocalhostWrite?: (opts: {
|
|
463
|
+
files: string[];
|
|
464
|
+
onGranted: LocalhostWriteConsentPayload["onGranted"];
|
|
465
|
+
onCancel?: () => void;
|
|
466
|
+
}) => void;
|
|
455
467
|
/** True when the selected component node has reached the accepted source. */
|
|
456
468
|
componentDetailsReady?: boolean;
|
|
457
469
|
/** True when the selected linked component instance stores local overrides. */
|
|
@@ -2429,6 +2441,8 @@ export const EditPanel = memo(function EditPanel({
|
|
|
2429
2441
|
reviewCommentsPanelProps,
|
|
2430
2442
|
reviewCommentsCount = 0,
|
|
2431
2443
|
componentNodeId,
|
|
2444
|
+
componentRuntime,
|
|
2445
|
+
requestLocalhostWrite,
|
|
2432
2446
|
componentDetailsReady = true,
|
|
2433
2447
|
componentInstanceHasLocalOverrides = false,
|
|
2434
2448
|
onResetComponentInstanceOverrides,
|
|
@@ -3064,6 +3078,8 @@ export const EditPanel = memo(function EditPanel({
|
|
|
3064
3078
|
activeFileUpdatedAt={activeFileUpdatedAt}
|
|
3065
3079
|
componentDetailsReady={componentDetailsReady}
|
|
3066
3080
|
nodeId={componentNodeId}
|
|
3081
|
+
runtime={componentRuntime}
|
|
3082
|
+
requestLocalhostWrite={requestLocalhostWrite}
|
|
3067
3083
|
hasLocalOverrides={componentInstanceHasLocalOverrides}
|
|
3068
3084
|
swapPickerRequest={componentSwapPickerRequest}
|
|
3069
3085
|
onResetOverrides={
|
|
@@ -582,6 +582,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
582
582
|
getScreenMetadata,
|
|
583
583
|
onDuplicate,
|
|
584
584
|
geometryById,
|
|
585
|
+
geometryOverridesById,
|
|
585
586
|
onGeometryChange,
|
|
586
587
|
onGeometryCommit,
|
|
587
588
|
onBreakpointContentHeightChange,
|
|
@@ -649,6 +650,9 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
649
650
|
gradientEditTarget,
|
|
650
651
|
onDropFiles,
|
|
651
652
|
cameraCommand,
|
|
653
|
+
suppressLineupRecenter,
|
|
654
|
+
preserveCameraOnScreenCountChange = false,
|
|
655
|
+
deferLineupZoomChange = false,
|
|
652
656
|
chromeInsetLeft = 0,
|
|
653
657
|
chromeInsetRight = 0,
|
|
654
658
|
}: MultiScreenCanvasProps) {
|
|
@@ -680,6 +684,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
680
684
|
const [surfaceSize, setSurfaceSize] = useState({ width: 0, height: 0 });
|
|
681
685
|
const [frameGeometry, setFrameGeometry] = useState<FrameGeometryById>({});
|
|
682
686
|
const frameGeometryRef = useRef(frameGeometry);
|
|
687
|
+
const renderedScreenIdsRef = useRef<Set<string>>(new Set());
|
|
683
688
|
// Resize commits must start from what the user sees, including content-fit
|
|
684
689
|
// height, because the parent pins the committed viewport dimensions.
|
|
685
690
|
const renderedFrameGeometryRef = useRef<FrameGeometryById>({});
|
|
@@ -982,6 +987,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
982
987
|
const lineupRecenterSuppressRef = useRef<LineupRecenterDuplicateArm | null>(
|
|
983
988
|
null,
|
|
984
989
|
);
|
|
990
|
+
const lastSuppressLineupRecenterNonceRef = useRef<number | null>(null);
|
|
985
991
|
const lineupRecenterDeviceFrameRef = useRef(previewDeviceFrame);
|
|
986
992
|
const lineupRecenterPrevCountRef = useRef<number | null>(null);
|
|
987
993
|
const handledSelectAllRequestRef = useRef(selectAllRequest);
|
|
@@ -1095,9 +1101,13 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
1095
1101
|
id: string;
|
|
1096
1102
|
geometry: FrameGeometry;
|
|
1097
1103
|
}
|
|
1098
|
-
/** Last claim
|
|
1099
|
-
* nothing does not spam postMessage.
|
|
1100
|
-
|
|
1104
|
+
/** Last claim sent to the source iframe, so a move tick that changes
|
|
1105
|
+
* nothing does not spam postMessage. Keep the source id with it so a
|
|
1106
|
+
* cleared claim can be revoked after the payload is gone. */
|
|
1107
|
+
const crossScreenClaimSentRef = useRef<{
|
|
1108
|
+
sourceScreenId: string;
|
|
1109
|
+
claimed: boolean;
|
|
1110
|
+
} | null>(null);
|
|
1101
1111
|
const crossScreenProxyTraceRef = useRef<string | null>(null);
|
|
1102
1112
|
const crossScreenResolveTraceRef = useRef<string | null>(null);
|
|
1103
1113
|
const [crossScreenGhost, setCrossScreenGhost] =
|
|
@@ -1709,7 +1719,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
1709
1719
|
zoomRef.current = canvasZoom;
|
|
1710
1720
|
}, [canvasZoom]);
|
|
1711
1721
|
|
|
1712
|
-
|
|
1722
|
+
useLayoutEffect(() => {
|
|
1713
1723
|
frameGeometryRef.current = frameGeometry;
|
|
1714
1724
|
}, [frameGeometry]);
|
|
1715
1725
|
|
|
@@ -1918,7 +1928,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
1918
1928
|
zoom,
|
|
1919
1929
|
]);
|
|
1920
1930
|
|
|
1921
|
-
|
|
1931
|
+
useLayoutEffect(() => {
|
|
1922
1932
|
const selectableIds = new Set(selectableScreens.map((screen) => screen.id));
|
|
1923
1933
|
// B5-9: see resolveFrameGeometrySync's doc comment — this used to notify
|
|
1924
1934
|
// the parent (onGeometryChange -> queueFrameGeometrySave) with a brand
|
|
@@ -1959,6 +1969,10 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
1959
1969
|
updateSelectedIds,
|
|
1960
1970
|
]);
|
|
1961
1971
|
|
|
1972
|
+
useLayoutEffect(() => {
|
|
1973
|
+
renderedScreenIdsRef.current = new Set(screens.map((screen) => screen.id));
|
|
1974
|
+
}, [screens]);
|
|
1975
|
+
|
|
1962
1976
|
useEffect(() => {
|
|
1963
1977
|
const previous = previousPreviewDeviceFrameRef.current;
|
|
1964
1978
|
previousPreviewDeviceFrameRef.current = previewDeviceFrame;
|
|
@@ -2030,8 +2044,25 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
2030
2044
|
setTransformBadge(null);
|
|
2031
2045
|
}, [clearSelectionRequest, updateSelectedDraftIds, updateSelectedIds]);
|
|
2032
2046
|
|
|
2047
|
+
useEffect(() => {
|
|
2048
|
+
if (
|
|
2049
|
+
!suppressLineupRecenter ||
|
|
2050
|
+
lastSuppressLineupRecenterNonceRef.current ===
|
|
2051
|
+
suppressLineupRecenter.nonce
|
|
2052
|
+
) {
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
lastSuppressLineupRecenterNonceRef.current = suppressLineupRecenter.nonce;
|
|
2056
|
+
lineupRecenterSuppressRef.current = {
|
|
2057
|
+
atMs: Date.now(),
|
|
2058
|
+
fromCount: suppressLineupRecenter.fromCount,
|
|
2059
|
+
addedCount: suppressLineupRecenter.addedCount,
|
|
2060
|
+
};
|
|
2061
|
+
}, [suppressLineupRecenter]);
|
|
2062
|
+
|
|
2033
2063
|
// Center the lineup when the screen footprint changes so new frames stay reachable.
|
|
2034
2064
|
useEffect(() => {
|
|
2065
|
+
if (preserveCameraOnScreenCountChange) return;
|
|
2035
2066
|
const deviceFrameChanged =
|
|
2036
2067
|
lineupRecenterDeviceFrameRef.current !== previewDeviceFrame;
|
|
2037
2068
|
lineupRecenterDeviceFrameRef.current = previewDeviceFrame;
|
|
@@ -2166,8 +2197,10 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
2166
2197
|
totalHeight > 0
|
|
2167
2198
|
? Math.max(minFitScale, (rect.height - 96) / totalHeight)
|
|
2168
2199
|
: scale;
|
|
2169
|
-
const nextScale =
|
|
2170
|
-
|
|
2200
|
+
const nextScale = deferLineupZoomChange
|
|
2201
|
+
? scale
|
|
2202
|
+
: Math.min(scale, widthFitScale, heightFitScale);
|
|
2203
|
+
if (!deferLineupZoomChange && nextScale < scale) {
|
|
2171
2204
|
const nextZoom = nextScale * 100;
|
|
2172
2205
|
zoomRef.current = nextZoom;
|
|
2173
2206
|
setCanvasZoom(nextZoom);
|
|
@@ -2192,7 +2225,14 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
2192
2225
|
};
|
|
2193
2226
|
// Only on mount, screen-count or chrome-inset changes, or device-preview changes.
|
|
2194
2227
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2195
|
-
}, [
|
|
2228
|
+
}, [
|
|
2229
|
+
chromeInsetLeft,
|
|
2230
|
+
chromeInsetRight,
|
|
2231
|
+
preserveCameraOnScreenCountChange,
|
|
2232
|
+
deferLineupZoomChange,
|
|
2233
|
+
previewDeviceFrame,
|
|
2234
|
+
screens.length,
|
|
2235
|
+
]);
|
|
2196
2236
|
|
|
2197
2237
|
useEffect(() => {
|
|
2198
2238
|
return () => {
|
|
@@ -2556,9 +2596,30 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
2556
2596
|
crossScreenParentDragCleanupRef.current = null;
|
|
2557
2597
|
};
|
|
2558
2598
|
|
|
2599
|
+
const postCrossScreenClaim = (sourceScreenId: string, claimed: boolean) => {
|
|
2600
|
+
const sourceScreen = screensRef.current.find(
|
|
2601
|
+
(item) => item.id === sourceScreenId,
|
|
2602
|
+
);
|
|
2603
|
+
const iframeId = sourceScreen
|
|
2604
|
+
? getActiveScreenIframeId(sourceScreen)
|
|
2605
|
+
: sourceScreenId;
|
|
2606
|
+
findCanvasIframeForScreen(
|
|
2607
|
+
surfaceRef.current,
|
|
2608
|
+
iframeId,
|
|
2609
|
+
boardFileId,
|
|
2610
|
+
)?.contentWindow?.postMessage(
|
|
2611
|
+
{ type: "agent-native:cross-screen-claim", claimed },
|
|
2612
|
+
"*",
|
|
2613
|
+
);
|
|
2614
|
+
};
|
|
2615
|
+
|
|
2559
2616
|
const clearCrossScreenDrag = () => {
|
|
2560
2617
|
stopParentCrossScreenDrag();
|
|
2561
2618
|
clearCrossScreenPreviewGuide();
|
|
2619
|
+
const previousClaim = crossScreenClaimSentRef.current;
|
|
2620
|
+
if (previousClaim?.claimed) {
|
|
2621
|
+
postCrossScreenClaim(previousClaim.sourceScreenId, false);
|
|
2622
|
+
}
|
|
2562
2623
|
crossScreenClaimSentRef.current = null;
|
|
2563
2624
|
crossScreenProxyTraceRef.current = null;
|
|
2564
2625
|
crossScreenResolveTraceRef.current = null;
|
|
@@ -2897,22 +2958,15 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
2897
2958
|
* knows where the screens are, and the board surface iframe covers all of
|
|
2898
2959
|
* them, so without this both sides commit the same gesture. */
|
|
2899
2960
|
const claimCrossScreenDrop = (sourceScreenId: string, claimed: boolean) => {
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
)
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
surfaceRef.current,
|
|
2910
|
-
iframeId,
|
|
2911
|
-
boardFileId,
|
|
2912
|
-
)?.contentWindow?.postMessage(
|
|
2913
|
-
{ type: "agent-native:cross-screen-claim", claimed },
|
|
2914
|
-
"*",
|
|
2915
|
-
);
|
|
2961
|
+
const previousClaim = crossScreenClaimSentRef.current;
|
|
2962
|
+
if (
|
|
2963
|
+
previousClaim?.sourceScreenId === sourceScreenId &&
|
|
2964
|
+
previousClaim.claimed === claimed
|
|
2965
|
+
) {
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2968
|
+
crossScreenClaimSentRef.current = { sourceScreenId, claimed };
|
|
2969
|
+
postCrossScreenClaim(sourceScreenId, claimed);
|
|
2916
2970
|
};
|
|
2917
2971
|
|
|
2918
2972
|
const updateCrossScreenTargetFromBoardPoint = (
|
|
@@ -3465,6 +3519,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
3465
3519
|
// every screen frame, so board-origin drags must still be checked
|
|
3466
3520
|
// against screen drop targets while technically "inside" the source.
|
|
3467
3521
|
if (pointerInsideSourceIframe && !sourceIsBoard) {
|
|
3522
|
+
claimCrossScreenDrop(sourceScreenId, false);
|
|
3468
3523
|
clearCrossScreenPreviewGuide();
|
|
3469
3524
|
setCrossScreenGhost(null);
|
|
3470
3525
|
setCrossScreenTarget(null);
|
|
@@ -6715,6 +6770,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6715
6770
|
mode: "alt-drag",
|
|
6716
6771
|
screen: target.screen,
|
|
6717
6772
|
canvasPosition,
|
|
6773
|
+
preserveCamera: true,
|
|
6718
6774
|
canvasOffset: {
|
|
6719
6775
|
x: dropCanvasPosition.x - canvasPosition.x,
|
|
6720
6776
|
y: dropCanvasPosition.y - canvasPosition.y,
|
|
@@ -7640,6 +7696,133 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
7640
7696
|
],
|
|
7641
7697
|
);
|
|
7642
7698
|
|
|
7699
|
+
const beginBoardElementDrag = useCallback(
|
|
7700
|
+
(e: React.MouseEvent) => {
|
|
7701
|
+
if (readOnly) return;
|
|
7702
|
+
if (e.button !== 0) return;
|
|
7703
|
+
if (!boardFileId || !boardSurfaceRenderGeometry) return;
|
|
7704
|
+
const iframe = findCanvasIframeForScreen(
|
|
7705
|
+
surfaceRef.current,
|
|
7706
|
+
boardFileId,
|
|
7707
|
+
boardFileId,
|
|
7708
|
+
);
|
|
7709
|
+
const iframeDoc = iframe?.contentWindow?.document;
|
|
7710
|
+
if (!iframeDoc) {
|
|
7711
|
+
dndHostLog("board-move:no-iframe", { boardFileId });
|
|
7712
|
+
return;
|
|
7713
|
+
}
|
|
7714
|
+
const selectionOverlay = iframeDoc.querySelector<HTMLElement>(
|
|
7715
|
+
'[data-agent-native-edit-overlay="selection"]',
|
|
7716
|
+
);
|
|
7717
|
+
if (!selectionOverlay) {
|
|
7718
|
+
dndHostLog("board-move:no-selection-overlay", { boardFileId });
|
|
7719
|
+
return;
|
|
7720
|
+
}
|
|
7721
|
+
e.preventDefault();
|
|
7722
|
+
e.stopPropagation();
|
|
7723
|
+
|
|
7724
|
+
const toIframePoint = (clientX: number, clientY: number) =>
|
|
7725
|
+
boardPointToBoardSurfaceLocalPoint(
|
|
7726
|
+
getCanvasPoint(clientX, clientY),
|
|
7727
|
+
boardSurfaceRenderGeometryRef.current ?? boardSurfaceRenderGeometry,
|
|
7728
|
+
);
|
|
7729
|
+
const dispatchAt = (
|
|
7730
|
+
target: EventTarget,
|
|
7731
|
+
type: string,
|
|
7732
|
+
point: { x: number; y: number },
|
|
7733
|
+
source: {
|
|
7734
|
+
shiftKey: boolean;
|
|
7735
|
+
altKey: boolean;
|
|
7736
|
+
metaKey: boolean;
|
|
7737
|
+
ctrlKey: boolean;
|
|
7738
|
+
},
|
|
7739
|
+
buttons: number,
|
|
7740
|
+
) => {
|
|
7741
|
+
target.dispatchEvent(
|
|
7742
|
+
new MouseEvent(type, {
|
|
7743
|
+
clientX: point.x,
|
|
7744
|
+
clientY: point.y,
|
|
7745
|
+
shiftKey: source.shiftKey,
|
|
7746
|
+
altKey: source.altKey,
|
|
7747
|
+
metaKey: source.metaKey,
|
|
7748
|
+
ctrlKey: source.ctrlKey,
|
|
7749
|
+
buttons,
|
|
7750
|
+
bubbles: true,
|
|
7751
|
+
cancelable: true,
|
|
7752
|
+
}),
|
|
7753
|
+
);
|
|
7754
|
+
};
|
|
7755
|
+
|
|
7756
|
+
setIsDragging(true);
|
|
7757
|
+
dispatchAt(
|
|
7758
|
+
selectionOverlay,
|
|
7759
|
+
"mousedown",
|
|
7760
|
+
toIframePoint(e.clientX, e.clientY),
|
|
7761
|
+
e,
|
|
7762
|
+
1,
|
|
7763
|
+
);
|
|
7764
|
+
|
|
7765
|
+
const handleMouseMove = (ev: MouseEvent) => {
|
|
7766
|
+
dispatchAt(
|
|
7767
|
+
iframeDoc,
|
|
7768
|
+
"mousemove",
|
|
7769
|
+
toIframePoint(ev.clientX, ev.clientY),
|
|
7770
|
+
ev,
|
|
7771
|
+
1,
|
|
7772
|
+
);
|
|
7773
|
+
};
|
|
7774
|
+
const cancelMove = (pressedAt: number) => {
|
|
7775
|
+
iframe.contentWindow?.postMessage(
|
|
7776
|
+
{ type: "agent-native:cancel-active-drag", pressedAt },
|
|
7777
|
+
"*",
|
|
7778
|
+
);
|
|
7779
|
+
};
|
|
7780
|
+
const handleMouseUp = (ev: MouseEvent) => {
|
|
7781
|
+
// The host selection box sits above every Screen and follows the
|
|
7782
|
+
// moving board object, so DOM hit testing sees the chrome instead of
|
|
7783
|
+
// the card underneath it. Resolve the release in board-space geometry
|
|
7784
|
+
// so selection chrome cannot steal a board-to-screen drop.
|
|
7785
|
+
const droppedOnScreen = Boolean(
|
|
7786
|
+
getFrameEntryAtPoint(getCanvasPoint(ev.clientX, ev.clientY)),
|
|
7787
|
+
);
|
|
7788
|
+
if (droppedOnScreen) {
|
|
7789
|
+
// The host owns board-to-screen drops. Cancel the iframe gesture
|
|
7790
|
+
// after its cross-screen writer has taken the release.
|
|
7791
|
+
cancelMove(performance.timeOrigin + performance.now());
|
|
7792
|
+
} else {
|
|
7793
|
+
// No screen owns this release, so let the board bridge commit its
|
|
7794
|
+
// ordinary board-to-board move or duplicate.
|
|
7795
|
+
iframe.contentWindow?.postMessage(
|
|
7796
|
+
{ type: "agent-native:cross-screen-claim", claimed: false },
|
|
7797
|
+
"*",
|
|
7798
|
+
);
|
|
7799
|
+
dispatchAt(
|
|
7800
|
+
iframeDoc,
|
|
7801
|
+
"mouseup",
|
|
7802
|
+
toIframePoint(ev.clientX, ev.clientY),
|
|
7803
|
+
ev,
|
|
7804
|
+
0,
|
|
7805
|
+
);
|
|
7806
|
+
}
|
|
7807
|
+
finishDrag();
|
|
7808
|
+
};
|
|
7809
|
+
boardElementResizeCancel.current = cancelMove;
|
|
7810
|
+
installDragListeners(handleMouseMove, handleMouseUp, () => {
|
|
7811
|
+
cancelMove(performance.timeOrigin + performance.now());
|
|
7812
|
+
finishDrag();
|
|
7813
|
+
});
|
|
7814
|
+
},
|
|
7815
|
+
[
|
|
7816
|
+
boardFileId,
|
|
7817
|
+
boardSurfaceRenderGeometry,
|
|
7818
|
+
finishDrag,
|
|
7819
|
+
getCanvasPoint,
|
|
7820
|
+
getFrameEntryAtPoint,
|
|
7821
|
+
installDragListeners,
|
|
7822
|
+
readOnly,
|
|
7823
|
+
],
|
|
7824
|
+
);
|
|
7825
|
+
|
|
7643
7826
|
const beginRotate = useCallback(
|
|
7644
7827
|
(id: string, e: React.MouseEvent) => {
|
|
7645
7828
|
if (readOnly) return;
|
|
@@ -9051,6 +9234,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
9051
9234
|
mode: "alt-click",
|
|
9052
9235
|
screen,
|
|
9053
9236
|
canvasPosition,
|
|
9237
|
+
preserveCamera: true,
|
|
9054
9238
|
dropCanvasPosition: canvasPosition,
|
|
9055
9239
|
});
|
|
9056
9240
|
dispatched += 1;
|
|
@@ -9272,8 +9456,25 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
9272
9456
|
const next = renderedScreens.map((screen) => {
|
|
9273
9457
|
nextIds.add(screen.id);
|
|
9274
9458
|
const metadata = getResolvedMetadata(screen);
|
|
9459
|
+
const geometryOverride = geometryOverridesById?.[screen.id];
|
|
9460
|
+
const persistedGeometry = geometryById?.[screen.id];
|
|
9461
|
+
const persistedGeometryIsComplete =
|
|
9462
|
+
persistedGeometry &&
|
|
9463
|
+
typeof persistedGeometry.x === "number" &&
|
|
9464
|
+
typeof persistedGeometry.y === "number" &&
|
|
9465
|
+
typeof persistedGeometry.width === "number" &&
|
|
9466
|
+
typeof persistedGeometry.height === "number";
|
|
9275
9467
|
const rawGeometry =
|
|
9468
|
+
geometryOverride ??
|
|
9469
|
+
(persistedGeometryIsComplete &&
|
|
9470
|
+
(!frameGeometry[screen.id] ||
|
|
9471
|
+
!renderedScreenIdsRef.current.has(screen.id))
|
|
9472
|
+
? (persistedGeometry as FrameGeometry)
|
|
9473
|
+
: undefined) ??
|
|
9276
9474
|
frameGeometry[screen.id] ??
|
|
9475
|
+
(persistedGeometryIsComplete
|
|
9476
|
+
? (persistedGeometry as FrameGeometry)
|
|
9477
|
+
: undefined) ??
|
|
9277
9478
|
getInitialFrameGeometry(screenIndexById.get(screen.id) ?? 0, metadata);
|
|
9278
9479
|
// Content-fit height, applied ONLY once the frame's own content has been
|
|
9279
9480
|
// measured, and only for inline (srcdoc) screens — URL-backed
|
|
@@ -9334,6 +9535,8 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
9334
9535
|
return next;
|
|
9335
9536
|
}, [
|
|
9336
9537
|
frameGeometry,
|
|
9538
|
+
geometryById,
|
|
9539
|
+
geometryOverridesById,
|
|
9337
9540
|
getResolvedMetadata,
|
|
9338
9541
|
measuredIframeHeights,
|
|
9339
9542
|
measuredIframeNaturalHeights,
|
|
@@ -10258,6 +10461,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
10258
10461
|
boardObject
|
|
10259
10462
|
onStartResize={beginBoardElementResize}
|
|
10260
10463
|
onStartRotate={() => {}}
|
|
10464
|
+
onStartDrag={beginBoardElementDrag}
|
|
10261
10465
|
/>
|
|
10262
10466
|
) : null}
|
|
10263
10467
|
|
|
@@ -404,7 +404,23 @@ export function ResponsiveInteractBar({
|
|
|
404
404
|
<div className="flex shrink-0 items-center bg-[var(--design-editor-panel-bg)] pl-1">
|
|
405
405
|
<ResponsiveInteractExitButton onClose={onClose} />
|
|
406
406
|
</div>
|
|
407
|
-
) :
|
|
407
|
+
) : (
|
|
408
|
+
<div
|
|
409
|
+
aria-hidden="true"
|
|
410
|
+
className="invisible flex shrink-0 items-center pl-1"
|
|
411
|
+
>
|
|
412
|
+
<Button
|
|
413
|
+
variant="ghost"
|
|
414
|
+
size="sm"
|
|
415
|
+
disabled
|
|
416
|
+
tabIndex={-1}
|
|
417
|
+
className="h-7 shrink-0 gap-1.5 rounded-md px-2 !text-[12px]"
|
|
418
|
+
>
|
|
419
|
+
<IconX className="size-4" />
|
|
420
|
+
{t("designEditor.responsiveInteract.exit")}
|
|
421
|
+
</Button>
|
|
422
|
+
</div>
|
|
423
|
+
)}
|
|
408
424
|
</div>
|
|
409
425
|
);
|
|
410
426
|
}
|