@larkup/workspace-app 0.0.0-stage → 0.2.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,466 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
+ import { Image as ImageIcon, Film, AudioLines, Maximize2, X } from 'lucide-react';
5
+ import { workspaceUrl } from '../../../host';
6
+
7
+ /**
8
+ * Inline media preview for chat messages.
9
+ *
10
+ * Renders a compact preview of media referenced in assistant responses:
11
+ * - Images: thumbnail with lightbox expand
12
+ * - Video: compact player with timestamp range
13
+ * - Audio: mini player bar
14
+ */
15
+
16
+ export function ChatMediaPreview({
17
+ assetId,
18
+ mediaType,
19
+ fileName,
20
+ mediaUrl,
21
+ sourceUrl,
22
+ startSecs,
23
+ endSecs,
24
+ }: {
25
+ assetId: string;
26
+ mediaType: 'image' | 'video' | 'audio' | 'frame-preview';
27
+ fileName?: string;
28
+ mediaUrl?: string;
29
+ sourceUrl?: string;
30
+ startSecs?: number;
31
+ endSecs?: number;
32
+ }) {
33
+ const [expanded, setExpanded] = useState(false);
34
+ const [imageUnavailable, setImageUnavailable] = useState(false);
35
+ const videoRef = useRef<HTMLVideoElement>(null);
36
+ // Media is rendered only from a server-validated presentMedia result. Do
37
+ // not invent a URL from an identifier emitted in model text: it could point
38
+ // to a missing or unrelated asset and creates the misleading dummy preview
39
+ // shown in the previous chat experience.
40
+ const assetUrl = mediaUrl ? workspaceUrl(mediaUrl) : undefined;
41
+ const playbackUrl =
42
+ assetUrl && startSecs !== undefined
43
+ ? `${assetUrl.split('#')[0]}#t=${startSecs}${endSecs !== undefined ? `,${endSecs}` : ''}`
44
+ : assetUrl;
45
+ const providerEmbedUrl = useMemo(
46
+ () => (mediaType === 'video' ? getProviderEmbedUrl(sourceUrl, startSecs, endSecs) : undefined),
47
+ [mediaType, sourceUrl, startSecs, endSecs],
48
+ );
49
+ const hasClipEnd =
50
+ mediaType === 'video' &&
51
+ Number.isFinite(startSecs) &&
52
+ Number.isFinite(endSecs) &&
53
+ (endSecs as number) > (startSecs as number);
54
+ useEffect(() => {
55
+ const video = videoRef.current;
56
+ if (!video || !Number.isFinite(startSecs)) return;
57
+ const seek = () => {
58
+ // Media fragments are not applied consistently after client-side
59
+ // navigation. Setting currentTime after metadata makes the cited moment
60
+ // deterministic for local uploads and downloaded provider videos.
61
+ video.currentTime = Math.max(0, startSecs as number);
62
+ };
63
+ if (video.readyState >= HTMLMediaElement.HAVE_METADATA) seek();
64
+ else video.addEventListener('loadedmetadata', seek, { once: true });
65
+ return () => video.removeEventListener('loadedmetadata', seek);
66
+ }, [playbackUrl, startSecs]);
67
+
68
+ if (!assetUrl) return null;
69
+
70
+ if (mediaType === 'image') {
71
+ if (imageUnavailable) {
72
+ return (
73
+ <div className="my-2 w-full max-w-2xl rounded-lg border border-border bg-card p-3">
74
+ <p className="text-xs text-muted-foreground">This source image is no longer available.</p>
75
+ <MediaCitationFooter
76
+ icon={<ImageIcon className="size-3 text-muted-foreground" />}
77
+ fileName={fileName}
78
+ sourceUrl={sourceUrl}
79
+ />
80
+ </div>
81
+ );
82
+ }
83
+ return (
84
+ <>
85
+ <div className="my-2 w-full max-w-2xl overflow-hidden rounded-lg border border-border bg-card">
86
+ <button
87
+ type="button"
88
+ onClick={() => setExpanded(true)}
89
+ className="group relative block w-full overflow-hidden"
90
+ >
91
+ <img
92
+ src={assetUrl}
93
+ alt={fileName ?? 'Image'}
94
+ className="max-h-105 w-full object-contain"
95
+ loading="lazy"
96
+ onError={() => setImageUnavailable(true)}
97
+ />
98
+ <div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition-all group-hover:bg-black/20 group-hover:opacity-100">
99
+ <Maximize2 className="size-5 text-white" />
100
+ </div>
101
+ </button>
102
+ <MediaCitationFooter
103
+ icon={<ImageIcon className="size-3 text-muted-foreground" />}
104
+ fileName={fileName}
105
+ sourceUrl={sourceUrl}
106
+ />
107
+ </div>
108
+
109
+ {expanded && (
110
+ <div
111
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-8"
112
+ onClick={() => setExpanded(false)}
113
+ >
114
+ <button
115
+ type="button"
116
+ aria-label="Close image preview"
117
+ className="absolute right-4 top-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
118
+ onClick={() => setExpanded(false)}
119
+ >
120
+ <X className="size-5" />
121
+ </button>
122
+ <img
123
+ src={assetUrl}
124
+ alt={fileName ?? 'Image'}
125
+ className="max-h-[80vh] max-w-[90vw] rounded-lg object-contain"
126
+ onClick={(event) => event.stopPropagation()}
127
+ onError={() => setImageUnavailable(true)}
128
+ />
129
+ </div>
130
+ )}
131
+ </>
132
+ );
133
+ }
134
+
135
+ if (mediaType === 'video') {
136
+ return (
137
+ <div className="my-2 w-full max-w-2xl overflow-hidden rounded-lg border border-border bg-card">
138
+ {providerEmbedUrl ? (
139
+ <iframe
140
+ src={providerEmbedUrl}
141
+ title={fileName ? `Watch ${fileName}` : 'Video preview'}
142
+ className="aspect-video w-full bg-black"
143
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
144
+ allowFullScreen
145
+ />
146
+ ) : (
147
+ <video
148
+ ref={videoRef}
149
+ src={playbackUrl}
150
+ controls
151
+ playsInline
152
+ preload="metadata"
153
+ className="max-h-90 w-full bg-black object-contain"
154
+ onTimeUpdate={(event) => {
155
+ // Media-fragment end times are not enforced consistently by
156
+ // browsers. Stop local playback at the cited evidence boundary
157
+ // so a supporting clip never continues into unrelated footage.
158
+ if (hasClipEnd && event.currentTarget.currentTime >= (endSecs as number)) {
159
+ event.currentTarget.pause();
160
+ event.currentTarget.currentTime = endSecs as number;
161
+ }
162
+ }}
163
+ />
164
+ )}
165
+ <MediaCitationFooter
166
+ icon={<Film className="size-3 text-muted-foreground" />}
167
+ fileName={fileName}
168
+ sourceUrl={timestampedMediaUrl(sourceUrl, startSecs, endSecs)}
169
+ timestamp={
170
+ startSecs !== undefined
171
+ ? `${formatTimestamp(startSecs)}${
172
+ endSecs !== undefined ? ` – ${formatTimestamp(endSecs)}` : ''
173
+ }`
174
+ : undefined
175
+ }
176
+ />
177
+ </div>
178
+ );
179
+ }
180
+
181
+ if (mediaType === 'audio') {
182
+ return (
183
+ <div className="my-2 w-full max-w-xl overflow-hidden rounded-lg border border-border bg-card">
184
+ <div className="flex items-center gap-2.5 border-b border-border px-3 py-2">
185
+ <div className="flex size-7 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
186
+ <AudioLines className="size-3.5" />
187
+ </div>
188
+ <div className="min-w-0 flex-1">
189
+ <p className="truncate text-[11px] font-medium text-foreground">
190
+ {fileName ?? 'Audio recording'}
191
+ </p>
192
+ {startSecs !== undefined ? (
193
+ <p className="mt-0.5 text-[10px] tabular-nums text-muted-foreground">
194
+ {formatTimestamp(startSecs)}
195
+ {endSecs !== undefined ? ` – ${formatTimestamp(endSecs)}` : ''}
196
+ </p>
197
+ ) : (
198
+ <p className="mt-0.5 text-[10px] text-muted-foreground">Audio reference</p>
199
+ )}
200
+ </div>
201
+ </div>
202
+ <div className="px-3 py-2">
203
+ <audio src={playbackUrl} controls preload="metadata" className="h-8 w-full" />
204
+ </div>
205
+ {sourceUrl ? (
206
+ <div className="flex justify-end border-t border-border px-3 py-1.5">
207
+ <a
208
+ href={timestampedMediaUrl(sourceUrl, startSecs, endSecs)}
209
+ target="_blank"
210
+ rel="noreferrer"
211
+ className="text-[11px] font-medium text-primary hover:text-primary/80"
212
+ >
213
+ Open source
214
+ </a>
215
+ </div>
216
+ ) : null}
217
+ </div>
218
+ );
219
+ }
220
+
221
+ if (mediaType === 'frame-preview') {
222
+ if (imageUnavailable) {
223
+ return (
224
+ <div className="my-2 w-full max-w-xl rounded-lg border border-border bg-card p-3">
225
+ <p className="text-xs text-muted-foreground">This frame could not be extracted.</p>
226
+ <MediaCitationFooter
227
+ icon={<Film className="size-3 text-muted-foreground" />}
228
+ fileName={fileName}
229
+ sourceUrl={timestampedMediaUrl(sourceUrl, startSecs)}
230
+ timestamp={startSecs !== undefined ? formatTimestamp(startSecs) : undefined}
231
+ />
232
+ </div>
233
+ );
234
+ }
235
+ return (
236
+ <>
237
+ <div className="my-2 w-full max-w-xl overflow-hidden rounded-lg border border-border bg-card">
238
+ <button
239
+ type="button"
240
+ onClick={() => setExpanded(true)}
241
+ className="group relative block w-full overflow-hidden"
242
+ >
243
+ <img
244
+ src={assetUrl}
245
+ alt={`Frame at ${startSecs !== undefined ? formatTimestamp(startSecs) : 'unknown'}`}
246
+ className="max-h-105 w-full object-contain"
247
+ loading="lazy"
248
+ onError={() => setImageUnavailable(true)}
249
+ />
250
+ {startSecs !== undefined && (
251
+ <div className="absolute bottom-2 left-2 rounded-md bg-black/70 px-2 py-0.5">
252
+ <span className="text-[11px] font-medium tabular-nums text-white">
253
+ {formatTimestamp(startSecs)}
254
+ </span>
255
+ </div>
256
+ )}
257
+ <div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition-all group-hover:bg-black/20 group-hover:opacity-100">
258
+ <Maximize2 className="size-5 text-white" />
259
+ </div>
260
+ </button>
261
+ <MediaCitationFooter
262
+ icon={<Film className="size-3 text-muted-foreground" />}
263
+ fileName={fileName ? `${fileName} — Frame` : 'Video Frame'}
264
+ sourceUrl={timestampedMediaUrl(sourceUrl, startSecs)}
265
+ timestamp={startSecs !== undefined ? formatTimestamp(startSecs) : undefined}
266
+ />
267
+ </div>
268
+
269
+ {expanded && (
270
+ <div
271
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-8"
272
+ onClick={() => setExpanded(false)}
273
+ >
274
+ <button
275
+ type="button"
276
+ aria-label="Close frame preview"
277
+ className="absolute right-4 top-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
278
+ onClick={() => setExpanded(false)}
279
+ >
280
+ <X className="size-5" />
281
+ </button>
282
+ <img
283
+ src={assetUrl}
284
+ alt={`Frame at ${startSecs !== undefined ? formatTimestamp(startSecs) : 'unknown'}`}
285
+ className="max-h-[80vh] max-w-[90vw] rounded-lg object-contain"
286
+ onClick={(event) => event.stopPropagation()}
287
+ onError={() => setImageUnavailable(true)}
288
+ />
289
+ </div>
290
+ )}
291
+ </>
292
+ );
293
+ }
294
+
295
+ return null;
296
+ }
297
+
298
+ /**
299
+ * Use a first-party embed only for providers with a known safe player. All
300
+ * other URLs fall back to the locally indexed asset, which also keeps private
301
+ * uploads and direct video links reliable in chat.
302
+ */
303
+ export function getProviderEmbedUrl(
304
+ sourceUrl?: string,
305
+ startSecs?: number,
306
+ endSecs?: number,
307
+ ): string | undefined {
308
+ if (!sourceUrl) return undefined;
309
+
310
+ try {
311
+ const url = new URL(sourceUrl);
312
+ const host = url.hostname.toLowerCase().replace(/^www\./, '');
313
+ const start = Math.max(0, Math.floor(startSecs ?? 0));
314
+ const end =
315
+ Number.isFinite(endSecs) && (endSecs as number) > start ? Math.floor(endSecs!) : undefined;
316
+
317
+ const youtubeEmbedUrl = (videoId: string) => {
318
+ const embed = new URL(`https://www.youtube-nocookie.com/embed/${videoId}`);
319
+ embed.searchParams.set('start', String(start));
320
+ embed.searchParams.set('rel', '0');
321
+ if (end !== undefined) embed.searchParams.set('end', String(end));
322
+ return embed.toString();
323
+ };
324
+
325
+ if (host === 'youtu.be' || host.endsWith('.youtu.be')) {
326
+ const videoId = url.pathname.split('/').filter(Boolean)[0];
327
+ return videoId ? youtubeEmbedUrl(videoId) : undefined;
328
+ }
329
+
330
+ if (host === 'youtube.com' || host.endsWith('.youtube.com')) {
331
+ const pathParts = url.pathname.split('/').filter(Boolean);
332
+ const videoId =
333
+ url.searchParams.get('v') ||
334
+ (['embed', 'shorts', 'live'].includes(pathParts[0]) ? pathParts[1] : undefined);
335
+ return videoId ? youtubeEmbedUrl(videoId) : undefined;
336
+ }
337
+
338
+ if (host === 'vimeo.com' || host.endsWith('.vimeo.com')) {
339
+ const videoId = url.pathname.split('/').find((part) => /^\d+$/.test(part));
340
+ return videoId ? `https://player.vimeo.com/video/${videoId}#t=${start}s` : undefined;
341
+ }
342
+ } catch {
343
+ // The local media URL remains the reliable fallback for malformed or
344
+ // unsupported provider URLs.
345
+ }
346
+
347
+ return undefined;
348
+ }
349
+
350
+ /** Build a provider-aware source link that opens at the cited evidence. */
351
+ export function timestampedMediaUrl(
352
+ sourceUrl?: string,
353
+ startSecs?: number,
354
+ endSecs?: number,
355
+ ): string | undefined {
356
+ if (!sourceUrl || !Number.isFinite(startSecs)) return sourceUrl;
357
+ const start = Math.max(0, Math.floor(startSecs as number));
358
+ const end =
359
+ Number.isFinite(endSecs) && (endSecs as number) > start ? Math.floor(endSecs as number) : null;
360
+ try {
361
+ const url = new URL(sourceUrl, 'http://larkup.local');
362
+ const host = url.hostname.toLowerCase().replace(/^www\./, '');
363
+ if (host === 'youtube.com' || host.endsWith('.youtube.com') || host === 'youtu.be') {
364
+ url.searchParams.set('t', `${start}s`);
365
+ return url.origin === 'http://larkup.local'
366
+ ? `${url.pathname}${url.search}${url.hash}`
367
+ : url.toString();
368
+ }
369
+ const fragment = `t=${start}${end !== null ? `,${end}` : ''}`;
370
+ url.hash = fragment;
371
+ return url.origin === 'http://larkup.local'
372
+ ? `${url.pathname}${url.search}${url.hash}`
373
+ : url.toString();
374
+ } catch {
375
+ return `${sourceUrl.split('#')[0]}#t=${start}${end !== null ? `,${end}` : ''}`;
376
+ }
377
+ }
378
+
379
+ function MediaCitationFooter({
380
+ icon,
381
+ fileName,
382
+ sourceUrl,
383
+ timestamp,
384
+ }: {
385
+ icon: React.ReactNode;
386
+ fileName?: string;
387
+ sourceUrl?: string;
388
+ timestamp?: string;
389
+ }) {
390
+ if (!fileName && !sourceUrl && !timestamp) return null;
391
+
392
+ return (
393
+ <div className="flex items-center justify-between gap-3 border-t border-border px-3 py-1.5">
394
+ <div className="flex min-w-0 items-center gap-1.5">
395
+ {icon}
396
+ <span className="truncate text-[11px] text-muted-foreground">
397
+ {fileName ?? 'Media citation'}
398
+ {timestamp ? ` · ${timestamp}` : ''}
399
+ </span>
400
+ </div>
401
+ {sourceUrl ? (
402
+ <a
403
+ href={sourceUrl}
404
+ target="_blank"
405
+ rel="noreferrer"
406
+ className="shrink-0 text-[11px] font-medium text-primary hover:text-primary/80"
407
+ >
408
+ Open source
409
+ </a>
410
+ ) : null}
411
+ </div>
412
+ );
413
+ }
414
+
415
+ /* Media preview helpers. */
416
+ /* Markdown media reference parser */
417
+ /* End media preview helpers. */
418
+
419
+ /**
420
+ * Parse media references from assistant text.
421
+ *
422
+ * Supported formats:
423
+ * - [IMAGE_REF:assetId]
424
+ * - [IMAGE_REF:assetId:fileName]
425
+ * - [VIDEO_REF:assetId:startSecs-endSecs]
426
+ * - [AUDIO_REF:assetId]
427
+ */
428
+ const MEDIA_REF_REGEX = /\[(IMAGE_REF|VIDEO_REF|AUDIO_REF):([a-f0-9-]+)(?::([^\]]*))?\]/g;
429
+
430
+ export interface ParsedMediaRef {
431
+ fullMatch: string;
432
+ type: 'image' | 'video' | 'audio';
433
+ assetId: string;
434
+ extra?: string;
435
+ startSecs?: number;
436
+ endSecs?: number;
437
+ }
438
+
439
+ export function parseMediaRefs(text: string): ParsedMediaRef[] {
440
+ const refs: ParsedMediaRef[] = [];
441
+ let match: RegExpExecArray | null;
442
+
443
+ while ((match = MEDIA_REF_REGEX.exec(text)) !== null) {
444
+ const [fullMatch, refType, assetId, extra] = match;
445
+ const type = refType === 'IMAGE_REF' ? 'image' : refType === 'VIDEO_REF' ? 'video' : 'audio';
446
+
447
+ let startSecs: number | undefined;
448
+ let endSecs: number | undefined;
449
+
450
+ if ((type === 'video' || type === 'audio') && extra?.includes('-')) {
451
+ const [s, e] = extra.split('-').map(Number);
452
+ if (!isNaN(s)) startSecs = s;
453
+ if (!isNaN(e)) endSecs = e;
454
+ }
455
+
456
+ refs.push({ fullMatch, type, assetId, extra, startSecs, endSecs });
457
+ }
458
+
459
+ return refs;
460
+ }
461
+
462
+ function formatTimestamp(secs: number): string {
463
+ const m = Math.floor(secs / 60);
464
+ const s = Math.floor(secs % 60);
465
+ return `${m}:${String(s).padStart(2, '0')}`;
466
+ }
@@ -0,0 +1,130 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import { ChevronDown, Play, Clock, Image as ImageIcon, Table2 } from 'lucide-react';
5
+ import { ChatDataTable, type DataTableConfig } from './chat-data-table';
6
+ import { Card, CardContent } from '../../ui/card';
7
+
8
+ interface SandboxArtifact {
9
+ name: string;
10
+ mimeType: string;
11
+ data: string; // base64
12
+ }
13
+
14
+ export interface SandboxResultConfig {
15
+ stdout: string;
16
+ stderr: string;
17
+ exitCode: number;
18
+ artifacts: SandboxArtifact[];
19
+ executionTimeMs: number;
20
+ }
21
+
22
+ function tryParseStdoutAsTable(stdout: string): DataTableConfig | null {
23
+ return null;
24
+ }
25
+
26
+ export function ChatSandboxResult({ config }: { config: SandboxResultConfig }) {
27
+ const { stdout, stderr, exitCode, artifacts, executionTimeMs } = config;
28
+
29
+ const images = useMemo(
30
+ () => artifacts.filter((a) => a.mimeType.startsWith('image/')),
31
+ [artifacts],
32
+ );
33
+
34
+ // Try to parse stdout as structured table data
35
+ const stdoutTable = useMemo(() => tryParseStdoutAsTable(stdout), [stdout]);
36
+
37
+ const isSuccess = exitCode === 0;
38
+ const isRuntimeUnavailable =
39
+ /docker\.sock|docker (?:isn't|is not) running|code execution is unavailable|needs python 3/i.test(
40
+ stderr,
41
+ );
42
+
43
+ return (
44
+ <Card className="overflow-hidden animate-in fade-in slide-in-from-bottom-2 duration-500">
45
+ {/* A successful result speaks for itself; retain a clear header only for failures. */}
46
+ {!isSuccess && (
47
+ <div className="flex items-center justify-between border-b border-border/40 px-4 py-2.5">
48
+ <div className="flex items-center gap-2">
49
+ <div className="flex h-6 w-6 items-center justify-center rounded-md bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400">
50
+ <Play className="size-3" />
51
+ </div>
52
+ <span className="text-xs font-medium text-foreground">Analysis failed</span>
53
+ </div>
54
+ <span className="flex items-center gap-1 text-[10px] tabular-nums text-muted-foreground">
55
+ <Clock className="size-3" />
56
+ {executionTimeMs < 1000
57
+ ? `${executionTimeMs}ms`
58
+ : `${(executionTimeMs / 1000).toFixed(1)}s`}
59
+ </span>
60
+ </div>
61
+ )}
62
+
63
+ {/* Generated images (matplotlib charts, etc.) */}
64
+ {images.length > 0 && (
65
+ <div className="space-y-3 p-4">
66
+ {images.map((img, i) => (
67
+ <div key={i} className="overflow-hidden rounded-lg border border-border/40">
68
+ <img
69
+ src={`data:${img.mimeType};base64,${img.data}`}
70
+ alt={img.name}
71
+ className="w-full"
72
+ />
73
+ <div className="flex items-center gap-2 border-t border-border/40 px-3 py-1.5">
74
+ <ImageIcon className="size-3 text-muted-foreground" />
75
+ <span className="text-[10px] text-muted-foreground">{img.name}</span>
76
+ </div>
77
+ </div>
78
+ ))}
79
+ </div>
80
+ )}
81
+
82
+ {/* Structured stdout (rendered as table) */}
83
+ {stdoutTable && (
84
+ <CardContent className="p-3 pt-3">
85
+ <div className="flex items-center gap-1.5 mb-2 text-xs text-muted-foreground">
86
+ <Table2 className="size-3" />
87
+ <span className="font-medium">Analysis Results</span>
88
+ </div>
89
+ <ChatDataTable config={stdoutTable} />
90
+ </CardContent>
91
+ )}
92
+
93
+ {/* Plain stdout (only if not parsed as table) */}
94
+ {stdout && !stdoutTable && (
95
+ <div className="border-b border-border/40 px-4 py-3">
96
+ <pre className="max-h-48 overflow-auto whitespace-pre-wrap text-xs leading-relaxed text-foreground [&::-webkit-scrollbar]:hidden">
97
+ {stdout}
98
+ </pre>
99
+ </div>
100
+ )}
101
+
102
+ {/* Error display */}
103
+ {!isSuccess && (
104
+ <div className="border-t border-border/40 bg-red-50/50 p-4 dark:bg-red-900/10">
105
+ <div className="flex flex-col gap-2">
106
+ <span className="text-sm font-medium text-red-800 dark:text-red-400">
107
+ {isRuntimeUnavailable ? 'Analysis is unavailable' : 'Analysis encountered an issue'}
108
+ </span>
109
+ <span className="text-xs text-red-600/80 dark:text-red-400/80">
110
+ {isRuntimeUnavailable
111
+ ? 'This workspace does not have a code-analysis runtime configured.'
112
+ : 'This calculation could not be completed.'}
113
+ </span>
114
+ {stderr && !isRuntimeUnavailable && (
115
+ <details className="mt-2 group">
116
+ <summary className="cursor-pointer text-xs font-medium text-red-600/70 hover:text-red-600 dark:text-red-400/70 dark:hover:text-red-400 transition-colors list-none flex items-center gap-1">
117
+ <ChevronDown className="size-3 transition-transform group-open:rotate-180" />
118
+ Technical Details
119
+ </summary>
120
+ <pre className="mt-2 max-h-40 overflow-auto whitespace-pre-wrap rounded bg-red-100/50 p-3 text-[11px] leading-relaxed text-red-900/70 dark:bg-red-900/20 dark:text-red-200/70 [&::-webkit-scrollbar]:hidden">
121
+ {stderr}
122
+ </pre>
123
+ </details>
124
+ )}
125
+ </div>
126
+ </div>
127
+ )}
128
+ </Card>
129
+ );
130
+ }