@larkup/workspace-app 0.0.0-stage → 0.1.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 (440) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +4 -2
  3. package/assets/github.svg +3 -0
  4. package/assets/icons/Confluence.png +0 -0
  5. package/assets/icons/Instagram.png +0 -0
  6. package/assets/icons/Northflank.png +0 -0
  7. package/assets/icons/Salesforce.png +0 -0
  8. package/assets/icons/agentskills.png +0 -0
  9. package/assets/icons/airtable.png +0 -0
  10. package/assets/icons/asana.png +0 -0
  11. package/assets/icons/ashby.png +0 -0
  12. package/assets/icons/audio.png +0 -0
  13. package/assets/icons/aws-kendra.png +0 -0
  14. package/assets/icons/aws.svg +38 -0
  15. package/assets/icons/azure-ai-search.png +0 -0
  16. package/assets/icons/azure.svg +1 -0
  17. package/assets/icons/bigquery.png +0 -0
  18. package/assets/icons/bing.png +0 -0
  19. package/assets/icons/bitbucket.png +0 -0
  20. package/assets/icons/brave.png +0 -0
  21. package/assets/icons/browserbase.png +0 -0
  22. package/assets/icons/calendly.png +0 -0
  23. package/assets/icons/chroma.png +0 -0
  24. package/assets/icons/cloudflare.png +0 -0
  25. package/assets/icons/cohere.svg +30 -0
  26. package/assets/icons/databricks.png +0 -0
  27. package/assets/icons/daytonaio.png +0 -0
  28. package/assets/icons/daytonaio2.png +0 -0
  29. package/assets/icons/deepgram.png +0 -0
  30. package/assets/icons/deepl.png +0 -0
  31. package/assets/icons/digital-ocean.webp +0 -0
  32. package/assets/icons/discord.png +0 -0
  33. package/assets/icons/e2b-2.png +0 -0
  34. package/assets/icons/e2b.png +0 -0
  35. package/assets/icons/elevenlabs.png +0 -0
  36. package/assets/icons/email.png +0 -0
  37. package/assets/icons/exa.png +0 -0
  38. package/assets/icons/excel.png +0 -0
  39. package/assets/icons/fal.png +0 -0
  40. package/assets/icons/firecrawl.png +0 -0
  41. package/assets/icons/firecrawl2.png +0 -0
  42. package/assets/icons/flyio.jpeg +0 -0
  43. package/assets/icons/gcp.svg +1 -0
  44. package/assets/icons/gemini.svg +1 -0
  45. package/assets/icons/gmail.png +0 -0
  46. package/assets/icons/google-analytics.png +0 -0
  47. package/assets/icons/google-calender.png +0 -0
  48. package/assets/icons/google-docs.png +0 -0
  49. package/assets/icons/google-drive.png +0 -0
  50. package/assets/icons/google-maps.png +0 -0
  51. package/assets/icons/google-meet.png +0 -0
  52. package/assets/icons/google-sheets.png +0 -0
  53. package/assets/icons/google-slides.png +0 -0
  54. package/assets/icons/google-tasks.png +0 -0
  55. package/assets/icons/google.png +0 -0
  56. package/assets/icons/grok.png +0 -0
  57. package/assets/icons/hubspot.png +0 -0
  58. package/assets/icons/image.png +0 -0
  59. package/assets/icons/jina.svg +1 -0
  60. package/assets/icons/jira.png +0 -0
  61. package/assets/icons/lancedb2.png +0 -0
  62. package/assets/icons/linear.png +0 -0
  63. package/assets/icons/linux.png +0 -0
  64. package/assets/icons/looker.png +0 -0
  65. package/assets/icons/luma.png +0 -0
  66. package/assets/icons/mac.png +0 -0
  67. package/assets/icons/markdown.png +0 -0
  68. package/assets/icons/mcp.png +0 -0
  69. package/assets/icons/mcp.svg +1 -0
  70. package/assets/icons/messenger.svg +1 -0
  71. package/assets/icons/microsoft-intra.png +0 -0
  72. package/assets/icons/microsoft-onenote.png +0 -0
  73. package/assets/icons/microsoft-planner.png +0 -0
  74. package/assets/icons/microsoft-teams.png +0 -0
  75. package/assets/icons/miro.png +0 -0
  76. package/assets/icons/mistral.svg +469 -0
  77. package/assets/icons/modal.png +0 -0
  78. package/assets/icons/ngrok.png +0 -0
  79. package/assets/icons/nomic.png +0 -0
  80. package/assets/icons/onedrive.png +0 -0
  81. package/assets/icons/openai.svg +1 -0
  82. package/assets/icons/outlook-calendar.png +0 -0
  83. package/assets/icons/outlook-email.png +0 -0
  84. package/assets/icons/outlook.png +0 -0
  85. package/assets/icons/pdf.png +0 -0
  86. package/assets/icons/personio.png +0 -0
  87. package/assets/icons/pgvector2.png +0 -0
  88. package/assets/icons/phone.png +0 -0
  89. package/assets/icons/pinecone.png +0 -0
  90. package/assets/icons/pipedrive.png +0 -0
  91. package/assets/icons/power-pi.png +0 -0
  92. package/assets/icons/pylon.png +0 -0
  93. package/assets/icons/python.png +0 -0
  94. package/assets/icons/qdrant.svg +35 -0
  95. package/assets/icons/revai.png +0 -0
  96. package/assets/icons/revai.svg +1 -0
  97. package/assets/icons/sap.png +0 -0
  98. package/assets/icons/serpapi.svg +34 -0
  99. package/assets/icons/serper.png +0 -0
  100. package/assets/icons/servicenow.png +0 -0
  101. package/assets/icons/sharepoint.png +0 -0
  102. package/assets/icons/slack.png +0 -0
  103. package/assets/icons/snowflake.png +0 -0
  104. package/assets/icons/statistia.png +0 -0
  105. package/assets/icons/stripe.png +0 -0
  106. package/assets/icons/supabase.png +0 -0
  107. package/assets/icons/tableau.png +0 -0
  108. package/assets/icons/tavily.png +0 -0
  109. package/assets/icons/telegram.png +0 -0
  110. package/assets/icons/typescript.png +0 -0
  111. package/assets/icons/vercel copy.svg +1 -0
  112. package/assets/icons/vercel.svg +1 -0
  113. package/assets/icons/vertex-ai.png +0 -0
  114. package/assets/icons/video.png +0 -0
  115. package/assets/icons/voyage-light.png +0 -0
  116. package/assets/icons/weaviate.webp +0 -0
  117. package/assets/icons/webcontainers.svg +11 -0
  118. package/assets/icons/webhook.png +0 -0
  119. package/assets/icons/whatsapp copy.png +0 -0
  120. package/assets/icons/whatsapp.png +0 -0
  121. package/assets/icons/windows.png +0 -0
  122. package/assets/icons/word.png +0 -0
  123. package/assets/icons/zendesk.png +0 -0
  124. package/assets/icons/zoom.png +0 -0
  125. package/assets/notion-white.png +0 -0
  126. package/assets/notion.png +0 -0
  127. package/dist-types/asset-server.d.ts +3 -0
  128. package/dist-types/asset-server.d.ts.map +1 -0
  129. package/dist-types/components/alerts/generic-alert.d.ts +15 -0
  130. package/dist-types/components/alerts/generic-alert.d.ts.map +1 -0
  131. package/dist-types/components/chat/canvas/doc-editor-provider.d.ts +62 -0
  132. package/dist-types/components/chat/canvas/doc-editor-provider.d.ts.map +1 -0
  133. package/dist-types/components/chat/canvas/document-canvas.d.ts +7 -0
  134. package/dist-types/components/chat/canvas/document-canvas.d.ts.map +1 -0
  135. package/dist-types/components/chat/canvas/document-field.d.ts +13 -0
  136. package/dist-types/components/chat/canvas/document-field.d.ts.map +1 -0
  137. package/dist-types/components/chat/canvas/document-viewer.d.ts +2 -0
  138. package/dist-types/components/chat/canvas/document-viewer.d.ts.map +1 -0
  139. package/dist-types/components/chat/canvas/file-attachment-button.d.ts +9 -0
  140. package/dist-types/components/chat/canvas/file-attachment-button.d.ts.map +1 -0
  141. package/dist-types/components/chat/chat-workspace.d.ts +4 -0
  142. package/dist-types/components/chat/chat-workspace.d.ts.map +1 -0
  143. package/dist-types/components/chat/message-actions.d.ts +11 -0
  144. package/dist-types/components/chat/message-actions.d.ts.map +1 -0
  145. package/dist-types/components/chat/message-item.d.ts +20 -0
  146. package/dist-types/components/chat/message-item.d.ts.map +1 -0
  147. package/dist-types/components/chat/message-queue.d.ts +15 -0
  148. package/dist-types/components/chat/message-queue.d.ts.map +1 -0
  149. package/dist-types/components/chat/tools/chat-chart.d.ts +6 -0
  150. package/dist-types/components/chat/tools/chat-chart.d.ts.map +1 -0
  151. package/dist-types/components/chat/tools/chat-citations.d.ts +32 -0
  152. package/dist-types/components/chat/tools/chat-citations.d.ts.map +1 -0
  153. package/dist-types/components/chat/tools/chat-data-table.d.ts +14 -0
  154. package/dist-types/components/chat/tools/chat-data-table.d.ts.map +1 -0
  155. package/dist-types/components/chat/tools/chat-file-export.d.ts +5 -0
  156. package/dist-types/components/chat/tools/chat-file-export.d.ts.map +1 -0
  157. package/dist-types/components/chat/tools/chat-media-preview.d.ts +35 -0
  158. package/dist-types/components/chat/tools/chat-media-preview.d.ts.map +1 -0
  159. package/dist-types/components/chat/tools/chat-sandbox-result.d.ts +17 -0
  160. package/dist-types/components/chat/tools/chat-sandbox-result.d.ts.map +1 -0
  161. package/dist-types/components/chat/tools/chat-signature-request.d.ts +13 -0
  162. package/dist-types/components/chat/tools/chat-signature-request.d.ts.map +1 -0
  163. package/dist-types/components/chat/tools/chat-tabs.d.ts +17 -0
  164. package/dist-types/components/chat/tools/chat-tabs.d.ts.map +1 -0
  165. package/dist-types/components/chat/tools/corpus-data-result.d.ts +36 -0
  166. package/dist-types/components/chat/tools/corpus-data-result.d.ts.map +1 -0
  167. package/dist-types/components/chat/tools/knowledge-base-result.d.ts +14 -0
  168. package/dist-types/components/chat/tools/knowledge-base-result.d.ts.map +1 -0
  169. package/dist-types/components/chat/tools/visual-placement-modal.d.ts +23 -0
  170. package/dist-types/components/chat/tools/visual-placement-modal.d.ts.map +1 -0
  171. package/dist-types/components/configure/custom-model-modal.d.ts +13 -0
  172. package/dist-types/components/configure/custom-model-modal.d.ts.map +1 -0
  173. package/dist-types/components/configure/store-fields.d.ts +11 -0
  174. package/dist-types/components/configure/store-fields.d.ts.map +1 -0
  175. package/dist-types/components/data/corpus-panel.d.ts +12 -0
  176. package/dist-types/components/data/corpus-panel.d.ts.map +1 -0
  177. package/dist-types/components/data/data-entry-mode-switch.d.ts +13 -0
  178. package/dist-types/components/data/data-entry-mode-switch.d.ts.map +1 -0
  179. package/dist-types/components/data/data-primary-action.d.ts +7 -0
  180. package/dist-types/components/data/data-primary-action.d.ts.map +1 -0
  181. package/dist-types/components/data/data-workspace.d.ts +6 -0
  182. package/dist-types/components/data/data-workspace.d.ts.map +1 -0
  183. package/dist-types/components/data/groups-panel.d.ts +5 -0
  184. package/dist-types/components/data/groups-panel.d.ts.map +1 -0
  185. package/dist-types/components/data/integration-resources-panel.d.ts +9 -0
  186. package/dist-types/components/data/integration-resources-panel.d.ts.map +1 -0
  187. package/dist-types/components/data/integrations-panel.d.ts +5 -0
  188. package/dist-types/components/data/integrations-panel.d.ts.map +1 -0
  189. package/dist-types/components/data/jobs-panel.d.ts +6 -0
  190. package/dist-types/components/data/jobs-panel.d.ts.map +1 -0
  191. package/dist-types/components/data/jobs-tracking-button.d.ts +2 -0
  192. package/dist-types/components/data/jobs-tracking-button.d.ts.map +1 -0
  193. package/dist-types/components/data/live-media-progress.d.ts +9 -0
  194. package/dist-types/components/data/live-media-progress.d.ts.map +1 -0
  195. package/dist-types/components/data/media-indexing-jobs-panel.d.ts +34 -0
  196. package/dist-types/components/data/media-indexing-jobs-panel.d.ts.map +1 -0
  197. package/dist-types/components/data/media-panel.d.ts +58 -0
  198. package/dist-types/components/data/media-panel.d.ts.map +1 -0
  199. package/dist-types/components/data/notion-panel.d.ts +6 -0
  200. package/dist-types/components/data/notion-panel.d.ts.map +1 -0
  201. package/dist-types/components/data/paste-panel.d.ts +7 -0
  202. package/dist-types/components/data/paste-panel.d.ts.map +1 -0
  203. package/dist-types/components/data/project-data-page.d.ts +3 -0
  204. package/dist-types/components/data/project-data-page.d.ts.map +1 -0
  205. package/dist-types/components/data/scrape-panel.d.ts +9 -0
  206. package/dist-types/components/data/scrape-panel.d.ts.map +1 -0
  207. package/dist-types/components/data/tabular-preview.d.ts +10 -0
  208. package/dist-types/components/data/tabular-preview.d.ts.map +1 -0
  209. package/dist-types/components/data/tool-indexing-dialog.d.ts +54 -0
  210. package/dist-types/components/data/tool-indexing-dialog.d.ts.map +1 -0
  211. package/dist-types/components/data/upload-panel.d.ts +7 -0
  212. package/dist-types/components/data/upload-panel.d.ts.map +1 -0
  213. package/dist-types/components/data/web-crawler-status.d.ts +3 -0
  214. package/dist-types/components/data/web-crawler-status.d.ts.map +1 -0
  215. package/dist-types/components/projects/project-form-dialog.d.ts +10 -0
  216. package/dist-types/components/projects/project-form-dialog.d.ts.map +1 -0
  217. package/dist-types/components/projects/project-provider.d.ts +34 -0
  218. package/dist-types/components/projects/project-provider.d.ts.map +1 -0
  219. package/dist-types/components/settings/general/agent-customization-section.d.ts +2 -0
  220. package/dist-types/components/settings/general/agent-customization-section.d.ts.map +1 -0
  221. package/dist-types/components/settings/general/mcp-connections-section.d.ts +2 -0
  222. package/dist-types/components/settings/general/mcp-connections-section.d.ts.map +1 -0
  223. package/dist-types/components/settings/general/model-combobox.d.ts +24 -0
  224. package/dist-types/components/settings/general/model-combobox.d.ts.map +1 -0
  225. package/dist-types/components/settings/general/models-section.d.ts +2 -0
  226. package/dist-types/components/settings/general/models-section.d.ts.map +1 -0
  227. package/dist-types/components/settings/general/sandbox-section.d.ts +7 -0
  228. package/dist-types/components/settings/general/sandbox-section.d.ts.map +1 -0
  229. package/dist-types/components/settings/general/search-scraping-section.d.ts +2 -0
  230. package/dist-types/components/settings/general/search-scraping-section.d.ts.map +1 -0
  231. package/dist-types/components/settings/general/storage-indexing-section.d.ts +2 -0
  232. package/dist-types/components/settings/general/storage-indexing-section.d.ts.map +1 -0
  233. package/dist-types/components/ui/alert-dialog.d.ts +19 -0
  234. package/dist-types/components/ui/alert-dialog.d.ts.map +1 -0
  235. package/dist-types/components/ui/alert.d.ts +11 -0
  236. package/dist-types/components/ui/alert.d.ts.map +1 -0
  237. package/dist-types/components/ui/badge.d.ts +8 -0
  238. package/dist-types/components/ui/badge.d.ts.map +1 -0
  239. package/dist-types/components/ui/card.d.ts +2 -0
  240. package/dist-types/components/ui/card.d.ts.map +1 -0
  241. package/dist-types/components/ui/checkbox.d.ts +4 -0
  242. package/dist-types/components/ui/checkbox.d.ts.map +1 -0
  243. package/dist-types/components/ui/collapsible.d.ts +6 -0
  244. package/dist-types/components/ui/collapsible.d.ts.map +1 -0
  245. package/dist-types/components/ui/command.d.ts +20 -0
  246. package/dist-types/components/ui/command.d.ts.map +1 -0
  247. package/dist-types/components/ui/dialog.d.ts +18 -0
  248. package/dist-types/components/ui/dialog.d.ts.map +1 -0
  249. package/dist-types/components/ui/dropdown-menu.d.ts +30 -0
  250. package/dist-types/components/ui/dropdown-menu.d.ts.map +1 -0
  251. package/dist-types/components/ui/input-group.d.ts +19 -0
  252. package/dist-types/components/ui/input-group.d.ts.map +1 -0
  253. package/dist-types/components/ui/input.d.ts +4 -0
  254. package/dist-types/components/ui/input.d.ts.map +1 -0
  255. package/dist-types/components/ui/line-tabs.d.ts +15 -0
  256. package/dist-types/components/ui/line-tabs.d.ts.map +1 -0
  257. package/dist-types/components/ui/popover.d.ts +10 -0
  258. package/dist-types/components/ui/popover.d.ts.map +1 -0
  259. package/dist-types/components/ui/progress.d.ts +8 -0
  260. package/dist-types/components/ui/progress.d.ts.map +1 -0
  261. package/dist-types/components/ui/scroll-area.d.ts +6 -0
  262. package/dist-types/components/ui/scroll-area.d.ts.map +1 -0
  263. package/dist-types/components/ui/sheet.d.ts +15 -0
  264. package/dist-types/components/ui/sheet.d.ts.map +1 -0
  265. package/dist-types/components/ui/slider.d.ts +4 -0
  266. package/dist-types/components/ui/slider.d.ts.map +1 -0
  267. package/dist-types/components/ui/switch.d.ts +6 -0
  268. package/dist-types/components/ui/switch.d.ts.map +1 -0
  269. package/dist-types/components/ui/table.d.ts +2 -0
  270. package/dist-types/components/ui/table.d.ts.map +1 -0
  271. package/dist-types/components/ui/tabs.d.ts +11 -0
  272. package/dist-types/components/ui/tabs.d.ts.map +1 -0
  273. package/dist-types/components/ui/textarea.d.ts +2 -0
  274. package/dist-types/components/ui/textarea.d.ts.map +1 -0
  275. package/dist-types/components/ui/tooltip.d.ts +7 -0
  276. package/dist-types/components/ui/tooltip.d.ts.map +1 -0
  277. package/dist-types/hooks/use-copy-to-clipboard.d.ts +5 -0
  278. package/dist-types/hooks/use-copy-to-clipboard.d.ts.map +1 -0
  279. package/dist-types/hooks/use-integration-auth.d.ts +10 -0
  280. package/dist-types/hooks/use-integration-auth.d.ts.map +1 -0
  281. package/dist-types/hooks/use-live-media-progress.d.ts +14 -0
  282. package/dist-types/hooks/use-live-media-progress.d.ts.map +1 -0
  283. package/dist-types/hooks/use-notion-auth.d.ts +9 -0
  284. package/dist-types/hooks/use-notion-auth.d.ts.map +1 -0
  285. package/dist-types/host.d.ts +20 -0
  286. package/dist-types/host.d.ts.map +1 -0
  287. package/dist-types/lib/browser-cache.d.ts +7 -0
  288. package/dist-types/lib/browser-cache.d.ts.map +1 -0
  289. package/dist-types/lib/chat/chart-config.d.ts +2 -0
  290. package/dist-types/lib/chat/chart-config.d.ts.map +1 -0
  291. package/dist-types/lib/chat/data-export.d.ts +2 -0
  292. package/dist-types/lib/chat/data-export.d.ts.map +1 -0
  293. package/dist-types/lib/chat/live-tool-progress.d.ts +44 -0
  294. package/dist-types/lib/chat/live-tool-progress.d.ts.map +1 -0
  295. package/dist-types/lib/chat/message-feedback.d.ts +2 -0
  296. package/dist-types/lib/chat/message-feedback.d.ts.map +1 -0
  297. package/dist-types/lib/chat/table-presentation.d.ts +2 -0
  298. package/dist-types/lib/chat/table-presentation.d.ts.map +1 -0
  299. package/dist-types/lib/chat/tool-output.d.ts +3 -0
  300. package/dist-types/lib/chat/tool-output.d.ts.map +1 -0
  301. package/dist-types/lib/chat-supporting-clip.d.ts +11 -0
  302. package/dist-types/lib/chat-supporting-clip.d.ts.map +1 -0
  303. package/dist-types/lib/constants/stores.d.ts +6 -0
  304. package/dist-types/lib/constants/stores.d.ts.map +1 -0
  305. package/dist-types/lib/constants/tools.d.ts +27 -0
  306. package/dist-types/lib/constants/tools.d.ts.map +1 -0
  307. package/dist-types/lib/error-formatter.d.ts +2 -0
  308. package/dist-types/lib/error-formatter.d.ts.map +1 -0
  309. package/dist-types/lib/jobs-client.d.ts +7 -0
  310. package/dist-types/lib/jobs-client.d.ts.map +1 -0
  311. package/dist-types/lib/media/pdf.d.ts +6 -0
  312. package/dist-types/lib/media/pdf.d.ts.map +1 -0
  313. package/dist-types/lib/media/progress.d.ts +61 -0
  314. package/dist-types/lib/media/progress.d.ts.map +1 -0
  315. package/dist-types/lib/media/source-selection.d.ts +10 -0
  316. package/dist-types/lib/media/source-selection.d.ts.map +1 -0
  317. package/dist-types/lib/media/tool-indexing-estimate.d.ts +13 -0
  318. package/dist-types/lib/media/tool-indexing-estimate.d.ts.map +1 -0
  319. package/dist-types/lib/media/video-intelligence-capacity.d.ts +2 -0
  320. package/dist-types/lib/media/video-intelligence-capacity.d.ts.map +1 -0
  321. package/dist-types/lib/media/video-runtime-scope.d.ts +2 -0
  322. package/dist-types/lib/media/video-runtime-scope.d.ts.map +1 -0
  323. package/dist-types/lib/shared/error-formatter.d.ts +2 -0
  324. package/dist-types/lib/shared/error-formatter.d.ts.map +1 -0
  325. package/dist-types/lib/utils.d.ts +3 -0
  326. package/dist-types/lib/utils.d.ts.map +1 -0
  327. package/dist-types/storage.d.ts +5 -0
  328. package/dist-types/storage.d.ts.map +1 -0
  329. package/dist-types/store/chat-store.d.ts +19 -0
  330. package/dist-types/store/chat-store.d.ts.map +1 -0
  331. package/dist-types/store/scrape-store.d.ts +42 -0
  332. package/dist-types/store/scrape-store.d.ts.map +1 -0
  333. package/package.json +579 -3
  334. package/scripts/build-assets.mjs +12 -0
  335. package/src/asset-data.json +1 -0
  336. package/src/asset-server.ts +19 -0
  337. package/src/components/alerts/generic-alert.tsx +76 -0
  338. package/src/components/chat/canvas/doc-editor-provider.tsx +338 -0
  339. package/src/components/chat/canvas/document-canvas.tsx +98 -0
  340. package/src/components/chat/canvas/document-field.tsx +167 -0
  341. package/src/components/chat/canvas/document-viewer.tsx +446 -0
  342. package/src/components/chat/canvas/file-attachment-button.tsx +88 -0
  343. package/src/components/chat/chat-workspace.tsx +1552 -0
  344. package/src/components/chat/message-actions.tsx +96 -0
  345. package/src/components/chat/message-item.tsx +1398 -0
  346. package/src/components/chat/message-queue.tsx +86 -0
  347. package/src/components/chat/tools/chat-chart.tsx +558 -0
  348. package/src/components/chat/tools/chat-citations.tsx +152 -0
  349. package/src/components/chat/tools/chat-data-table.tsx +373 -0
  350. package/src/components/chat/tools/chat-file-export.tsx +49 -0
  351. package/src/components/chat/tools/chat-media-preview.tsx +466 -0
  352. package/src/components/chat/tools/chat-sandbox-result.tsx +130 -0
  353. package/src/components/chat/tools/chat-signature-request.tsx +810 -0
  354. package/src/components/chat/tools/chat-tabs.tsx +59 -0
  355. package/src/components/chat/tools/corpus-data-result.tsx +194 -0
  356. package/src/components/chat/tools/knowledge-base-result.tsx +345 -0
  357. package/src/components/chat/tools/visual-placement-modal.tsx +308 -0
  358. package/src/components/configure/custom-model-modal.tsx +209 -0
  359. package/src/components/configure/store-fields.tsx +129 -0
  360. package/src/components/data/corpus-panel.tsx +1223 -0
  361. package/src/components/data/data-entry-mode-switch.tsx +53 -0
  362. package/src/components/data/data-primary-action.ts +6 -0
  363. package/src/components/data/data-workspace.tsx +623 -0
  364. package/src/components/data/groups-panel.tsx +530 -0
  365. package/src/components/data/integration-resources-panel.tsx +282 -0
  366. package/src/components/data/integrations-panel.tsx +392 -0
  367. package/src/components/data/jobs-panel.tsx +490 -0
  368. package/src/components/data/jobs-tracking-button.tsx +224 -0
  369. package/src/components/data/live-media-progress.tsx +79 -0
  370. package/src/components/data/media-indexing-jobs-panel.tsx +190 -0
  371. package/src/components/data/media-panel.tsx +2905 -0
  372. package/src/components/data/notion-panel.tsx +408 -0
  373. package/src/components/data/paste-panel.tsx +82 -0
  374. package/src/components/data/project-data-page.tsx +55 -0
  375. package/src/components/data/scrape-panel.tsx +1290 -0
  376. package/src/components/data/tabular-preview.tsx +205 -0
  377. package/src/components/data/tool-indexing-dialog.tsx +210 -0
  378. package/src/components/data/upload-panel.tsx +1313 -0
  379. package/src/components/data/web-crawler-status.tsx +132 -0
  380. package/src/components/projects/project-form-dialog.tsx +97 -0
  381. package/src/components/projects/project-provider.tsx +143 -0
  382. package/src/components/settings/general/agent-customization-section.tsx +620 -0
  383. package/src/components/settings/general/mcp-connections-section.tsx +907 -0
  384. package/src/components/settings/general/model-combobox.tsx +235 -0
  385. package/src/components/settings/general/models-section.tsx +909 -0
  386. package/src/components/settings/general/sandbox-section.tsx +394 -0
  387. package/src/components/settings/general/search-scraping-section.tsx +757 -0
  388. package/src/components/settings/general/storage-indexing-section.tsx +394 -0
  389. package/src/components/ui/alert-dialog.tsx +162 -0
  390. package/src/components/ui/alert.tsx +69 -0
  391. package/src/components/ui/badge.tsx +49 -0
  392. package/src/components/ui/card.tsx +9 -0
  393. package/src/components/ui/checkbox.tsx +28 -0
  394. package/src/components/ui/collapsible.tsx +17 -0
  395. package/src/components/ui/command.tsx +175 -0
  396. package/src/components/ui/dialog.tsx +138 -0
  397. package/src/components/ui/dropdown-menu.tsx +267 -0
  398. package/src/components/ui/input-group.tsx +146 -0
  399. package/src/components/ui/input.tsx +19 -0
  400. package/src/components/ui/line-tabs.tsx +49 -0
  401. package/src/components/ui/popover.tsx +77 -0
  402. package/src/components/ui/progress.tsx +66 -0
  403. package/src/components/ui/scroll-area.tsx +51 -0
  404. package/src/components/ui/sheet.tsx +125 -0
  405. package/src/components/ui/slider.tsx +52 -0
  406. package/src/components/ui/switch.tsx +32 -0
  407. package/src/components/ui/table.tsx +10 -0
  408. package/src/components/ui/tabs.tsx +77 -0
  409. package/src/components/ui/textarea.tsx +1 -0
  410. package/src/components/ui/tooltip.tsx +54 -0
  411. package/src/hooks/use-copy-to-clipboard.ts +25 -0
  412. package/src/hooks/use-integration-auth.ts +39 -0
  413. package/src/hooks/use-live-media-progress.ts +71 -0
  414. package/src/hooks/use-notion-auth.ts +66 -0
  415. package/src/host.ts +55 -0
  416. package/src/lib/browser-cache.ts +17 -0
  417. package/src/lib/chat/chart-config.ts +1 -0
  418. package/src/lib/chat/data-export.ts +1 -0
  419. package/src/lib/chat/live-tool-progress.ts +69 -0
  420. package/src/lib/chat/message-feedback.ts +1 -0
  421. package/src/lib/chat/table-presentation.ts +1 -0
  422. package/src/lib/chat/tool-output.ts +14 -0
  423. package/src/lib/chat-supporting-clip.ts +16 -0
  424. package/src/lib/constants/stores.ts +12 -0
  425. package/src/lib/constants/tools.ts +178 -0
  426. package/src/lib/error-formatter.ts +30 -0
  427. package/src/lib/jobs-client.ts +27 -0
  428. package/src/lib/media/pdf.ts +57 -0
  429. package/src/lib/media/progress.ts +209 -0
  430. package/src/lib/media/source-selection.ts +47 -0
  431. package/src/lib/media/tool-indexing-estimate.ts +39 -0
  432. package/src/lib/media/video-intelligence-capacity.ts +1 -0
  433. package/src/lib/media/video-runtime-scope.ts +1 -0
  434. package/src/lib/shared/error-formatter.ts +1 -0
  435. package/src/lib/utils.ts +6 -0
  436. package/src/source.css +1 -0
  437. package/src/storage.ts +16 -0
  438. package/src/store/chat-store.ts +19 -0
  439. package/src/store/scrape-store.ts +76 -0
  440. package/src/styles.d.ts +1 -0
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { ChevronDown, ChevronUp, ExternalLink, FileText, Play, TriangleAlert } from 'lucide-react';
5
+ import { ChatMediaPreview } from './chat-media-preview';
6
+
7
+ export interface ChatCitationItem {
8
+ label: string;
9
+ detail?: string;
10
+ timestampSecs?: number;
11
+ seekUrl?: string;
12
+ conflicted?: boolean;
13
+ }
14
+
15
+ export interface ChatCitationsUi {
16
+ kind: 'citations';
17
+ title?: string;
18
+ mediaUrl?: string;
19
+ /** Original public source, when it has a browser-supported player. */
20
+ sourceUrl?: string;
21
+ mediaType?: 'video' | 'audio';
22
+ fileName?: string;
23
+ primaryTimestampSecs?: number;
24
+ primaryEndSecs?: number;
25
+ items: ChatCitationItem[];
26
+ }
27
+
28
+ function formatTimestamp(seconds: number) {
29
+ const total = Math.max(0, Math.floor(seconds));
30
+ const hours = Math.floor(total / 3600);
31
+ const minutes = Math.floor((total % 3600) / 60);
32
+ const secs = total % 60;
33
+ return hours > 0
34
+ ? `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
35
+ : `${minutes}:${String(secs).padStart(2, '0')}`;
36
+ }
37
+
38
+ /**
39
+ * Generic citation list + optional supporting clip, part of the same
40
+ * host-generic `output.ui.kind` contract as notice/card/table
41
+ * (message-item.tsx) -- any tool can return this shape, not just video ones.
42
+ * The first assistant message may opt into opening one supporting clip. All
43
+ * later clips remain compact references until the user opens them.
44
+ */
45
+ export function ChatCitations({
46
+ ui,
47
+ assetId,
48
+ autoOpenSupportingClip = false,
49
+ }: {
50
+ ui: ChatCitationsUi;
51
+ assetId?: string;
52
+ autoOpenSupportingClip?: boolean;
53
+ }) {
54
+ const [clipOpen, setClipOpen] = useState(false);
55
+ const [sourcesOpen, setSourcesOpen] = useState(false);
56
+ useEffect(() => {
57
+ setClipOpen(!!(autoOpenSupportingClip && ui.mediaUrl));
58
+ }, [autoOpenSupportingClip, ui.mediaUrl]);
59
+ if (!ui.items?.length && !ui.mediaUrl) return null;
60
+
61
+ return (
62
+ <div className="mb-1 w-full max-w-2xl" data-testid="chat-citations">
63
+ {ui.mediaUrl ? (
64
+ <div className="mb-2">
65
+ <button
66
+ type="button"
67
+ onClick={() => setClipOpen((open) => !open)}
68
+ className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground"
69
+ >
70
+ <Play className="size-3" />
71
+ <span className="font-medium">{clipOpen ? 'Hide' : 'Watch'} supporting clip</span>
72
+ {typeof ui.primaryTimestampSecs === 'number' ? (
73
+ <span>· starts at {formatTimestamp(ui.primaryTimestampSecs)}</span>
74
+ ) : null}
75
+ </button>
76
+ {clipOpen ? (
77
+ <div className="mt-1.5">
78
+ <ChatMediaPreview
79
+ assetId={assetId ?? 'chat-citation'}
80
+ mediaType={ui.mediaType ?? 'video'}
81
+ fileName={ui.fileName}
82
+ mediaUrl={ui.mediaUrl}
83
+ sourceUrl={ui.sourceUrl ?? ui.mediaUrl}
84
+ startSecs={ui.primaryTimestampSecs}
85
+ endSecs={ui.primaryEndSecs}
86
+ />
87
+ </div>
88
+ ) : null}
89
+ </div>
90
+ ) : null}
91
+
92
+ {ui.items?.length ? (
93
+ <div className="overflow-hidden rounded-lg border border-border/70 bg-card/60">
94
+ <button
95
+ type="button"
96
+ onClick={() => setSourcesOpen((open) => !open)}
97
+ className="flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-xs transition-colors hover:bg-muted/40"
98
+ aria-expanded={sourcesOpen}
99
+ >
100
+ <span className="flex min-w-0 items-center gap-2 text-muted-foreground">
101
+ <FileText className="size-3.5 shrink-0" />
102
+ <span className="font-medium text-foreground">{ui.title ?? 'Sources'}</span>
103
+ </span>
104
+ <span className="flex shrink-0 items-center gap-1 text-[11px] text-muted-foreground">
105
+ {ui.items.length} citation{ui.items.length === 1 ? '' : 's'}
106
+ {sourcesOpen ? <ChevronUp className="size-3" /> : <ChevronDown className="size-3" />}
107
+ </span>
108
+ </button>
109
+
110
+ {sourcesOpen ? (
111
+ <div className="border-t border-border/60 px-2 py-1.5">
112
+ {ui.items.map((item, i) => (
113
+ <div
114
+ key={i}
115
+ className="flex items-start gap-2 rounded-md px-1.5 py-2 hover:bg-muted/40"
116
+ >
117
+ <div className="min-w-0 flex-1">
118
+ <div className="flex items-center gap-2">
119
+ {typeof item.timestampSecs === 'number' ? (
120
+ <span className="text-[11px] font-semibold tabular-nums text-foreground">
121
+ {formatTimestamp(item.timestampSecs)}
122
+ </span>
123
+ ) : null}
124
+ <span className="text-[10px] text-muted-foreground">{item.label}</span>
125
+ {item.conflicted ? (
126
+ <TriangleAlert
127
+ className="size-3 shrink-0 text-amber-600 dark:text-amber-400"
128
+ aria-label="Conflicting evidence"
129
+ />
130
+ ) : null}
131
+ </div>
132
+ </div>
133
+ {item.seekUrl ? (
134
+ <a
135
+ href={item.seekUrl}
136
+ target="_blank"
137
+ rel="noreferrer"
138
+ aria-label={`Open source: ${item.label}`}
139
+ className="mt-0.5 shrink-0 p-1 text-muted-foreground transition-colors hover:text-foreground"
140
+ >
141
+ <ExternalLink className="size-3.5" />
142
+ </a>
143
+ ) : null}
144
+ </div>
145
+ ))}
146
+ </div>
147
+ ) : null}
148
+ </div>
149
+ ) : null}
150
+ </div>
151
+ );
152
+ }
@@ -0,0 +1,373 @@
1
+ 'use client';
2
+
3
+ import { useState, useMemo, useCallback } from 'react';
4
+ import {
5
+ ArrowUpDown,
6
+ ArrowUp,
7
+ ArrowDown,
8
+ Copy,
9
+ Download,
10
+ ChevronLeft,
11
+ ChevronRight,
12
+ Check,
13
+ Search,
14
+ X,
15
+ Type,
16
+ Hash,
17
+ CalendarDays,
18
+ Table2,
19
+ } from 'lucide-react';
20
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../../ui/table';
21
+ import { Button } from '@larkup/workspace-ui/button';
22
+ import { Input } from '../../ui/input';
23
+ import { Select, SelectContent, SelectItem, SelectTrigger } from '@larkup/workspace-ui/select';
24
+ import { cn } from '../../../lib/utils';
25
+ import {
26
+ DEFAULT_CHAT_TABLE_PAGE_SIZE,
27
+ normalizeTableData,
28
+ } from '../../../lib/chat/table-presentation';
29
+
30
+ export interface DataTableConfig {
31
+ columns: string[];
32
+ rows: Record<string, any>[];
33
+ totalRows: number;
34
+ aggregationResults?: Record<string, number>;
35
+ }
36
+
37
+ export interface ChatDataTableProps {
38
+ config: DataTableConfig;
39
+ /** Tool output is evidence for the answer, not the answer itself. Keep it
40
+ * tucked away until the user asks to inspect the rows. */
41
+ compact?: boolean;
42
+ }
43
+
44
+ function formatCell(value: any): string {
45
+ if (value === null || value === undefined) return '—';
46
+ if (typeof value === 'number') {
47
+ return value.toLocaleString(undefined, { maximumFractionDigits: 4 });
48
+ }
49
+ return String(value);
50
+ }
51
+
52
+ function downloadCSV(columns: string[], rows: Record<string, any>[]) {
53
+ const csv = [
54
+ columns.join(','),
55
+ ...rows.map((row) =>
56
+ columns
57
+ .map((c) => {
58
+ const v = String(row[c] ?? '');
59
+ return v.includes(',') || v.includes('"') ? `"${v.replace(/"/g, '""')}"` : v;
60
+ })
61
+ .join(','),
62
+ ),
63
+ ].join('\n');
64
+
65
+ const blob = new Blob([csv], { type: 'text/csv' });
66
+ const url = URL.createObjectURL(blob);
67
+ const a = document.createElement('a');
68
+ a.href = url;
69
+ a.download = 'data.csv';
70
+ a.click();
71
+ URL.revokeObjectURL(url);
72
+ }
73
+
74
+ function copyTable(columns: string[], rows: Record<string, any>[]) {
75
+ const text = [
76
+ columns.join('\t'),
77
+ ...rows.map((row) => columns.map((c) => String(row[c] ?? '')).join('\t')),
78
+ ].join('\n');
79
+ navigator.clipboard.writeText(text);
80
+ }
81
+
82
+ /* Column type indicators */
83
+ function columnTypeIndicator(
84
+ col: string,
85
+ rows: Record<string, any>[],
86
+ ): { icon: React.ReactNode; label: string } {
87
+ const sample = rows.slice(0, 10).map((r) => r[col]);
88
+ const nonNull = sample.filter((v) => v !== null && v !== undefined && v !== '');
89
+ if (nonNull.length === 0)
90
+ return { icon: <Type className="size-3 text-blue-500" />, label: 'text' };
91
+ const allNum = nonNull.every((v) => !isNaN(Number(v)));
92
+ if (allNum) return { icon: <Hash className="size-3 text-emerald-500" />, label: 'number' };
93
+ const allDate = nonNull.every((v) => !isNaN(Date.parse(String(v))));
94
+ if (allDate) return { icon: <CalendarDays className="size-3 text-orange-500" />, label: 'date' };
95
+ return { icon: <Type className="size-3 text-blue-500" />, label: 'text' };
96
+ }
97
+
98
+ const PAGE_SIZES = [DEFAULT_CHAT_TABLE_PAGE_SIZE, 25, 50, 100];
99
+
100
+ export function ChatDataTable({ config, compact = false }: ChatDataTableProps) {
101
+ const normalized = useMemo(
102
+ () => normalizeTableData(config.columns, config.rows),
103
+ [config.columns, config.rows],
104
+ );
105
+ const { columns, rows } = normalized;
106
+ const { totalRows, aggregationResults } = config;
107
+ const [sortCol, setSortCol] = useState<string | null>(null);
108
+ const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
109
+ const [page, setPage] = useState(0);
110
+ const [pageSize, setPageSize] = useState(DEFAULT_CHAT_TABLE_PAGE_SIZE);
111
+ const [searchQuery, setSearchQuery] = useState('');
112
+ const [copied, setCopied] = useState(false);
113
+ const [isExpanded, setIsExpanded] = useState(!compact);
114
+
115
+ // Filter rows by search
116
+ const filteredRows = useMemo(() => {
117
+ if (!searchQuery.trim()) return rows;
118
+ const q = searchQuery.toLowerCase();
119
+ return rows.filter((row) =>
120
+ columns.some((col) =>
121
+ String(row[col] ?? '')
122
+ .toLowerCase()
123
+ .includes(q),
124
+ ),
125
+ );
126
+ }, [rows, columns, searchQuery]);
127
+
128
+ // Sort
129
+ const sortedRows = useMemo(() => {
130
+ if (!sortCol) return filteredRows;
131
+ return [...filteredRows].sort((a, b) => {
132
+ const va = a[sortCol];
133
+ const vb = b[sortCol];
134
+ if (va == null && vb == null) return 0;
135
+ if (va == null) return 1;
136
+ if (vb == null) return -1;
137
+ const na = Number(va);
138
+ const nb = Number(vb);
139
+ if (!isNaN(na) && !isNaN(nb)) return sortDir === 'asc' ? na - nb : nb - na;
140
+ return sortDir === 'asc'
141
+ ? String(va).localeCompare(String(vb))
142
+ : String(vb).localeCompare(String(va));
143
+ });
144
+ }, [filteredRows, sortCol, sortDir]);
145
+
146
+ // Pagination
147
+ const totalPages = Math.max(1, Math.ceil(sortedRows.length / pageSize));
148
+ const displayRows = sortedRows.slice(page * pageSize, (page + 1) * pageSize);
149
+
150
+ const handleSort = useCallback(
151
+ (col: string) => {
152
+ if (sortCol === col) {
153
+ setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'));
154
+ } else {
155
+ setSortCol(col);
156
+ setSortDir('asc');
157
+ }
158
+ setPage(0);
159
+ },
160
+ [sortCol],
161
+ );
162
+
163
+ const handleCopy = useCallback(() => {
164
+ copyTable(columns, rows);
165
+ setCopied(true);
166
+ setTimeout(() => setCopied(false), 2000);
167
+ }, [columns, rows]);
168
+
169
+ const handleDownload = useCallback(() => downloadCSV(columns, rows), [columns, rows]);
170
+
171
+ if (columns.length === 0 || rows.length === 0) {
172
+ return <div className="py-4 text-center text-sm text-muted-foreground">No data returned</div>;
173
+ }
174
+
175
+ if (compact && !isExpanded) {
176
+ const count = totalRows || rows.length;
177
+ return (
178
+ <button
179
+ type="button"
180
+ onClick={() => setIsExpanded(true)}
181
+ className="my-1 inline-flex items-center gap-2 rounded-md border border-border/50 bg-muted/30 px-2.5 py-1.5 text-xs text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
182
+ >
183
+ <Table2 className="size-3.5" />
184
+ <span>Data checked</span>
185
+ <span className="rounded-full bg-secondary px-1.5 py-0.5 text-[10px] tabular-nums text-foreground">
186
+ {count.toLocaleString()} {count === 1 ? 'row' : 'rows'}
187
+ </span>
188
+ </button>
189
+ );
190
+ }
191
+
192
+ return (
193
+ <div className="overflow-hidden rounded-xl border border-border/90 bg-muted my-4 animate-in fade-in slide-in-from-bottom-2 duration-500 [&_*:focus]:outline-none [&_*:focus-visible]:ring-0">
194
+ {/* Aggregation results (KPI cards) */}
195
+ {aggregationResults && Object.keys(aggregationResults).length > 0 && (
196
+ <div className="flex flex-wrap gap-3 border-b border-border/30 px-4 py-3">
197
+ {Object.entries(aggregationResults).map(([key, value]) => {
198
+ const [op, ...colParts] = key.split('_');
199
+ const col = colParts.join('_');
200
+ return (
201
+ <div key={key} className="flex flex-col rounded-lg bg-transparent px-2 py-1">
202
+ <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
203
+ {op} of {col}
204
+ </span>
205
+ <span className="text-lg font-semibold tabular-nums text-foreground">
206
+ {typeof value === 'number'
207
+ ? value.toLocaleString(undefined, {
208
+ maximumFractionDigits: 4,
209
+ })
210
+ : value}
211
+ </span>
212
+ </div>
213
+ );
214
+ })}
215
+ </div>
216
+ )}
217
+
218
+ {/* Toolbar: search + actions */}
219
+ <div className="flex items-center justify-between gap-2 border-b border-border/30 px-3 py-2">
220
+ <div className="relative flex-1 max-w-60">
221
+ <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground" />
222
+ <Input
223
+ value={searchQuery}
224
+ onChange={(e) => {
225
+ setSearchQuery(e.target.value);
226
+ setPage(0);
227
+ }}
228
+ placeholder="Search rows…"
229
+ className="h-7 pl-8 pr-8 text-xs border-border/40 focus-visible:ring-0 focus-visible:border-border/60"
230
+ />
231
+ {searchQuery && (
232
+ <button
233
+ type="button"
234
+ onClick={() => setSearchQuery('')}
235
+ className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
236
+ >
237
+ <X className="size-3" />
238
+ </button>
239
+ )}
240
+ </div>
241
+ <div className="flex items-center gap-1">
242
+ <Button
243
+ variant="ghost"
244
+ size="sm"
245
+ onClick={handleCopy}
246
+ className="h-7 gap-1.5 text-[11px] text-muted-foreground focus:ring-0 focus-visible:ring-0"
247
+ >
248
+ {copied ? <Check className="size-3 text-emerald-500" /> : <Copy className="size-3" />}
249
+ {copied ? 'Copied' : 'Copy'}
250
+ </Button>
251
+ <Button
252
+ variant="ghost"
253
+ size="sm"
254
+ onClick={handleDownload}
255
+ className="h-7 gap-1.5 text-[11px] text-muted-foreground focus:ring-0 focus-visible:ring-0"
256
+ >
257
+ <Download className="size-3" />
258
+ CSV
259
+ </Button>
260
+ </div>
261
+ </div>
262
+
263
+ {/* Table */}
264
+ <div className="overflow-x-auto [&::-webkit-scrollbar]:hidden">
265
+ <Table>
266
+ <TableHeader>
267
+ <TableRow className="bg-background hover:bg-background border-b border-border/30">
268
+ {columns.map((col) => {
269
+ const { icon } = columnTypeIndicator(col, rows);
270
+ return (
271
+ <TableHead
272
+ key={col}
273
+ className="cursor-pointer select-none px-3 py-2.5 text-xs font-medium transition hover:text-foreground"
274
+ onClick={() => handleSort(col)}
275
+ >
276
+ <span className="flex items-center gap-1.5">
277
+ <span className="flex items-center justify-center size-5 bg-background border border-border/50 rounded-sm">
278
+ {icon}
279
+ </span>
280
+ <span>{col}</span>
281
+ {sortCol === col ? (
282
+ sortDir === 'asc' ? (
283
+ <ArrowUp className="size-3 text-primary" />
284
+ ) : (
285
+ <ArrowDown className="size-3 text-primary" />
286
+ )
287
+ ) : (
288
+ <ArrowUpDown className="size-3 opacity-20" />
289
+ )}
290
+ </span>
291
+ </TableHead>
292
+ );
293
+ })}
294
+ </TableRow>
295
+ </TableHeader>
296
+ <TableBody>
297
+ {displayRows.map((row, i) => (
298
+ <TableRow
299
+ key={i}
300
+ className={cn('transition border-border/25', i % 2 === 1 && 'bg-muted/8')}
301
+ >
302
+ {columns.map((col) => (
303
+ <TableCell
304
+ key={col}
305
+ className="max-w-55 truncate px-3 py-2 text-xs tabular-nums"
306
+ title={String(row[col] ?? '')}
307
+ >
308
+ {formatCell(row[col])}
309
+ </TableCell>
310
+ ))}
311
+ </TableRow>
312
+ ))}
313
+ </TableBody>
314
+ </Table>
315
+ </div>
316
+
317
+ {/* Pagination footer */}
318
+ <div className="flex items-center justify-between border-t border-border/30 px-3 py-2">
319
+ <div className="flex items-center gap-2">
320
+ <span className="text-[11px] text-muted-foreground">
321
+ {filteredRows.length !== rows.length ? `${filteredRows.length} matched of ` : ''}
322
+ {totalRows.toLocaleString()} rows
323
+ </span>
324
+ <div className="h-3 w-px bg-border/40" />
325
+ <div className="flex items-center gap-1.5">
326
+ <span className="text-[11px] text-muted-foreground">Show</span>
327
+ <Select
328
+ value={String(pageSize)}
329
+ onValueChange={(v) => {
330
+ setPageSize(Number(v));
331
+ setPage(0);
332
+ }}
333
+ >
334
+ <SelectTrigger className="h-6 w-13 text-[11px] px-2 focus:ring-0 focus-visible:ring-0">
335
+ {pageSize}
336
+ </SelectTrigger>
337
+ <SelectContent>
338
+ {PAGE_SIZES.map((s) => (
339
+ <SelectItem key={s} value={String(s)} className="text-xs">
340
+ {s}
341
+ </SelectItem>
342
+ ))}
343
+ </SelectContent>
344
+ </Select>
345
+ </div>
346
+ </div>
347
+ <div className="flex items-center gap-1">
348
+ <span className="text-[11px] tabular-nums text-muted-foreground mr-1">
349
+ {page + 1} / {totalPages}
350
+ </span>
351
+ <Button
352
+ variant="ghost"
353
+ size="icon"
354
+ className="h-7 w-7 focus:ring-0 focus-visible:ring-0"
355
+ disabled={page === 0}
356
+ onClick={() => setPage((p) => p - 1)}
357
+ >
358
+ <ChevronLeft className="size-3.5" />
359
+ </Button>
360
+ <Button
361
+ variant="ghost"
362
+ size="icon"
363
+ className="h-7 w-7 focus:ring-0 focus-visible:ring-0"
364
+ disabled={page >= totalPages - 1}
365
+ onClick={() => setPage((p) => p + 1)}
366
+ >
367
+ <ChevronRight className="size-3.5" />
368
+ </Button>
369
+ </div>
370
+ </div>
371
+ </div>
372
+ );
373
+ }
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useState } from 'react';
4
+ import { Check, Download, FileSpreadsheet, FileText } from 'lucide-react';
5
+ import { Button } from '@larkup/workspace-ui/button';
6
+ import type { DataExportArtifact } from '../../../lib/chat/data-export';
7
+
8
+ function base64Bytes(value: string) {
9
+ const binary = window.atob(value);
10
+ const bytes = new Uint8Array(binary.length);
11
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
12
+ return bytes;
13
+ }
14
+
15
+ export function ChatFileExport({ artifact }: { artifact: DataExportArtifact }) {
16
+ const [downloaded, setDownloaded] = useState(false);
17
+ const download = useCallback(() => {
18
+ const blob = new Blob([base64Bytes(artifact.fileBase64)], { type: artifact.mimeType });
19
+ const url = URL.createObjectURL(blob);
20
+ const anchor = document.createElement('a');
21
+ anchor.href = url;
22
+ anchor.download = artifact.fileName;
23
+ document.body.appendChild(anchor);
24
+ anchor.click();
25
+ anchor.remove();
26
+ window.setTimeout(() => URL.revokeObjectURL(url), 1_000);
27
+ setDownloaded(true);
28
+ }, [artifact]);
29
+ const Icon = artifact.format === 'xlsx' || artifact.format === 'csv' ? FileSpreadsheet : FileText;
30
+
31
+ return (
32
+ <div className="my-3 flex items-center gap-3 rounded-xl border border-border/70 bg-card px-3.5 py-3">
33
+ <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
34
+ <Icon className="size-4.5" />
35
+ </div>
36
+ <div className="min-w-0 flex-1">
37
+ <div className="truncate text-sm font-medium text-foreground">{artifact.fileName}</div>
38
+ <div className="mt-0.5 text-xs text-muted-foreground">
39
+ {artifact.rowCount.toLocaleString()} {artifact.rowCount === 1 ? 'row' : 'rows'} ·{' '}
40
+ {artifact.format.toUpperCase()}
41
+ </div>
42
+ </div>
43
+ <Button size="sm" onClick={download} className="h-8 shrink-0 gap-1.5 px-3 text-xs">
44
+ {downloaded ? <Check className="size-3.5" /> : <Download className="size-3.5" />}
45
+ {downloaded ? 'Downloaded' : 'Download'}
46
+ </Button>
47
+ </div>
48
+ );
49
+ }