@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
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
TooltipTrigger,
|
|
61
61
|
} from "@/components/ui/tooltip";
|
|
62
62
|
|
|
63
|
+
import type { LocalhostWriteConsentPayload } from "../LocalhostWriteConsentDialog";
|
|
63
64
|
import { findCanvasIframeForScreen } from "../multi-screen/iframe-targeting";
|
|
64
65
|
import {
|
|
65
66
|
canRebuildAlpineDataLosslessly,
|
|
@@ -74,6 +75,15 @@ import {
|
|
|
74
75
|
InspectorGridCell,
|
|
75
76
|
} from "./inspector-grid";
|
|
76
77
|
|
|
78
|
+
function isLocalhostWriteConsentError(error: unknown): boolean {
|
|
79
|
+
return (
|
|
80
|
+
error instanceof Error &&
|
|
81
|
+
(error.name === "LocalWriteConsentRequiredError" ||
|
|
82
|
+
error.name === "WriteConsentRequiredError" ||
|
|
83
|
+
/write-consent grant|grant expired/i.test(error.message))
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
77
87
|
// ─── Make it real — inline upgrade card (§3, §6.6) ──────────────────────────
|
|
78
88
|
|
|
79
89
|
/**
|
|
@@ -311,6 +321,7 @@ interface ComponentDetailsResult {
|
|
|
311
321
|
isMain?: boolean;
|
|
312
322
|
canRestore?: boolean;
|
|
313
323
|
observedProps: Array<{ name: string; value: string }>;
|
|
324
|
+
literalProps?: Array<{ name: string; value: string }>;
|
|
314
325
|
persistedVariants: Record<string, string[]>;
|
|
315
326
|
sourceLocation?: { filePath: string; exportName?: string } | null;
|
|
316
327
|
/** Component instance shape, including the Alpine `x-data` expression. */
|
|
@@ -328,6 +339,37 @@ interface ComponentDetailsResult {
|
|
|
328
339
|
};
|
|
329
340
|
}
|
|
330
341
|
|
|
342
|
+
export interface ComponentLocalSource {
|
|
343
|
+
connectionId: string;
|
|
344
|
+
path: string;
|
|
345
|
+
line: number;
|
|
346
|
+
column: number;
|
|
347
|
+
positionPrecision?: "authored" | "transformed" | "unknown";
|
|
348
|
+
runtimeMultiplicity?: number;
|
|
349
|
+
scope?:
|
|
350
|
+
| "single-instance"
|
|
351
|
+
| "repeated-render"
|
|
352
|
+
| "shared-component-definition"
|
|
353
|
+
| "unknown";
|
|
354
|
+
expectedVersionHash?: string;
|
|
355
|
+
expectedValue?: string;
|
|
356
|
+
propStamps?: Array<{ name: string; value: string }>;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export interface RuntimeComponentDetails {
|
|
360
|
+
name: string;
|
|
361
|
+
nodeId: string;
|
|
362
|
+
selector: string;
|
|
363
|
+
props: Array<{ name: string; value: string }>;
|
|
364
|
+
literalProps?: Array<{ name: string; value: string }>;
|
|
365
|
+
alpineData?: string | null;
|
|
366
|
+
componentId?: string;
|
|
367
|
+
componentRef?: string;
|
|
368
|
+
isMain?: boolean;
|
|
369
|
+
sourceLocation?: { filePath: string; exportName?: string };
|
|
370
|
+
local?: ComponentLocalSource;
|
|
371
|
+
}
|
|
372
|
+
|
|
331
373
|
/** Shape returned by `go-to-main-component`. */
|
|
332
374
|
interface GoToMainComponentResult {
|
|
333
375
|
isMain?: boolean;
|
|
@@ -368,6 +410,7 @@ interface DetachComponentInstanceResult {
|
|
|
368
410
|
export type PropRow = {
|
|
369
411
|
name: string;
|
|
370
412
|
value: string;
|
|
413
|
+
literalValue?: string;
|
|
371
414
|
/** Variant/enum options when the prop is a known group. */
|
|
372
415
|
options?: string[];
|
|
373
416
|
/** Persist surface for this prop. */
|
|
@@ -421,9 +464,18 @@ export function buildComponentPropRows(data: {
|
|
|
421
464
|
instance?: { alpineData?: string | null } | null;
|
|
422
465
|
observedProps: Array<{ name: string; value: string }>;
|
|
423
466
|
persistedVariants: Record<string, string[]>;
|
|
467
|
+
literalProps?: Array<{ name: string; value: string }>;
|
|
424
468
|
}): PropRow[] {
|
|
425
|
-
const {
|
|
469
|
+
const {
|
|
470
|
+
observedProps,
|
|
471
|
+
persistedVariants,
|
|
472
|
+
instance,
|
|
473
|
+
literalProps = [],
|
|
474
|
+
} = data;
|
|
426
475
|
const alpineData = parseAlpineDataObject(instance?.alpineData);
|
|
476
|
+
const literalValues = new Map(
|
|
477
|
+
literalProps.map(({ name, value }) => [name, value]),
|
|
478
|
+
);
|
|
427
479
|
|
|
428
480
|
const rows: PropRow[] = [];
|
|
429
481
|
const seen = new Set<string>();
|
|
@@ -447,13 +499,29 @@ export function buildComponentPropRows(data: {
|
|
|
447
499
|
rows.push({
|
|
448
500
|
name: prop.name,
|
|
449
501
|
value: prop.value,
|
|
502
|
+
...(literalValues.has(prop.name)
|
|
503
|
+
? { literalValue: literalValues.get(prop.name) }
|
|
504
|
+
: {}),
|
|
450
505
|
options: persistedVariants[prop.name],
|
|
451
506
|
surface: "attribute",
|
|
452
507
|
});
|
|
453
508
|
seen.add(prop.name);
|
|
454
509
|
}
|
|
455
510
|
|
|
456
|
-
// 3)
|
|
511
|
+
// 3) Verified literal invocation props that are not reflected in the host DOM.
|
|
512
|
+
for (const prop of literalProps) {
|
|
513
|
+
if (seen.has(prop.name)) continue;
|
|
514
|
+
rows.push({
|
|
515
|
+
name: prop.name,
|
|
516
|
+
value: prop.value,
|
|
517
|
+
literalValue: prop.value,
|
|
518
|
+
options: persistedVariants[prop.name],
|
|
519
|
+
surface: "attribute",
|
|
520
|
+
});
|
|
521
|
+
seen.add(prop.name);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// 4) persistedVariant groups with no observed value yet (default to first).
|
|
457
525
|
// Surface is always "attribute" here, NOT "alpineData" even when this
|
|
458
526
|
// instance's x-data happens to be non-empty for other keys: x-data blocks
|
|
459
527
|
// for a real component instance are written with every prop the component
|
|
@@ -470,7 +538,10 @@ export function buildComponentPropRows(data: {
|
|
|
470
538
|
if (seen.has(group)) continue;
|
|
471
539
|
rows.push({
|
|
472
540
|
name: group,
|
|
473
|
-
value: options[0] ?? "",
|
|
541
|
+
value: literalValues.get(group) ?? options[0] ?? "",
|
|
542
|
+
...(literalValues.has(group)
|
|
543
|
+
? { literalValue: literalValues.get(group) }
|
|
544
|
+
: {}),
|
|
474
545
|
options,
|
|
475
546
|
surface: "attribute",
|
|
476
547
|
});
|
|
@@ -533,6 +604,8 @@ export function ComponentSection({
|
|
|
533
604
|
onRestoreComponent,
|
|
534
605
|
onComponentPropApplied,
|
|
535
606
|
sourceCapabilities = [],
|
|
607
|
+
runtime,
|
|
608
|
+
requestLocalhostWrite,
|
|
536
609
|
}: {
|
|
537
610
|
designId: string;
|
|
538
611
|
fileId?: string;
|
|
@@ -560,10 +633,40 @@ export function ComponentSection({
|
|
|
560
633
|
) => void;
|
|
561
634
|
/** Capability names advertised by the current source. */
|
|
562
635
|
sourceCapabilities?: string[];
|
|
636
|
+
/** Live component metadata used when a URL file stores only its route URL. */
|
|
637
|
+
runtime?: RuntimeComponentDetails;
|
|
638
|
+
/** Request the existing localhost write-consent dialog before source writes. */
|
|
639
|
+
requestLocalhostWrite?: (opts: {
|
|
640
|
+
files: string[];
|
|
641
|
+
onGranted: LocalhostWriteConsentPayload["onGranted"];
|
|
642
|
+
onCancel?: () => void;
|
|
643
|
+
}) => void;
|
|
563
644
|
}) {
|
|
564
645
|
const t = useT();
|
|
565
646
|
const queryClient = useQueryClient();
|
|
566
|
-
const
|
|
647
|
+
const [runtimePropOverride, setRuntimePropOverride] = useState<Array<{
|
|
648
|
+
name: string;
|
|
649
|
+
value: string;
|
|
650
|
+
}> | null>(null);
|
|
651
|
+
const effectiveRuntime = runtime
|
|
652
|
+
? {
|
|
653
|
+
...runtime,
|
|
654
|
+
props: runtimePropOverride ?? runtime.props,
|
|
655
|
+
}
|
|
656
|
+
: undefined;
|
|
657
|
+
const propPreviewStateRef = useRef(
|
|
658
|
+
new Map<string, { generation: number; authoritativeValue: string }>(),
|
|
659
|
+
);
|
|
660
|
+
useEffect(() => {
|
|
661
|
+
setRuntimePropOverride(null);
|
|
662
|
+
propPreviewStateRef.current.clear();
|
|
663
|
+
}, [nodeId, runtime?.nodeId, runtime?.props]);
|
|
664
|
+
const detailsParams = {
|
|
665
|
+
designId,
|
|
666
|
+
nodeId,
|
|
667
|
+
...(fileId ? { fileId } : {}),
|
|
668
|
+
...(effectiveRuntime ? { runtime: effectiveRuntime } : {}),
|
|
669
|
+
};
|
|
567
670
|
const detailsKey = ["action", "get-component-details", detailsParams];
|
|
568
671
|
const latestSourceRef = useRef<{
|
|
569
672
|
content: string;
|
|
@@ -661,6 +764,7 @@ export function ComponentSection({
|
|
|
661
764
|
};
|
|
662
765
|
|
|
663
766
|
const sourceForMutation = () => {
|
|
767
|
+
if (effectiveRuntime?.local) return undefined;
|
|
664
768
|
const latestSource = latestSourceRef.current;
|
|
665
769
|
return latestSource.content
|
|
666
770
|
? {
|
|
@@ -794,56 +898,161 @@ export function ComponentSection({
|
|
|
794
898
|
],
|
|
795
899
|
);
|
|
796
900
|
|
|
901
|
+
const updateLocalSourceCache = (content: unknown, versionHash: unknown) => {
|
|
902
|
+
if (
|
|
903
|
+
!effectiveRuntime?.local ||
|
|
904
|
+
(typeof content !== "string" && typeof versionHash !== "string")
|
|
905
|
+
)
|
|
906
|
+
return;
|
|
907
|
+
queryClient.setQueryData(
|
|
908
|
+
[
|
|
909
|
+
"action",
|
|
910
|
+
"read-local-file",
|
|
911
|
+
{
|
|
912
|
+
designId,
|
|
913
|
+
connectionId: effectiveRuntime.local.connectionId,
|
|
914
|
+
path: effectiveRuntime.local.path,
|
|
915
|
+
},
|
|
916
|
+
],
|
|
917
|
+
(previous: { content?: string; versionHash?: string } | undefined) => ({
|
|
918
|
+
...previous,
|
|
919
|
+
...(typeof content === "string" ? { content } : {}),
|
|
920
|
+
...(typeof versionHash === "string" ? { versionHash } : {}),
|
|
921
|
+
}),
|
|
922
|
+
);
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
const updateRuntimeProp = (propName: string | undefined, value: string) => {
|
|
926
|
+
if (!effectiveRuntime || !propName) return;
|
|
927
|
+
setRuntimePropOverride((previous) => {
|
|
928
|
+
const props = previous ?? effectiveRuntime.props;
|
|
929
|
+
const index = props.findIndex((prop) => prop.name === propName);
|
|
930
|
+
if (index === -1) return [...props, { name: propName, value }];
|
|
931
|
+
return props.map((prop, propIndex) =>
|
|
932
|
+
propIndex === index ? { ...prop, value } : prop,
|
|
933
|
+
);
|
|
934
|
+
});
|
|
935
|
+
};
|
|
936
|
+
|
|
797
937
|
// Persist a single prop change through apply-component-prop-edit. Attribute
|
|
798
938
|
// props also preview immediately in the iframe so the selected component
|
|
799
939
|
// changes without waiting for the write/refetch round-trip.
|
|
800
940
|
const persistPropEdit = (
|
|
801
941
|
edit:
|
|
802
942
|
| { kind: "alpineData"; value: string }
|
|
803
|
-
| {
|
|
804
|
-
|
|
943
|
+
| {
|
|
944
|
+
kind: "attribute";
|
|
945
|
+
attribute: string;
|
|
946
|
+
value: string;
|
|
947
|
+
expectedValue?: string;
|
|
948
|
+
previewValue?: string;
|
|
949
|
+
propName?: string;
|
|
950
|
+
},
|
|
805
951
|
) => {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
952
|
+
const previousPreviewValue =
|
|
953
|
+
edit.kind === "attribute"
|
|
954
|
+
? (edit.previewValue ?? edit.expectedValue ?? "")
|
|
955
|
+
: undefined;
|
|
956
|
+
const previewGeneration =
|
|
957
|
+
edit.kind === "attribute"
|
|
958
|
+
? (propPreviewStateRef.current.get(edit.attribute)?.generation ?? 0) + 1
|
|
959
|
+
: undefined;
|
|
960
|
+
if (edit.kind === "attribute") {
|
|
961
|
+
const previous = propPreviewStateRef.current.get(edit.attribute);
|
|
962
|
+
propPreviewStateRef.current.set(edit.attribute, {
|
|
963
|
+
generation: previewGeneration!,
|
|
964
|
+
authoritativeValue:
|
|
965
|
+
previous?.authoritativeValue ?? previousPreviewValue!,
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
const rollbackPreview = () => {
|
|
969
|
+
if (edit.kind !== "attribute") return;
|
|
970
|
+
const current = propPreviewStateRef.current.get(edit.attribute);
|
|
971
|
+
if (!current || current.generation !== previewGeneration) return;
|
|
972
|
+
postComponentPropPreview(edit.attribute, current.authoritativeValue);
|
|
973
|
+
};
|
|
809
974
|
if (edit.kind === "attribute") {
|
|
810
975
|
postComponentPropPreview(edit.attribute, edit.value);
|
|
811
976
|
}
|
|
812
977
|
const latestSource = latestSourceRef.current;
|
|
813
|
-
|
|
814
|
-
{
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
978
|
+
const mutationSource = effectiveRuntime?.local
|
|
979
|
+
? {
|
|
980
|
+
local: {
|
|
981
|
+
...effectiveRuntime.local,
|
|
982
|
+
...(edit.kind === "attribute" && edit.expectedValue !== undefined
|
|
983
|
+
? { expectedValue: edit.expectedValue }
|
|
984
|
+
: {}),
|
|
985
|
+
},
|
|
986
|
+
}
|
|
987
|
+
: latestSource.content
|
|
988
|
+
? {
|
|
989
|
+
currentContent: latestSource.content,
|
|
990
|
+
...(latestSource.revision
|
|
991
|
+
? { revision: latestSource.revision }
|
|
992
|
+
: {}),
|
|
993
|
+
}
|
|
994
|
+
: undefined;
|
|
995
|
+
const payload = {
|
|
996
|
+
designId,
|
|
997
|
+
nodeId,
|
|
998
|
+
...(fileId ? { fileId } : {}),
|
|
999
|
+
edit:
|
|
1000
|
+
edit.kind === "attribute"
|
|
820
1001
|
? {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
? { revision: latestSource.revision }
|
|
825
|
-
: {}),
|
|
826
|
-
},
|
|
1002
|
+
kind: "attribute" as const,
|
|
1003
|
+
attribute: edit.attribute,
|
|
1004
|
+
value: edit.value,
|
|
827
1005
|
}
|
|
828
|
-
:
|
|
829
|
-
},
|
|
830
|
-
|
|
1006
|
+
: edit,
|
|
1007
|
+
...(mutationSource ? { source: mutationSource } : {}),
|
|
1008
|
+
};
|
|
1009
|
+
const submit = (retriedAfterConsent = false) => {
|
|
1010
|
+
applyPropMutation.mutate(payload, {
|
|
831
1011
|
onSuccess: (result) => {
|
|
832
1012
|
const response = result as {
|
|
833
1013
|
content?: unknown;
|
|
834
1014
|
fileId?: unknown;
|
|
835
1015
|
updatedAt?: unknown;
|
|
836
1016
|
conflict?: unknown;
|
|
1017
|
+
ctaRequired?: unknown;
|
|
1018
|
+
persisted?: unknown;
|
|
837
1019
|
error?: unknown;
|
|
1020
|
+
source?: {
|
|
1021
|
+
connectionId?: unknown;
|
|
1022
|
+
path?: unknown;
|
|
1023
|
+
versionHash?: unknown;
|
|
1024
|
+
};
|
|
1025
|
+
result?: { status?: unknown; message?: unknown };
|
|
838
1026
|
};
|
|
839
|
-
|
|
1027
|
+
const resultStatus = response.result?.status;
|
|
1028
|
+
updateLocalSourceCache(
|
|
1029
|
+
response.content,
|
|
1030
|
+
response.source?.versionHash,
|
|
1031
|
+
);
|
|
1032
|
+
if (
|
|
1033
|
+
response.conflict ||
|
|
1034
|
+
response.ctaRequired ||
|
|
1035
|
+
response.persisted === false ||
|
|
1036
|
+
(typeof resultStatus === "string" && resultStatus !== "applied")
|
|
1037
|
+
) {
|
|
1038
|
+
rollbackPreview();
|
|
840
1039
|
toast.error(
|
|
841
1040
|
typeof response.error === "string"
|
|
842
1041
|
? response.error
|
|
843
|
-
: "
|
|
1042
|
+
: t("designEditor.toasts.componentCreateFailed"),
|
|
844
1043
|
);
|
|
845
1044
|
return;
|
|
846
1045
|
}
|
|
1046
|
+
if (edit.kind === "attribute") {
|
|
1047
|
+
const current = propPreviewStateRef.current.get(edit.attribute);
|
|
1048
|
+
if (current) {
|
|
1049
|
+
propPreviewStateRef.current.set(edit.attribute, {
|
|
1050
|
+
...current,
|
|
1051
|
+
authoritativeValue: edit.value,
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
updateRuntimeProp(edit.propName, edit.value);
|
|
1055
|
+
}
|
|
847
1056
|
if (
|
|
848
1057
|
typeof response.fileId === "string" &&
|
|
849
1058
|
typeof response.content === "string"
|
|
@@ -863,14 +1072,36 @@ export function ComponentSection({
|
|
|
863
1072
|
);
|
|
864
1073
|
}
|
|
865
1074
|
},
|
|
1075
|
+
onError: (error: unknown) => {
|
|
1076
|
+
if (
|
|
1077
|
+
!retriedAfterConsent &&
|
|
1078
|
+
effectiveRuntime?.local &&
|
|
1079
|
+
requestLocalhostWrite &&
|
|
1080
|
+
isLocalhostWriteConsentError(error)
|
|
1081
|
+
) {
|
|
1082
|
+
requestLocalhostWrite({
|
|
1083
|
+
files: [effectiveRuntime.local.path],
|
|
1084
|
+
onGranted: () => submit(true),
|
|
1085
|
+
onCancel: rollbackPreview,
|
|
1086
|
+
});
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
rollbackPreview();
|
|
1090
|
+
toast.error(
|
|
1091
|
+
error instanceof Error
|
|
1092
|
+
? error.message
|
|
1093
|
+
: t("designEditor.toasts.componentCreateFailed"),
|
|
1094
|
+
);
|
|
1095
|
+
},
|
|
866
1096
|
onSettled: () => {
|
|
867
1097
|
void queryClient.invalidateQueries({
|
|
868
1098
|
queryKey: ["action", "get-design"],
|
|
869
1099
|
});
|
|
870
1100
|
void queryClient.invalidateQueries({ queryKey: detailsKey });
|
|
871
1101
|
},
|
|
872
|
-
}
|
|
873
|
-
|
|
1102
|
+
});
|
|
1103
|
+
};
|
|
1104
|
+
submit();
|
|
874
1105
|
};
|
|
875
1106
|
|
|
876
1107
|
useEffect(() => {
|
|
@@ -933,19 +1164,20 @@ export function ComponentSection({
|
|
|
933
1164
|
// Real-app sources keep the deeper source-prop controls gated as-is, so for
|
|
934
1165
|
// non-inline sources the controls are read-only here.
|
|
935
1166
|
const isInline = sourceType === "inline";
|
|
936
|
-
const editingEnabled =
|
|
1167
|
+
const editingEnabled =
|
|
1168
|
+
(isInline || Boolean(effectiveRuntime?.local)) && capabilities.canEditProps;
|
|
937
1169
|
const alpineData = parseAlpineDataObject(instance?.alpineData);
|
|
938
1170
|
|
|
939
1171
|
const rows: PropRow[] = buildComponentPropRows({
|
|
940
1172
|
instance,
|
|
941
1173
|
observedProps,
|
|
942
1174
|
persistedVariants,
|
|
1175
|
+
literalProps: data.literalProps ?? effectiveRuntime?.literalProps,
|
|
943
1176
|
});
|
|
944
1177
|
|
|
945
1178
|
const hasRows = rows.length > 0;
|
|
946
1179
|
|
|
947
|
-
// Build the apply-component-prop-edit payload
|
|
948
|
-
// single prop change.
|
|
1180
|
+
// Build the apply-component-prop-edit payload for a single prop change.
|
|
949
1181
|
const commitProp = (row: PropRow, nextValue: string) => {
|
|
950
1182
|
if (!editingEnabled || nextValue === row.value) return;
|
|
951
1183
|
|
|
@@ -977,35 +1209,16 @@ export function ComponentSection({
|
|
|
977
1209
|
}
|
|
978
1210
|
|
|
979
1211
|
const nextSerialized = serialized;
|
|
980
|
-
persistPropEdit(
|
|
981
|
-
{ kind: "alpineData", value: nextSerialized },
|
|
982
|
-
(prev) => ({
|
|
983
|
-
...prev,
|
|
984
|
-
instance: { ...(prev.instance ?? {}), alpineData: nextSerialized },
|
|
985
|
-
observedProps: prev.observedProps.map((p) =>
|
|
986
|
-
p.name === row.name ? { ...p, value: nextValue } : p,
|
|
987
|
-
),
|
|
988
|
-
}),
|
|
989
|
-
);
|
|
1212
|
+
persistPropEdit({ kind: "alpineData", value: nextSerialized });
|
|
990
1213
|
} else {
|
|
991
|
-
persistPropEdit(
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
return {
|
|
1000
|
-
...prev,
|
|
1001
|
-
observedProps: exists
|
|
1002
|
-
? prev.observedProps.map((p) =>
|
|
1003
|
-
p.name === row.name ? { ...p, value: nextValue } : p,
|
|
1004
|
-
)
|
|
1005
|
-
: [...prev.observedProps, { name: row.name, value: nextValue }],
|
|
1006
|
-
};
|
|
1007
|
-
},
|
|
1008
|
-
);
|
|
1214
|
+
persistPropEdit({
|
|
1215
|
+
kind: "attribute",
|
|
1216
|
+
attribute: propNameToDataAttribute(row.name),
|
|
1217
|
+
value: nextValue,
|
|
1218
|
+
expectedValue: row.literalValue,
|
|
1219
|
+
previewValue: row.value,
|
|
1220
|
+
propName: row.name,
|
|
1221
|
+
});
|
|
1009
1222
|
}
|
|
1010
1223
|
};
|
|
1011
1224
|
|
|
@@ -60,7 +60,10 @@ export function elementIsComponentSelection(
|
|
|
60
60
|
export function elementHasComponentAnnotation(
|
|
61
61
|
element: ElementInfo | null | undefined,
|
|
62
62
|
): boolean {
|
|
63
|
-
return Boolean(
|
|
63
|
+
return Boolean(
|
|
64
|
+
element?.componentAnnotation?.trim() ||
|
|
65
|
+
(!element?.runtimeComponent && element?.componentName?.trim()),
|
|
66
|
+
);
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
export function displayLabel(value: string | undefined): string {
|
|
@@ -460,7 +463,11 @@ export function commitElementMinMax(
|
|
|
460
463
|
onStyleChange(property, kind === "min" ? "0px" : "none", meta);
|
|
461
464
|
return;
|
|
462
465
|
}
|
|
463
|
-
onStyleChange(
|
|
466
|
+
onStyleChange(
|
|
467
|
+
property,
|
|
468
|
+
`${Math.max(0, Math.round(value * 10) / 10)}px`,
|
|
469
|
+
meta,
|
|
470
|
+
);
|
|
464
471
|
}
|
|
465
472
|
|
|
466
473
|
export function inferElementSizing(
|
|
@@ -128,6 +128,35 @@ export interface AutoLayoutPadding {
|
|
|
128
128
|
left: number;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
const OPPOSITE_PADDING_SIDE: Record<
|
|
132
|
+
keyof AutoLayoutPadding,
|
|
133
|
+
keyof AutoLayoutPadding
|
|
134
|
+
> = {
|
|
135
|
+
top: "bottom",
|
|
136
|
+
right: "left",
|
|
137
|
+
bottom: "top",
|
|
138
|
+
left: "right",
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
type PaddingChangeMeta = {
|
|
142
|
+
source: ScrubInputChangeMeta["source"];
|
|
143
|
+
phase: ScrubInputChangeMeta["phase"];
|
|
144
|
+
altKey?: boolean;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/** Apply Figma's Alt/Option mirror to one unlinked padding side. */
|
|
148
|
+
export function mirrorPaddingChange(
|
|
149
|
+
padding: AutoLayoutPadding,
|
|
150
|
+
side: keyof AutoLayoutPadding,
|
|
151
|
+
meta?: PaddingChangeMeta,
|
|
152
|
+
): AutoLayoutPadding {
|
|
153
|
+
if (!meta?.altKey) return padding;
|
|
154
|
+
return {
|
|
155
|
+
...padding,
|
|
156
|
+
[OPPOSITE_PADDING_SIDE[side]]: padding[side],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
131
160
|
export interface AutoLayoutMatrixValue {
|
|
132
161
|
direction: AutoLayoutDirection;
|
|
133
162
|
wrap: AutoLayoutWrap;
|
|
@@ -424,6 +453,14 @@ export function AutoLayoutMatrix({
|
|
|
424
453
|
value.paddingMixed?.top || value.paddingMixed?.bottom,
|
|
425
454
|
);
|
|
426
455
|
|
|
456
|
+
const updatePadding = (
|
|
457
|
+
side: keyof AutoLayoutPadding,
|
|
458
|
+
padding: AutoLayoutPadding,
|
|
459
|
+
meta?: PaddingChangeMeta,
|
|
460
|
+
) => {
|
|
461
|
+
onPaddingChange(mirrorPaddingChange(padding, side, meta), meta);
|
|
462
|
+
};
|
|
463
|
+
|
|
427
464
|
const activeFlow = getFlowOption(value);
|
|
428
465
|
const isBlock = activeFlow === "normal";
|
|
429
466
|
const canResizeToFit =
|
|
@@ -731,7 +768,8 @@ export function AutoLayoutMatrix({
|
|
|
731
768
|
value={horizontalPaddingValue}
|
|
732
769
|
mixed={horizontalPaddingMixed}
|
|
733
770
|
onChange={(next, meta) =>
|
|
734
|
-
|
|
771
|
+
updatePadding(
|
|
772
|
+
"left",
|
|
735
773
|
{
|
|
736
774
|
top: value.padding.top,
|
|
737
775
|
bottom: value.padding.bottom,
|
|
@@ -752,7 +790,8 @@ export function AutoLayoutMatrix({
|
|
|
752
790
|
value={verticalPaddingValue}
|
|
753
791
|
mixed={verticalPaddingMixed}
|
|
754
792
|
onChange={(next, meta) =>
|
|
755
|
-
|
|
793
|
+
updatePadding(
|
|
794
|
+
"top",
|
|
756
795
|
{
|
|
757
796
|
top: next,
|
|
758
797
|
bottom: next,
|
|
@@ -788,7 +827,11 @@ export function AutoLayoutMatrix({
|
|
|
788
827
|
value={value.padding.top}
|
|
789
828
|
mixed={value.paddingMixed?.top}
|
|
790
829
|
onChange={(next, meta) =>
|
|
791
|
-
|
|
830
|
+
updatePadding(
|
|
831
|
+
"top",
|
|
832
|
+
{ ...value.padding, top: next },
|
|
833
|
+
meta,
|
|
834
|
+
)
|
|
792
835
|
}
|
|
793
836
|
disabled={disabled}
|
|
794
837
|
/>
|
|
@@ -804,7 +847,8 @@ export function AutoLayoutMatrix({
|
|
|
804
847
|
value={value.padding.right}
|
|
805
848
|
mixed={value.paddingMixed?.right}
|
|
806
849
|
onChange={(next, meta) =>
|
|
807
|
-
|
|
850
|
+
updatePadding(
|
|
851
|
+
"right",
|
|
808
852
|
{ ...value.padding, right: next },
|
|
809
853
|
meta,
|
|
810
854
|
)
|
|
@@ -819,7 +863,8 @@ export function AutoLayoutMatrix({
|
|
|
819
863
|
value={value.padding.bottom}
|
|
820
864
|
mixed={value.paddingMixed?.bottom}
|
|
821
865
|
onChange={(next, meta) =>
|
|
822
|
-
|
|
866
|
+
updatePadding(
|
|
867
|
+
"bottom",
|
|
823
868
|
{ ...value.padding, bottom: next },
|
|
824
869
|
meta,
|
|
825
870
|
)
|
|
@@ -838,7 +883,8 @@ export function AutoLayoutMatrix({
|
|
|
838
883
|
value={value.padding.left}
|
|
839
884
|
mixed={value.paddingMixed?.left}
|
|
840
885
|
onChange={(next, meta) =>
|
|
841
|
-
|
|
886
|
+
updatePadding(
|
|
887
|
+
"left",
|
|
842
888
|
{ ...value.padding, left: next },
|
|
843
889
|
meta,
|
|
844
890
|
)
|
|
@@ -1945,7 +1991,7 @@ export function SizingField({
|
|
|
1945
1991
|
? "Mixed"
|
|
1946
1992
|
: resolvedSize == null
|
|
1947
1993
|
? ""
|
|
1948
|
-
: String(
|
|
1994
|
+
: String(roundToOneDecimal(resolvedSize));
|
|
1949
1995
|
|
|
1950
1996
|
const addMinLabel = isWidth ? labels.addMinWidth : labels.addMinHeight;
|
|
1951
1997
|
const addMaxLabel = isWidth ? labels.addMaxWidth : labels.addMaxHeight;
|
|
@@ -1958,7 +2004,7 @@ export function SizingField({
|
|
|
1958
2004
|
const openEditor = (kind: "min" | "max") => {
|
|
1959
2005
|
// Commit immediately so the shown row always reflects real state and
|
|
1960
2006
|
// persists across selection changes, remounts, and parent re-renders.
|
|
1961
|
-
const seed = Math.max(0,
|
|
2007
|
+
const seed = Math.max(0, roundToOneDecimal(resolvedSize ?? 0));
|
|
1962
2008
|
const seedValue = kind === "min" ? seed : seed || 1;
|
|
1963
2009
|
onMinMaxChange?.(sizingAxis, kind, seedValue);
|
|
1964
2010
|
};
|
|
@@ -2250,7 +2296,9 @@ function ConstraintSubRow({
|
|
|
2250
2296
|
icon={icon}
|
|
2251
2297
|
prefix="icon"
|
|
2252
2298
|
value={value}
|
|
2253
|
-
onChange={(next, meta) =>
|
|
2299
|
+
onChange={(next, meta) =>
|
|
2300
|
+
onChange(Math.max(0, roundToOneDecimal(next)), meta)
|
|
2301
|
+
}
|
|
2254
2302
|
unit="px"
|
|
2255
2303
|
min={0}
|
|
2256
2304
|
step={1}
|
|
@@ -668,6 +668,19 @@ export function getPrimitiveDropTargetForPoint(
|
|
|
668
668
|
continue;
|
|
669
669
|
}
|
|
670
670
|
const boardRect = toBoardRect(primitive, topScreen.geometry, metadata);
|
|
671
|
+
// In-screen reparenting only accepts a frame that can contain the dragged
|
|
672
|
+
// layer's rendered bounds. A nested cross-screen target may fall back to
|
|
673
|
+
// its ancestor, but a top-level target has no larger fallback available.
|
|
674
|
+
if (
|
|
675
|
+
draggedBoardRect &&
|
|
676
|
+
(draggedScreenId === topScreen.screen.id ||
|
|
677
|
+
primitive.parentNodeId !== undefined ||
|
|
678
|
+
primitive.parentProjectionNodeId !== undefined) &&
|
|
679
|
+
(draggedBoardRect.width > boardRect.width + 1 ||
|
|
680
|
+
draggedBoardRect.height > boardRect.height + 1)
|
|
681
|
+
) {
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
671
684
|
if (
|
|
672
685
|
draggedBoardRect &&
|
|
673
686
|
draggedScreenId === topScreen.screen.id &&
|