@agent-native/core 0.80.10 → 0.81.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 (267) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +53 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +108 -15
  5. package/corpus/core/src/cli/design-connect.ts +419 -19
  6. package/corpus/core/src/client/AssistantChat.tsx +123 -26
  7. package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
  8. package/corpus/core/src/client/chat/index.ts +1 -0
  9. package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
  10. package/corpus/core/src/client/chat-view-transition.ts +33 -2
  11. package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
  12. package/corpus/core/src/client/index.ts +1 -0
  13. package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
  14. package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
  15. package/corpus/core/src/client/sse-event-processor.ts +9 -0
  16. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
  17. package/corpus/core/src/db/client.ts +29 -2
  18. package/corpus/core/src/provider-api/index.ts +29 -0
  19. package/corpus/core/src/server/core-routes-plugin.ts +58 -20
  20. package/corpus/core/src/styles/agent-native.css +54 -0
  21. package/corpus/core/src/usage/store.ts +2 -2
  22. package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
  23. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
  24. package/corpus/templates/analytics/app/global.css +8 -0
  25. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
  26. package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
  27. package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
  28. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
  29. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
  30. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
  31. package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
  36. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
  37. package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
  38. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
  39. package/corpus/templates/assets/AGENTS.md +4 -0
  40. package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
  41. package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
  42. package/corpus/templates/assets/actions/generate-image.ts +118 -67
  43. package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
  44. package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
  45. package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
  46. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
  47. package/corpus/templates/assets/app/global.css +15 -0
  48. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
  49. package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
  50. package/corpus/templates/assets/app/i18n-data.ts +61 -0
  51. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  52. package/corpus/templates/assets/app/routes/library.tsx +233 -52
  53. package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
  54. package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
  55. package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
  56. package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
  57. package/corpus/templates/assets/server/lib/generation.ts +135 -11
  58. package/corpus/templates/assets/shared/api.ts +4 -0
  59. package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
  60. package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
  61. package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
  62. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
  63. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
  64. package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
  65. package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
  66. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  67. package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
  68. package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
  69. package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
  70. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
  71. package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
  72. package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
  73. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
  74. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
  75. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
  76. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
  77. package/corpus/templates/design/AGENTS.md +27 -0
  78. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
  79. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
  80. package/corpus/templates/design/actions/apply-component-prop-edit.ts +76 -25
  81. package/corpus/templates/design/actions/apply-design-state.ts +17 -4
  82. package/corpus/templates/design/actions/apply-motion-edit.ts +76 -115
  83. package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
  84. package/corpus/templates/design/actions/capture-design-state.ts +27 -4
  85. package/corpus/templates/design/actions/connect-localhost.ts +59 -22
  86. package/corpus/templates/design/actions/delete-design.ts +20 -0
  87. package/corpus/templates/design/actions/export-zip.ts +23 -8
  88. package/corpus/templates/design/actions/generate-design.ts +53 -1
  89. package/corpus/templates/design/actions/get-component-details.ts +21 -26
  90. package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
  91. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
  92. package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
  93. package/corpus/templates/design/actions/index-design-tokens.ts +18 -5
  94. package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
  95. package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
  96. package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
  97. package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
  98. package/corpus/templates/design/actions/list-design-states.ts +19 -1
  99. package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
  100. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
  101. package/corpus/templates/design/actions/navigate.ts +18 -3
  102. package/corpus/templates/design/actions/open-component-source.ts +7 -2
  103. package/corpus/templates/design/actions/preview-component-prop-edit.ts +32 -26
  104. package/corpus/templates/design/actions/preview-shader-fill.ts +9 -11
  105. package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
  106. package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
  107. package/corpus/templates/design/actions/provider-api-request.ts +3 -3
  108. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
  109. package/corpus/templates/design/actions/run-design-audit.ts +74 -10
  110. package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
  111. package/corpus/templates/design/actions/view-screen.ts +1 -1
  112. package/corpus/templates/design/actions/write-local-file.ts +213 -0
  113. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +244 -3197
  114. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
  115. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +795 -289
  116. package/corpus/templates/design/app/components/design/EditPanel.tsx +635 -234
  117. package/corpus/templates/design/app/components/design/LayersPanel.tsx +124 -32
  118. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
  119. package/corpus/templates/design/app/components/design/MotionDock.tsx +244 -266
  120. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +491 -56
  121. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +55 -67
  122. package/corpus/templates/design/app/components/design/StatesPanel.tsx +38 -63
  123. package/corpus/templates/design/app/components/design/TokensPanel.tsx +264 -8
  124. package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
  125. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
  126. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
  127. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
  128. package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
  129. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
  130. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
  131. package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
  132. package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
  133. package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
  134. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
  135. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
  136. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
  137. package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
  138. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  139. package/corpus/templates/design/app/global.css +7 -0
  140. package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
  141. package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
  142. package/corpus/templates/design/app/i18n-data.ts +369 -0
  143. package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
  144. package/corpus/templates/design/app/pages/DesignEditor.tsx +3024 -616
  145. package/corpus/templates/design/app/pages/Index.tsx +5 -1
  146. package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
  147. package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
  148. package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
  149. package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
  150. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
  151. package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
  154. package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
  159. package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
  160. package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
  161. package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
  162. package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
  163. package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
  164. package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
  165. package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
  166. package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
  167. package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
  168. package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
  169. package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
  170. package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
  171. package/corpus/templates/design/e2e/global-setup.ts +94 -2
  172. package/corpus/templates/design/e2e/helpers.ts +168 -65
  173. package/corpus/templates/design/package.json +3 -1
  174. package/corpus/templates/design/server/db/schema.ts +23 -0
  175. package/corpus/templates/design/server/handlers/uploads.ts +22 -1
  176. package/corpus/templates/design/server/lib/provider-api.ts +7 -4
  177. package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
  178. package/corpus/templates/design/server/plugins/db.ts +39 -0
  179. package/corpus/templates/design/server/register-secrets.ts +38 -0
  180. package/corpus/templates/design/shared/board-file.ts +228 -0
  181. package/corpus/templates/design/shared/board-objects.ts +288 -0
  182. package/corpus/templates/design/shared/code-layer.ts +52 -0
  183. package/corpus/templates/design/shared/component-model.ts +35 -0
  184. package/corpus/templates/design/shared/motion-compiler.ts +137 -15
  185. package/corpus/templates/design/shared/motion-timeline.ts +2 -2
  186. package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
  187. package/corpus/templates/design/tsconfig.json +2 -1
  188. package/corpus/templates/slides/AGENTS.md +9 -0
  189. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
  190. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
  191. package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
  192. package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
  193. package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
  194. package/corpus/templates/slides/app/vite-env.d.ts +18 -0
  195. package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
  196. package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
  197. package/corpus/templates/slides/package.json +1 -0
  198. package/corpus/templates/slides/vite.config.ts +1 -0
  199. package/dist/agent/production-agent.d.ts.map +1 -1
  200. package/dist/agent/production-agent.js +94 -10
  201. package/dist/agent/production-agent.js.map +1 -1
  202. package/dist/cli/design-connect.d.ts +25 -0
  203. package/dist/cli/design-connect.d.ts.map +1 -1
  204. package/dist/cli/design-connect.js +357 -18
  205. package/dist/cli/design-connect.js.map +1 -1
  206. package/dist/client/AssistantChat.d.ts +14 -0
  207. package/dist/client/AssistantChat.d.ts.map +1 -1
  208. package/dist/client/AssistantChat.js +115 -22
  209. package/dist/client/AssistantChat.js.map +1 -1
  210. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  211. package/dist/client/ConnectBuilderCard.js +1 -0
  212. package/dist/client/ConnectBuilderCard.js.map +1 -1
  213. package/dist/client/chat/index.d.ts +1 -1
  214. package/dist/client/chat/index.d.ts.map +1 -1
  215. package/dist/client/chat/index.js +1 -1
  216. package/dist/client/chat/index.js.map +1 -1
  217. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  218. package/dist/client/chat/run-recovery.js +5 -5
  219. package/dist/client/chat/run-recovery.js.map +1 -1
  220. package/dist/client/chat-view-transition.d.ts +6 -0
  221. package/dist/client/chat-view-transition.d.ts.map +1 -1
  222. package/dist/client/chat-view-transition.js +23 -2
  223. package/dist/client/chat-view-transition.js.map +1 -1
  224. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  225. package/dist/client/composer/PromptComposer.js +3 -2
  226. package/dist/client/composer/PromptComposer.js.map +1 -1
  227. package/dist/client/index.d.ts +1 -1
  228. package/dist/client/index.d.ts.map +1 -1
  229. package/dist/client/index.js +1 -1
  230. package/dist/client/index.js.map +1 -1
  231. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  232. package/dist/client/org/OrgSwitcher.js +11 -4
  233. package/dist/client/org/OrgSwitcher.js.map +1 -1
  234. package/dist/client/sharing/ShareButton.js +3 -3
  235. package/dist/client/sharing/ShareButton.js.map +1 -1
  236. package/dist/client/sse-event-processor.d.ts.map +1 -1
  237. package/dist/client/sse-event-processor.js +10 -0
  238. package/dist/client/sse-event-processor.js.map +1 -1
  239. package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
  240. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  241. package/dist/client/use-agent-chat-home-handoff.js +20 -6
  242. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  243. package/dist/collab/awareness.d.ts +2 -2
  244. package/dist/collab/awareness.d.ts.map +1 -1
  245. package/dist/collab/routes.d.ts +2 -2
  246. package/dist/db/client.d.ts +1 -0
  247. package/dist/db/client.d.ts.map +1 -1
  248. package/dist/db/client.js +28 -2
  249. package/dist/db/client.js.map +1 -1
  250. package/dist/notifications/routes.d.ts +3 -3
  251. package/dist/observability/routes.d.ts +3 -3
  252. package/dist/progress/routes.d.ts +1 -1
  253. package/dist/provider-api/index.d.ts +5 -4
  254. package/dist/provider-api/index.d.ts.map +1 -1
  255. package/dist/provider-api/index.js +28 -0
  256. package/dist/provider-api/index.js.map +1 -1
  257. package/dist/resources/handlers.d.ts +3 -3
  258. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  259. package/dist/server/core-routes-plugin.d.ts +25 -0
  260. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  261. package/dist/server/core-routes-plugin.js +43 -18
  262. package/dist/server/core-routes-plugin.js.map +1 -1
  263. package/dist/server/transcribe-voice.d.ts +1 -1
  264. package/dist/styles/agent-native.css +54 -0
  265. package/dist/usage/store.js +2 -2
  266. package/dist/usage/store.js.map +1 -1
  267. package/package.json +1 -1
@@ -25,6 +25,8 @@ import {
25
25
  IconChevronRight,
26
26
  IconCircle,
27
27
  IconCode,
28
+ IconFileText,
29
+ IconFolder,
28
30
  IconLetterCase,
29
31
  IconPalette,
30
32
  IconPlus,
@@ -32,6 +34,7 @@ import {
32
34
  IconRuler,
33
35
  IconSpacingVertical,
34
36
  IconShadow,
37
+ IconUpload,
35
38
  } from "@tabler/icons-react";
36
39
  import { useEffect, useRef, useState } from "react";
37
40
 
@@ -43,6 +46,7 @@ import {
43
46
  PopoverContent,
44
47
  PopoverTrigger,
45
48
  } from "@/components/ui/popover";
49
+ import { Textarea } from "@/components/ui/textarea";
46
50
  import {
47
51
  Tooltip,
48
52
  TooltipContent,
@@ -75,6 +79,18 @@ interface IndexDesignTokensResult {
75
79
  tokens: DesignToken[];
76
80
  }
77
81
 
82
+ interface ImportDesignTokensResult {
83
+ designId: string;
84
+ importedCount: number;
85
+ filesAnalyzed: string[];
86
+ resolvedCssVars?: Record<string, string>;
87
+ }
88
+
89
+ interface TokenImportFile {
90
+ filename: string;
91
+ content: string;
92
+ }
93
+
78
94
  // ---------------------------------------------------------------------------
79
95
  // Props
80
96
  // ---------------------------------------------------------------------------
@@ -182,18 +198,21 @@ function TokenRow({
182
198
  )}
183
199
 
184
200
  {/* Friendly name */}
185
- <span
186
- className="flex-1 cursor-pointer truncate text-[11px] text-foreground"
201
+ <button
202
+ type="button"
203
+ className="min-w-0 flex-1 cursor-pointer truncate bg-transparent p-0 text-left text-[11px] text-foreground"
187
204
  onClick={onStartEdit}
205
+ aria-label={`Edit ${token.name}`}
188
206
  title={token.name}
189
207
  >
190
208
  {token.name}
191
- </span>
209
+ </button>
192
210
 
193
211
  {/* Value / edit input */}
194
212
  {editing ? (
195
213
  <Input
196
214
  ref={inputRef}
215
+ aria-label={`Token value for ${token.name}`}
197
216
  value={editDraft}
198
217
  onChange={(e) => onDraftChange(e.target.value)}
199
218
  onKeyDown={(e) => {
@@ -204,20 +223,22 @@ function TokenRow({
204
223
  className="h-5 w-24 px-1 py-0 text-[11px] font-mono"
205
224
  />
206
225
  ) : (
207
- <span
208
- className="max-w-[6rem] cursor-pointer truncate text-right font-mono text-[10px] text-muted-foreground hover:text-foreground"
226
+ <button
227
+ type="button"
228
+ className="max-w-[6rem] cursor-pointer truncate bg-transparent p-0 text-right font-mono text-[10px] text-muted-foreground hover:text-foreground"
209
229
  title={token.value}
230
+ aria-label={`Edit value for ${token.name}`}
210
231
  onClick={onStartEdit}
211
232
  >
212
233
  {token.value}
213
- </span>
234
+ </button>
214
235
  )}
215
236
 
216
237
  {/* CSS var chip (hidden when editing, visible on hover) */}
217
238
  {!editing && (
218
239
  <Tooltip>
219
240
  <TooltipTrigger asChild>
220
- <span className="hidden cursor-default select-all rounded bg-muted px-1 py-0 font-mono text-[9px] text-muted-foreground/70 group-hover:inline">
241
+ <span className="pointer-events-none hidden max-w-[5.5rem] shrink-0 cursor-default select-all truncate rounded bg-muted px-1 py-0 font-mono text-[9px] text-muted-foreground/70 group-hover:inline-block">
221
242
  {token.cssVar}
222
243
  </span>
223
244
  </TooltipTrigger>
@@ -231,7 +252,7 @@ function TokenRow({
231
252
  {!editing && (
232
253
  <Badge
233
254
  variant="outline"
234
- className="hidden h-4 cursor-default px-1 py-0 text-[9px] text-muted-foreground/60 group-hover:flex"
255
+ className="pointer-events-none hidden h-4 shrink-0 cursor-default px-1 py-0 text-[9px] text-muted-foreground/60 group-hover:flex"
235
256
  >
236
257
  {token.source}
237
258
  </Badge>
@@ -383,6 +404,200 @@ function NewTokenPopover({ onAdd }: NewTokenPopoverProps) {
383
404
  );
384
405
  }
385
406
 
407
+ // ---------------------------------------------------------------------------
408
+ // Import popover
409
+ // ---------------------------------------------------------------------------
410
+
411
+ const TOKEN_IMPORT_ACCEPT = [
412
+ ".css",
413
+ ".scss",
414
+ ".sass",
415
+ ".less",
416
+ ".json",
417
+ ".ts",
418
+ ".tsx",
419
+ ".js",
420
+ ".jsx",
421
+ ".md",
422
+ ".mdx",
423
+ ".txt",
424
+ ].join(",");
425
+
426
+ async function readImportFiles(fileList: FileList): Promise<TokenImportFile[]> {
427
+ const selected = [...fileList].slice(0, 20);
428
+ const files = await Promise.all(
429
+ selected.map(async (file) => ({
430
+ filename:
431
+ (file as File & { webkitRelativePath?: string }).webkitRelativePath ||
432
+ file.name,
433
+ content: await file.text(),
434
+ })),
435
+ );
436
+
437
+ return files.filter((file) => file.content.trim().length > 0);
438
+ }
439
+
440
+ interface ImportTokensPopoverProps {
441
+ onImportFiles: (
442
+ files: TokenImportFile[],
443
+ ) => Promise<ImportDesignTokensResult>;
444
+ onImportText: (text: string) => Promise<ImportDesignTokensResult>;
445
+ onImportCurrentDesign: () => Promise<ImportDesignTokensResult>;
446
+ isPending: boolean;
447
+ }
448
+
449
+ function ImportTokensPopover({
450
+ onImportFiles,
451
+ onImportText,
452
+ onImportCurrentDesign,
453
+ isPending,
454
+ }: ImportTokensPopoverProps) {
455
+ const t = useT();
456
+ const fileInputRef = useRef<HTMLInputElement>(null);
457
+ const folderInputRef = useRef<HTMLInputElement>(null);
458
+ const [open, setOpen] = useState(false);
459
+ const [text, setText] = useState("");
460
+ const [status, setStatus] = useState<string | null>(null);
461
+
462
+ const runImport = async (
463
+ importer: () => Promise<ImportDesignTokensResult>,
464
+ ) => {
465
+ setStatus(null);
466
+ try {
467
+ const result = await importer();
468
+ setStatus(
469
+ t("designEditor.tokens.importedCount", {
470
+ count: result.importedCount,
471
+ }),
472
+ );
473
+ if (result.importedCount > 0) {
474
+ setText("");
475
+ }
476
+ } catch (error) {
477
+ setStatus(error instanceof Error ? error.message : String(error));
478
+ }
479
+ };
480
+
481
+ const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
482
+ const files = event.currentTarget.files;
483
+ event.currentTarget.value = "";
484
+ if (!files?.length) return;
485
+ void runImport(async () => onImportFiles(await readImportFiles(files)));
486
+ };
487
+
488
+ return (
489
+ <Popover open={open} onOpenChange={setOpen}>
490
+ <Tooltip>
491
+ <TooltipTrigger asChild>
492
+ <PopoverTrigger asChild>
493
+ <Button
494
+ type="button"
495
+ variant="ghost"
496
+ size="icon"
497
+ className="size-6 cursor-pointer text-muted-foreground/60 hover:text-foreground"
498
+ aria-label={t("designEditor.tokens.import")}
499
+ >
500
+ <IconUpload className="size-3" />
501
+ </Button>
502
+ </PopoverTrigger>
503
+ </TooltipTrigger>
504
+ <TooltipContent>{t("designEditor.tokens.import")}</TooltipContent>
505
+ </Tooltip>
506
+ <PopoverContent align="end" className="w-72 p-3 text-[12px]">
507
+ <div className="space-y-3">
508
+ <div className="space-y-1">
509
+ <p className="text-[11px] font-medium text-foreground">
510
+ {t("designEditor.tokens.importTitle")}
511
+ </p>
512
+ <p className="text-[10px] leading-snug text-muted-foreground">
513
+ {t("designEditor.tokens.importHint")}
514
+ </p>
515
+ </div>
516
+
517
+ <div className="grid grid-cols-3 gap-1.5">
518
+ <Button
519
+ type="button"
520
+ variant="outline"
521
+ size="sm"
522
+ className="h-7 cursor-pointer gap-1 px-2 text-[10px]"
523
+ disabled={isPending}
524
+ onClick={() => fileInputRef.current?.click()}
525
+ >
526
+ <IconFileText className="size-3" />
527
+ {t("designEditor.tokens.files")}
528
+ </Button>
529
+ <Button
530
+ type="button"
531
+ variant="outline"
532
+ size="sm"
533
+ className="h-7 cursor-pointer gap-1 px-2 text-[10px]"
534
+ disabled={isPending}
535
+ onClick={() => folderInputRef.current?.click()}
536
+ >
537
+ <IconFolder className="size-3" />
538
+ {t("designEditor.tokens.folder")}
539
+ </Button>
540
+ <Button
541
+ type="button"
542
+ variant="outline"
543
+ size="sm"
544
+ className="h-7 cursor-pointer px-2 text-[10px]"
545
+ disabled={isPending}
546
+ onClick={() => void runImport(onImportCurrentDesign)}
547
+ >
548
+ {t("designEditor.tokens.current")}
549
+ </Button>
550
+ </div>
551
+
552
+ <input
553
+ ref={fileInputRef}
554
+ type="file"
555
+ multiple
556
+ accept={TOKEN_IMPORT_ACCEPT}
557
+ className="hidden"
558
+ onChange={handleFileChange}
559
+ />
560
+ <input
561
+ ref={folderInputRef}
562
+ type="file"
563
+ multiple
564
+ accept={TOKEN_IMPORT_ACCEPT}
565
+ className="hidden"
566
+ onChange={handleFileChange}
567
+ {...({ directory: "", webkitdirectory: "" } as Record<
568
+ string,
569
+ string
570
+ >)}
571
+ />
572
+
573
+ <div className="space-y-1.5">
574
+ <Textarea
575
+ value={text}
576
+ onChange={(event) => setText(event.target.value)}
577
+ placeholder={t("designEditor.tokens.pastePlaceholder")}
578
+ className="min-h-24 resize-none font-mono text-[11px]"
579
+ />
580
+ <Button
581
+ type="button"
582
+ className="h-7 w-full cursor-pointer text-[11px]"
583
+ disabled={isPending || !text.trim()}
584
+ onClick={() => void runImport(() => onImportText(text))}
585
+ >
586
+ {t("designEditor.tokens.importPasted")}
587
+ </Button>
588
+ </div>
589
+
590
+ {status && (
591
+ <p className="text-[10px] leading-snug text-muted-foreground">
592
+ {status}
593
+ </p>
594
+ )}
595
+ </div>
596
+ </PopoverContent>
597
+ </Popover>
598
+ );
599
+ }
600
+
386
601
  // ---------------------------------------------------------------------------
387
602
  // Main panel
388
603
  // ---------------------------------------------------------------------------
@@ -404,6 +619,7 @@ export function TokensPanel({ designId, onTokensApplied }: TokensPanelProps) {
404
619
  );
405
620
 
406
621
  const applyMutation = useActionMutation("apply-design-token-edit");
622
+ const importMutation = useActionMutation("import-design-tokens");
407
623
 
408
624
  // ------------------------------------------------------------------
409
625
  // Local edit state
@@ -458,6 +674,40 @@ export function TokensPanel({ designId, onTokensApplied }: TokensPanelProps) {
458
674
  );
459
675
  };
460
676
 
677
+ const handleImportSuccess = (result: ImportDesignTokensResult) => {
678
+ void refetch();
679
+ if (result.resolvedCssVars && onTokensApplied) {
680
+ onTokensApplied(result.resolvedCssVars);
681
+ }
682
+ return result;
683
+ };
684
+
685
+ const importFiles = async (files: TokenImportFile[]) => {
686
+ const result = (await importMutation.mutateAsync({
687
+ designId,
688
+ source: "files",
689
+ files,
690
+ })) as ImportDesignTokensResult;
691
+ return handleImportSuccess(result);
692
+ };
693
+
694
+ const importText = async (text: string) => {
695
+ const result = (await importMutation.mutateAsync({
696
+ designId,
697
+ source: "paste",
698
+ text,
699
+ })) as ImportDesignTokensResult;
700
+ return handleImportSuccess(result);
701
+ };
702
+
703
+ const importCurrentDesign = async () => {
704
+ const result = (await importMutation.mutateAsync({
705
+ designId,
706
+ source: "current-design",
707
+ })) as ImportDesignTokensResult;
708
+ return handleImportSuccess(result);
709
+ };
710
+
461
711
  // ------------------------------------------------------------------
462
712
  // Render
463
713
  // ------------------------------------------------------------------
@@ -495,6 +745,12 @@ export function TokensPanel({ designId, onTokensApplied }: TokensPanelProps) {
495
745
  </TooltipTrigger>
496
746
  <TooltipContent>{t("designEditor.tokens.refresh")}</TooltipContent>
497
747
  </Tooltip>
748
+ <ImportTokensPopover
749
+ onImportFiles={importFiles}
750
+ onImportText={importText}
751
+ onImportCurrentDesign={importCurrentDesign}
752
+ isPending={importMutation.isPending}
753
+ />
498
754
  <NewTokenPopover onAdd={handleNewToken} />
499
755
  </div>
500
756
  </div>
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Bridge codegen — compiles every *.bridge.ts in this directory to a self-contained
3
+ * injectable string and writes the result as a generated TypeScript module under
4
+ * .generated/bridge/<name>.generated.ts.
5
+ *
6
+ * Usage (from the design template root):
7
+ * pnpm exec tsx app/components/design/bridge/codegen.ts
8
+ *
9
+ * Called automatically by:
10
+ * - The freshness guard test (bridge.guard.spec.ts)
11
+ * - The project build script (see scripts.build in package.json)
12
+ *
13
+ * Mechanism
14
+ * ---------
15
+ * esbuild is invoked with bundle:true, format:'iife', platform:'browser',
16
+ * write:false for each .bridge.ts file. This means:
17
+ * • Any ES import/require that CAN'T be resolved by esbuild causes a build error
18
+ * (self-containment guarantee for non-local deps, e.g. npm packages).
19
+ * • Any ES import that CAN be resolved (e.g. a relative .ts file) gets bundled
20
+ * inline — the guard test's regex check on source prevents this.
21
+ * • The output is an IIFE-wrapped, browser-runnable JS string.
22
+ *
23
+ * The generated module is a plain committed .ts file that any bundler (Vite, Nitro,
24
+ * Rolldown) handles as a normal module — no Vite plugin, no SSR complications.
25
+ */
26
+
27
+ import { execSync } from "node:child_process";
28
+ import { readdirSync, readFileSync, writeFileSync } from "node:fs";
29
+ import { join, resolve } from "node:path";
30
+ import { fileURLToPath } from "node:url";
31
+
32
+ import * as esbuild from "esbuild";
33
+
34
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
35
+ const designRoot = resolve(__dirname, "../../../..");
36
+ const bridgeDir = __dirname;
37
+ const generatedDir = join(designRoot, ".generated", "bridge");
38
+
39
+ const GENERATED_BANNER = `// AUTO-GENERATED by bridge/codegen.ts — do not edit manually.
40
+ // Run: pnpm exec tsx app/components/design/bridge/codegen.ts
41
+ `;
42
+
43
+ async function compileBridge(srcFile: string): Promise<string> {
44
+ const result = await esbuild.build({
45
+ entryPoints: [srcFile],
46
+ bundle: true,
47
+ format: "iife",
48
+ platform: "browser",
49
+ target: "es2020",
50
+ write: false,
51
+ // No external packages — unresolved imports are a hard error.
52
+ // (Relative imports that CAN be resolved get bundled inline, which is also
53
+ // wrong; the guard test prevents those at the source level.)
54
+ external: [],
55
+ });
56
+
57
+ if (result.errors.length > 0) {
58
+ const msgs = await esbuild.formatMessages(result.errors, { kind: "error" });
59
+ throw new Error(`esbuild error for ${srcFile}:\n${msgs.join("\n")}`);
60
+ }
61
+
62
+ const output = result.outputFiles[0];
63
+ if (!output) throw new Error(`No output from esbuild for ${srcFile}`);
64
+ return output.text;
65
+ }
66
+
67
+ function bridgeName(filename: string): string {
68
+ return filename.replace(/\.bridge\.ts$/, "");
69
+ }
70
+
71
+ function generatedModuleSrc(name: string, compiled: string): string {
72
+ // Escape backticks and ${} in the compiled string so it embeds safely.
73
+ const escaped = compiled
74
+ .replace(/\\/g, "\\\\")
75
+ .replace(/`/g, "\\`")
76
+ .replace(/\$\{/g, "\\${");
77
+ return `${GENERATED_BANNER}
78
+ /** Compiled IIFE string for ${name}.bridge.ts — inject into an iframe via srcdoc or a <script> tag. */
79
+ export const ${camelCase(name)}BridgeScript: string = \`${escaped}\`;
80
+ `;
81
+ }
82
+
83
+ function camelCase(name: string): string {
84
+ return name.replace(/[-_]([a-z])/g, (_, c: string) => c.toUpperCase());
85
+ }
86
+
87
+ async function runCodegen(): Promise<void> {
88
+ const bridgeFiles = readdirSync(bridgeDir).filter((f) =>
89
+ f.endsWith(".bridge.ts"),
90
+ );
91
+
92
+ if (bridgeFiles.length === 0) {
93
+ console.log("[bridge/codegen] No *.bridge.ts files found.");
94
+ return;
95
+ }
96
+
97
+ for (const filename of bridgeFiles) {
98
+ const srcPath = join(bridgeDir, filename);
99
+ const name = bridgeName(filename);
100
+ const outPath = join(generatedDir, `${name}.generated.ts`);
101
+
102
+ console.log(`[bridge/codegen] Compiling ${filename} ...`);
103
+ const compiled = await compileBridge(srcPath);
104
+ const src = generatedModuleSrc(name, compiled);
105
+ writeFileSync(outPath, src, "utf-8");
106
+ console.log(`[bridge/codegen] → .generated/bridge/${name}.generated.ts`);
107
+ }
108
+
109
+ // Run oxfmt on generated files so they pass the formatter check.
110
+ try {
111
+ execSync(`pnpm exec oxfmt .generated/bridge/`, {
112
+ cwd: designRoot,
113
+ stdio: "pipe",
114
+ });
115
+ } catch {
116
+ // oxfmt not critical — formatting mismatches caught by CI linter, not here.
117
+ }
118
+
119
+ console.log("[bridge/codegen] Done.");
120
+ }
121
+
122
+ runCodegen().catch((err) => {
123
+ console.error("[bridge/codegen] FAILED:", err);
124
+ process.exit(1);
125
+ });