@agent-native/core 0.119.0 → 0.119.2

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 (274) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +23 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +4 -0
  5. package/corpus/core/src/agent/production-agent.ts +1 -0
  6. package/corpus/core/src/client/AssistantChat.tsx +11 -50
  7. package/corpus/core/src/client/ErrorBoundary.tsx +25 -19
  8. package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +1 -1
  9. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +23 -0
  10. package/corpus/core/src/client/components/ui/message-scroller.tsx +1 -9
  11. package/corpus/core/src/client/resources/ResourceTree.tsx +3 -3
  12. package/corpus/core/src/client/resources/ResourcesPanel.tsx +64 -60
  13. package/corpus/core/src/client/settings/AccountSettingsCard.tsx +228 -0
  14. package/corpus/core/src/client/settings/SecretsSection.tsx +7 -5
  15. package/corpus/core/src/client/settings/SettingsPanel.tsx +67 -244
  16. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +88 -21
  17. package/corpus/core/src/client/settings/agent-settings-search.ts +0 -2
  18. package/corpus/core/src/client/settings/index.ts +7 -0
  19. package/corpus/core/src/client/vite-dev-recovery-script.ts +18 -1
  20. package/corpus/core/src/db/client.ts +65 -15
  21. package/corpus/core/src/db/index.ts +2 -0
  22. package/corpus/core/src/ingestion/figma-node-to-html.ts +20 -99
  23. package/corpus/core/src/ingestion/figma-paint-math.ts +371 -0
  24. package/corpus/core/src/ingestion/index.ts +20 -0
  25. package/corpus/core/src/localization/default-messages.ts +1 -0
  26. package/corpus/core/src/server/email.ts +33 -1
  27. package/corpus/core/src/styles/agent-native.css +23 -0
  28. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  29. package/corpus/core/src/templates/default/app/routes/settings.tsx +2 -0
  30. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  31. package/corpus/core/src/vite/client.ts +76 -8
  32. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +28 -0
  33. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +3 -1
  34. package/corpus/templates/analytics/app/components/layout/Layout.tsx +9 -13
  35. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +2 -2
  36. package/corpus/templates/analytics/app/global.css +0 -5
  37. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -13
  38. package/corpus/templates/analytics/app/lib/sql-query.ts +15 -2
  39. package/corpus/templates/analytics/app/pages/Settings.tsx +2 -24
  40. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +6 -0
  41. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  42. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +1 -1
  43. package/corpus/templates/analytics/changelog/2026-07-23-ask-analytics-is-better-centered-with-a-quieter-chat-rail-an.md +6 -0
  44. package/corpus/templates/analytics/changelog/2026-07-23-daily-dashboard-emails-load-read-heavy-panels-reliably.md +6 -0
  45. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +2 -2
  46. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +39 -5
  47. package/corpus/templates/analytics/server/lib/dashboard-report.ts +186 -62
  48. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +3 -0
  49. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +18 -2
  50. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +3 -0
  51. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +28 -0
  52. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -4
  53. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +1 -1
  54. package/corpus/templates/assets/app/global.css +0 -5
  55. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +0 -22
  56. package/corpus/templates/assets/app/routes/library.tsx +17 -9
  57. package/corpus/templates/assets/app/routes/settings.tsx +2 -0
  58. package/corpus/templates/assets/changelog/2026-07-23-create-and-chat-surfaces-are-better-centered-with-quieter-ch.md +6 -0
  59. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +28 -0
  60. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  61. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  62. package/corpus/templates/brain/app/global.css +0 -9
  63. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +0 -12
  64. package/corpus/templates/brain/app/i18n-data.ts +1 -0
  65. package/corpus/templates/brain/app/lib/settings-navigation.ts +1 -1
  66. package/corpus/templates/brain/app/routes/settings.tsx +21 -22
  67. package/corpus/templates/brain/changelog/2026-07-23-ask-brain-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  68. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +28 -0
  69. package/corpus/templates/calendar/app/pages/Settings.tsx +2 -0
  70. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  71. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  72. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  73. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  74. package/corpus/templates/chat/app/routes/settings.tsx +2 -0
  75. package/corpus/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  76. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +28 -0
  77. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +2 -0
  78. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +28 -0
  79. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -0
  80. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +28 -0
  81. package/corpus/templates/design/AGENTS.md +18 -5
  82. package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +25 -12
  83. package/corpus/templates/design/actions/hydrate-figma-paste-images.ts +115 -0
  84. package/corpus/templates/design/actions/import-figma-clipboard.ts +83 -18
  85. package/corpus/templates/design/actions/import-figma-frame.ts +34 -21
  86. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +28 -1
  87. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +106 -11
  88. package/corpus/templates/design/app/components/design/FigmaHydrationDialog.tsx +273 -0
  89. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -7
  90. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +57 -9
  91. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +10 -0
  92. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +8 -1
  93. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  94. package/corpus/templates/design/app/i18n-data.ts +150 -2
  95. package/corpus/templates/design/app/lib/design-file-upload.ts +85 -0
  96. package/corpus/templates/design/app/lib/design-import.ts +19 -1
  97. package/corpus/templates/design/app/lib/figma-clipboard.ts +110 -10
  98. package/corpus/templates/design/app/routes/settings.tsx +2 -0
  99. package/corpus/templates/design/changelog/2026-07-20-paste-figma-frames-directly-onto-the-canvas-no-token-needed.md +6 -0
  100. package/corpus/templates/design/changelog/2026-07-21-fig-upload-frame-url-and-rate-limit-errors.md +5 -0
  101. package/corpus/templates/design/changelog/2026-07-22-connecting-a-figma-token-now-actually-fills-in-a-pasted-desi.md +6 -0
  102. package/corpus/templates/design/changelog/2026-07-22-figma-line-arrow-vectors-and-stroked-icons-now-render-instea.md +6 -0
  103. package/corpus/templates/design/changelog/2026-07-22-fill-a-no-token-figma-paste-s-images-by-dropping-the-origina.md +6 -0
  104. package/corpus/templates/design/changelog/2026-07-22-imported-and-pasted-figma-frames-now-render-faithfully-fixed.md +6 -0
  105. package/corpus/templates/design/server/handlers/import-design-file.ts +50 -0
  106. package/corpus/templates/design/server/lib/fig-file-decoder.ts +90 -49
  107. package/corpus/templates/design/server/lib/fig-file-import.ts +19 -5
  108. package/corpus/templates/design/server/lib/fig-file-to-html.ts +583 -78
  109. package/corpus/templates/design/server/lib/figma-clipboard-local-decode.ts +238 -0
  110. package/corpus/templates/design/server/lib/figma-image-hydration.ts +389 -0
  111. package/corpus/templates/design/server/lib/figma-node-import.ts +136 -36
  112. package/corpus/templates/design/server/register-secrets.ts +14 -2
  113. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +28 -0
  114. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -0
  115. package/corpus/templates/dispatch/changelog/2026-07-23-quieter-progressive-disclosure.md +6 -0
  116. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +28 -0
  117. package/corpus/templates/forms/app/global.css +0 -5
  118. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +0 -7
  119. package/corpus/templates/forms/app/root.tsx +1 -7
  120. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -0
  121. package/corpus/templates/forms/changelog/2026-07-23-ask-forms-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  122. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +28 -0
  123. package/corpus/templates/macros/app/routes/settings.tsx +2 -0
  124. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +28 -0
  125. package/corpus/templates/mail/app/pages/SettingsPage.tsx +3 -1
  126. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +28 -0
  127. package/corpus/templates/plan/app/components/layout/Layout.tsx +2 -2
  128. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +1 -13
  129. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +0 -4
  130. package/corpus/templates/plan/app/routes/settings.tsx +2 -0
  131. package/corpus/templates/plan/changelog/2026-07-23-ask-plan-is-now-a-clean-full-page-chat-surface-with-better-c.md +6 -0
  132. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +28 -0
  133. package/corpus/templates/slides/app/routes/settings.tsx +2 -0
  134. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +28 -0
  135. package/corpus/toolkit/CHANGELOG.md +6 -0
  136. package/corpus/toolkit/package.json +1 -1
  137. package/corpus/toolkit/src/chat-history.css +2 -2
  138. package/corpus/toolkit/src/composer/PromptComposer.tsx +0 -31
  139. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +7 -2
  140. package/corpus/toolkit/src/styles.css +183 -36
  141. package/dist/a2a/client.d.ts +2 -0
  142. package/dist/a2a/client.d.ts.map +1 -1
  143. package/dist/a2a/client.js +2 -0
  144. package/dist/a2a/client.js.map +1 -1
  145. package/dist/agent/production-agent.d.ts.map +1 -1
  146. package/dist/agent/production-agent.js +1 -0
  147. package/dist/agent/production-agent.js.map +1 -1
  148. package/dist/client/AssistantChat.d.ts.map +1 -1
  149. package/dist/client/AssistantChat.js +11 -42
  150. package/dist/client/AssistantChat.js.map +1 -1
  151. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  152. package/dist/client/ErrorBoundary.js +3 -2
  153. package/dist/client/ErrorBoundary.js.map +1 -1
  154. package/dist/client/agent-page/AgentTabFrame.js +1 -1
  155. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  156. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  157. package/dist/client/agent-page/AgentTabsPage.js +13 -1
  158. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  159. package/dist/client/components/ui/message-scroller.d.ts.map +1 -1
  160. package/dist/client/components/ui/message-scroller.js +2 -2
  161. package/dist/client/components/ui/message-scroller.js.map +1 -1
  162. package/dist/client/resources/ResourceTree.js +2 -2
  163. package/dist/client/resources/ResourceTree.js.map +1 -1
  164. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  165. package/dist/client/resources/ResourcesPanel.js +11 -11
  166. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  167. package/dist/client/settings/AccountSettingsCard.d.ts +9 -0
  168. package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -0
  169. package/dist/client/settings/AccountSettingsCard.js +78 -0
  170. package/dist/client/settings/AccountSettingsCard.js.map +1 -0
  171. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  172. package/dist/client/settings/SecretsSection.js +2 -2
  173. package/dist/client/settings/SecretsSection.js.map +1 -1
  174. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  175. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  176. package/dist/client/settings/SettingsPanel.js +25 -77
  177. package/dist/client/settings/SettingsPanel.js.map +1 -1
  178. package/dist/client/settings/SettingsTabsPage.d.ts +5 -1
  179. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  180. package/dist/client/settings/SettingsTabsPage.js +35 -9
  181. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  182. package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
  183. package/dist/client/settings/agent-settings-search.js +0 -2
  184. package/dist/client/settings/agent-settings-search.js.map +1 -1
  185. package/dist/client/settings/index.d.ts +2 -1
  186. package/dist/client/settings/index.d.ts.map +1 -1
  187. package/dist/client/settings/index.js +2 -1
  188. package/dist/client/settings/index.js.map +1 -1
  189. package/dist/client/vite-dev-recovery-script.d.ts.map +1 -1
  190. package/dist/client/vite-dev-recovery-script.js +18 -1
  191. package/dist/client/vite-dev-recovery-script.js.map +1 -1
  192. package/dist/collab/routes.d.ts +1 -1
  193. package/dist/db/client.d.ts +18 -8
  194. package/dist/db/client.d.ts.map +1 -1
  195. package/dist/db/client.js +34 -11
  196. package/dist/db/client.js.map +1 -1
  197. package/dist/db/index.d.ts +1 -1
  198. package/dist/db/index.d.ts.map +1 -1
  199. package/dist/db/index.js.map +1 -1
  200. package/dist/ingestion/figma-node-to-html.d.ts.map +1 -1
  201. package/dist/ingestion/figma-node-to-html.js +14 -88
  202. package/dist/ingestion/figma-node-to-html.js.map +1 -1
  203. package/dist/ingestion/figma-paint-math.d.ts +157 -0
  204. package/dist/ingestion/figma-paint-math.d.ts.map +1 -0
  205. package/dist/ingestion/figma-paint-math.js +274 -0
  206. package/dist/ingestion/figma-paint-math.js.map +1 -0
  207. package/dist/ingestion/index.d.ts +1 -0
  208. package/dist/ingestion/index.d.ts.map +1 -1
  209. package/dist/ingestion/index.js +1 -0
  210. package/dist/ingestion/index.js.map +1 -1
  211. package/dist/localization/default-messages.d.ts +1 -0
  212. package/dist/localization/default-messages.d.ts.map +1 -1
  213. package/dist/localization/default-messages.js +1 -0
  214. package/dist/localization/default-messages.js.map +1 -1
  215. package/dist/notifications/routes.d.ts +2 -2
  216. package/dist/observability/routes.d.ts +3 -3
  217. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  218. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  219. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  220. package/dist/secrets/routes.d.ts +6 -6
  221. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  222. package/dist/server/email.d.ts +1 -0
  223. package/dist/server/email.d.ts.map +1 -1
  224. package/dist/server/email.js +29 -1
  225. package/dist/server/email.js.map +1 -1
  226. package/dist/server/transcribe-voice.d.ts +1 -1
  227. package/dist/styles/agent-native.css +23 -0
  228. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  229. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  230. package/dist/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  231. package/dist/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  232. package/dist/templates/chat/app/routes/settings.tsx +2 -0
  233. package/dist/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  234. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  235. package/dist/templates/default/app/routes/settings.tsx +2 -0
  236. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  237. package/dist/vite/client.d.ts.map +1 -1
  238. package/dist/vite/client.js +68 -6
  239. package/dist/vite/client.js.map +1 -1
  240. package/package.json +2 -2
  241. package/src/a2a/client.ts +4 -0
  242. package/src/agent/production-agent.ts +1 -0
  243. package/src/client/AssistantChat.tsx +11 -50
  244. package/src/client/ErrorBoundary.tsx +25 -19
  245. package/src/client/agent-page/AgentTabFrame.tsx +1 -1
  246. package/src/client/agent-page/AgentTabsPage.tsx +23 -0
  247. package/src/client/components/ui/message-scroller.tsx +1 -9
  248. package/src/client/resources/ResourceTree.tsx +3 -3
  249. package/src/client/resources/ResourcesPanel.tsx +64 -60
  250. package/src/client/settings/AccountSettingsCard.tsx +228 -0
  251. package/src/client/settings/SecretsSection.tsx +7 -5
  252. package/src/client/settings/SettingsPanel.tsx +67 -244
  253. package/src/client/settings/SettingsTabsPage.tsx +88 -21
  254. package/src/client/settings/agent-settings-search.ts +0 -2
  255. package/src/client/settings/index.ts +7 -0
  256. package/src/client/vite-dev-recovery-script.ts +18 -1
  257. package/src/db/client.ts +65 -15
  258. package/src/db/index.ts +2 -0
  259. package/src/ingestion/figma-node-to-html.ts +20 -99
  260. package/src/ingestion/figma-paint-math.ts +371 -0
  261. package/src/ingestion/index.ts +20 -0
  262. package/src/localization/default-messages.ts +1 -0
  263. package/src/server/email.ts +33 -1
  264. package/src/styles/agent-native.css +23 -0
  265. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  266. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  267. package/src/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  268. package/src/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  269. package/src/templates/chat/app/routes/settings.tsx +2 -0
  270. package/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  271. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  272. package/src/templates/default/app/routes/settings.tsx +2 -0
  273. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  274. package/src/vite/client.ts +76 -8
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Pure, synchronous math helpers for Figma gradient geometry and blend-mode
3
+ * normalisation. No DOM, no CSS, no network.
4
+ *
5
+ * Supports two gradient-transform sources:
6
+ * - REST API `gradientHandlePositions` (3-element Vec2 array, already in
7
+ * normalized 0..1 node-space).
8
+ * - 2×3 affine transform in **either** the Kiwi/fig-file object form
9
+ * `{m00..m12}` or the REST/Plugin API array form `[[a,b,tx],[c,d,ty]]`.
10
+ * Both encode the **node-to-gradient** mapping (same convention as Figma's
11
+ * own `gradientTransform` field); invert to obtain handle positions.
12
+ */
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface Vec2 {
19
+ x: number;
20
+ y: number;
21
+ }
22
+
23
+ export interface GradientHandles {
24
+ start: Vec2;
25
+ end: Vec2;
26
+ width: Vec2;
27
+ }
28
+
29
+ /**
30
+ * 2×3 affine transform in the row-major object form used by Kiwi/fig-file
31
+ * decoded paint nodes:
32
+ * [m00, m01, m02]
33
+ * [m10, m11, m12]
34
+ */
35
+ export interface Mat2x3Object {
36
+ m00: number;
37
+ m01: number;
38
+ m02: number;
39
+ m10: number;
40
+ m11: number;
41
+ m12: number;
42
+ }
43
+
44
+ /**
45
+ * 2×3 affine transform in the nested-array form used by the Figma REST API
46
+ * and Plugin API `gradientTransform` field:
47
+ * [[a, b, tx], [c, d, ty]]
48
+ */
49
+ export type Mat2x3Array = [[number, number, number], [number, number, number]];
50
+
51
+ export type BlendVerdict = "exact" | "approximated";
52
+
53
+ export interface BlendModeResult {
54
+ cssMode: string;
55
+ verdict: BlendVerdict;
56
+ }
57
+
58
+ export type GradientKind = "LINEAR" | "RADIAL" | "ANGULAR" | "DIAMOND";
59
+
60
+ export interface GradientGeometry {
61
+ kind: GradientKind;
62
+ handles: GradientHandles;
63
+ start: Vec2;
64
+ end: Vec2;
65
+ center: Vec2;
66
+ rx: number;
67
+ ry: number;
68
+ rotationDeg: number;
69
+ fromDeg: number;
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Matrix helpers
74
+ // ---------------------------------------------------------------------------
75
+
76
+ /** Convert the REST/Plugin nested-array form to the object form. */
77
+ export function mat2x3FromArray(m: Mat2x3Array): Mat2x3Object {
78
+ return {
79
+ m00: m[0][0],
80
+ m01: m[0][1],
81
+ m02: m[0][2],
82
+ m10: m[1][0],
83
+ m11: m[1][1],
84
+ m12: m[1][2],
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Invert a 2×3 affine transform. Returns null when the matrix is singular
90
+ * (determinant near zero, i.e. the gradient has collapsed to a line or point).
91
+ *
92
+ * The 2×2 rotation/scale sub-matrix is [[m00,m01],[m10,m11]]; the
93
+ * standard 2×2 inverse is applied and the translation is back-solved:
94
+ * inv_tx = (-m11*m02 + m01*m12) / det
95
+ * inv_ty = ( m10*m02 - m00*m12) / det
96
+ */
97
+ export function invert2x3(m: Mat2x3Object): Mat2x3Object | null {
98
+ const det = m.m00 * m.m11 - m.m01 * m.m10;
99
+ if (Math.abs(det) < 1e-8) return null;
100
+ const inv00 = m.m11 / det;
101
+ const inv01 = -m.m01 / det;
102
+ const inv10 = -m.m10 / det;
103
+ const inv11 = m.m00 / det;
104
+ return {
105
+ m00: inv00,
106
+ m01: inv01,
107
+ m02: (-m.m11 * m.m02 + m.m01 * m.m12) / det,
108
+ m10: inv10,
109
+ m11: inv11,
110
+ m12: (m.m10 * m.m02 - m.m00 * m.m12) / det,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Apply a 2×3 transform to a 2-D point.
116
+ * The point is treated as a homogeneous [x, y, 1]^T column vector.
117
+ */
118
+ function applyMat2x3(m: Mat2x3Object, v: Vec2): Vec2 {
119
+ return {
120
+ x: m.m00 * v.x + m.m01 * v.y + m.m02,
121
+ y: m.m10 * v.x + m.m11 * v.y + m.m12,
122
+ };
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Handle positions from transforms
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Derive REST-style `GradientHandles` from a 2×3 **node-to-gradient**
131
+ * transform in Kiwi/fig-file object form.
132
+ *
133
+ * Figma's gradient transform encodes where the gradient's natural coordinate
134
+ * system sits inside the node's normalized [0,1]² box. The three canonical
135
+ * gradient-space points (start, end, width) map back to node-space by
136
+ * inverting the transform:
137
+ * handles = inv(M) * {(0,0), (1,0), (0,1)}
138
+ */
139
+ export function handlePositionsFromObjectTransform(
140
+ t: Mat2x3Object,
141
+ ): GradientHandles | null {
142
+ const inv = invert2x3(t);
143
+ if (!inv) return null;
144
+ return {
145
+ start: applyMat2x3(inv, { x: 0, y: 0 }),
146
+ end: applyMat2x3(inv, { x: 1, y: 0 }),
147
+ width: applyMat2x3(inv, { x: 0, y: 1 }),
148
+ };
149
+ }
150
+
151
+ export function gradientGeometryFromTransform(
152
+ kind: GradientKind,
153
+ transform: Mat2x3Object,
154
+ box: { width: number; height: number },
155
+ ): GradientGeometry | null {
156
+ const inverse = invert2x3(transform);
157
+ if (!inverse) return null;
158
+ const toPixels = (point: Vec2): Vec2 => ({
159
+ x: point.x * box.width,
160
+ y: point.y * box.height,
161
+ });
162
+ const startNormalized = applyMat2x3(inverse, { x: 0, y: 0.5 });
163
+ const endNormalized = applyMat2x3(inverse, { x: 1, y: 0.5 });
164
+ const widthNormalized = applyMat2x3(inverse, { x: 1, y: 0 });
165
+ const center = toPixels(applyMat2x3(inverse, { x: 0.5, y: 0.5 }));
166
+ const vertex = toPixels(applyMat2x3(inverse, { x: 1, y: 0.5 }));
167
+ const covertex = toPixels(applyMat2x3(inverse, { x: 0.5, y: 1 }));
168
+ const vertexDx = vertex.x - center.x;
169
+ const vertexDy = vertex.y - center.y;
170
+ return {
171
+ kind,
172
+ handles: {
173
+ start: startNormalized,
174
+ end: endNormalized,
175
+ width: widthNormalized,
176
+ },
177
+ start: toPixels(startNormalized),
178
+ end: toPixels(endNormalized),
179
+ center,
180
+ rx: Math.hypot(vertexDx, vertexDy),
181
+ ry: Math.hypot(covertex.x - center.x, covertex.y - center.y),
182
+ rotationDeg: (Math.atan2(vertexDy, vertexDx) * 180) / Math.PI,
183
+ fromDeg: (Math.atan2(-transform.m10, transform.m00) * 180) / Math.PI,
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Derive REST-style `GradientHandles` from a 2×3 **node-to-gradient**
189
+ * transform in REST/Plugin API nested-array form.
190
+ */
191
+ export function handlePositionsFromArrayTransform(
192
+ t: Mat2x3Array,
193
+ ): GradientHandles | null {
194
+ return handlePositionsFromObjectTransform(mat2x3FromArray(t));
195
+ }
196
+
197
+ // ---------------------------------------------------------------------------
198
+ // Gradient geometry from REST handle positions
199
+ // ---------------------------------------------------------------------------
200
+
201
+ /**
202
+ * Resolve `GradientHandles` from a raw `gradientHandlePositions` array as
203
+ * returned by the Figma REST API. Returns null when the array is too short.
204
+ */
205
+ export function resolveGradientHandles(
206
+ gradientHandlePositions: Array<Vec2> | undefined,
207
+ ): GradientHandles | null {
208
+ if (!gradientHandlePositions || gradientHandlePositions.length < 3)
209
+ return null;
210
+ return {
211
+ start: gradientHandlePositions[0]!,
212
+ end: gradientHandlePositions[1]!,
213
+ width: gradientHandlePositions[2]!,
214
+ };
215
+ }
216
+
217
+ // ---------------------------------------------------------------------------
218
+ // Gradient angle
219
+ // ---------------------------------------------------------------------------
220
+
221
+ /**
222
+ * Derive a CSS `linear-gradient()` angle (degrees) from Figma's normalized
223
+ * `gradientHandlePositions`. Handle positions are normalized independently in
224
+ * x and y (0..1 relative to the node's bounding box), so the angle must be
225
+ * computed in actual pixel space using the node's real width/height —
226
+ * otherwise a non-square box silently distorts the angle.
227
+ *
228
+ * Identity: left-to-right handles (start=(0,0.5), end=(1,0.5)) → 90 deg.
229
+ * Top-to-bottom handles (start=(0.5,0), end=(0.5,1)) → 180 deg.
230
+ *
231
+ * This is the same function previously inlined in figma-node-to-html.ts and
232
+ * is preserved here verbatim for public API compatibility.
233
+ */
234
+ export function gradientAngleDegrees(
235
+ paint: { gradientHandlePositions?: Array<Vec2> },
236
+ box: { width: number; height: number },
237
+ ): number | null {
238
+ const handles = resolveGradientHandles(paint.gradientHandlePositions);
239
+ if (!handles) return null;
240
+ return gradientAngleDegreesFromHandles(handles, box);
241
+ }
242
+
243
+ /**
244
+ * Same calculation as `gradientAngleDegrees` but accepts already-resolved
245
+ * `GradientHandles` — useful when handles come from a transform inversion.
246
+ */
247
+ export function gradientAngleDegreesFromHandles(
248
+ handles: GradientHandles,
249
+ box: { width: number; height: number },
250
+ ): number {
251
+ const dx = (handles.end.x - handles.start.x) * box.width;
252
+ const dy = (handles.end.y - handles.start.y) * box.height;
253
+ const angleRad = Math.atan2(dy, dx);
254
+ const angleDeg = (angleRad * 180) / Math.PI + 90;
255
+ return ((angleDeg % 360) + 360) % 360;
256
+ }
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // Linear stop position remapping
260
+ // ---------------------------------------------------------------------------
261
+
262
+ /**
263
+ * CSS `linear-gradient(angle, ...)` always stretches its 0%/100% stops across
264
+ * the box's full diagonal at that angle (the CSS "gradient line" always spans
265
+ * corner-to-corner). Figma's stop positions are fractions of the literal
266
+ * handle-to-handle distance, which only coincides with the CSS span when the
267
+ * handles are dragged exactly corner-to-corner.
268
+ *
269
+ * This function returns a remap closure that projects each Figma stop's real
270
+ * pixel position onto the CSS gradient line and re-expresses it as a
271
+ * percentage of the CSS line's length, so a partial/offset gradient renders at
272
+ * the same pixel positions Figma draws it at.
273
+ */
274
+ export function remapLinearStopPosition(
275
+ handles: GradientHandles,
276
+ box: { width: number; height: number },
277
+ angleDeg: number,
278
+ ): (position: number) => number {
279
+ const angleRad = (angleDeg * Math.PI) / 180;
280
+ const ux = Math.sin(angleRad);
281
+ const uy = -Math.cos(angleRad);
282
+ const lineLength = box.width * Math.abs(ux) + box.height * Math.abs(uy);
283
+ if (lineLength < 1e-6) return (position) => position;
284
+ const startPx = {
285
+ x: handles.start.x * box.width,
286
+ y: handles.start.y * box.height,
287
+ };
288
+ const endPx = { x: handles.end.x * box.width, y: handles.end.y * box.height };
289
+ const cx = box.width / 2;
290
+ const cy = box.height / 2;
291
+ return (position: number): number => {
292
+ const px = startPx.x + position * (endPx.x - startPx.x);
293
+ const py = startPx.y + position * (endPx.y - startPx.y);
294
+ const projected = (px - cx) * ux + (py - cy) * uy;
295
+ return (projected + lineLength / 2) / lineLength;
296
+ };
297
+ }
298
+
299
+ // ---------------------------------------------------------------------------
300
+ // Vector length (pixel-space)
301
+ // ---------------------------------------------------------------------------
302
+
303
+ /**
304
+ * Euclidean distance between two normalized-coordinate points after scaling
305
+ * into actual pixel space.
306
+ */
307
+ export function vectorLength(
308
+ from: Vec2,
309
+ to: Vec2,
310
+ box: { width: number; height: number },
311
+ ): number {
312
+ const dx = (to.x - from.x) * box.width;
313
+ const dy = (to.y - from.y) * box.height;
314
+ return Math.sqrt(dx * dx + dy * dy);
315
+ }
316
+
317
+ // ---------------------------------------------------------------------------
318
+ // CSS blend mode mapping
319
+ // ---------------------------------------------------------------------------
320
+
321
+ const CSS_BLEND_MODES = new Set([
322
+ "multiply",
323
+ "screen",
324
+ "overlay",
325
+ "darken",
326
+ "lighten",
327
+ "color-dodge",
328
+ "color-burn",
329
+ "hard-light",
330
+ "soft-light",
331
+ "difference",
332
+ "exclusion",
333
+ "hue",
334
+ "saturation",
335
+ "color",
336
+ "luminosity",
337
+ ]);
338
+
339
+ /**
340
+ * Figma-only blend modes that have no exact CSS equivalent. The value is the
341
+ * closest CSS mode (approximation) and callers should record the verdict.
342
+ */
343
+ const FIGMA_ONLY_BLEND_MODE_FALLBACK: Record<string, string> = {
344
+ LINEAR_BURN: "plus-darker",
345
+ LINEAR_DODGE: "plus-lighter",
346
+ LIGHTER: "plus-lighter",
347
+ DARKER: "darken",
348
+ };
349
+
350
+ /**
351
+ * Map a Figma blend mode string to a CSS `mix-blend-mode` value with an
352
+ * explicit fidelity verdict:
353
+ * - `"exact"` — CSS supports the mode natively.
354
+ * - `"approximated"` — mapped to the closest CSS equivalent.
355
+ *
356
+ * Returns `null` for `PASS_THROUGH`, `NORMAL`, and unrecognised modes (caller
357
+ * should omit the CSS property entirely in those cases).
358
+ */
359
+ export function cssBlendMode(figmaBlendMode: string): BlendModeResult | null {
360
+ if (
361
+ !figmaBlendMode ||
362
+ figmaBlendMode === "PASS_THROUGH" ||
363
+ figmaBlendMode === "NORMAL"
364
+ )
365
+ return null;
366
+ const cssMode = figmaBlendMode.toLowerCase().replace(/_/g, "-");
367
+ if (CSS_BLEND_MODES.has(cssMode)) return { cssMode, verdict: "exact" };
368
+ const fallback = FIGMA_ONLY_BLEND_MODE_FALLBACK[figmaBlendMode];
369
+ if (fallback) return { cssMode: fallback, verdict: "approximated" };
370
+ return null;
371
+ }
@@ -53,6 +53,26 @@ export {
53
53
  type FigmaRgba,
54
54
  type SummarizeFigmaNodeResult,
55
55
  } from "./figma.js";
56
+ export {
57
+ cssBlendMode,
58
+ gradientAngleDegreesFromHandles,
59
+ gradientGeometryFromTransform,
60
+ handlePositionsFromArrayTransform,
61
+ handlePositionsFromObjectTransform,
62
+ invert2x3,
63
+ mat2x3FromArray,
64
+ remapLinearStopPosition,
65
+ resolveGradientHandles,
66
+ vectorLength,
67
+ type BlendModeResult,
68
+ type BlendVerdict,
69
+ type GradientGeometry,
70
+ type GradientHandles,
71
+ type GradientKind,
72
+ type Mat2x3Array,
73
+ type Mat2x3Object,
74
+ type Vec2,
75
+ } from "./figma-paint-math.js";
56
76
  export {
57
77
  assertFigmaNodeTreeComplexity,
58
78
  collectFallbackNodeIds,
@@ -18,6 +18,7 @@ const messages = {
18
18
  },
19
19
  settings: {
20
20
  title: "Settings",
21
+ pageTitle: "Settings",
21
22
  description: "Language and workspace preferences for this app.",
22
23
  languageTitle: "Language",
23
24
  languageDescription:
@@ -36,6 +36,7 @@ export interface SendEmailArgs {
36
36
  inReplyTo?: string;
37
37
  references?: string;
38
38
  attachments?: EmailAttachment[];
39
+ timeoutMs?: number;
39
40
  }
40
41
 
41
42
  let cachedAgentNativeLogo: Buffer | undefined;
@@ -125,8 +126,12 @@ function getFromAddress(
125
126
  return "Agent Native <onboarding@resend.dev>";
126
127
  }
127
128
 
128
- export async function sendEmail(args: SendEmailArgs): Promise<void> {
129
+ async function sendEmailWithSignal(
130
+ args: SendEmailArgs,
131
+ signal?: AbortSignal,
132
+ ): Promise<void> {
129
133
  const config = await resolveEmailTransport();
134
+ signal?.throwIfAborted();
130
135
  const provider = config.provider;
131
136
  const from = getFromAddress(config, args.from);
132
137
  const attachments = resolveAttachments(args);
@@ -164,6 +169,7 @@ export async function sendEmail(args: SendEmailArgs): Promise<void> {
164
169
  "Content-Type": "application/json",
165
170
  },
166
171
  body: JSON.stringify(payload),
172
+ signal,
167
173
  });
168
174
  if (!res.ok) {
169
175
  const body = await res.text().catch(() => "");
@@ -215,6 +221,7 @@ export async function sendEmail(args: SendEmailArgs): Promise<void> {
215
221
  "Content-Type": "application/json",
216
222
  },
217
223
  body: JSON.stringify(sgPayload),
224
+ signal,
218
225
  });
219
226
  if (!res.ok) {
220
227
  const body = await res.text().catch(() => "");
@@ -239,6 +246,31 @@ export async function sendEmail(args: SendEmailArgs): Promise<void> {
239
246
  );
240
247
  }
241
248
 
249
+ export async function sendEmail(args: SendEmailArgs): Promise<void> {
250
+ const requestedTimeoutMs = Number(args.timeoutMs);
251
+ if (!Number.isFinite(requestedTimeoutMs) || requestedTimeoutMs <= 0) {
252
+ return sendEmailWithSignal(args);
253
+ }
254
+
255
+ const timeoutMs = Math.floor(requestedTimeoutMs);
256
+ const controller = new AbortController();
257
+ const timeoutError = new Error(`Email send timed out after ${timeoutMs}ms`);
258
+ let timeout: ReturnType<typeof setTimeout> | undefined;
259
+ try {
260
+ await Promise.race([
261
+ sendEmailWithSignal(args, controller.signal),
262
+ new Promise<never>((_, reject) => {
263
+ timeout = setTimeout(() => {
264
+ controller.abort(timeoutError);
265
+ reject(timeoutError);
266
+ }, timeoutMs);
267
+ }),
268
+ ]);
269
+ } finally {
270
+ if (timeout) clearTimeout(timeout);
271
+ }
272
+ }
273
+
242
274
  function parseSendGridFrom(from: string): { email: string; name?: string } {
243
275
  const m = from.match(/^\s*(.*?)\s*<(.+)>\s*$/);
244
276
  if (m && m[2]) return { name: m[1] || undefined, email: m[2] };
@@ -139,6 +139,28 @@
139
139
  background: inherit;
140
140
  }
141
141
 
142
+ .message-scroller-viewport--top-fade {
143
+ --message-scroller-top-fade-size: 48px;
144
+ -webkit-mask-image: linear-gradient(
145
+ to bottom,
146
+ transparent 0,
147
+ black var(--message-scroller-top-fade-size),
148
+ black 100%
149
+ );
150
+ mask-image: linear-gradient(
151
+ to bottom,
152
+ transparent 0,
153
+ black var(--message-scroller-top-fade-size),
154
+ black 100%
155
+ );
156
+ -webkit-mask-position: 0 0;
157
+ mask-position: 0 0;
158
+ -webkit-mask-repeat: no-repeat;
159
+ mask-repeat: no-repeat;
160
+ -webkit-mask-size: 100% 100%;
161
+ mask-size: 100% 100%;
162
+ }
163
+
142
164
  .agent-sidebar-resize-handle {
143
165
  background: transparent;
144
166
  }
@@ -888,6 +910,7 @@
888
910
 
889
911
  [data-agent-empty-state="centered"] {
890
912
  justify-content: center;
913
+ padding-bottom: clamp(3rem, 8vh, 6rem);
891
914
  }
892
915
 
893
916
  [data-agent-empty-state="centered"] > .agent-chat-scroll {
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -1,5 +1,6 @@
1
1
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
2
2
  import {
3
+ AccountSettingsCard,
3
4
  SettingsTabsPage,
4
5
  useAgentSettingsTabs,
5
6
  } from "@agent-native/core/client/settings";
@@ -32,6 +33,7 @@ export default function SettingsPage() {
32
33
  </div>
33
34
 
34
35
  <SettingsTabsPage
36
+ account={<AccountSettingsCard />}
35
37
  extraTabs={agentSettingsTabs}
36
38
  general={
37
39
  <section className="mx-auto w-full max-w-2xl rounded-lg border border-border/50 bg-card px-4 py-4">
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.