@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,810 @@
1
+ import { workspaceUrl, workspaceFetch as fetch } from '../../../host';
2
+ import { useState, useRef, useEffect } from 'react';
3
+ import { useDocEditor } from '../canvas/doc-editor-provider';
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardFooter,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from '../../ui/card';
12
+ import { Button } from '@larkup/workspace-ui/button';
13
+ import { Input } from '../../ui/input';
14
+ import { Label } from '@larkup/workspace-ui/label';
15
+ import { motion } from 'framer-motion';
16
+ import { cn } from '../../../lib/utils';
17
+ import {
18
+ Select,
19
+ SelectContent,
20
+ SelectItem,
21
+ SelectTrigger,
22
+ SelectValue,
23
+ } from '@larkup/workspace-ui/select';
24
+ import { Loader2, PenTool, Type, Upload, Trash2, ChevronDown, Pencil } from 'lucide-react';
25
+ import { toast } from 'sonner';
26
+ import { get, set } from 'idb-keyval';
27
+ import { Checkbox } from '../../ui/checkbox';
28
+ import { Slider } from '../../ui/slider';
29
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../../ui/collapsible';
30
+ import { VisualPlacementModal, Placement } from './visual-placement-modal';
31
+
32
+ interface DetectedLocation {
33
+ pageIndex: number;
34
+ context: string;
35
+ }
36
+
37
+ interface SavedSignature {
38
+ id: string;
39
+ imagePayload: string;
40
+ mode: 'draw' | 'upload';
41
+ }
42
+
43
+ export function ChatSignatureRequest({
44
+ detectedLocations,
45
+ fileName,
46
+ mimeType,
47
+ toolCallId,
48
+ addToolResult,
49
+ }: {
50
+ detectedLocations?: DetectedLocation[];
51
+ fileName?: string;
52
+ mimeType?: string;
53
+ toolCallId?: string;
54
+ addToolResult?: Function;
55
+ }) {
56
+ const {
57
+ sessionId,
58
+ parsedDocument,
59
+ updateFromToolResult,
60
+ activeFileName,
61
+ fileMimeType,
62
+ fileBase64,
63
+ } = useDocEditor();
64
+ const [isSubmitting, setIsSubmitting] = useState(false);
65
+ const [hasSigned, setHasSigned] = useState(false);
66
+
67
+ // Form State
68
+ const [mode, setMode] = useState<'type' | 'draw' | 'upload'>('type');
69
+ const [selectedPagesStr, setSelectedPagesStr] = useState(
70
+ detectedLocations &&
71
+ detectedLocations.length > 0 &&
72
+ detectedLocations[0].pageIndex !== undefined
73
+ ? (detectedLocations[0].pageIndex + 1).toString()
74
+ : '1',
75
+ );
76
+
77
+ // Placements State
78
+ const [placements, setPlacements] = useState<Placement[]>([]);
79
+ const [isVisualPlacementOpen, setIsVisualPlacementOpen] = useState(false);
80
+
81
+ // Saved Signatures State
82
+ const [savedSignatures, setSavedSignatures] = useState<SavedSignature[]>([]);
83
+ const [saveSignature, setSaveSignature] = useState(false);
84
+
85
+ // Advanced & Notes State
86
+ const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
87
+ const [editingLocation, setEditingLocation] = useState<number | null>(null);
88
+ const [locationNotes, setLocationNotes] = useState<Record<number, string>>({});
89
+
90
+ useEffect(() => {
91
+ get('saved_signatures').then((val) => {
92
+ if (val) setSavedSignatures(val);
93
+ });
94
+ }, []);
95
+
96
+ const saveToIDB = async (newSig: SavedSignature) => {
97
+ const updated = [newSig, ...savedSignatures];
98
+ setSavedSignatures(updated);
99
+ await set('saved_signatures', updated);
100
+ };
101
+
102
+ const deleteSavedSignature = async (id: string, e: React.MouseEvent) => {
103
+ e.stopPropagation();
104
+ const updated = savedSignatures.filter((s) => s.id !== id);
105
+ setSavedSignatures(updated);
106
+ await set('saved_signatures', updated);
107
+ };
108
+
109
+ // Type State
110
+ const [text, setText] = useState('');
111
+ const [font, setFont] = useState('Helvetica');
112
+ const [color, setColor] = useState('black');
113
+ const [scale, setScale] = useState(100);
114
+
115
+ // Default to bottom-left area of the page (which is standard for signatures)
116
+ const [xPos, setXPos] = useState(20);
117
+ const [yPos, setYPos] = useState(85);
118
+
119
+ // Customization Fields
120
+ const [date, setDate] = useState('');
121
+ const [extraText, setExtraText] = useState('');
122
+
123
+ // Undo/replace state
124
+ const [base64BeforeSign, setBase64BeforeSign] = useState<string | null>(null);
125
+
126
+ // Canvas Ref for Draw
127
+ const canvasRef = useRef<HTMLCanvasElement>(null);
128
+ const [isDrawing, setIsDrawing] = useState(false);
129
+
130
+ // Upload State
131
+ const [uploadedImage, setUploadedImage] = useState<string | null>(null);
132
+
133
+ const startDrawing = (
134
+ e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>,
135
+ ) => {
136
+ setIsDrawing(true);
137
+ draw(e);
138
+ };
139
+
140
+ const endDrawing = () => {
141
+ setIsDrawing(false);
142
+ if (canvasRef.current) {
143
+ const ctx = canvasRef.current.getContext('2d');
144
+ ctx?.beginPath();
145
+ }
146
+ };
147
+
148
+ const draw = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
149
+ if (!isDrawing || !canvasRef.current) return;
150
+ const canvas = canvasRef.current;
151
+ const ctx = canvas.getContext('2d');
152
+ if (!ctx) return;
153
+
154
+ // Get coordinates
155
+ let clientX, clientY;
156
+ if ('touches' in e) {
157
+ clientX = e.touches[0].clientX;
158
+ clientY = e.touches[0].clientY;
159
+ } else {
160
+ clientX = e.clientX;
161
+ clientY = e.clientY;
162
+ }
163
+
164
+ const rect = canvas.getBoundingClientRect();
165
+ const scaleX = canvas.width / rect.width;
166
+ const scaleY = canvas.height / rect.height;
167
+ const x = (clientX - rect.left) * scaleX;
168
+ const y = (clientY - rect.top) * scaleY;
169
+
170
+ ctx.lineWidth = 2;
171
+ ctx.lineCap = 'round';
172
+ ctx.strokeStyle = color === 'black' ? '#000000' : color === 'blue' ? '#0000ff' : '#ff0000';
173
+
174
+ ctx.lineTo(x, y);
175
+ ctx.stroke();
176
+ ctx.beginPath();
177
+ ctx.moveTo(x, y);
178
+ };
179
+
180
+ const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
181
+ const file = e.target.files?.[0];
182
+ if (!file) return;
183
+
184
+ const reader = new FileReader();
185
+ reader.onload = (event) => {
186
+ setUploadedImage(event.target?.result as string);
187
+ };
188
+ reader.readAsDataURL(file);
189
+ };
190
+
191
+ const clearCanvas = () => {
192
+ if (!canvasRef.current) return;
193
+ const canvas = canvasRef.current;
194
+ const ctx = canvas.getContext('2d');
195
+ if (ctx) {
196
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
197
+ }
198
+ };
199
+
200
+ const handleSubmit = async (overridePlacements?: Placement[]) => {
201
+ if (!sessionId) {
202
+ toast.error('No active document session');
203
+ return;
204
+ }
205
+
206
+ setIsSubmitting(true);
207
+ try {
208
+ let imagePayload: string | undefined;
209
+
210
+ if (mode === 'draw' && canvasRef.current) {
211
+ imagePayload = canvasRef.current.toDataURL('image/png');
212
+ } else if (mode === 'upload' && uploadedImage) {
213
+ imagePayload = uploadedImage;
214
+ }
215
+
216
+ if (mode === 'type' && !text.trim()) {
217
+ toast.error('Please type a signature');
218
+ setIsSubmitting(false);
219
+ return;
220
+ }
221
+
222
+ if ((mode === 'draw' || mode === 'upload') && !imagePayload) {
223
+ toast.error('Please provide a signature image');
224
+ setIsSubmitting(false);
225
+ return;
226
+ }
227
+
228
+ // Save for future use
229
+ if (saveSignature && (mode === 'draw' || mode === 'upload') && imagePayload) {
230
+ await saveToIDB({
231
+ id: crypto.randomUUID(),
232
+ imagePayload,
233
+ mode,
234
+ });
235
+ }
236
+
237
+ const parsedPages = new Set<number>();
238
+ selectedPagesStr.split(',').forEach((part) => {
239
+ const p = part.trim();
240
+ if (p.includes('-')) {
241
+ const [start, end] = p.split('-').map(Number);
242
+ if (!isNaN(start) && !isNaN(end)) {
243
+ for (let i = start; i <= end; i++) parsedPages.add(i - 1);
244
+ }
245
+ } else {
246
+ const num = Number(p);
247
+ if (!isNaN(num)) parsedPages.add(num - 1);
248
+ }
249
+ });
250
+
251
+ let finalPlacements = overridePlacements || [...placements];
252
+ if (finalPlacements.length === 0) {
253
+ if (parsedPages.size === 0) parsedPages.add(0);
254
+ Array.from(parsedPages).forEach((pIdx) => {
255
+ finalPlacements.push({
256
+ pageIndex: pIdx,
257
+ x: xPos,
258
+ y: yPos,
259
+ scale,
260
+ });
261
+ });
262
+ }
263
+
264
+ let override = base64BeforeSign;
265
+ if (!override && fileBase64) {
266
+ override = fileBase64;
267
+ setBase64BeforeSign(fileBase64);
268
+ }
269
+
270
+ let lastResData = null;
271
+
272
+ for (const placement of finalPlacements) {
273
+ const res = await fetch(workspaceUrl('/api/document/sign'), {
274
+ method: 'POST',
275
+ headers: { 'Content-Type': 'application/json' },
276
+ body: JSON.stringify({
277
+ sessionId,
278
+ signatureData: {
279
+ mode,
280
+ imagePayload,
281
+ text,
282
+ font,
283
+ color,
284
+ date,
285
+ extraText,
286
+ pageIndex: placement.pageIndex,
287
+ placementNote: locationNotes[placement.pageIndex] || '',
288
+ detectedContext: detectedLocations?.find((l) => l.pageIndex === placement.pageIndex)
289
+ ?.context,
290
+ x: placement.x,
291
+ y: placement.y,
292
+ scale: placement.scale / 100, // Pass scale as a multiplier
293
+ base64Override: override, // Pass the original state to allow replacing previous signatures
294
+ },
295
+ }),
296
+ });
297
+
298
+ if (!res.ok) {
299
+ const data = await res.json();
300
+ throw new Error(
301
+ data.error || `Failed to sign document on page ${placement.pageIndex + 1}`,
302
+ );
303
+ }
304
+
305
+ lastResData = await res.json();
306
+ // Update override for the next placement
307
+ if (lastResData?.document?.fileBase64) {
308
+ override = lastResData.document.fileBase64;
309
+ }
310
+ }
311
+
312
+ if (lastResData) {
313
+ setSignedResult(lastResData.document);
314
+ updateFromToolResult({
315
+ sessionId,
316
+ fields: lastResData.document.parsed.fields,
317
+ updatedFields: lastResData.document.updatedFields,
318
+ fileBase64: lastResData.document.fileBase64,
319
+ pages: lastResData.document.parsed.pages,
320
+ totalPages: lastResData.document.parsed.totalPages,
321
+ });
322
+ }
323
+
324
+ setHasSigned(true);
325
+ toast.success('Signature applied for preview. Please confirm or adjust.');
326
+ } catch (err: any) {
327
+ toast.error(err.message);
328
+ } finally {
329
+ setIsSubmitting(false);
330
+ }
331
+ };
332
+
333
+ const [signedResult, setSignedResult] = useState<any>(null);
334
+
335
+ const handleConfirm = () => {
336
+ if (toolCallId && addToolResult) {
337
+ addToolResult({
338
+ toolCallId,
339
+ result: {
340
+ success: true,
341
+ action: 'request_signature',
342
+ message: 'User has successfully signed the document.',
343
+ fileBase64: signedResult?.fileBase64,
344
+ fields: signedResult?.parsed?.fields,
345
+ updatedFields: signedResult?.updatedFields,
346
+ pages: signedResult?.parsed?.pages,
347
+ totalPages: signedResult?.parsed?.totalPages,
348
+ },
349
+ output: {
350
+ success: true,
351
+ action: 'request_signature',
352
+ message: 'User has successfully signed the document.',
353
+ fileBase64: signedResult?.fileBase64,
354
+ fields: signedResult?.parsed?.fields,
355
+ updatedFields: signedResult?.updatedFields,
356
+ pages: signedResult?.parsed?.pages,
357
+ totalPages: signedResult?.parsed?.totalPages,
358
+ },
359
+ });
360
+ }
361
+ };
362
+
363
+ const handleUndo = () => {
364
+ if (base64BeforeSign && sessionId) {
365
+ updateFromToolResult({
366
+ sessionId,
367
+ fileBase64: base64BeforeSign,
368
+ fields: parsedDocument?.fields || [],
369
+ updatedFields: [],
370
+ pages: parsedDocument?.pages || [],
371
+ totalPages: parsedDocument?.totalPages || 1,
372
+ });
373
+ }
374
+ setHasSigned(false);
375
+ };
376
+
377
+ const displayFileName = fileName || activeFileName || 'Document';
378
+ const displayMimeType = mimeType || fileMimeType || '';
379
+ const ext = displayFileName.split('.').pop()?.toLowerCase() || '';
380
+
381
+ let iconPath = '/icons/image.png';
382
+ if (
383
+ ['csv', 'xls', 'xlsx'].includes(ext) ||
384
+ displayMimeType.includes('excel') ||
385
+ displayMimeType.includes('spreadsheet')
386
+ )
387
+ iconPath = '/icons/excel.png';
388
+ else if (['doc', 'docx'].includes(ext) || displayMimeType.includes('word'))
389
+ iconPath = '/icons/word.png';
390
+ else if (['md', 'markdown'].includes(ext)) iconPath = '/icons/markdown.png';
391
+ else if (['pdf'].includes(ext) || displayMimeType === 'application/pdf')
392
+ iconPath = '/icons/pdf.png';
393
+ else if (!ext) iconPath = '/icons/word.png';
394
+
395
+ return (
396
+ <Card className="my-4 shadow-none! bg-transparent w-full max-w-3xl border-border">
397
+ <CardHeader className="flex flex-row items-center gap-3 space-y-0">
398
+ <div className="size-10 shrink-0 rounded-md overflow-hidden bg-white border border-border flex items-center justify-center p-1.5 35">
399
+ <img src={iconPath} alt={displayFileName} className="w-full h-full object-contain" />
400
+ </div>
401
+ <div>
402
+ <CardTitle className="text-lg">Signature Request</CardTitle>
403
+ <CardDescription>
404
+ Sign <span className="font-semibold text-foreground/80">{displayFileName}</span> to
405
+ continue.
406
+ </CardDescription>
407
+ </div>
408
+ </CardHeader>
409
+ <CardContent className="space-y-4 pt-2">
410
+ {!sessionId && (
411
+ <div className="bg-amber-50 text-amber-900 border border-amber-200 rounded-md p-3 text-xs mb-4">
412
+ <strong>Action Required:</strong> Please click on your document in the chat to open it
413
+ in the Canvas first. The document must be open before you can sign it.
414
+ </div>
415
+ )}
416
+ {detectedLocations && detectedLocations.length > 0 && (
417
+ <div className="bg-muted p-3 rounded-md text-sm mb-4">
418
+ <span className="font-semibold block mb-1">Detected Placement:</span>
419
+ {detectedLocations.map((loc, i) => (
420
+ <div key={i} className="group mb-2 last:mb-0">
421
+ <div className="text-muted-foreground flex justify-between items-center">
422
+ <span>Page {loc.pageIndex + 1}</span>
423
+ <div className="flex items-center space-x-2">
424
+ <span className="italic truncate max-w-50">{loc.context}</span>
425
+ <Button
426
+ variant="ghost"
427
+ size="icon"
428
+ className="h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity"
429
+ onClick={() => setEditingLocation(editingLocation === i ? null : i)}
430
+ >
431
+ <Pencil className="h-3 w-3" />
432
+ </Button>
433
+ </div>
434
+ </div>
435
+ {editingLocation === i && (
436
+ <div className="mt-2">
437
+ <Input
438
+ placeholder="Add notes for placement (e.g. bottom right corner)"
439
+ className="h-8 text-xs bg-white"
440
+ value={locationNotes[i] || ''}
441
+ onChange={(e) => setLocationNotes({ ...locationNotes, [i]: e.target.value })}
442
+ />
443
+ </div>
444
+ )}
445
+ </div>
446
+ ))}
447
+ </div>
448
+ )}
449
+
450
+ <Collapsible open={isAdvancedOpen} onOpenChange={setIsAdvancedOpen}>
451
+ <div className="grid grid-cols-2 gap-2 items-end mb-4">
452
+ <div className="space-y-1.5">
453
+ <Label className="text-xs">Pages (e.g. 1, 3-5)</Label>
454
+ <Input
455
+ value={selectedPagesStr}
456
+ onChange={(e) => setSelectedPagesStr(e.target.value)}
457
+ placeholder="1"
458
+ className="h-10 text-xs bg-white"
459
+ />
460
+ </div>
461
+
462
+ <div className="flex flex-col justify-end w-full">
463
+ <CollapsibleTrigger
464
+ className={'mb-px'}
465
+ render={
466
+ <Button
467
+ variant="outline"
468
+ size="sm"
469
+ className="h-10 w-full justify-between bg-white text-xs px-3"
470
+ >
471
+ <span>Advanced Settings</span>
472
+ <ChevronDown
473
+ className={`h-4 w-4 transition-transform duration-200 ${
474
+ isAdvancedOpen ? 'rotate-180' : ''
475
+ }`}
476
+ />
477
+ </Button>
478
+ }
479
+ />
480
+ </div>
481
+ </div>
482
+
483
+ <CollapsibleContent className="space-y-4 mb-4 rounded-md p-3 bg-white/50 animate-in fade-in duration-200">
484
+ <div className="grid grid-cols-2 gap-4">
485
+ <div className="space-y-1.5">
486
+ <Label className="text-xs">Font Style</Label>
487
+ <Select
488
+ value={font}
489
+ onValueChange={(val) => val && setFont(val)}
490
+ disabled={mode !== 'type'}
491
+ >
492
+ <SelectTrigger className="bg-white h-8 text-xs">
493
+ <SelectValue placeholder="Select font" />
494
+ </SelectTrigger>
495
+ <SelectContent>
496
+ <SelectItem value="Helvetica">Standard</SelectItem>
497
+ <SelectItem value="Cursive">Cursive</SelectItem>
498
+ </SelectContent>
499
+ </Select>
500
+ </div>
501
+
502
+ <div className="space-y-2">
503
+ <Label className="text-xs block mb-1">Signature Color</Label>
504
+ <div className="flex gap-2 h-8 items-center">
505
+ {['black', 'blue', 'red'].map((c) => (
506
+ <button
507
+ key={c}
508
+ onClick={() => setColor(c)}
509
+ className={`size-5 rounded-full border ${
510
+ color === c ? 'ring-2 ring-primary ring-offset-1' : ''
511
+ }`}
512
+ style={{ backgroundColor: c }}
513
+ title={c}
514
+ />
515
+ ))}
516
+ </div>
517
+ </div>
518
+ </div>
519
+
520
+ <div className="grid grid-cols-2 gap-4">
521
+ <div className="space-y-2">
522
+ <Label className="text-xs block mb-1">Size: {scale}%</Label>
523
+ <Slider
524
+ value={[scale]}
525
+ onValueChange={(val) => setScale(Array.isArray(val) ? val[0] : val)}
526
+ min={50}
527
+ max={200}
528
+ step={10}
529
+ className="mt-2"
530
+ />
531
+ </div>
532
+
533
+ <div className="space-y-1.5">
534
+ <Label className="text-xs">Date (Optional)</Label>
535
+ <Input
536
+ type="date"
537
+ value={date}
538
+ onChange={(e) => setDate(e.target.value)}
539
+ className="h-8 text-xs bg-white"
540
+ />
541
+ </div>
542
+ </div>
543
+
544
+ <div className="grid grid-cols-2 gap-4">
545
+ <div className="space-y-1.5">
546
+ <Label className="text-xs">Position X (%)</Label>
547
+ <Input
548
+ type="number"
549
+ min={0}
550
+ max={100}
551
+ value={xPos}
552
+ onChange={(e) => setXPos(Number(e.target.value))}
553
+ className="h-8 text-xs bg-white"
554
+ />
555
+ </div>
556
+ <div className="space-y-1.5">
557
+ <Label className="text-xs">Position Y (%)</Label>
558
+ <Input
559
+ type="number"
560
+ min={0}
561
+ max={100}
562
+ value={yPos}
563
+ onChange={(e) => setYPos(Number(e.target.value))}
564
+ className="h-8 text-xs bg-white"
565
+ />
566
+ </div>
567
+ </div>
568
+
569
+ <div className="space-y-1.5">
570
+ <Label className="text-xs">Extra Text (Optional)</Label>
571
+ <Input
572
+ value={extraText}
573
+ onChange={(e) => setExtraText(e.target.value)}
574
+ placeholder="Title, Company, etc."
575
+ className="h-8 text-xs bg-white"
576
+ />
577
+ </div>
578
+ </CollapsibleContent>
579
+ </Collapsible>
580
+
581
+ {savedSignatures.length > 0 && (
582
+ <div className="space-y-2 mb-4">
583
+ <Label className="text-xs">Saved Signatures</Label>
584
+ <div className="flex gap-2 overflow-x-auto pb-2">
585
+ {savedSignatures.map((sig) => (
586
+ <div
587
+ key={sig.id}
588
+ className="relative shrink-0 w-25 h-12.5 border rounded bg-white flex items-center justify-center cursor-pointer hover:border-primary group overflow-hidden"
589
+ onClick={() => {
590
+ setMode(sig.mode);
591
+ if (sig.mode === 'upload') setUploadedImage(sig.imagePayload);
592
+ else if (sig.mode === 'draw') {
593
+ // Hacky way to reuse drawn signature via upload flow since canvas isn't easily hydrated with data URL
594
+ setMode('upload');
595
+ setUploadedImage(sig.imagePayload);
596
+ }
597
+ }}
598
+ >
599
+ <img
600
+ src={sig.imagePayload}
601
+ alt="Saved Signature"
602
+ className="max-h-full max-w-full object-contain"
603
+ />
604
+ <button
605
+ onClick={(e) => deleteSavedSignature(sig.id, e)}
606
+ className="absolute top-1 right-1 bg-destructive text-white rounded p-0.5 opacity-0 group-hover:opacity-100 transition-opacity"
607
+ >
608
+ <Trash2 className="size-3" />
609
+ </button>
610
+ </div>
611
+ ))}
612
+ </div>
613
+ </div>
614
+ )}
615
+
616
+ <div className="w-full mt-4">
617
+ <div className="border-b border-border mb-6">
618
+ <div className="flex items-center gap-1 -mb-px">
619
+ {[
620
+ { id: 'type', label: 'Type', icon: Type },
621
+ { id: 'draw', label: 'Draw', icon: PenTool },
622
+ { id: 'upload', label: 'Upload', icon: Upload },
623
+ ].map((tab) => {
624
+ const isActive = mode === tab.id;
625
+ const Icon = tab.icon;
626
+ return (
627
+ <button
628
+ key={tab.id}
629
+ onClick={() => setMode(tab.id as any)}
630
+ className={cn(
631
+ 'relative flex items-center gap-2 px-4 py-2.5 text-sm font-medium transition-colors outline-none',
632
+ isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground',
633
+ )}
634
+ >
635
+ <Icon className="size-4" />
636
+ {tab.label}
637
+ {isActive && (
638
+ <motion.div
639
+ layoutId="signature-tab-indicator"
640
+ className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary rounded-full"
641
+ initial={false}
642
+ transition={{
643
+ type: 'spring',
644
+ stiffness: 500,
645
+ damping: 35,
646
+ }}
647
+ />
648
+ )}
649
+ </button>
650
+ );
651
+ })}
652
+ </div>
653
+ </div>
654
+
655
+ <div className="relative">
656
+ {mode === 'type' && (
657
+ <div className="space-y-1.5 animate-in fade-in duration-200">
658
+ <Label className="text-xs">Your Name</Label>
659
+ <Input
660
+ value={text}
661
+ onChange={(e) => setText(e.target.value)}
662
+ placeholder="John Doe"
663
+ />
664
+ </div>
665
+ )}
666
+
667
+ {mode === 'draw' && (
668
+ <div className="border rounded-md bg-white overflow-hidden touch-none relative animate-in fade-in duration-200">
669
+ <canvas
670
+ ref={canvasRef}
671
+ width={380}
672
+ height={150}
673
+ onMouseDown={startDrawing}
674
+ onMouseUp={endDrawing}
675
+ onMouseOut={endDrawing}
676
+ onMouseMove={draw}
677
+ onTouchStart={startDrawing}
678
+ onTouchEnd={endDrawing}
679
+ onTouchMove={draw}
680
+ className="w-full h-37.5 cursor-crosshair block"
681
+ />
682
+ <Button
683
+ variant="outline"
684
+ size="sm"
685
+ className="absolute bottom-2 right-2 h-7 text-xs"
686
+ onClick={clearCanvas}
687
+ >
688
+ Clear
689
+ </Button>
690
+ </div>
691
+ )}
692
+
693
+ {mode === 'upload' && (
694
+ <div className="border-2 border-dashed rounded-md p-6 flex flex-col items-center justify-center text-center relative overflow-hidden animate-in fade-in duration-200">
695
+ <input
696
+ type="file"
697
+ accept="image/png, image/jpeg"
698
+ onChange={handleFileUpload}
699
+ className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
700
+ id="signature-upload"
701
+ />
702
+ {!uploadedImage ? (
703
+ <div className="flex flex-col items-center space-y-2 pointer-events-none">
704
+ <Upload className="size-8 text-muted-foreground mb-2" />
705
+ <span className="text-sm font-medium">Click or drag to upload signature</span>
706
+ <span className="text-xs text-muted-foreground">
707
+ PNG or JPG with transparent background
708
+ </span>
709
+ </div>
710
+ ) : (
711
+ <div className="mt-2 max-h-25 overflow-hidden rounded bg-white w-full flex justify-center pointer-events-none">
712
+ <img
713
+ src={uploadedImage}
714
+ alt="Signature Preview"
715
+ className="max-h-full object-contain"
716
+ />
717
+ </div>
718
+ )}
719
+ </div>
720
+ )}
721
+ </div>
722
+ </div>
723
+
724
+ {(mode === 'draw' || mode === 'upload') && (
725
+ <div className="flex items-center space-x-2 mt-4">
726
+ <Checkbox
727
+ id="save-signature"
728
+ checked={saveSignature}
729
+ onCheckedChange={(c) => setSaveSignature(!!c)}
730
+ />
731
+ <Label htmlFor="save-signature" className="text-sm font-medium leading-none">
732
+ Save signature for future use
733
+ </Label>
734
+ </div>
735
+ )}
736
+ </CardContent>
737
+ <CardFooter className="flex flex-col items-end pt-4 border-t gap-2">
738
+ {!sessionId && (
739
+ <span className="text-xs text-amber-600 bg-amber-50 px-2 py-1 rounded-md border border-amber-200">
740
+ Click the document in the chat to open it in the canvas before signing.
741
+ </span>
742
+ )}
743
+
744
+ <div className="flex items-center gap-2">
745
+ {!hasSigned ? (
746
+ <>
747
+ <Button
748
+ variant="outline"
749
+ size="sm"
750
+ className="h-10 text-xs px-3 text-primary"
751
+ onClick={() => setIsVisualPlacementOpen(true)}
752
+ >
753
+ Place Visually
754
+ </Button>
755
+ <Button
756
+ onClick={() => handleSubmit()}
757
+ disabled={isSubmitting || !sessionId}
758
+ className="w-full sm:w-auto"
759
+ >
760
+ {isSubmitting ? <Loader2 className="mr-2 size-4 animate-spin" /> : null}
761
+ Preview Signature
762
+ </Button>
763
+ </>
764
+ ) : (
765
+ <>
766
+ <Button
767
+ variant="outline"
768
+ onClick={() => handleUndo()}
769
+ disabled={isSubmitting}
770
+ className="w-full sm:w-auto"
771
+ >
772
+ Undo & Adjust
773
+ </Button>
774
+ <Button
775
+ onClick={() => handleConfirm()}
776
+ className="bg-emerald-600 hover:bg-emerald-700 text-white w-full sm:w-auto"
777
+ >
778
+ Confirm Signature
779
+ </Button>
780
+ </>
781
+ )}
782
+ </div>
783
+ </CardFooter>
784
+
785
+ <VisualPlacementModal
786
+ open={isVisualPlacementOpen}
787
+ onOpenChange={setIsVisualPlacementOpen}
788
+ fileBase64={base64BeforeSign || fileBase64 || ''}
789
+ totalPages={parsedDocument?.totalPages || 1}
790
+ placements={placements}
791
+ onSave={(newPlacements) => {
792
+ setPlacements(newPlacements);
793
+ handleSubmit(newPlacements);
794
+ }}
795
+ mode={mode}
796
+ imagePayload={
797
+ mode === 'draw' && canvasRef.current
798
+ ? canvasRef.current.toDataURL('image/png')
799
+ : mode === 'upload'
800
+ ? uploadedImage
801
+ : null
802
+ }
803
+ text={text}
804
+ font={font}
805
+ color={color}
806
+ scale={scale}
807
+ />
808
+ </Card>
809
+ );
810
+ }