@agent-native/core 0.84.0 → 0.84.2
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 +13 -0
- package/corpus/core/docs/content/evals.mdx +34 -12
- package/corpus/core/docs/content/locales/ar-SA/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/de-DE/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/fr-FR/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/hi-IN/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/ja-JP/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/ko-KR/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/pt-BR/evals.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-CN/evals.mdx +7 -0
- package/corpus/core/docs/content/locales/zh-TW/evals.mdx +7 -0
- package/corpus/core/docs/design/durable-agent-runs.md +34 -35
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/builder-engine.ts +81 -9
- package/corpus/core/src/agent/run-manager.ts +8 -7
- package/corpus/core/src/cli/design-connect.ts +175 -0
- package/corpus/core/src/cli/skills.ts +5 -7
- package/corpus/core/src/client/AgentPanel.tsx +8 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +4 -1
- package/corpus/core/src/client/agent-chat-adapter.ts +45 -15
- package/corpus/core/src/client/sse-event-processor.ts +31 -4
- package/corpus/core/src/eval/define-eval.ts +4 -1
- package/corpus/core/src/eval/report.ts +23 -8
- package/corpus/core/src/eval/runner.ts +41 -6
- package/corpus/core/src/eval/types.ts +11 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
- package/corpus/templates/content/AGENTS.md +9 -2
- package/corpus/templates/content/actions/_database-row-batch.ts +230 -0
- package/corpus/templates/content/actions/_database-utils.ts +17 -9
- package/corpus/templates/content/actions/_notion-action-utils.ts +26 -0
- package/corpus/templates/content/actions/connect-notion-status.ts +13 -4
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +23 -0
- package/corpus/templates/content/actions/delete-database-items.ts +55 -0
- package/corpus/templates/content/actions/delete-document.ts +4 -4
- package/corpus/templates/content/actions/disconnect-notion.ts +16 -0
- package/corpus/templates/content/actions/duplicate-database-item.ts +1 -1
- package/corpus/templates/content/actions/duplicate-database-items.ts +191 -0
- package/corpus/templates/content/actions/link-notion-page.ts +10 -7
- package/corpus/templates/content/actions/list-notion-links.ts +3 -4
- package/corpus/templates/content/actions/pull-notion-page.ts +7 -9
- package/corpus/templates/content/actions/push-notion-page.ts +7 -9
- package/corpus/templates/content/actions/refresh-notion-sync-status.ts +24 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +23 -0
- package/corpus/templates/content/actions/search-notion-pages.ts +67 -0
- package/corpus/templates/content/actions/unlink-notion-page.ts +23 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +27 -34
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +16 -13
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +10 -10
- package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +1 -1
- package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +18 -9
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +27 -34
- package/corpus/templates/content/app/components/editor/database/navigation-state.ts +10 -4
- package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +32 -17
- package/corpus/templates/content/app/hooks/use-content-database.ts +35 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +108 -99
- package/corpus/templates/content/package.json +3 -0
- package/corpus/templates/content/parity/README.md +69 -0
- package/corpus/templates/content/parity/eval-scenarios.ts +118 -0
- package/corpus/templates/content/parity/exceptions.allowlist.ts +17 -0
- package/corpus/templates/content/parity/matrix.md +28 -0
- package/corpus/templates/content/parity/matrix.ts +606 -0
- package/corpus/templates/content/parity/matrix.types.ts +42 -0
- package/corpus/templates/content/parity/parity-evals.eval.ts +4 -0
- package/corpus/templates/content/parity/render-matrix.ts +90 -0
- package/corpus/templates/content/parity/scenario-to-eval.ts +57 -0
- package/corpus/templates/content/shared/api.ts +11 -0
- package/corpus/templates/design/actions/apply-motion-edit.ts +50 -18
- package/corpus/templates/design/actions/get-motion-timeline.ts +4 -14
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +156 -12
- package/corpus/templates/design/app/components/design/MotionDock.tsx +2 -3
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +217 -75
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +39 -39
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +558 -37
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +75 -3
- package/corpus/templates/design/app/components/design/types.ts +13 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +17 -0
- package/corpus/templates/design/app/i18n-data.ts +245 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +762 -153
- package/corpus/templates/design/changelog/2026-06-30-copying-or-dragging-screen-elements-onto-the-infinite-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-questions-now-use-tighter-editor-typography-and-contr.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-element-drags-can-be-cancelled-with-escape-before-they-commi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-pending-visual-style-edits-now-warn-before-you-leave-the-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-visual-style-drags-stay-live-while-pending-edits-can-be-appl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-design-chat-no-longer-shows-a-redundant-context-tab-above-the-composer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-motion-track-creation-no-longer-fails-in-local-editors.md +6 -0
- package/corpus/templates/design/server/plugins/db.ts +10 -0
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +61 -10
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/run-manager.d.ts +8 -7
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +8 -7
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/design-connect.d.ts +2 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +140 -0
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +5 -7
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +3 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +2 -2
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +37 -14
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +14 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +21 -5
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/eval/define-eval.d.ts.map +1 -1
- package/dist/eval/define-eval.js +2 -1
- package/dist/eval/define-eval.js.map +1 -1
- package/dist/eval/report.d.ts.map +1 -1
- package/dist/eval/report.js +19 -4
- package/dist/eval/report.js.map +1 -1
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +38 -6
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval/types.d.ts +11 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/eval/types.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
- package/docs/content/evals.mdx +34 -12
- package/docs/content/locales/ar-SA/evals.mdx +7 -0
- package/docs/content/locales/de-DE/evals.mdx +8 -0
- package/docs/content/locales/es-ES/evals.mdx +8 -0
- package/docs/content/locales/fr-FR/evals.mdx +8 -0
- package/docs/content/locales/hi-IN/evals.mdx +8 -0
- package/docs/content/locales/ja-JP/evals.mdx +7 -0
- package/docs/content/locales/ko-KR/evals.mdx +7 -0
- package/docs/content/locales/pt-BR/evals.mdx +8 -0
- package/docs/content/locales/zh-CN/evals.mdx +7 -0
- package/docs/content/locales/zh-TW/evals.mdx +7 -0
- package/docs/design/durable-agent-runs.md +34 -35
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
- package/corpus/templates/content/server/routes/api/notion/disconnect.post.ts +0 -12
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
type DeviceFrameType,
|
|
61
61
|
type ElementInfo,
|
|
62
62
|
type ElementSelectionIntent,
|
|
63
|
+
type PortableStyleSnapshot,
|
|
63
64
|
} from "./types";
|
|
64
65
|
|
|
65
66
|
// Re-export so consumers of MultiScreenCanvas can use the same script without
|
|
@@ -241,12 +242,18 @@ interface MultiScreenCanvasProps {
|
|
|
241
242
|
targetAnchorNodeId?: string;
|
|
242
243
|
/** DOM insertion placement relative to the anchor node. */
|
|
243
244
|
targetAnchorPlacement?: "before" | "after" | "inside";
|
|
245
|
+
/** Whether the target should receive an in-flow insert or an absolute child. */
|
|
246
|
+
targetDropMode?: CrossScreenDropMode;
|
|
247
|
+
/** Target anchor rect in the destination iframe/content coordinate space. */
|
|
248
|
+
targetAnchorRect?: CrossScreenHitTestAnchorRect;
|
|
244
249
|
/** Final drop point in logical overview canvas coordinates. */
|
|
245
250
|
targetCanvasPoint?: Point;
|
|
246
251
|
/** Final drop point in the destination iframe/content coordinate space. */
|
|
247
252
|
targetLocalPoint?: Point;
|
|
248
253
|
/** Pointer offset from the dragged element's top-left in source iframe px. */
|
|
249
254
|
sourcePointerOffset?: Point;
|
|
255
|
+
/** Portable computed styles captured in the source iframe before the move. */
|
|
256
|
+
styleSnapshot?: PortableStyleSnapshot;
|
|
250
257
|
}) => void;
|
|
251
258
|
// ── Board file (new model) ───────────────────────────────────────────────
|
|
252
259
|
/**
|
|
@@ -338,6 +345,9 @@ interface MultiScreenCanvasProps {
|
|
|
338
345
|
sourceId?: string;
|
|
339
346
|
anchorSourceId?: string;
|
|
340
347
|
requestId?: string;
|
|
348
|
+
dropMode?: "flow-insert" | "absolute-container";
|
|
349
|
+
sourceRect?: { x: number; y: number; width: number; height: number };
|
|
350
|
+
anchorRect?: { x: number; y: number; width: number; height: number };
|
|
341
351
|
},
|
|
342
352
|
) => boolean | void;
|
|
343
353
|
/**
|
|
@@ -480,7 +490,7 @@ export function hasBoardSurfaceContent(html: string | undefined) {
|
|
|
480
490
|
return content.replace(/<!--[\s\S]*?-->/g, "").trim().length > 0;
|
|
481
491
|
}
|
|
482
492
|
|
|
483
|
-
const BOARD_SURFACE_RENDER_STYLE = `<style data-agent-native-board-surface-render>html,body{background:transparent!important;background-color:transparent!important;background-image:none!important;}body{margin:0!important;position:relative;overflow:visible;}body>:not([data-agent-native-node-id]):not(style):not(script),body>[data-agent-native-node-id]:not([data-an-primitive]):has([data-agent-native-node-id]),body>[data-agent-native-node-id="body"],body>[data-agent-native-node-id="Body"],body>[data-agent-native-layer-name="body"],body>[data-agent-native-layer-name="Body"],body>[data-agent-native-layer-name="<body>"]{background:transparent!important;background-color:transparent!important;background-image:none!important;box-shadow:none!important;}[data-agent-native-board-backdrop-candidate="true"]{display:none!important;pointer-events:none!important;}</style>`;
|
|
493
|
+
const BOARD_SURFACE_RENDER_STYLE = `<style data-agent-native-board-surface-render>html,body{background:transparent!important;background-color:transparent!important;background-image:none!important;}body{margin:0!important;position:relative;overflow:visible;}body>:not([data-agent-native-node-id]):not(style):not(script),body>[data-agent-native-node-id]:not([data-an-primitive]):not([data-agent-native-preserve-styles="true"]):has([data-agent-native-node-id]),body>[data-agent-native-node-id="body"],body>[data-agent-native-node-id="Body"],body>[data-agent-native-layer-name="body"],body>[data-agent-native-layer-name="Body"],body>[data-agent-native-layer-name="<body>"]{background:transparent!important;background-color:transparent!important;background-image:none!important;box-shadow:none!important;}[data-agent-native-board-backdrop-candidate="true"]{display:none!important;pointer-events:none!important;}</style>`;
|
|
484
494
|
const BOARD_SURFACE_BACKGROUND = "hsl(0 0% 10%)";
|
|
485
495
|
|
|
486
496
|
const BOARD_SURFACE_BACKDROP_MIN_EDGE_PX = 2400;
|
|
@@ -678,7 +688,7 @@ export function getBoardContentKey(args: {
|
|
|
678
688
|
boardFileContent: string;
|
|
679
689
|
boardIsActive: boolean;
|
|
680
690
|
}) {
|
|
681
|
-
return `${args.boardFileId}:
|
|
691
|
+
return `${args.boardFileId}:surface`;
|
|
682
692
|
}
|
|
683
693
|
|
|
684
694
|
function getChromeHandleTransition(chromeSettling: boolean) {
|
|
@@ -746,6 +756,7 @@ export interface Point {
|
|
|
746
756
|
|
|
747
757
|
export type CrossScreenDropPlacement = "before" | "after" | "inside";
|
|
748
758
|
export type CrossScreenDropAxis = "x" | "y";
|
|
759
|
+
export type CrossScreenDropMode = "flow-insert" | "absolute-container";
|
|
749
760
|
|
|
750
761
|
export interface CrossScreenHitTestAnchorRect {
|
|
751
762
|
left: number;
|
|
@@ -758,6 +769,7 @@ export interface CrossScreenHitTestResult {
|
|
|
758
769
|
anchorNodeId?: string;
|
|
759
770
|
placement?: CrossScreenDropPlacement;
|
|
760
771
|
axis?: CrossScreenDropAxis;
|
|
772
|
+
dropMode?: CrossScreenDropMode;
|
|
761
773
|
anchorRect?: CrossScreenHitTestAnchorRect;
|
|
762
774
|
}
|
|
763
775
|
|
|
@@ -774,6 +786,14 @@ function isFinitePoint(value: unknown): value is Point {
|
|
|
774
786
|
return Number.isFinite(point.x) && Number.isFinite(point.y);
|
|
775
787
|
}
|
|
776
788
|
|
|
789
|
+
function isPortableStyleSnapshot(
|
|
790
|
+
value: unknown,
|
|
791
|
+
): value is PortableStyleSnapshot {
|
|
792
|
+
if (!value || typeof value !== "object") return false;
|
|
793
|
+
const snapshot = value as Record<string, unknown>;
|
|
794
|
+
return snapshot.version === 1 && Array.isArray(snapshot.nodes);
|
|
795
|
+
}
|
|
796
|
+
|
|
777
797
|
interface CanvasLayerMarqueeCandidate {
|
|
778
798
|
screenId: string;
|
|
779
799
|
info: ElementInfo;
|
|
@@ -802,6 +822,10 @@ function isCrossScreenDropAxis(value: unknown): value is CrossScreenDropAxis {
|
|
|
802
822
|
return value === "x" || value === "y";
|
|
803
823
|
}
|
|
804
824
|
|
|
825
|
+
function isCrossScreenDropMode(value: unknown): value is CrossScreenDropMode {
|
|
826
|
+
return value === "flow-insert" || value === "absolute-container";
|
|
827
|
+
}
|
|
828
|
+
|
|
805
829
|
function isCrossScreenHitTestAnchorRect(
|
|
806
830
|
value: unknown,
|
|
807
831
|
): value is CrossScreenHitTestAnchorRect {
|
|
@@ -1183,6 +1207,9 @@ export function MultiScreenCanvas({
|
|
|
1183
1207
|
/** Board-space point where the ghost is shown (follows the cursor). */
|
|
1184
1208
|
boardX: number;
|
|
1185
1209
|
boardY: number;
|
|
1210
|
+
width?: number;
|
|
1211
|
+
height?: number;
|
|
1212
|
+
dimmed?: boolean;
|
|
1186
1213
|
}
|
|
1187
1214
|
interface CrossScreenDragTarget {
|
|
1188
1215
|
/** The screen frame that is the candidate drop target. */
|
|
@@ -1206,6 +1233,8 @@ export function MultiScreenCanvas({
|
|
|
1206
1233
|
selector: string;
|
|
1207
1234
|
sourceId?: string;
|
|
1208
1235
|
sourcePointerOffset?: Point;
|
|
1236
|
+
sourceElementSize?: { width: number; height: number };
|
|
1237
|
+
styleSnapshot?: PortableStyleSnapshot;
|
|
1209
1238
|
} | null>(null);
|
|
1210
1239
|
const crossScreenParentDragCleanupRef = useRef<(() => void) | null>(null);
|
|
1211
1240
|
/** Board-space point from the last cross-screen-drag "move" message. */
|
|
@@ -1715,6 +1744,9 @@ export function MultiScreenCanvas({
|
|
|
1715
1744
|
axis: isCrossScreenDropAxis(ev.data.axis)
|
|
1716
1745
|
? ev.data.axis
|
|
1717
1746
|
: undefined,
|
|
1747
|
+
dropMode: isCrossScreenDropMode(ev.data.dropMode)
|
|
1748
|
+
? ev.data.dropMode
|
|
1749
|
+
: undefined,
|
|
1718
1750
|
anchorRect: isCrossScreenHitTestAnchorRect(ev.data.anchorRect)
|
|
1719
1751
|
? ev.data.anchorRect
|
|
1720
1752
|
: undefined,
|
|
@@ -1767,26 +1799,23 @@ export function MultiScreenCanvas({
|
|
|
1767
1799
|
const requestCrossScreenDropGuide = (
|
|
1768
1800
|
candidate: CrossScreenDragTarget,
|
|
1769
1801
|
boardPoint: Point,
|
|
1770
|
-
sourceIsBoard: boolean,
|
|
1771
1802
|
) => {
|
|
1772
1803
|
const requestSeq = ++crossScreenHitTestSeqRef.current;
|
|
1773
|
-
void runHitTest(candidate, boardPoint
|
|
1774
|
-
(
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
},
|
|
1789
|
-
);
|
|
1804
|
+
void runHitTest(candidate, boardPoint).then((hit) => {
|
|
1805
|
+
if (crossScreenHitTestSeqRef.current !== requestSeq) return;
|
|
1806
|
+
if (crossScreenTargetRef.current?.id !== candidate.id) return;
|
|
1807
|
+
const targetScreen = screensRef.current.find(
|
|
1808
|
+
(s) => s.id === candidate.id,
|
|
1809
|
+
);
|
|
1810
|
+
const guide = targetScreen
|
|
1811
|
+
? getCrossScreenDropGuideForHitTest({
|
|
1812
|
+
hit,
|
|
1813
|
+
targetGeometry: candidate.geometry,
|
|
1814
|
+
targetMetadata: getResolvedMetadata(targetScreen),
|
|
1815
|
+
})
|
|
1816
|
+
: null;
|
|
1817
|
+
setCrossScreenDropGuide(guide);
|
|
1818
|
+
});
|
|
1790
1819
|
};
|
|
1791
1820
|
|
|
1792
1821
|
const updateCrossScreenTargetFromBoardPoint = (
|
|
@@ -1796,9 +1825,6 @@ export function MultiScreenCanvas({
|
|
|
1796
1825
|
crossScreenLastBoardPointRef.current = boardPoint;
|
|
1797
1826
|
const sourceIsBoard = sourceScreenId === boardFileId;
|
|
1798
1827
|
setCrossScreenSourceIsBoard(sourceIsBoard);
|
|
1799
|
-
setCrossScreenGhost(
|
|
1800
|
-
sourceIsBoard ? null : { boardX: boardPoint.x, boardY: boardPoint.y },
|
|
1801
|
-
);
|
|
1802
1828
|
const target = getFrameEntryAtPoint(boardPoint);
|
|
1803
1829
|
if (target && target.id !== sourceScreenId) {
|
|
1804
1830
|
const nextTarget = { id: target.id, geometry: target.geometry };
|
|
@@ -1807,11 +1833,30 @@ export function MultiScreenCanvas({
|
|
|
1807
1833
|
}
|
|
1808
1834
|
crossScreenTargetRef.current = nextTarget;
|
|
1809
1835
|
setCrossScreenTarget(nextTarget);
|
|
1810
|
-
|
|
1836
|
+
const dragPayload = crossScreenDragMsgRef.current;
|
|
1837
|
+
const sourceElementSize = dragPayload?.sourceElementSize;
|
|
1838
|
+
const sourcePointerOffset = dragPayload?.sourcePointerOffset;
|
|
1839
|
+
setCrossScreenGhost(
|
|
1840
|
+
sourceIsBoard && sourceElementSize && sourcePointerOffset
|
|
1841
|
+
? {
|
|
1842
|
+
boardX: boardPoint.x - sourcePointerOffset.x,
|
|
1843
|
+
boardY: boardPoint.y - sourcePointerOffset.y,
|
|
1844
|
+
width: sourceElementSize.width,
|
|
1845
|
+
height: sourceElementSize.height,
|
|
1846
|
+
dimmed: true,
|
|
1847
|
+
}
|
|
1848
|
+
: sourceIsBoard
|
|
1849
|
+
? null
|
|
1850
|
+
: { boardX: boardPoint.x, boardY: boardPoint.y },
|
|
1851
|
+
);
|
|
1852
|
+
requestCrossScreenDropGuide(nextTarget, boardPoint);
|
|
1811
1853
|
} else {
|
|
1812
1854
|
clearCrossScreenPreviewGuide();
|
|
1813
1855
|
crossScreenTargetRef.current = null;
|
|
1814
1856
|
setCrossScreenTarget(null);
|
|
1857
|
+
setCrossScreenGhost(
|
|
1858
|
+
sourceIsBoard ? null : { boardX: boardPoint.x, boardY: boardPoint.y },
|
|
1859
|
+
);
|
|
1815
1860
|
clearCrossScreenDropGuide();
|
|
1816
1861
|
}
|
|
1817
1862
|
};
|
|
@@ -1823,6 +1868,7 @@ export function MultiScreenCanvas({
|
|
|
1823
1868
|
selector: string;
|
|
1824
1869
|
sourceId?: string;
|
|
1825
1870
|
sourcePointerOffset?: Point;
|
|
1871
|
+
styleSnapshot?: PortableStyleSnapshot;
|
|
1826
1872
|
},
|
|
1827
1873
|
lastBoardPoint: Point | null,
|
|
1828
1874
|
) => {
|
|
@@ -1847,12 +1893,13 @@ export function MultiScreenCanvas({
|
|
|
1847
1893
|
targetCanvasPoint: lastBoardPoint,
|
|
1848
1894
|
targetLocalPoint: lastBoardPoint,
|
|
1849
1895
|
sourcePointerOffset: payload.sourcePointerOffset,
|
|
1896
|
+
styleSnapshot: payload.styleSnapshot,
|
|
1850
1897
|
});
|
|
1851
1898
|
return;
|
|
1852
1899
|
}
|
|
1853
1900
|
|
|
1854
1901
|
void runHitTest(targetCandidate, lastBoardPoint).then(
|
|
1855
|
-
({ anchorNodeId, placement }) => {
|
|
1902
|
+
({ anchorNodeId, placement, dropMode, anchorRect }) => {
|
|
1856
1903
|
const targetAnchorPlacement = isCrossScreenDropPlacement(placement)
|
|
1857
1904
|
? placement
|
|
1858
1905
|
: undefined;
|
|
@@ -1867,9 +1914,12 @@ export function MultiScreenCanvas({
|
|
|
1867
1914
|
targetScreenId: targetCandidate.id,
|
|
1868
1915
|
targetAnchorNodeId: anchorNodeId,
|
|
1869
1916
|
targetAnchorPlacement,
|
|
1917
|
+
targetDropMode: dropMode,
|
|
1918
|
+
targetAnchorRect: anchorRect,
|
|
1870
1919
|
targetCanvasPoint: lastBoardPoint,
|
|
1871
1920
|
targetLocalPoint: targetLocalPoint ?? undefined,
|
|
1872
1921
|
sourcePointerOffset: payload.sourcePointerOffset,
|
|
1922
|
+
styleSnapshot: payload.styleSnapshot,
|
|
1873
1923
|
});
|
|
1874
1924
|
},
|
|
1875
1925
|
);
|
|
@@ -1891,7 +1941,22 @@ export function MultiScreenCanvas({
|
|
|
1891
1941
|
viewportH?: number;
|
|
1892
1942
|
elementRect?: CrossScreenDragElementRect;
|
|
1893
1943
|
pointerOffset?: Point;
|
|
1944
|
+
styleSnapshot?: unknown;
|
|
1894
1945
|
};
|
|
1946
|
+
const sourcePointerOffset = isFinitePoint(msg.pointerOffset)
|
|
1947
|
+
? msg.pointerOffset
|
|
1948
|
+
: undefined;
|
|
1949
|
+
const sourceElementSize =
|
|
1950
|
+
msg.elementRect &&
|
|
1951
|
+
Number.isFinite(msg.elementRect.width) &&
|
|
1952
|
+
Number.isFinite(msg.elementRect.height) &&
|
|
1953
|
+
msg.elementRect.width > 0 &&
|
|
1954
|
+
msg.elementRect.height > 0
|
|
1955
|
+
? { width: msg.elementRect.width, height: msg.elementRect.height }
|
|
1956
|
+
: undefined;
|
|
1957
|
+
const styleSnapshot = isPortableStyleSnapshot(msg.styleSnapshot)
|
|
1958
|
+
? msg.styleSnapshot
|
|
1959
|
+
: undefined;
|
|
1895
1960
|
|
|
1896
1961
|
if (msg.phase === "cancel") {
|
|
1897
1962
|
clearCrossScreenDrag();
|
|
@@ -1919,9 +1984,9 @@ export function MultiScreenCanvas({
|
|
|
1919
1984
|
crossScreenDragMsgRef.current = {
|
|
1920
1985
|
selector: msg.selector ?? "",
|
|
1921
1986
|
sourceId: msg.sourceId,
|
|
1922
|
-
sourcePointerOffset
|
|
1923
|
-
|
|
1924
|
-
|
|
1987
|
+
sourcePointerOffset,
|
|
1988
|
+
sourceElementSize,
|
|
1989
|
+
styleSnapshot,
|
|
1925
1990
|
};
|
|
1926
1991
|
stopParentCrossScreenDrag();
|
|
1927
1992
|
const restorePreviewPointerEvents = mutePreviewIframePointerEvents(
|
|
@@ -1944,9 +2009,9 @@ export function MultiScreenCanvas({
|
|
|
1944
2009
|
const payload = crossScreenDragMsgRef.current ?? {
|
|
1945
2010
|
selector: msg.selector ?? "",
|
|
1946
2011
|
sourceId: msg.sourceId,
|
|
1947
|
-
sourcePointerOffset
|
|
1948
|
-
|
|
1949
|
-
|
|
2012
|
+
sourcePointerOffset,
|
|
2013
|
+
sourceElementSize,
|
|
2014
|
+
styleSnapshot,
|
|
1950
2015
|
};
|
|
1951
2016
|
const lastBoardPoint = crossScreenLastBoardPointRef.current;
|
|
1952
2017
|
finalizeCrossScreenDrop(
|
|
@@ -1993,9 +2058,14 @@ export function MultiScreenCanvas({
|
|
|
1993
2058
|
crossScreenDragMsgRef.current = {
|
|
1994
2059
|
selector: selector ?? "",
|
|
1995
2060
|
sourceId,
|
|
1996
|
-
sourcePointerOffset:
|
|
1997
|
-
|
|
1998
|
-
|
|
2061
|
+
sourcePointerOffset:
|
|
2062
|
+
sourcePointerOffset ??
|
|
2063
|
+
crossScreenDragMsgRef.current?.sourcePointerOffset,
|
|
2064
|
+
sourceElementSize:
|
|
2065
|
+
sourceElementSize ??
|
|
2066
|
+
crossScreenDragMsgRef.current?.sourceElementSize,
|
|
2067
|
+
styleSnapshot:
|
|
2068
|
+
styleSnapshot ?? crossScreenDragMsgRef.current?.styleSnapshot,
|
|
1999
2069
|
};
|
|
2000
2070
|
|
|
2001
2071
|
const pointerInsideSourceIframe =
|
|
@@ -2060,9 +2130,9 @@ export function MultiScreenCanvas({
|
|
|
2060
2130
|
const payload = crossScreenDragMsgRef.current ?? {
|
|
2061
2131
|
selector: msg.selector ?? "",
|
|
2062
2132
|
sourceId: msg.sourceId,
|
|
2063
|
-
sourcePointerOffset
|
|
2064
|
-
|
|
2065
|
-
|
|
2133
|
+
sourcePointerOffset,
|
|
2134
|
+
sourceElementSize,
|
|
2135
|
+
styleSnapshot,
|
|
2066
2136
|
};
|
|
2067
2137
|
const lastBoardPoint = crossScreenLastBoardPointRef.current;
|
|
2068
2138
|
finalizeCrossScreenDrop(
|
|
@@ -2341,15 +2411,62 @@ export function MultiScreenCanvas({
|
|
|
2341
2411
|
draggedNodeId: string | null,
|
|
2342
2412
|
): PrimitiveDropTarget | null => {
|
|
2343
2413
|
if (!onPrimitiveReparentRef.current) return null;
|
|
2414
|
+
const screensForPrimitiveHitTest =
|
|
2415
|
+
boardFileId && boardFileContent !== undefined && boardFrameGeometry
|
|
2416
|
+
? [
|
|
2417
|
+
...screensRef.current,
|
|
2418
|
+
{
|
|
2419
|
+
id: boardFileId,
|
|
2420
|
+
filename: "__board__.html",
|
|
2421
|
+
content: boardFileContent,
|
|
2422
|
+
},
|
|
2423
|
+
]
|
|
2424
|
+
: screensRef.current;
|
|
2425
|
+
const frameGeometryForPrimitiveHitTest =
|
|
2426
|
+
boardFileId && boardFrameGeometry
|
|
2427
|
+
? {
|
|
2428
|
+
...frameGeometryRef.current,
|
|
2429
|
+
[boardFileId]: boardFrameGeometry,
|
|
2430
|
+
}
|
|
2431
|
+
: frameGeometryRef.current;
|
|
2344
2432
|
return getPrimitiveDropTargetForPoint(
|
|
2345
2433
|
point,
|
|
2346
2434
|
draggedNodeId,
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2435
|
+
screensForPrimitiveHitTest,
|
|
2436
|
+
frameGeometryForPrimitiveHitTest,
|
|
2437
|
+
(screen) =>
|
|
2438
|
+
screen.id === boardFileId && boardFrameGeometry
|
|
2439
|
+
? {
|
|
2440
|
+
width: Math.max(1, boardFrameGeometry.width),
|
|
2441
|
+
height: Math.max(1, boardFrameGeometry.height),
|
|
2442
|
+
}
|
|
2443
|
+
: getResolvedMetadata(screen),
|
|
2444
|
+
{
|
|
2445
|
+
identityCoordinateScreenIds: boardFileId
|
|
2446
|
+
? new Set([boardFileId])
|
|
2447
|
+
: undefined,
|
|
2448
|
+
},
|
|
2350
2449
|
);
|
|
2351
2450
|
},
|
|
2352
|
-
[getResolvedMetadata],
|
|
2451
|
+
[boardFileContent, boardFileId, boardFrameGeometry, getResolvedMetadata],
|
|
2452
|
+
);
|
|
2453
|
+
|
|
2454
|
+
const resolvePrimitiveScreenId = useCallback(
|
|
2455
|
+
(nodeId: string): string | null => {
|
|
2456
|
+
const screensForPrimitiveLookup =
|
|
2457
|
+
boardFileId && boardFileContent !== undefined
|
|
2458
|
+
? [
|
|
2459
|
+
...screensRef.current,
|
|
2460
|
+
{
|
|
2461
|
+
id: boardFileId,
|
|
2462
|
+
filename: "__board__.html",
|
|
2463
|
+
content: boardFileContent,
|
|
2464
|
+
},
|
|
2465
|
+
]
|
|
2466
|
+
: screensRef.current;
|
|
2467
|
+
return resolveNodeScreenId(nodeId, screensForPrimitiveLookup);
|
|
2468
|
+
},
|
|
2469
|
+
[boardFileContent, boardFileId],
|
|
2353
2470
|
);
|
|
2354
2471
|
|
|
2355
2472
|
const finishDrag = useCallback(() => {
|
|
@@ -3613,10 +3730,7 @@ export function MultiScreenCanvas({
|
|
|
3613
3730
|
(targetId) => !currentFrameIds.includes(targetId),
|
|
3614
3731
|
);
|
|
3615
3732
|
if (allCommitted && dropTarget) {
|
|
3616
|
-
const sourceScreenId =
|
|
3617
|
-
state.primaryId,
|
|
3618
|
-
screensRef.current,
|
|
3619
|
-
);
|
|
3733
|
+
const sourceScreenId = resolvePrimitiveScreenId(state.primaryId);
|
|
3620
3734
|
if (sourceScreenId) {
|
|
3621
3735
|
onPrimitiveReparentRef.current?.({
|
|
3622
3736
|
sourceNodeId: state.primaryId,
|
|
@@ -3652,6 +3766,7 @@ export function MultiScreenCanvas({
|
|
|
3652
3766
|
getCurrentFrameEntries,
|
|
3653
3767
|
installDragListeners,
|
|
3654
3768
|
onPick,
|
|
3769
|
+
resolvePrimitiveScreenId,
|
|
3655
3770
|
updateFrameGeometry,
|
|
3656
3771
|
updatePrimitiveDropTarget,
|
|
3657
3772
|
updateSelectedDraftIds,
|
|
@@ -4711,6 +4826,8 @@ export function MultiScreenCanvas({
|
|
|
4711
4826
|
boardFileContent,
|
|
4712
4827
|
boardIsActive,
|
|
4713
4828
|
});
|
|
4829
|
+
const boardLayerSignature =
|
|
4830
|
+
getBoardContentLayerSignature(boardFileContent);
|
|
4714
4831
|
const boardRenderContent =
|
|
4715
4832
|
getBoardSurfaceRenderContent(boardFileContent);
|
|
4716
4833
|
return (
|
|
@@ -4727,6 +4844,8 @@ export function MultiScreenCanvas({
|
|
|
4727
4844
|
<DesignCanvas
|
|
4728
4845
|
content={boardRenderContent}
|
|
4729
4846
|
contentKey={boardContentKey}
|
|
4847
|
+
runtimeReplacementContent={boardRenderContent}
|
|
4848
|
+
runtimeReplacementKey={`${boardFileId}:layers:${boardLayerSignature}`}
|
|
4730
4849
|
screenId={boardFileId}
|
|
4731
4850
|
zoom={100}
|
|
4732
4851
|
deviceFrame="none"
|
|
@@ -5060,22 +5179,27 @@ export function MultiScreenCanvas({
|
|
|
5060
5179
|
/>
|
|
5061
5180
|
) : null}
|
|
5062
5181
|
|
|
5063
|
-
{/* Cross-screen element drag:
|
|
5182
|
+
{/* Cross-screen element drag: ghost follows the board-space cursor. */}
|
|
5064
5183
|
{crossScreenGhost &&
|
|
5065
|
-
|
|
5066
|
-
!crossScreenDropGuide ? (
|
|
5184
|
+
(crossScreenSourceIsBoard || !crossScreenDropGuide) ? (
|
|
5067
5185
|
<span
|
|
5068
5186
|
data-cross-screen-drag-ghost
|
|
5069
|
-
className="pointer-events-none absolute z-
|
|
5187
|
+
className="pointer-events-none absolute z-40 rounded border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-accent-color)]/20 shadow"
|
|
5070
5188
|
style={{
|
|
5071
|
-
//
|
|
5072
|
-
//
|
|
5189
|
+
// Board-origin drags use the real layer size/top-left so the
|
|
5190
|
+
// proxy stays above screen iframes; screen-origin drags keep the
|
|
5191
|
+
// older compact cursor ghost.
|
|
5073
5192
|
left:
|
|
5074
|
-
pan.x +
|
|
5193
|
+
pan.x +
|
|
5194
|
+
(SURFACE_PADDING + crossScreenGhost.boardX) * scale -
|
|
5195
|
+
(crossScreenGhost.width ? 0 : 8),
|
|
5075
5196
|
top:
|
|
5076
|
-
pan.y +
|
|
5077
|
-
|
|
5078
|
-
|
|
5197
|
+
pan.y +
|
|
5198
|
+
(SURFACE_PADDING + crossScreenGhost.boardY) * scale -
|
|
5199
|
+
(crossScreenGhost.height ? 0 : 8),
|
|
5200
|
+
width: Math.max(1, (crossScreenGhost.width ?? 16) * scale),
|
|
5201
|
+
height: Math.max(1, (crossScreenGhost.height ?? 16) * scale),
|
|
5202
|
+
opacity: crossScreenGhost.dimmed ? 0.4 : 1,
|
|
5079
5203
|
}}
|
|
5080
5204
|
/>
|
|
5081
5205
|
) : null}
|
|
@@ -7334,6 +7458,9 @@ export function parsePrimitivesFromScreen(
|
|
|
7334
7458
|
const htmlEl = el as HTMLElement;
|
|
7335
7459
|
const style = htmlEl.style;
|
|
7336
7460
|
const tag = el.tagName.toLowerCase();
|
|
7461
|
+
const primitiveKind = (
|
|
7462
|
+
el.getAttribute("data-an-primitive") || ""
|
|
7463
|
+
).toLowerCase();
|
|
7337
7464
|
|
|
7338
7465
|
// Only block elements with explicit absolute positioning are considered
|
|
7339
7466
|
// positioned primitives drawn by appendCanvasPrimitiveToHtml.
|
|
@@ -7347,9 +7474,9 @@ export function parsePrimitivesFromScreen(
|
|
|
7347
7474
|
// Validity: must have non-zero size
|
|
7348
7475
|
if (width <= 0 || height <= 0) return;
|
|
7349
7476
|
|
|
7350
|
-
// Only explicit
|
|
7351
|
-
//
|
|
7352
|
-
//
|
|
7477
|
+
// Only explicit layout primitives and canvas-created rectangles accept
|
|
7478
|
+
// drops. Avoid treating every absolute div as a container, because imported
|
|
7479
|
+
// app markup can contain structural wrappers that should stay inert here.
|
|
7353
7480
|
const isDiv = tag === "div";
|
|
7354
7481
|
const isEllipse =
|
|
7355
7482
|
style.borderRadius === "50%" ||
|
|
@@ -7360,8 +7487,13 @@ export function parsePrimitivesFromScreen(
|
|
|
7360
7487
|
style.display === "inline-flex" ||
|
|
7361
7488
|
style.display === "grid" ||
|
|
7362
7489
|
style.display === "inline-grid";
|
|
7490
|
+
const isCanvasRectangle =
|
|
7491
|
+
isDiv && (primitiveKind === "rectangle" || primitiveKind === "rect");
|
|
7363
7492
|
const isContainer =
|
|
7364
|
-
isDiv &&
|
|
7493
|
+
isDiv &&
|
|
7494
|
+
!isEllipse &&
|
|
7495
|
+
!isTextAutoSize &&
|
|
7496
|
+
(isAutoLayout || isCanvasRectangle);
|
|
7365
7497
|
|
|
7366
7498
|
result.push({
|
|
7367
7499
|
nodeId,
|
|
@@ -7429,7 +7561,31 @@ export function getPrimitiveDropTargetForPoint(
|
|
|
7429
7561
|
screens: ScreenFile[],
|
|
7430
7562
|
frameGeometryById: FrameGeometryById,
|
|
7431
7563
|
getMetadata: (screen: ScreenFile) => { width: number; height: number },
|
|
7564
|
+
options: { identityCoordinateScreenIds?: ReadonlySet<string> } = {},
|
|
7432
7565
|
): PrimitiveDropTarget | null {
|
|
7566
|
+
const toBoardRect = (
|
|
7567
|
+
prim: ParsedScreenPrimitive,
|
|
7568
|
+
frameGeometry: FrameGeometry,
|
|
7569
|
+
metadata: { width: number; height: number },
|
|
7570
|
+
): FrameGeometry => {
|
|
7571
|
+
if (options.identityCoordinateScreenIds?.has(prim.screenId)) {
|
|
7572
|
+
return {
|
|
7573
|
+
x: prim.localLeft,
|
|
7574
|
+
y: prim.localTop,
|
|
7575
|
+
width: Math.max(1, prim.localWidth),
|
|
7576
|
+
height: Math.max(1, prim.localHeight),
|
|
7577
|
+
};
|
|
7578
|
+
}
|
|
7579
|
+
return primitiveLocalToBoardRect(
|
|
7580
|
+
prim.localLeft,
|
|
7581
|
+
prim.localTop,
|
|
7582
|
+
prim.localWidth,
|
|
7583
|
+
prim.localHeight,
|
|
7584
|
+
frameGeometry,
|
|
7585
|
+
metadata,
|
|
7586
|
+
);
|
|
7587
|
+
};
|
|
7588
|
+
|
|
7433
7589
|
// Pre-compute the dragged node's board rect so we can exclude its descendants.
|
|
7434
7590
|
let draggedBoardRect: FrameGeometry | null = null;
|
|
7435
7591
|
if (draggedNodeId) {
|
|
@@ -7440,14 +7596,7 @@ export function getPrimitiveDropTargetForPoint(
|
|
|
7440
7596
|
const primitives = parsePrimitivesFromScreen(screen);
|
|
7441
7597
|
for (const prim of primitives) {
|
|
7442
7598
|
if (prim.nodeId === draggedNodeId) {
|
|
7443
|
-
draggedBoardRect =
|
|
7444
|
-
prim.localLeft,
|
|
7445
|
-
prim.localTop,
|
|
7446
|
-
prim.localWidth,
|
|
7447
|
-
prim.localHeight,
|
|
7448
|
-
frameGeometry,
|
|
7449
|
-
metadata,
|
|
7450
|
-
);
|
|
7599
|
+
draggedBoardRect = toBoardRect(prim, frameGeometry, metadata);
|
|
7451
7600
|
break outer;
|
|
7452
7601
|
}
|
|
7453
7602
|
}
|
|
@@ -7477,14 +7626,7 @@ export function getPrimitiveDropTargetForPoint(
|
|
|
7477
7626
|
if (!prim.isContainer) continue;
|
|
7478
7627
|
if (draggedNodeId && prim.nodeId === draggedNodeId) continue;
|
|
7479
7628
|
|
|
7480
|
-
const boardRect =
|
|
7481
|
-
prim.localLeft,
|
|
7482
|
-
prim.localTop,
|
|
7483
|
-
prim.localWidth,
|
|
7484
|
-
prim.localHeight,
|
|
7485
|
-
frameGeometry,
|
|
7486
|
-
metadata,
|
|
7487
|
-
);
|
|
7629
|
+
const boardRect = toBoardRect(prim, frameGeometry, metadata);
|
|
7488
7630
|
|
|
7489
7631
|
// Exclude geometric descendants: a primitive whose board rect is fully
|
|
7490
7632
|
// contained within the dragged node's board rect is a child/descendant
|