@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,53 @@
1
+ 'use client';
2
+
3
+ import type { LucideIcon } from 'lucide-react';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ export interface DataEntryModeOption {
7
+ value: string;
8
+ label: string;
9
+ icon: LucideIcon;
10
+ }
11
+
12
+ export function DataEntryModeSwitch({
13
+ value,
14
+ onValueChange,
15
+ options,
16
+ label,
17
+ }: {
18
+ value: string;
19
+ onValueChange: (value: string) => void;
20
+ options: readonly DataEntryModeOption[];
21
+ label: string;
22
+ }) {
23
+ return (
24
+ <div
25
+ role="group"
26
+ aria-label={label}
27
+ className="inline-flex h-10 w-fit items-center gap-1 rounded-lg border border-border/90 bg-muted/60 p-1 text-muted-foreground"
28
+ >
29
+ {options.map((option) => {
30
+ const Icon = option.icon;
31
+ const active = value === option.value;
32
+ return (
33
+ <button
34
+ key={option.value}
35
+ type="button"
36
+ aria-pressed={active}
37
+ data-state={active ? 'active' : 'inactive'}
38
+ onClick={() => onValueChange(option.value)}
39
+ className={cn(
40
+ 'inline-flex h-8 items-center justify-center gap-1.5 whitespace-nowrap rounded-md px-3 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50',
41
+ active
42
+ ? 'bg-background text-foreground ring-1 ring-border'
43
+ : 'text-muted-foreground hover:bg-background/60 hover:text-foreground',
44
+ )}
45
+ >
46
+ <Icon className="size-3.5" strokeWidth={active ? 2 : 1.75} aria-hidden="true" />
47
+ {option.label}
48
+ </button>
49
+ );
50
+ })}
51
+ </div>
52
+ );
53
+ }
@@ -0,0 +1,6 @@
1
+ export interface DataPrimaryAction {
2
+ label: string;
3
+ onClick: () => void;
4
+ disabled?: boolean;
5
+ loading?: boolean;
6
+ }
@@ -0,0 +1,623 @@
1
+ 'use client';
2
+
3
+ import { workspaceUrl, workspaceFetch as fetch } from '../../host';
4
+ import { useState, useEffect, useRef, useCallback } from 'react';
5
+ import { useSearchParams, usePathname } from 'next/navigation';
6
+ import Link from 'next/link';
7
+ import useSWR, { useSWRConfig } from 'swr';
8
+ import { toast } from 'sonner';
9
+ import { cn } from '../../lib/utils';
10
+ import type { CrawlJob, DataGroup, SourceDocument, IndexRun, RagConfig } from '@larkup/core/types';
11
+ import { Globe, FileUp, Type, Image, Plug, History, Loader2, Plus, KeyRound } from 'lucide-react';
12
+ import { ScrapePanel } from './scrape-panel';
13
+ import { WebCrawlerStatus } from './web-crawler-status';
14
+ import { PastePanel } from './paste-panel';
15
+ import { UploadPanel } from './upload-panel';
16
+ import { MediaPanel } from './media-panel';
17
+ import { IntegrationsPanel } from './integrations-panel';
18
+ import { JobsPanel } from './jobs-panel';
19
+ import {
20
+ isActiveMediaIndexing,
21
+ MediaIndexingJobsPanel,
22
+ type MediaIndexingResponse,
23
+ } from './media-indexing-jobs-panel';
24
+ import { CorpusPanel } from './corpus-panel';
25
+ import type { DataPrimaryAction } from './data-primary-action';
26
+ import { useProject } from '../projects/project-provider';
27
+ import { WorkspaceLineTabs, WorkspacePageHeading } from '@larkup/workspace-ui';
28
+ import { Button } from '@larkup/workspace-ui/button';
29
+ import { Select, SelectContent, SelectItem, SelectTrigger } from '@larkup/workspace-ui/select';
30
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip';
31
+ import { fetchJobsWithSync } from '../../lib/jobs-client';
32
+ import { videoRuntimeScopeFromConfig } from '../../lib/media/video-runtime-scope';
33
+
34
+ const fetcher = async <T,>(url: string): Promise<T> => {
35
+ const response = await fetch(url);
36
+ const payload = await response.json().catch(() => null);
37
+ if (!response.ok) {
38
+ const message =
39
+ payload &&
40
+ typeof payload === 'object' &&
41
+ 'error' in payload &&
42
+ typeof payload.error === 'string'
43
+ ? payload.error
44
+ : 'Could not load workspace data.';
45
+ throw new Error(message);
46
+ }
47
+ return payload as T;
48
+ };
49
+
50
+ interface DocsResponse {
51
+ documents: SourceDocument[];
52
+ groups: DataGroup[];
53
+ stats: {
54
+ docCount: number;
55
+ charCount: number;
56
+ bySource: Record<string, number>;
57
+ };
58
+ }
59
+
60
+ const SUB_TABS = [
61
+ { id: 'files', label: 'Files', icon: FileUp },
62
+ { id: 'text', label: 'Text', icon: Type },
63
+ { id: 'website', label: 'Website', icon: Globe },
64
+ { id: 'media', label: 'Media', icon: Image },
65
+ { id: 'integrations', label: 'Integrations', icon: Plug },
66
+ ] as const;
67
+
68
+ type TopTabId = 'add' | 'corpus';
69
+ type SubTabId = (typeof SUB_TABS)[number]['id'];
70
+
71
+ function normalizeGroupId(value: string | null): string {
72
+ const groupId = value?.split(',')[0]?.trim();
73
+ return !groupId || groupId === 'undefined' || groupId === 'null' ? 'default' : groupId;
74
+ }
75
+
76
+ const SUB_TAB_INTRO: Record<SubTabId, { title: string; description: string }> = {
77
+ files: {
78
+ title: 'Add Files',
79
+ description: 'Upload PDFs, documents, spreadsheets, CSVs, JSON files, and more.',
80
+ },
81
+ text: {
82
+ title: 'Add Text',
83
+ description: 'Paste notes, documentation, transcripts, or any text you want to search.',
84
+ },
85
+ website: {
86
+ title: 'Add Websites',
87
+ description: 'Add a URL or discover and crawl websites for your knowledge base.',
88
+ },
89
+ media: {
90
+ title: 'Add Media',
91
+ description: 'Upload images, audio, and video, or add media from a URL.',
92
+ },
93
+ integrations: {
94
+ title: 'Add Integrations',
95
+ description: 'Connect your tools and import the data your team already uses.',
96
+ },
97
+ };
98
+
99
+ export function DataWorkspace({ view }: { view?: TopTabId } = {}) {
100
+ const searchParams = useSearchParams();
101
+ const queryGroupId = normalizeGroupId(searchParams.get('groupId'));
102
+ const pathname = usePathname();
103
+ const { activeProject } = useProject();
104
+ const serverId = activeProject?.id;
105
+
106
+ const getInitialTab = (): TopTabId => {
107
+ if (view) return view;
108
+ return 'add';
109
+ };
110
+
111
+ const getInitialSubTab = (): SubTabId => {
112
+ const subtab = searchParams.get('subtab') as SubTabId;
113
+ if (subtab && SUB_TABS.some((t) => t.id === subtab)) return subtab;
114
+ return 'files';
115
+ };
116
+
117
+ const [activeTab, setActiveTabState] = useState<TopTabId>(getInitialTab());
118
+ const [activeSubTab, setActiveSubTabState] = useState<SubTabId>(getInitialSubTab());
119
+ const [selectedGroupId, setSelectedGroupId] = useState(queryGroupId);
120
+
121
+ useEffect(() => {
122
+ if (view) {
123
+ setActiveTabState(view);
124
+ return;
125
+ }
126
+ const subtab = searchParams.get('subtab') as SubTabId;
127
+ if (subtab && subtab !== activeSubTab && SUB_TABS.some((t) => t.id === subtab)) {
128
+ setActiveSubTabState(subtab);
129
+ }
130
+ }, [searchParams, activeTab, activeSubTab, view]);
131
+
132
+ useEffect(() => setSelectedGroupId(queryGroupId), [queryGroupId]);
133
+
134
+ const setActiveSubTab = (subtab: SubTabId) => {
135
+ // The outgoing panel owns the shared primary action. Clear it before changing
136
+ // tabs so the incoming panel can register its action after it mounts.
137
+ // Clearing it in an effect races with that registration on initial render.
138
+ setPrimaryAction(null);
139
+ setActiveSubTabState(subtab);
140
+ const params = new URLSearchParams(searchParams.toString());
141
+ params.set('subtab', subtab);
142
+ window.history.replaceState(null, '', `${pathname}?${params.toString()}`);
143
+ };
144
+ const [showJobsDrawer, setShowJobsDrawer] = useState(false);
145
+ const [primaryAction, setPrimaryAction] = useState<DataPrimaryAction | null>(null);
146
+ const registerPrimaryAction = useCallback((action: DataPrimaryAction | null) => {
147
+ setPrimaryAction(action);
148
+ }, []);
149
+ const prevJobsRef = useRef<CrawlJob[]>([]);
150
+ const pendingIndexRef = useRef(false);
151
+
152
+ const jobsQuery = useSWR(workspaceUrl('/api/jobs'), fetchJobsWithSync, {
153
+ refreshInterval: (data) =>
154
+ data?.jobs?.some((j) => j.status === 'running' || j.status === 'queued') ? 4000 : 0,
155
+ });
156
+ const jobs = jobsQuery.data?.jobs ?? [];
157
+ const hasActiveCrawlJobs = jobs.some((j) => j.status === 'running' || j.status === 'queued');
158
+
159
+ const indexQuery = useSWR<{
160
+ unindexedCount: number;
161
+ running: boolean;
162
+ run: IndexRun | null;
163
+ blockers: string[];
164
+ }>(workspaceUrl('/api/index'), fetcher, {
165
+ refreshInterval: (d) => (d?.running ? 2000 : 0),
166
+ });
167
+ const indexRunning = indexQuery.data?.running ?? false;
168
+ const dataAddBlocked = indexQuery.data?.blockers?.includes('MISSING_EMBEDDING_API_KEY') ?? false;
169
+ const { mutate: mutateIndex } = indexQuery;
170
+
171
+ const mediaQuery = useSWR<MediaIndexingResponse>(
172
+ workspaceUrl(
173
+ `/api/media?type=all${serverId ? `&serverId=${encodeURIComponent(serverId)}` : ''}`,
174
+ ),
175
+ fetcher,
176
+ {
177
+ refreshInterval: (data) => (data?.assets?.some(isActiveMediaIndexing) ? 2_000 : 8_000),
178
+ revalidateOnFocus: true,
179
+ },
180
+ );
181
+ const activeMediaAssets = (mediaQuery.data?.assets ?? []).filter(isActiveMediaIndexing);
182
+ const hasActiveMedia = activeMediaAssets.length > 0;
183
+ const hasActive = hasActiveCrawlJobs || hasActiveMedia || indexRunning;
184
+ const activeCrawlTargetCount = jobs
185
+ .filter((job) => job.status === 'running' || job.status === 'queued')
186
+ .reduce((count, job) => count + (job.targets?.length || 1), 0);
187
+ const activeJobCount = activeCrawlTargetCount + activeMediaAssets.length + (indexRunning ? 1 : 0);
188
+ const hasJobsDrawerContent = jobs.length > 0 || hasActiveMedia || indexRunning;
189
+ const activeOrAttentionJobs = jobs.filter((job) => job.status !== 'completed');
190
+ const completedJobs = jobs.filter((job) => job.status === 'completed');
191
+ const [jobsFilterTab, setJobsFilterTab] = useState<'running' | 'all'>('running');
192
+
193
+ const docsQuery = useSWR<DocsResponse>(workspaceUrl('/api/documents'), fetcher, {
194
+ refreshInterval: hasActive ? 5000 : 0,
195
+ });
196
+ const { mutate: mutateDocuments } = docsQuery;
197
+ const documents = docsQuery.data?.documents ?? [];
198
+ const groups = docsQuery.data?.groups ?? [];
199
+ const configQuery = useSWR<{ config: RagConfig }>(
200
+ workspaceUrl(`/api/config${serverId ? `?serverId=${encodeURIComponent(serverId)}` : ''}`),
201
+ fetcher,
202
+ );
203
+ const activeVideoRuntimeScope = videoRuntimeScopeFromConfig(configQuery.data?.config);
204
+
205
+ const selectedGroup = groups.find((group) => group.id === selectedGroupId);
206
+ // Keep the requested destination stable while groups are loading. Resolving
207
+ // through the temporarily empty SWR result used to turn a valid selection
208
+ // into Default during the first render and made fast submissions race.
209
+ const targetGroupId = selectedGroupId;
210
+ const groupSelectionReady =
211
+ docsQuery.data !== undefined &&
212
+ !docsQuery.error &&
213
+ (targetGroupId === 'default' || selectedGroup !== undefined);
214
+
215
+ useEffect(() => {
216
+ if (dataAddBlocked) setPrimaryAction(null);
217
+ }, [dataAddBlocked]);
218
+
219
+ const { mutate: mutateGlobal } = useSWRConfig();
220
+
221
+ const refreshAll = async () => {
222
+ await Promise.all([
223
+ jobsQuery.mutate(),
224
+ mediaQuery.mutate(),
225
+ docsQuery.mutate(),
226
+ indexQuery.mutate(),
227
+ mutateGlobal(workspaceUrl('/api/index')),
228
+ ]);
229
+ };
230
+
231
+ const startAutomaticIndex = useCallback(async () => {
232
+ try {
233
+ const res = await fetch(workspaceUrl('/api/index'), {
234
+ method: 'POST',
235
+ headers: { 'Content-Type': 'application/json' },
236
+ body: JSON.stringify({ incremental: Boolean(indexQuery.data?.run) }),
237
+ });
238
+ const body = await res.json();
239
+ if (res.status === 409 || body.queued) {
240
+ pendingIndexRef.current = res.status === 409;
241
+ toast.message('Added to the indexing queue', {
242
+ id: 'document-index-queued',
243
+ description: 'It will start automatically after the current indexing job.',
244
+ });
245
+ void mutateIndex();
246
+ return;
247
+ }
248
+ if (!res.ok) {
249
+ toast.error(body.error || 'Your data was added, but indexing could not start.');
250
+ return;
251
+ }
252
+ toast.success('Making your data searchable', {
253
+ description: 'This runs in the background. You can keep adding files.',
254
+ duration: 7_000,
255
+ });
256
+ pendingIndexRef.current = false;
257
+ void mutateIndex();
258
+ } catch {
259
+ toast.error('Your data was added, but indexing could not start.');
260
+ }
261
+ }, [indexQuery.data?.run, mutateIndex]);
262
+
263
+ const prevIndexRunning = useRef(indexRunning);
264
+ useEffect(() => {
265
+ if (prevIndexRunning.current && !indexRunning) {
266
+ void mutateDocuments();
267
+ // Compatibility with an older local server that still returns 409.
268
+ // Current servers own this queue durably and start the follow-up pass.
269
+ if (pendingIndexRef.current) {
270
+ pendingIndexRef.current = false;
271
+ void startAutomaticIndex();
272
+ }
273
+ }
274
+ prevIndexRunning.current = indexRunning;
275
+ }, [indexRunning, mutateDocuments, startAutomaticIndex]);
276
+
277
+ useEffect(() => {
278
+ const prevJobs = prevJobsRef.current;
279
+ if (prevJobs.length > 0 && jobs.length > 0) {
280
+ const justCompleted = jobs.filter(
281
+ (j) =>
282
+ j.status === 'completed' &&
283
+ prevJobs.some(
284
+ (pj) => pj.id === j.id && (pj.status === 'running' || pj.status === 'queued'),
285
+ ),
286
+ );
287
+
288
+ if (justCompleted.length > 0) {
289
+ toast.success('Scraping completed. Making the new pages searchable…');
290
+ void mutateDocuments();
291
+ setTimeout(() => void mutateDocuments(), 2500);
292
+ void startAutomaticIndex();
293
+ }
294
+ }
295
+ prevJobsRef.current = jobs;
296
+ }, [jobs, mutateDocuments, startAutomaticIndex]);
297
+
298
+ const handleDataAdded = () => {
299
+ refreshAll();
300
+ void startAutomaticIndex();
301
+ };
302
+
303
+ const handleScrapeStarted = useCallback(
304
+ (job: CrawlJob) => {
305
+ void jobsQuery.mutate(
306
+ (current) => ({
307
+ jobs: [job, ...(current?.jobs ?? []).filter((existing) => existing.id !== job.id)],
308
+ configured: current?.configured ?? true,
309
+ }),
310
+ { revalidate: true },
311
+ );
312
+ },
313
+ [jobsQuery],
314
+ );
315
+
316
+ const handleMediaIndexed = useCallback(() => {
317
+ void mutateDocuments();
318
+ void mutateIndex();
319
+ toast.success('Media indexed and added to your Knowledge Base.');
320
+ }, [mutateDocuments, mutateIndex]);
321
+
322
+ const handleMediaProcessingStarted = useCallback(() => {
323
+ void mediaQuery.mutate();
324
+ }, [mediaQuery]);
325
+
326
+ const activeSubTabIntro = SUB_TAB_INTRO[activeSubTab];
327
+
328
+ const selectGroup = (groupId: string | null) => {
329
+ const next = normalizeGroupId(groupId);
330
+ setSelectedGroupId(next);
331
+
332
+ const params = new URLSearchParams(searchParams.toString());
333
+ if (next !== 'default') params.set('groupId', next);
334
+ else params.delete('groupId');
335
+ window.history.replaceState(null, '', `${pathname}?${params.toString()}`);
336
+ };
337
+
338
+ return (
339
+ <div className="px-6 md:px-8">
340
+ {view === 'add' && (
341
+ <>
342
+ <WorkspacePageHeading
343
+ className="mb-2"
344
+ title={activeSubTabIntro.title}
345
+ description={activeSubTabIntro.description}
346
+ actions={
347
+ <>
348
+ {activeSubTab === 'website' && <WebCrawlerStatus />}
349
+ {hasJobsDrawerContent && (
350
+ <TooltipProvider delay={150}>
351
+ <Tooltip>
352
+ <TooltipTrigger
353
+ render={
354
+ <button
355
+ type="button"
356
+ onClick={() => setShowJobsDrawer(!showJobsDrawer)}
357
+ className={cn(
358
+ 'relative cursor-pointer flex size-9 items-center justify-center rounded-lg border transition-colors',
359
+ hasActive
360
+ ? 'border-green-600 bg-green-100/30 text-green-600 hover:bg-green-100/50'
361
+ : 'border-border bg-card text-muted-foreground hover:bg-muted/50 hover:text-foreground',
362
+ showJobsDrawer && 'bg-white hover:bg-white',
363
+ )}
364
+ aria-label="Toggle jobs panel"
365
+ >
366
+ <History className="size-4" />
367
+ {hasActive && (
368
+ <>
369
+ <span className="absolute -top-1 -right-1 z-10 flex h-3.5 min-w-3.5 items-center justify-center rounded-full bg-emerald-500 px-1 text-[9px] font-bold text-white shadow-sm ring-1 ring-white">
370
+ {activeJobCount}
371
+ </span>
372
+ <span className="absolute -top-1 -right-1 z-0 flex h-3.5 w-3.5 animate-ping rounded-full bg-emerald-400 opacity-75"></span>
373
+ </>
374
+ )}
375
+ </button>
376
+ }
377
+ />
378
+ <TooltipContent>
379
+ <p>{hasActive ? 'Active jobs' : 'Job history'}</p>
380
+ </TooltipContent>
381
+ </Tooltip>
382
+ </TooltipProvider>
383
+ )}
384
+ </>
385
+ }
386
+ />
387
+ <WorkspaceLineTabs
388
+ className="mb-4 mt-9"
389
+ items={SUB_TABS}
390
+ activeId={activeSubTab}
391
+ onChange={setActiveSubTab}
392
+ trailing={
393
+ <div className="flex items-center gap-2">
394
+ <Select value={targetGroupId} onValueChange={selectGroup}>
395
+ <SelectTrigger
396
+ aria-label="Data group"
397
+ className="h-9 min-w-42 max-w-56 bg-white text-xs"
398
+ >
399
+ <span className="flex flex-1 items-center gap-1.5 truncate text-left">
400
+ {!groupSelectionReady && docsQuery.data === undefined
401
+ ? 'Loading group…'
402
+ : targetGroupId === 'default'
403
+ ? 'Default group'
404
+ : selectedGroup
405
+ ? `${selectedGroup.icon ? `${selectedGroup.icon} ` : ''}${selectedGroup.name}`
406
+ : 'Group unavailable'}
407
+ </span>
408
+ </SelectTrigger>
409
+ <SelectContent>
410
+ <div className="space-y-1 p-1">
411
+ {groups.map((group) => (
412
+ <SelectItem key={group.id} value={group.id} className="px-2 py-1.5 text-sm">
413
+ <span className="truncate">
414
+ {group.icon ? `${group.icon} ` : ''}
415
+ {group.name}
416
+ </span>
417
+ </SelectItem>
418
+ ))}
419
+ </div>
420
+ </SelectContent>
421
+ </Select>
422
+ {primaryAction && (
423
+ <Button
424
+ size="default"
425
+ data-ready={
426
+ !dataAddBlocked &&
427
+ groupSelectionReady &&
428
+ !primaryAction.disabled &&
429
+ !primaryAction.loading
430
+ }
431
+ className="h-10 min-w-28 transition-colors"
432
+ onClick={primaryAction.onClick}
433
+ disabled={
434
+ dataAddBlocked ||
435
+ !groupSelectionReady ||
436
+ primaryAction.disabled ||
437
+ primaryAction.loading
438
+ }
439
+ >
440
+ {primaryAction.loading ? (
441
+ <Loader2 className="mr-1.5 size-3.5 animate-spin" />
442
+ ) : (
443
+ <Plus className="mr-1.5 size-3.5" />
444
+ )}
445
+ {primaryAction.label}
446
+ </Button>
447
+ )}
448
+ </div>
449
+ }
450
+ />
451
+ </>
452
+ )}
453
+
454
+ {showJobsDrawer && hasJobsDrawerContent && (
455
+ <div className="mb-6 animate-in slide-in-from-top-2 fade-in duration-200">
456
+ <div
457
+ className={cn(
458
+ 'rounded-lg border border-border bg-card/70 p-4',
459
+ jobs?.length == 0 && 'bg-background',
460
+ )}
461
+ >
462
+ <div className="flex items-center justify-end mb-3">
463
+ <button
464
+ type="button"
465
+ onClick={() => setShowJobsDrawer(false)}
466
+ className="text-xs text-muted-foreground hover:text-foreground transition-colors"
467
+ >
468
+ Hide
469
+ </button>
470
+ </div>
471
+ <MediaIndexingJobsPanel assets={activeMediaAssets} />
472
+ {indexRunning && (
473
+ <div
474
+ className={cn(
475
+ 'flex items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5',
476
+ activeMediaAssets.length > 0 && 'mt-3',
477
+ )}
478
+ >
479
+ <Loader2 className="size-4 shrink-0 animate-spin text-emerald-600" />
480
+ <div className="min-w-0">
481
+ <p className="text-xs font-medium">Indexing files</p>
482
+ <p className="text-[11px] text-muted-foreground">
483
+ Running in the background — new files will join the queue.
484
+ </p>
485
+ </div>
486
+ </div>
487
+ )}
488
+ {jobs.length > 0 && (
489
+ <div
490
+ className={cn(activeMediaAssets.length > 0 && 'mt-4 border-t border-border pt-4')}
491
+ >
492
+ <div className="mb-3 flex flex-wrap items-center gap-2">
493
+ {[
494
+ { id: 'running', label: 'Running' },
495
+ { id: 'all', label: 'All Jobs' },
496
+ ].map((tab) => {
497
+ const isActive = jobsFilterTab === tab.id;
498
+ return (
499
+ <button
500
+ key={tab.id}
501
+ type="button"
502
+ onClick={() => setJobsFilterTab(tab.id as 'running' | 'all')}
503
+ className={cn(
504
+ 'rounded-full px-3 py-1 text-xs font-medium transition-colors',
505
+ isActive
506
+ ? 'bg-muted text-foreground'
507
+ : 'border border-border bg-transparent text-muted-foreground hover:bg-muted/50',
508
+ )}
509
+ >
510
+ {tab.label}
511
+ </button>
512
+ );
513
+ })}
514
+ </div>
515
+ <JobsPanel
516
+ jobs={
517
+ jobsFilterTab === 'running'
518
+ ? jobs.filter((j) => j.status === 'running' || j.status === 'queued')
519
+ : jobs
520
+ }
521
+ onChanged={refreshAll}
522
+ />
523
+ </div>
524
+ )}
525
+ </div>
526
+ </div>
527
+ )}
528
+
529
+ <div>
530
+ {activeTab === 'add' && (
531
+ <div className="w-full ">
532
+ <div className="relative">
533
+ {dataAddBlocked ? (
534
+ <div className="mx-auto flex min-h-72 max-w-xl mt-5 flex-col items-center justify-center rounded-xl px-6 text-center">
535
+ <div className="mb-4 rounded-full bg-primary/10 p-3 text-primary">
536
+ <KeyRound className="size-5" />
537
+ </div>
538
+ <h2 className="text-base font-semibold">Set an embedding API key first</h2>
539
+ <p className="mt-2 max-w-md text-sm text-muted-foreground">
540
+ Data is kept out of the Knowledge Base until it can be indexed. Configure an
541
+ embedding provider and API key, then return here to add data.
542
+ </p>
543
+ <Link
544
+ href="/settings?section=models"
545
+ className="mt-5 inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
546
+ >
547
+ Open AI model settings
548
+ </Link>
549
+ </div>
550
+ ) : (
551
+ <>
552
+ {activeSubTab === 'website' && (
553
+ <div className="w-full flex flex-col gap-8 animate-in fade-in duration-200">
554
+ <div>
555
+ <ScrapePanel
556
+ onStarted={handleScrapeStarted}
557
+ onActionChange={registerPrimaryAction}
558
+ groupId={targetGroupId}
559
+ />
560
+ </div>
561
+ </div>
562
+ )}
563
+
564
+ {activeSubTab === 'files' && (
565
+ <div className="animate-in fade-in duration-200">
566
+ <UploadPanel
567
+ onAdded={handleDataAdded}
568
+ onActionChange={registerPrimaryAction}
569
+ groupId={targetGroupId}
570
+ />
571
+ </div>
572
+ )}
573
+
574
+ {activeSubTab === 'text' && (
575
+ <div className=" animate-in fade-in duration-200">
576
+ <PastePanel
577
+ onAdded={handleDataAdded}
578
+ onActionChange={registerPrimaryAction}
579
+ groupId={targetGroupId}
580
+ />
581
+ </div>
582
+ )}
583
+
584
+ {activeSubTab === 'media' && (
585
+ <div className="animate-in fade-in duration-200">
586
+ <MediaPanel
587
+ onAdded={refreshAll}
588
+ onIndexed={handleMediaIndexed}
589
+ onProcessingStarted={handleMediaProcessingStarted}
590
+ onActionChange={registerPrimaryAction}
591
+ groupId={targetGroupId}
592
+ />
593
+ </div>
594
+ )}
595
+
596
+ {activeSubTab === 'integrations' && (
597
+ <div className="animate-in fade-in duration-200">
598
+ <IntegrationsPanel onAdded={handleDataAdded} groupId={targetGroupId} />
599
+ </div>
600
+ )}
601
+ </>
602
+ )}
603
+ </div>
604
+ </div>
605
+ )}
606
+
607
+ {activeTab === 'corpus' && (
608
+ <div className="animate-in fade-in duration-300">
609
+ <CorpusPanel
610
+ documents={documents}
611
+ groups={groups}
612
+ onChanged={refreshAll}
613
+ isIndexing={indexRunning}
614
+ activeVideoRuntimeScope={activeVideoRuntimeScope}
615
+ initialGroupId={searchParams.has('groupId') ? queryGroupId : undefined}
616
+ isLoading={docsQuery.isLoading || (docsQuery.data === undefined && !docsQuery.error)}
617
+ />
618
+ </div>
619
+ )}
620
+ </div>
621
+ </div>
622
+ );
623
+ }