@agent-native/core 0.165.4 → 0.167.0

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.
Files changed (223) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +5 -1
  3. package/corpus/templates/analytics/.agents/skills/workspace-conventions/SKILL.md +4 -1
  4. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +93 -56
  5. package/corpus/templates/analytics/app/i18n-data.ts +23 -20
  6. package/corpus/templates/analytics/app/pages/Settings.tsx +0 -1
  7. package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +0 -1
  8. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +1 -1
  9. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +2 -1
  10. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +0 -1
  11. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +0 -32
  12. package/corpus/templates/analytics/server/lib/session-replay.ts +1 -5
  13. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +5 -1
  14. package/corpus/templates/assets/.agents/skills/workspace-conventions/SKILL.md +4 -1
  15. package/corpus/templates/assets/actions/navigate.ts +2 -2
  16. package/corpus/templates/assets/actions/save-generated-image.ts +0 -5
  17. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +2 -262
  18. package/corpus/templates/assets/app/routes/library.tsx +1 -1
  19. package/corpus/templates/assets/app/routes/settings.tsx +0 -3
  20. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +5 -1
  21. package/corpus/templates/brain/.agents/skills/workspace-conventions/SKILL.md +4 -1
  22. package/corpus/templates/brain/actions/ask-brain.ts +0 -1
  23. package/corpus/templates/brain/actions/navigate.ts +2 -2
  24. package/corpus/templates/brain/app/routes/sources.tsx +0 -4
  25. package/corpus/templates/brain/server/lib/capture-sanitization.ts +0 -11
  26. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +5 -1
  27. package/corpus/templates/calendar/.agents/skills/workspace-conventions/SKILL.md +4 -1
  28. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +0 -20
  29. package/corpus/templates/calendar/app/pages/CalendarView.tsx +0 -1
  30. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +5 -1
  31. package/corpus/templates/chat/.agents/skills/workspace-conventions/SKILL.md +4 -1
  32. package/corpus/templates/chat/actions/navigate.ts +2 -2
  33. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +5 -1
  34. package/corpus/templates/clips/.agents/skills/workspace-conventions/SKILL.md +4 -1
  35. package/corpus/templates/clips/CHANGELOG.md +1 -0
  36. package/corpus/templates/clips/actions/import-loom-recording.ts +16 -4
  37. package/corpus/templates/clips/actions/navigate.ts +2 -2
  38. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +0 -1
  39. package/corpus/templates/clips/app/components/library/space-dialogs.tsx +0 -1
  40. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +0 -1
  41. package/corpus/templates/clips/app/components/player/clips-share-trigger.tsx +17 -0
  42. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +3 -3
  43. package/corpus/templates/clips/app/components/player/video-player.tsx +2 -2
  44. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +0 -1
  45. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +0 -1
  46. package/corpus/templates/clips/app/global.css +31 -0
  47. package/corpus/templates/clips/app/hooks/use-playback-position.ts +0 -2
  48. package/corpus/templates/clips/app/lib/download-release-channel.ts +10 -0
  49. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +1 -3
  50. package/corpus/templates/clips/app/routes/download.tsx +67 -44
  51. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +7 -12
  52. package/corpus/templates/clips/app/routes/record.tsx +6 -6
  53. package/corpus/templates/clips/app/routes/share.$shareId.tsx +26 -29
  54. package/corpus/templates/clips/changelog/2026-08-20-clip-viewer-share-actions.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-08-20-preparing-recording-countdown-overlay.md +1 -0
  56. package/corpus/templates/clips/chrome-extension/src/background.ts +0 -13
  57. package/corpus/templates/clips/chrome-extension/src/overlay.ts +0 -1
  58. package/corpus/templates/clips/chrome-extension/src/popup.ts +0 -20
  59. package/corpus/templates/clips/desktop/src/app.tsx +6 -18
  60. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +0 -1
  61. package/corpus/templates/clips/desktop/src/lib/recorder.ts +0 -8
  62. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +5 -1
  63. package/corpus/templates/content/.agents/skills/workspace-conventions/SKILL.md +4 -1
  64. package/corpus/templates/content/actions/_database-source-utils.ts +0 -3
  65. package/corpus/templates/content/actions/attach-content-database-source.ts +1 -4
  66. package/corpus/templates/content/actions/navigate.ts +2 -2
  67. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +1 -1
  68. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +0 -6
  69. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +0 -580
  70. package/corpus/templates/content/app/components/editor/database/FormView.tsx +2 -2
  71. package/corpus/templates/content/app/components/editor/database/filter-sort.ts +1 -1
  72. package/corpus/templates/content/app/components/editor/database/settings.tsx +0 -23
  73. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +0 -1
  74. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +8 -304
  75. package/corpus/templates/content/app/hooks/use-documents.ts +0 -1
  76. package/corpus/templates/content/shared/api.ts +0 -1
  77. package/corpus/templates/content/shared/builder-mdx.ts +0 -1
  78. package/corpus/templates/content/shared/nfm.ts +1 -7
  79. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +5 -1
  80. package/corpus/templates/crm/.agents/skills/workspace-conventions/SKILL.md +4 -1
  81. package/corpus/templates/crm/actions/merge-crm-records.ts +0 -4
  82. package/corpus/templates/crm/app/components/crm/record/RecordLists.tsx +0 -1
  83. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +0 -1
  84. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +0 -1
  85. package/corpus/templates/crm/server/db/crm-store.ts +1 -1
  86. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +5 -1
  87. package/corpus/templates/design/.agents/skills/workspace-conventions/SKILL.md +4 -1
  88. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1 -14
  89. package/corpus/templates/design/AGENTS.md +2 -1
  90. package/corpus/templates/design/actions/apply-component-prop-edit.ts +1 -5
  91. package/corpus/templates/design/actions/generate-design.ts +0 -1
  92. package/corpus/templates/design/actions/generate-screens.ts +5 -2
  93. package/corpus/templates/design/actions/go-to-main-component.ts +2 -2
  94. package/corpus/templates/design/actions/list-design-systems.ts +10 -0
  95. package/corpus/templates/design/actions/navigate.ts +2 -2
  96. package/corpus/templates/design/actions/open-component-source.ts +4 -4
  97. package/corpus/templates/design/actions/present-design-variants.ts +1 -2
  98. package/corpus/templates/design/actions/run-design-audit.ts +0 -22
  99. package/corpus/templates/design/actions/show-design-questions.ts +5 -2
  100. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +19 -8
  101. package/corpus/templates/design/app/components/design/EditPanel.tsx +11 -183
  102. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +21 -4
  103. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +1 -5
  104. package/corpus/templates/design/app/components/design/bridge/codegen.ts +1 -1
  105. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +10 -19
  106. package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +1 -1
  107. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +13 -3
  108. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +38 -32
  109. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +1 -4
  110. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +0 -4
  111. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +15 -6
  112. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +2 -2
  113. package/corpus/templates/design/app/components/visual-editor/NodeRewriteProposal.tsx +0 -1
  114. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +1 -1
  115. package/corpus/templates/design/app/i18n-data.ts +122 -0
  116. package/corpus/templates/design/app/lib/clipboard-content-lineage.ts +11 -1
  117. package/corpus/templates/design/app/pages/DesignEditor.tsx +50 -254
  118. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +163 -8
  119. package/corpus/templates/design/app/pages/Index.tsx +17 -0
  120. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +44 -32
  121. package/corpus/templates/design/app/pages/design-editor/canvas-primitives.ts +10 -0
  122. package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +49 -3
  123. package/corpus/templates/design/app/pages/design-editor/clone-idrefs.ts +31 -0
  124. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +28 -0
  125. package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +3 -3
  126. package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +44 -14
  127. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +16 -5
  128. package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +1 -1
  129. package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +1 -1
  130. package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +1 -1
  131. package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +1 -1
  132. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +142 -17
  133. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +1 -0
  134. package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +1 -1
  135. package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +1 -0
  136. package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +25 -9
  137. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +1 -0
  138. package/corpus/templates/design/app/pages/design-editor/measure-child-rects.ts +83 -0
  139. package/corpus/templates/design/app/pages/design-editor/paste-placement.ts +66 -0
  140. package/corpus/templates/design/server/routes/[...page].get.ts +0 -1
  141. package/corpus/templates/design/shared/code-layer.ts +120 -70
  142. package/corpus/templates/design/shared/design-surface-index.ts +0 -1
  143. package/corpus/templates/design/shared/pen-path.ts +0 -4
  144. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +5 -1
  145. package/corpus/templates/dispatch/.agents/skills/workspace-conventions/SKILL.md +4 -1
  146. package/corpus/templates/dispatch/app/routes/integrations.tsx +0 -109
  147. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +5 -1
  148. package/corpus/templates/factory/.agents/skills/workspace-conventions/SKILL.md +4 -1
  149. package/corpus/templates/factory/actions/navigate.ts +2 -2
  150. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +0 -13
  151. package/corpus/templates/factory/app/routes/factory.tsx +0 -1
  152. package/corpus/templates/factory/server/factory-graph/store.ts +0 -1
  153. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +5 -1
  154. package/corpus/templates/forms/.agents/skills/workspace-conventions/SKILL.md +4 -1
  155. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +5 -1
  156. package/corpus/templates/macros/.agents/skills/workspace-conventions/SKILL.md +4 -1
  157. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +0 -1
  158. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +5 -1
  159. package/corpus/templates/mail/.agents/skills/workspace-conventions/SKILL.md +4 -1
  160. package/corpus/templates/mail/app/components/email/EmailList.tsx +0 -1
  161. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
  162. package/corpus/templates/mail/shared/markdown.ts +3 -0
  163. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +5 -1
  164. package/corpus/templates/plan/.agents/skills/workspace-conventions/SKILL.md +4 -1
  165. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +5 -1
  166. package/corpus/templates/slides/.agents/skills/workspace-conventions/SKILL.md +4 -1
  167. package/corpus/templates/slides/actions/add-slide.ts +0 -1
  168. package/corpus/templates/slides/actions/list-decks.ts +1 -1
  169. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +1 -4
  170. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +0 -1
  171. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
  172. package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -6
  173. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +5 -1
  174. package/corpus/templates/tasks/.agents/skills/workspace-conventions/SKILL.md +4 -1
  175. package/corpus/templates/tasks/app/components/layout/Layout.tsx +0 -1
  176. package/corpus/templates/tasks/app/hooks/use-visible-task-fields.ts +1 -4
  177. package/corpus/templates/tasks/server/custom-fields/store.ts +1 -6
  178. package/corpus/templates/tasks/shared/list-screen-snapshot.ts +2 -2
  179. package/dist/agent/engine/registry.js +27 -14
  180. package/dist/agent/index.d.ts +1 -1
  181. package/dist/agent/production-agent.d.ts +19 -6
  182. package/dist/agent/production-agent.js +57 -16
  183. package/dist/agent/types.d.ts +3 -0
  184. package/dist/client/EnvironmentBadge.d.ts +1 -0
  185. package/dist/client/EnvironmentBadge.js +7 -0
  186. package/dist/collab/awareness.d.ts +2 -2
  187. package/dist/collab/routes.d.ts +1 -1
  188. package/dist/config.d.ts +11 -0
  189. package/dist/config.js +248 -0
  190. package/dist/deploy/build.js +12 -1
  191. package/dist/index.d.ts +1 -1
  192. package/dist/notifications/routes.d.ts +3 -3
  193. package/dist/observability/routes.d.ts +2 -2
  194. package/dist/progress/routes.d.ts +1 -1
  195. package/dist/resources/handlers.d.ts +2 -2
  196. package/dist/server/agent-chat/plugin-options.d.ts +3 -2
  197. package/dist/server/agent-chat-plugin.js +5 -2
  198. package/dist/server/index.d.ts +1 -1
  199. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +5 -1
  200. package/dist/templates/chat/.agents/skills/workspace-conventions/SKILL.md +4 -1
  201. package/dist/templates/chat/actions/navigate.ts +2 -2
  202. package/dist/templates/default/.agents/skills/secrets/SKILL.md +5 -1
  203. package/dist/templates/default/.agents/skills/workspace-conventions/SKILL.md +4 -1
  204. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +5 -1
  205. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +5 -1
  206. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +4 -1
  207. package/dist/vite/agent-native-config-loader.d.ts +1 -0
  208. package/dist/vite/agent-native-config-loader.js +3 -3
  209. package/dist/vite/agents-bundle-plugin.d.ts +1 -1
  210. package/dist/vite/agents-bundle-plugin.js +13 -1
  211. package/dist/vite/client.js +14 -14
  212. package/docs/content/agent-native-config.mdx +62 -11
  213. package/docs/content/agent-surfaces.mdx +6 -0
  214. package/docs/content/environment-variables.mdx +5 -2
  215. package/package.json +3 -3
  216. package/src/templates/chat/.agents/skills/secrets/SKILL.md +5 -1
  217. package/src/templates/chat/.agents/skills/workspace-conventions/SKILL.md +4 -1
  218. package/src/templates/chat/actions/navigate.ts +2 -2
  219. package/src/templates/default/.agents/skills/secrets/SKILL.md +5 -1
  220. package/src/templates/default/.agents/skills/workspace-conventions/SKILL.md +4 -1
  221. package/src/templates/headless/.agents/skills/secrets/SKILL.md +5 -1
  222. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +5 -1
  223. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +4 -1
@@ -0,0 +1,83 @@
1
+ export interface FreeformGeometry {
2
+ container: { width: number; height: number } | null;
3
+ children: Record<
4
+ string,
5
+ { x: number; y: number; width: number; height: number }
6
+ >;
7
+ }
8
+
9
+ const NOTHING_MEASURED: FreeformGeometry = { container: null, children: {} };
10
+
11
+ /**
12
+ * Container-relative geometry for a node and its direct children, read from
13
+ * whichever preview iframe actually holds the node — the board and each screen
14
+ * have their own document, so a single global iframe lookup finds the wrong one.
15
+ */
16
+ export function measureFreeformGeometry(nodeId: string): FreeformGeometry {
17
+ if (typeof document === "undefined") return NOTHING_MEASURED;
18
+ const selector = `[data-agent-native-node-id="${CSS.escape(nodeId)}"]`;
19
+ const iframes = Array.from(
20
+ document.querySelectorAll<HTMLIFrameElement>(
21
+ "iframe[data-design-preview-iframe],iframe[data-screen-iframe-id]",
22
+ ),
23
+ );
24
+ for (const iframe of iframes) {
25
+ let container: HTMLElement | null = null;
26
+ try {
27
+ container =
28
+ iframe.contentDocument?.querySelector<HTMLElement>(selector) ?? null;
29
+ } catch {
30
+ // Cross-origin preview surfaces are not measurable; try the next one.
31
+ continue;
32
+ }
33
+ if (!container) continue;
34
+ const view = container.ownerDocument.defaultView;
35
+ const origin = container.getBoundingClientRect();
36
+ // Absolute offsets resolve against the padding box, but a client rect is
37
+ // the border box, so a bordered container shifts every child it pins.
38
+ const borders = view?.getComputedStyle(container);
39
+ // Client rects are viewport-relative, so a scrolled container reports its
40
+ // children where they currently sit, not where the content box holds them.
41
+ const originLeft =
42
+ origin.left + edgeWidth(borders?.borderLeftWidth) - container.scrollLeft;
43
+ const originTop =
44
+ origin.top + edgeWidth(borders?.borderTopWidth) - container.scrollTop;
45
+ const rects: Record<
46
+ string,
47
+ { x: number; y: number; width: number; height: number }
48
+ > = {};
49
+ for (const child of Array.from(container.children)) {
50
+ const childId = child.getAttribute("data-agent-native-node-id");
51
+ if (!childId) continue;
52
+ const rect = child.getBoundingClientRect();
53
+ rects[childId] = {
54
+ x: rect.left - originLeft,
55
+ y: rect.top - originTop,
56
+ width: rect.width,
57
+ height: rect.height,
58
+ };
59
+ }
60
+ // Own box, in the same box-sizing terms as a width/height declaration on
61
+ // this element, so writing it back reproduces the rendered size.
62
+ const own = borders
63
+ ? {
64
+ width: Number.parseFloat(borders.width),
65
+ height: Number.parseFloat(borders.height),
66
+ }
67
+ : null;
68
+ return {
69
+ container:
70
+ own && Number.isFinite(own.width) && Number.isFinite(own.height)
71
+ ? own
72
+ : null,
73
+ children: rects,
74
+ };
75
+ }
76
+ return NOTHING_MEASURED;
77
+ }
78
+
79
+ function edgeWidth(value: string | undefined): number {
80
+ const parsed = Number.parseFloat(value ?? "");
81
+ // A missing or non-length border computes to no edge; both mean zero inset.
82
+ return Number.isFinite(parsed) ? parsed : 0;
83
+ }
@@ -131,3 +131,69 @@ export function resolvePastePlacementForSelection(args: {
131
131
  if (!node) return null;
132
132
  return resolvePastePlacement(pasteTargetFromCodeLayerNode(node));
133
133
  }
134
+
135
+ export interface PasteSourceAnchor {
136
+ fileId: string;
137
+ /**
138
+ * null when the entries do not share one resolvable parent. Never an empty
139
+ * array — a caller must not read "unresolved" as "insert at the root here",
140
+ * because the stored left/top are relative to a parent that is not there.
141
+ */
142
+ parentSelectors: string[] | null;
143
+ }
144
+
145
+ /**
146
+ * A copied layer's left/top are parent-relative, so a paste that lands
147
+ * anywhere else reads them in the wrong coordinate space. Null means the
148
+ * source is unknown — never "the document root".
149
+ */
150
+ export function resolvePasteSourceAnchor(args: {
151
+ entries: ReadonlyArray<{ rootNodeId?: string; sourceFileId: string }>;
152
+ getContent: (fileId: string) => string | undefined;
153
+ }): PasteSourceAnchor | null {
154
+ const first = args.entries[0];
155
+ if (!first) return null;
156
+ if (args.entries.some((entry) => entry.sourceFileId !== first.sourceFileId)) {
157
+ return null;
158
+ }
159
+ const content = args.getContent(first.sourceFileId);
160
+ if (!content) return null;
161
+ const projection = buildCodeLayerProjection(content);
162
+ const unresolved: PasteSourceAnchor = {
163
+ fileId: first.sourceFileId,
164
+ parentSelectors: null,
165
+ };
166
+ const parentIds = args.entries.map((entry) => {
167
+ const node = entry.rootNodeId
168
+ ? projection.nodes.find(
169
+ (candidate) =>
170
+ candidate.dataAttributes["data-agent-native-node-id"] ===
171
+ entry.rootNodeId || candidate.id === entry.rootNodeId,
172
+ )
173
+ : undefined;
174
+ return node?.parentId ?? null;
175
+ });
176
+ const [sharedParentId] = parentIds;
177
+ // Layers copied out of different parents have no common coordinate space, so
178
+ // forcing them into the first one's parent moves the rest.
179
+ if (!sharedParentId || parentIds.some((id) => id !== sharedParentId)) {
180
+ return unresolved;
181
+ }
182
+ const parent = projection.nodes.find(
183
+ (candidate) => candidate.id === sharedParentId,
184
+ );
185
+ if (!parent) return unresolved;
186
+ const parentNodeId = parent.dataAttributes["data-agent-native-node-id"];
187
+ const parentSelectors = [
188
+ // A node-id selector is unique by construction; the projection's
189
+ // class/path aliases can match several siblings, and
190
+ // insertClonedHtmlLayers fails closed on that ambiguity.
191
+ parentNodeId
192
+ ? `[data-agent-native-node-id="${parentNodeId.replace(/["\\]/g, "\\$&")}"]`
193
+ : null,
194
+ parent.selector,
195
+ ...parent.selectors,
196
+ ].filter((selector): selector is string => Boolean(selector));
197
+ if (parentSelectors.length === 0) return unresolved;
198
+ return { fileId: first.sourceFileId, parentSelectors };
199
+ }
@@ -10,7 +10,6 @@ import {
10
10
  } from "@agent-native/core/shared";
11
11
  import {
12
12
  defineEventHandler,
13
- getHeader,
14
13
  getQuery,
15
14
  getRequestURL,
16
15
  setResponseHeader,
@@ -498,6 +498,17 @@ export interface AutoLayoutEditIntent {
498
498
  enabled: boolean;
499
499
  direction?: "row" | "column";
500
500
  gap?: string;
501
+ /**
502
+ * Measured child geometry, container-relative, keyed by stable node id.
503
+ * Supplied when disabling so children keep their rendered positions and
504
+ * become freely draggable; without it they re-stack in block flow.
505
+ */
506
+ childRects?: Record<
507
+ string,
508
+ { x: number; y: number; width: number; height: number }
509
+ >;
510
+ /** Rendered size of the container, so it cannot collapse once flow empties. */
511
+ containerRect?: { width: number; height: number };
501
512
  }
502
513
 
503
514
  /**
@@ -950,17 +961,6 @@ function cssIdent(value: string): string | null {
950
961
  return null;
951
962
  }
952
963
 
953
- function unquoteHtmlAttributeValue(value: string): string {
954
- const trimmed = value.trim();
955
- if (
956
- (trimmed.startsWith('"') && trimmed.endsWith('"')) ||
957
- (trimmed.startsWith("'") && trimmed.endsWith("'"))
958
- ) {
959
- return trimmed.slice(1, -1);
960
- }
961
- return trimmed;
962
- }
963
-
964
964
  function escapeHtmlAttribute(value: string): string {
965
965
  return value
966
966
  .replace(/&/g, "&amp;")
@@ -1777,18 +1777,6 @@ function utilityToStyleProperty(
1777
1777
  return null;
1778
1778
  }
1779
1779
 
1780
- function classStyleToken(token: string): StyleToken | null {
1781
- const { utility } = parseClassToken(token);
1782
- const mapped = utilityToStyleProperty(utility);
1783
- if (!mapped) return null;
1784
- const { property, confidence } = mapped;
1785
- // The legacy value field preserves the original full token (including prefix)
1786
- // for backward compatibility. The responsive path uses `breakpointValues`.
1787
- const value =
1788
- property === "display" ? (utility === "hidden" ? "none" : utility) : token;
1789
- return { property, value, token, source: "class", confidence };
1790
- }
1791
-
1792
1780
  function layoutFor(
1793
1781
  element: ParsedElement,
1794
1782
  parent: ParsedElement | undefined,
@@ -3385,43 +3373,6 @@ const FLEX_ITEM_STRIP_PROPS = [
3385
3373
  "order",
3386
3374
  ] as const;
3387
3375
 
3388
- /**
3389
- * Apply display:flex + direction + gap to a raw open-tag string and return it.
3390
- * Only touches the style attribute.
3391
- */
3392
- function addAutoLayoutStyleToOpenTag(
3393
- openTag: string,
3394
- element: ParsedElement,
3395
- html: string,
3396
- direction: "row" | "column",
3397
- gap: string,
3398
- ): string {
3399
- const currentStyle = attributeValue(element, "style");
3400
- let declarations = parseStyleDeclarations(currentStyle);
3401
- const setOrReplace = (prop: string, val: string) => {
3402
- const existing = declarations.find((d) => d.property === prop);
3403
- if (existing) {
3404
- existing.value = val;
3405
- } else {
3406
- declarations.push({ property: prop, value: val });
3407
- }
3408
- };
3409
- setOrReplace("display", "flex");
3410
- setOrReplace("flex-direction", direction);
3411
- setOrReplace("gap", gap);
3412
- const nextStyle = serializeStyleDeclarations(declarations);
3413
- // We work on a scratch copy relative to element boundaries
3414
- const attr = getAttribute(element, "style");
3415
- if (attr) {
3416
- return `${openTag.slice(0, attr.start - element.start)}${attr.name}="${escapeHtmlAttribute(nextStyle)}"${openTag.slice(attr.end - element.start)}`;
3417
- }
3418
- // Insert before closing >
3419
- const closeChar = openTag.endsWith("/>")
3420
- ? openTag.length - 2
3421
- : openTag.length - 1;
3422
- return `${openTag.slice(0, closeChar)} style="${escapeHtmlAttribute(nextStyle)}"${openTag.slice(closeChar)}`;
3423
- }
3424
-
3425
3376
  /**
3426
3377
  * Strip absolute-positioning properties from a child's inline style, applying
3427
3378
  * the edit directly to the html string at the child element's source spans.
@@ -3889,6 +3840,13 @@ function applyUnwrap(
3889
3840
  /**
3890
3841
  * CONVERT: toggle auto-layout (display:flex) on an existing container.
3891
3842
  */
3843
+ /** Whether an existing min-width/min-height already preserves `extent` px. */
3844
+ function holdsOpen(value: string, extent: number): boolean {
3845
+ const trimmed = value.trim();
3846
+ if (!/^[\d.]+px$/i.test(trimmed)) return !/^0[a-z%]*$/i.test(trimmed);
3847
+ return Number.parseFloat(trimmed) >= extent;
3848
+ }
3849
+
3892
3850
  function applyAutoLayout(
3893
3851
  html: string,
3894
3852
  build: ProjectionBuild,
@@ -3907,21 +3865,113 @@ function applyAutoLayout(
3907
3865
  if (!element) return "conflict";
3908
3866
 
3909
3867
  if (!enabled) {
3910
- // Turn off auto-layout: set display:block.
3868
+ const childRects = intent.childRects;
3869
+ const hasRects = !!childRects && Object.keys(childRects).length > 0;
3911
3870
  const currentStyle = attributeValue(element, "style");
3912
- let declarations = parseStyleDeclarations(currentStyle);
3913
- const displayDecl = declarations.find((d) => d.property === "display");
3914
- if (displayDecl) {
3915
- displayDecl.value = "block";
3916
- } else {
3917
- declarations.push({ property: "display", value: "block" });
3871
+ const declarations = parseStyleDeclarations(currentStyle);
3872
+ const setOnContainer = (property: string, value: string) => {
3873
+ const existing = declarations.find((d) => d.property === property);
3874
+ if (existing) existing.value = value;
3875
+ else declarations.push({ property, value });
3876
+ };
3877
+ setOnContainer("display", "block");
3878
+ if (hasRects) {
3879
+ // Absolute children resolve against the nearest positioned ancestor, so
3880
+ // a static container would let them escape to the page.
3881
+ const position = declarations.find((d) => d.property === "position");
3882
+ if (!position || position.value === "static") {
3883
+ setOnContainer("position", "relative");
3884
+ }
3885
+ // With every child absolute the content box is empty, so a hug-sized
3886
+ // container collapses and overflow:hidden then hides what we just pinned.
3887
+ const rect = intent.containerRect;
3888
+ if (rect) {
3889
+ for (const [property, value] of [
3890
+ ["min-width", rect.width],
3891
+ ["min-height", rect.height],
3892
+ ] as const) {
3893
+ const existing = declarations.find((d) => d.property === property);
3894
+ // `min-height: 0` is the standard flex idiom and holds nothing open.
3895
+ // Only a px minimum at least as large as the measured extent does.
3896
+ if (existing && !holdsOpen(existing.value, value)) {
3897
+ existing.value = `${Math.round(value)}px`;
3898
+ } else if (!existing) {
3899
+ declarations.push({ property, value: `${Math.round(value)}px` });
3900
+ }
3901
+ }
3902
+ }
3903
+ }
3904
+ let result = replaceOrInsertAttribute(
3905
+ html,
3906
+ element,
3907
+ "style",
3908
+ serializeStyleDeclarations(declarations),
3909
+ );
3910
+ if (!hasRects) {
3911
+ return {
3912
+ content: result,
3913
+ capability: { kind: "style", properties: ["display"], confidence: 0.9 },
3914
+ };
3915
+ }
3916
+
3917
+ const updatedElements = parseHtmlElements(result);
3918
+ const targetAttr = attributeValue(element, "data-agent-native-node-id");
3919
+ const updatedTarget =
3920
+ (targetAttr
3921
+ ? updatedElements.find(
3922
+ (fe) =>
3923
+ attributeValue(fe, "data-agent-native-node-id") === targetAttr,
3924
+ )
3925
+ : undefined) ??
3926
+ updatedElements.find((fe) => fe.start === element.start);
3927
+ if (updatedTarget) {
3928
+ // Reverse order keeps earlier offsets valid as each write shifts the rest.
3929
+ for (const childIndex of [...updatedTarget.childIndexes].reverse()) {
3930
+ const child = updatedElements[childIndex];
3931
+ if (!child) continue;
3932
+ const childId = attributeValue(child, "data-agent-native-node-id");
3933
+ const rect = childId ? childRects[childId] : undefined;
3934
+ if (!rect) continue;
3935
+ const childDecls = parseStyleDeclarations(
3936
+ attributeValue(child, "style"),
3937
+ );
3938
+ const setOnChild = (property: string, value: string) => {
3939
+ const existing = childDecls.find((d) => d.property === property);
3940
+ if (existing) existing.value = value;
3941
+ else childDecls.push({ property, value });
3942
+ };
3943
+ // The measured rect is a border box placed by its margin edge, so a
3944
+ // content-box child would grow by its padding and a margin would shift
3945
+ // it off the position we just measured.
3946
+ setOnChild("box-sizing", "border-box");
3947
+ setOnChild("margin", "0");
3948
+ setOnChild("position", "absolute");
3949
+ setOnChild("left", `${Math.round(rect.x)}px`);
3950
+ setOnChild("top", `${Math.round(rect.y)}px`);
3951
+ setOnChild("width", `${Math.round(rect.width)}px`);
3952
+ setOnChild("height", `${Math.round(rect.height)}px`);
3953
+ result = replaceOrInsertAttribute(
3954
+ result,
3955
+ child,
3956
+ "style",
3957
+ serializeStyleDeclarations(childDecls),
3958
+ );
3959
+ }
3918
3960
  }
3919
- const nextStyle = serializeStyleDeclarations(declarations);
3920
3961
  return {
3921
- content: replaceOrInsertAttribute(html, element, "style", nextStyle),
3962
+ content: result,
3922
3963
  capability: {
3923
3964
  kind: "style",
3924
- properties: ["display"],
3965
+ properties: [
3966
+ "display",
3967
+ "position",
3968
+ "min-width",
3969
+ "min-height",
3970
+ "left",
3971
+ "top",
3972
+ "width",
3973
+ "height",
3974
+ ],
3925
3975
  confidence: 0.9,
3926
3976
  },
3927
3977
  };
@@ -13,7 +13,6 @@
13
13
  * See `DESIGN-STUDIO-PLAN.md` §4.2 for rationale.
14
14
  */
15
15
 
16
- import type { CodeLayerNode } from "./code-layer";
17
16
  import type { DesignCapabilityName } from "./design-source-capabilities";
18
17
  import type { DesignSourceType } from "./source-mode";
19
18
 
@@ -683,7 +683,3 @@ function roundCoord(value: number) {
683
683
  function samePoint(a: PenPoint, b: PenPoint) {
684
684
  return a.x === b.x && a.y === b.y;
685
685
  }
686
-
687
- function isPenPoint(point: PenPoint | undefined): point is PenPoint {
688
- return !!point;
689
- }
@@ -38,7 +38,11 @@ connection exists:
38
38
  register a `kind: "oauth"` entry so the shared UI renders Connect and the
39
39
  runtime owns status, refresh, and reauthorization.
40
40
  - **Deploy- or app-level configuration** - use deployment/runtime
41
- configuration, not a per-user secret row.
41
+ configuration, not a per-user secret row. For a non-secret public setting
42
+ already represented by `AgentNativeConfig`, put the default in
43
+ `agent-native.config.ts` and use its `AGENT_NATIVE_CONFIG_<PATH>` alias only
44
+ for a deployment override. Never put a credential or provider key in that
45
+ public namespace.
42
46
  - **Account, customer, manager, or other non-secret identifier** - store it as
43
47
  scoped connection metadata or app data, not as a masked secret field.
44
48
 
@@ -55,7 +55,10 @@ Preview before `--apply`, commit `agent-native.ejections.json`, and never edit
55
55
  ZIPs, screenshots, thumbnails, session replay chunks) in configured file/blob
56
56
  storage and persist only URLs, ids, or handles.
57
57
  - Store shared runtime configuration in the workspace root `.env`; use
58
- `apps/<app>/.env` only for app-specific overrides. Never hardcode API keys,
58
+ `apps/<app>/.env` only for app-specific overrides. For public app behavior,
59
+ prefer the default in `agent-native.config.ts` and use the deterministic
60
+ `AGENT_NATIVE_CONFIG_<UPPER_SNAKE_PATH>` alias only when a non-secret value
61
+ needs a deployment override. Never hardcode API keys,
59
62
  tokens, webhook URLs, signing secrets, private Builder/internal data, customer
60
63
  data, or credential-looking literals in source, docs, prompts, fixtures,
61
64
  application state, action responses, or generated app content. Use
@@ -68,7 +68,6 @@ import {
68
68
  IconChartBar,
69
69
  IconCheck,
70
70
  IconChevronDown,
71
- IconCircleDashed,
72
71
  IconClock,
73
72
  IconDatabase,
74
73
  IconEdit,
@@ -473,13 +472,6 @@ function statusLabel(t: Translate, status: WorkspaceConnectionStatus): string {
473
472
  return t(`integrations.status.${status}`);
474
473
  }
475
474
 
476
- function readinessLabel(
477
- t: Translate,
478
- status: WorkspaceConnectionProviderReadinessStatus,
479
- ): string {
480
- return t(`integrations.readiness.${status}`);
481
- }
482
-
483
475
  function scopeLabel(
484
476
  t: Translate,
485
477
  scope: WorkspaceConnectionCredentialRef["scope"],
@@ -742,32 +734,6 @@ function nextAllowedApps(
742
734
  return current.filter((id) => id !== appId);
743
735
  }
744
736
 
745
- function summarizeGrant(
746
- connection: WorkspaceConnection,
747
- grantApps: GrantApp[],
748
- grants: WorkspaceConnectionsResponse["grants"],
749
- t: Translate,
750
- ) {
751
- if (connection.allowedApps.length === 0) return t("integrations.allApps");
752
- const grantedAppIds = Array.from(
753
- new Set([
754
- ...connection.allowedApps,
755
- ...grants
756
- .filter((grant) => grant.connectionId === connection.id)
757
- .map((grant) => grant.appId)
758
- .filter((appId) => appId !== "*"),
759
- ]),
760
- );
761
- const labels = grantedAppIds
762
- .map((appId) => grantApps.find((app) => app.id === appId)?.label ?? appId)
763
- .slice(0, 3);
764
- const suffix =
765
- grantedAppIds.length > labels.length
766
- ? ` +${grantedAppIds.length - labels.length}`
767
- : "";
768
- return `${labels.join(", ")}${suffix}`;
769
- }
770
-
771
737
  function summarizeUserAccess(
772
738
  connection: WorkspaceConnection,
773
739
  t: Translate,
@@ -800,21 +766,6 @@ function summarizeUserAccess(
800
766
  return `${labels.join(", ")}${suffix}`;
801
767
  }
802
768
 
803
- function summarizeAppList(
804
- appIds: string[],
805
- grantApps: GrantApp[],
806
- t: Translate,
807
- ): string {
808
- const labels = appIds
809
- .map((appId) => grantApps.find((app) => app.id === appId)?.label ?? appId)
810
- .slice(0, 3);
811
- const suffix =
812
- appIds.length > labels.length ? ` +${appIds.length - labels.length}` : "";
813
- return labels.length > 0
814
- ? `${labels.join(", ")}${suffix}`
815
- : t("integrations.noApps");
816
- }
817
-
818
769
  function startWorkspaceProviderOAuth(provider: WorkspaceConnectionProvider) {
819
770
  const returnPath = `${window.location.pathname}${window.location.search}`;
820
771
  if (provider.id === "slack") {
@@ -3516,66 +3467,6 @@ export default function WorkspaceIntegrationsRoute() {
3516
3467
  );
3517
3468
  }
3518
3469
 
3519
- function IntegrationOnboarding() {
3520
- const t = useT();
3521
- const steps: Array<{
3522
- icon: IconComponent;
3523
- title: string;
3524
- detail: string;
3525
- }> = [
3526
- {
3527
- icon: IconPlugConnected,
3528
- title: t("integrations.onboardingConnectTitle"),
3529
- detail: t("integrations.onboardingConnectDetail"),
3530
- },
3531
- {
3532
- icon: IconShieldCheck,
3533
- title: t("integrations.onboardingGrantTitle"),
3534
- detail: t("integrations.onboardingGrantDetail"),
3535
- },
3536
- {
3537
- icon: IconDatabase,
3538
- title: t("integrations.onboardingLocalTitle"),
3539
- detail: t("integrations.onboardingLocalDetail"),
3540
- },
3541
- ];
3542
-
3543
- return (
3544
- <section className="rounded-lg bg-muted/30 px-4 py-3">
3545
- <div className="dispatch-onboarding-grid grid gap-3">
3546
- <div>
3547
- <h2 className="text-sm font-semibold text-foreground">
3548
- {t("integrations.onboardingTitle")}
3549
- </h2>
3550
- <p className="mt-1 text-sm leading-5 text-muted-foreground">
3551
- {t("integrations.onboardingDescription")}
3552
- </p>
3553
- </div>
3554
- <div className="dispatch-onboarding-steps-grid grid gap-2">
3555
- {steps.map((step) => {
3556
- const Icon = step.icon;
3557
- return (
3558
- <div key={step.title} className="flex min-w-0 gap-2">
3559
- <div className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md bg-muted/50">
3560
- <Icon size={14} className="text-muted-foreground" />
3561
- </div>
3562
- <div className="min-w-0">
3563
- <div className="text-xs font-medium text-foreground">
3564
- {step.title}
3565
- </div>
3566
- <div className="mt-0.5 text-xs leading-5 text-muted-foreground">
3567
- {step.detail}
3568
- </div>
3569
- </div>
3570
- </div>
3571
- );
3572
- })}
3573
- </div>
3574
- </div>
3575
- </section>
3576
- );
3577
- }
3578
-
3579
3470
  function SummaryCard({
3580
3471
  icon: Icon,
3581
3472
  label,
@@ -38,7 +38,11 @@ connection exists:
38
38
  register a `kind: "oauth"` entry so the shared UI renders Connect and the
39
39
  runtime owns status, refresh, and reauthorization.
40
40
  - **Deploy- or app-level configuration** - use deployment/runtime
41
- configuration, not a per-user secret row.
41
+ configuration, not a per-user secret row. For a non-secret public setting
42
+ already represented by `AgentNativeConfig`, put the default in
43
+ `agent-native.config.ts` and use its `AGENT_NATIVE_CONFIG_<PATH>` alias only
44
+ for a deployment override. Never put a credential or provider key in that
45
+ public namespace.
42
46
  - **Account, customer, manager, or other non-secret identifier** - store it as
43
47
  scoped connection metadata or app data, not as a masked secret field.
44
48
 
@@ -55,7 +55,10 @@ Preview before `--apply`, commit `agent-native.ejections.json`, and never edit
55
55
  ZIPs, screenshots, thumbnails, session replay chunks) in configured file/blob
56
56
  storage and persist only URLs, ids, or handles.
57
57
  - Store shared runtime configuration in the workspace root `.env`; use
58
- `apps/<app>/.env` only for app-specific overrides. Never hardcode API keys,
58
+ `apps/<app>/.env` only for app-specific overrides. For public app behavior,
59
+ prefer the default in `agent-native.config.ts` and use the deterministic
60
+ `AGENT_NATIVE_CONFIG_<UPPER_SNAKE_PATH>` alias only when a non-secret value
61
+ needs a deployment override. Never hardcode API keys,
59
62
  tokens, webhook URLs, signing secrets, private Builder/internal data, customer
60
63
  data, or credential-looking literals in source, docs, prompts, fixtures,
61
64
  application state, action responses, or generated app content. Use
@@ -14,7 +14,7 @@
14
14
  */
15
15
 
16
16
  import { defineAction } from "@agent-native/core/action";
17
- import { writeAppState } from "@agent-native/core/application-state";
17
+ import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
18
18
  import { z } from "zod";
19
19
 
20
20
  export default defineAction({
@@ -35,7 +35,7 @@ export default defineAction({
35
35
  if (args.path) nav.path = args.path;
36
36
  if (args.threadId) nav.threadId = args.threadId;
37
37
  nav._writeId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
38
- await writeAppState("navigate", nav);
38
+ await writeAppStateForCurrentTab("navigate", nav);
39
39
  return `Navigating to ${args.view || args.path}`;
40
40
  },
41
41
  });
@@ -695,19 +695,6 @@ function formatAuditLabel(value: string): string {
695
695
  .replace(/\b\w/g, (letter) => letter.toUpperCase());
696
696
  }
697
697
 
698
- function formatAuditValue(value: unknown): string {
699
- if (value === null || value === undefined) return "-";
700
- if (typeof value === "string") return value;
701
- if (typeof value === "number" || typeof value === "boolean") {
702
- return String(value);
703
- }
704
- try {
705
- return JSON.stringify(value);
706
- } catch {
707
- return "unreadable";
708
- }
709
- }
710
-
711
698
  function truncateAuditText(value: string, maxLength: number): string {
712
699
  return value.length > maxLength ? `${value.slice(0, maxLength - 1)}…` : value;
713
700
  }
@@ -24,7 +24,6 @@ import { FactoryAgentsView } from "@/components/factory/FactoryAgentsView";
24
24
  import { FactoryAuditView } from "@/components/factory/FactoryAuditView";
25
25
  import {
26
26
  FactoryCanvas,
27
- type FactoryCanvasEdge,
28
27
  type FactoryCanvasGraph,
29
28
  type FactoryCanvasNode,
30
29
  } from "@/components/factory/FactoryCanvas";
@@ -63,7 +63,6 @@ export async function readFactoryDefinition(orgId: string, factoryId: string) {
63
63
  export async function readFactoryMetrics(
64
64
  orgId: string,
65
65
  ): Promise<FactoryMetricSummary> {
66
- const db = getDb();
67
66
  const [
68
67
  totalItems,
69
68
  slackItems,