@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
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "../server/lib/design-data-mutation.js";
|
|
18
18
|
import { snapshotDesignBeforeAgentEdit } from "../server/lib/design-versions.js";
|
|
19
19
|
import { resolveLocalhostConnectionScope } from "../server/lib/localhost-connection.js";
|
|
20
|
+
import { withDesignSourceMutationTransaction } from "../server/source-workspace.js";
|
|
20
21
|
import {
|
|
21
22
|
mergeCanvasFramePlacements,
|
|
22
23
|
parseCanvasFrameGeometryById,
|
|
@@ -938,7 +939,7 @@ export default defineAction({
|
|
|
938
939
|
)
|
|
939
940
|
? preferredExisting
|
|
940
941
|
: undefined;
|
|
941
|
-
|
|
942
|
+
let existing =
|
|
942
943
|
matchingPreferredExisting ??
|
|
943
944
|
routeCandidates.find((candidate) => {
|
|
944
945
|
const frame = existingCanvasFrames[candidate.id];
|
|
@@ -997,27 +998,51 @@ export default defineAction({
|
|
|
997
998
|
seenRouteRequestKeys.add(routeRequestKey);
|
|
998
999
|
|
|
999
1000
|
if (existing) {
|
|
1000
|
-
await
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1001
|
+
const updated = await withDesignSourceMutationTransaction(
|
|
1002
|
+
designId,
|
|
1003
|
+
async (tx) => {
|
|
1004
|
+
const [current] = await tx
|
|
1005
|
+
.select({ id: schema.designFiles.id })
|
|
1006
|
+
.from(schema.designFiles)
|
|
1007
|
+
.where(
|
|
1008
|
+
and(
|
|
1009
|
+
eq(schema.designFiles.id, existing!.id),
|
|
1010
|
+
eq(schema.designFiles.designId, designId),
|
|
1011
|
+
),
|
|
1012
|
+
)
|
|
1013
|
+
.limit(1);
|
|
1014
|
+
if (!current) return false;
|
|
1015
|
+
await tx
|
|
1016
|
+
.update(schema.designFiles)
|
|
1017
|
+
.set({ content: url, fileType: "html", updatedAt: now })
|
|
1018
|
+
.where(eq(schema.designFiles.id, existing!.id));
|
|
1019
|
+
return true;
|
|
1020
|
+
},
|
|
1021
|
+
);
|
|
1022
|
+
if (updated) {
|
|
1023
|
+
if (await hasCollabState(existing.id)) {
|
|
1024
|
+
await applyText(existing.id, url, "content", "agent");
|
|
1025
|
+
} else {
|
|
1026
|
+
await seedFromText(existing.id, url);
|
|
1027
|
+
}
|
|
1006
1028
|
} else {
|
|
1007
|
-
|
|
1029
|
+
existing = undefined;
|
|
1008
1030
|
}
|
|
1009
|
-
}
|
|
1031
|
+
}
|
|
1032
|
+
if (!existing) {
|
|
1010
1033
|
for (let attempt = 0; ; attempt += 1) {
|
|
1011
1034
|
try {
|
|
1012
|
-
await
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1035
|
+
await withDesignSourceMutationTransaction(designId, (tx) =>
|
|
1036
|
+
tx.insert(schema.designFiles).values({
|
|
1037
|
+
id: fileId,
|
|
1038
|
+
designId,
|
|
1039
|
+
filename,
|
|
1040
|
+
fileType: "html",
|
|
1041
|
+
content: url,
|
|
1042
|
+
createdAt: now,
|
|
1043
|
+
updatedAt: now,
|
|
1044
|
+
}),
|
|
1045
|
+
);
|
|
1021
1046
|
await seedFromText(fileId, url);
|
|
1022
1047
|
break;
|
|
1023
1048
|
} catch (err) {
|
|
@@ -1048,10 +1073,17 @@ export default defineAction({
|
|
|
1048
1073
|
routeUrlsMatch(winner.content, url)
|
|
1049
1074
|
) {
|
|
1050
1075
|
fileId = winner.id;
|
|
1051
|
-
await
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1076
|
+
await withDesignSourceMutationTransaction(designId, (tx) =>
|
|
1077
|
+
tx
|
|
1078
|
+
.update(schema.designFiles)
|
|
1079
|
+
.set({ content: url, fileType: "html", updatedAt: now })
|
|
1080
|
+
.where(
|
|
1081
|
+
and(
|
|
1082
|
+
eq(schema.designFiles.id, winner.id),
|
|
1083
|
+
eq(schema.designFiles.designId, designId),
|
|
1084
|
+
),
|
|
1085
|
+
),
|
|
1086
|
+
);
|
|
1055
1087
|
if (await hasCollabState(winner.id)) {
|
|
1056
1088
|
await applyText(winner.id, url, "content", "agent");
|
|
1057
1089
|
} else {
|
|
@@ -12,12 +12,9 @@
|
|
|
12
12
|
* HTML attribute on the component root.
|
|
13
13
|
* - `classReplace` — replaces one Tailwind class with another on the root node.
|
|
14
14
|
*
|
|
15
|
-
* **Real-app sources
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* dedicated consented, version-guarded bridge transform. Until that exists,
|
|
19
|
-
* callers may preview but this action returns `ctaRequired: true` without
|
|
20
|
-
* reading or modifying a design file.
|
|
15
|
+
* **Real-app sources:** a literal JSX attribute on a single authored localhost
|
|
16
|
+
* anchor uses the consented, version-guarded local-file CAS path. Transformed,
|
|
17
|
+
* repeated, shared, and fusion sources still fail closed for agent handoff.
|
|
21
18
|
*
|
|
22
19
|
* See DESIGN-STUDIO-PLAN.md §6.1, §7 (preview/apply contract), §11 phase 2.
|
|
23
20
|
*/
|
|
@@ -76,9 +73,16 @@ import {
|
|
|
76
73
|
type ComponentSourceDocument,
|
|
77
74
|
} from "../shared/component-links.js";
|
|
78
75
|
import {
|
|
76
|
+
COMPONENT_PROP_PREFIX,
|
|
79
77
|
componentNameFor,
|
|
80
78
|
componentNodeIdMatches,
|
|
79
|
+
propNameToDataAttribute,
|
|
81
80
|
} from "../shared/component-model.js";
|
|
81
|
+
import {
|
|
82
|
+
planLocalJsxVisualEdit,
|
|
83
|
+
readLiteralJsxPropsAtAnchor,
|
|
84
|
+
type LocalJsxSourceAnchor,
|
|
85
|
+
} from "../shared/local-jsx-visual-edit.js";
|
|
82
86
|
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
83
87
|
import { sourceContentHash } from "../shared/source-workspace.js";
|
|
84
88
|
import {
|
|
@@ -87,6 +91,8 @@ import {
|
|
|
87
91
|
restoreComponentMainInDesign,
|
|
88
92
|
type ComponentArchiveMutationResult,
|
|
89
93
|
} from "./_component-archive.js";
|
|
94
|
+
import readLocalFileAction from "./read-local-file.js";
|
|
95
|
+
import writeLocalFileAction from "./write-local-file.js";
|
|
90
96
|
|
|
91
97
|
type SupportedStructureIntent =
|
|
92
98
|
| DeleteNodeEditIntent
|
|
@@ -105,6 +111,32 @@ type ComponentStructureEdit =
|
|
|
105
111
|
}
|
|
106
112
|
| { kind: "structure"; intents: SupportedStructureIntent[] };
|
|
107
113
|
|
|
114
|
+
interface LocalComponentSource extends LocalJsxSourceAnchor {
|
|
115
|
+
connectionId: string;
|
|
116
|
+
path: string;
|
|
117
|
+
expectedVersionHash?: string;
|
|
118
|
+
expectedValue?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function jsxPropNameForComponentAttribute(attribute: string): string {
|
|
122
|
+
if (!attribute.startsWith(COMPONENT_PROP_PREFIX)) return attribute;
|
|
123
|
+
return attribute
|
|
124
|
+
.slice(COMPONENT_PROP_PREFIX.length)
|
|
125
|
+
.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function literalJsxPropNameForComponentAttribute(
|
|
129
|
+
content: string,
|
|
130
|
+
anchor: LocalJsxSourceAnchor,
|
|
131
|
+
attribute: string,
|
|
132
|
+
): string | undefined {
|
|
133
|
+
const literalProps = readLiteralJsxPropsAtAnchor({ content, anchor });
|
|
134
|
+
const matches = literalProps?.filter(
|
|
135
|
+
({ name }) => propNameToDataAttribute(name) === attribute,
|
|
136
|
+
);
|
|
137
|
+
return matches?.length === 1 ? matches[0]!.name : undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
108
140
|
interface LinkedComponentSelection {
|
|
109
141
|
fileId: string;
|
|
110
142
|
nodeIds: string[];
|
|
@@ -808,8 +840,9 @@ export default defineAction({
|
|
|
808
840
|
"For inline/Alpine designs, edits the data-agent-native-prop-* attributes, " +
|
|
809
841
|
"x-data expression, or class list of the component root via the deterministic " +
|
|
810
842
|
"HTML-patch path (same seam as apply-visual-edit). " +
|
|
811
|
-
"For
|
|
812
|
-
"
|
|
843
|
+
"For a literal JSX attribute on a single authored localhost anchor, uses the " +
|
|
844
|
+
"consented local-file CAS path; transformed, repeated, shared, and fusion " +
|
|
845
|
+
"sources return ctaRequired=true. " +
|
|
813
846
|
"The structure variant atomically propagates bounded delete, wrap, unwrap, " +
|
|
814
847
|
"move, and style intents across linked HTML files; unsupported structural " +
|
|
815
848
|
"operations fail before any file is written. " +
|
|
@@ -923,6 +956,31 @@ export default defineAction({
|
|
|
923
956
|
.describe(
|
|
924
957
|
"Exact source version hashes for every HTML file in the Design. Required for linked property and structure edits.",
|
|
925
958
|
),
|
|
959
|
+
local: z
|
|
960
|
+
.object({
|
|
961
|
+
connectionId: z.string().min(1),
|
|
962
|
+
path: z.string().min(1),
|
|
963
|
+
line: z.number().int().positive(),
|
|
964
|
+
column: z.number().int().positive(),
|
|
965
|
+
positionPrecision: z
|
|
966
|
+
.enum(["authored", "transformed", "unknown"])
|
|
967
|
+
.optional(),
|
|
968
|
+
runtimeMultiplicity: z.number().int().positive().optional(),
|
|
969
|
+
scope: z
|
|
970
|
+
.enum([
|
|
971
|
+
"single-instance",
|
|
972
|
+
"repeated-render",
|
|
973
|
+
"shared-component-definition",
|
|
974
|
+
"unknown",
|
|
975
|
+
])
|
|
976
|
+
.optional(),
|
|
977
|
+
expectedVersionHash: z.string().optional(),
|
|
978
|
+
expectedValue: z
|
|
979
|
+
.string()
|
|
980
|
+
.optional()
|
|
981
|
+
.describe("Current literal JSX prop value used as a CAS guard."),
|
|
982
|
+
})
|
|
983
|
+
.optional(),
|
|
926
984
|
})
|
|
927
985
|
.optional(),
|
|
928
986
|
}),
|
|
@@ -935,13 +993,115 @@ export default defineAction({
|
|
|
935
993
|
const rawData = (access.resource as { data?: unknown }).data;
|
|
936
994
|
const sourceType = designSourceTypeFromData(rawData);
|
|
937
995
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
//
|
|
941
|
-
//
|
|
942
|
-
// must perform consent, canonical path resolution, AST anchoring, and an
|
|
943
|
-
// expected-version bridge write as one dedicated transaction.
|
|
996
|
+
const localSource = source?.local as LocalComponentSource | undefined;
|
|
997
|
+
|
|
998
|
+
// Literal localhost JSX props reuse the existing authored-anchor planner
|
|
999
|
+
// and consented bridge CAS. All other real-app edits stay fail-closed.
|
|
944
1000
|
if (sourceType !== "inline") {
|
|
1001
|
+
if (
|
|
1002
|
+
sourceType === "localhost" &&
|
|
1003
|
+
localSource &&
|
|
1004
|
+
edit.kind === "attribute"
|
|
1005
|
+
) {
|
|
1006
|
+
await assertAccess("design", designId, "editor");
|
|
1007
|
+
if (!localSource.expectedVersionHash) {
|
|
1008
|
+
return {
|
|
1009
|
+
designId,
|
|
1010
|
+
nodeId,
|
|
1011
|
+
sourceType,
|
|
1012
|
+
persisted: false,
|
|
1013
|
+
conflict: true,
|
|
1014
|
+
error:
|
|
1015
|
+
"Component prop edits require the source version captured with the live selection. Refresh the selection and retry.",
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
const live = await readLocalFileAction.run({
|
|
1019
|
+
designId,
|
|
1020
|
+
connectionId: localSource.connectionId,
|
|
1021
|
+
path: localSource.path,
|
|
1022
|
+
});
|
|
1023
|
+
if (
|
|
1024
|
+
localSource.expectedVersionHash &&
|
|
1025
|
+
live.versionHash !== localSource.expectedVersionHash
|
|
1026
|
+
) {
|
|
1027
|
+
return {
|
|
1028
|
+
designId,
|
|
1029
|
+
nodeId,
|
|
1030
|
+
sourceType,
|
|
1031
|
+
persisted: false,
|
|
1032
|
+
conflict: true,
|
|
1033
|
+
error:
|
|
1034
|
+
"The local component source changed while the prop edit was being prepared. Refresh and retry.",
|
|
1035
|
+
source: {
|
|
1036
|
+
kind: "local-file" as const,
|
|
1037
|
+
connectionId: localSource.connectionId,
|
|
1038
|
+
path: localSource.path,
|
|
1039
|
+
versionHash: live.versionHash,
|
|
1040
|
+
},
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const jsxPropName =
|
|
1045
|
+
literalJsxPropNameForComponentAttribute(
|
|
1046
|
+
live.content,
|
|
1047
|
+
localSource,
|
|
1048
|
+
edit.attribute,
|
|
1049
|
+
) ?? jsxPropNameForComponentAttribute(edit.attribute);
|
|
1050
|
+
const planned = planLocalJsxVisualEdit({
|
|
1051
|
+
content: live.content,
|
|
1052
|
+
anchor: localSource,
|
|
1053
|
+
intent: {
|
|
1054
|
+
kind: "attributes",
|
|
1055
|
+
values: {
|
|
1056
|
+
[jsxPropName]: edit.value,
|
|
1057
|
+
[edit.attribute]: edit.value,
|
|
1058
|
+
},
|
|
1059
|
+
expectedValues: {
|
|
1060
|
+
[jsxPropName]: localSource.expectedValue,
|
|
1061
|
+
},
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
if (planned.result.status !== "applied") {
|
|
1065
|
+
return {
|
|
1066
|
+
designId,
|
|
1067
|
+
nodeId,
|
|
1068
|
+
sourceType,
|
|
1069
|
+
persisted: false,
|
|
1070
|
+
ctaRequired: planned.result.status === "needsAgent",
|
|
1071
|
+
error: planned.result.message,
|
|
1072
|
+
result: planned.result,
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
let write: Awaited<ReturnType<typeof writeLocalFileAction.run>> | null =
|
|
1077
|
+
null;
|
|
1078
|
+
if (planned.result.changed) {
|
|
1079
|
+
await snapshotDesignBeforeAgentEdit(designId, context);
|
|
1080
|
+
write = await writeLocalFileAction.run({
|
|
1081
|
+
designId,
|
|
1082
|
+
connectionId: localSource.connectionId,
|
|
1083
|
+
relPath: localSource.path,
|
|
1084
|
+
content: planned.content,
|
|
1085
|
+
expectedVersionHash: live.versionHash,
|
|
1086
|
+
requireExpectedVersionHash: true,
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
return {
|
|
1090
|
+
designId,
|
|
1091
|
+
nodeId,
|
|
1092
|
+
sourceType,
|
|
1093
|
+
persisted: write ? write.written : !planned.result.changed,
|
|
1094
|
+
ctaRequired: false,
|
|
1095
|
+
source: {
|
|
1096
|
+
kind: "local-file" as const,
|
|
1097
|
+
connectionId: localSource.connectionId,
|
|
1098
|
+
path: localSource.path,
|
|
1099
|
+
versionHash: write?.versionHash ?? live.versionHash,
|
|
1100
|
+
},
|
|
1101
|
+
content: planned.content,
|
|
1102
|
+
result: planned.result,
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
945
1105
|
return {
|
|
946
1106
|
designId,
|
|
947
1107
|
nodeId,
|
|
@@ -1064,7 +1064,8 @@ export default defineAction({
|
|
|
1064
1064
|
if (
|
|
1065
1065
|
editIntent.kind !== "textContent" &&
|
|
1066
1066
|
editIntent.kind !== "class" &&
|
|
1067
|
-
editIntent.kind !== "style"
|
|
1067
|
+
editIntent.kind !== "style" &&
|
|
1068
|
+
editIntent.kind !== "attribute"
|
|
1068
1069
|
) {
|
|
1069
1070
|
return {
|
|
1070
1071
|
result: {
|
|
@@ -1458,6 +1459,7 @@ export default defineAction({
|
|
|
1458
1459
|
|
|
1459
1460
|
const workspace = await resolveSourceWorkspace(file.designId, {
|
|
1460
1461
|
includeContent: true,
|
|
1462
|
+
includeBoard: true,
|
|
1461
1463
|
});
|
|
1462
1464
|
if (workspace.sourceType !== "inline") {
|
|
1463
1465
|
return noWrite(
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
* **Tier A (Alpine / inline):** always available — the design HTML is the
|
|
21
21
|
* source of truth.
|
|
22
22
|
*
|
|
23
|
-
* **Tier B (real-app, localhost
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* **Tier B (real-app, localhost):** a single authored JSX opening tag can be
|
|
24
|
+
* promoted through the consented local-file CAS path. Transformed, repeated,
|
|
25
|
+
* shared, or fusion sources still return `ctaRequired: true`.
|
|
26
26
|
*
|
|
27
27
|
* See DESIGN-STUDIO-PLAN.md §6.1 (component model) and §7 (action surface).
|
|
28
28
|
*/
|
|
@@ -68,7 +68,13 @@ import {
|
|
|
68
68
|
linkedComponentRootForNode,
|
|
69
69
|
} from "../shared/component-model.js";
|
|
70
70
|
import { hasCapability } from "../shared/design-source-capabilities.js";
|
|
71
|
+
import {
|
|
72
|
+
planLocalJsxVisualEdit,
|
|
73
|
+
type LocalJsxSourceAnchor,
|
|
74
|
+
} from "../shared/local-jsx-visual-edit.js";
|
|
71
75
|
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
76
|
+
import readLocalFileAction from "./read-local-file.js";
|
|
77
|
+
import writeLocalFileAction from "./write-local-file.js";
|
|
72
78
|
|
|
73
79
|
// ─── Pure helpers ─────────────────────────────────────────────────────────────
|
|
74
80
|
|
|
@@ -81,8 +87,22 @@ export interface ComponentAttributeStamp {
|
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
export interface CreateComponentSourceExpectation {
|
|
84
|
-
currentContent
|
|
85
|
-
expectedVersionHash
|
|
90
|
+
currentContent?: string;
|
|
91
|
+
expectedVersionHash?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CreateComponentLocalSourceReceipt {
|
|
95
|
+
kind: "local-file";
|
|
96
|
+
connectionId: string;
|
|
97
|
+
path: string;
|
|
98
|
+
versionHash: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface LocalComponentSource extends LocalJsxSourceAnchor {
|
|
102
|
+
connectionId: string;
|
|
103
|
+
path: string;
|
|
104
|
+
expectedVersionHash?: string;
|
|
105
|
+
propStamps?: ComponentAttributeStamp[];
|
|
86
106
|
}
|
|
87
107
|
|
|
88
108
|
/** A linked component owns its descendants; they cannot become nested mains. */
|
|
@@ -260,9 +280,9 @@ export default defineAction({
|
|
|
260
280
|
'data-agent-native-component="<Name>" plus data-agent-native-prop-* for ' +
|
|
261
281
|
"obvious variant-like attributes (data-variant/size/state, aria-pressed, " +
|
|
262
282
|
"etc.). For inline/Alpine designs this writes the HTML directly via the " +
|
|
263
|
-
"deterministic patch path. For
|
|
264
|
-
"
|
|
265
|
-
"
|
|
283
|
+
"deterministic patch path. For localhost React, a single authored JSX " +
|
|
284
|
+
"anchor uses the consented local-file CAS path; transformed, repeated, " +
|
|
285
|
+
"shared, or fusion sources return ctaRequired=true. After it runs the node is a recognised component instance, so " +
|
|
266
286
|
"component-model detection, the canvas outline, and the Component section " +
|
|
267
287
|
"all pick it up.",
|
|
268
288
|
schema: z.object({
|
|
@@ -293,10 +313,41 @@ export default defineAction({
|
|
|
293
313
|
.object({
|
|
294
314
|
currentContent: z
|
|
295
315
|
.string()
|
|
316
|
+
.optional()
|
|
296
317
|
.describe("Exact source preimage the editor planned against."),
|
|
297
318
|
expectedVersionHash: z
|
|
298
319
|
.string()
|
|
320
|
+
.optional()
|
|
299
321
|
.describe("Hash of the live source preimage."),
|
|
322
|
+
local: z
|
|
323
|
+
.object({
|
|
324
|
+
connectionId: z.string().min(1),
|
|
325
|
+
path: z.string().min(1),
|
|
326
|
+
line: z.number().int().positive(),
|
|
327
|
+
column: z.number().int().positive(),
|
|
328
|
+
positionPrecision: z
|
|
329
|
+
.enum(["authored", "transformed", "unknown"])
|
|
330
|
+
.optional(),
|
|
331
|
+
runtimeMultiplicity: z.number().int().positive().optional(),
|
|
332
|
+
scope: z
|
|
333
|
+
.enum([
|
|
334
|
+
"single-instance",
|
|
335
|
+
"repeated-render",
|
|
336
|
+
"shared-component-definition",
|
|
337
|
+
"unknown",
|
|
338
|
+
])
|
|
339
|
+
.optional(),
|
|
340
|
+
expectedVersionHash: z.string().optional(),
|
|
341
|
+
propStamps: z
|
|
342
|
+
.array(
|
|
343
|
+
z.object({
|
|
344
|
+
name: z.string().min(1),
|
|
345
|
+
value: z.string(),
|
|
346
|
+
}),
|
|
347
|
+
)
|
|
348
|
+
.optional(),
|
|
349
|
+
})
|
|
350
|
+
.optional(),
|
|
300
351
|
})
|
|
301
352
|
.optional()
|
|
302
353
|
.describe(
|
|
@@ -323,9 +374,16 @@ export default defineAction({
|
|
|
323
374
|
const rawData = (access.resource as { data?: unknown }).data;
|
|
324
375
|
const sourceType = designSourceTypeFromData(rawData);
|
|
325
376
|
const caps = resolveSourceCapabilities(sourceType);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
377
|
+
const localSource = source?.local as LocalComponentSource | undefined;
|
|
378
|
+
|
|
379
|
+
// Real-app sources gate on `applyEdit` (bridge write hardening). A
|
|
380
|
+
// localhost source with an authored JSX anchor can use the existing
|
|
381
|
+
// consented local-file CAS path for literal component annotations.
|
|
382
|
+
if (
|
|
383
|
+
sourceType !== "inline" &&
|
|
384
|
+
!(sourceType === "localhost" && localSource) &&
|
|
385
|
+
!hasCapability(caps, "applyEdit")
|
|
386
|
+
) {
|
|
329
387
|
return {
|
|
330
388
|
designId,
|
|
331
389
|
sourceType,
|
|
@@ -338,7 +396,109 @@ export default defineAction({
|
|
|
338
396
|
}
|
|
339
397
|
|
|
340
398
|
await assertAccess("design", designId, "editor");
|
|
341
|
-
|
|
399
|
+
|
|
400
|
+
if (sourceType !== "inline") {
|
|
401
|
+
if (sourceType !== "localhost" || !localSource) {
|
|
402
|
+
return {
|
|
403
|
+
designId,
|
|
404
|
+
sourceType,
|
|
405
|
+
persisted: false,
|
|
406
|
+
ctaRequired: true,
|
|
407
|
+
ctaMessage:
|
|
408
|
+
"Creating a component from this source requires an authored localhost JSX anchor.",
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (!localSource.expectedVersionHash) {
|
|
413
|
+
return {
|
|
414
|
+
designId,
|
|
415
|
+
sourceType,
|
|
416
|
+
persisted: false,
|
|
417
|
+
conflict: true,
|
|
418
|
+
error:
|
|
419
|
+
"Create Component requires the source version captured with the live selection. Refresh the selection and retry.",
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const live = await readLocalFileAction.run({
|
|
424
|
+
designId,
|
|
425
|
+
connectionId: localSource.connectionId,
|
|
426
|
+
path: localSource.path,
|
|
427
|
+
});
|
|
428
|
+
if (
|
|
429
|
+
localSource.expectedVersionHash &&
|
|
430
|
+
live.versionHash !== localSource.expectedVersionHash
|
|
431
|
+
) {
|
|
432
|
+
return {
|
|
433
|
+
designId,
|
|
434
|
+
sourceType,
|
|
435
|
+
persisted: false,
|
|
436
|
+
conflict: true,
|
|
437
|
+
error:
|
|
438
|
+
"The local component source changed while Create Component was being prepared. Refresh and retry.",
|
|
439
|
+
source: {
|
|
440
|
+
kind: "local-file" as const,
|
|
441
|
+
connectionId: localSource.connectionId,
|
|
442
|
+
path: localSource.path,
|
|
443
|
+
versionHash: live.versionHash,
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const componentName = normalizeComponentName(name);
|
|
449
|
+
const values = Object.fromEntries([
|
|
450
|
+
[COMPONENT_NAME_ATTR, componentName],
|
|
451
|
+
...(localSource.propStamps ?? []).map((stamp) => [
|
|
452
|
+
stamp.name,
|
|
453
|
+
stamp.value,
|
|
454
|
+
]),
|
|
455
|
+
]);
|
|
456
|
+
const planned = planLocalJsxVisualEdit({
|
|
457
|
+
content: live.content,
|
|
458
|
+
anchor: localSource,
|
|
459
|
+
intent: { kind: "attributes", values },
|
|
460
|
+
});
|
|
461
|
+
if (planned.result.status !== "applied") {
|
|
462
|
+
return {
|
|
463
|
+
designId,
|
|
464
|
+
sourceType,
|
|
465
|
+
persisted: false,
|
|
466
|
+
ctaRequired: planned.result.status === "needsAgent",
|
|
467
|
+
error: planned.result.message,
|
|
468
|
+
result: planned.result,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let write: Awaited<ReturnType<typeof writeLocalFileAction.run>> | null =
|
|
473
|
+
null;
|
|
474
|
+
if (planned.result.changed) {
|
|
475
|
+
await snapshotDesignBeforeAgentEdit(designId, context);
|
|
476
|
+
write = await writeLocalFileAction.run({
|
|
477
|
+
designId,
|
|
478
|
+
connectionId: localSource.connectionId,
|
|
479
|
+
relPath: localSource.path,
|
|
480
|
+
content: planned.content,
|
|
481
|
+
expectedVersionHash: live.versionHash,
|
|
482
|
+
requireExpectedVersionHash: true,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
designId,
|
|
487
|
+
nodeId,
|
|
488
|
+
componentName,
|
|
489
|
+
sourceType,
|
|
490
|
+
persisted: write ? write.written : !planned.result.changed,
|
|
491
|
+
ctaRequired: false,
|
|
492
|
+
source: {
|
|
493
|
+
kind: "local-file" as const,
|
|
494
|
+
connectionId: localSource.connectionId,
|
|
495
|
+
path: localSource.path,
|
|
496
|
+
versionHash: write?.versionHash ?? live.versionHash,
|
|
497
|
+
},
|
|
498
|
+
content: planned.content,
|
|
499
|
+
result: planned.result,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
342
502
|
|
|
343
503
|
// ── Fetch file ───────────────────────────────────────────────────────────
|
|
344
504
|
const conditions = [
|
|
@@ -490,6 +650,7 @@ export default defineAction({
|
|
|
490
650
|
updatedAt: string;
|
|
491
651
|
} | null = null;
|
|
492
652
|
if (contentChanged) {
|
|
653
|
+
await snapshotDesignBeforeAgentEdit(designId, context);
|
|
493
654
|
persistedReceipt = await writeInlineSourceFile({
|
|
494
655
|
designId: file.designId,
|
|
495
656
|
file: workspaceFile,
|