@agent-native/core 0.117.2 → 0.118.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 (524) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +30 -0
  3. package/corpus/core/docs/content/custom-design-system.mdx +445 -0
  4. package/corpus/core/docs/content/toolkit-ui.mdx +16 -0
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/types.ts +1 -0
  7. package/corpus/core/src/agent/types.ts +1 -1
  8. package/corpus/core/src/client/AgentPanel.tsx +90 -107
  9. package/corpus/core/src/client/AssistantChat.tsx +30 -13
  10. package/corpus/core/src/client/CommandMenu.tsx +68 -178
  11. package/corpus/core/src/client/agent-chat-adapter.ts +3 -1
  12. package/corpus/core/src/client/app-providers.tsx +1 -1
  13. package/corpus/core/src/client/chat/message-components.tsx +116 -10
  14. package/corpus/core/src/client/chat/tool-call-display.tsx +26 -5
  15. package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +14 -40
  16. package/corpus/core/src/client/components/ui/message-scroller.tsx +15 -1
  17. package/corpus/core/src/client/i18n.tsx +22 -0
  18. package/corpus/core/src/client/index.ts +17 -0
  19. package/corpus/core/src/client/org/TeamPage.tsx +257 -153
  20. package/corpus/core/src/client/progress/RunsTray.tsx +89 -88
  21. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +5 -2
  22. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +34 -13
  23. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +13 -0
  24. package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -18
  25. package/corpus/core/src/client/settings/SecretsSection.tsx +97 -52
  26. package/corpus/core/src/client/settings/SettingsPanel.tsx +202 -246
  27. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +23 -2
  28. package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +7 -18
  29. package/corpus/core/src/client/settings/index.ts +7 -0
  30. package/corpus/core/src/client/settings/useSettingsPanelController.ts +155 -0
  31. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +116 -51
  32. package/corpus/core/src/client/setup-connections/index.ts +10 -0
  33. package/corpus/core/src/client/setup-connections/useBuilderConnectCardController.ts +82 -0
  34. package/corpus/core/src/client/sharing/ShareButton.tsx +64 -586
  35. package/corpus/core/src/client/sharing/ShareDialog.tsx +299 -633
  36. package/corpus/core/src/client/sharing/index.ts +42 -0
  37. package/corpus/core/src/client/sharing/share-controller-helpers.ts +318 -0
  38. package/corpus/core/src/client/sharing/useShareButtonController.ts +600 -0
  39. package/corpus/core/src/client/sharing/useShareDialogController.ts +559 -0
  40. package/corpus/core/src/client/sse-event-processor.ts +23 -0
  41. package/corpus/core/src/client/tool-display.ts +71 -0
  42. package/corpus/core/src/client/use-agent-engine-configured.ts +12 -19
  43. package/corpus/core/src/client/use-chat-threads.ts +33 -12
  44. package/corpus/core/src/localization/default-messages.ts +5 -0
  45. package/corpus/core/src/mcp-client/oauth-routes.ts +16 -2
  46. package/corpus/core/src/scripts/call-agent.ts +5 -1
  47. package/corpus/core/src/server/realtime-voice.ts +1 -1
  48. package/corpus/core/src/styles/agent-native.css +0 -24
  49. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  50. package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  51. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +22 -2
  52. package/corpus/core/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +28 -4
  53. package/corpus/core/src/templates/default/app/components/ui/toolkit-provider.tsx +10 -0
  54. package/corpus/core/src/templates/default/app/design-system.ts +3 -0
  55. package/corpus/core/src/templates/default/app/root.tsx +5 -2
  56. package/corpus/core/src/templates/default/package.json +1 -0
  57. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  58. package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  59. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  60. package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  61. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +22 -2
  62. package/corpus/core/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +28 -4
  63. package/corpus/core/src/vite/client.ts +39 -0
  64. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  65. package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  66. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +22 -2
  67. package/corpus/templates/analytics/.agents/skills/shadcn-ui/SKILL.md +28 -4
  68. package/corpus/templates/analytics/app/components/ui/toolkit-provider.tsx +6 -1
  69. package/corpus/templates/analytics/app/design-system.ts +3 -0
  70. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  71. package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  72. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +22 -2
  73. package/corpus/templates/assets/.agents/skills/shadcn-ui/SKILL.md +28 -4
  74. package/corpus/templates/assets/app/components/ui/alert.tsx +1 -0
  75. package/corpus/templates/assets/app/components/ui/toolkit-provider.tsx +6 -1
  76. package/corpus/templates/assets/app/design-system.ts +3 -0
  77. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +26 -30
  78. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  79. package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  80. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +22 -2
  81. package/corpus/templates/brain/.agents/skills/shadcn-ui/SKILL.md +28 -4
  82. package/corpus/templates/brain/app/components/ui/dialog.tsx +1 -0
  83. package/corpus/templates/brain/app/components/ui/toolkit-provider.tsx +6 -1
  84. package/corpus/templates/brain/app/design-system.ts +3 -0
  85. package/corpus/templates/brain/app/routes/sources.tsx +8 -8
  86. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  87. package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  88. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +22 -2
  89. package/corpus/templates/calendar/.agents/skills/shadcn-ui/SKILL.md +28 -4
  90. package/corpus/templates/calendar/app/components/ui/toolkit-provider.tsx +6 -1
  91. package/corpus/templates/calendar/app/design-system.ts +3 -0
  92. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  93. package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  94. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +22 -2
  95. package/corpus/templates/chat/.agents/skills/shadcn-ui/SKILL.md +28 -4
  96. package/corpus/templates/chat/app/components/ui/toolkit-provider.tsx +6 -1
  97. package/corpus/templates/chat/app/design-system.ts +3 -0
  98. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  99. package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  100. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +22 -2
  101. package/corpus/templates/clips/.agents/skills/shadcn-ui/SKILL.md +28 -4
  102. package/corpus/templates/clips/app/components/ui/toolkit-provider.tsx +6 -1
  103. package/corpus/templates/clips/app/design-system.ts +3 -0
  104. package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -1
  105. package/corpus/templates/clips/changelog/2026-07-22-fixed-windows-recordings-aborting-during-the-countdown.md +6 -0
  106. package/corpus/templates/clips/changelog/2026-07-22-permissions-and-rewind-controls-are-now-grouped-with-clearer.md +6 -0
  107. package/corpus/templates/clips/changelog/2026-07-22-the-desktop-popover-opens-centered-under-the-menu-bar-icon-o.md +6 -0
  108. package/corpus/templates/clips/desktop/src/app.tsx +86 -82
  109. package/corpus/templates/clips/desktop/src/styles.css +13 -0
  110. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +10 -1
  111. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +37 -2
  112. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -0
  113. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  114. package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  115. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +22 -2
  116. package/corpus/templates/content/.agents/skills/shadcn-ui/SKILL.md +28 -4
  117. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +41 -59
  118. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +41 -41
  119. package/corpus/templates/content/app/components/editor/database/GalleryView.tsx +2 -1
  120. package/corpus/templates/content/app/components/editor/database/ListView.tsx +2 -1
  121. package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +9 -9
  122. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +7 -7
  123. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +11 -11
  124. package/corpus/templates/content/app/components/ui/checkbox.tsx +1 -0
  125. package/corpus/templates/content/app/components/ui/collapsible.tsx +1 -0
  126. package/corpus/templates/content/app/components/ui/toolkit-provider.tsx +6 -1
  127. package/corpus/templates/content/app/design-system.ts +3 -0
  128. package/corpus/templates/content/app/global.css +29 -3
  129. package/corpus/templates/content/changelog/2026-07-22-page-actions-are-grouped-into-the-three-dots-menu-for-a-clea.md +6 -0
  130. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  131. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  132. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +10 -10
  133. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +1 -6
  134. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +3 -2
  135. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +13 -12
  136. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +17 -17
  137. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +8 -8
  138. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +3 -8
  139. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +2 -1
  140. package/corpus/templates/crm/app/components/crm/Surface.tsx +3 -2
  141. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +2 -2
  142. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +7 -7
  143. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +1 -1
  144. package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +1 -0
  145. package/corpus/templates/crm/app/components/ui/badge.tsx +1 -0
  146. package/corpus/templates/crm/app/components/ui/button.tsx +1 -0
  147. package/corpus/templates/crm/app/components/ui/card.tsx +1 -0
  148. package/corpus/templates/crm/app/components/ui/dialog.tsx +1 -0
  149. package/corpus/templates/crm/app/components/ui/input.tsx +1 -0
  150. package/corpus/templates/crm/app/components/ui/label.tsx +1 -0
  151. package/corpus/templates/crm/app/components/ui/select.tsx +1 -0
  152. package/corpus/templates/crm/app/components/ui/sheet.tsx +1 -0
  153. package/corpus/templates/crm/app/components/ui/skeleton.tsx +1 -0
  154. package/corpus/templates/crm/app/components/ui/switch.tsx +1 -0
  155. package/corpus/templates/crm/app/components/ui/table.tsx +1 -0
  156. package/corpus/templates/crm/app/components/ui/tabs.tsx +1 -0
  157. package/corpus/templates/crm/app/components/ui/textarea.tsx +1 -0
  158. package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +17 -0
  159. package/corpus/templates/crm/app/design-system.ts +3 -0
  160. package/corpus/templates/crm/app/root.tsx +9 -6
  161. package/corpus/templates/crm/app/routes/dashboard.tsx +1 -1
  162. package/corpus/templates/crm/app/routes/proposals.tsx +13 -13
  163. package/corpus/templates/crm/app/routes/setup.tsx +9 -9
  164. package/corpus/templates/crm/app/routes/tasks.tsx +14 -14
  165. package/corpus/templates/crm/app/routes/views.tsx +16 -16
  166. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  167. package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  168. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +22 -2
  169. package/corpus/templates/design/.agents/skills/shadcn-ui/SKILL.md +28 -4
  170. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +8 -8
  171. package/corpus/templates/design/app/components/ui/command.tsx +1 -0
  172. package/corpus/templates/design/app/components/ui/toolkit-provider.tsx +6 -1
  173. package/corpus/templates/design/app/design-system.ts +3 -0
  174. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  175. package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  176. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +22 -2
  177. package/corpus/templates/dispatch/.agents/skills/shadcn-ui/SKILL.md +28 -4
  178. package/corpus/templates/dispatch/app/components/ui/button.tsx +1 -0
  179. package/corpus/templates/dispatch/app/components/ui/toolkit-provider.tsx +17 -0
  180. package/corpus/templates/dispatch/app/design-system.ts +3 -0
  181. package/corpus/templates/dispatch/app/root.tsx +10 -7
  182. package/corpus/templates/dispatch/package.json +1 -0
  183. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  184. package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  185. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +22 -2
  186. package/corpus/templates/forms/.agents/skills/shadcn-ui/SKILL.md +28 -4
  187. package/corpus/templates/forms/app/components/ui/toolkit-provider.tsx +6 -1
  188. package/corpus/templates/forms/app/design-system.ts +3 -0
  189. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  190. package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  191. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +22 -2
  192. package/corpus/templates/macros/.agents/skills/shadcn-ui/SKILL.md +28 -4
  193. package/corpus/templates/macros/app/components/ui/toolkit-provider.tsx +6 -1
  194. package/corpus/templates/macros/app/design-system.ts +3 -0
  195. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  196. package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  197. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +22 -2
  198. package/corpus/templates/mail/.agents/skills/shadcn-ui/SKILL.md +28 -4
  199. package/corpus/templates/mail/app/components/email/EmailThread.tsx +134 -93
  200. package/corpus/templates/mail/app/components/email/email-iframe-document.ts +8 -2
  201. package/corpus/templates/mail/app/components/ui/textarea.tsx +1 -21
  202. package/corpus/templates/mail/app/components/ui/toolkit-provider.tsx +6 -1
  203. package/corpus/templates/mail/app/design-system.ts +3 -0
  204. package/corpus/templates/mail/changelog/2026-07-22-email-messages-no-longer-flash-with-an-unstyled-page-while-l.md +6 -0
  205. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  206. package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  207. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +22 -2
  208. package/corpus/templates/plan/.agents/skills/shadcn-ui/SKILL.md +28 -4
  209. package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +1 -1
  210. package/corpus/templates/plan/app/components/ui/toolkit-provider.tsx +6 -1
  211. package/corpus/templates/plan/app/design-system.ts +3 -0
  212. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  213. package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  214. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +22 -2
  215. package/corpus/templates/slides/.agents/skills/shadcn-ui/SKILL.md +28 -4
  216. package/corpus/templates/slides/app/components/ui/toolkit-provider.tsx +6 -1
  217. package/corpus/templates/slides/app/design-system.ts +3 -0
  218. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  219. package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  220. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +22 -2
  221. package/corpus/templates/tasks/.agents/skills/shadcn-ui/SKILL.md +28 -4
  222. package/corpus/templates/tasks/app/components/ui/toolkit-provider.tsx +17 -0
  223. package/corpus/templates/tasks/app/design-system.ts +3 -0
  224. package/corpus/templates/tasks/app/root.tsx +5 -2
  225. package/corpus/toolkit/CHANGELOG.md +14 -0
  226. package/corpus/toolkit/agent-native.eject.json +71 -0
  227. package/corpus/toolkit/package.json +31 -1
  228. package/corpus/toolkit/src/chat-history/ChatHistoryRail.tsx +100 -39
  229. package/corpus/toolkit/src/chat-history/index.ts +9 -0
  230. package/corpus/toolkit/src/chat-history/useChatHistoryRailController.ts +76 -0
  231. package/corpus/toolkit/src/chat-history.css +2 -0
  232. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +11 -8
  233. package/corpus/toolkit/src/composer/PromptComposer.tsx +27 -6
  234. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +107 -22
  235. package/corpus/toolkit/src/conformance/__fixtures__/css-in-js-adapter.tsx +401 -0
  236. package/corpus/toolkit/src/conformance/index.ts +3 -0
  237. package/corpus/toolkit/src/conformance/runner.tsx +710 -0
  238. package/corpus/toolkit/src/conformance/types.ts +45 -0
  239. package/corpus/toolkit/src/conformance/version.ts +22 -0
  240. package/corpus/toolkit/src/design-system/components.tsx +146 -0
  241. package/corpus/toolkit/src/design-system/context.tsx +31 -0
  242. package/corpus/toolkit/src/design-system/default-adapter.tsx +915 -0
  243. package/corpus/toolkit/src/design-system/definition.ts +14 -0
  244. package/corpus/toolkit/src/design-system/error-boundary.tsx +37 -0
  245. package/corpus/toolkit/src/design-system/index.ts +7 -0
  246. package/corpus/toolkit/src/design-system/theme.ts +145 -0
  247. package/corpus/toolkit/src/design-system/types.ts +363 -0
  248. package/corpus/toolkit/src/provider.tsx +98 -5
  249. package/corpus/toolkit/src/ui/button.tsx +95 -6
  250. package/corpus/toolkit/src/ui/dialog.tsx +48 -23
  251. package/corpus/toolkit/src/ui/popover.tsx +16 -2
  252. package/corpus/toolkit/src/ui/select.tsx +7 -3
  253. package/corpus/toolkit/src/ui/sidebar.tsx +47 -48
  254. package/corpus/toolkit/src/ui/tooltip.tsx +7 -3
  255. package/dist/a2a/types.d.ts +1 -1
  256. package/dist/a2a/types.d.ts.map +1 -1
  257. package/dist/a2a/types.js.map +1 -1
  258. package/dist/agent/types.d.ts +1 -1
  259. package/dist/agent/types.d.ts.map +1 -1
  260. package/dist/agent/types.js.map +1 -1
  261. package/dist/client/AgentPanel.d.ts.map +1 -1
  262. package/dist/client/AgentPanel.js +14 -14
  263. package/dist/client/AgentPanel.js.map +1 -1
  264. package/dist/client/AssistantChat.d.ts.map +1 -1
  265. package/dist/client/AssistantChat.js +26 -15
  266. package/dist/client/AssistantChat.js.map +1 -1
  267. package/dist/client/CommandMenu.d.ts.map +1 -1
  268. package/dist/client/CommandMenu.js +18 -106
  269. package/dist/client/CommandMenu.js.map +1 -1
  270. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  271. package/dist/client/agent-chat-adapter.js +2 -1
  272. package/dist/client/agent-chat-adapter.js.map +1 -1
  273. package/dist/client/app-providers.d.ts.map +1 -1
  274. package/dist/client/app-providers.js +1 -1
  275. package/dist/client/app-providers.js.map +1 -1
  276. package/dist/client/chat/message-components.d.ts +9 -1
  277. package/dist/client/chat/message-components.d.ts.map +1 -1
  278. package/dist/client/chat/message-components.js +62 -7
  279. package/dist/client/chat/message-components.js.map +1 -1
  280. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  281. package/dist/client/chat/tool-call-display.js +20 -5
  282. package/dist/client/chat/tool-call-display.js.map +1 -1
  283. package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -1
  284. package/dist/client/chat/widgets/InlineExtensionWidget.js +9 -12
  285. package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -1
  286. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  287. package/dist/client/components/ui/message-scroller.d.ts.map +1 -1
  288. package/dist/client/components/ui/message-scroller.js +4 -3
  289. package/dist/client/components/ui/message-scroller.js.map +1 -1
  290. package/dist/client/i18n.d.ts.map +1 -1
  291. package/dist/client/i18n.js +9 -0
  292. package/dist/client/i18n.js.map +1 -1
  293. package/dist/client/index.d.ts +2 -2
  294. package/dist/client/index.d.ts.map +1 -1
  295. package/dist/client/index.js +2 -2
  296. package/dist/client/index.js.map +1 -1
  297. package/dist/client/org/TeamPage.d.ts.map +1 -1
  298. package/dist/client/org/TeamPage.js +41 -33
  299. package/dist/client/org/TeamPage.js.map +1 -1
  300. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  301. package/dist/client/progress/RunsTray.js +9 -10
  302. package/dist/client/progress/RunsTray.js.map +1 -1
  303. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  304. package/dist/client/resources/McpConnectionSuggestion.js +5 -4
  305. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  306. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  307. package/dist/client/resources/McpIntegrationDialog.js +10 -8
  308. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  309. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  310. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  311. package/dist/client/resources/mcp-integration-catalog.js +13 -0
  312. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  313. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  314. package/dist/client/settings/DemoModeSection.js +2 -5
  315. package/dist/client/settings/DemoModeSection.js.map +1 -1
  316. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  317. package/dist/client/settings/SecretsSection.js +18 -6
  318. package/dist/client/settings/SecretsSection.js.map +1 -1
  319. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  320. package/dist/client/settings/SettingsPanel.js +52 -117
  321. package/dist/client/settings/SettingsPanel.js.map +1 -1
  322. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  323. package/dist/client/settings/SettingsTabsPage.js +16 -1
  324. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  325. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  326. package/dist/client/settings/VoiceTranscriptionSection.js +2 -5
  327. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  328. package/dist/client/settings/index.d.ts +1 -0
  329. package/dist/client/settings/index.d.ts.map +1 -1
  330. package/dist/client/settings/index.js +1 -0
  331. package/dist/client/settings/index.js.map +1 -1
  332. package/dist/client/settings/useSettingsPanelController.d.ts +22 -0
  333. package/dist/client/settings/useSettingsPanelController.d.ts.map +1 -0
  334. package/dist/client/settings/useSettingsPanelController.js +103 -0
  335. package/dist/client/settings/useSettingsPanelController.js.map +1 -0
  336. package/dist/client/setup-connections/BuilderConnectCard.d.ts +14 -6
  337. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
  338. package/dist/client/setup-connections/BuilderConnectCard.js +24 -16
  339. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
  340. package/dist/client/setup-connections/index.d.ts +2 -1
  341. package/dist/client/setup-connections/index.d.ts.map +1 -1
  342. package/dist/client/setup-connections/index.js +2 -1
  343. package/dist/client/setup-connections/index.js.map +1 -1
  344. package/dist/client/setup-connections/useBuilderConnectCardController.d.ts +34 -0
  345. package/dist/client/setup-connections/useBuilderConnectCardController.d.ts.map +1 -0
  346. package/dist/client/setup-connections/useBuilderConnectCardController.js +39 -0
  347. package/dist/client/setup-connections/useBuilderConnectCardController.js.map +1 -0
  348. package/dist/client/sharing/ShareButton.d.ts +2 -1
  349. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  350. package/dist/client/sharing/ShareButton.js +17 -463
  351. package/dist/client/sharing/ShareButton.js.map +1 -1
  352. package/dist/client/sharing/ShareDialog.d.ts +6 -6
  353. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  354. package/dist/client/sharing/ShareDialog.js +69 -283
  355. package/dist/client/sharing/ShareDialog.js.map +1 -1
  356. package/dist/client/sharing/index.d.ts +3 -0
  357. package/dist/client/sharing/index.d.ts.map +1 -1
  358. package/dist/client/sharing/index.js +3 -0
  359. package/dist/client/sharing/index.js.map +1 -1
  360. package/dist/client/sharing/share-controller-helpers.d.ts +70 -0
  361. package/dist/client/sharing/share-controller-helpers.d.ts.map +1 -0
  362. package/dist/client/sharing/share-controller-helpers.js +224 -0
  363. package/dist/client/sharing/share-controller-helpers.js.map +1 -0
  364. package/dist/client/sharing/useShareButtonController.d.ts +91 -0
  365. package/dist/client/sharing/useShareButtonController.d.ts.map +1 -0
  366. package/dist/client/sharing/useShareButtonController.js +397 -0
  367. package/dist/client/sharing/useShareButtonController.js.map +1 -0
  368. package/dist/client/sharing/useShareDialogController.d.ts +109 -0
  369. package/dist/client/sharing/useShareDialogController.d.ts.map +1 -0
  370. package/dist/client/sharing/useShareDialogController.js +348 -0
  371. package/dist/client/sharing/useShareDialogController.js.map +1 -0
  372. package/dist/client/sse-event-processor.d.ts.map +1 -1
  373. package/dist/client/sse-event-processor.js +21 -0
  374. package/dist/client/sse-event-processor.js.map +1 -1
  375. package/dist/client/tool-display.d.ts +16 -0
  376. package/dist/client/tool-display.d.ts.map +1 -1
  377. package/dist/client/tool-display.js +52 -0
  378. package/dist/client/tool-display.js.map +1 -1
  379. package/dist/client/use-agent-engine-configured.d.ts +3 -2
  380. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  381. package/dist/client/use-agent-engine-configured.js +7 -19
  382. package/dist/client/use-agent-engine-configured.js.map +1 -1
  383. package/dist/client/use-chat-threads.d.ts.map +1 -1
  384. package/dist/client/use-chat-threads.js +21 -8
  385. package/dist/client/use-chat-threads.js.map +1 -1
  386. package/dist/localization/default-messages.d.ts +4 -0
  387. package/dist/localization/default-messages.d.ts.map +1 -1
  388. package/dist/localization/default-messages.js +4 -0
  389. package/dist/localization/default-messages.js.map +1 -1
  390. package/dist/mcp-client/oauth-routes.d.ts +2 -0
  391. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  392. package/dist/mcp-client/oauth-routes.js +12 -2
  393. package/dist/mcp-client/oauth-routes.js.map +1 -1
  394. package/dist/observability/routes.d.ts +3 -3
  395. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  396. package/dist/scripts/call-agent.d.ts.map +1 -1
  397. package/dist/scripts/call-agent.js +5 -1
  398. package/dist/scripts/call-agent.js.map +1 -1
  399. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  400. package/dist/server/realtime-voice.js +1 -1
  401. package/dist/server/realtime-voice.js.map +1 -1
  402. package/dist/server/transcribe-voice.d.ts +1 -1
  403. package/dist/styles/agent-native.css +0 -24
  404. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  405. package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  406. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +22 -2
  407. package/dist/templates/chat/.agents/skills/shadcn-ui/SKILL.md +28 -4
  408. package/dist/templates/chat/app/components/ui/toolkit-provider.tsx +6 -1
  409. package/dist/templates/chat/app/design-system.ts +3 -0
  410. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  411. package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  412. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +22 -2
  413. package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +28 -4
  414. package/dist/templates/default/app/components/ui/toolkit-provider.tsx +10 -0
  415. package/dist/templates/default/app/design-system.ts +3 -0
  416. package/dist/templates/default/app/root.tsx +5 -2
  417. package/dist/templates/default/package.json +1 -0
  418. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  419. package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  420. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  421. package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  422. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +22 -2
  423. package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +28 -4
  424. package/dist/vite/client.d.ts +3 -0
  425. package/dist/vite/client.d.ts.map +1 -1
  426. package/dist/vite/client.js +31 -0
  427. package/dist/vite/client.js.map +1 -1
  428. package/docs/content/custom-design-system.mdx +445 -0
  429. package/docs/content/toolkit-ui.mdx +16 -0
  430. package/package.json +2 -2
  431. package/src/a2a/types.ts +1 -0
  432. package/src/agent/types.ts +1 -1
  433. package/src/client/AgentPanel.tsx +90 -107
  434. package/src/client/AssistantChat.tsx +30 -13
  435. package/src/client/CommandMenu.tsx +68 -178
  436. package/src/client/agent-chat-adapter.ts +3 -1
  437. package/src/client/app-providers.tsx +1 -1
  438. package/src/client/chat/message-components.tsx +116 -10
  439. package/src/client/chat/tool-call-display.tsx +26 -5
  440. package/src/client/chat/widgets/InlineExtensionWidget.tsx +14 -40
  441. package/src/client/components/ui/message-scroller.tsx +15 -1
  442. package/src/client/i18n.tsx +22 -0
  443. package/src/client/index.ts +17 -0
  444. package/src/client/org/TeamPage.tsx +257 -153
  445. package/src/client/progress/RunsTray.tsx +89 -88
  446. package/src/client/resources/McpConnectionSuggestion.tsx +5 -2
  447. package/src/client/resources/McpIntegrationDialog.tsx +34 -13
  448. package/src/client/resources/mcp-integration-catalog.ts +13 -0
  449. package/src/client/settings/DemoModeSection.tsx +6 -18
  450. package/src/client/settings/SecretsSection.tsx +97 -52
  451. package/src/client/settings/SettingsPanel.tsx +202 -246
  452. package/src/client/settings/SettingsTabsPage.tsx +23 -2
  453. package/src/client/settings/VoiceTranscriptionSection.tsx +7 -18
  454. package/src/client/settings/index.ts +7 -0
  455. package/src/client/settings/useSettingsPanelController.ts +155 -0
  456. package/src/client/setup-connections/BuilderConnectCard.tsx +116 -51
  457. package/src/client/setup-connections/index.ts +10 -0
  458. package/src/client/setup-connections/useBuilderConnectCardController.ts +82 -0
  459. package/src/client/sharing/ShareButton.tsx +64 -586
  460. package/src/client/sharing/ShareDialog.tsx +299 -633
  461. package/src/client/sharing/index.ts +42 -0
  462. package/src/client/sharing/share-controller-helpers.ts +318 -0
  463. package/src/client/sharing/useShareButtonController.ts +600 -0
  464. package/src/client/sharing/useShareDialogController.ts +559 -0
  465. package/src/client/sse-event-processor.ts +23 -0
  466. package/src/client/tool-display.ts +71 -0
  467. package/src/client/use-agent-engine-configured.ts +12 -19
  468. package/src/client/use-chat-threads.ts +33 -12
  469. package/src/localization/default-messages.ts +5 -0
  470. package/src/mcp-client/oauth-routes.ts +16 -2
  471. package/src/scripts/call-agent.ts +5 -1
  472. package/src/server/realtime-voice.ts +1 -1
  473. package/src/styles/agent-native.css +0 -24
  474. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  475. package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  476. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +22 -2
  477. package/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +28 -4
  478. package/src/templates/chat/app/components/ui/toolkit-provider.tsx +6 -1
  479. package/src/templates/chat/app/design-system.ts +3 -0
  480. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  481. package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  482. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +22 -2
  483. package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +28 -4
  484. package/src/templates/default/app/components/ui/toolkit-provider.tsx +10 -0
  485. package/src/templates/default/app/design-system.ts +3 -0
  486. package/src/templates/default/app/root.tsx +5 -2
  487. package/src/templates/default/package.json +1 -0
  488. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  489. package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  490. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  491. package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  492. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +22 -2
  493. package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +28 -4
  494. package/src/vite/client.ts +39 -0
  495. package/corpus/templates/analytics/app/components/ui/calendar.tsx +0 -70
  496. package/corpus/templates/analytics/app/components/ui/chart.tsx +0 -386
  497. package/corpus/templates/calendar/app/components/ui/sonner.tsx +0 -58
  498. package/corpus/templates/forms/app/components/ui/calendar.tsx +0 -214
  499. package/corpus/templates/forms/app/components/ui/chart.tsx +0 -379
  500. package/corpus/templates/forms/app/components/ui/command.tsx +0 -153
  501. package/corpus/templates/forms/app/components/ui/context-menu.tsx +0 -198
  502. package/corpus/templates/forms/app/components/ui/menubar.tsx +0 -254
  503. package/corpus/templates/macros/app/components/ui/menubar.tsx +0 -238
  504. package/corpus/templates/macros/app/components/ui/progress.tsx +0 -26
  505. package/corpus/templates/mail/app/components/ui/calendar.tsx +0 -211
  506. package/corpus/templates/mail/app/components/ui/chart.tsx +0 -379
  507. package/corpus/templates/mail/app/components/ui/command.tsx +0 -153
  508. package/corpus/templates/mail/app/components/ui/context-menu.tsx +0 -198
  509. package/corpus/templates/mail/app/components/ui/menubar.tsx +0 -254
  510. package/corpus/templates/tasks/app/components/ui/accordion.tsx +0 -56
  511. package/corpus/templates/tasks/app/components/ui/aspect-ratio.tsx +0 -5
  512. package/corpus/templates/tasks/app/components/ui/breadcrumb.tsx +0 -115
  513. package/corpus/templates/tasks/app/components/ui/calendar.tsx +0 -70
  514. package/corpus/templates/tasks/app/components/ui/carousel.tsx +0 -260
  515. package/corpus/templates/tasks/app/components/ui/chart.tsx +0 -379
  516. package/corpus/templates/tasks/app/components/ui/form.tsx +0 -177
  517. package/corpus/templates/tasks/app/components/ui/hover-card.tsx +0 -27
  518. package/corpus/templates/tasks/app/components/ui/input-otp.tsx +0 -69
  519. package/corpus/templates/tasks/app/components/ui/menubar.tsx +0 -254
  520. package/corpus/templates/tasks/app/components/ui/navigation-menu.tsx +0 -128
  521. package/corpus/templates/tasks/app/components/ui/pagination.tsx +0 -121
  522. package/corpus/templates/tasks/app/components/ui/resizable.tsx +0 -43
  523. package/corpus/templates/tasks/app/components/ui/sidebar.tsx +0 -782
  524. package/corpus/templates/tasks/app/components/ui/toggle.tsx +0 -43
@@ -1,25 +1,35 @@
1
- import * as Select from "@radix-ui/react-select";
2
1
  import {
3
- IconX,
4
- IconTrash,
5
- IconLock,
6
- IconWorld,
2
+ ActionButton,
3
+ Avatar as DesignSystemAvatar,
4
+ Dialog as DesignSystemDialog,
5
+ IconButton,
6
+ Picker,
7
+ Status,
8
+ TextArea,
9
+ TextField,
10
+ } from "@agent-native/toolkit/design-system";
11
+ import {
7
12
  IconCheck,
13
+ IconCode,
8
14
  IconCopy,
9
15
  IconLink,
16
+ IconLock,
10
17
  IconMail,
11
- IconCode,
12
- IconChevronDown,
18
+ IconTrash,
13
19
  IconUsersGroup,
20
+ IconWorld,
14
21
  } from "@tabler/icons-react";
15
- import { useEffect, useState, type ReactNode } from "react";
16
- import { createPortal } from "react-dom";
22
+ import type { ReactNode } from "react";
17
23
 
18
- import { agentNativePath } from "../api-path.js";
19
- import { writeClipboardText } from "../clipboard.js";
20
- import { useT } from "../i18n.js";
21
- import { useActionQuery, useActionMutation } from "../use-action.js";
22
24
  import { cn } from "../utils.js";
25
+ import {
26
+ useShareDialogController,
27
+ type ResourceShare,
28
+ type ShareDialogController,
29
+ type ShareDialogPerson,
30
+ type ShareDialogTab,
31
+ type ShareVisibility,
32
+ } from "./useShareDialogController.js";
23
33
 
24
34
  export interface ShareDialogProps {
25
35
  open: boolean;
@@ -45,138 +55,52 @@ export interface ShareDialogProps {
45
55
  linkTabExtras?: ReactNode;
46
56
  }
47
57
 
48
- type Visibility = "private" | "org" | "public";
49
- type Role = "viewer" | "editor" | "admin";
50
-
51
- interface Share {
52
- id: string;
53
- principalType: "user" | "org";
54
- principalId: string;
55
- displayName?: string | null;
56
- role: Role;
57
- }
58
-
59
- interface SharesResponse {
60
- ownerEmail: string | null;
61
- orgId: string | null;
62
- visibility: Visibility | null;
63
- role?: "owner" | Role;
64
- shares: Share[];
65
- }
66
-
67
- interface OrgMember {
68
- email: string;
69
- name?: string | null;
70
- }
71
-
72
- function useOrgMembers(): OrgMember[] {
73
- const [members, setMembers] = useState<OrgMember[]>([]);
74
- useEffect(() => {
75
- let cancelled = false;
76
- fetch(agentNativePath("/_agent-native/org/members"))
77
- .then((r) => (r.ok ? r.json() : null))
78
- .then((data) => {
79
- if (cancelled || !data) return;
80
- const list = Array.isArray(data?.members) ? data.members : [];
81
- setMembers(
82
- list
83
- .map((m: any) => ({
84
- email: typeof m?.email === "string" ? m.email : "",
85
- name: typeof m?.name === "string" ? m.name : null,
86
- }))
87
- .filter((m: OrgMember) => m.email),
88
- );
89
- })
90
- .catch(() => {});
91
- return () => {
92
- cancelled = true;
93
- };
94
- }, []);
95
- return members;
96
- }
97
-
98
- function displayName(email: string, members: OrgMember[]): string {
99
- const normalized = email.trim().toLowerCase();
100
- const match = members.find((m) => m.email.toLowerCase() === normalized);
101
- if (match?.name && match.name.trim()) return match.name;
102
- return normalized.includes("@") ? email : "Unknown person";
103
- }
104
-
105
58
  const BUTTON_BASE =
106
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0";
59
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors active:!scale-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0";
107
60
  const BUTTON_OUTLINE_SM = cn(
108
61
  BUTTON_BASE,
109
- "h-9 px-3 border border-input bg-background hover:bg-accent hover:text-accent-foreground",
62
+ "!h-9 !px-3 border border-input bg-background hover:bg-accent hover:text-accent-foreground",
110
63
  );
111
64
  const BUTTON_PRIMARY_SM = cn(
112
65
  BUTTON_BASE,
113
- "h-9 px-4 bg-primary text-primary-foreground hover:bg-primary/90",
66
+ "!h-9 !px-4 bg-primary text-primary-foreground hover:bg-primary/90",
114
67
  );
115
68
  const BUTTON_GHOST_ICON = cn(
116
69
  BUTTON_BASE,
117
- "h-8 w-8 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
70
+ "!h-8 !w-8 !p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
118
71
  );
119
72
 
120
- const VIS_ICONS: Record<Visibility, typeof IconLock> = {
73
+ const VIS_ICONS: Record<ShareVisibility, typeof IconLock> = {
121
74
  private: IconLock,
122
75
  org: IconUsersGroup,
123
76
  public: IconWorld,
124
77
  };
125
78
 
126
- function useVisibilityMeta() {
127
- const t = useT();
128
- return {
129
- private: {
130
- label: t("share.private"),
131
- description: t("share.privateDescription"),
132
- Icon: VIS_ICONS.private,
133
- },
134
- org: {
135
- label: t("share.organization"),
136
- description: t("share.organizationDescription"),
137
- Icon: VIS_ICONS.org,
138
- },
139
- public: {
140
- label: t("share.public"),
141
- description: t("share.publicDescription"),
142
- Icon: VIS_ICONS.public,
143
- },
144
- } satisfies Record<
145
- Visibility,
146
- { label: string; description: string; Icon: typeof IconLock }
147
- >;
148
- }
149
-
150
- function useRoleOptions() {
151
- const t = useT();
152
- return [
153
- {
154
- value: "viewer",
155
- label: t("share.viewer"),
156
- description: t("share.viewerDescription"),
157
- },
158
- {
159
- value: "editor",
160
- label: t("share.editor"),
161
- description: t("share.editorDescription"),
162
- },
163
- {
164
- value: "admin",
165
- label: t("share.admin"),
166
- description: t("share.adminDescription"),
167
- },
168
- ] satisfies Array<{ value: Role; label: string; description: string }>;
169
- }
79
+ const TAB_ICONS: Record<ShareDialogTab, typeof IconLink> = {
80
+ link: IconLink,
81
+ invite: IconMail,
82
+ embed: IconCode,
83
+ };
170
84
 
171
85
  /**
172
86
  * Framework share dialog. Drop into any template via
173
- * `<ShareDialog open onClose resourceType resourceId />`. Passing
174
- * `shareUrl` lights up a Link tab with a copy field; passing `embedUrl`
175
- * lights up an Embed tab. With neither prop, renders a single Invite +
176
- * general-access panel (Google-Docs-lite).
87
+ * `<ShareDialog open onClose resourceType resourceId />`. Passing `shareUrl`
88
+ * lights up a Link tab with a copy field; passing `embedUrl` lights up an
89
+ * Embed tab. With neither prop, renders a single Invite + general-access
90
+ * panel (Google-Docs-lite).
177
91
  */
178
- export function ShareDialog(props: ShareDialogProps) {
179
- const {
92
+ export function ShareDialog({
93
+ open,
94
+ onClose,
95
+ resourceType,
96
+ resourceId,
97
+ resourceTitle,
98
+ shareUrl,
99
+ embedUrl,
100
+ embedTabContent,
101
+ linkTabExtras,
102
+ }: ShareDialogProps) {
103
+ const controller = useShareDialogController({
180
104
  open,
181
105
  onClose,
182
106
  resourceType,
@@ -184,349 +108,194 @@ export function ShareDialog(props: ShareDialogProps) {
184
108
  resourceTitle,
185
109
  shareUrl,
186
110
  embedUrl,
187
- embedTabContent,
188
- linkTabExtras,
189
- } = props;
190
- const t = useT();
191
-
192
- const sharesQuery = useActionQuery<SharesResponse>("list-resource-shares", {
193
- resourceType,
194
- resourceId,
195
111
  });
196
- const orgMembers = useOrgMembers();
197
-
198
- const hasLinkTab = Boolean(shareUrl);
199
- const hasEmbedTab = Boolean(embedUrl);
200
- const tabsEnabled = hasLinkTab || hasEmbedTab;
201
-
202
- const [tab, setTab] = useState<"link" | "invite" | "embed">(
203
- hasLinkTab ? "link" : "invite",
204
- );
205
-
206
- useEffect(() => {
207
- if (!open) return;
208
- setTab(hasLinkTab ? "link" : "invite");
209
- }, [open, hasLinkTab]);
210
-
211
- useEffect(() => {
212
- if (!open) return;
213
- const onKey = (e: KeyboardEvent) => {
214
- if (e.key === "Escape") onClose();
215
- };
216
- window.addEventListener("keydown", onKey);
217
- return () => window.removeEventListener("keydown", onKey);
218
- }, [open, onClose]);
219
-
220
112
  if (!open) return null;
221
113
 
222
- const titleText = resourceTitle
223
- ? t("share.titleWithResource", { title: resourceTitle })
224
- : t("share.titleWithType", { type: resourceType });
225
-
226
- return createPortal(
227
- <div
228
- className="fixed inset-0 z-[2000] flex items-start justify-center bg-black/40 p-4 sm:items-center"
229
- onClick={onClose}
114
+ return (
115
+ <DesignSystemDialog
116
+ open={controller.open}
117
+ onOpenChange={controller.onOpenChange}
118
+ title={
119
+ <span
120
+ className="truncate !text-base !leading-normal !tracking-normal !text-inherit"
121
+ title={controller.title}
122
+ >
123
+ {controller.title}
124
+ </span>
125
+ }
126
+ closeLabel={controller.labels.close}
127
+ size="large"
128
+ className="!top-4 !z-[2010] !block !max-h-none !w-[calc(100vw-2rem)] !max-w-lg !translate-y-0 !gap-0 !overflow-visible !rounded-xl !border-border !bg-popover !p-0 !text-popover-foreground !shadow-2xl sm:!top-1/2 sm:!-translate-y-1/2"
129
+ aria-label={controller.title}
230
130
  >
231
- <div
232
- role="dialog"
233
- aria-modal="true"
234
- aria-label={titleText}
235
- className="w-full max-w-lg rounded-xl border border-border bg-popover text-popover-foreground shadow-2xl outline-none"
236
- onClick={(e) => e.stopPropagation()}
237
- >
238
- <div className="flex items-start justify-between gap-3 px-5 pt-4 pb-3">
239
- <div className="min-w-0 flex-1">
240
- <div className="truncate text-base font-semibold" title={titleText}>
241
- {titleText}
242
- </div>
243
- {sharesQuery.data?.ownerEmail ? (
244
- <div className="mt-0.5 truncate text-xs text-muted-foreground">
245
- {t("share.owner", {
246
- name: displayName(sharesQuery.data.ownerEmail, orgMembers),
247
- })}
248
- </div>
249
- ) : null}
131
+ <div className="px-5 pt-0 pb-3">
132
+ {controller.ownerLabel ? (
133
+ <div className="truncate text-xs text-muted-foreground">
134
+ {controller.ownerLabel}
250
135
  </div>
251
- <button
252
- type="button"
253
- aria-label={t("share.close")}
254
- onClick={onClose}
255
- className={BUTTON_GHOST_ICON}
256
- >
257
- <IconX size={16} />
258
- </button>
259
- </div>
136
+ ) : null}
137
+ </div>
260
138
 
261
- {tabsEnabled ? (
262
- <div
263
- role="tablist"
264
- aria-label={t("share.shareOptions")}
265
- className="mx-5 mt-1 flex gap-1 border-b border-border"
266
- >
267
- {hasLinkTab ? (
268
- <TabTrigger
269
- active={tab === "link"}
270
- onClick={() => setTab("link")}
271
- icon={<IconLink size={14} strokeWidth={1.75} />}
272
- label={t("share.link")}
273
- />
274
- ) : null}
275
- <TabTrigger
276
- active={tab === "invite"}
277
- onClick={() => setTab("invite")}
278
- icon={<IconMail size={14} strokeWidth={1.75} />}
279
- label={t("share.invite")}
280
- />
281
- {hasEmbedTab ? (
139
+ {controller.tabsEnabled ? (
140
+ <div
141
+ role="tablist"
142
+ aria-label={controller.labels.shareOptions}
143
+ className="mx-5 mt-1 flex gap-1 border-b border-border"
144
+ >
145
+ {controller.tabs.map((tab) => {
146
+ const Icon = TAB_ICONS[tab.value];
147
+ return (
282
148
  <TabTrigger
283
- active={tab === "embed"}
284
- onClick={() => setTab("embed")}
285
- icon={<IconCode size={14} strokeWidth={1.75} />}
286
- label={t("share.embed")}
149
+ key={tab.value}
150
+ active={controller.activeTab === tab.value}
151
+ onClick={() => controller.setActiveTab(tab.value)}
152
+ icon={<Icon size={14} strokeWidth={1.75} />}
153
+ label={tab.label}
287
154
  />
288
- ) : null}
289
- </div>
290
- ) : null}
291
-
292
- <div className="px-5 py-4">
293
- {tabsEnabled && tab === "link" && hasLinkTab ? (
294
- <LinkTab
295
- resourceType={resourceType}
296
- resourceId={resourceId}
297
- shareUrl={shareUrl!}
298
- sharesQuery={sharesQuery}
299
- extras={linkTabExtras}
300
- />
301
- ) : null}
302
- {!tabsEnabled || tab === "invite" ? (
303
- <InviteTab
304
- resourceType={resourceType}
305
- resourceId={resourceId}
306
- shareUrl={shareUrl}
307
- sharesQuery={sharesQuery}
308
- showVisibility={!tabsEnabled}
309
- orgMembers={orgMembers}
310
- />
311
- ) : null}
312
- {tabsEnabled && tab === "embed" && hasEmbedTab
313
- ? (embedTabContent ?? <DefaultEmbedBody embedUrl={embedUrl!} />)
314
- : null}
155
+ );
156
+ })}
315
157
  </div>
158
+ ) : null}
316
159
 
317
- <div className="flex justify-end border-t border-border px-5 py-3">
318
- <button type="button" onClick={onClose} className={BUTTON_PRIMARY_SM}>
319
- {t("share.done")}
320
- </button>
321
- </div>
160
+ <div className="px-5 py-4">
161
+ {controller.tabsEnabled && controller.activeTab === "link" ? (
162
+ <LinkTab controller={controller} extras={linkTabExtras} />
163
+ ) : null}
164
+ {!controller.tabsEnabled || controller.activeTab === "invite" ? (
165
+ <InviteTab
166
+ controller={controller}
167
+ showVisibility={!controller.tabsEnabled}
168
+ />
169
+ ) : null}
170
+ {controller.tabsEnabled && controller.activeTab === "embed"
171
+ ? (embedTabContent ?? <DefaultEmbedBody controller={controller} />)
172
+ : null}
173
+ </div>
174
+
175
+ <div className="flex justify-end border-t border-border px-5 py-3">
176
+ <ActionButton
177
+ type="button"
178
+ intent="primary"
179
+ emphasis="solid"
180
+ onPress={controller.close}
181
+ className={BUTTON_PRIMARY_SM}
182
+ >
183
+ {controller.labels.done}
184
+ </ActionButton>
322
185
  </div>
323
- </div>,
324
- document.body,
186
+ </DesignSystemDialog>
325
187
  );
326
188
  }
327
189
 
328
- // ---------------------------------------------------------------------------
329
- // Tabs
330
- // ---------------------------------------------------------------------------
331
-
332
- function TabTrigger(props: {
190
+ function TabTrigger({
191
+ active,
192
+ onClick,
193
+ icon,
194
+ label,
195
+ }: {
333
196
  active: boolean;
334
197
  onClick: () => void;
335
198
  icon: ReactNode;
336
199
  label: string;
337
200
  }) {
338
201
  return (
339
- <button
202
+ <ActionButton
340
203
  type="button"
341
- role="tab"
342
- aria-selected={props.active}
343
- onClick={props.onClick}
204
+ emphasis="ghost"
205
+ aria-pressed={active}
206
+ onPress={onClick}
344
207
  className={cn(
345
- "inline-flex items-center gap-1.5 border-b-2 px-3 py-2 text-sm font-medium transition-colors",
346
- props.active
208
+ "inline-flex !h-auto items-center gap-1.5 !rounded-none border-b-2 !px-3 !py-2 text-sm font-medium transition-colors hover:!bg-transparent active:!scale-100 focus-visible:!ring-0 focus-visible:!ring-offset-0 [&_svg]:!size-auto",
209
+ active
347
210
  ? "border-foreground text-foreground"
348
211
  : "border-transparent text-muted-foreground hover:text-foreground",
349
212
  )}
350
213
  >
351
- {props.icon}
352
- {props.label}
353
- </button>
214
+ {icon}
215
+ {label}
216
+ </ActionButton>
354
217
  );
355
218
  }
356
219
 
357
- // ---------------------------------------------------------------------------
358
- // Link tab — visibility picker + copy-link field + optional extras
359
- // ---------------------------------------------------------------------------
360
-
361
- function LinkTab(props: {
362
- resourceType: string;
363
- resourceId: string;
364
- shareUrl: string;
365
- sharesQuery: ReturnType<typeof useActionQuery<SharesResponse>>;
220
+ function LinkTab({
221
+ controller,
222
+ extras,
223
+ }: {
224
+ controller: ShareDialogController;
366
225
  extras?: ReactNode;
367
226
  }) {
368
- const { resourceType, resourceId, shareUrl, sharesQuery, extras } = props;
369
- const t = useT();
370
- const visibilityMeta = useVisibilityMeta();
371
-
372
- const setVisibility = useActionMutation("set-resource-visibility");
373
- const data = sharesQuery.data;
374
- const visibility: Visibility =
375
- (data?.visibility as Visibility | null) ?? "private";
376
- const canManage = data?.role === "owner" || data?.role === "admin";
377
- const meta = visibilityMeta[visibility];
378
-
379
- const handleVisibility = (next: Visibility) => {
380
- if (next === visibility) return;
381
- if (!canManage) return;
382
- setVisibility.mutate(
383
- { resourceType, resourceId, visibility: next } as any,
384
- { onSuccess: () => sharesQuery.refetch() },
385
- );
386
- };
387
-
227
+ const Icon = VIS_ICONS[controller.visibility.value];
388
228
  return (
389
229
  <div className="space-y-4">
390
230
  <div>
391
231
  <div className="mb-2 text-sm font-semibold">
392
- {t("share.generalAccess")}
232
+ {controller.labels.generalAccess}
393
233
  </div>
394
234
  <div className="flex items-center gap-3">
395
235
  <span
396
236
  aria-hidden
397
237
  className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground"
398
238
  >
399
- <meta.Icon size={16} strokeWidth={1.75} />
239
+ <Icon size={16} strokeWidth={1.75} />
400
240
  </span>
401
241
  <div className="min-w-0 flex-1">
402
- <VisibilitySelect
403
- value={visibility}
404
- onChange={handleVisibility}
405
- disabled={!canManage}
406
- />
242
+ <VisibilitySelect controller={controller} />
407
243
  <div className="mt-0.5 text-xs text-muted-foreground">
408
- {meta.description}
244
+ {controller.visibility.description}
409
245
  </div>
410
246
  </div>
411
247
  </div>
412
248
  </div>
413
-
414
- <CopyField label={t("share.shareLink")} value={shareUrl} />
415
-
249
+ <CopyField
250
+ field="share-link"
251
+ label={controller.labels.shareLink}
252
+ value={controller.shareUrl!}
253
+ controller={controller}
254
+ />
416
255
  {extras}
417
256
  </div>
418
257
  );
419
258
  }
420
259
 
421
- // ---------------------------------------------------------------------------
422
- // Invite tab — invite-by-email + shares list + (optional) visibility
423
- // ---------------------------------------------------------------------------
424
-
425
- function InviteTab(props: {
426
- resourceType: string;
427
- resourceId: string;
428
- shareUrl?: string;
429
- sharesQuery: ReturnType<typeof useActionQuery<SharesResponse>>;
260
+ function InviteTab({
261
+ controller,
262
+ showVisibility,
263
+ }: {
264
+ controller: ShareDialogController;
430
265
  showVisibility: boolean;
431
- orgMembers: OrgMember[];
432
266
  }) {
433
- const {
434
- resourceType,
435
- resourceId,
436
- shareUrl,
437
- sharesQuery,
438
- showVisibility,
439
- orgMembers,
440
- } = props;
441
- const t = useT();
442
- const visibilityMeta = useVisibilityMeta();
443
-
444
- const share = useActionMutation("share-resource");
445
- const unshare = useActionMutation("unshare-resource");
446
- const setVisibility = useActionMutation("set-resource-visibility");
447
-
448
- const [email, setEmail] = useState("");
449
- const [role, setRole] = useState<Role>("viewer");
450
- const [notifyPeople, setNotifyPeople] = useState(true);
451
- const hasInviteEmail = email.trim().length > 0;
452
-
453
- const data = sharesQuery.data;
454
- const shares = data?.shares ?? [];
455
- const visibility: Visibility =
456
- (data?.visibility as Visibility | null) ?? "private";
457
- const canManage = data?.role === "owner" || data?.role === "admin";
458
- const meta = visibilityMeta[visibility];
459
-
460
- const handleAdd = () => {
461
- const trimmed = email.trim();
462
- if (!trimmed) return;
463
- share.mutate(
464
- {
465
- resourceType,
466
- resourceId,
467
- principalType: "user",
468
- principalId: trimmed,
469
- role,
470
- notify: notifyPeople,
471
- resourceUrl: getNotificationUrl(shareUrl),
472
- } as any,
473
- {
474
- onSuccess: () => {
475
- setEmail("");
476
- sharesQuery.refetch();
477
- },
478
- },
479
- );
480
- };
481
-
482
- const handleRemove = (s: Share) => {
483
- unshare.mutate(
484
- {
485
- resourceType,
486
- resourceId,
487
- principalType: s.principalType,
488
- principalId: s.principalId,
489
- } as any,
490
- { onSuccess: () => sharesQuery.refetch() },
491
- );
492
- };
493
-
494
- const handleVisibility = (next: Visibility) => {
495
- if (next === visibility) return;
496
- if (!canManage) return;
497
- setVisibility.mutate(
498
- { resourceType, resourceId, visibility: next } as any,
499
- { onSuccess: () => sharesQuery.refetch() },
500
- );
501
- };
502
-
267
+ const Icon = VIS_ICONS[controller.visibility.value];
503
268
  return (
504
269
  <div className="space-y-4">
505
- {canManage ? (
270
+ {controller.canManage ? (
506
271
  <div className="space-y-2">
507
272
  <div className="flex items-stretch gap-2">
508
273
  <input
509
274
  type="email"
510
- placeholder={t("share.addPeopleByEmail")}
511
- value={email}
512
- onChange={(e) => setEmail(e.target.value)}
513
- onKeyDown={(e) => {
514
- if (e.key === "Enter") handleAdd();
275
+ placeholder={controller.labels.addPeopleByEmail}
276
+ value={controller.invite.email}
277
+ onChange={(event) =>
278
+ controller.invite.setEmail(event.currentTarget.value)
279
+ }
280
+ onKeyDown={(event) => {
281
+ if (event.key === "Enter") controller.invite.submit();
515
282
  }}
516
283
  autoComplete="off"
517
284
  className="flex-1 min-w-0 h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
518
285
  />
519
- <RoleSelect value={role} onChange={setRole} />
286
+ <RoleSelect controller={controller} />
520
287
  </div>
521
- {hasInviteEmail ? (
288
+ {controller.invite.showNotifyPeople ? (
522
289
  <label className="inline-flex items-center gap-2 text-xs text-muted-foreground">
523
290
  <input
524
291
  type="checkbox"
525
- checked={notifyPeople}
526
- onChange={(e) => setNotifyPeople(e.target.checked)}
292
+ checked={controller.invite.notifyPeople}
293
+ onChange={(event) =>
294
+ controller.invite.setNotifyPeople(event.currentTarget.checked)
295
+ }
527
296
  className="h-4 w-4 rounded border-input accent-primary"
528
297
  />
529
- {t("share.notifyPeople")}
298
+ {controller.labels.notifyPeople}
530
299
  </label>
531
300
  ) : null}
532
301
  </div>
@@ -534,50 +303,21 @@ function InviteTab(props: {
534
303
 
535
304
  <div>
536
305
  <div className="mb-2 text-sm font-semibold">
537
- {t("share.peopleWithAccess")}
306
+ {controller.labels.peopleWithAccess}
538
307
  </div>
539
308
  <ul className="flex flex-col gap-1 list-none p-0 m-0">
540
- {data?.ownerEmail ? (
541
- <li className="flex items-center gap-3 px-1 py-1.5 text-sm">
542
- <Avatar label={displayName(data.ownerEmail, orgMembers)} />
543
- <span className="flex-1 min-w-0 truncate">
544
- {displayName(data.ownerEmail, orgMembers)}
545
- </span>
546
- <span className="text-xs text-muted-foreground">
547
- {t("share.ownerRole")}
548
- </span>
549
- </li>
550
- ) : null}
551
- {shares.map((s) => (
552
- <li
553
- key={`${s.principalType}:${s.principalId}`}
554
- className="flex items-center gap-3 px-1 py-1.5 text-sm"
555
- >
556
- <Avatar
557
- label={principalLabel(s, orgMembers)}
558
- org={s.principalType === "org"}
559
- />
560
- <span className="flex-1 min-w-0 truncate">
561
- {principalLabel(s, orgMembers)}
562
- </span>
563
- <span className="text-xs text-muted-foreground">
564
- {cap(s.role)}
565
- </span>
566
- {canManage ? (
567
- <button
568
- type="button"
569
- aria-label={t("share.remove")}
570
- onClick={() => handleRemove(s)}
571
- className={BUTTON_GHOST_ICON}
572
- >
573
- <IconTrash size={14} />
574
- </button>
575
- ) : null}
576
- </li>
309
+ {controller.people.map((person) => (
310
+ <PersonRow
311
+ key={person.key}
312
+ person={person}
313
+ canManage={controller.canManage}
314
+ removeLabel={controller.labels.remove}
315
+ onRemove={controller.removeShare}
316
+ />
577
317
  ))}
578
- {!shares.length && !data?.ownerEmail ? (
318
+ {!controller.people.length ? (
579
319
  <li className="px-1 py-1.5 text-sm text-muted-foreground">
580
- {t("share.noAccess")}
320
+ {controller.labels.noAccess}
581
321
  </li>
582
322
  ) : null}
583
323
  </ul>
@@ -586,23 +326,19 @@ function InviteTab(props: {
586
326
  {showVisibility ? (
587
327
  <div>
588
328
  <div className="mb-2 text-sm font-semibold">
589
- {t("share.generalAccess")}
329
+ {controller.labels.generalAccess}
590
330
  </div>
591
331
  <div className="flex items-center gap-3">
592
332
  <span
593
333
  aria-hidden
594
334
  className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground"
595
335
  >
596
- <meta.Icon size={16} strokeWidth={1.75} />
336
+ <Icon size={16} strokeWidth={1.75} />
597
337
  </span>
598
338
  <div className="min-w-0 flex-1">
599
- <VisibilitySelect
600
- value={visibility}
601
- onChange={handleVisibility}
602
- disabled={!canManage}
603
- />
339
+ <VisibilitySelect controller={controller} />
604
340
  <div className="mt-0.5 text-xs text-muted-foreground">
605
- {meta.description}
341
+ {controller.visibility.description}
606
342
  </div>
607
343
  </div>
608
344
  </div>
@@ -612,44 +348,93 @@ function InviteTab(props: {
612
348
  );
613
349
  }
614
350
 
615
- // ---------------------------------------------------------------------------
616
- // Default Embed body (simple responsive iframe snippet)
617
- // ---------------------------------------------------------------------------
351
+ function PersonRow({
352
+ person,
353
+ canManage,
354
+ removeLabel,
355
+ onRemove,
356
+ }: {
357
+ person: ShareDialogPerson;
358
+ canManage: boolean;
359
+ removeLabel: string;
360
+ onRemove: (share: ResourceShare) => void;
361
+ }) {
362
+ return (
363
+ <li className="flex items-center gap-3 px-1 py-1.5 text-sm">
364
+ <DesignSystemAvatar
365
+ name={person.label}
366
+ fallback={
367
+ person.principalType === "org" ? (
368
+ <IconUsersGroup size={14} strokeWidth={1.75} />
369
+ ) : (
370
+ person.avatarText
371
+ )
372
+ }
373
+ size="compact"
374
+ className="inline-flex h-7 w-7 shrink-0 text-[11px] font-semibold"
375
+ />
376
+ <span className="flex-1 min-w-0 truncate">{person.label}</span>
377
+ <Status
378
+ tone="neutral"
379
+ size="compact"
380
+ className="border-0 bg-transparent px-0 text-xs text-muted-foreground"
381
+ >
382
+ {person.roleLabel}
383
+ </Status>
384
+ {canManage && person.share ? (
385
+ <IconButton
386
+ intent="danger"
387
+ emphasis="ghost"
388
+ size="compact"
389
+ icon={<IconTrash size={14} />}
390
+ label={removeLabel}
391
+ aria-label={removeLabel}
392
+ onPress={() => onRemove(person.share!)}
393
+ className={cn(BUTTON_GHOST_ICON, "[&_svg]:!size-auto")}
394
+ />
395
+ ) : null}
396
+ </li>
397
+ );
398
+ }
618
399
 
619
- function DefaultEmbedBody({ embedUrl }: { embedUrl: string }) {
620
- const t = useT();
621
- const code = `<div style="position:relative;padding-bottom:56.25%;height:0"><iframe src="${embedUrl}" frameborder="0" allowfullscreen allow="autoplay; picture-in-picture" style="position:absolute;inset:0;width:100%;height:100%"></iframe></div>`;
400
+ function DefaultEmbedBody({
401
+ controller,
402
+ }: {
403
+ controller: ShareDialogController;
404
+ }) {
622
405
  return (
623
406
  <div className="space-y-3">
624
- <CopyField label={t("share.embedUrl")} value={embedUrl} />
625
- <CopyField label={t("share.embedCode")} value={code} multiline />
407
+ <CopyField
408
+ field="embed-url"
409
+ label={controller.labels.embedUrl}
410
+ value={controller.embedUrl!}
411
+ controller={controller}
412
+ />
413
+ <CopyField
414
+ field="embed-code"
415
+ label={controller.labels.embedCode}
416
+ value={controller.embedCode!}
417
+ controller={controller}
418
+ multiline
419
+ />
626
420
  </div>
627
421
  );
628
422
  }
629
423
 
630
- // ---------------------------------------------------------------------------
631
- // Primitives
632
- // ---------------------------------------------------------------------------
633
-
634
424
  function CopyField({
425
+ field,
635
426
  label,
636
427
  value,
428
+ controller,
637
429
  multiline,
638
430
  }: {
431
+ field: string;
639
432
  label: string;
640
433
  value: string;
434
+ controller: ShareDialogController;
641
435
  multiline?: boolean;
642
436
  }) {
643
- const [copied, setCopied] = useState(false);
644
- const t = useT();
645
- const copy = async () => {
646
- if (!(await writeClipboardText(value))) {
647
- setCopied(false);
648
- return;
649
- }
650
- setCopied(true);
651
- setTimeout(() => setCopied(false), 1400);
652
- };
437
+ const copied = controller.copiedField === field;
653
438
  return (
654
439
  <div>
655
440
  <div className="mb-1 text-xs font-medium text-muted-foreground">
@@ -657,190 +442,71 @@ function CopyField({
657
442
  </div>
658
443
  <div className="flex items-stretch gap-2">
659
444
  {multiline ? (
660
- <textarea
445
+ <TextArea
661
446
  readOnly
662
447
  value={value}
663
- className="flex-1 h-20 rounded-md border border-input bg-background px-3 py-2 text-xs font-mono text-foreground resize-none focus:outline-none focus:ring-2 focus:ring-ring"
448
+ onChange={() => undefined}
449
+ aria-label={label}
450
+ className="flex-1 h-20 text-xs font-mono"
664
451
  />
665
452
  ) : (
666
- <input
453
+ <TextField
667
454
  readOnly
668
455
  value={value}
669
- className="flex-1 min-w-0 h-9 rounded-md border border-input bg-background px-3 text-xs font-mono text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
456
+ onChange={() => undefined}
457
+ aria-label={label}
458
+ className="flex-1 min-w-0 text-xs font-mono"
670
459
  />
671
460
  )}
672
- <button
673
- type="button"
674
- onClick={copy}
675
- aria-label={t("share.copy")}
676
- className={cn(BUTTON_OUTLINE_SM, "w-9 px-0")}
677
- >
678
- {copied ? <IconCheck size={14} /> : <IconCopy size={14} />}
679
- </button>
461
+ <IconButton
462
+ emphasis="outline"
463
+ size="compact"
464
+ icon={copied ? <IconCheck size={14} /> : <IconCopy size={14} />}
465
+ label={controller.labels.copy}
466
+ onPress={() => void controller.copy(field, value)}
467
+ aria-label={controller.labels.copy}
468
+ className={cn(BUTTON_OUTLINE_SM, "!w-9 !px-0 [&_svg]:!size-auto")}
469
+ />
680
470
  </div>
681
471
  </div>
682
472
  );
683
473
  }
684
474
 
685
- const selectContentClass =
686
- "z-[2100] min-w-[12rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md";
687
- const selectItemClass =
688
- "relative flex w-full cursor-pointer select-none items-start gap-2 rounded-sm py-2 ps-8 pe-3 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
689
-
690
- function SelectItems({
691
- items,
692
- }: {
693
- items: Array<{ value: string; label: string; description?: string }>;
694
- }) {
695
- return (
696
- <>
697
- {items.map((it) => (
698
- <Select.Item
699
- key={it.value}
700
- value={it.value}
701
- className={selectItemClass}
702
- >
703
- <span className="absolute start-2 top-2 flex h-4 w-4 items-center justify-center">
704
- <Select.ItemIndicator>
705
- <IconCheck size={14} />
706
- </Select.ItemIndicator>
707
- </span>
708
- <span className="flex flex-col">
709
- <Select.ItemText>{it.label}</Select.ItemText>
710
- {it.description ? (
711
- <span className="text-xs text-muted-foreground">
712
- {it.description}
713
- </span>
714
- ) : null}
715
- </span>
716
- </Select.Item>
717
- ))}
718
- </>
719
- );
720
- }
721
-
722
- function RoleSelect(props: { value: Role; onChange: (v: Role) => void }) {
723
- const t = useT();
724
- const roleOptions = useRoleOptions();
725
- const current =
726
- roleOptions.find((o) => o.value === props.value) ?? roleOptions[0];
475
+ function RoleSelect({ controller }: { controller: ShareDialogController }) {
727
476
  return (
728
- <Select.Root
729
- value={props.value}
730
- onValueChange={(v) => props.onChange(v as Role)}
731
- >
732
- <Select.Trigger
733
- aria-label={t("share.role")}
734
- className={cn(
735
- BUTTON_BASE,
736
- "h-9 px-3 border border-input bg-background hover:bg-accent hover:text-accent-foreground",
737
- )}
738
- >
739
- <Select.Value>{current.label}</Select.Value>
740
- <Select.Icon>
741
- <IconChevronDown size={14} />
742
- </Select.Icon>
743
- </Select.Trigger>
744
- <Select.Portal>
745
- <Select.Content
746
- className={selectContentClass}
747
- position="popper"
748
- sideOffset={4}
749
- >
750
- <Select.Viewport>
751
- <SelectItems items={roleOptions} />
752
- </Select.Viewport>
753
- </Select.Content>
754
- </Select.Portal>
755
- </Select.Root>
477
+ <Picker
478
+ mode="select"
479
+ options={controller.invite.roleOptions}
480
+ value={controller.invite.role}
481
+ onChange={(value) => {
482
+ if (value === "viewer" || value === "editor" || value === "admin") {
483
+ controller.invite.setRole(value);
484
+ }
485
+ }}
486
+ aria-label={controller.labels.role}
487
+ className="w-auto"
488
+ />
756
489
  );
757
490
  }
758
491
 
759
- function VisibilitySelect(props: {
760
- value: Visibility;
761
- onChange: (v: Visibility) => void;
762
- disabled?: boolean;
763
- allowPrivate?: boolean;
764
- allowPublic?: boolean;
492
+ function VisibilitySelect({
493
+ controller,
494
+ }: {
495
+ controller: ShareDialogController;
765
496
  }) {
766
- const t = useT();
767
- const visibilityMeta = useVisibilityMeta();
768
- const current = visibilityMeta[props.value];
769
- const allowPrivate = props.allowPrivate !== false;
770
- const allowPublic = props.allowPublic !== false;
771
- const options = (Object.keys(VIS_ICONS) as Visibility[]).filter((k) => {
772
- if (k === props.value) return true;
773
- if (k === "private" && !allowPrivate) return false;
774
- if (k === "public" && !allowPublic) return false;
775
- return true;
776
- });
777
- return (
778
- <Select.Root
779
- value={props.value}
780
- onValueChange={(v) => props.onChange(v as Visibility)}
781
- disabled={props.disabled}
782
- >
783
- <Select.Trigger
784
- aria-label={t("share.generalAccess")}
785
- className={cn(
786
- BUTTON_BASE,
787
- "h-7 px-1 -ms-1 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground",
788
- )}
789
- >
790
- <Select.Value>{current.label}</Select.Value>
791
- <Select.Icon>
792
- <IconChevronDown size={14} />
793
- </Select.Icon>
794
- </Select.Trigger>
795
- <Select.Portal>
796
- <Select.Content
797
- className={selectContentClass}
798
- position="popper"
799
- sideOffset={4}
800
- >
801
- <Select.Viewport>
802
- <SelectItems
803
- items={options.map((k) => ({
804
- value: k,
805
- label: visibilityMeta[k].label,
806
- description: visibilityMeta[k].description,
807
- }))}
808
- />
809
- </Select.Viewport>
810
- </Select.Content>
811
- </Select.Portal>
812
- </Select.Root>
813
- );
814
- }
815
-
816
- function Avatar({ label, org }: { label: string; org?: boolean }) {
817
497
  return (
818
- <span
819
- aria-hidden
820
- className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted text-[11px] font-semibold text-muted-foreground"
821
- >
822
- {org ? (
823
- <IconUsersGroup size={14} strokeWidth={1.75} />
824
- ) : (
825
- (label.split("@")[0]?.[0] ?? label[0] ?? "?").toUpperCase()
826
- )}
827
- </span>
498
+ <Picker
499
+ mode="select"
500
+ options={controller.visibility.options}
501
+ value={controller.visibility.value}
502
+ onChange={(value) => {
503
+ if (value === "private" || value === "org" || value === "public") {
504
+ controller.visibility.set(value);
505
+ }
506
+ }}
507
+ disabled={controller.visibility.disabled}
508
+ aria-label={controller.labels.generalAccess}
509
+ className="-ms-1 w-auto"
510
+ />
828
511
  );
829
512
  }
830
-
831
- function getNotificationUrl(explicit?: string): string | undefined {
832
- if (explicit) return explicit;
833
- if (typeof window === "undefined") return undefined;
834
- return window.location.href;
835
- }
836
-
837
- function cap(s: string): string {
838
- return s.charAt(0).toUpperCase() + s.slice(1);
839
- }
840
-
841
- function principalLabel(share: Share, members: OrgMember[]): string {
842
- const serverLabel = share.displayName?.trim();
843
- if (serverLabel) return serverLabel;
844
- if (share.principalType === "org") return "Organization";
845
- return displayName(share.principalId, members);
846
- }