@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
|
@@ -252,6 +252,7 @@ export function runVisualDuplicateChange(
|
|
|
252
252
|
applyLocalContentUpdate(nextContent, {
|
|
253
253
|
refreshPreview: false,
|
|
254
254
|
forcePreviewFullDocument: true,
|
|
255
|
+
historyBeforeContent: baseContent,
|
|
255
256
|
// Covers the write landing on the NON-Yjs local fallback stack (the
|
|
256
257
|
// Yjs UndoManager isn't ready yet — e.g. `!isSynced` right after a
|
|
257
258
|
// fresh page load). The stampYjsUndoSelection call below covers the
|
|
@@ -21,26 +21,76 @@ export function runtimeMultiplicityForElementProvenance(
|
|
|
21
21
|
info: ElementInfo | null | undefined,
|
|
22
22
|
): number {
|
|
23
23
|
const provenance = info?.provenance;
|
|
24
|
-
|
|
24
|
+
const runtimeComponent = info?.runtimeComponent;
|
|
25
|
+
const hasInvocationProvenance = Boolean(
|
|
26
|
+
runtimeComponent?.sourceFile &&
|
|
27
|
+
runtimeComponent.line &&
|
|
28
|
+
runtimeComponent.column,
|
|
29
|
+
);
|
|
30
|
+
const sourceFile = hasInvocationProvenance
|
|
31
|
+
? runtimeComponent?.sourceFile
|
|
32
|
+
: provenance?.sourceFile;
|
|
33
|
+
const line = hasInvocationProvenance
|
|
34
|
+
? runtimeComponent?.line
|
|
35
|
+
: provenance?.line;
|
|
36
|
+
const column = hasInvocationProvenance
|
|
37
|
+
? runtimeComponent?.column
|
|
38
|
+
: provenance?.column;
|
|
39
|
+
const componentName = hasInvocationProvenance
|
|
40
|
+
? runtimeComponent?.name
|
|
41
|
+
: provenance?.component;
|
|
42
|
+
if (!sourceFile || !line || !column) {
|
|
25
43
|
return 1;
|
|
26
44
|
}
|
|
27
45
|
let count = 0;
|
|
46
|
+
const invocationKeys = hasInvocationProvenance
|
|
47
|
+
? new Set<string>()
|
|
48
|
+
: undefined;
|
|
28
49
|
for (const snapshot of Object.values(snapshots)) {
|
|
29
50
|
const projection = buildCodeLayerProjection(snapshot.html);
|
|
30
51
|
for (const node of projection.nodes) {
|
|
31
52
|
const attrs = node.dataAttributes;
|
|
32
53
|
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
hasInvocationProvenance &&
|
|
55
|
+
runtimeComponent?.componentId &&
|
|
56
|
+
attrs["data-agent-native-runtime-component-id"] !==
|
|
57
|
+
runtimeComponent.componentId
|
|
58
|
+
) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const sourceFileAttribute = hasInvocationProvenance
|
|
62
|
+
? "data-source-owner-file"
|
|
63
|
+
: "data-source-file";
|
|
64
|
+
const lineAttribute = hasInvocationProvenance
|
|
65
|
+
? "data-source-owner-line"
|
|
66
|
+
: "data-source-line";
|
|
67
|
+
const columnAttribute = hasInvocationProvenance
|
|
68
|
+
? "data-source-owner-column"
|
|
69
|
+
: "data-source-column";
|
|
70
|
+
if (
|
|
71
|
+
attrs[sourceFileAttribute] === sourceFile &&
|
|
72
|
+
Number(attrs[lineAttribute]) === line &&
|
|
73
|
+
Number(attrs[columnAttribute]) === column &&
|
|
74
|
+
(!componentName || attrs["data-component-name"] === componentName)
|
|
38
75
|
) {
|
|
39
|
-
|
|
76
|
+
if (invocationKeys) {
|
|
77
|
+
invocationKeys.add(
|
|
78
|
+
JSON.stringify([
|
|
79
|
+
runtimeComponent?.componentId ?? "",
|
|
80
|
+
attrs[sourceFileAttribute],
|
|
81
|
+
attrs[lineAttribute],
|
|
82
|
+
attrs[columnAttribute],
|
|
83
|
+
attrs["data-source-owner-key"] ?? "",
|
|
84
|
+
attrs["data-component-name"] ?? "",
|
|
85
|
+
]),
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
count += 1;
|
|
89
|
+
}
|
|
40
90
|
}
|
|
41
91
|
}
|
|
42
92
|
}
|
|
43
|
-
return Math.max(1, count);
|
|
93
|
+
return Math.max(1, invocationKeys?.size ?? count);
|
|
44
94
|
}
|
|
45
95
|
|
|
46
96
|
export function buildSignInHrefForDesignIntent(
|
|
@@ -599,6 +599,12 @@ export function restorePendingFileContent<
|
|
|
599
599
|
|
|
600
600
|
export interface FileContentSaveRequest {
|
|
601
601
|
identityMigrationSourceContent?: string;
|
|
602
|
+
/**
|
|
603
|
+
* CAS base for the durable latest-content replay. Normal saves keep their
|
|
604
|
+
* per-edit base so the serialized chain remains strict; the outbox needs the
|
|
605
|
+
* oldest unacknowledged base so a pagehide can replay the full snapshot.
|
|
606
|
+
*/
|
|
607
|
+
unloadExpectedVersionHash?: string;
|
|
602
608
|
id: string;
|
|
603
609
|
content: string;
|
|
604
610
|
syncCollab: boolean;
|
|
@@ -610,6 +616,19 @@ export interface FileContentSaveRequest {
|
|
|
610
616
|
expectedVersionHash: string;
|
|
611
617
|
}
|
|
612
618
|
|
|
619
|
+
/**
|
|
620
|
+
* A pagehide request must retain its own CAS base. The durable outbox may fold
|
|
621
|
+
* later edits onto the oldest base, but a direct keepalive can race that
|
|
622
|
+
* predecessor and must remain replayable in operation order.
|
|
623
|
+
*/
|
|
624
|
+
export function prepareFileContentSaveKeepalive(
|
|
625
|
+
pending: FileContentSaveRequest,
|
|
626
|
+
): FileContentSaveRequest {
|
|
627
|
+
return pending.unloadExpectedVersionHash === undefined
|
|
628
|
+
? pending
|
|
629
|
+
: { ...pending, unloadExpectedVersionHash: undefined };
|
|
630
|
+
}
|
|
631
|
+
|
|
613
632
|
export function coalescePendingFileContentSave(
|
|
614
633
|
next: FileContentSaveRequest,
|
|
615
634
|
pending: FileContentSaveRequest | undefined,
|
|
@@ -637,10 +656,8 @@ type FileContentSaveRequestsById = Readonly<
|
|
|
637
656
|
export function shouldClearLatestUnloadSave(
|
|
638
657
|
latest: FileContentSaveRequest | undefined,
|
|
639
658
|
completed: FileContentSaveRequest,
|
|
640
|
-
skippedStaleMirror = false,
|
|
641
659
|
): boolean {
|
|
642
660
|
return Boolean(
|
|
643
|
-
!skippedStaleMirror &&
|
|
644
661
|
latest &&
|
|
645
662
|
latest.id === completed.id &&
|
|
646
663
|
latest.content === completed.content &&
|
|
@@ -650,6 +667,50 @@ export function shouldClearLatestUnloadSave(
|
|
|
650
667
|
);
|
|
651
668
|
}
|
|
652
669
|
|
|
670
|
+
/**
|
|
671
|
+
* A completed predecessor advances a newer unload replay from its old CAS
|
|
672
|
+
* base. Mutate the request in place so debounce slots and save chains keep the
|
|
673
|
+
* same request object, then let the caller re-journal its updated payload.
|
|
674
|
+
*/
|
|
675
|
+
export function advanceLatestUnloadSaveBase(
|
|
676
|
+
latest: FileContentSaveRequest | undefined,
|
|
677
|
+
completed: FileContentSaveRequest,
|
|
678
|
+
persistedVersionHash: string,
|
|
679
|
+
): boolean {
|
|
680
|
+
const completedBase =
|
|
681
|
+
completed.unloadExpectedVersionHash ?? completed.expectedVersionHash;
|
|
682
|
+
const latestBase =
|
|
683
|
+
latest?.unloadExpectedVersionHash ?? latest?.expectedVersionHash;
|
|
684
|
+
if (
|
|
685
|
+
!latest ||
|
|
686
|
+
latest === completed ||
|
|
687
|
+
latest.id !== completed.id ||
|
|
688
|
+
latest.operationSource !== completed.operationSource ||
|
|
689
|
+
latest.operationRevision <= completed.operationRevision ||
|
|
690
|
+
latestBase !== completedBase
|
|
691
|
+
) {
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
latest.unloadExpectedVersionHash = persistedVersionHash;
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
export function shouldClearLatestUnloadSaveForOutboxEntry(
|
|
699
|
+
latest: FileContentSaveRequest | undefined,
|
|
700
|
+
entry: {
|
|
701
|
+
resourceId: string;
|
|
702
|
+
operationSource: string;
|
|
703
|
+
operationRevision: number;
|
|
704
|
+
},
|
|
705
|
+
): boolean {
|
|
706
|
+
return Boolean(
|
|
707
|
+
latest &&
|
|
708
|
+
latest.id === entry.resourceId &&
|
|
709
|
+
latest.operationSource === entry.operationSource &&
|
|
710
|
+
latest.operationRevision === entry.operationRevision,
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
653
714
|
/**
|
|
654
715
|
* Flushes debounce slots through the ordinary serialized mutation path during
|
|
655
716
|
* React cleanup (route/design changes). Real document unload is handled by the
|
|
@@ -170,6 +170,9 @@ export interface FileCreationHistoryEntry {
|
|
|
170
170
|
content: string;
|
|
171
171
|
fileType: string;
|
|
172
172
|
geometry?: CanvasFrameGeometry;
|
|
173
|
+
preserveCamera?: boolean;
|
|
174
|
+
screenMetadata?: Record<string, unknown>;
|
|
175
|
+
localhostScreen?: Record<string, unknown>;
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
export interface FileDeletionHistorySnapshot {
|
|
@@ -421,6 +421,19 @@ function isRenderedBlockDisplay(display: string | null | undefined): boolean {
|
|
|
421
421
|
return display === "block" || display === "list-item";
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
+
function hasRenderedGridPlacement(
|
|
425
|
+
computedStyles: Record<string, string> | undefined,
|
|
426
|
+
): boolean {
|
|
427
|
+
return [computedStyles?.gridColumn, computedStyles?.gridRow].some((value) => {
|
|
428
|
+
const normalized = value?.trim().replace(/\s+/g, " ").toLowerCase();
|
|
429
|
+
if (!normalized) return false;
|
|
430
|
+
return normalized.split("/").some((line) => {
|
|
431
|
+
const trimmed = line.trim();
|
|
432
|
+
return trimmed !== "auto" && !/^span(?:\s|$)/.test(trimmed);
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
424
437
|
export interface ResolveElementNudgeIntentArgs {
|
|
425
438
|
content: string;
|
|
426
439
|
source?: CodeLayerSource;
|
|
@@ -477,14 +490,45 @@ export function resolveElementNudgeIntent(
|
|
|
477
490
|
// round-trip has happened yet and `parentDisplay` is simply absent. An
|
|
478
491
|
// element that really is inline or a flex child is handled above — the parser
|
|
479
492
|
// sees those, and a rendered value always wins over this default.
|
|
480
|
-
const rendered =
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
//
|
|
484
|
-
|
|
493
|
+
const rendered =
|
|
494
|
+
args.selectedElement.parentDisplay ??
|
|
495
|
+
args.selectedElement.parentLayout?.display;
|
|
496
|
+
// A rendered grid needs its track count to map an arrow onto the next visual
|
|
497
|
+
// cell, and `display: grid` alone does not carry it. Reuse the computed track
|
|
498
|
+
// templates already reported by the bridge instead of guessing a flex row.
|
|
499
|
+
const renderedGrid =
|
|
485
500
|
parsedContainer.kind === "none" &&
|
|
486
501
|
!escapesFlow(position) &&
|
|
487
502
|
(rendered === "grid" || rendered === "inline-grid")
|
|
503
|
+
? describeFlowContainer({
|
|
504
|
+
style: {
|
|
505
|
+
display: rendered,
|
|
506
|
+
...(args.selectedElement.parentLayout?.gridTemplateColumns
|
|
507
|
+
? {
|
|
508
|
+
"grid-template-columns":
|
|
509
|
+
args.selectedElement.parentLayout.gridTemplateColumns,
|
|
510
|
+
}
|
|
511
|
+
: {}),
|
|
512
|
+
...(args.selectedElement.parentLayout?.gridTemplateRows
|
|
513
|
+
? {
|
|
514
|
+
"grid-template-rows":
|
|
515
|
+
args.selectedElement.parentLayout.gridTemplateRows,
|
|
516
|
+
}
|
|
517
|
+
: {}),
|
|
518
|
+
...(args.selectedElement.parentLayout?.gridAutoFlow
|
|
519
|
+
? {
|
|
520
|
+
"grid-auto-flow":
|
|
521
|
+
args.selectedElement.parentLayout.gridAutoFlow,
|
|
522
|
+
}
|
|
523
|
+
: {}),
|
|
524
|
+
},
|
|
525
|
+
})
|
|
526
|
+
: NO_FLOW_CONTAINER;
|
|
527
|
+
if (
|
|
528
|
+
parsedContainer.kind === "none" &&
|
|
529
|
+
!escapesFlow(position) &&
|
|
530
|
+
(rendered === "grid" || rendered === "inline-grid") &&
|
|
531
|
+
(renderedGrid.kind !== "grid" || renderedGrid.lineLength === null)
|
|
488
532
|
) {
|
|
489
533
|
return { kind: "none" };
|
|
490
534
|
}
|
|
@@ -496,29 +540,31 @@ export function resolveElementNudgeIntent(
|
|
|
496
540
|
if (
|
|
497
541
|
parsedContainer.kind === "none" &&
|
|
498
542
|
!escapesFlow(position) &&
|
|
499
|
-
|
|
543
|
+
(rendered === "flex" || rendered === "inline-flex") &&
|
|
500
544
|
!renderedFlexDirection
|
|
501
545
|
) {
|
|
502
546
|
return { kind: "none" };
|
|
503
547
|
}
|
|
504
548
|
const container: FlowContainerInfo =
|
|
505
549
|
parsedContainer.kind === "none" && !escapesFlow(position)
|
|
506
|
-
?
|
|
507
|
-
?
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
: "
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
//
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
550
|
+
? renderedGrid.kind === "grid"
|
|
551
|
+
? renderedGrid
|
|
552
|
+
: isRenderedFlowDisplay(rendered)
|
|
553
|
+
? {
|
|
554
|
+
...NO_FLOW_CONTAINER,
|
|
555
|
+
kind: "flex",
|
|
556
|
+
axis: renderedFlexDirection?.startsWith("column")
|
|
557
|
+
? "vertical"
|
|
558
|
+
: "horizontal",
|
|
559
|
+
reversed: renderedFlexDirection?.endsWith("-reverse") ?? false,
|
|
560
|
+
}
|
|
561
|
+
: // `parent` null means the node is a projection root: it has no flow to
|
|
562
|
+
// reorder within, and the bridge reports `parentDisplay: undefined`
|
|
563
|
+
// for it exactly as it does for a not-yet-measured selection.
|
|
564
|
+
isRenderedBlockDisplay(rendered) ||
|
|
565
|
+
(rendered === undefined && parent !== null)
|
|
566
|
+
? BLOCK_FLOW_CONTAINER
|
|
567
|
+
: parsedContainer
|
|
522
568
|
: parsedContainer;
|
|
523
569
|
// Flex/grid paint children by `order` and explicit grid placement, not DOM
|
|
524
570
|
// position, so moving the node would write a source change that produces no
|
|
@@ -552,6 +598,12 @@ export function resolveElementNudgeIntent(
|
|
|
552
598
|
) {
|
|
553
599
|
return { kind: "none" };
|
|
554
600
|
}
|
|
601
|
+
if (
|
|
602
|
+
container.kind === "grid" &&
|
|
603
|
+
hasRenderedGridPlacement(args.selectedElement.computedStyles)
|
|
604
|
+
) {
|
|
605
|
+
return { kind: "none" };
|
|
606
|
+
}
|
|
555
607
|
|
|
556
608
|
const intent = resolveNudgeIntent({
|
|
557
609
|
direction: args.direction,
|
|
@@ -102,9 +102,21 @@ export function resolveSourceBaseForPublication(args: {
|
|
|
102
102
|
persistedContent?: string | null;
|
|
103
103
|
beforeContent: string;
|
|
104
104
|
}): string {
|
|
105
|
+
const pendingContent = args.pending?.content;
|
|
106
|
+
const migrationSource = args.pending?.identityMigrationSourceContent;
|
|
107
|
+
// Identity repair is a real full-document save. Keep its raw CAS base while
|
|
108
|
+
// that save is still in flight, but switch to the canonical pending bytes as
|
|
109
|
+
// soon as the collab or persisted mirror has acknowledged them.
|
|
110
|
+
if (
|
|
111
|
+
pendingContent !== undefined &&
|
|
112
|
+
migrationSource !== undefined &&
|
|
113
|
+
args.collabContent !== pendingContent &&
|
|
114
|
+
args.persistedContent !== pendingContent
|
|
115
|
+
) {
|
|
116
|
+
return migrationSource;
|
|
117
|
+
}
|
|
105
118
|
const raw =
|
|
106
|
-
|
|
107
|
-
args.pending?.content ??
|
|
119
|
+
pendingContent ??
|
|
108
120
|
args.collabContent ??
|
|
109
121
|
args.persistedContent ??
|
|
110
122
|
args.beforeContent;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Design component React fixture</title>
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
margin: 0;
|
|
9
|
+
min-height: 700px;
|
|
10
|
+
background: #111827;
|
|
11
|
+
color: white;
|
|
12
|
+
font: 16px system-ui;
|
|
13
|
+
}
|
|
14
|
+
main {
|
|
15
|
+
padding: 40px;
|
|
16
|
+
}
|
|
17
|
+
.card {
|
|
18
|
+
display: grid;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
width: 260px;
|
|
21
|
+
padding: 20px;
|
|
22
|
+
border-radius: 14px;
|
|
23
|
+
background: #1f2937;
|
|
24
|
+
}
|
|
25
|
+
.card button {
|
|
26
|
+
padding: 10px 14px;
|
|
27
|
+
border: 0;
|
|
28
|
+
border-radius: 8px;
|
|
29
|
+
background: #6366f1;
|
|
30
|
+
color: white;
|
|
31
|
+
}
|
|
32
|
+
[data-agent-native-component-ref] {
|
|
33
|
+
outline: 1px dashed #f59e0b;
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
36
|
+
</head>
|
|
37
|
+
<body>
|
|
38
|
+
<main
|
|
39
|
+
data-agent-native-node-id="react-screen"
|
|
40
|
+
data-agent-native-layer-name="React Component Screen"
|
|
41
|
+
data-agent-native-source-kind="react"
|
|
42
|
+
data-agent-native-source-path="src/App.tsx"
|
|
43
|
+
data-agent-native-source-file="src/App.tsx"
|
|
44
|
+
data-agent-native-source-line="1"
|
|
45
|
+
data-agent-native-source-column="1"
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
data-agent-native-node-id="promotion-target"
|
|
49
|
+
data-agent-native-layer-name="Promotion target"
|
|
50
|
+
>
|
|
51
|
+
Promote me
|
|
52
|
+
</div>
|
|
53
|
+
<section
|
|
54
|
+
class="card"
|
|
55
|
+
data-agent-native-node-id="card-main"
|
|
56
|
+
data-agent-native-layer-name="Card Main"
|
|
57
|
+
data-agent-native-component="ReusableCard"
|
|
58
|
+
data-agent-native-component-id="cmp-reusable-card"
|
|
59
|
+
data-agent-native-source-file="src/App.tsx"
|
|
60
|
+
data-agent-native-source-line="3"
|
|
61
|
+
data-agent-native-source-column="1"
|
|
62
|
+
data-source-framework="react"
|
|
63
|
+
data-source-file="src/App.tsx"
|
|
64
|
+
data-source-line="3"
|
|
65
|
+
data-source-column="1"
|
|
66
|
+
data-component-name="ReusableCard"
|
|
67
|
+
>
|
|
68
|
+
<strong data-agent-native-node-id="card-title">Canonical card</strong>
|
|
69
|
+
<button
|
|
70
|
+
data-agent-native-node-id="card-button"
|
|
71
|
+
data-agent-native-prop-label="Open"
|
|
72
|
+
>
|
|
73
|
+
Open
|
|
74
|
+
</button>
|
|
75
|
+
</section>
|
|
76
|
+
<section
|
|
77
|
+
class="card"
|
|
78
|
+
data-agent-native-node-id="card-instance"
|
|
79
|
+
data-agent-native-layer-name="Card Instance"
|
|
80
|
+
data-agent-native-component="ReusableCard"
|
|
81
|
+
data-agent-native-component-ref="cmp-reusable-card"
|
|
82
|
+
data-agent-native-component-overrides="%7B%22card-title%22%3A%7B%22textContent%22%3A%22Instance%20override%22%7D%7D"
|
|
83
|
+
>
|
|
84
|
+
<strong
|
|
85
|
+
data-agent-native-node-id="card-instance-title"
|
|
86
|
+
data-agent-native-component-source-node-id="card-title"
|
|
87
|
+
>Instance override</strong
|
|
88
|
+
>
|
|
89
|
+
<button
|
|
90
|
+
data-agent-native-node-id="card-instance-button"
|
|
91
|
+
data-agent-native-component-source-node-id="card-button"
|
|
92
|
+
>
|
|
93
|
+
Open
|
|
94
|
+
</button>
|
|
95
|
+
</section>
|
|
96
|
+
</main>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Inline component fixture</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body style="margin: 0; position: relative; min-height: 900px">
|
|
8
|
+
<aside
|
|
9
|
+
data-agent-native-node-id="legacy-copy"
|
|
10
|
+
data-agent-native-layer-name="Legacy same-name copy"
|
|
11
|
+
data-agent-native-component="ReusableCard"
|
|
12
|
+
style="
|
|
13
|
+
position: absolute;
|
|
14
|
+
left: 40px;
|
|
15
|
+
top: 40px;
|
|
16
|
+
width: 180px;
|
|
17
|
+
height: 80px;
|
|
18
|
+
background: #ddd;
|
|
19
|
+
"
|
|
20
|
+
>
|
|
21
|
+
Legacy
|
|
22
|
+
</aside>
|
|
23
|
+
<section
|
|
24
|
+
data-agent-native-node-id="card-main"
|
|
25
|
+
data-agent-native-layer-name="Card Main"
|
|
26
|
+
style="
|
|
27
|
+
position: absolute;
|
|
28
|
+
left: 300px;
|
|
29
|
+
top: 40px;
|
|
30
|
+
width: 420px;
|
|
31
|
+
height: 360px;
|
|
32
|
+
padding: 20px;
|
|
33
|
+
background: #1f2937;
|
|
34
|
+
color: white;
|
|
35
|
+
"
|
|
36
|
+
>
|
|
37
|
+
<div
|
|
38
|
+
data-agent-native-node-id="card-body"
|
|
39
|
+
data-agent-native-layer-name="Card Body"
|
|
40
|
+
style="display: grid; gap: 8px"
|
|
41
|
+
>
|
|
42
|
+
<strong data-agent-native-node-id="card-title"
|
|
43
|
+
><span data-agent-native-node-id="card-title-text"
|
|
44
|
+
>Canonical card</span
|
|
45
|
+
></strong
|
|
46
|
+
>
|
|
47
|
+
<button
|
|
48
|
+
data-agent-native-node-id="card-button"
|
|
49
|
+
data-agent-native-layer-name="Card Action"
|
|
50
|
+
data-agent-native-prop-label="Open"
|
|
51
|
+
style="width: 120px; padding: 10px; background: #6366f1; color: white"
|
|
52
|
+
>
|
|
53
|
+
Open
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
<div
|
|
57
|
+
data-agent-native-node-id="card-slot"
|
|
58
|
+
data-agent-native-layer-name="Card Slot"
|
|
59
|
+
style="min-height: 70px; padding: 10px; background: #374151"
|
|
60
|
+
>
|
|
61
|
+
Reparent target
|
|
62
|
+
</div>
|
|
63
|
+
</section>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
@@ -442,7 +442,12 @@ export async function enterInteractView(
|
|
|
442
442
|
.locator("[data-frame-full-view]")
|
|
443
443
|
: page.locator("[data-frame-full-view]").last();
|
|
444
444
|
await expect(fullView).toHaveCount(1);
|
|
445
|
-
|
|
445
|
+
// The screen card can extend beneath the fixed inspector at narrow canvas
|
|
446
|
+
// widths; invoke the button without relying on the panel's overlapping
|
|
447
|
+
// physical hit area.
|
|
448
|
+
await fullView.evaluate((element) => {
|
|
449
|
+
(element as HTMLButtonElement).click();
|
|
450
|
+
});
|
|
446
451
|
// The overview screen shells are the boundary that actually unmounts; the
|
|
447
452
|
// toolbar's own Interact button stays mounted and merely becomes pressed.
|
|
448
453
|
await expect(page.locator("[data-screen-shell]")).toHaveCount(0);
|
|
@@ -2,6 +2,10 @@ import { assertAccess } from "@agent-native/core/sharing";
|
|
|
2
2
|
import { and, eq, isNull, sql } from "drizzle-orm";
|
|
3
3
|
|
|
4
4
|
import { getDb, schema } from "../db/index.js";
|
|
5
|
+
import {
|
|
6
|
+
designSourceMutationLockKey,
|
|
7
|
+
lockDesignFilesTable,
|
|
8
|
+
} from "../source-workspace.js";
|
|
5
9
|
|
|
6
10
|
const DEFAULT_MAX_ATTEMPTS = 8;
|
|
7
11
|
const CONFLICT_BACKOFF_MS = 8;
|
|
@@ -199,11 +203,10 @@ async function mutateDesignDataUnlocked({
|
|
|
199
203
|
// Keep the design-data CAS and HTML rewrites in one transaction.
|
|
200
204
|
// ponytail: reuse the existing design-file lock; split by design only
|
|
201
205
|
// if breakpoint edits become a measurable multi-tenant bottleneck.
|
|
202
|
-
await (
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
).execute(sql`LOCK TABLE design_files IN SHARE ROW EXCLUSIVE MODE`);
|
|
206
|
+
await tx.execute(
|
|
207
|
+
sql`SELECT pg_advisory_xact_lock(hashtextextended(${designSourceMutationLockKey(designId)}, 0::bigint))`,
|
|
208
|
+
);
|
|
209
|
+
await lockDesignFilesTable(tx);
|
|
207
210
|
}
|
|
208
211
|
|
|
209
212
|
const [currentRow] = await tx
|
|
@@ -233,6 +236,7 @@ async function mutateDesignDataUnlocked({
|
|
|
233
236
|
})
|
|
234
237
|
.from(schema.designFiles)
|
|
235
238
|
.where(eq(schema.designFiles.designId, designId))
|
|
239
|
+
.for("update")
|
|
236
240
|
: [];
|
|
237
241
|
const revisionConditions = [
|
|
238
242
|
eq(schema.designs.id, designId),
|