@agent-native/core 0.80.9 → 0.80.11
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/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +108 -15
- package/corpus/core/src/cli/design-connect.ts +28 -2
- package/corpus/core/src/cli/skills.ts +45 -24
- package/corpus/core/src/client/AssistantChat.tsx +123 -26
- package/corpus/core/src/client/sse-event-processor.ts +9 -0
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
- package/corpus/templates/assets/AGENTS.md +4 -0
- package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
- package/corpus/templates/assets/actions/generate-image.ts +118 -67
- package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
- package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
- package/corpus/templates/assets/app/i18n-data.ts +61 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/library.tsx +145 -38
- package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +135 -11
- package/corpus/templates/assets/shared/api.ts +4 -0
- package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
- package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +717 -0
- package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
- package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +441 -0
- package/corpus/templates/design/actions/apply-design-state.ts +213 -0
- package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
- package/corpus/templates/design/actions/apply-motion-edit.ts +413 -0
- package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +191 -5
- package/corpus/templates/design/actions/capture-design-state.ts +224 -0
- package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
- package/corpus/templates/design/actions/create-component.ts +447 -0
- package/corpus/templates/design/actions/create-design-branch.ts +263 -0
- package/corpus/templates/design/actions/create-design-state.ts +162 -0
- package/corpus/templates/design/actions/delete-design-state.ts +55 -0
- package/corpus/templates/design/actions/delete-design.ts +20 -0
- package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
- package/corpus/templates/design/actions/get-component-details.ts +251 -0
- package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
- package/corpus/templates/design/actions/get-design-review.ts +314 -0
- package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
- package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
- package/corpus/templates/design/actions/index-components.ts +258 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +277 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
- package/corpus/templates/design/actions/list-design-states.ts +90 -0
- package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
- package/corpus/templates/design/actions/open-component-source.ts +242 -0
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +260 -0
- package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
- package/corpus/templates/design/actions/preview-shader-fill.ts +187 -0
- package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
- package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
- package/corpus/templates/design/actions/run-design-audit.ts +436 -0
- package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
- package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1550 -82
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +649 -43
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1788 -52
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +114 -33
- package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1071 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1053 -99
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +766 -0
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +581 -0
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +578 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
- package/corpus/templates/design/app/components/design/index.ts +16 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +15 -6
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +66 -3
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +145 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +131 -0
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +32 -0
- package/corpus/templates/design/app/i18n-data.ts +280 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +3087 -490
- package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-copying-and-pasting-layers-no-longer-shows-success-notificat.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-and-canvas-drags-keep-the-layer-list-stable-while-chan.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-moves-can-be-undone-and-redone-without-flashing-the-ca.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-design-editor-adds-a-studio-layer-with-tokens-respon.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +94 -2
- package/corpus/templates/design/e2e/helpers.ts +10 -4
- package/corpus/templates/design/server/db/schema.ts +123 -0
- package/corpus/templates/design/server/plugins/db.ts +90 -0
- package/corpus/templates/design/shared/builder-app.ts +297 -0
- package/corpus/templates/design/shared/capability-resolver.ts +123 -0
- package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
- package/corpus/templates/design/shared/code-layer.ts +1016 -71
- package/corpus/templates/design/shared/component-model.ts +274 -0
- package/corpus/templates/design/shared/design-review.ts +275 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
- package/corpus/templates/design/shared/design-state.ts +112 -0
- package/corpus/templates/design/shared/design-surface-index.ts +258 -0
- package/corpus/templates/design/shared/motion-compiler.ts +349 -0
- package/corpus/templates/design/shared/motion-timeline.ts +193 -0
- package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
- package/corpus/templates/design/shared/responsive-classes.ts +452 -0
- package/corpus/templates/design/shared/shader-fill.ts +323 -0
- package/corpus/templates/design/shared/source-mode.ts +245 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +6 -2
- package/corpus/templates/plan/changelog/2026-06-29-plan-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +94 -10
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +28 -2
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +37 -21
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +14 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +115 -22
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +8 -8
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -170,7 +170,7 @@ export interface LayersPanelProps {
|
|
|
170
170
|
canMoveLayer?: (intent: LayersPanelMoveIntent) => boolean;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
interface FlatLayerRow {
|
|
173
|
+
export interface FlatLayerRow {
|
|
174
174
|
node: LayersPanelNode;
|
|
175
175
|
rowKey: string;
|
|
176
176
|
depth: number;
|
|
@@ -412,6 +412,65 @@ function collectAncestorIds(
|
|
|
412
412
|
return Array.from(ancestors);
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
+
export function getLayerSelectionAnchorFromExternalSelection(args: {
|
|
416
|
+
selectedIds: readonly string[];
|
|
417
|
+
selectableVisibleIds: readonly string[];
|
|
418
|
+
}): string | null {
|
|
419
|
+
const selectableVisibleIdSet = new Set(args.selectableVisibleIds);
|
|
420
|
+
return (
|
|
421
|
+
[...args.selectedIds]
|
|
422
|
+
.reverse()
|
|
423
|
+
.find((id) => selectableVisibleIdSet.has(id)) ?? null
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function getTreeOrderedLayerIds(
|
|
428
|
+
ids: readonly string[],
|
|
429
|
+
visibleRows: readonly FlatLayerRow[],
|
|
430
|
+
): string[] {
|
|
431
|
+
const idSet = new Set(ids.filter((id) => id && !id.startsWith("__")));
|
|
432
|
+
const orderedIds = visibleRows
|
|
433
|
+
.map((row) => row.node.id)
|
|
434
|
+
.filter((id) => idSet.has(id));
|
|
435
|
+
const orderedIdSet = new Set(orderedIds);
|
|
436
|
+
return [
|
|
437
|
+
...orderedIds,
|
|
438
|
+
...ids.filter((id) => id && !id.startsWith("__") && !orderedIdSet.has(id)),
|
|
439
|
+
];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function getDraggedLayerIdsForRows(args: {
|
|
443
|
+
selectedIds: readonly string[];
|
|
444
|
+
nodeId: string;
|
|
445
|
+
visibleRows: readonly FlatLayerRow[];
|
|
446
|
+
}): string[] {
|
|
447
|
+
const rawDraggedIds = args.selectedIds.includes(args.nodeId)
|
|
448
|
+
? getTreeOrderedLayerIds(args.selectedIds, args.visibleRows)
|
|
449
|
+
: [args.nodeId];
|
|
450
|
+
const draggedIdSet = new Set(rawDraggedIds);
|
|
451
|
+
return rawDraggedIds.filter((id) => {
|
|
452
|
+
const rowForId = args.visibleRows.find((row) => row.node.id === id);
|
|
453
|
+
return !rowForId?.ancestorIds.some((ancestorId) =>
|
|
454
|
+
draggedIdSet.has(ancestorId),
|
|
455
|
+
);
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export function shouldResyncLayerSelectionAnchor(args: {
|
|
460
|
+
selectionSignature: string;
|
|
461
|
+
lastPanelSelectionSignature: string;
|
|
462
|
+
currentAnchor: string | null;
|
|
463
|
+
selectableVisibleIds: readonly string[];
|
|
464
|
+
}) {
|
|
465
|
+
const anchorStillVisible =
|
|
466
|
+
args.currentAnchor !== null &&
|
|
467
|
+
args.selectableVisibleIds.includes(args.currentAnchor);
|
|
468
|
+
return (
|
|
469
|
+
args.selectionSignature !== args.lastPanelSelectionSignature ||
|
|
470
|
+
!anchorStillVisible
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
415
474
|
function layerCanShowBadge(node: LayersPanelNode) {
|
|
416
475
|
return (
|
|
417
476
|
node.type === "file" ||
|
|
@@ -451,6 +510,7 @@ export function LayersPanel({
|
|
|
451
510
|
const labels = useMemo(() => mergeLabels(labelsProp, t), [labelsProp, t]);
|
|
452
511
|
const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]);
|
|
453
512
|
const selectedIdsRef = useRef<readonly string[]>(selectedIds);
|
|
513
|
+
const lastPanelSelectionSignatureRef = useRef(selectedIds.join("\0"));
|
|
454
514
|
const expandedIdSet = useMemo(() => new Set(expandedIds), [expandedIds]);
|
|
455
515
|
const lastSelectionAnchorRef = useRef<string | null>(selectedIds[0] ?? null);
|
|
456
516
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
@@ -486,9 +546,35 @@ export function LayersPanel({
|
|
|
486
546
|
[roots, selectedIdSet],
|
|
487
547
|
);
|
|
488
548
|
|
|
549
|
+
const selectableVisibleIds = useMemo(
|
|
550
|
+
() =>
|
|
551
|
+
visibleRows
|
|
552
|
+
.map(({ node }) => node)
|
|
553
|
+
.filter((node) => node.selectable !== false)
|
|
554
|
+
.map((node) => node.id),
|
|
555
|
+
[visibleRows],
|
|
556
|
+
);
|
|
557
|
+
|
|
489
558
|
useLayoutEffect(() => {
|
|
490
559
|
selectedIdsRef.current = selectedIds;
|
|
491
|
-
|
|
560
|
+
const signature = selectedIds.join("\0");
|
|
561
|
+
if (
|
|
562
|
+
!shouldResyncLayerSelectionAnchor({
|
|
563
|
+
selectionSignature: signature,
|
|
564
|
+
lastPanelSelectionSignature: lastPanelSelectionSignatureRef.current,
|
|
565
|
+
currentAnchor: lastSelectionAnchorRef.current,
|
|
566
|
+
selectableVisibleIds,
|
|
567
|
+
})
|
|
568
|
+
) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
lastPanelSelectionSignatureRef.current = signature;
|
|
572
|
+
lastSelectionAnchorRef.current =
|
|
573
|
+
getLayerSelectionAnchorFromExternalSelection({
|
|
574
|
+
selectedIds,
|
|
575
|
+
selectableVisibleIds,
|
|
576
|
+
});
|
|
577
|
+
}, [selectableVisibleIds, selectedIds]);
|
|
492
578
|
|
|
493
579
|
useEffect(() => {
|
|
494
580
|
if (selectedAncestorIds.length === 0) return;
|
|
@@ -522,15 +608,6 @@ export function LayersPanel({
|
|
|
522
608
|
return () => window.cancelAnimationFrame(frame);
|
|
523
609
|
}, [selectedScrollRowKey]);
|
|
524
610
|
|
|
525
|
-
const selectableVisibleIds = useMemo(
|
|
526
|
-
() =>
|
|
527
|
-
visibleRows
|
|
528
|
-
.map(({ node }) => node)
|
|
529
|
-
.filter((node) => node.selectable !== false)
|
|
530
|
-
.map((node) => node.id),
|
|
531
|
-
[visibleRows],
|
|
532
|
-
);
|
|
533
|
-
|
|
534
611
|
const selectNode = useCallback(
|
|
535
612
|
(
|
|
536
613
|
id: string,
|
|
@@ -582,6 +659,7 @@ export function LayersPanel({
|
|
|
582
659
|
if (!options.range) {
|
|
583
660
|
lastSelectionAnchorRef.current = id;
|
|
584
661
|
}
|
|
662
|
+
lastPanelSelectionSignatureRef.current = nextIds.join("\0");
|
|
585
663
|
onSelectionChange(nextIds, { id, selectedIds: nextIds, ...options });
|
|
586
664
|
},
|
|
587
665
|
[onSelectionChange, selectableVisibleIds],
|
|
@@ -909,7 +987,8 @@ function LayerRow({
|
|
|
909
987
|
const selectable = node.selectable !== false;
|
|
910
988
|
const lockable = node.lockable !== false && Boolean(onToggleLocked);
|
|
911
989
|
const hideable = node.hideable !== false && Boolean(onToggleHidden);
|
|
912
|
-
const
|
|
990
|
+
const dragEligible = selectable && !node.locked && !node.hidden;
|
|
991
|
+
const draggable = dragEligible && Boolean(onMoveLayer);
|
|
913
992
|
const canDropInside = layerCanDropInside(node, hasChildren);
|
|
914
993
|
const activeDrop =
|
|
915
994
|
dropIndicator?.targetId === node.id ? dropIndicator.placement : null;
|
|
@@ -947,12 +1026,15 @@ function LayerRow({
|
|
|
947
1026
|
};
|
|
948
1027
|
|
|
949
1028
|
const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
|
|
950
|
-
const
|
|
1029
|
+
const getFocusableButtons = () => {
|
|
951
1030
|
const tree = event.currentTarget.closest('[role="tree"]');
|
|
952
|
-
if (!tree) return
|
|
953
|
-
|
|
1031
|
+
if (!tree) return [];
|
|
1032
|
+
return Array.from(
|
|
954
1033
|
tree.querySelectorAll<HTMLButtonElement>("[data-layer-row-button]"),
|
|
955
1034
|
).filter((button) => !button.disabled);
|
|
1035
|
+
};
|
|
1036
|
+
const focusVisibleButton = (nextIndex: number) => {
|
|
1037
|
+
const buttons = getFocusableButtons();
|
|
956
1038
|
const target =
|
|
957
1039
|
buttons[Math.max(0, Math.min(buttons.length - 1, nextIndex))];
|
|
958
1040
|
target?.focus();
|
|
@@ -970,6 +1052,8 @@ function LayerRow({
|
|
|
970
1052
|
const currentIndex = visibleRows.findIndex(
|
|
971
1053
|
(visibleRow) => visibleRow.rowKey === row.rowKey,
|
|
972
1054
|
);
|
|
1055
|
+
const focusableButtons = getFocusableButtons();
|
|
1056
|
+
const currentFocusableIndex = focusableButtons.indexOf(event.currentTarget);
|
|
973
1057
|
|
|
974
1058
|
if (event.key === "Enter" || event.key === " " || event.key === "Space") {
|
|
975
1059
|
event.preventDefault();
|
|
@@ -989,12 +1073,12 @@ function LayerRow({
|
|
|
989
1073
|
}
|
|
990
1074
|
if (event.key === "ArrowDown") {
|
|
991
1075
|
event.preventDefault();
|
|
992
|
-
focusVisibleButton(
|
|
1076
|
+
focusVisibleButton(currentFocusableIndex + 1);
|
|
993
1077
|
return;
|
|
994
1078
|
}
|
|
995
1079
|
if (event.key === "ArrowUp") {
|
|
996
1080
|
event.preventDefault();
|
|
997
|
-
focusVisibleButton(
|
|
1081
|
+
focusVisibleButton(currentFocusableIndex - 1);
|
|
998
1082
|
return;
|
|
999
1083
|
}
|
|
1000
1084
|
if (event.key === "Home") {
|
|
@@ -1004,7 +1088,7 @@ function LayerRow({
|
|
|
1004
1088
|
}
|
|
1005
1089
|
if (event.key === "End") {
|
|
1006
1090
|
event.preventDefault();
|
|
1007
|
-
focusVisibleButton(
|
|
1091
|
+
focusVisibleButton(focusableButtons.length - 1);
|
|
1008
1092
|
return;
|
|
1009
1093
|
}
|
|
1010
1094
|
if (event.key === "ArrowRight" && hasChildren && !isExpanded) {
|
|
@@ -1014,9 +1098,13 @@ function LayerRow({
|
|
|
1014
1098
|
}
|
|
1015
1099
|
if (event.key === "ArrowRight" && hasChildren && isExpanded) {
|
|
1016
1100
|
event.preventDefault();
|
|
1017
|
-
const
|
|
1101
|
+
const nextButton = focusableButtons[currentFocusableIndex + 1];
|
|
1102
|
+
const childId = nextButton?.dataset.layerNodeId;
|
|
1103
|
+
const child = childId
|
|
1104
|
+
? visibleRows.find((visibleRow) => visibleRow.node.id === childId)
|
|
1105
|
+
: null;
|
|
1018
1106
|
if (child?.ancestorIds.includes(node.id)) {
|
|
1019
|
-
focusVisibleButton(
|
|
1107
|
+
focusVisibleButton(currentFocusableIndex + 1);
|
|
1020
1108
|
}
|
|
1021
1109
|
return;
|
|
1022
1110
|
}
|
|
@@ -1037,17 +1125,10 @@ function LayerRow({
|
|
|
1037
1125
|
event.preventDefault();
|
|
1038
1126
|
return;
|
|
1039
1127
|
}
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
:
|
|
1043
|
-
|
|
1044
|
-
// When a parent is moved, its children move with it automatically.
|
|
1045
|
-
const draggedIdSet = new Set(rawDraggedIds);
|
|
1046
|
-
const draggedIds = rawDraggedIds.filter((id) => {
|
|
1047
|
-
const rowForId = visibleRows.find((r) => r.node.id === id);
|
|
1048
|
-
return !rowForId?.ancestorIds.some((ancestorId) =>
|
|
1049
|
-
draggedIdSet.has(ancestorId),
|
|
1050
|
-
);
|
|
1128
|
+
const draggedIds = getDraggedLayerIdsForRows({
|
|
1129
|
+
selectedIds,
|
|
1130
|
+
nodeId: node.id,
|
|
1131
|
+
visibleRows,
|
|
1051
1132
|
});
|
|
1052
1133
|
event.dataTransfer.effectAllowed = "move";
|
|
1053
1134
|
event.dataTransfer.setData("application/x-design-layer-id", node.id);
|
|
@@ -1061,7 +1142,7 @@ function LayerRow({
|
|
|
1061
1142
|
};
|
|
1062
1143
|
|
|
1063
1144
|
const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
|
|
1064
|
-
if (!onMoveLayer || !
|
|
1145
|
+
if (!onMoveLayer || !dragEligible) return;
|
|
1065
1146
|
// dataTransfer.getData() always returns "" during dragover per spec.
|
|
1066
1147
|
// Read from the module-level activeDragState set in handleDragStart instead.
|
|
1067
1148
|
if (!activeDragState) return;
|
|
@@ -1084,7 +1165,7 @@ function LayerRow({
|
|
|
1084
1165
|
};
|
|
1085
1166
|
|
|
1086
1167
|
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
|
1087
|
-
if (!onMoveLayer || !
|
|
1168
|
+
if (!onMoveLayer || !dragEligible) {
|
|
1088
1169
|
onDropIndicatorChange(null);
|
|
1089
1170
|
return;
|
|
1090
1171
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client";
|
|
2
|
+
import {
|
|
3
|
+
IconInfoCircle,
|
|
4
|
+
IconMessageBolt,
|
|
5
|
+
IconClipboard,
|
|
6
|
+
IconX,
|
|
7
|
+
} from "@tabler/icons-react";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
|
|
10
|
+
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
11
|
+
import { Button } from "@/components/ui/button";
|
|
12
|
+
import {
|
|
13
|
+
Dialog,
|
|
14
|
+
DialogContent,
|
|
15
|
+
DialogDescription,
|
|
16
|
+
DialogFooter,
|
|
17
|
+
DialogHeader,
|
|
18
|
+
DialogTitle,
|
|
19
|
+
} from "@/components/ui/dialog";
|
|
20
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
21
|
+
import { useAgentEditRequest } from "@/hooks/useAgentEditRequest";
|
|
22
|
+
|
|
23
|
+
export interface LocalSourceEditBannerProps {
|
|
24
|
+
/** The design id for agent context. */
|
|
25
|
+
designId: string;
|
|
26
|
+
/** The active file id for agent context, if known. */
|
|
27
|
+
fileId?: string;
|
|
28
|
+
/** Path to the localhost source file that should receive edits, if known. */
|
|
29
|
+
routeSourceFile?: string;
|
|
30
|
+
/** Called when the user dismisses the banner. */
|
|
31
|
+
onDismiss?: () => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Dismissible informational banner shown for screens connected to a local
|
|
36
|
+
* source (localhost / React dev server). Explains that edits are routed
|
|
37
|
+
* through the AI agent rather than applied directly, and provides quick
|
|
38
|
+
* "Ask AI to edit" and "Copy prompt" affordances.
|
|
39
|
+
*/
|
|
40
|
+
export function LocalSourceEditBanner({
|
|
41
|
+
designId,
|
|
42
|
+
fileId,
|
|
43
|
+
routeSourceFile,
|
|
44
|
+
onDismiss,
|
|
45
|
+
}: LocalSourceEditBannerProps) {
|
|
46
|
+
const t = useT();
|
|
47
|
+
const { sendEdit, copyPrompt } = useAgentEditRequest();
|
|
48
|
+
const [promptOpen, setPromptOpen] = useState(false);
|
|
49
|
+
const [request, setRequest] = useState("");
|
|
50
|
+
|
|
51
|
+
const baseArgs = { designId, fileId, routeSourceFile };
|
|
52
|
+
const submitArgs = { ...baseArgs, message: request };
|
|
53
|
+
const trimmed = request.trim();
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
<Alert className="relative flex items-start gap-2 rounded-none border-x-0 border-t-0 py-2 px-3 text-xs">
|
|
58
|
+
<IconInfoCircle className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
|
|
59
|
+
<AlertDescription className="flex flex-1 flex-col gap-1.5 text-xs leading-snug">
|
|
60
|
+
<span>{t("designEditor.localSourceEdit.bannerNotice")}</span>
|
|
61
|
+
<div className="flex flex-wrap gap-1.5">
|
|
62
|
+
<Button
|
|
63
|
+
size="sm"
|
|
64
|
+
variant="outline"
|
|
65
|
+
className="h-6 gap-1 px-2 text-[11px]"
|
|
66
|
+
onClick={() => setPromptOpen(true)}
|
|
67
|
+
>
|
|
68
|
+
<IconMessageBolt className="size-3 shrink-0" />
|
|
69
|
+
{t("designEditor.localSourceEdit.askAiToEdit")}
|
|
70
|
+
</Button>
|
|
71
|
+
<Button
|
|
72
|
+
size="sm"
|
|
73
|
+
variant="ghost"
|
|
74
|
+
className="h-6 gap-1 px-2 text-[11px] text-muted-foreground"
|
|
75
|
+
onClick={() => {
|
|
76
|
+
void copyPrompt({
|
|
77
|
+
...baseArgs,
|
|
78
|
+
message: routeSourceFile
|
|
79
|
+
? `Edit the connected source file "${routeSourceFile}" as requested.`
|
|
80
|
+
: "Edit the connected source code as requested.",
|
|
81
|
+
});
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
<IconClipboard className="size-3 shrink-0" />
|
|
85
|
+
{t("designEditor.localSourceEdit.copyPrompt")}
|
|
86
|
+
</Button>
|
|
87
|
+
</div>
|
|
88
|
+
</AlertDescription>
|
|
89
|
+
{onDismiss && (
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
aria-label={t("designEditor.dismiss")}
|
|
93
|
+
onClick={onDismiss}
|
|
94
|
+
className="absolute end-2 top-2 rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
|
95
|
+
>
|
|
96
|
+
<IconX className="size-3.5" />
|
|
97
|
+
</button>
|
|
98
|
+
)}
|
|
99
|
+
</Alert>
|
|
100
|
+
|
|
101
|
+
<Dialog open={promptOpen} onOpenChange={setPromptOpen}>
|
|
102
|
+
<DialogContent className="max-w-md">
|
|
103
|
+
<DialogHeader>
|
|
104
|
+
<DialogTitle>
|
|
105
|
+
{t("designEditor.localSourceEdit.dialogTitle")}
|
|
106
|
+
</DialogTitle>
|
|
107
|
+
<DialogDescription>
|
|
108
|
+
{routeSourceFile
|
|
109
|
+
? t("designEditor.localSourceEdit.dialogDescriptionFile", {
|
|
110
|
+
file: routeSourceFile,
|
|
111
|
+
})
|
|
112
|
+
: t("designEditor.localSourceEdit.dialogDescription")}
|
|
113
|
+
</DialogDescription>
|
|
114
|
+
</DialogHeader>
|
|
115
|
+
<Textarea
|
|
116
|
+
autoFocus
|
|
117
|
+
value={request}
|
|
118
|
+
onChange={(e) => setRequest(e.target.value)}
|
|
119
|
+
placeholder={t("designEditor.localSourceEdit.requestPlaceholder")}
|
|
120
|
+
className="min-h-[96px] resize-none"
|
|
121
|
+
onKeyDown={(e) => {
|
|
122
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && trimmed) {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
void sendEdit(submitArgs);
|
|
125
|
+
setRequest("");
|
|
126
|
+
setPromptOpen(false);
|
|
127
|
+
}
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
<DialogFooter className="gap-2">
|
|
131
|
+
<Button
|
|
132
|
+
variant="ghost"
|
|
133
|
+
onClick={() => {
|
|
134
|
+
void copyPrompt(submitArgs);
|
|
135
|
+
}}
|
|
136
|
+
disabled={!trimmed}
|
|
137
|
+
>
|
|
138
|
+
<IconClipboard className="size-4" />
|
|
139
|
+
{t("designEditor.localSourceEdit.copyPrompt")}
|
|
140
|
+
</Button>
|
|
141
|
+
<Button
|
|
142
|
+
onClick={() => {
|
|
143
|
+
void sendEdit(submitArgs);
|
|
144
|
+
setRequest("");
|
|
145
|
+
setPromptOpen(false);
|
|
146
|
+
}}
|
|
147
|
+
disabled={!trimmed}
|
|
148
|
+
>
|
|
149
|
+
<IconMessageBolt className="size-4" />
|
|
150
|
+
{t("designEditor.localSourceEdit.askAiToEdit")}
|
|
151
|
+
</Button>
|
|
152
|
+
</DialogFooter>
|
|
153
|
+
</DialogContent>
|
|
154
|
+
</Dialog>
|
|
155
|
+
</>
|
|
156
|
+
);
|
|
157
|
+
}
|