@agent-native/core 0.80.0 → 0.80.1

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 (220) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +8 -0
  3. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
  5. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
  7. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
  9. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
  11. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  12. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
  13. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  14. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
  15. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  16. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
  17. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  18. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
  19. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  20. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
  21. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  22. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
  23. package/corpus/core/docs/content/skills-guide.mdx +5 -0
  24. package/corpus/core/docs/content/template-design.mdx +14 -1
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/application-state/handlers.ts +1 -1
  27. package/corpus/core/src/brand-kit/index.ts +1 -1
  28. package/corpus/core/src/cli/skills.ts +18 -0
  29. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  30. package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
  31. package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
  32. package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
  33. package/corpus/core/src/extensions/path.ts +1 -4
  34. package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
  35. package/corpus/core/src/mcp/build-server.ts +1 -1
  36. package/corpus/core/src/org/handlers.ts +5 -5
  37. package/corpus/core/src/org/plugin.ts +2 -2
  38. package/corpus/core/src/scripts/call-agent.ts +1 -1
  39. package/corpus/core/src/scripts/db/safety.ts +1 -1
  40. package/corpus/core/src/server/auth.ts +3 -0
  41. package/corpus/core/src/server/builder-design-systems.ts +308 -0
  42. package/corpus/core/src/server/core-routes-plugin.ts +29 -3
  43. package/corpus/core/src/server/index.ts +8 -0
  44. package/corpus/core/src/styles/agent-native.css +14 -7
  45. package/corpus/core/src/usage/store.ts +2 -2
  46. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
  47. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
  48. package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
  49. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
  50. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
  51. package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
  52. package/corpus/templates/analytics/app/i18n-data.ts +404 -0
  53. package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
  54. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
  55. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
  56. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
  57. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
  58. package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
  59. package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
  61. package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
  62. package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
  63. package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
  65. package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
  66. package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
  67. package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
  68. package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
  69. package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
  70. package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
  71. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
  72. package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
  73. package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
  74. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
  75. package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
  76. package/corpus/templates/content/shared/document-export.ts +1 -1
  77. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
  78. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  79. package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
  80. package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
  81. package/corpus/templates/design/actions/update-file.ts +9 -2
  82. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
  83. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
  84. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
  85. package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
  86. package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
  87. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
  88. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
  89. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
  90. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
  91. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
  92. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
  93. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
  94. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
  95. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
  96. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
  97. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
  98. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
  99. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
  100. package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
  101. package/corpus/templates/design/app/global.css +29 -3
  102. package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
  103. package/corpus/templates/design/app/i18n-data.ts +239 -1
  104. package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
  105. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
  106. package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
  107. package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
  108. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
  109. package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
  110. package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
  111. package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
  112. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
  113. package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
  114. package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
  115. package/corpus/templates/design/shared/code-layer.ts +209 -9
  116. package/corpus/templates/design/shared/html-content.ts +21 -0
  117. package/corpus/templates/design/shared/pen-path.ts +207 -0
  118. package/corpus/templates/mail/app/lib/utils.ts +2 -2
  119. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
  120. package/corpus/templates/slides/AGENTS.md +3 -2
  121. package/corpus/templates/slides/actions/import-file.ts +26 -17
  122. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
  123. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
  124. package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
  125. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
  126. package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
  127. package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
  128. package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
  129. package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
  130. package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
  131. package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
  132. package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
  133. package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
  134. package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
  135. package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
  136. package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
  137. package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
  138. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  139. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
  140. package/dist/application-state/handlers.js +1 -1
  141. package/dist/application-state/handlers.js.map +1 -1
  142. package/dist/brand-kit/index.d.ts +1 -1
  143. package/dist/brand-kit/index.js +1 -1
  144. package/dist/brand-kit/index.js.map +1 -1
  145. package/dist/cli/skills.d.ts.map +1 -1
  146. package/dist/cli/skills.js +18 -0
  147. package/dist/cli/skills.js.map +1 -1
  148. package/dist/client/AgentPanel.js +1 -1
  149. package/dist/client/AgentPanel.js.map +1 -1
  150. package/dist/client/MultiTabAssistantChat.js +1 -1
  151. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  152. package/dist/client/extensions/portable-extension.d.ts +1 -1
  153. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  154. package/dist/client/extensions/portable-extension.js.map +1 -1
  155. package/dist/code-agents/transcript-normalizer.js +1 -1
  156. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  157. package/dist/collab/awareness.d.ts +2 -2
  158. package/dist/collab/awareness.d.ts.map +1 -1
  159. package/dist/extensions/path.d.ts +1 -1
  160. package/dist/extensions/path.d.ts.map +1 -1
  161. package/dist/extensions/path.js.map +1 -1
  162. package/dist/integrations/a2a-continuation-processor.js +1 -1
  163. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  164. package/dist/mcp/build-server.d.ts +1 -1
  165. package/dist/mcp/build-server.d.ts.map +1 -1
  166. package/dist/mcp/build-server.js.map +1 -1
  167. package/dist/observability/routes.d.ts +3 -3
  168. package/dist/org/handlers.js +5 -5
  169. package/dist/org/handlers.js.map +1 -1
  170. package/dist/org/plugin.js +2 -2
  171. package/dist/org/plugin.js.map +1 -1
  172. package/dist/progress/routes.d.ts +1 -1
  173. package/dist/scripts/call-agent.js +1 -1
  174. package/dist/scripts/call-agent.js.map +1 -1
  175. package/dist/scripts/db/safety.js +1 -1
  176. package/dist/scripts/db/safety.js.map +1 -1
  177. package/dist/server/auth.d.ts.map +1 -1
  178. package/dist/server/auth.js +3 -0
  179. package/dist/server/auth.js.map +1 -1
  180. package/dist/server/builder-design-systems.d.ts +28 -0
  181. package/dist/server/builder-design-systems.d.ts.map +1 -0
  182. package/dist/server/builder-design-systems.js +217 -0
  183. package/dist/server/builder-design-systems.js.map +1 -0
  184. package/dist/server/core-routes-plugin.d.ts +1 -0
  185. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  186. package/dist/server/core-routes-plugin.js +20 -3
  187. package/dist/server/core-routes-plugin.js.map +1 -1
  188. package/dist/server/index.d.ts +1 -0
  189. package/dist/server/index.d.ts.map +1 -1
  190. package/dist/server/index.js +1 -0
  191. package/dist/server/index.js.map +1 -1
  192. package/dist/server/transcribe-voice.d.ts +1 -1
  193. package/dist/styles/agent-native.css +14 -7
  194. package/dist/usage/store.js +2 -2
  195. package/dist/usage/store.js.map +1 -1
  196. package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  197. package/docs/content/locales/ar-SA/template-design.mdx +14 -1
  198. package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  199. package/docs/content/locales/de-DE/template-design.mdx +15 -1
  200. package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  201. package/docs/content/locales/es-ES/template-design.mdx +15 -1
  202. package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  203. package/docs/content/locales/fr-FR/template-design.mdx +15 -1
  204. package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  205. package/docs/content/locales/hi-IN/template-design.mdx +15 -1
  206. package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  207. package/docs/content/locales/ja-JP/template-design.mdx +16 -1
  208. package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  209. package/docs/content/locales/ko-KR/template-design.mdx +14 -1
  210. package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  211. package/docs/content/locales/pt-BR/template-design.mdx +14 -1
  212. package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  213. package/docs/content/locales/zh-CN/template-design.mdx +14 -1
  214. package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  215. package/docs/content/locales/zh-TW/template-design.mdx +14 -1
  216. package/docs/content/skills-guide.mdx +5 -0
  217. package/docs/content/template-design.mdx +14 -1
  218. package/package.json +1 -1
  219. package/corpus/templates/design/actions/import-figma-file.ts +0 -166
  220. package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
@@ -153,6 +153,8 @@ export interface LayoutContext {
153
153
  width?: string;
154
154
  height?: string;
155
155
  flexDirection?: string;
156
+ alignItems?: string;
157
+ justifyContent?: string;
156
158
  gap?: string;
157
159
  padding?: string;
158
160
  parentDisplay?: string;
@@ -243,6 +245,15 @@ export interface CodeLayerTreeNode {
243
245
  tag: string;
244
246
  selector: string;
245
247
  detail: string;
248
+ layout?: Pick<
249
+ LayoutContext,
250
+ | "display"
251
+ | "flexDirection"
252
+ | "alignItems"
253
+ | "justifyContent"
254
+ | "isFlexContainer"
255
+ | "isGridContainer"
256
+ >;
246
257
  badge?: string;
247
258
  renamable: boolean;
248
259
  children: CodeLayerTreeNode[];
@@ -544,6 +555,8 @@ const NON_VISUAL_TAGS = new Set([
544
555
  "noscript",
545
556
  ]);
546
557
 
558
+ const RAW_TEXT_VISUAL_TAGS = new Set(["textarea"]);
559
+
547
560
  const DATA_SELECTOR_PRIORITY = [
548
561
  "data-agent-native-node-id",
549
562
  "data-code-layer-id",
@@ -967,6 +980,21 @@ function findHtmlTagEnd(html: string, start: number): number {
967
980
  return html.length;
968
981
  }
969
982
 
983
+ function findClosingTag(
984
+ html: string,
985
+ tag: string,
986
+ from: number,
987
+ ): { closeStart: number; closeEnd: number } | null {
988
+ const closeRe = new RegExp(`<\\/\\s*${tag}\\s*>`, "gi");
989
+ closeRe.lastIndex = from;
990
+ const match = closeRe.exec(html);
991
+ if (!match) return null;
992
+ return {
993
+ closeStart: match.index,
994
+ closeEnd: match.index + match[0].length,
995
+ };
996
+ }
997
+
970
998
  function parseHtmlElements(html: string): ParsedElement[] {
971
999
  const elements: ParsedElement[] = [];
972
1000
  const stack: number[] = [];
@@ -1003,15 +1031,36 @@ function parseHtmlElements(html: string): ParsedElement[] {
1003
1031
  const nthOfType = (sameTypeCounts.get(parentKey) ?? 0) + 1;
1004
1032
  sameTypeCounts.set(parentKey, nthOfType);
1005
1033
  const selfClosing = raw.endsWith("/>") || VOID_TAGS.has(tag);
1034
+
1035
+ if (NON_VISUAL_TAGS.has(tag)) {
1036
+ if (!selfClosing) {
1037
+ const close = findClosingTag(html, tag, match.index + raw.length);
1038
+ tagRe.lastIndex = close ? close.closeEnd : html.length;
1039
+ }
1040
+ continue;
1041
+ }
1042
+
1006
1043
  const index = elements.length;
1044
+ const rawTextClose =
1045
+ !selfClosing && RAW_TEXT_VISUAL_TAGS.has(tag)
1046
+ ? findClosingTag(html, tag, match.index + raw.length)
1047
+ : null;
1007
1048
  const element: ParsedElement = {
1008
1049
  index,
1009
1050
  tag,
1010
1051
  start: match.index,
1011
1052
  openEnd: match.index + raw.length,
1012
- end: selfClosing ? match.index + raw.length : html.length,
1053
+ end: rawTextClose
1054
+ ? rawTextClose.closeEnd
1055
+ : selfClosing
1056
+ ? match.index + raw.length
1057
+ : html.length,
1013
1058
  contentStart: match.index + raw.length,
1014
- contentEnd: selfClosing ? match.index + raw.length : html.length,
1059
+ contentEnd: rawTextClose
1060
+ ? rawTextClose.closeStart
1061
+ : selfClosing
1062
+ ? match.index + raw.length
1063
+ : html.length,
1015
1064
  selfClosing,
1016
1065
  attributes: parseAttributes(raw, match.index),
1017
1066
  parentIndex,
@@ -1026,6 +1075,12 @@ function parseHtmlElements(html: string): ParsedElement[] {
1026
1075
  if (parentIndex !== undefined) {
1027
1076
  elements[parentIndex]?.childIndexes.push(index);
1028
1077
  }
1078
+ if (rawTextClose) {
1079
+ element.closeStart = rawTextClose.closeStart;
1080
+ element.closeEnd = rawTextClose.closeEnd;
1081
+ tagRe.lastIndex = rawTextClose.closeEnd;
1082
+ continue;
1083
+ }
1029
1084
  if (!selfClosing) stack.push(index);
1030
1085
  }
1031
1086
 
@@ -1284,6 +1339,34 @@ function layoutFor(
1284
1339
  : classes.has("flex-row")
1285
1340
  ? "row"
1286
1341
  : undefined);
1342
+ const alignItems =
1343
+ style["align-items"] ??
1344
+ (classes.has("items-start")
1345
+ ? "flex-start"
1346
+ : classes.has("items-center")
1347
+ ? "center"
1348
+ : classes.has("items-end")
1349
+ ? "flex-end"
1350
+ : classes.has("items-stretch")
1351
+ ? "stretch"
1352
+ : classes.has("items-baseline")
1353
+ ? "baseline"
1354
+ : undefined);
1355
+ const justifyContent =
1356
+ style["justify-content"] ??
1357
+ (classes.has("justify-start")
1358
+ ? "flex-start"
1359
+ : classes.has("justify-center")
1360
+ ? "center"
1361
+ : classes.has("justify-end")
1362
+ ? "flex-end"
1363
+ : classes.has("justify-between")
1364
+ ? "space-between"
1365
+ : classes.has("justify-around")
1366
+ ? "space-around"
1367
+ : classes.has("justify-evenly")
1368
+ ? "space-evenly"
1369
+ : undefined);
1287
1370
  const parentFlexDirection =
1288
1371
  parentStyle?.["flex-direction"] ??
1289
1372
  (parentClasses?.has("flex-col")
@@ -1300,6 +1383,8 @@ function layoutFor(
1300
1383
  width: style.width,
1301
1384
  height: style.height,
1302
1385
  flexDirection,
1386
+ alignItems,
1387
+ justifyContent,
1303
1388
  gap: style.gap,
1304
1389
  padding: style.padding,
1305
1390
  parentDisplay,
@@ -1620,6 +1705,14 @@ export function buildCodeLayerTree(
1620
1705
  tag: node.tag,
1621
1706
  selector: node.selector,
1622
1707
  detail: `<${node.tag}>`,
1708
+ layout: {
1709
+ display: node.layout.display,
1710
+ flexDirection: node.layout.flexDirection,
1711
+ alignItems: node.layout.alignItems,
1712
+ justifyContent: node.layout.justifyContent,
1713
+ isFlexContainer: node.layout.isFlexContainer,
1714
+ isGridContainer: node.layout.isGridContainer,
1715
+ },
1623
1716
  badge:
1624
1717
  node.layerNameSource === "attribute" && node.layerNameAttribute
1625
1718
  ? node.layerNameAttribute
@@ -1719,6 +1812,87 @@ function simpleSelectorMatches(node: CodeLayerNode, selector: string): boolean {
1719
1812
  return node.tag === selector.toLowerCase();
1720
1813
  }
1721
1814
 
1815
+ function unescapeCssAttributeValue(value: string): string {
1816
+ return value.replace(/\\"/g, '"').replace(/\\\\/g, "\\");
1817
+ }
1818
+
1819
+ function simpleSelectorMatchesElement(
1820
+ element: ParsedElement,
1821
+ selector: string,
1822
+ ): boolean {
1823
+ let remaining = selector.trim();
1824
+ if (!remaining) return false;
1825
+
1826
+ const nthMatch = remaining.match(/:nth-of-type\((\d+)\)$/);
1827
+ if (nthMatch?.[1]) {
1828
+ if (element.nthOfType !== Number(nthMatch[1])) return false;
1829
+ remaining = remaining.slice(0, nthMatch.index).trim();
1830
+ }
1831
+
1832
+ const tagMatch = remaining.match(/^([A-Za-z][A-Za-z0-9:-]*)/);
1833
+ if (tagMatch?.[1] && element.tag !== tagMatch[1].toLowerCase()) {
1834
+ return false;
1835
+ }
1836
+
1837
+ const idMatch = remaining.match(/#([A-Za-z_][A-Za-z0-9_-]*)/);
1838
+ if (idMatch?.[1] && attributeValue(element, "id") !== idMatch[1]) {
1839
+ return false;
1840
+ }
1841
+
1842
+ const attributes = Array.from(
1843
+ remaining.matchAll(
1844
+ /\[([A-Za-z_][A-Za-z0-9_:.-]*)=(?:"((?:\\"|[^"])*)"|'((?:\\'|[^'])*)')\]/g,
1845
+ ),
1846
+ );
1847
+ for (const match of attributes) {
1848
+ const name = match[1];
1849
+ if (!name) return false;
1850
+ const expected = unescapeCssAttributeValue(match[2] ?? match[3] ?? "");
1851
+ if (attributeValue(element, name) !== expected) return false;
1852
+ }
1853
+
1854
+ const classes = classList(element);
1855
+ const requiredClasses = Array.from(
1856
+ remaining.matchAll(/\.([A-Za-z_][A-Za-z0-9_-]*)/g),
1857
+ (match) => match[1],
1858
+ ).filter((value): value is string => Boolean(value));
1859
+ if (requiredClasses.some((className) => !classes.includes(className))) {
1860
+ return false;
1861
+ }
1862
+
1863
+ return Boolean(
1864
+ tagMatch ||
1865
+ idMatch ||
1866
+ attributes.length > 0 ||
1867
+ requiredClasses.length > 0 ||
1868
+ nthMatch,
1869
+ );
1870
+ }
1871
+
1872
+ function selectorPathMatchesElement(
1873
+ element: ParsedElement | undefined,
1874
+ selector: string,
1875
+ elementByIndex: Map<number, ParsedElement>,
1876
+ ): boolean {
1877
+ const parts = normalizeSelectorForMatch(selector)
1878
+ .split(" > ")
1879
+ .map((part) => part.trim())
1880
+ .filter(Boolean);
1881
+ if (parts.length <= 1) return false;
1882
+
1883
+ let current: ParsedElement | undefined = element;
1884
+ for (let index = parts.length - 1; index >= 0; index -= 1) {
1885
+ const selectorPart = parts[index];
1886
+ if (!current || !selectorPart) return false;
1887
+ if (!simpleSelectorMatchesElement(current, selectorPart)) return false;
1888
+ current =
1889
+ current.parentIndex === undefined
1890
+ ? undefined
1891
+ : elementByIndex.get(current.parentIndex);
1892
+ }
1893
+ return true;
1894
+ }
1895
+
1722
1896
  function nodeMatchesStableSourceId(
1723
1897
  node: CodeLayerNode,
1724
1898
  sourceId: string,
@@ -1731,7 +1905,12 @@ function nodeMatchesStableSourceId(
1731
1905
  return node.attributes.id === sourceId;
1732
1906
  }
1733
1907
 
1734
- function selectorMatches(node: CodeLayerNode, selector: string): boolean {
1908
+ function selectorMatches(
1909
+ node: CodeLayerNode,
1910
+ selector: string,
1911
+ element: ParsedElement | undefined,
1912
+ elementByIndex: Map<number, ParsedElement>,
1913
+ ): boolean {
1735
1914
  const normalizedSelector = normalizeSelectorForMatch(selector);
1736
1915
  const normalizedNodeSelectors = [
1737
1916
  node.selector,
@@ -1739,16 +1918,25 @@ function selectorMatches(node: CodeLayerNode, selector: string): boolean {
1739
1918
  ...node.selectors,
1740
1919
  ].map(normalizeSelectorForMatch);
1741
1920
  if (normalizedNodeSelectors.includes(normalizedSelector)) return true;
1921
+ const selectorHasDirectPath = normalizedSelector.includes(" > ");
1742
1922
  if (
1743
- normalizedSelector.includes(" > ") &&
1923
+ selectorHasDirectPath &&
1744
1924
  normalizedNodeSelectors.some(
1745
1925
  (candidate) =>
1746
1926
  candidate.endsWith(` > ${normalizedSelector}`) ||
1747
- normalizedSelector.endsWith(` > ${candidate}`),
1927
+ (candidate.includes(" > ") &&
1928
+ normalizedSelector.endsWith(` > ${candidate}`)),
1748
1929
  )
1749
1930
  ) {
1750
1931
  return true;
1751
1932
  }
1933
+ if (
1934
+ selectorHasDirectPath &&
1935
+ selectorPathMatchesElement(element, normalizedSelector, elementByIndex)
1936
+ ) {
1937
+ return true;
1938
+ }
1939
+ if (selectorHasDirectPath) return false;
1752
1940
  if (simpleSelectorMatches(node, normalizedSelector)) return true;
1753
1941
  const lastPart = lastSelectorPart(normalizedSelector);
1754
1942
  return (
@@ -1757,9 +1945,16 @@ function selectorMatches(node: CodeLayerNode, selector: string): boolean {
1757
1945
  }
1758
1946
 
1759
1947
  function resolveTarget(
1760
- projection: CodeLayerProjection,
1948
+ build: ProjectionBuild,
1761
1949
  target: EditIntentTarget,
1762
1950
  ): EditIntentResolution {
1951
+ const { projection, elementByNodeId } = build;
1952
+ const elementByIndex = new Map(
1953
+ Array.from(elementByNodeId.values()).map((element) => [
1954
+ element.index,
1955
+ element,
1956
+ ]),
1957
+ );
1763
1958
  if (target.nodeId) {
1764
1959
  const matches = projection.nodes.filter((candidate) =>
1765
1960
  nodeMatchesStableSourceId(candidate, target.nodeId ?? ""),
@@ -1790,7 +1985,12 @@ function resolveTarget(
1790
1985
  }
1791
1986
 
1792
1987
  const matches = projection.nodes.filter((node) =>
1793
- selectorMatches(node, target.selector ?? ""),
1988
+ selectorMatches(
1989
+ node,
1990
+ target.selector ?? "",
1991
+ elementByNodeId.get(node.id),
1992
+ elementByIndex,
1993
+ ),
1794
1994
  );
1795
1995
  if (matches.length === 1 && matches[0]) {
1796
1996
  return { status: "resolved", node: matches[0] };
@@ -2086,7 +2286,7 @@ export function applyVisualEdit(
2086
2286
  }
2087
2287
 
2088
2288
  const initial = buildProjection(html, source);
2089
- const resolution = resolveTarget(initial.projection, intent.target);
2289
+ const resolution = resolveTarget(initial, intent.target);
2090
2290
  if (resolution.status !== "resolved" || !resolution.node) {
2091
2291
  return {
2092
2292
  content: html,
@@ -2129,7 +2329,7 @@ export function applyVisualEdit(
2129
2329
  } else if (intent.kind === "textContent") {
2130
2330
  edit = applyTextEdit(html, element, intent);
2131
2331
  } else {
2132
- const anchorResolution = resolveTarget(initial.projection, intent.anchor);
2332
+ const anchorResolution = resolveTarget(initial, intent.anchor);
2133
2333
  if (anchorResolution.status !== "resolved" || !anchorResolution.node) {
2134
2334
  return {
2135
2335
  content: html,
@@ -0,0 +1,21 @@
1
+ export function isProbablyHtmlDocumentContent(content: string): boolean {
2
+ const trimmed = content.trimStart();
3
+ if (!trimmed) return true;
4
+ if (trimmed.startsWith("<")) return true;
5
+ return false;
6
+ }
7
+
8
+ export function shouldUseLiveFileContent({
9
+ liveContent,
10
+ storedContent,
11
+ fileType,
12
+ }: {
13
+ liveContent: string;
14
+ storedContent: string;
15
+ fileType: string;
16
+ }): boolean {
17
+ if (liveContent === storedContent) return true;
18
+ if (fileType.toLowerCase() !== "html") return true;
19
+ if (!isProbablyHtmlDocumentContent(storedContent)) return true;
20
+ return isProbablyHtmlDocumentContent(liveContent);
21
+ }
@@ -0,0 +1,207 @@
1
+ export interface PenPoint {
2
+ x: number;
3
+ y: number;
4
+ }
5
+
6
+ export interface PenNode {
7
+ point: PenPoint;
8
+ handleIn?: PenPoint;
9
+ handleOut?: PenPoint;
10
+ }
11
+
12
+ export interface PenPath {
13
+ nodes: PenNode[];
14
+ closed: boolean;
15
+ }
16
+
17
+ export interface PenGeometry {
18
+ x: number;
19
+ y: number;
20
+ width: number;
21
+ height: number;
22
+ }
23
+
24
+ const MIN_PATH_SIZE = 12;
25
+
26
+ export function createCornerNode(point: PenPoint): PenNode {
27
+ return { point: { ...point } };
28
+ }
29
+
30
+ export function createSmoothNode(anchor: PenPoint, handleOut: PenPoint) {
31
+ return {
32
+ point: { ...anchor },
33
+ handleIn: mirrorPoint(anchor, handleOut),
34
+ handleOut: { ...handleOut },
35
+ };
36
+ }
37
+
38
+ export function appendPenNode(path: PenPath | null, node: PenNode): PenPath {
39
+ return {
40
+ nodes: [...(path?.nodes ?? []), clonePenNode(node)],
41
+ closed: false,
42
+ };
43
+ }
44
+
45
+ export function clonePenPath(path: PenPath): PenPath {
46
+ return {
47
+ nodes: path.nodes.map(clonePenNode),
48
+ closed: path.closed,
49
+ };
50
+ }
51
+
52
+ export function closePenPath(path: PenPath): PenPath {
53
+ return {
54
+ nodes: path.nodes.map(clonePenNode),
55
+ closed: path.nodes.length > 1,
56
+ };
57
+ }
58
+
59
+ export function constrainPointTo45Degrees(
60
+ origin: PenPoint,
61
+ point: PenPoint,
62
+ ): PenPoint {
63
+ const dx = point.x - origin.x;
64
+ const dy = point.y - origin.y;
65
+ const distance = Math.hypot(dx, dy);
66
+ if (distance === 0) return { ...point };
67
+
68
+ const angle = Math.atan2(dy, dx);
69
+ const snappedAngle = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4);
70
+ return {
71
+ x: origin.x + Math.cos(snappedAngle) * distance,
72
+ y: origin.y + Math.sin(snappedAngle) * distance,
73
+ };
74
+ }
75
+
76
+ export function isPenCloseTarget(
77
+ path: PenPath | null,
78
+ point: PenPoint,
79
+ hitRadius: number,
80
+ ) {
81
+ const start = path?.nodes[0]?.point;
82
+ if (!start || (path?.nodes.length ?? 0) < 2) return false;
83
+ return Math.hypot(point.x - start.x, point.y - start.y) <= hitRadius;
84
+ }
85
+
86
+ export function getPenPathGeometry(path: PenPath): PenGeometry {
87
+ const points = path.nodes.flatMap((node) =>
88
+ [node.point, node.handleIn, node.handleOut].filter(isPenPoint),
89
+ );
90
+ if (points.length === 0) {
91
+ return { x: 0, y: 0, width: MIN_PATH_SIZE, height: MIN_PATH_SIZE };
92
+ }
93
+
94
+ const left = Math.min(...points.map((point) => point.x));
95
+ const top = Math.min(...points.map((point) => point.y));
96
+ const right = Math.max(...points.map((point) => point.x));
97
+ const bottom = Math.max(...points.map((point) => point.y));
98
+ return {
99
+ x: left,
100
+ y: top,
101
+ width: Math.max(MIN_PATH_SIZE, right - left),
102
+ height: Math.max(MIN_PATH_SIZE, bottom - top),
103
+ };
104
+ }
105
+
106
+ export function serializePenPath(path: PenPath): string {
107
+ const [first, ...rest] = path.nodes;
108
+ if (!first) return "";
109
+
110
+ const commands = [`M ${formatPoint(first.point)}`];
111
+ rest.forEach((node, index) => {
112
+ const previous = path.nodes[index];
113
+ commands.push(serializeSegment(previous, node));
114
+ });
115
+
116
+ if (path.closed && path.nodes.length > 1) {
117
+ commands.push(serializeSegment(path.nodes[path.nodes.length - 1], first));
118
+ commands.push("Z");
119
+ }
120
+
121
+ return commands.join(" ");
122
+ }
123
+
124
+ export function offsetPenPath(path: PenPath, offset: PenPoint): PenPath {
125
+ return transformPenPath(path, (point) => ({
126
+ x: point.x - offset.x,
127
+ y: point.y - offset.y,
128
+ }));
129
+ }
130
+
131
+ export function translatePenPath(
132
+ path: PenPath,
133
+ dx: number,
134
+ dy: number,
135
+ ): PenPath {
136
+ return transformPenPath(path, (point) => ({
137
+ x: point.x + dx,
138
+ y: point.y + dy,
139
+ }));
140
+ }
141
+
142
+ export function scalePenPathToGeometry(
143
+ path: PenPath,
144
+ origin: PenGeometry,
145
+ next: PenGeometry,
146
+ ): PenPath {
147
+ const scaleX = next.width / Math.max(1, origin.width);
148
+ const scaleY = next.height / Math.max(1, origin.height);
149
+ return transformPenPath(path, (point) => ({
150
+ x: next.x + (point.x - origin.x) * scaleX,
151
+ y: next.y + (point.y - origin.y) * scaleY,
152
+ }));
153
+ }
154
+
155
+ function serializeSegment(from: PenNode, to: PenNode) {
156
+ const c1 = from.handleOut ?? from.point;
157
+ const c2 = to.handleIn ?? to.point;
158
+ if (samePoint(c1, from.point) && samePoint(c2, to.point)) {
159
+ return `L ${formatPoint(to.point)}`;
160
+ }
161
+ return `C ${formatPoint(c1)} ${formatPoint(c2)} ${formatPoint(to.point)}`;
162
+ }
163
+
164
+ function transformPenPath(
165
+ path: PenPath,
166
+ transform: (point: PenPoint) => PenPoint,
167
+ ): PenPath {
168
+ return {
169
+ nodes: path.nodes.map((node) => ({
170
+ point: transform(node.point),
171
+ handleIn: node.handleIn ? transform(node.handleIn) : undefined,
172
+ handleOut: node.handleOut ? transform(node.handleOut) : undefined,
173
+ })),
174
+ closed: path.closed,
175
+ };
176
+ }
177
+
178
+ function clonePenNode(node: PenNode): PenNode {
179
+ return {
180
+ point: { ...node.point },
181
+ handleIn: node.handleIn ? { ...node.handleIn } : undefined,
182
+ handleOut: node.handleOut ? { ...node.handleOut } : undefined,
183
+ };
184
+ }
185
+
186
+ function mirrorPoint(anchor: PenPoint, point: PenPoint): PenPoint {
187
+ return {
188
+ x: anchor.x - (point.x - anchor.x),
189
+ y: anchor.y - (point.y - anchor.y),
190
+ };
191
+ }
192
+
193
+ function formatPoint(point: PenPoint) {
194
+ return `${roundCoord(point.x)} ${roundCoord(point.y)}`;
195
+ }
196
+
197
+ function roundCoord(value: number) {
198
+ return Math.round(value * 10) / 10;
199
+ }
200
+
201
+ function samePoint(a: PenPoint, b: PenPoint) {
202
+ return a.x === b.x && a.y === b.y;
203
+ }
204
+
205
+ function isPenPoint(point: PenPoint | undefined): point is PenPoint {
206
+ return !!point;
207
+ }
@@ -74,12 +74,12 @@ export function markdownToHtml(markdown: string): string {
74
74
  return `<h${level}>${renderInlineMarkdown(heading[2])}</h${level}>`;
75
75
  }
76
76
 
77
- if (/^(\-|\*|\+)\s+/m.test(block)) {
77
+ if (/^(-|\*|\+)\s+/m.test(block)) {
78
78
  const items = block
79
79
  .split("\n")
80
80
  .map((line) => line.trim())
81
81
  .filter(Boolean)
82
- .map((line) => line.replace(/^(\-|\*|\+)\s+/, ""))
82
+ .map((line) => line.replace(/^(-|\*|\+)\s+/, ""))
83
83
  .map((line) => `<li>${renderInlineMarkdown(line)}</li>`)
84
84
  .join("");
85
85
  return `<ul>${items}</ul>`;
@@ -24,22 +24,21 @@ Design systems are stored in the `design_systems` SQL table. Each has a `data` c
24
24
 
25
25
  ### Source: Figma `.fig` file
26
26
 
27
- When the user uploads a raw Figma local copy (`.fig`), parse it in-process with
28
- `import-file` instead of treating it like a document:
27
+ When the user uploads a raw Figma local copy (`.fig`), start Builder
28
+ design-system indexing with `import-file` instead of treating it like a
29
+ document:
29
30
 
30
31
  ```bash
31
32
  pnpm action import-file --filePath "data/uploads/brand.fig" --format fig
32
33
  ```
33
34
 
34
- The action returns `designSystem`, `customInstructions`, and `preview`. Review
35
- the result, then call `create-design-system` with:
35
+ The action requires Builder to be connected and returns Builder `projectId`,
36
+ `jobId`, `designSystemId`, and `builderUrl`. Builder is the source of truth for
37
+ the indexed brand kit, generated docs, and usage guidance.
36
38
 
37
- - `title`: the returned title or a user-approved name
38
- - `data`: `JSON.stringify(designSystem)`
39
- - `customInstructions`: the returned `customInstructions`
40
-
41
- Do not call `import-document` for `.fig` files; it only handles metadata and
42
- will miss the real design tokens.
39
+ Do not call `create-design-system` locally from `.fig` uploads. Do not call
40
+ `import-document` for `.fig` files; it only handles metadata and will miss the
41
+ Builder indexing flow.
43
42
 
44
43
  ## Applying to Slides
45
44
 
@@ -19,8 +19,9 @@ Detailed deck, slide-editing, image, design-system, and export workflows live in
19
19
  - Preserve deck structure and visual consistency. Prefer focused slide edits over
20
20
  regenerating whole decks unless requested.
21
21
  - Follow linked design-system tokens and custom instructions.
22
- - For raw Figma `.fig` uploads, call `import-file --format fig`, then create a
23
- design system from the returned `designSystem` and `customInstructions`.
22
+ - For raw Figma `.fig` uploads, call `import-file --format fig` to start
23
+ Builder design-system indexing. Do not create a local design system from raw
24
+ `.fig` output.
24
25
  - Treat import/export actions as shortcuts, not capability limits. When the
25
26
  exact Google Drive endpoint, file metadata field, export format, pagination
26
27
  mode, or API version matters, use `provider-api-catalog`,