@arbidocs/blocks 0.4.153 → 0.4.155

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 (248) hide show
  1. package/dist/{ConnectedNativeDocxComparison-OFPIXNJB.js → ConnectedNativeDocxComparison-OLWRQSRS.js} +11 -11
  2. package/dist/{ConnectedNativeDocxComparison-OFPIXNJB.js.map → ConnectedNativeDocxComparison-OLWRQSRS.js.map} +1 -1
  3. package/dist/{ConnectedNativeDocxComparison-Y5BNLNH6.cjs → ConnectedNativeDocxComparison-YORMOPPN.cjs} +12 -12
  4. package/dist/{ConnectedNativeDocxComparison-Y5BNLNH6.cjs.map → ConnectedNativeDocxComparison-YORMOPPN.cjs.map} +1 -1
  5. package/dist/{ConnectedNativeDocxEditor-LRYU5HPA.js → ConnectedNativeDocxEditor-34EBIIFS.js} +10 -10
  6. package/dist/{ConnectedNativeDocxEditor-LRYU5HPA.js.map → ConnectedNativeDocxEditor-34EBIIFS.js.map} +1 -1
  7. package/dist/{ConnectedNativeDocxEditor-HTEW5JAH.cjs → ConnectedNativeDocxEditor-EHTWGGX3.cjs} +12 -12
  8. package/dist/{ConnectedNativeDocxEditor-HTEW5JAH.cjs.map → ConnectedNativeDocxEditor-EHTWGGX3.cjs.map} +1 -1
  9. package/dist/{ConnectedStreamedWordArtifact-QW74LKHN.cjs → ConnectedStreamedWordArtifact-5DK6QGYN.cjs} +12 -12
  10. package/dist/ConnectedStreamedWordArtifact-5DK6QGYN.cjs.map +1 -0
  11. package/dist/{ConnectedStreamedWordArtifact-QAUVUCSX.js → ConnectedStreamedWordArtifact-GLIDLSAS.js} +11 -11
  12. package/dist/{ConnectedStreamedWordArtifact-QAUVUCSX.js.map → ConnectedStreamedWordArtifact-GLIDLSAS.js.map} +1 -1
  13. package/dist/WorkspaceTableView-5OWX53R5.js +376 -0
  14. package/dist/WorkspaceTableView-5OWX53R5.js.map +1 -0
  15. package/dist/WorkspaceTableView-ZFMAYHUD.cjs +378 -0
  16. package/dist/WorkspaceTableView-ZFMAYHUD.cjs.map +1 -0
  17. package/dist/agents/index.cjs +282 -282
  18. package/dist/agents/index.js +7 -7
  19. package/dist/{chunk-RQ2Y7USX.cjs → chunk-2VHYOTWW.cjs} +3 -3
  20. package/dist/chunk-2VHYOTWW.cjs.map +1 -0
  21. package/dist/{chunk-DTROMMDX.cjs → chunk-3VULDTNL.cjs} +4 -4
  22. package/dist/{chunk-DTROMMDX.cjs.map → chunk-3VULDTNL.cjs.map} +1 -1
  23. package/dist/{chunk-UTTTJSQU.cjs → chunk-4RPBUMHB.cjs} +4 -4
  24. package/dist/{chunk-UTTTJSQU.cjs.map → chunk-4RPBUMHB.cjs.map} +1 -1
  25. package/dist/chunk-53RCZPMR.cjs +496 -0
  26. package/dist/chunk-53RCZPMR.cjs.map +1 -0
  27. package/dist/chunk-5TDGVOR5.js +142 -0
  28. package/dist/chunk-5TDGVOR5.js.map +1 -0
  29. package/dist/chunk-6HMCF4AJ.js +1720 -0
  30. package/dist/chunk-6HMCF4AJ.js.map +1 -0
  31. package/dist/{chunk-CU2O32Z4.cjs → chunk-6QAAIDQQ.cjs} +62 -99
  32. package/dist/chunk-6QAAIDQQ.cjs.map +1 -0
  33. package/dist/chunk-7HHYMDPE.js +431 -0
  34. package/dist/chunk-7HHYMDPE.js.map +1 -0
  35. package/dist/{chunk-SMN32BQ3.cjs → chunk-7NL6WJER.cjs} +3 -160
  36. package/dist/chunk-7NL6WJER.cjs.map +1 -0
  37. package/dist/{chunk-IXWJGOM2.js → chunk-BQ77KSXU.js} +3 -3
  38. package/dist/{chunk-IXWJGOM2.js.map → chunk-BQ77KSXU.js.map} +1 -1
  39. package/dist/chunk-BTF4AZMG.js +148 -0
  40. package/dist/chunk-BTF4AZMG.js.map +1 -0
  41. package/dist/{chunk-HALD6ZB5.cjs → chunk-C3SMTA3C.cjs} +4 -4
  42. package/dist/{chunk-HALD6ZB5.cjs.map → chunk-C3SMTA3C.cjs.map} +1 -1
  43. package/dist/{chunk-GAYMJWTC.cjs → chunk-CB4TIDEX.cjs} +6 -6
  44. package/dist/{chunk-GAYMJWTC.cjs.map → chunk-CB4TIDEX.cjs.map} +1 -1
  45. package/dist/{chunk-KPZZ5FHE.cjs → chunk-CNHUJRQ4.cjs} +10 -6
  46. package/dist/chunk-CNHUJRQ4.cjs.map +1 -0
  47. package/dist/{chunk-Q4BY2CQQ.cjs → chunk-CQCNU5KT.cjs} +28 -28
  48. package/dist/{chunk-Q4BY2CQQ.cjs.map → chunk-CQCNU5KT.cjs.map} +1 -1
  49. package/dist/{chunk-GRGS5M6O.cjs → chunk-CROGFB46.cjs} +160 -3
  50. package/dist/chunk-CROGFB46.cjs.map +1 -0
  51. package/dist/{chunk-QN47Q4N7.js → chunk-CU6LDQ57.js} +3 -3
  52. package/dist/{chunk-QN47Q4N7.js.map → chunk-CU6LDQ57.js.map} +1 -1
  53. package/dist/{chunk-EF4ZLKRM.js → chunk-DEPF5LQI.js} +3 -3
  54. package/dist/{chunk-EF4ZLKRM.js.map → chunk-DEPF5LQI.js.map} +1 -1
  55. package/dist/{chunk-JAANWQGB.js → chunk-ETZ5A3GU.js} +3 -3
  56. package/dist/{chunk-JAANWQGB.js.map → chunk-ETZ5A3GU.js.map} +1 -1
  57. package/dist/{chunk-TBEVGWY3.js → chunk-F57LNUSZ.js} +10 -6
  58. package/dist/chunk-F57LNUSZ.js.map +1 -0
  59. package/dist/{chunk-GFNELPDP.cjs → chunk-FM3IRQJW.cjs} +10 -5
  60. package/dist/chunk-FM3IRQJW.cjs.map +1 -0
  61. package/dist/chunk-FPHTSNOE.js +465 -0
  62. package/dist/chunk-FPHTSNOE.js.map +1 -0
  63. package/dist/{chunk-OVR3XMYL.js → chunk-FYZXDZM7.js} +3 -3
  64. package/dist/{chunk-OVR3XMYL.js.map → chunk-FYZXDZM7.js.map} +1 -1
  65. package/dist/{chunk-2FLWPM3B.cjs → chunk-GC2FSPSQ.cjs} +7 -7
  66. package/dist/{chunk-2FLWPM3B.cjs.map → chunk-GC2FSPSQ.cjs.map} +1 -1
  67. package/dist/{chunk-DB2OXRQC.js → chunk-GO3BN76C.js} +7 -7
  68. package/dist/{chunk-DB2OXRQC.js.map → chunk-GO3BN76C.js.map} +1 -1
  69. package/dist/{chunk-XZIYRKLW.cjs → chunk-H32DV7XS.cjs} +5 -139
  70. package/dist/chunk-H32DV7XS.cjs.map +1 -0
  71. package/dist/{chunk-4236BRZK.js → chunk-HP2C4MD3.js} +29 -3
  72. package/dist/chunk-HP2C4MD3.js.map +1 -0
  73. package/dist/{chunk-ZP6Q3KXU.cjs → chunk-HUATKA3M.cjs} +4 -4
  74. package/dist/{chunk-ZP6Q3KXU.cjs.map → chunk-HUATKA3M.cjs.map} +1 -1
  75. package/dist/chunk-ISHFVKVU.js +304 -0
  76. package/dist/chunk-ISHFVKVU.js.map +1 -0
  77. package/dist/chunk-JUUOJP7I.cjs +164 -0
  78. package/dist/chunk-JUUOJP7I.cjs.map +1 -0
  79. package/dist/{chunk-4KMLM3GY.cjs → chunk-KBO75NLM.cjs} +7 -7
  80. package/dist/{chunk-4KMLM3GY.cjs.map → chunk-KBO75NLM.cjs.map} +1 -1
  81. package/dist/{chunk-Z4NQ3TED.js → chunk-KCSIX7CY.js} +3 -3
  82. package/dist/{chunk-Z4NQ3TED.js.map → chunk-KCSIX7CY.js.map} +1 -1
  83. package/dist/{chunk-DN2KI4BU.js → chunk-KDLX5AA7.js} +3 -3
  84. package/dist/{chunk-DN2KI4BU.js.map → chunk-KDLX5AA7.js.map} +1 -1
  85. package/dist/{chunk-WFA2GVZJ.js → chunk-MF5KQ4PS.js} +3 -3
  86. package/dist/{chunk-WFA2GVZJ.js.map → chunk-MF5KQ4PS.js.map} +1 -1
  87. package/dist/{chunk-YXNNNP57.js → chunk-MNG7N6M2.js} +4 -4
  88. package/dist/{chunk-YXNNNP57.js.map → chunk-MNG7N6M2.js.map} +1 -1
  89. package/dist/chunk-MSFQDELO.cjs +482 -0
  90. package/dist/chunk-MSFQDELO.cjs.map +1 -0
  91. package/dist/{chunk-T74Q3MCZ.cjs → chunk-MUDEL7UL.cjs} +25 -884
  92. package/dist/chunk-MUDEL7UL.cjs.map +1 -0
  93. package/dist/{chunk-KSHKTOY2.js → chunk-NUSBINUM.js} +3 -3
  94. package/dist/chunk-NUSBINUM.js.map +1 -0
  95. package/dist/{chunk-EYKISBDO.cjs → chunk-OALEDG67.cjs} +5 -5
  96. package/dist/{chunk-EYKISBDO.cjs.map → chunk-OALEDG67.cjs.map} +1 -1
  97. package/dist/{chunk-COCGRJLA.cjs → chunk-OFV3PRF2.cjs} +7 -7
  98. package/dist/{chunk-COCGRJLA.cjs.map → chunk-OFV3PRF2.cjs.map} +1 -1
  99. package/dist/chunk-OOU4QLRK.cjs +319 -0
  100. package/dist/chunk-OOU4QLRK.cjs.map +1 -0
  101. package/dist/chunk-OU3RGQFX.cjs +136 -0
  102. package/dist/chunk-OU3RGQFX.cjs.map +1 -0
  103. package/dist/{chunk-3PCBYOIZ.js → chunk-P6RIBIVN.js} +38 -7
  104. package/dist/chunk-P6RIBIVN.js.map +1 -0
  105. package/dist/{chunk-LXTOOUEN.cjs → chunk-Q7CQYYZW.cjs} +5 -5
  106. package/dist/{chunk-LXTOOUEN.cjs.map → chunk-Q7CQYYZW.cjs.map} +1 -1
  107. package/dist/{chunk-CROTH2BS.js → chunk-QKR5LWDO.js} +9 -40
  108. package/dist/{chunk-CROTH2BS.js.map → chunk-QKR5LWDO.js.map} +1 -1
  109. package/dist/chunk-REH7PX3N.cjs +145 -0
  110. package/dist/chunk-REH7PX3N.cjs.map +1 -0
  111. package/dist/chunk-RQKWYKZY.js +127 -0
  112. package/dist/chunk-RQKWYKZY.js.map +1 -0
  113. package/dist/{chunk-U6JQ4KMX.cjs → chunk-SN2GZ4ZM.cjs} +29 -2
  114. package/dist/chunk-SN2GZ4ZM.cjs.map +1 -0
  115. package/dist/{chunk-R6VCPDPF.js → chunk-SZSJGZSO.js} +156 -6
  116. package/dist/chunk-SZSJGZSO.js.map +1 -0
  117. package/dist/{chunk-US3S4DCB.js → chunk-TF7HN5EU.js} +10 -5
  118. package/dist/chunk-TF7HN5EU.js.map +1 -0
  119. package/dist/{chunk-YKQ2DPIP.js → chunk-VF5EUAIV.js} +3 -3
  120. package/dist/{chunk-YKQ2DPIP.js.map → chunk-VF5EUAIV.js.map} +1 -1
  121. package/dist/{chunk-KMZWKSKO.cjs → chunk-VZPFYZZU.cjs} +4 -4
  122. package/dist/{chunk-KMZWKSKO.cjs.map → chunk-VZPFYZZU.cjs.map} +1 -1
  123. package/dist/{chunk-HUJF2XM2.js → chunk-W7CWGURO.js} +28 -873
  124. package/dist/chunk-W7CWGURO.js.map +1 -0
  125. package/dist/{chunk-ZLEYLCE4.js → chunk-WK6FVIF7.js} +6 -156
  126. package/dist/chunk-WK6FVIF7.js.map +1 -0
  127. package/dist/chunk-WV4TD2HZ.cjs +1785 -0
  128. package/dist/chunk-WV4TD2HZ.cjs.map +1 -0
  129. package/dist/{chunk-COA3S3CZ.js → chunk-XI37A7HH.js} +4 -4
  130. package/dist/{chunk-COA3S3CZ.js.map → chunk-XI37A7HH.js.map} +1 -1
  131. package/dist/{chunk-LGBAZD37.js → chunk-XRL4R2TT.js} +7 -139
  132. package/dist/chunk-XRL4R2TT.js.map +1 -0
  133. package/dist/{chunk-4JNKXOFZ.cjs → chunk-YBJ4QGNH.cjs} +11 -11
  134. package/dist/{chunk-4JNKXOFZ.cjs.map → chunk-YBJ4QGNH.cjs.map} +1 -1
  135. package/dist/{chunk-GFOEWPAP.cjs → chunk-YF3QUHKN.cjs} +49 -12
  136. package/dist/chunk-YF3QUHKN.cjs.map +1 -0
  137. package/dist/{chunk-H4D7WKDY.js → chunk-YNMO3DSR.js} +3 -3
  138. package/dist/{chunk-H4D7WKDY.js.map → chunk-YNMO3DSR.js.map} +1 -1
  139. package/dist/citations/index.cjs +10 -10
  140. package/dist/citations/index.js +2 -2
  141. package/dist/collaboration/index.cjs +77 -77
  142. package/dist/collaboration/index.js +2 -2
  143. package/dist/connect/docx.cjs +14 -14
  144. package/dist/connect/docx.js +10 -10
  145. package/dist/connect/index.cjs +2820 -1043
  146. package/dist/connect/index.cjs.map +1 -1
  147. package/dist/connect/index.d.ts +4 -0
  148. package/dist/connect/index.js +2216 -504
  149. package/dist/connect/index.js.map +1 -1
  150. package/dist/connect/viewer.cjs +68 -72
  151. package/dist/connect/viewer.cjs.map +1 -1
  152. package/dist/connect/viewer.js +11 -15
  153. package/dist/connect/viewer.js.map +1 -1
  154. package/dist/connection/ConnectionProvider.d.ts +7 -0
  155. package/dist/connection/files/DocumentSyncGlyph.d.ts +28 -0
  156. package/dist/connection/files/explorer/ConnectedFilesExplorer.d.ts +56 -0
  157. package/dist/connection/files/explorer/FileTiles.d.ts +87 -0
  158. package/dist/connection/files/index.d.ts +5 -0
  159. package/dist/connection/files/store.d.ts +27 -0
  160. package/dist/connection/files/support.d.ts +38 -0
  161. package/dist/connection/projects/ConnectedProjectsPanel.d.ts +34 -0
  162. package/dist/connection/projects/index.d.ts +1 -0
  163. package/dist/connection/workspace-dashboard/ConnectedWorkspaceDashboard.d.ts +37 -0
  164. package/dist/connection/workspace-dashboard/dashboard-rows.d.ts +42 -0
  165. package/dist/connection/workspace-dashboard/index.d.ts +1 -0
  166. package/dist/connection/workspace-list/ConnectedWorkspaceList.d.ts +37 -0
  167. package/dist/connection/workspace-list/WorkspaceCardsView.d.ts +15 -0
  168. package/dist/connection/workspace-list/WorkspaceFilterMenu.d.ts +4 -0
  169. package/dist/connection/workspace-list/WorkspaceTableView.d.ts +13 -0
  170. package/dist/connection/workspace-list/index.d.ts +5 -0
  171. package/dist/connection/workspace-list/projectBilling.d.ts +21 -0
  172. package/dist/connection/workspace-list/useConnectedWorkspaceList.d.ts +251 -0
  173. package/dist/connection/workspace-list/workspaceTable.d.ts +58 -0
  174. package/dist/connection/workspace-settings/ConnectedWorkspaceMembers.d.ts +26 -0
  175. package/dist/connection/workspace-settings/ConnectedWorkspaceSettings.d.ts +7 -9
  176. package/dist/connection/workspace-settings/index.d.ts +2 -1
  177. package/dist/connection/workspace-settings/useConnectedWorkspaceSettings.d.ts +0 -1
  178. package/dist/facts/index.cjs +3 -3
  179. package/dist/facts/index.js +2 -2
  180. package/dist/files/DocStatusIcon.d.ts +5 -4
  181. package/dist/files/FailedStageIcon.d.ts +26 -0
  182. package/dist/files/folders/FolderProviderIcon.d.ts +29 -0
  183. package/dist/files/folders/folderPaths.d.ts +86 -0
  184. package/dist/files/folders/index.d.ts +2 -0
  185. package/dist/files/index.cjs +328 -240
  186. package/dist/files/index.d.ts +5 -0
  187. package/dist/files/index.js +4 -4
  188. package/dist/files/permissions.d.ts +28 -0
  189. package/dist/files/recently-deleted.d.ts +7 -0
  190. package/dist/files/visibility.d.ts +31 -0
  191. package/dist/home/index.cjs +119 -459
  192. package/dist/home/index.cjs.map +1 -1
  193. package/dist/home/index.js +13 -419
  194. package/dist/home/index.js.map +1 -1
  195. package/dist/index.cjs +480 -480
  196. package/dist/index.js +16 -16
  197. package/dist/provider/BlocksProvider.d.ts +9 -1
  198. package/dist/retrieval/index.cjs +4 -4
  199. package/dist/retrieval/index.js +2 -2
  200. package/dist/settings/index.cjs +169 -165
  201. package/dist/settings/index.d.ts +1 -0
  202. package/dist/settings/index.js +2 -2
  203. package/dist/settings/tri-state-filters.d.ts +13 -0
  204. package/dist/shell/index.cjs +44 -44
  205. package/dist/shell/index.js +3 -3
  206. package/dist/site/index.cjs +5 -5
  207. package/dist/site/index.js +1 -1
  208. package/dist/studio/index.cjs +14 -14
  209. package/dist/studio/index.js +6 -6
  210. package/dist/workflow-canvas/index.cjs +27 -27
  211. package/dist/workflow-canvas/index.js +9 -9
  212. package/dist/workspaces/index.cjs +110 -369
  213. package/dist/workspaces/index.cjs.map +1 -1
  214. package/dist/workspaces/index.d.ts +2 -0
  215. package/dist/workspaces/index.js +12 -350
  216. package/dist/workspaces/index.js.map +1 -1
  217. package/dist/workspaces/workspace-groups.d.ts +90 -0
  218. package/dist/workspaces/workspace-roles.d.ts +11 -0
  219. package/node_modules/@arbidocs/coedit/package.json +1 -1
  220. package/package.json +6 -5
  221. package/dist/ConnectedStreamedWordArtifact-QW74LKHN.cjs.map +0 -1
  222. package/dist/chunk-3PCBYOIZ.js.map +0 -1
  223. package/dist/chunk-4236BRZK.js.map +0 -1
  224. package/dist/chunk-CU2O32Z4.cjs.map +0 -1
  225. package/dist/chunk-EV7W64B6.js +0 -134
  226. package/dist/chunk-EV7W64B6.js.map +0 -1
  227. package/dist/chunk-GFNELPDP.cjs.map +0 -1
  228. package/dist/chunk-GFOEWPAP.cjs.map +0 -1
  229. package/dist/chunk-GRGS5M6O.cjs.map +0 -1
  230. package/dist/chunk-HUJF2XM2.js.map +0 -1
  231. package/dist/chunk-JGOST4IL.js +0 -648
  232. package/dist/chunk-JGOST4IL.js.map +0 -1
  233. package/dist/chunk-KPZZ5FHE.cjs.map +0 -1
  234. package/dist/chunk-KSHKTOY2.js.map +0 -1
  235. package/dist/chunk-LGBAZD37.js.map +0 -1
  236. package/dist/chunk-R6VCPDPF.js.map +0 -1
  237. package/dist/chunk-RQ2Y7USX.cjs.map +0 -1
  238. package/dist/chunk-RXK3XP62.cjs +0 -144
  239. package/dist/chunk-RXK3XP62.cjs.map +0 -1
  240. package/dist/chunk-SMN32BQ3.cjs.map +0 -1
  241. package/dist/chunk-T74Q3MCZ.cjs.map +0 -1
  242. package/dist/chunk-TBEVGWY3.js.map +0 -1
  243. package/dist/chunk-U6JQ4KMX.cjs.map +0 -1
  244. package/dist/chunk-US3S4DCB.js.map +0 -1
  245. package/dist/chunk-XZIYRKLW.cjs.map +0 -1
  246. package/dist/chunk-YAEXSCUW.cjs +0 -677
  247. package/dist/chunk-YAEXSCUW.cjs.map +0 -1
  248. package/dist/chunk-ZLEYLCE4.js.map +0 -1
@@ -1,46 +1,54 @@
1
1
  "use client";
2
- import { PageLoading, WorkspacePageFrame, WorkspaceIdentityPanel, WorkspaceProjectSlot, WorkspaceAudienceLine, CommonWorkspaceToggle, ExpandableSection, LeaveFooter } from '../chunk-EV7W64B6.js';
3
- import { DraggableHeader, SettingsSection, ProfileCard, AvatarPicker, IdentityVerificationCard, IdentityVerificationDialog, RequireVerified, PasswordChangeDialog, SettingsCard, SettingsActionRow, EMAIL_NOTIFICATION_KEYS, EMAIL_NOTIFICATION_CATEGORIES, NotificationCategoryList, toNotificationCategories, toActiveSessionViews, SettingsRow, ActiveSessionsDialog, LogoutDialog, toLogoutSessionViews, SettingsDangerZone, DeleteAccountDialog, toDeleteBlockerViews, SettingsPanel, PremiumModelsFrame, ConnectionsLoading, ConnectionsError, ConnectionsEmpty, SettingsPage, SyncDialogBody, SyncField, SyncSelect, SyncSmallNote, SyncLoading, SyncReconnectNotice, SyncNote, SyncTreeFrame, SyncLinkRow, SyncLinkButton, SyncOptions, SyncOptionToggle, SyncDialogFooter, InvoicesDialog, PlanDialogFrame, ProjectList, RenameDialog, SharedProjectsSection, SharedProjectRow, MoreInfo, MoreInfoList, ProjectDetailHeader, PlanBadge, ProjectDetailSection, PlanStack, PlanNotice, PlanPickerCard, PlanPickerHeader, PricingToggle, PromoCodeInput, PlanActionButton, ProjectRow, QuotaMeter, FilterChip, applyInvertedIndexFilter, buildInvertedIndex, serverConnectionView, ServerConnectionButton, ModelProviderLabel, SyncFolderRow, SyncFolderStatus, PlanTierOption, PlanActionRow, SubscriptionStatusView, DailyCapField, UsagePanel, UsageSummary, UsageQuotaHint, UsageToggle, UsageSection, UsageLineChart, UsageLegend, UsageProportionBar, UsageActionButton, PlanInfoPopover, PlanCardGrid, ReferralRewardSummary, CheckoutDialogFrame, ConnectionCard, ReferralRewardNote } from '../chunk-4236BRZK.js';
4
- export { ConnectedAssistantDock, redlineStats, textToArtifact, toRedlineMarkdown, useConnectedAiTask, useConnectedChat, useSemanticSearch, useWorkspaceDocs } from '../chunk-Z4NQ3TED.js';
5
- import { useConnectedWorkspaceRole, ConnectedNativeDocxEditor } from '../chunk-DB2OXRQC.js';
6
- export { ConnectedWorkflowDocumentsField, ConnectedWorkflowLauncher, ConnectedWorkflowsTable, DEFAULT_CONNECTED_WORKFLOWS_TABLE_LABELS, NativeDocxSupportProvider, toWorkflowFieldDocument, useConnectedAgentOptions, useConnectedWorkflowScopeSource, useConnectedWorkspaceRole, useNativeDocxSupport } from '../chunk-DB2OXRQC.js';
2
+ import { useBillingSupport, formatMoney, isProProject, useSelfExtId, addonCharge, formatPeriodEnd, quoteParts, taxDetail, money2, ANNUAL_PREMIUM_AI_NOTE, totalWithTaxWord, planBadgeView, DEFAULT_SPEND_CAP_PENCE, findProPrices, packPrice, calcSavingsPercent, parseBudgetPounds, getQuotaItems, PRO_PACKS, planNameForPacks, proTierName, STORAGE_BLOCK_GB, ADDON_BLOCK_AI_GBP, formatResetDate, quoteError, POSTCODE_COUNTRIES, addonPrice, checkoutBlockPriceText, quoteTotalLine, apiErrorDetail, checkoutItemsLabel, projectGroupBilling, SUGGESTED_DAILY_CAP_PER_USER, FREE_STORAGE_MB, FREE_AI_GBP, PRO_AI_GBP, PRO_STORAGE_GB, plusVat, planPreviewLines, PAY_TODAY_UNPRICED, aiUsageResetTimestamp, TAX_LOCATION_INVALID, FREE_COLLABORATORS, MAX_CHECKOUT_BLOCKS, reduceBounds, splitBlocks, autoRoom, isAddonBudgetZero, nextRemovedBlockSize, stepLabel, addStepCaption, blocksLabel, MIN_AUTO_TOPUP_THRESHOLD_GBP, USAGE_CAP_LABEL, CAP_REACHED_MESSAGE, USAGE_CAP_HELP, PLAN_CHOICE_PACKS, nextPeriodBody, addonSettingsBody, budgetSavedMessage } from '../chunk-7HHYMDPE.js';
3
+ export { BillingSupportProvider, CAP_REACHED_MESSAGE, ZERO_BUDGET_MESSAGE, aiUsageResetTimestamp, formatMoney, formatResetDate, getQuotaItems, isAddonBudgetZero, isProProject, planBadgeView, planName, projectGroupBilling, useBillingSupport, useRewardBalance } from '../chunk-7HHYMDPE.js';
4
+ import { getRoleBadgeClass, formatRole, getRoleDescription, PageLoading, WorkspacePageFrame, WorkspaceIdentityPanel, WorkspaceProjectSlot, WorkspaceAudienceLine, CommonWorkspaceToggle, ExpandableSection, LeaveFooter, WorkspaceList, WorkspaceGroup, WorkspaceCard } from '../chunk-FPHTSNOE.js';
5
+ import { isAgentAccount, workspaceConversationCount, workspaceDocumentCount, isListedWorkspace, WORKSPACE_CATEGORY_FILTERS, projectFilterId, workspaceFilterAttributes, groupWorkspacesByProject, byRecentUpdate } from '../chunk-BTF4AZMG.js';
6
+ import { HomeLoadError, HomeFrame, HomeMain, HomeCenteredScroll, HomeCenteredColumn, DashboardRoot, DashboardBand, DashboardColumns, RecentList, DashboardAction, FactsStrip, FactMark, CreateTile } from '../chunk-ISHFVKVU.js';
7
+ import { ProjectBandTitle, PlainBandTitle } from '../chunk-RQKWYKZY.js';
8
+ import '../chunk-5TDGVOR5.js';
9
+ import { DraggableHeader, SettingsSection, ProfileCard, AvatarPicker, IdentityVerificationCard, IdentityVerificationDialog, RequireVerified, PasswordChangeDialog, SettingsCard, SettingsActionRow, EMAIL_NOTIFICATION_KEYS, EMAIL_NOTIFICATION_CATEGORIES, NotificationCategoryList, toNotificationCategories, toActiveSessionViews, SettingsRow, ActiveSessionsDialog, LogoutDialog, toLogoutSessionViews, SettingsDangerZone, DeleteAccountDialog, toDeleteBlockerViews, SettingsPanel, PremiumModelsFrame, ConnectionsLoading, ConnectionsError, ConnectionsEmpty, SettingsPage, SyncDialogBody, SyncField, SyncSelect, SyncSmallNote, SyncLoading, SyncReconnectNotice, SyncNote, SyncTreeFrame, SyncLinkRow, SyncLinkButton, SyncOptions, SyncOptionToggle, SyncDialogFooter, InvoicesDialog, PlanDialogFrame, ProjectList, RenameDialog, SharedProjectsSection, SharedProjectRow, MoreInfo, MoreInfoList, ProjectDetailHeader, PlanBadge, ProjectDetailSection, PlanStack, PlanNotice, PlanPickerCard, PlanPickerHeader, PricingToggle, PromoCodeInput, PlanActionButton, applyTriStateFilters, PanelFrame, PanelAddButton, PanelFrameBody, PanelScroll, PanelActionsRow, ProjectRow, QuotaMeter, FilterChip, applyInvertedIndexFilter, buildInvertedIndex, serverConnectionView, ServerConnectionButton, ModelProviderLabel, SyncFolderRow, SyncFolderStatus, PlanTierOption, PlanActionRow, SubscriptionStatusView, DailyCapField, UsagePanel, UsageSummary, UsageQuotaHint, UsageToggle, UsageSection, UsageLineChart, UsageLegend, UsageProportionBar, UsageActionButton, PlanInfoPopover, PlanCardGrid, ReferralRewardSummary, CheckoutDialogFrame, ConnectionCard, ReferralRewardNote } from '../chunk-HP2C4MD3.js';
10
+ export { ConnectedAssistantDock, redlineStats, textToArtifact, toRedlineMarkdown, useConnectedAiTask, useConnectedChat, useSemanticSearch, useWorkspaceDocs } from '../chunk-KCSIX7CY.js';
11
+ import { useConnectedWorkspaceRole, ConnectedNativeDocxEditor } from '../chunk-GO3BN76C.js';
12
+ export { ConnectedWorkflowDocumentsField, ConnectedWorkflowLauncher, ConnectedWorkflowsTable, DEFAULT_CONNECTED_WORKFLOWS_TABLE_LABELS, NativeDocxSupportProvider, toWorkflowFieldDocument, useConnectedAgentOptions, useConnectedWorkflowScopeSource, useConnectedWorkspaceRole, useNativeDocxSupport } from '../chunk-GO3BN76C.js';
7
13
  export { DocumentViewerSupportProvider, useDocumentViewerSupport } from '../chunk-EYJMMGXF.js';
8
- import { FilterButton, FilesViewCenter, FilesFilterBar, ROW_MIN, rowHeightForLines, linesForRowHeight, RowDensityControl, FolderFilterButton, FolderPickerRow, FieldLabelsSelect } from '../chunk-JGOST4IL.js';
14
+ import { FilterButton, ExplorerFileTile, formatFolderSegment, hasFolderProviderIcon, FolderProviderIcon, ExplorerFolderTile, searchExplorerDocuments, buildExplorerEntries, collectFolderDocs, joinFolderPath, getExplorerDisplayName, RECENTLY_DELETED_HINT, RECENTLY_DELETED_HINT_PLURAL, canDeleteDocument, ExplorerFrame, ExplorerStage, ExplorerScroller, ExplorerLoadingTiles, ExplorerEmpty, ExplorerMarquee, ExplorerDropOverlay, ExplorerDragPreview, ExplorerFileTooltip, visibilityAudience, StatusPill, FailedStageIcon, FilesViewCenter, FilesFilterBar, ROW_MIN, rowHeightForLines, linesForRowHeight, RowDensityControl, FolderFilterButton, FolderPickerRow, FieldLabelsSelect } from '../chunk-6HMCF4AJ.js';
9
15
  import '../chunk-RC6VTY2H.js';
10
- import { useConnectedTags, useConnectedDocuments, useConnectedConversations } from '../chunk-DN2KI4BU.js';
11
- export { ConnectedWorkspaceDataProvider, useConnectedConversations, useConnectedDocuments, useConnectedTags } from '../chunk-DN2KI4BU.js';
12
- import { ConnectionProvider, useConnection, useConnectedWorkspace } from '../chunk-US3S4DCB.js';
13
- export { ConnectionProvider, SESSION_EXPIRED_MESSAGE, isAuthenticated, useConnectedWorkspace, useConnection } from '../chunk-US3S4DCB.js';
16
+ import { useConnectedTags, useConnectedDocuments, useConnectedConversations } from '../chunk-KDLX5AA7.js';
17
+ export { ConnectedWorkspaceDataProvider, useConnectedConversations, useConnectedDocuments, useConnectedTags } from '../chunk-KDLX5AA7.js';
18
+ import { useConnectedWorkspace, ConnectionProvider, useConnection } from '../chunk-TF7HN5EU.js';
19
+ export { ConnectionProvider, SESSION_EXPIRED_MESSAGE, isAuthenticated, useConnectedWorkspace, useConnection } from '../chunk-TF7HN5EU.js';
14
20
  import { SignDocumentDialog } from '../chunk-2YOVK524.js';
15
21
  import '../chunk-MTSKK3BN.js';
16
22
  import '../chunk-265GOG4W.js';
17
23
  import { docIdsOf, toFactCitations, FactsToolbar, FactsFieldsButton, FactComposer, FactDateInput, FactSourcesRow, FactsLoading, FactsLoadError, FactsEmptyState, FactsCalendar, FactsGanttPane, FactsBulkBar, FactSourceGroup, EMPTY_FACT_DATE_FIELDS, withoutChunk, factDateSpanToFields, FactFieldRow, FactLabelsCell, enableFactTableColumnResize, citedVersionRef, withChunk, factDateFieldsToSpan, createFactColumns, FactRowActions, withoutDoc, withDoc, sourceDocCount, DEFAULT_FACT_COLUMN_LABELS } from '../chunk-WU34CHU5.js';
18
24
  import { ViewToggle } from '../chunk-JJA6GQKH.js';
19
25
  import { FactSourcesPanel, FactPassageItem } from '../chunk-HFZ45FD3.js';
20
- import { WorkspaceActivityList, WorkspaceNotificationMenu, ThreadFollowMenuItem, ThreadFollowButton, TagTable, TagPicker, tagsPageNouns, entitiesPhrase, ExtractEntitiesDialog, GridRowNavigator, LoadingIndicator, EntityIdentityBar, IDENTITY_NAME_MAX_LENGTH, tagTableDocx, documentCount, tagTableLabels, TagDetail, tagDetailLabels, tagNoteLabel, tagKindDescription, glossaryEntries, glossaryDocx, glossaryMarkdown, glossaryFileName } from '../chunk-YXNNNP57.js';
26
+ import { WorkspaceActivityList, WorkspaceNotificationMenu, ThreadFollowMenuItem, ThreadFollowButton, TagTable, TagPicker, tagsPageNouns, entitiesPhrase, ExtractEntitiesDialog, GridRowNavigator, LoadingIndicator, EntityIdentityBar, IDENTITY_NAME_MAX_LENGTH, tagTableDocx, documentCount, tagTableLabels, TagDetail, tagDetailLabels, tagNoteLabel, tagKindDescription, glossaryEntries, glossaryDocx, glossaryMarkdown, glossaryFileName } from '../chunk-MNG7N6M2.js';
21
27
  import { MemberAvatarStack } from '../chunk-XNOASBGW.js';
22
28
  import { renderSearchSnippet, SearchGroupHeader, isMeaningMatch, extractHighlightTerms, passesRelevanceCutoff, groupSearchResults, SearchPillToggle, SearchPagination, SearchPageFrame, SearchPageHeader, ApplyLabelDialog, SearchPageBody, SearchToolbarRow, SearchBar, SearchControlsBar, RelevanceThreshold, SearchTotalsHeadline, ApplyLabelButton, getSearchMatchHint, SearchResultsMain, SearchResultsInner, SearchErrorBanner, SearchResultList, SearchGroupCard, SearchNoResults, SearchEmptyState } from '../chunk-G4IYES7G.js';
23
29
  import { UsageMeter } from '../chunk-66TA5IBN.js';
30
+ import '../chunk-UJE32SST.js';
24
31
  import '../chunk-NHZK64TQ.js';
25
- import { PlainMailBodyEditor, buildMailFolderTree, MailFolderTree, draftMailCompose, replyMailCompose, emptyMailCompose, MailGrid, MailMessageView, MailDocumentPicker, parseMailRecipients } from '../chunk-R6VCPDPF.js';
32
+ import { PlainMailBodyEditor, MemberNameCell, MemberMutedText, MemberRoleCell, MemberListLoading, MemberBulkActions, buildMailFolderTree, MailFolderTree, draftMailCompose, replyMailCompose, emptyMailCompose, MailGrid, MailMessageView, MailDocumentPicker, parseMailRecipients } from '../chunk-SZSJGZSO.js';
33
+ import '../chunk-NBV56SGU.js';
26
34
  import '../chunk-SDOEHEJP.js';
27
35
  import { WorkspaceIdentity, workspaceKindColor } from '../chunk-A3XPS2EU.js';
28
- import '../chunk-SV65E4QW.js';
36
+ import { buildDeepLinkPath } from '../chunk-SV65E4QW.js';
29
37
  import '../chunk-ZCOVDK2E.js';
30
38
  import { isDefinitionsLabel, DOCX_MIME_TYPE, toDefinitionSource, definitionLabels } from '../chunk-DIB4JYUY.js';
31
- import { SearchHit } from '../chunk-EF4ZLKRM.js';
39
+ import { SearchHit } from '../chunk-DEPF5LQI.js';
32
40
  import { TimelineZoomControl } from '../chunk-4EZVCGRL.js';
33
41
  import { decodeWhitespaceEntities } from '../chunk-AF6RDAJF.js';
34
- import { buildLibrarySkills, filterLibrarySkills, SKILL_SOURCE_LABELS, SKILL_TYPE_LABELS, SkillViewToggle, SkillFilterChips, SkillTile, SkillGenerateForm, SkillLibraryDetail, parseSkillFrontmatter, SKILL_MAIN_FILENAME, SkillEditForm, DEFAULT_AGENT_CAPABILITIES, stripPersonaPreamble, AgentCreator, DEFAULT_AGENT_TYPES, DEFAULT_AGENT_TEMPLATES, AgentAvatarField, isValidAgentName, AgentConnectInstructions, TaskRunsDialog, TaskActivityDialog, WorkflowStepBar, composePersona, importSkillFile, importSkillFromUrl, WorkflowRunner, skillSlug, serializeSkill, fromPlanSteps, toPlanSteps, useWorkflowView, workflowToSkillMarkdown } from '../chunk-3PCBYOIZ.js';
42
+ import { buildLibrarySkills, filterLibrarySkills, SKILL_SOURCE_LABELS, SKILL_TYPE_LABELS, SkillViewToggle, SkillFilterChips, SkillTile, SkillGenerateForm, SkillLibraryDetail, parseSkillFrontmatter, SKILL_MAIN_FILENAME, SkillEditForm, DEFAULT_AGENT_CAPABILITIES, stripPersonaPreamble, AgentCreator, DEFAULT_AGENT_TYPES, DEFAULT_AGENT_TEMPLATES, AgentAvatarField, isValidAgentName, AgentConnectInstructions, TaskRunsDialog, TaskActivityDialog, WorkflowStepBar, composePersona, importSkillFile, importSkillFromUrl, WorkflowRunner, skillSlug, serializeSkill, fromPlanSteps, toPlanSteps, useWorkflowView, STATUS_LABELS, asTaskStatus, workflowToSkillMarkdown } from '../chunk-P6RIBIVN.js';
35
43
  import { MultiSelect } from '../chunk-CQCC7LE7.js';
36
- import { PersonPill } from '../chunk-COA3S3CZ.js';
37
- import '../chunk-IXWJGOM2.js';
38
- import { TriStateFilterMenu, DocumentPicker } from '../chunk-KSHKTOY2.js';
44
+ import { PersonPill } from '../chunk-XI37A7HH.js';
45
+ import { SectionLabel } from '../chunk-BQ77KSXU.js';
46
+ import { TriStateFilterMenu, DocumentPicker } from '../chunk-NUSBINUM.js';
39
47
  import '../chunk-P6GHJSLR.js';
40
48
  import '../chunk-M4S75AEJ.js';
41
49
  import '../chunk-5LXQPP2K.js';
42
50
  import '../chunk-RLWIUUQ5.js';
43
- import '../chunk-YKQ2DPIP.js';
51
+ import '../chunk-VF5EUAIV.js';
44
52
  import { SelectionBar, SelectionBarAction } from '../chunk-NUNKSZKU.js';
45
53
  import { FilterInput } from '../chunk-YU2KUMGE.js';
46
54
  import { GridView } from '../chunk-N5YEF3LC.js';
@@ -57,21 +65,21 @@ import { splitDocRef, stepLevels, stepDocuments, stepKindOf, initialRunInputs, w
57
65
  import '../chunk-TZGBRLPK.js';
58
66
  import '../chunk-AUS3KBHJ.js';
59
67
  import '../chunk-AA3PT6KR.js';
60
- import '../chunk-SLSBJRWW.js';
68
+ import { useIsPhone } from '../chunk-SLSBJRWW.js';
61
69
  import '../chunk-NPLYHR2E.js';
62
70
  import '../chunk-7F6CBP3Z.js';
63
- import { PassagePill, useCopyText } from '../chunk-JAANWQGB.js';
64
- import { useBlocksEnvironment, useRequestedSettingsSection } from '../chunk-TBEVGWY3.js';
71
+ import { PassagePill, useCopyText } from '../chunk-ETZ5A3GU.js';
72
+ import { useBlocksEnvironment, useRequestedSettingsSection, BlocksProvider } from '../chunk-F57LNUSZ.js';
65
73
  import { ModalScaffold, ModalSection } from '../chunk-TX2ZICS7.js';
66
74
  import { fmtDate, initials } from '../chunk-OXF2IFXG.js';
67
75
  import '../chunk-VUNV25KB.js';
68
- import { lazy, memo, useState, useEffect, createContext, useCallback, useMemo, useRef, Suspense, useContext, useSyncExternalStore, useLayoutEffect, useId } from 'react';
76
+ import { lazy, memo, useState, useEffect, useCallback, createContext, useMemo, forwardRef, useRef, useLayoutEffect, useImperativeHandle, useContext, Suspense, useSyncExternalStore, useId } from 'react';
69
77
  import { QueryClient, QueryClientProvider, useQueryClient, useQuery } from '@tanstack/react-query';
70
- import { HtmlIcon, useArbiSessionState, ArbiProvider, useConversations, useRunConversation, useWorkspaceActivity, useWorkspaceUsers, isWorkspaceNotificationType, useArbi, useNotifications, useDeleteNotifications, useMarkNotificationsRead, useWorkspaces, useWorkspaceNotificationLevel, useFollowThread, useUploadSkillFiles, useSkills, useSetSkillVisibility, useDeleteSkill, useSkillSource, useAgentModels, useMcpTools, useAgentConfig, useAgentWorkspaces, useCreateAgent, useSaveAgentConfig, useGrantAgentAccess, useRevokeAgentAccess, useGenerateAgentAvatar, useWorkflowRuns, useConversationActivity, useAgents, ArtifactCanvas, useArbiChat, useDocuments, useTagKindSchema, useTags, useCreateTag, useSetTagValues, useUpdateTagKindSchema, useFacts, useDeleteTag, useExtractEntities, useShellWorkspaces, ArbiBotHeadIcon, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useSignOut, useDeleteAccount, useDeleteAccountFlow, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailPolicy, useMailAccounts, useUserSettings, useUpdateUserSettings, useAvailableModels, useConnectorVendorVisibility, useConnectorCatalog, useMyConnectors, useDisconnectConnector, useSlackConnection, useDisconnectSlack, isMailboxSyncVendor, isSharePointVendor, useUpdateWorkspace, useAddWorkspaceMembers, useRemoveWorkspaceMembers, toShellWorkspace, useEnableMailboxSync, useMailboxFolders, useEnableDriveSync, useDriveFolderChildren, connectorErrorStatus, formatGbp, useProjectInvoices, useProjects, useUpdateProjectFields, useUserUsageToday, useChargeWatch, useAddonQuote, STILL_WORKING, useProjectBillingPortal, useIsSlow, useProducts, useCreateProject, useDeleteProject, useRefreshProject, MAX_RETRIEVE_DOC_IDS, useSemanticSearch, useAssignDocTags, useArbiWebSocket, useWorkNotifications, describeServerNotification, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailMessage, useMailCache, MailOfflineUnavailableError, accountPath, useCalendarEvents, useCalendars, useFetchCalendarEvent, useCalendarSettings, useCalendarOverlays, useCalendarOutbox, useCalendarAccounts, useSyncAccountsOnOpen, useResumeCalendarAccountConnect, useCreateEvent, useUpdateEvent, useDeleteEvent, useMoveEvent, useRespondToEvent, useCreateCalendar, useUpdateCalendar, useDeleteCalendar, useImportCalendar, useSaveCalendarSettings, useRefreshCalendar, useCalendarEvent, useMailAttachmentText, useCalendarEventByUid, getPublicBookingPage, bookPublicSlot, PublicBookingError, useAgentSession, useStartAgentSession, useCloseAgentSession, isQueuedCalendarWrite, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, connectorReturnTo, ArbiWebSocketProvider, isDeploymentUserNotification, getWorkspaceActionText, getWorkspaceSentPhrase, BOX_SEAT_REFUSED, BOX_SEAT_REFUSED_TITLE, documentActivityPhrase, isSignatureNotificationType, inputRequestTitle, WORK_NOTIFICATION_LABELS, documentDisplayName, getWorkspaceKind, formatRelativeTime, useCreateWork, useActOnStep, useConversationThreads, useUpdateTag, usePassageQuotes, useUpdateFact, useDeleteFact, useCreateFact, ProviderIcon, formatGbpPerMillionTokens, useDriveFolderDocCount, useChangeProjectPlan, useCancelProjectSubscription, usePreviewProjectPlanChange, useAddProjectAddonBlock, useProjectUsage, usePlanQuote, useCreateProjectCheckout, useInvalidateProjects, fileAttachment, useMailProjects, useSaveMailDraft, useSendMail, useMailDraftReplaced, useCalendarInvitations, useDecideCounter, useSearchContacts, usePublishCalendar, useCalendarShares, useShareCalendar, useUnshareCalendar, useSubscribeCalendar, useHolidayCountries, useAddHolidayCalendar, cancelPublicBooking, AiAnswer, signatureNotificationPhrase, signatureRequestMeta, getNotificationTypeMeta, useMailDraftEditor, useFreeBusy, useRooms, useFindTime, useCalendarSupport, useStartCalendarAccountOAuth, useConnectOAuthAccount, useBookingPages, useDeleteBookingPage, SHAREPOINT_VENDORS, useUpdateProjectNextPeriodAddons, useUpdateProjectAddonSettings, useSyncCalendarAccount, useUpdateCalendarAccount, useDisconnectCalendarAccount, ACCOUNT_VENDOR, useCalDavProfiles, useConnectCalDavAccount, useSaveBookingPage, useCreateRoom } from '@arbidocs/react';
78
+ import { HtmlIcon, useMailPolicy, useProcessingTasks, useUpdateDocuments, useDeleteDocuments, useRenameFolder, useDownloadDocuments, useTags, useDocumentThumbnails, useArbiSessionState, useProcessingTask, getDocumentSubject, useWorkspaces, ArbiProvider, useConversations, useRunConversation, useWorkspaceActivity, useWorkspaceUsers, isWorkspaceNotificationType, useArbi, useNotifications, useDeleteNotifications, useMarkNotificationsRead, useWorkspaceNotificationLevel, useFollowThread, useUploadSkillFiles, useSkills, useSetSkillVisibility, useDeleteSkill, useSkillSource, useAgentModels, useMcpTools, useAgentConfig, useAgentWorkspaces, useCreateAgent, useSaveAgentConfig, useGrantAgentAccess, useRevokeAgentAccess, useGenerateAgentAvatar, useWorkflowRuns, useConversationActivity, useAgents, ArtifactCanvas, useArbiChat, useDocuments, useTagKindSchema, useCreateTag, useSetTagValues, useUpdateTagKindSchema, useFacts, useDeleteTag, useExtractEntities, useShellWorkspaces, ArbiBotHeadIcon, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useSignOut, useDeleteAccount, useDeleteAccountFlow, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailAccounts, useUserSettings, useUpdateUserSettings, useAvailableModels, useConnectorVendorVisibility, useConnectorCatalog, useMyConnectors, useDisconnectConnector, useSlackConnection, useDisconnectSlack, isMailboxSyncVendor, isSharePointVendor, useUpdateWorkspace, useAddWorkspaceMembers, useRemoveWorkspaceMembers, useSetWorkspaceMemberRoles, toShellWorkspace, useEnableMailboxSync, useMailboxFolders, useEnableDriveSync, useDriveFolderChildren, connectorErrorStatus, useProjectInvoices, useProjects, useUpdateProjectFields, useUserUsageToday, formatGbp, useChargeWatch, useAddonQuote, STILL_WORKING, useProjectBillingPortal, useIsSlow, useProducts, useCreateProject, useDeleteProject, useRefreshProject, useCanCreateProjects, useDeleteWorkspaces, PROJECT_CREATION_DENIED_REASON, MAX_RETRIEVE_DOC_IDS, useSemanticSearch, useAssignDocTags, useArbiWebSocket, useWorkNotifications, describeServerNotification, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailMessage, useMailCache, MailOfflineUnavailableError, accountPath, useCalendarEvents, useCalendars, useFetchCalendarEvent, useCalendarSettings, useCalendarOverlays, useCalendarOutbox, useCalendarAccounts, useSyncAccountsOnOpen, useResumeCalendarAccountConnect, useCreateEvent, useUpdateEvent, useDeleteEvent, useMoveEvent, useRespondToEvent, useCreateCalendar, useUpdateCalendar, useDeleteCalendar, useImportCalendar, useSaveCalendarSettings, useRefreshCalendar, useCalendarEvent, useMailAttachmentText, useCalendarEventByUid, getPublicBookingPage, bookPublicSlot, PublicBookingError, useAgentSession, useStartAgentSession, useCloseAgentSession, isQueuedCalendarWrite, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, connectorReturnTo, ArbiWebSocketProvider, isDeploymentUserNotification, getWorkspaceActionText, getWorkspaceSentPhrase, BOX_SEAT_REFUSED, BOX_SEAT_REFUSED_TITLE, documentActivityPhrase, isSignatureNotificationType, inputRequestTitle, WORK_NOTIFICATION_LABELS, documentDisplayName, getWorkspaceKind, formatRelativeTime, useCreateWork, useActOnStep, useConversationThreads, useUpdateTag, usePassageQuotes, useUpdateFact, useDeleteFact, useCreateFact, ProviderIcon, formatGbpPerMillionTokens, useDriveFolderDocCount, useChangeProjectPlan, useCancelProjectSubscription, usePreviewProjectPlanChange, useAddProjectAddonBlock, useProjectUsage, usePlanQuote, useCreateProjectCheckout, useInvalidateProjects, fileAttachment, useMailProjects, useSaveMailDraft, useSendMail, useMailDraftReplaced, useCalendarInvitations, useDecideCounter, useSearchContacts, usePublishCalendar, useCalendarShares, useShareCalendar, useUnshareCalendar, useSubscribeCalendar, useHolidayCountries, useAddHolidayCalendar, cancelPublicBooking, AiAnswer, signatureNotificationPhrase, signatureRequestMeta, getNotificationTypeMeta, useMailDraftEditor, useFreeBusy, useRooms, useFindTime, useCalendarSupport, useStartCalendarAccountOAuth, useConnectOAuthAccount, useBookingPages, useDeleteBookingPage, SHAREPOINT_VENDORS, useUpdateProjectNextPeriodAddons, useUpdateProjectAddonSettings, useSyncCalendarAccount, useUpdateCalendarAccount, useDisconnectCalendarAccount, ACCOUNT_VENDOR, useCalDavProfiles, useConnectCalDavAccount, useSaveBookingPage, useCreateRoom } from '@arbidocs/react';
71
79
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
72
- import { Badge, Button, Label, Input, cn, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, tagKindNouns, tagKindTitle, isImageDocument, getDocumentTitle, getEntityColor, IconToggle, VisibilityToggle, Switch, getEntityLabel, ProjectEntity, DialogFooter, EntityIcon, DocumentEntity, EntityPill, ShieldIcon, getTagTypeLabel, Popover, PopoverTrigger, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Tooltip, TooltipTrigger, TooltipContent, PopoverContent, Tabs, TabsList, TabsTrigger, TabsContent, UserEntity, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, formatFriendlyDate, VerifiedToggle, Progress } from '@arbidocs/react/ui';
80
+ import { getFileTypeIcon, DocumentEntity, cn, getDocumentTitle, Badge, VisibilityIcon, formatFriendlyDate, getEntityLabel, Avatar, AvatarImage, getUserDisplayName, AvatarFallback, getUserInitials, getUserAvatarColor, Button, Label, Input, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, tagKindNouns, tagKindTitle, isImageDocument, getEntityColor, IconToggle, VisibilityToggle, Switch, ProjectEntity, DialogFooter, EntityIcon, getDocIcon, Tooltip, TooltipTrigger, TooltipContent, EntityPill, ShieldIcon, getTagTypeLabel, Popover, PopoverTrigger, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, UserEntity, PopoverContent, Tabs, TabsList, TabsTrigger, TabsContent, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, VerifiedToggle, Progress } from '@arbidocs/react/ui';
73
81
  import { useNotify, notify, sharedSearchSession, takePendingSearch } from '@arbidocs/react/shell';
74
- import { Bell, Activity, Info, UserCheck, Trash2, Loader2, Upload, Link2, Search, Plus, ArrowLeft, ExternalLink, Tags, CheckCircle2, Milestone, Monitor, UserCircle, KeyRound, Radio, LogOut, Wifi, RotateCcw, Mail, User, HatGlasses, MessageSquare, PlugZap, FolderInput, PoundSterling, HardDrive, Users, FileText, Tag, Settings, Video, Printer, CalendarDays, Clock, MapPin, Globe, Sparkles, ArrowUpCircle, Building2, Contact, Scale, NotebookPen, Check, X, Pencil, BookA, ChevronDown, Paperclip, Quote, Download, Send, Inbox, CalendarX, CalendarCheck, Minimize2, Maximize2, Minus, CalendarSearch, DoorOpen, RefreshCw, AlertTriangle } from 'lucide-react';
82
+ import { SquarePen, BookOpen, MessagesSquare, Pencil, Download, Mail, FolderInput, Trash2, Sparkles, Play, ArrowDownToLine, Bell, Activity, Info, UserCheck, Loader2, Upload, Link2, Search, Plus, ArrowLeft, ExternalLink, Tags, CheckCircle2, Milestone, Monitor, UserCircle, KeyRound, Radio, LogOut, Wifi, RotateCcw, User, HatGlasses, MessageSquare, PlugZap, SquareStack, Table, FileText, Tag, Settings, Video, Printer, CalendarDays, Clock, MapPin, Globe, AlertTriangle, FileX, SkipForward, CheckCircle, ArrowUpCircle, Building2, Contact, Scale, NotebookPen, Check, X, BookA, ChevronDown, Users, Paperclip, Quote, Send, Inbox, CalendarX, CalendarCheck, Minimize2, Maximize2, Minus, CalendarSearch, DoorOpen, RefreshCw } from 'lucide-react';
75
83
  import { downloadBlob, useIsOnline, useOptionalDocumentTools, fetchDocumentPdf, DocumentToolsProvider } from '@arbidocs/react/document-tools';
76
84
  import { useStreamedArtifact } from '@arbidocs/react/artifacts';
77
85
  import { FLAT_TAG_KINDS, TAG_KINDS, refusalReason, getChildTags, tagUseCount, flattenTagTree, isFlatKind, isEntityTag, tagsForScope, filterDocumentTags, isLabelTag, getTagDescendantIds, getEligibleParentTags, canTagHaveParent } from '@arbidocs/sdk/browser';
@@ -79,6 +87,8 @@ import { bytesToBase64 } from '@arbidocs/client';
79
87
  import { createPortal } from 'react-dom';
80
88
  import { create } from 'zustand';
81
89
  import { persist, createJSONStorage } from 'zustand/middleware';
90
+ import { useDraggable, useDroppable, useSensors, useSensor, MouseSensor, TouchSensor, pointerWithin, rectIntersection, DndContext, DragOverlay } from '@dnd-kit/core';
91
+ import { useVirtualizer } from '@tanstack/react-virtual';
82
92
 
83
93
  function ConnectionBoundWebSocket({ children }) {
84
94
  const { status } = useConnection();
@@ -672,13 +682,13 @@ function NotificationCenter({
672
682
  var PAGE_SIZE = 100;
673
683
  function localTime(timestamp) {
674
684
  const date = new Date(timestamp);
675
- const time = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
685
+ const time2 = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
676
686
  const today = /* @__PURE__ */ new Date();
677
687
  const yesterday = new Date(today);
678
688
  yesterday.setDate(today.getDate() - 1);
679
- if (date.toDateString() === today.toDateString()) return `Today, ${time}`;
680
- if (date.toDateString() === yesterday.toDateString()) return `Yesterday, ${time}`;
681
- return `${date.toLocaleDateString([], { month: "short", day: "numeric" })}, ${time}`;
689
+ if (date.toDateString() === today.toDateString()) return `Today, ${time2}`;
690
+ if (date.toDateString() === yesterday.toDateString()) return `Yesterday, ${time2}`;
691
+ return `${date.toLocaleDateString([], { month: "short", day: "numeric" })}, ${time2}`;
682
692
  }
683
693
  function DefaultUser({ user }) {
684
694
  const arbi = useArbi();
@@ -6660,27 +6670,11 @@ function useConnectedWorkspaceSettings(workspaceId, { onLeft } = {}) {
6660
6670
  }
6661
6671
  return "cancelled";
6662
6672
  }
6663
- const quota2 = status === 402 || lower.includes("limit reached") || lower.includes("quota") || lower.includes("upgrade");
6664
- if (!quota2) notify14(detail, "error");
6673
+ const quota = status === 402 || lower.includes("limit reached") || lower.includes("quota") || lower.includes("upgrade");
6674
+ if (!quota) notify14(detail, "error");
6665
6675
  return "error";
6666
6676
  }
6667
6677
  };
6668
- const removeMember = async (userExtId, email, name) => {
6669
- if (!workspace || !isOwner) return;
6670
- const ok = await confirm({
6671
- message: `Remove ${name} (${email}) from the "${workspace.name}" ${getEntityLabel("WORKSPACE")}?
6672
-
6673
- Note that ${getEntityLabel("WORKSPACEUSER", "PLURAL").toLowerCase()} may have saved private content in the workspace that you cannot see. If you remove them from the ${workspaceWord()} they will immediately lose access to any content in the ${workspaceWord()}.`,
6674
- title: `Remove ${getEntityLabel("WORKSPACEUSER")}`
6675
- });
6676
- if (!ok) return;
6677
- try {
6678
- await remove.mutateAsync([userExtId]);
6679
- notify14(`Successfully removed ${name} from workspace`, "success");
6680
- } catch {
6681
- notify14(`Failed to remove ${name} from workspace`, "error");
6682
- }
6683
- };
6684
6678
  const leave = async () => {
6685
6679
  if (!workspace || !userId || isOwner) return;
6686
6680
  const name = workspace.name;
@@ -6708,19 +6702,246 @@ You will lose access to all content in this ${workspaceWord()}.`,
6708
6702
  updateIdentity,
6709
6703
  setPublic,
6710
6704
  share,
6711
- removeMember,
6712
6705
  leave,
6713
6706
  isSharing: add.isPending,
6714
6707
  isRemoving: remove.isPending
6715
6708
  };
6716
6709
  }
6710
+ var memberName = (user) => user.family_name ? `${user.given_name} ${user.family_name}` : user.given_name;
6711
+ var ALL_ROLES = ["owner", "collaborator", "guest"].map((value) => ({ value, label: formatRole(value), badgeClass: getRoleBadgeClass(value) }));
6712
+ var BULK_ROLES = ALL_ROLES.filter((r) => r.value !== "owner");
6713
+ var ROLE_ORDER = { owner: 0, collaborator: 1, guest: 2 };
6714
+ function roleLock(targetId, selfId, isOwner) {
6715
+ if (!isOwner) return "Only owners can change roles";
6716
+ if (targetId === selfId) return "Your role can only be changed by another owner";
6717
+ return null;
6718
+ }
6719
+ var defaultRenderMember = (member) => {
6720
+ const name = memberName(member.user);
6721
+ return /* @__PURE__ */ jsx(
6722
+ UserEntity,
6723
+ {
6724
+ size: "xs",
6725
+ variant: "pill",
6726
+ showTooltip: false,
6727
+ interactive: false,
6728
+ displayName: name,
6729
+ initials: initials(name || member.user.email),
6730
+ picture: member.user.picture
6731
+ }
6732
+ );
6733
+ };
6734
+ function ConnectedWorkspaceMembers({
6735
+ workspace,
6736
+ members,
6737
+ selfId,
6738
+ isOwner: isOwnerRaw,
6739
+ isLoading = false,
6740
+ readOnly = false,
6741
+ renderMember = defaultRenderMember,
6742
+ gridState,
6743
+ onGridStateChange
6744
+ }) {
6745
+ const { confirm, notify: notify14 } = useBlocksEnvironment();
6746
+ const isOwner = isOwnerRaw && !readOnly;
6747
+ const [selected, setSelected] = useState([]);
6748
+ const updateRoles = useSetWorkspaceMemberRoles();
6749
+ const remove = useRemoveWorkspaceMembers();
6750
+ const memberWord = (count) => getEntityLabel("WORKSPACEUSER", count === 1 ? "SINGULAR" : "PLURAL");
6751
+ const workspaceWord2 = getEntityLabel("WORKSPACE").toLowerCase();
6752
+ const inWorkspace = `"${workspace.name}"`;
6753
+ const setRoles = async (rows2, role) => {
6754
+ const single = rows2.length === 1 ? memberName(rows2[0].user) : null;
6755
+ try {
6756
+ await updateRoles.mutateAsync({ userIds: rows2.map((r) => r.user.external_id), role });
6757
+ notify14(
6758
+ single ? `${single}'s role updated to ${formatRole(role)} in "${workspace.name}"` : `Updated ${rows2.length} users to ${formatRole(role)} in "${workspace.name}"`,
6759
+ "success"
6760
+ );
6761
+ return true;
6762
+ } catch {
6763
+ notify14(
6764
+ single ? `Failed to update ${single}'s role` : `Failed to update roles in "${workspace.name}"`,
6765
+ "error"
6766
+ );
6767
+ return false;
6768
+ }
6769
+ };
6770
+ const rows = useMemo(
6771
+ () => [...members].sort((a, b) => {
6772
+ if (a.user.external_id === selfId) return -1;
6773
+ if (b.user.external_id === selfId) return 1;
6774
+ return (ROLE_ORDER[a.role] ?? 3) - (ROLE_ORDER[b.role] ?? 3);
6775
+ }),
6776
+ [members, selfId]
6777
+ );
6778
+ const columnDefs = useMemo(
6779
+ () => [
6780
+ {
6781
+ colId: "name",
6782
+ headerName: "Name",
6783
+ flex: 1,
6784
+ minWidth: 150,
6785
+ sortable: true,
6786
+ valueGetter: (p) => p.data ? memberName(p.data.user) : "",
6787
+ cellRenderer: (p) => p.data ? /* @__PURE__ */ jsx(MemberNameCell, { children: renderMember(p.data) }) : null
6788
+ },
6789
+ {
6790
+ colId: "email",
6791
+ headerName: "Email",
6792
+ flex: 1,
6793
+ minWidth: 180,
6794
+ sortable: true,
6795
+ valueGetter: (p) => p.data?.user.email ?? "",
6796
+ // Plain text, not a mailto: the row's click selects it.
6797
+ cellRenderer: (p) => p.data?.user.email ? /* @__PURE__ */ jsx(MemberMutedText, { className: "truncate", title: p.data.user.email, children: p.data.user.email }) : null
6798
+ },
6799
+ {
6800
+ colId: "role",
6801
+ headerName: "Role",
6802
+ width: 145,
6803
+ minWidth: 130,
6804
+ sortable: true,
6805
+ valueGetter: (p) => p.data?.role ?? "",
6806
+ cellRenderer: (p) => {
6807
+ const row = p.data;
6808
+ if (!row) return null;
6809
+ const lock = roleLock(row.user.external_id, selfId, isOwner);
6810
+ return /* @__PURE__ */ jsx(
6811
+ MemberRoleCell,
6812
+ {
6813
+ role: row.role,
6814
+ roles: ALL_ROLES,
6815
+ tooltip: lock ?? getRoleDescription(row.role),
6816
+ canChange: !lock,
6817
+ isPending: updateRoles.isPending,
6818
+ onChange: (value) => void setRoles([row], value),
6819
+ temporaryHint: row.is_temporary ? "Temporary, session-scoped grant \u2014 expires with the session." : void 0
6820
+ }
6821
+ );
6822
+ }
6823
+ },
6824
+ {
6825
+ colId: "joined_at",
6826
+ headerName: "Joined",
6827
+ width: 145,
6828
+ minWidth: 125,
6829
+ sortable: true,
6830
+ valueGetter: (p) => p.data ? new Date(p.data.joined_at).getTime() : "",
6831
+ cellRenderer: (p) => p.data ? /* @__PURE__ */ jsx(MemberMutedText, { className: "cursor-default", children: formatFriendlyDate(p.data.joined_at) }) : null
6832
+ },
6833
+ {
6834
+ colId: "conversation_count",
6835
+ headerName: getEntityLabel("CONVERSATION", "PLURAL"),
6836
+ width: 95,
6837
+ minWidth: 85,
6838
+ sortable: true,
6839
+ type: "numericColumn",
6840
+ valueGetter: (p) => p.data?.conversation_count ?? 0,
6841
+ cellRenderer: (p) => p.data ? /* @__PURE__ */ jsx(MemberMutedText, { children: p.data.conversation_count }) : null
6842
+ },
6843
+ {
6844
+ colId: "document_count",
6845
+ headerName: "Docs",
6846
+ width: 90,
6847
+ minWidth: 80,
6848
+ sortable: true,
6849
+ type: "numericColumn",
6850
+ valueGetter: (p) => p.data?.document_count ?? 0,
6851
+ cellRenderer: (p) => p.data ? /* @__PURE__ */ jsx(MemberMutedText, { children: p.data.document_count }) : null
6852
+ },
6853
+ {
6854
+ colId: "files_mb",
6855
+ headerName: "Storage",
6856
+ width: 110,
6857
+ minWidth: 95,
6858
+ sortable: true,
6859
+ type: "numericColumn",
6860
+ valueGetter: (p) => p.data?.files_mb ?? 0,
6861
+ cellRenderer: (p) => {
6862
+ if (!p.data) return null;
6863
+ const mb = p.data.files_mb ?? 0;
6864
+ return /* @__PURE__ */ jsxs(MemberMutedText, { children: [
6865
+ mb >= 100 ? Math.round(mb).toLocaleString("en-US") : mb.toFixed(1),
6866
+ " MB"
6867
+ ] });
6868
+ }
6869
+ }
6870
+ ],
6871
+ // eslint-disable-next-line react-hooks/exhaustive-deps
6872
+ [selfId, isOwner, updateRoles.isPending, renderMember, workspace.name]
6873
+ );
6874
+ const removeSelected = async () => {
6875
+ const count = selected.length;
6876
+ if (count === 0) return;
6877
+ const names = count === 1 ? `"${memberName(selected[0].user)}"` : `${count} ${memberWord(count).toLowerCase()}`;
6878
+ const members2 = getEntityLabel("WORKSPACEUSER", "PLURAL").toLowerCase();
6879
+ const confirmed = await confirm({
6880
+ title: `Remove ${count} ${memberWord(count)}`,
6881
+ message: `Remove ${names} from ${inWorkspace}?
6882
+
6883
+ Note that ${members2} may have saved private content in the ${workspaceWord2} that you cannot see. Once removed they immediately lose access to any content in the ${workspaceWord2}.`
6884
+ });
6885
+ if (!confirmed) return;
6886
+ try {
6887
+ await remove.mutateAsync(selected.map((m) => m.user.external_id));
6888
+ setSelected([]);
6889
+ notify14(`Removed ${names} from ${inWorkspace}`, "success");
6890
+ } catch {
6891
+ notify14(`Failed to remove some users from ${inWorkspace}`, "error");
6892
+ }
6893
+ };
6894
+ const changeSelectedRoles = async (role) => {
6895
+ const changeable = selected.filter((m) => !roleLock(m.user.external_id, selfId, isOwner));
6896
+ if (changeable.length === 0) return;
6897
+ if (await setRoles(changeable, role)) setSelected([]);
6898
+ };
6899
+ const canRemoveSelected = selected.every(
6900
+ (m) => m.role !== "owner" && m.user.external_id !== selfId
6901
+ );
6902
+ const canChangeRoleSelected = selected.some((m) => !roleLock(m.user.external_id, selfId, isOwner));
6903
+ const ownerOnly = `Only available to ${workspaceWord2} owner`;
6904
+ if (isLoading) return /* @__PURE__ */ jsx(MemberListLoading, {});
6905
+ return /* @__PURE__ */ jsxs("div", { "data-testid": "workspace-users", className: "relative flex flex-col", children: [
6906
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsx(
6907
+ GridView,
6908
+ {
6909
+ items: rows,
6910
+ columnDefs,
6911
+ getItemId: (m) => m.user.external_id,
6912
+ selection: { mode: "multiple", selected, onSelectionChange: setSelected },
6913
+ persistState: !!onGridStateChange,
6914
+ gridId: `workspace-users-${workspace.external_id}-v6`,
6915
+ gridState,
6916
+ onGridStateChange,
6917
+ height: "300px",
6918
+ className: "w-full grid-sort-left"
6919
+ }
6920
+ ) }),
6921
+ !readOnly && /* @__PURE__ */ jsx(SelectionBar, { count: selected.length, onClear: () => setSelected([]), children: /* @__PURE__ */ jsx(
6922
+ MemberBulkActions,
6923
+ {
6924
+ roles: BULK_ROLES,
6925
+ onChangeRole: (value) => void changeSelectedRoles(value),
6926
+ isRoleDisabled: !isOwner || !canChangeRoleSelected || updateRoles.isPending,
6927
+ roleTooltip: !isOwner ? ownerOnly : selected.length === 0 ? "Select users to change their role" : !canChangeRoleSelected ? "You cannot change your own role" : "Change role for selected users",
6928
+ isOwner,
6929
+ onRemove: () => void removeSelected(),
6930
+ isRemoveDisabled: !isOwner || !canRemoveSelected || remove.isPending,
6931
+ removeTooltip: !isOwner ? ownerOnly : "Remove selected from workspace"
6932
+ }
6933
+ ) })
6934
+ ] });
6935
+ }
6717
6936
  var isAgentMember = (member) => member.user.external_id.startsWith("agt-");
6718
- var memberName = (member) => member.user.family_name ? `${member.user.given_name} ${member.user.family_name}` : member.user.given_name;
6937
+ var memberName2 = (member) => member.user.family_name ? `${member.user.given_name} ${member.user.family_name}` : member.user.given_name;
6719
6938
  function ConnectedWorkspaceSettings({
6720
6939
  workspaceId,
6721
6940
  project,
6722
6941
  projectLine,
6723
- membersTable,
6942
+ renderMember,
6943
+ membersGridState,
6944
+ onMembersGridStateChange,
6724
6945
  shareDialog,
6725
6946
  children,
6726
6947
  onExport,
@@ -6730,6 +6951,7 @@ function ConnectedWorkspaceSettings({
6730
6951
  }) {
6731
6952
  const settings = useConnectedWorkspaceSettings(workspaceId, { onLeft });
6732
6953
  const { workspace, members, isOwner, isMember } = settings;
6954
+ const { userId } = useConnectedWorkspace();
6733
6955
  const { data: userSettings } = useUserSettings();
6734
6956
  const isDeveloper = userSettings?.developer === true;
6735
6957
  const showAgents = userSettings?.show_agent_sessions ?? false;
@@ -6817,7 +7039,7 @@ function ConnectedWorkspaceSettings({
6817
7039
  MemberAvatarStack,
6818
7040
  {
6819
7041
  members: visibleMembers.map((m) => ({
6820
- name: memberName(m),
7042
+ name: memberName2(m),
6821
7043
  picture: m.user.picture
6822
7044
  })),
6823
7045
  total: visibleMembers.length,
@@ -6838,17 +7060,23 @@ function ConnectedWorkspaceSettings({
6838
7060
  ]
6839
7061
  }
6840
7062
  ),
6841
- isMember && membersTable && /* @__PURE__ */ jsx(
7063
+ isMember && /* @__PURE__ */ jsx(
6842
7064
  ExpandableSection,
6843
7065
  {
6844
7066
  expanded: membersExpanded,
6845
7067
  testId: "workspace-settings-members-section",
6846
- children: membersTable({
6847
- workspace,
6848
- members: visibleMembers,
6849
- onRemove: settings.removeMember,
6850
- isRemoving: settings.isRemoving
6851
- })
7068
+ children: /* @__PURE__ */ jsx(
7069
+ ConnectedWorkspaceMembers,
7070
+ {
7071
+ workspace,
7072
+ members: visibleMembers,
7073
+ selfId: userId,
7074
+ isOwner,
7075
+ renderMember,
7076
+ gridState: membersGridState,
7077
+ onGridStateChange: onMembersGridStateChange
7078
+ }
7079
+ )
6852
7080
  }
6853
7081
  )
6854
7082
  ] }),
@@ -7516,20 +7744,6 @@ function ConnectedDriveSyncDialog({
7516
7744
  }
7517
7745
  );
7518
7746
  }
7519
- var BillingSupportContext = createContext({ stripeEnabled: false });
7520
- function BillingSupportProvider({
7521
- value,
7522
- children
7523
- }) {
7524
- return /* @__PURE__ */ jsx(BillingSupportContext.Provider, { value, children });
7525
- }
7526
- function useBillingSupport() {
7527
- return useContext(BillingSupportContext);
7528
- }
7529
- function useSelfExtId() {
7530
- const arbi = useArbi();
7531
- return arbi.isLoggedIn ? arbi.user.identity()?.externalId ?? void 0 : void 0;
7532
- }
7533
7747
  var focus = null;
7534
7748
  var listeners = /* @__PURE__ */ new Set();
7535
7749
  function requestProjectBillingFocus(next) {
@@ -7549,363 +7763,8 @@ function BillingProjectEntity({
7549
7763
  ...props
7550
7764
  }) {
7551
7765
  const { stripeEnabled } = useBillingSupport();
7552
- const isPaid = stripeEnabled && (subscription === "active" || subscription === "past_due" || subscription === "dev");
7553
- return /* @__PURE__ */ jsx(ProjectEntity, { ...props, subscription, isPaid });
7554
- }
7555
-
7556
- // src/connection/billing/plan-constants.ts
7557
- var PRO_AI_GBP = 10;
7558
- var PRO_STORAGE_GB = 5;
7559
- var ADDON_BLOCK_AI_GBP = 25;
7560
- var STORAGE_BLOCK_GB = 25;
7561
- var DEFAULT_SPEND_CAP_PENCE = 1e4;
7562
- var MIN_AUTO_TOPUP_THRESHOLD_GBP = 1;
7563
- var FREE_AI_GBP = 1;
7564
- var FREE_STORAGE_MB = 50;
7565
- var UNLIMITED_COLLABORATORS = 999;
7566
- var SUGGESTED_DAILY_CAP_PER_USER = 10;
7567
-
7568
- // src/connection/billing/quota-items.ts
7569
- var fmt = (v) => Math.round(v).toLocaleString("en-US");
7570
- function formatResetDate(unixTimestamp) {
7571
- if (!unixTimestamp) return null;
7572
- const date = new Date(unixTimestamp * 1e3);
7573
- return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
7574
- }
7575
- function aiUsageResetTimestamp(project) {
7576
- const periodEnd = project.plan_interval === "year" ? null : project.current_period_end;
7577
- const ts = project.addons?.ai_usage_reset_at ?? periodEnd ?? project.quotas?.budget_reset_at ?? null;
7578
- return ts && ts * 1e3 > Date.now() ? ts : null;
7579
- }
7580
- var quota = (q) => ({
7581
- used: q?.used ?? 0,
7582
- limit: q?.limit ?? 0
7583
- });
7584
- function storageText(gbValue, limitGb, unit = true) {
7585
- if (limitGb >= 1) {
7586
- const n = Math.round(gbValue * 10) / 10;
7587
- return `${n.toLocaleString("en-US")}${unit ? " GB" : ""}`;
7588
- }
7589
- return `${fmt(gbValue * 1e3)}${unit ? " MB" : ""}`;
7590
- }
7591
- var pctOf = (used, limit) => limit > 0 ? used / limit * 100 : 0;
7592
- function getQuotaItems(quotas) {
7593
- const aiUsage = quota(quotas?.ai_usage);
7594
- const storage = quota(quotas?.storage_gb);
7595
- const collaborators = quota(quotas?.collaborators);
7596
- const unlimitedCollaborators = collaborators.limit >= UNLIMITED_COLLABORATORS;
7597
- return [
7598
- {
7599
- key: "ai-usage",
7600
- icon: PoundSterling,
7601
- label: "AI usage",
7602
- used: formatGbp(aiUsage.used),
7603
- usedOfLimit: `${formatGbp(aiUsage.used)} / ${formatGbp(aiUsage.limit)}`,
7604
- pct: pctOf(aiUsage.used, aiUsage.limit)
7605
- },
7606
- {
7607
- key: "storage",
7608
- icon: HardDrive,
7609
- label: "Storage",
7610
- used: storageText(storage.used, storage.limit),
7611
- usedOfLimit: `${storageText(storage.used, storage.limit, false)} / ${storageText(
7612
- storage.limit,
7613
- storage.limit
7614
- )}`,
7615
- pct: pctOf(storage.used, storage.limit)
7616
- },
7617
- {
7618
- key: "collaborators",
7619
- icon: Users,
7620
- label: "Collaborators",
7621
- used: fmt(collaborators.used),
7622
- usedOfLimit: unlimitedCollaborators ? `${fmt(collaborators.used)} / Unlimited` : `${fmt(collaborators.used)} / ${fmt(collaborators.limit)}`,
7623
- // Zero seats (Free) IS the cap: 0 / 0 shows a full destructive bar,
7624
- // because you can't add anyone — the truth the upgrade door answers.
7625
- // (Only when the quota is actually present; a missing field on a
7626
- // partial payload stays an empty bar, not a false alarm.)
7627
- pct: unlimitedCollaborators ? 0 : quotas?.collaborators && collaborators.limit === 0 ? 100 : pctOf(collaborators.used, collaborators.limit)
7628
- }
7629
- ];
7630
- }
7631
- function useRewardBalance() {
7632
- const { data } = useUserSettings();
7633
- const ed = data?.extra_discount;
7634
- if (!ed || !ed.amount || ed.amount <= 0) return null;
7635
- return { amount: ed.amount, currency: ed.currency || "gbp" };
7636
- }
7637
- function formatMoney(cents, currency = "gbp") {
7638
- const value = cents / 100;
7639
- const isWhole = Number.isInteger(value);
7640
- const c = currency.toLowerCase();
7641
- const symbol = c === "usd" ? "$" : c === "gbp" ? "\xA3" : c === "eur" ? "\u20AC" : currency.toUpperCase() + " ";
7642
- return `${symbol}${value.toLocaleString("en-US", {
7643
- minimumFractionDigits: isWhole ? 0 : 2,
7644
- maximumFractionDigits: isWhole ? 0 : 2
7645
- })}`;
7646
- }
7647
- var gbp = (pence) => formatMoney(pence, "gbp");
7648
- function formatPeriodEnd(unix) {
7649
- if (!unix) return null;
7650
- return new Date(unix * 1e3).toLocaleDateString("en-GB", {
7651
- day: "numeric",
7652
- month: "short",
7653
- year: "numeric"
7654
- });
7655
- }
7656
- function addonPricePence(kind, addons) {
7657
- return kind === "ai_usage" ? addons.ai_block_price_pence : addons.storage_block_price_pence;
7658
- }
7659
- function addonPriceExVatPence(kind, addons) {
7660
- return kind === "ai_usage" ? addons.ai_block_price_ex_vat_pence : addons.storage_block_price_ex_vat_pence;
7661
- }
7662
- function addonSettingsBody(addons, change = {}) {
7663
- return {
7664
- spend_cap_pence: addons.spend_cap_pence ?? null,
7665
- auto_topup_threshold_gbp: addons.auto_topup_threshold_gbp,
7666
- ...change
7667
- };
7668
- }
7669
- function splitBlocks(kind, count, addons) {
7670
- const granted = (kind === "ai_usage" ? addons.legacy_ai_blocks : addons.legacy_storage_blocks) ?? 0;
7671
- const legacy = Math.max(0, Math.min(granted, count));
7672
- return { legacy, current: count - legacy };
7673
- }
7674
- function blockSize(kind, addons, legacy) {
7675
- if (kind === "ai_usage") {
7676
- return formatGbp(legacy ? addons.legacy_block_ai_gbp ?? 10 : addons.block_ai_gbp);
7677
- }
7678
- return `${legacy ? addons.legacy_block_storage_gb ?? 10 : addons.block_storage_gb} GB`;
7679
- }
7680
- function blocksLabel(kind, count, addons) {
7681
- const { legacy, current } = splitBlocks(kind, count, addons);
7682
- const parts = [
7683
- ...legacy > 0 ? [`${legacy} \xD7 ${blockSize(kind, addons, true)}`] : [],
7684
- ...current > 0 || legacy === 0 ? [`${current} \xD7 ${blockSize(kind, addons, false)}`] : []
7685
- ];
7686
- return `${parts.join(" + ")}${kind === "ai_usage" ? " of AI usage" : ""}`;
7687
- }
7688
- function reduceBounds(kind, addons) {
7689
- const current = kind === "ai_usage" ? addons.ai_blocks : addons.storage_blocks;
7690
- const pending = kind === "ai_usage" ? addons.ai_blocks_next : addons.storage_blocks_next;
7691
- const min = kind === "ai_usage" ? 0 : Math.min(addons.storage_blocks_min, current);
7692
- return { min, current, next: pending ?? current };
7693
- }
7694
- function blockMonthlyExVatPence(kind, addons) {
7695
- return kind === "ai_usage" ? addons.ai_block_monthly_ex_vat_pence : addons.storage_block_monthly_ex_vat_pence;
7696
- }
7697
- function blockPeriodExVatPence(kind, addons) {
7698
- return kind === "ai_usage" ? addons.ai_block_period_ex_vat_pence : addons.storage_block_period_ex_vat_pence;
7699
- }
7700
- function blockRenewalShort(kind, addons, isAnnual) {
7701
- return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? "year" : "month"} + VAT`;
7702
- }
7703
- var ANNUAL_PREMIUM_AI_NOTE = "No markup on Premium AI on annual plans";
7704
- function addonCharge(kind, addons, isAnnual, periodEnd) {
7705
- return {
7706
- now: gbp(addonPricePence(kind, addons)),
7707
- nowExVat: gbp(addonPriceExVatPence(kind, addons)),
7708
- nowVat: gbp(addonPricePence(kind, addons) - addonPriceExVatPence(kind, addons)),
7709
- until: isAnnual ? formatPeriodEnd(periodEnd) : null,
7710
- then: blockRenewalShort(kind, addons, isAnnual)
7711
- };
7712
- }
7713
- var CAP_REACHED_MESSAGE = "Usage cap reached: no more automatic top-ups this month.";
7714
- var ZERO_BUDGET_MESSAGE = "Automatic top-ups are off (\xA30 usage cap).";
7715
- var USAGE_CAP_LABEL = "Usage cap";
7716
- var USAGE_CAP_HELP = "The most automatic top-ups (AI usage added when it runs low, storage added when an upload needs it) may cost in a month. Adding or removing by hand is never limited by it; the plan and renewals are not counted. \xA30 = no automatic top-ups: AI usage and uploads stop at your allowance unless you add more. No limit = no cap.";
7717
- function isAddonBudgetZero(addons) {
7718
- return addons.spend_cap_pence === 0;
7719
- }
7720
- function autoRoom(kind, addons) {
7721
- const left = addons.cap_left_pence;
7722
- if (left == null) return addons.spend_cap_pence === 0 ? 0 : null;
7723
- const price = addonPriceExVatPence(kind, addons);
7724
- if (price <= 0) return 0;
7725
- const blocks = Math.floor(left / price);
7726
- return blocks * (kind === "ai_usage" ? addons.block_ai_gbp : addons.block_storage_gb);
7727
- }
7728
- function nextRemovedBlockSize(kind, addons) {
7729
- const { next } = reduceBounds(kind, addons);
7730
- const { current } = splitBlocks(kind, next, addons);
7731
- if (kind === "ai_usage") {
7732
- return current > 0 ? addons.block_ai_gbp : addons.legacy_block_ai_gbp ?? 10;
7733
- }
7734
- return current > 0 ? addons.block_storage_gb : addons.legacy_block_storage_gb ?? 10;
7735
- }
7736
- function parseBudgetPounds(value) {
7737
- if (value.trim() === "") return null;
7738
- const n = Number(value);
7739
- if (!Number.isFinite(n) || n < 0) return void 0;
7740
- return Math.round(n * 100);
7741
- }
7742
- function budgetSavedMessage(capPence) {
7743
- if (capPence == null) return "Usage cap: no limit";
7744
- if (capPence === 0) return "Usage cap: \xA30 (no automatic top-ups)";
7745
- return `Usage cap: ${gbp(capPence)} a month`;
7746
- }
7747
- var gbText = (n) => `${Math.round(n * 10) / 10} GB`;
7748
- function stepLabel(kind, size) {
7749
- return kind === "ai_usage" ? `${formatGbp(size)} of AI usage` : gbText(size);
7750
- }
7751
- function nextPeriodBody(kind, keep) {
7752
- return kind === "ai_usage" ? { ai_blocks: keep, storage_blocks: null } : { ai_blocks: null, storage_blocks: keep };
7753
- }
7754
- function addStepCaption(kind, addons) {
7755
- const now = gbp(addonPriceExVatPence(kind, addons));
7756
- const today = kind === "storage" ? `prorated today ${now}` : `today ${now}`;
7757
- return `${gbp(blockMonthlyExVatPence(kind, addons))}/month \xB7 ${today}`;
7758
- }
7759
-
7760
- // src/connection/billing/api-error.ts
7761
- function apiErrorDetail(error) {
7762
- const body = error?.apiError ?? error;
7763
- return body?.detail;
7764
- }
7765
-
7766
- // src/connection/billing/tax-quote.ts
7767
- var POSTCODE_COUNTRIES = ["US", "CA"];
7768
- var TAX_LOCATION_INVALID = "TAX_LOCATION_INVALID";
7769
- function money2(pence, currency = "gbp") {
7770
- const symbol = { gbp: "\xA3", usd: "$", eur: "\u20AC" }[currency.toLowerCase()] ?? `${currency} `;
7771
- return `${symbol}${(pence / 100).toLocaleString("en-GB", {
7772
- minimumFractionDigits: 2,
7773
- maximumFractionDigits: 2
7774
- })}`;
7775
- }
7776
- function taxKind(label) {
7777
- return /^vat\b/i.test(label ?? "") ? "VAT" : "tax";
7778
- }
7779
- function taxDetail({ subtotal, tax, label, reverseCharge }) {
7780
- if (reverseCharge) return "\xB7 no VAT (reverse charge)";
7781
- if (tax <= 0) return "\xB7 no tax";
7782
- return `incl. ${taxKind(label)} (${money2(subtotal)} + ${money2(tax)} ${label ?? "tax"})`;
7783
- }
7784
- function taxSummary(parts) {
7785
- return `${money2(parts.total)} ${taxDetail(parts)}`;
7786
- }
7787
- function totalWithTaxWord({ total, tax, label }) {
7788
- return tax > 0 ? `${money2(total)} incl. ${taxKind(label)}` : money2(total);
7789
- }
7790
- function quoteParts(quote) {
7791
- return {
7792
- subtotal: quote.subtotal_pence,
7793
- tax: quote.tax_pence,
7794
- total: quote.total_pence,
7795
- label: quote.tax_label,
7796
- reverseCharge: quote.reverse_charge
7797
- };
7798
- }
7799
- function quoteTotalLine(quote) {
7800
- return `Total today ${taxSummary(quoteParts(quote))}`;
7801
- }
7802
- function quoteError(error) {
7803
- const detail = apiErrorDetail(error);
7804
- if (detail && typeof detail === "object") {
7805
- const { code, message } = detail;
7806
- return {
7807
- code: typeof code === "string" ? code : void 0,
7808
- message: typeof message === "string" ? message : void 0
7809
- };
7810
- }
7811
- return { message: typeof detail === "string" ? detail : void 0 };
7812
- }
7813
-
7814
- // src/connection/billing/plan-pricing.ts
7815
- var gbp2 = (pence) => formatMoney(pence, "gbp");
7816
- function plusVat(exVatPence, currency = "gbp") {
7817
- return `${formatMoney(exVatPence, currency)} + VAT`;
7818
- }
7819
- var PRO_PACKS = 1;
7820
- var PLAN_CHOICE_PACKS = {
7821
- pro: PRO_PACKS
7822
- };
7823
- function planNameForPacks(packs) {
7824
- const n = packs ?? 0;
7825
- if (n <= 0) return "Free";
7826
- if (n === PRO_PACKS) return "Pro";
7827
- return `Pro \xD7${n}`;
7828
- }
7829
- function planPreviewLines(preview) {
7830
- if (preview.unchanged) return [{ id: "no-change", text: "No change" }];
7831
- const date = formatPeriodEnd(preview.next_renewal_at);
7832
- const cadence = preview.interval === "year" ? "year" : "month";
7833
- const lines = [
7834
- preview.awaiting_confirmation ? { id: "awaiting-bank", text: "Waiting for your bank" } : {
7835
- id: "pay-today",
7836
- text: preview.effective_now ? `Pay today ${taxSummary({
7837
- subtotal: preview.due_now_ex_vat_pence,
7838
- tax: preview.due_now_tax_pence,
7839
- total: preview.due_now_pence,
7840
- label: preview.tax_label
7841
- })}` : `Pay today ${gbp2(0)}`
7842
- },
7843
- {
7844
- id: "from",
7845
- text: `${date ? `From ${date}` : "From renewal"}: ${taxSummary({
7846
- subtotal: preview.next_renewal_ex_vat_pence,
7847
- tax: preview.next_renewal_tax_pence,
7848
- total: preview.next_renewal_pence,
7849
- label: preview.tax_label
7850
- })} a ${cadence}`
7851
- }
7852
- ];
7853
- if (!preview.effective_now) {
7854
- lines.push({ id: "current-until", text: `Current plan until ${date ?? "renewal"}` });
7855
- }
7856
- return lines;
7857
- }
7858
- var PAY_TODAY_UNPRICED = "Pay today: the difference for the rest of this period";
7859
- function addonPrice(price, kind = "ai_usage") {
7860
- return price?.addon_prices?.find((p) => p.kind === kind);
7861
- }
7862
- function checkoutItemsLabel(aiBlocks, storageBlocks) {
7863
- return [
7864
- "Pro",
7865
- aiBlocks > 0 && `${aiBlocks} \xD7 ${formatGbp(ADDON_BLOCK_AI_GBP)} of AI usage`,
7866
- storageBlocks > 0 && `${storageBlocks} \xD7 ${STORAGE_BLOCK_GB} GB`
7867
- ].filter(Boolean).join(" + ");
7868
- }
7869
- function packPrice(price, packs = PRO_PACKS) {
7870
- return price?.pack_prices?.find((p) => p.packs === packs);
7871
- }
7872
- function findProPrices(products) {
7873
- const prices = products?.[0]?.prices ?? [];
7874
- const pick = (interval) => prices.find((p) => p.interval === interval && p.default_price) ?? prices.find((p) => p.interval === interval);
7875
- return { monthly: pick("month"), annual: pick("year") };
7876
- }
7877
- function calcSavingsPercent(monthlyPrice, annualPrice) {
7878
- const monthly = packPrice(monthlyPrice)?.amount ?? monthlyPrice?.amount;
7879
- const annual = packPrice(annualPrice)?.amount ?? annualPrice?.amount;
7880
- if (monthly == null || annual == null || monthly === 0) return null;
7881
- const savings = Math.round((monthly * 12 - annual) / (monthly * 12) * 100);
7882
- return savings > 0 ? savings : null;
7883
- }
7884
- var MAX_CHECKOUT_BLOCKS = 100;
7885
- function checkoutBlockPriceText(price, kind) {
7886
- const block = addonPrice(price, kind);
7887
- if (!block) return null;
7888
- const currency = price?.currency ?? "gbp";
7889
- return price?.interval === "year" ? `${formatMoney(block.amount_ex_vat, currency)}/year + VAT each` : `${formatMoney(block.amount_ex_vat, currency)}/month + VAT each`;
7890
- }
7891
-
7892
- // src/connection/billing/plan-badge.ts
7893
- function isProProject(project) {
7894
- return project.plan_interval != null;
7895
- }
7896
- function proTierName(project) {
7897
- return project.addons?.plan_name || planNameForPacks(Math.max(project.packs ?? 1, 1));
7898
- }
7899
- function planName(project) {
7900
- if (project.subscription === "dev") return "Dev";
7901
- if (!isProProject(project)) return "Free";
7902
- return `${proTierName(project)} ${project.plan_interval === "year" ? "annual" : "monthly"}`;
7903
- }
7904
- function planBadgeView(project) {
7905
- const { subscription } = project;
7906
- const label = subscription === "canceled" ? "Cancelling" : subscription === "dev" ? "Dev" : isProProject(project) ? proTierName(project) : "Free";
7907
- const tone = subscription === "dev" ? "dev" : subscription === "canceled" || subscription === "past_due" ? "pending" : isProProject(project) ? "paid" : "free";
7908
- return { label, tone };
7766
+ const isPaid2 = stripeEnabled && (subscription === "active" || subscription === "past_due" || subscription === "dev");
7767
+ return /* @__PURE__ */ jsx(ProjectEntity, { ...props, subscription, isPaid: isPaid2 });
7909
7768
  }
7910
7769
  function formatInvoiceDate(iso) {
7911
7770
  const d = /* @__PURE__ */ new Date(`${iso}T00:00:00`);
@@ -7925,7 +7784,7 @@ function InvoicesModal({
7925
7784
  const { data, isLoading } = useProjectInvoices(open ? projectExtId : void 0);
7926
7785
  const invoices = data?.invoices;
7927
7786
  const invoiceViews = (invoices ?? []).map((inv, i) => {
7928
- const isPaid = inv.status === "paid";
7787
+ const isPaid2 = inv.status === "paid";
7929
7788
  return {
7930
7789
  key: inv.number ?? String(i),
7931
7790
  date: formatInvoiceDate(inv.date),
@@ -7933,9 +7792,9 @@ function InvoicesModal({
7933
7792
  amount: formatMoney(inv.amount, inv.currency),
7934
7793
  amountNote: inv.amount_ex_vat != null && inv.amount_ex_vat !== inv.amount ? `${formatMoney(inv.amount_ex_vat, inv.currency)} ex VAT` : void 0,
7935
7794
  status: inv.status,
7936
- isPaid,
7795
+ isPaid: isPaid2,
7937
7796
  href: inv.hosted_invoice_url ?? inv.url ?? inv.pdf_url ?? void 0,
7938
- linkLabel: isPaid ? "Receipt" : "Invoice"
7797
+ linkLabel: isPaid2 ? "Receipt" : "Invoice"
7939
7798
  };
7940
7799
  });
7941
7800
  return /* @__PURE__ */ jsx(
@@ -8134,7 +7993,7 @@ function buildPlanCards(selected, isAnnual, prices, isOnFree) {
8134
7993
  features: [
8135
7994
  `${formatGbp(FREE_AI_GBP)} of AI usage / month`,
8136
7995
  `${FREE_STORAGE_MB.toLocaleString("en-US")} MB storage`,
8137
- "Just you"
7996
+ FREE_COLLABORATORS === 0 ? "Just you" : `${FREE_COLLABORATORS} collaborators`
8138
7997
  ]
8139
7998
  };
8140
7999
  return [free, proCard(selected, isAnnual, prices)];
@@ -9349,7 +9208,7 @@ function ReduceAddonDialog({
9349
9208
  }
9350
9209
  ) });
9351
9210
  }
9352
- var gbp3 = (pence) => formatMoney(pence, "gbp");
9211
+ var gbp = (pence) => formatMoney(pence, "gbp");
9353
9212
  var gb2 = (n) => `${Math.round(n * 10) / 10} GB`;
9354
9213
  var pct = (used, limit) => limit > 0 ? Math.min(100, used / limit * 100) : 0;
9355
9214
  function formatShortDate(unix) {
@@ -9475,7 +9334,7 @@ function ResourceMeter({
9475
9334
  const from = periodEnd ?? "your next renewal";
9476
9335
  const fromShort = formatShortDate(addons.period_end ?? project.current_period_end) ?? "renewal";
9477
9336
  const { min, current, next } = reduceBounds(kind, addons);
9478
- const fmt2 = (n) => isAi ? formatGbp(n) : gb2(n);
9337
+ const fmt = (n) => isAi ? formatGbp(n) : gb2(n);
9479
9338
  const used = isAi ? addons.ai_used_gbp : addons.storage_used_gb;
9480
9339
  const allowance = isAi ? addons.ai_allowance_gbp : addons.storage_allowance_gb;
9481
9340
  const allowanceNext = isAi ? addons.ai_allowance_next_gbp : addons.storage_allowance_next_gb;
@@ -9503,12 +9362,12 @@ function ResourceMeter({
9503
9362
  {
9504
9363
  label: isAi ? "AI usage" : "Storage",
9505
9364
  hint: resets ? `resets ${resets}` : void 0,
9506
- value: `${fmt2(used)} of ${fmt2(allowance)}`,
9365
+ value: `${fmt(used)} of ${fmt(allowance)}`,
9507
9366
  allowance: { used, plan, topUps, autoRoom: capZero ? 0 : room },
9508
9367
  legend: {
9509
- plan: `Plan ${fmt2(plan)}`,
9510
- topUps: topUps > 0 ? `Top-ups ${fmt2(topUps)}${hasLegacy ? ` (${blocksLabel(kind, blocks, addons)})` : ""}` : void 0,
9511
- autoRoom: room != null && room > 0 && !capZero ? `Automatic up to ${fmt2(room)}` : void 0
9368
+ plan: `Plan ${fmt(plan)}`,
9369
+ topUps: topUps > 0 ? `Top-ups ${fmt(topUps)}${hasLegacy ? ` (${blocksLabel(kind, blocks, addons)})` : ""}` : void 0,
9370
+ autoRoom: room != null && room > 0 && !capZero ? `Automatic up to ${fmt(room)}` : void 0
9512
9371
  },
9513
9372
  testId: `usage-meter-${slug}`,
9514
9373
  details: autoLine || allowanceNext !== allowance || children ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -9596,7 +9455,7 @@ function UsageCapEditor({ project, addons }) {
9596
9455
  }
9597
9456
  );
9598
9457
  };
9599
- const readout = savedCap == null ? `${gbp3(spent)} this month \xB7 no limit` : savedCap === 0 ? "\xA30 \xB7 no automatic top-ups" : `${gbp3(spent)} of ${gbp3(savedCap)} used`;
9458
+ const readout = savedCap == null ? `${gbp(spent)} this month \xB7 no limit` : savedCap === 0 ? "\xA30 \xB7 no automatic top-ups" : `${gbp(spent)} of ${gbp(savedCap)} used`;
9600
9459
  const inputId = `spend-cap-${project.external_id}`;
9601
9460
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3", ref: sectionRef, "data-testid": "project-spend-cap", children: [
9602
9461
  /* @__PURE__ */ jsx(
@@ -9612,7 +9471,7 @@ function UsageCapEditor({ project, addons }) {
9612
9471
  addons.cap_reached && savedCap != null && savedCap > 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", "data-testid": "addon-cap-reached", children: CAP_REACHED_MESSAGE }),
9613
9472
  addons.manual_spent_pence > 0 && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", "data-testid": "addon-manual-spend", children: [
9614
9473
  "Added by hand this month: ",
9615
- gbp3(addons.manual_spent_pence),
9474
+ gbp(addons.manual_spent_pence),
9616
9475
  " \xB7 not capped"
9617
9476
  ] }),
9618
9477
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-3", children: [
@@ -9788,7 +9647,7 @@ function formatShortDate2(unix) {
9788
9647
  if (!unix) return null;
9789
9648
  return new Date(unix * 1e3).toLocaleDateString("en-GB", { day: "numeric", month: "short" });
9790
9649
  }
9791
- var gbp4 = (pence) => formatMoney(pence, "gbp");
9650
+ var gbp2 = (pence) => formatMoney(pence, "gbp");
9792
9651
  var gbNum = (n) => `${Math.round(n * 10) / 10}`;
9793
9652
  var pct2 = (used, limit) => limit > 0 ? Math.min(100, used / limit * 100) : 0;
9794
9653
  function Meter({
@@ -9819,7 +9678,7 @@ function usePlanPrice(project) {
9819
9678
  }
9820
9679
  function paidLine(payment, addons) {
9821
9680
  const what = payment.kind === "storage" ? `${addons.block_storage_gb} GB added` : payment.kind === "ai_usage" ? `${formatGbp(addons.block_ai_gbp)} of AI usage added` : null;
9822
- const paid = payment.amount_pence != null ? `Paid ${gbp4(payment.amount_pence)}` : "Paid";
9681
+ const paid = payment.amount_pence != null ? `Paid ${gbp2(payment.amount_pence)}` : "Paid";
9823
9682
  return what ? `${paid} \xB7 ${what}` : paid;
9824
9683
  }
9825
9684
  function ProjectPlanCard({
@@ -9890,11 +9749,11 @@ function ProjectPlanCard({
9890
9749
  ] }),
9891
9750
  price && /* @__PURE__ */ jsxs("div", { className: "ml-auto text-right", "data-testid": "project-plan-price", children: [
9892
9751
  /* @__PURE__ */ jsxs("div", { className: "text-lg font-semibold tabular-nums text-foreground", children: [
9893
- price.ex != null ? plusVat(price.ex) : gbp4(price.incl),
9752
+ price.ex != null ? plusVat(price.ex) : gbp2(price.incl),
9894
9753
  /* @__PURE__ */ jsx("span", { className: "text-sm font-normal text-muted-foreground", children: price.isAnnual ? "/year" : "/month" })
9895
9754
  ] }),
9896
9755
  price.ex != null && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
9897
- gbp4(price.incl),
9756
+ gbp2(price.incl),
9898
9757
  " incl. VAT"
9899
9758
  ] })
9900
9759
  ] })
@@ -9976,7 +9835,7 @@ function ProjectPlanCard({
9976
9835
  addons?.billing_error && /* @__PURE__ */ jsx(PlanNotice, { tone: "error", testId: "plan-billing-error", children: addons.billing_error }),
9977
9836
  confirmUrl ? /* @__PURE__ */ jsxs(PlanNotice, { testId: "plan-payment-state", children: [
9978
9837
  "Waiting for your bank",
9979
- pendingPayment?.amount_pence ? ` \xB7 ${gbp4(pendingPayment.amount_pence)}` : "",
9838
+ pendingPayment?.amount_pence ? ` \xB7 ${gbp2(pendingPayment.amount_pence)}` : "",
9980
9839
  " \xB7",
9981
9840
  " ",
9982
9841
  /* @__PURE__ */ jsxs(
@@ -10646,61 +10505,768 @@ function useAutoRefreshPendingProjects() {
10646
10505
  }
10647
10506
  }, [projects, refreshProject, currentUserExtId]);
10648
10507
  }
10649
- var DEFAULT_SEARCH_ROW_LABELS = {
10650
- meaningMatch: "Matched by meaning",
10651
- meaningMatchTitle: "None of your search words appear in this passage. It matched by meaning.",
10652
- page: (page) => `p.${page}`,
10653
- passage: "Passage"
10654
- };
10655
- function useIsOpenPassage(chunkExtId) {
10656
- return useArbiSessionState((s) => !!chunkExtId && s.viewingDocument?.passageId === chunkExtId);
10657
- }
10658
- function MeaningMatchBadge({
10659
- content,
10660
- terms,
10661
- labels
10662
- }) {
10663
- return isMeaningMatch(content, terms) ? /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-muted-foreground", title: labels.meaningMatchTitle, children: labels.meaningMatch }) : null;
10508
+ function toWorkspaceSummary(workspace, selfId) {
10509
+ const people = (workspace.users ?? []).filter((m) => !isAgentAccount(m.user));
10510
+ const ordered = [
10511
+ ...people.filter((m) => m.role === "owner"),
10512
+ ...people.filter((m) => m.role !== "owner")
10513
+ ];
10514
+ const members = ordered.map((m) => ({
10515
+ name: m.user.family_name ? `${m.user.given_name} ${m.user.family_name}` : m.user.given_name,
10516
+ picture: m.user.picture
10517
+ }));
10518
+ const isMember = people.some((m) => m.user.external_id === selfId);
10519
+ return {
10520
+ id: workspace.external_id,
10521
+ name: workspace.name,
10522
+ description: workspace.description,
10523
+ docCount: workspaceDocumentCount(workspace),
10524
+ chatCount: workspaceConversationCount(workspace),
10525
+ memberCount: members.length,
10526
+ members,
10527
+ // A public workspace's members are hidden from someone who has not joined it.
10528
+ membersHidden: workspace.is_public && !isMember,
10529
+ updatedAt: workspace.updated_at
10530
+ };
10664
10531
  }
10665
- var FlatResultRow = memo(function FlatResultRow2({
10666
- hit,
10667
- position,
10668
- terms,
10669
- onOpen,
10670
- labels
10532
+ function WorkspaceCardsView({
10533
+ list,
10534
+ currentWorkspaceId,
10535
+ onOpenWorkspace,
10536
+ onOpenProject,
10537
+ onCreateWorkspace,
10538
+ createDisabledReason
10671
10539
  }) {
10672
- const handleClick = useCallback((e) => onOpen(e, hit), [onOpen, hit]);
10673
- return /* @__PURE__ */ jsx(
10674
- SearchHit,
10675
- {
10676
- active: useIsOpenPassage(hit.chunkExtId),
10677
- header: (
10678
- // No `onClick` on the pill: it would render a real <button> inside the card's
10679
- // role="button" target (invalid nesting). The card already opens the passage.
10680
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
10681
- /* @__PURE__ */ jsx(
10682
- PassagePill,
10540
+ const { stripeEnabled } = useBillingSupport();
10541
+ const workspaceLabel = getEntityLabel("WORKSPACE");
10542
+ const projectLabel = getEntityLabel("PROJECT").toLowerCase();
10543
+ const { groups, projects, selfId } = list;
10544
+ if (groups.length === 0) {
10545
+ return /* @__PURE__ */ jsx(WorkspaceList, { testId: "workspace-cards-list", className: "gap-0", children: /* @__PURE__ */ jsx(
10546
+ CreateTile,
10547
+ {
10548
+ label: `New ${workspaceLabel.toLowerCase()}`,
10549
+ ariaLabel: `Create ${workspaceLabel}`,
10550
+ onSelect: () => onCreateWorkspace(),
10551
+ disabledReason: createDisabledReason,
10552
+ help: `A ${workspaceLabel.toLowerCase()} holds the documents and conversations for one matter or case.`,
10553
+ testId: "create-workspace-card"
10554
+ }
10555
+ ) });
10556
+ }
10557
+ return /* @__PURE__ */ jsx(WorkspaceList, { testId: "workspace-cards-list", children: groups.map((group) => {
10558
+ const project = projects.find((p) => p.external_id === group.projectExtId);
10559
+ const { breakdown, readout } = projectGroupBilling(project, {
10560
+ isOwned: group.isOwned,
10561
+ stripeEnabled
10562
+ });
10563
+ const count = group.workspaces.length;
10564
+ return /* @__PURE__ */ jsxs(
10565
+ WorkspaceGroup,
10566
+ {
10567
+ title: group.projectExtId ? /* @__PURE__ */ jsx(
10568
+ ProjectBandTitle,
10569
+ {
10570
+ name: group.title,
10571
+ count,
10572
+ nameTestId: `project-name-${group.projectExtId}`,
10573
+ breakdown,
10574
+ readout
10575
+ }
10576
+ ) : /* @__PURE__ */ jsx(
10577
+ PlainBandTitle,
10578
+ {
10579
+ title: group.title,
10580
+ count,
10581
+ hint: group.isCommon ? "Shared with everyone" : void 0
10582
+ }
10583
+ ),
10584
+ onOpen: group.projectExtId ? () => onOpenProject(group.projectExtId) : void 0,
10585
+ openLabel: "Plans & Usage",
10586
+ collapsedHint: group.isCommon ? `Show ${count} shared ${getEntityLabel("WORKSPACE", "PLURAL").toLowerCase()}` : void 0,
10587
+ isExpanded: !list.isCollapsed(group.key),
10588
+ onToggle: () => list.toggleGroup(group.key),
10589
+ children: [
10590
+ group.isOwned && group.projectExtId && /* @__PURE__ */ jsx(
10591
+ CreateTile,
10683
10592
  {
10684
- icon: /* @__PURE__ */ jsx(HtmlIcon, { size: 16, className: "flex-shrink-0" }),
10685
- documentTitle: hit.docTitle,
10686
- pageNumber: hit.page,
10687
- passageIndex: hit.index,
10688
- showTooltip: false,
10689
- labelMaxWidth: "max-w-none"
10593
+ label: `New ${workspaceLabel.toLowerCase()}`,
10594
+ ariaLabel: `Create ${workspaceLabel}`,
10595
+ onSelect: () => onCreateWorkspace(group.projectExtId),
10596
+ disabledReason: createDisabledReason,
10597
+ help: count === 0 ? `Already have one? Open it and change its ${projectLabel} to move it here.` : void 0,
10598
+ testId: "add-workspace-button"
10690
10599
  }
10691
10600
  ),
10692
- /* @__PURE__ */ jsx(MeaningMatchBadge, { content: hit.content, terms, labels })
10693
- ] })
10694
- ),
10695
- content: hit.content,
10696
- terms,
10697
- renderSnippet: renderSearchSnippet,
10698
- onClick: handleClick,
10699
- testId: `search-result-flat-${position}`
10700
- }
10701
- );
10702
- });
10703
- var GroupedResultRow = memo(function GroupedResultRow2({
10601
+ [...group.workspaces].sort(byRecentUpdate).map((workspace) => /* @__PURE__ */ jsx(
10602
+ WorkspaceCard,
10603
+ {
10604
+ workspace: toWorkspaceSummary(workspace, selfId),
10605
+ current: currentWorkspaceId === workspace.external_id,
10606
+ onSelect: () => onOpenWorkspace(workspace),
10607
+ href: buildDeepLinkPath({ workspaceId: workspace.external_id }),
10608
+ mark: /* @__PURE__ */ jsx(
10609
+ ShieldIcon,
10610
+ {
10611
+ char: workspace.name.charAt(0),
10612
+ workspaceColor: workspaceKindColor(getWorkspaceKind(workspace)),
10613
+ size: 28,
10614
+ isSelected: true
10615
+ }
10616
+ ),
10617
+ testId: `workspace-card-${workspace.external_id}`,
10618
+ openCueTestId: `workspace-open-${workspace.external_id}`
10619
+ },
10620
+ workspace.external_id
10621
+ ))
10622
+ ]
10623
+ },
10624
+ group.key
10625
+ );
10626
+ }) });
10627
+ }
10628
+ function WorkspaceFilterMenu({ list }) {
10629
+ const [open, setOpen] = useState(false);
10630
+ const { workspaces, projects, selfId, pinned, filters, setFilters } = list;
10631
+ const { options: options2, groups } = useMemo(() => {
10632
+ const attributes = workspaces.map((w) => workspaceFilterAttributes(w, { selfId, pinned }));
10633
+ const countOf = (id) => attributes.filter((a) => a[id]).length;
10634
+ const projectIds = projects.map((p) => projectFilterId(p.external_id));
10635
+ const options3 = [
10636
+ ...projects.map((p) => ({
10637
+ id: projectFilterId(p.external_id),
10638
+ label: p.name,
10639
+ count: countOf(projectFilterId(p.external_id))
10640
+ })),
10641
+ ...WORKSPACE_CATEGORY_FILTERS.map(({ id, label }) => ({ id, label, count: countOf(id) }))
10642
+ ];
10643
+ const groups2 = [
10644
+ { label: "Projects", options: projectIds },
10645
+ { label: "Workspaces", options: WORKSPACE_CATEGORY_FILTERS.map((f) => f.id) }
10646
+ ];
10647
+ return { options: options3, groups: groups2 };
10648
+ }, [workspaces, projects, selfId, pinned]);
10649
+ return /* @__PURE__ */ jsx(
10650
+ TriStateFilterMenu,
10651
+ {
10652
+ testId: "workspace-filter-button",
10653
+ open,
10654
+ onOpenChange: setOpen,
10655
+ title: "Filter workspaces",
10656
+ triggerLabel: "Filter",
10657
+ renderChevron: true,
10658
+ filterStates: filters,
10659
+ onFiltersChange: setFilters,
10660
+ options: options2,
10661
+ groups
10662
+ }
10663
+ );
10664
+ }
10665
+ var DEFAULT_WORKSPACE_LIST_LABELS = {
10666
+ recentlyDeletedHint: (count) => `You can restore ${count === 1 ? "it" : "them"} from Recently deleted (Settings > Storage) for at least 30 days.`
10667
+ };
10668
+ function useConnectedWorkspaceList({
10669
+ filters: controlledFilters,
10670
+ onFiltersChange,
10671
+ labels
10672
+ } = {}) {
10673
+ const recentlyDeletedHint = labels?.recentlyDeletedHint ?? DEFAULT_WORKSPACE_LIST_LABELS.recentlyDeletedHint;
10674
+ const { confirm, notify: notify14 } = useBlocksEnvironment();
10675
+ const { userId: selfId } = useConnectedWorkspace();
10676
+ const workspacesQuery = useWorkspaces();
10677
+ const { data: projects } = useProjects();
10678
+ const { data: settings } = useUserSettings();
10679
+ const canCreateProjects = useCanCreateProjects();
10680
+ const deleteWorkspaces = useDeleteWorkspaces();
10681
+ const workspaces = useMemo(
10682
+ () => (workspacesQuery.data ?? []).filter(isListedWorkspace),
10683
+ [workspacesQuery.data]
10684
+ );
10685
+ const pinned = useMemo(
10686
+ () => new Set(settings?.pinned_workspaces ?? []),
10687
+ [settings?.pinned_workspaces]
10688
+ );
10689
+ const [localFilters, setLocalFilters] = useState({});
10690
+ const filters = controlledFilters ?? localFilters;
10691
+ const filterMap = useMemo(() => {
10692
+ const known = new Set(WORKSPACE_CATEGORY_FILTERS.map((f) => f.id));
10693
+ for (const p of projects ?? []) known.add(projectFilterId(p.external_id));
10694
+ for (const w of workspaces) if (w.project_ext_id) known.add(projectFilterId(w.project_ext_id));
10695
+ return new Map(Object.entries(filters).filter(([id, state]) => state !== null && known.has(id)));
10696
+ }, [filters, projects, workspaces]);
10697
+ const setFilters = useCallback(
10698
+ (next) => {
10699
+ const record = Object.fromEntries(next);
10700
+ if (onFiltersChange) onFiltersChange(record);
10701
+ if (controlledFilters === void 0) setLocalFilters(record);
10702
+ },
10703
+ [controlledFilters, onFiltersChange]
10704
+ );
10705
+ const attributesOf = useCallback(
10706
+ (workspace) => workspaceFilterAttributes(workspace, { selfId, pinned }),
10707
+ [selfId, pinned]
10708
+ );
10709
+ const filtered = useMemo(
10710
+ () => applyTriStateFilters(workspaces, filterMap, attributesOf),
10711
+ [workspaces, filterMap, attributesOf]
10712
+ );
10713
+ const groups = useMemo(
10714
+ () => groupWorkspacesByProject(filtered, {
10715
+ selfId,
10716
+ pinned,
10717
+ projects: projects ?? [],
10718
+ workspacesLabel: getEntityLabel("WORKSPACE", "PLURAL")
10719
+ }),
10720
+ [filtered, selfId, pinned, projects]
10721
+ );
10722
+ const [toggled, setToggled] = useState({});
10723
+ const isCollapsed = useCallback(
10724
+ (key) => toggled[key] ?? groups.some((g) => g.key === key && g.isCommon),
10725
+ [toggled, groups]
10726
+ );
10727
+ const toggleGroup = useCallback(
10728
+ (key) => setToggled((prev) => ({ ...prev, [key]: !isCollapsed(key) })),
10729
+ [isCollapsed]
10730
+ );
10731
+ const [selected, setSelected] = useState([]);
10732
+ const canDeleteSelected = selected.every((w) => !!selfId && w.created_by_ext_id === selfId);
10733
+ const deleteSelected = useCallback(async () => {
10734
+ const count = selected.length;
10735
+ if (count === 0) return;
10736
+ const noun = getEntityLabel("WORKSPACE", count === 1 ? "SINGULAR" : "PLURAL");
10737
+ const confirmed = await confirm({
10738
+ title: `Delete ${count} ${noun}`,
10739
+ message: `Delete ${count} ${noun.toLowerCase()}? ${recentlyDeletedHint(count)}`
10740
+ });
10741
+ if (!confirmed) return;
10742
+ try {
10743
+ await deleteWorkspaces.mutateAsync(selected.map((w) => w.external_id));
10744
+ setSelected([]);
10745
+ notify14(`Deleted ${count} ${noun.toLowerCase()}`, "success");
10746
+ } catch {
10747
+ notify14("Failed to delete some workspaces", "error");
10748
+ }
10749
+ }, [selected, confirm, notify14, deleteWorkspaces, recentlyDeletedHint]);
10750
+ return {
10751
+ selfId,
10752
+ /** Every listed workspace (unfiltered): the filter menu's counts. */
10753
+ workspaces,
10754
+ projects: projects ?? [],
10755
+ pinned,
10756
+ /** The filtered workspaces grouped by project, in list order. */
10757
+ groups,
10758
+ /** How many workspaces the filters let through. */
10759
+ shownCount: filtered.length,
10760
+ isLoading: workspacesQuery.isLoading,
10761
+ error: workspacesQuery.error,
10762
+ canCreateProjects,
10763
+ filters: filterMap,
10764
+ setFilters,
10765
+ isCollapsed,
10766
+ toggleGroup,
10767
+ selected,
10768
+ setSelected,
10769
+ canDeleteSelected,
10770
+ deleteSelected,
10771
+ isDeleting: deleteWorkspaces.isPending
10772
+ };
10773
+ }
10774
+ var WorkspaceTableView = lazy(
10775
+ () => import('../WorkspaceTableView-5OWX53R5.js').then((m) => ({ default: m.WorkspaceTableView }))
10776
+ );
10777
+ var WORKSPACES_TAB_ID = "workspaces";
10778
+ var defaultRenderMember2 = (user, size) => {
10779
+ const name = user.family_name ? `${user.given_name} ${user.family_name}` : user.given_name;
10780
+ return /* @__PURE__ */ jsx(
10781
+ UserEntity,
10782
+ {
10783
+ size,
10784
+ variant: "icon",
10785
+ showTooltip: false,
10786
+ interactive: false,
10787
+ displayName: name,
10788
+ initials: initials(name || user.email),
10789
+ picture: user.picture
10790
+ }
10791
+ );
10792
+ };
10793
+ function ConnectedWorkspaceList({
10794
+ onCreateWorkspace,
10795
+ view: controlledView,
10796
+ onViewChange,
10797
+ filters,
10798
+ onFiltersChange,
10799
+ gridState,
10800
+ onGridStateChange,
10801
+ renderMember = defaultRenderMember2,
10802
+ extraTabs = [],
10803
+ activeTab = WORKSPACES_TAB_ID,
10804
+ onTabChange,
10805
+ children,
10806
+ labels
10807
+ }) {
10808
+ const list = useConnectedWorkspaceList({ filters, onFiltersChange, labels });
10809
+ const { openProject } = useBlocksEnvironment();
10810
+ const { workspaceId: currentWorkspaceId, openWorkspace } = useConnectedWorkspace();
10811
+ const [localView, setLocalView] = useState("cards");
10812
+ const view = controlledView ?? localView;
10813
+ const isPhone = useIsPhone();
10814
+ const shownView = view === "table" && !isPhone ? "table" : "cards";
10815
+ const showList = activeTab === WORKSPACES_TAB_ID || !extraTabs.some((t) => t.id === activeTab);
10816
+ const setView = (next) => {
10817
+ onViewChange?.(next);
10818
+ if (controlledView === void 0) setLocalView(next);
10819
+ };
10820
+ const open = (workspace) => void openWorkspace(workspace.external_id, { name: workspace.name });
10821
+ const createDisabledReason = list.canCreateProjects === false ? PROJECT_CREATION_DENIED_REASON : void 0;
10822
+ if (list.isLoading) return /* @__PURE__ */ jsx(LoadingIndicator, { message: "Loading workspaces..." });
10823
+ if (list.error) {
10824
+ return /* @__PURE__ */ jsx(
10825
+ HomeLoadError,
10826
+ {
10827
+ title: "Unable to load workspaces",
10828
+ body: "Failed to connect to the server. Please check your connection and try again."
10829
+ }
10830
+ );
10831
+ }
10832
+ const newButton = createDisabledReason ? (
10833
+ // A disabled button emits no pointer events: the span carries the tooltip.
10834
+ /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 50, children: [
10835
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { tabIndex: 0, "data-testid": "home-new-workspace-disabled", children: /* @__PURE__ */ jsxs(Button, { disabled: true, "data-testid": "home-new-workspace", children: [
10836
+ /* @__PURE__ */ jsx(Plus, { className: "mr-0.5 size-4" }),
10837
+ "New"
10838
+ ] }) }) }),
10839
+ /* @__PURE__ */ jsx(TooltipContent, { children: createDisabledReason })
10840
+ ] })
10841
+ ) : /* @__PURE__ */ jsxs(Button, { onClick: () => onCreateWorkspace(), "data-testid": "home-new-workspace", children: [
10842
+ /* @__PURE__ */ jsx(Plus, { className: "mr-0.5 size-4" }),
10843
+ "New"
10844
+ ] });
10845
+ return /* @__PURE__ */ jsxs(HomeFrame, { children: [
10846
+ /* @__PURE__ */ jsx(
10847
+ TabHeader,
10848
+ {
10849
+ testId: "home-header",
10850
+ tabs: [
10851
+ {
10852
+ id: WORKSPACES_TAB_ID,
10853
+ label: getEntityLabel("WORKSPACE", "PLURAL"),
10854
+ count: list.shownCount,
10855
+ icon: /* @__PURE__ */ jsx(SquareStack, { className: "size-4" })
10856
+ },
10857
+ ...extraTabs
10858
+ ],
10859
+ activeTabId: showList ? WORKSPACES_TAB_ID : activeTab,
10860
+ onTabChange: (id) => onTabChange?.(id),
10861
+ centerContent: showList && !isPhone ? /* @__PURE__ */ jsx(
10862
+ ViewToggle,
10863
+ {
10864
+ options: [
10865
+ { value: "cards", icon: /* @__PURE__ */ jsx(SquareStack, { className: "size-4" }), label: "Cards" },
10866
+ { value: "grid", icon: /* @__PURE__ */ jsx(Table, { className: "size-4" }), label: "Table" }
10867
+ ],
10868
+ value: shownView === "table" ? "grid" : "cards",
10869
+ onChange: (value) => setView(value === "grid" ? "table" : "cards"),
10870
+ testIdPrefix: "workspaces",
10871
+ size: "sm",
10872
+ showLabels: true,
10873
+ hideTooltips: true
10874
+ }
10875
+ ) : void 0,
10876
+ rightContent: showList ? newButton : void 0
10877
+ }
10878
+ ),
10879
+ showList && // Above whichever view shows: both draw the same filtered groups.
10880
+ /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-6xl px-4 pb-3 sm:px-8", children: /* @__PURE__ */ jsx(WorkspaceFilterMenu, { list }) }),
10881
+ /* @__PURE__ */ jsx(HomeMain, { children: !showList ? children : shownView === "cards" ? (
10882
+ // Full-width scroller (its scrollbar rides the window edge) around a centred column.
10883
+ /* @__PURE__ */ jsx(HomeCenteredScroll, { children: /* @__PURE__ */ jsx(
10884
+ WorkspaceCardsView,
10885
+ {
10886
+ list,
10887
+ currentWorkspaceId,
10888
+ onOpenWorkspace: open,
10889
+ onOpenProject: openProject,
10890
+ onCreateWorkspace,
10891
+ createDisabledReason
10892
+ }
10893
+ ) })
10894
+ ) : /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingIndicator, { message: "Loading grid..." }), children: /* @__PURE__ */ jsx(HomeCenteredColumn, { children: /* @__PURE__ */ jsx(
10895
+ WorkspaceTableView,
10896
+ {
10897
+ list,
10898
+ onOpenWorkspace: open,
10899
+ onOpenProject: openProject,
10900
+ renderMember,
10901
+ gridState,
10902
+ onGridStateChange
10903
+ }
10904
+ ) }) }) })
10905
+ ] });
10906
+ }
10907
+ var PROJECTS_BASE_TAB_ID = "projects";
10908
+ var isPaid = (p) => p.subscription === "active" || p.subscription === "past_due";
10909
+ function useLocalProjectTabs() {
10910
+ const [openProjectIds, setOpen] = useState([]);
10911
+ const [activeProjectId, setActive] = useState(null);
10912
+ return {
10913
+ openProjectIds,
10914
+ activeProjectId,
10915
+ onSelect: (id) => {
10916
+ if (id) setOpen((open) => open.includes(id) ? open : [...open, id]);
10917
+ setActive(id);
10918
+ },
10919
+ onClose: (id) => {
10920
+ setOpen((open) => open.filter((openId) => openId !== id));
10921
+ setActive((active) => active === id ? null : active);
10922
+ }
10923
+ };
10924
+ }
10925
+ function ConnectedProjectsPanel({
10926
+ tabs: hostTabs,
10927
+ projectStorage,
10928
+ footer,
10929
+ headerRight,
10930
+ className
10931
+ }) {
10932
+ const localTabs = useLocalProjectTabs();
10933
+ const tabs = hostTabs ?? localTabs;
10934
+ const { data: projects } = useProjects();
10935
+ const canCreateProjects = useCanCreateProjects();
10936
+ const { userId } = useConnectedWorkspace();
10937
+ const { stripeEnabled } = useBillingSupport();
10938
+ const billingPortal = useOpenBillingPortal();
10939
+ const [newProjectOpen, setNewProjectOpen] = useState(false);
10940
+ useAutoRefreshPendingProjects();
10941
+ const ownedCount = (projects ?? []).filter((p) => p.created_by === userId).length;
10942
+ const activeProjectId = tabs.activeProjectId && tabs.openProjectIds.includes(tabs.activeProjectId) ? tabs.activeProjectId : null;
10943
+ const headerTabs = useMemo(
10944
+ () => [
10945
+ {
10946
+ id: PROJECTS_BASE_TAB_ID,
10947
+ // Named by its job; "project" is for the per-project tabs, where owning several matters.
10948
+ label: "Plan & Usage",
10949
+ icon: /* @__PURE__ */ jsx(EntityIcon, { entityType: "PROJECT" }),
10950
+ // Most users own one project: a count only once there are several.
10951
+ count: ownedCount > 1 ? ownedCount : void 0
10952
+ },
10953
+ ...tabs.openProjectIds.map((id) => ({
10954
+ id,
10955
+ label: projects?.find((p) => p.external_id === id)?.name ?? "Project",
10956
+ icon: /* @__PURE__ */ jsx(EntityIcon, { entityType: "PROJECT" }),
10957
+ onClose: () => tabs.onClose(id),
10958
+ closeTooltip: "Close project"
10959
+ }))
10960
+ ],
10961
+ [ownedCount, tabs, projects]
10962
+ );
10963
+ const paidProject = projects?.find(isPaid);
10964
+ const panel = /* @__PURE__ */ jsxs(PanelFrame, { className, testId: "projects-panel", children: [
10965
+ /* @__PURE__ */ jsx(DraggableHeader, { children: /* @__PURE__ */ jsx(
10966
+ TabHeader,
10967
+ {
10968
+ tabs: headerTabs,
10969
+ activeTabId: activeProjectId ?? PROJECTS_BASE_TAB_ID,
10970
+ onTabChange: (id) => tabs.onSelect(id === PROJECTS_BASE_TAB_ID ? null : id),
10971
+ afterTabs: (isCompact) => /* @__PURE__ */ jsx(
10972
+ PanelAddButton,
10973
+ {
10974
+ label: "New project",
10975
+ onClick: () => setNewProjectOpen(true),
10976
+ icon: /* @__PURE__ */ jsx(Plus, { className: "size-4" }),
10977
+ isCompact,
10978
+ testId: "new-project-button",
10979
+ disabledReason: canCreateProjects === false ? PROJECT_CREATION_DENIED_REASON : void 0
10980
+ }
10981
+ ),
10982
+ rightContent: headerRight,
10983
+ variant: "underline",
10984
+ compactBreakpoint: 360
10985
+ }
10986
+ ) }),
10987
+ /* @__PURE__ */ jsx(PanelFrameBody, { children: activeProjectId ? /* @__PURE__ */ jsx(PanelScroll, { children: /* @__PURE__ */ jsx(
10988
+ ProjectDetail,
10989
+ {
10990
+ projectExtId: activeProjectId,
10991
+ storage: projectStorage?.(activeProjectId)
10992
+ }
10993
+ ) }) : /* @__PURE__ */ jsxs(PanelScroll, { variant: "padded", children: [
10994
+ /* @__PURE__ */ jsx(ProjectsTable, {}),
10995
+ stripeEnabled && paidProject && /* @__PURE__ */ jsx(PanelActionsRow, { children: /* @__PURE__ */ jsxs(
10996
+ Button,
10997
+ {
10998
+ onClick: () => billingPortal.open(paidProject.external_id),
10999
+ size: "sm",
11000
+ variant: "outline",
11001
+ disabled: billingPortal.isPending,
11002
+ className: "flex items-center gap-1",
11003
+ children: [
11004
+ billingPortal.isPending ? "Opening\u2026" : "Billing portal",
11005
+ !billingPortal.isPending && /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5" })
11006
+ ]
11007
+ }
11008
+ ) }),
11009
+ /* @__PURE__ */ jsx(SharedProjects, {}),
11010
+ footer
11011
+ ] }) }),
11012
+ /* @__PURE__ */ jsx(
11013
+ NewProjectFlow,
11014
+ {
11015
+ open: newProjectOpen,
11016
+ onOpenChange: setNewProjectOpen,
11017
+ defaultName: `Project ${(projects?.length ?? 0) + 1}`
11018
+ }
11019
+ )
11020
+ ] });
11021
+ return hostTabs ? panel : /* @__PURE__ */ jsx(BlocksProvider, { openProject: localTabs.onSelect, children: panel });
11022
+ }
11023
+
11024
+ // src/connection/workspace-dashboard/dashboard-rows.ts
11025
+ var RECENT_ROWS = 5;
11026
+ var FACT_ROWS = 3;
11027
+ function shortDate(iso) {
11028
+ return new Date(iso).toLocaleDateString(void 0, { day: "numeric", month: "short" });
11029
+ }
11030
+ function timeAndDate(iso) {
11031
+ if (!iso) return "";
11032
+ const d = new Date(iso);
11033
+ if (Number.isNaN(d.getTime())) return "";
11034
+ const time2 = d.toLocaleTimeString(void 0, {
11035
+ hour: "2-digit",
11036
+ minute: "2-digit",
11037
+ hour12: false
11038
+ });
11039
+ return `${time2} \xB7 ${shortDate(iso)}`;
11040
+ }
11041
+ function humanizeStatus(status) {
11042
+ const words = status.replace(/[-_]/g, " ");
11043
+ return words.charAt(0).toUpperCase() + words.slice(1);
11044
+ }
11045
+ var time = (iso) => +new Date(iso ?? 0);
11046
+ function recentConversations(conversations) {
11047
+ return [...conversations].sort((a, b) => time(b.updated_at) - time(a.updated_at)).slice(0, RECENT_ROWS).map((c) => ({
11048
+ id: c.external_id,
11049
+ title: c.title?.trim() || "Untitled thread",
11050
+ subtitle: c.status ? [STATUS_LABELS[asTaskStatus(c.status)], c.due_at ? `due ${shortDate(c.due_at)}` : null].filter(Boolean).join(" \xB7 ") : `${c.message_count} ${c.message_count === 1 ? "message" : "messages"}`,
11051
+ time: timeAndDate(c.updated_at)
11052
+ }));
11053
+ }
11054
+ function recentDocuments(documents) {
11055
+ return [...documents].sort((a, b) => time(b.updated_at ?? b.created_at) - time(a.updated_at ?? a.created_at)).slice(0, RECENT_ROWS).map((d) => ({
11056
+ id: d.external_id,
11057
+ fileName: d.file_name,
11058
+ title: d.file_name || "Untitled document",
11059
+ subtitle: d.status && d.status !== "ready" && d.status !== "completed" ? humanizeStatus(d.status) : d.n_pages ? `${d.n_pages} ${d.n_pages === 1 ? "page" : "pages"}` : d.file_type ?? "Document",
11060
+ time: timeAndDate(d.updated_at ?? d.created_at)
11061
+ }));
11062
+ }
11063
+ function splitFacts(facts, now = /* @__PURE__ */ new Date()) {
11064
+ const startOfToday = new Date(now);
11065
+ startOfToday.setHours(0, 0, 0, 0);
11066
+ const dated = facts.filter((f) => f.date_start && new Date(f.date_start) >= startOfToday).sort((a, b) => time(a.date_start) - time(b.date_start)).slice(0, FACT_ROWS);
11067
+ const datedIds = new Set(dated.map((f) => f.external_id));
11068
+ const rest = facts.filter((f) => !datedIds.has(f.external_id)).sort((a, b) => time(b.created_at) - time(a.created_at)).slice(0, FACT_ROWS);
11069
+ const row = (f, stamp) => ({
11070
+ id: f.external_id,
11071
+ title: f.text,
11072
+ time: stamp
11073
+ });
11074
+ return {
11075
+ upcoming: dated.map((f) => row(f, f.date_start ? shortDate(f.date_start) : "")),
11076
+ recent: rest.map((f) => row(f, timeAndDate(f.created_at)))
11077
+ };
11078
+ }
11079
+ function ConnectedWorkspaceDashboard({
11080
+ actions,
11081
+ start,
11082
+ addFilesAction,
11083
+ aside,
11084
+ testId = "workspace-dashboard"
11085
+ }) {
11086
+ const { openDocument, openConversation } = useBlocksEnvironment();
11087
+ const { workspaceId } = useConnectedWorkspace();
11088
+ const { data: documents } = useConnectedDocuments();
11089
+ const { data: conversations } = useConnectedConversations();
11090
+ const { data: facts = [] } = useFacts(workspaceId);
11091
+ const conversationIcon = /* @__PURE__ */ jsx(EntityIcon, { entityType: "CONVERSATION", className: "mt-0.5 size-5 shrink-0" });
11092
+ const chatRows = useMemo(
11093
+ () => recentConversations(conversations).map((row) => ({
11094
+ ...row,
11095
+ leading: conversationIcon,
11096
+ href: workspaceId ? buildDeepLinkPath({ workspaceId, conversationId: row.id }) : void 0,
11097
+ onSelect: () => openConversation(row.id)
11098
+ })),
11099
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11100
+ [conversations, workspaceId, openConversation]
11101
+ );
11102
+ const docRows = useMemo(
11103
+ () => recentDocuments(documents).map(({ fileName, ...row }) => ({
11104
+ ...row,
11105
+ // The file-type mark, bare: it carries its own identity.
11106
+ leading: /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0", children: getDocIcon(fileName, 20) }),
11107
+ href: workspaceId ? buildDeepLinkPath({ workspaceId, documentId: row.id }) : void 0,
11108
+ onSelect: () => openDocument(row.id)
11109
+ })),
11110
+ [documents, workspaceId, openDocument]
11111
+ );
11112
+ const { upcoming, recent } = useMemo(() => {
11113
+ const { upcoming: upcoming2, recent: recent2 } = splitFacts(facts);
11114
+ const toRow = (row) => ({
11115
+ ...row,
11116
+ leading: /* @__PURE__ */ jsx(FactMark, {}),
11117
+ // The item, not the list: the Facts page scrolls to this fact.
11118
+ onSelect: () => actions.openFact(row.id)
11119
+ });
11120
+ return { upcoming: upcoming2.map(toRow), recent: recent2.map(toRow) };
11121
+ }, [facts, actions]);
11122
+ return /* @__PURE__ */ jsxs(DashboardRoot, { testId, children: [
11123
+ start && /* @__PURE__ */ jsx(DashboardBand, { kind: "start", children: start }),
11124
+ /* @__PURE__ */ jsxs(DashboardBand, { kind: "recent", children: [
11125
+ /* @__PURE__ */ jsx(SectionLabel, { testId: `${testId}-recent-caption`, children: "Recent" }),
11126
+ /* @__PURE__ */ jsxs(DashboardColumns, { children: [
11127
+ /* @__PURE__ */ jsx(
11128
+ RecentList,
11129
+ {
11130
+ testId: `${testId}-docs`,
11131
+ icon: FileText,
11132
+ title: getEntityLabel("DOCUMENT", "PLURAL"),
11133
+ count: documents.length,
11134
+ onOpen: () => actions.openSection("documents"),
11135
+ action: addFilesAction,
11136
+ rows: docRows,
11137
+ caption: "Added",
11138
+ empty: {
11139
+ icon: Upload,
11140
+ label: "Add files or emails",
11141
+ description: "From your computer, a web link, or a connected mailbox.",
11142
+ onSelect: actions.addFiles
11143
+ }
11144
+ }
11145
+ ),
11146
+ /* @__PURE__ */ jsx(
11147
+ RecentList,
11148
+ {
11149
+ testId: `${testId}-chats`,
11150
+ icon: MessagesSquare,
11151
+ title: getEntityLabel("CONVERSATION", "PLURAL"),
11152
+ count: conversations.length,
11153
+ onOpen: () => actions.openSection("threads"),
11154
+ action: (
11155
+ // Quiet like every section action: the Start band is the page's first job.
11156
+ /* @__PURE__ */ jsx(
11157
+ DashboardAction,
11158
+ {
11159
+ emphasis: "quiet",
11160
+ icon: /* @__PURE__ */ jsx(Plus, { className: "size-4" }),
11161
+ label: "New",
11162
+ onClick: actions.newThread,
11163
+ testId: `${testId}-chats-new`
11164
+ }
11165
+ )
11166
+ ),
11167
+ rows: chatRows,
11168
+ caption: "Messaged",
11169
+ empty: {
11170
+ icon: Plus,
11171
+ label: "Ask ARBI anything",
11172
+ description: "Questions are answered from this workspace's files.",
11173
+ onSelect: actions.ask
11174
+ }
11175
+ }
11176
+ )
11177
+ ] })
11178
+ ] }),
11179
+ /* @__PURE__ */ jsxs(DashboardColumns, { children: [
11180
+ /* @__PURE__ */ jsx(
11181
+ FactsStrip,
11182
+ {
11183
+ testId: `${testId}-facts`,
11184
+ icon: Milestone,
11185
+ title: getEntityLabel("FACT", "PLURAL"),
11186
+ count: facts.length,
11187
+ onOpen: () => actions.openSection("facts"),
11188
+ action: /* @__PURE__ */ jsx(
11189
+ DashboardAction,
11190
+ {
11191
+ emphasis: "quiet",
11192
+ icon: /* @__PURE__ */ jsx(Plus, { className: "size-4" }),
11193
+ label: "New fact",
11194
+ onClick: actions.newFact,
11195
+ testId: `${testId}-facts-action`
11196
+ }
11197
+ ),
11198
+ groups: [
11199
+ { caption: "Upcoming", rows: upcoming, rowTestId: `${testId}-facts-upcoming-row` },
11200
+ { caption: "Created", rows: recent, rowTestId: `${testId}-facts-row` }
11201
+ ],
11202
+ empty: {
11203
+ icon: Milestone,
11204
+ label: `Add your first ${getEntityLabel("FACT").toLowerCase()}`,
11205
+ description: "A date, a decision, a preference \u2014 anything worth remembering.",
11206
+ onSelect: actions.newFact,
11207
+ testId: `${testId}-facts-empty-action`
11208
+ }
11209
+ }
11210
+ ),
11211
+ aside
11212
+ ] })
11213
+ ] });
11214
+ }
11215
+ var DEFAULT_SEARCH_ROW_LABELS = {
11216
+ meaningMatch: "Matched by meaning",
11217
+ meaningMatchTitle: "None of your search words appear in this passage. It matched by meaning.",
11218
+ page: (page) => `p.${page}`,
11219
+ passage: "Passage"
11220
+ };
11221
+ function useIsOpenPassage(chunkExtId) {
11222
+ return useArbiSessionState((s) => !!chunkExtId && s.viewingDocument?.passageId === chunkExtId);
11223
+ }
11224
+ function MeaningMatchBadge({
11225
+ content,
11226
+ terms,
11227
+ labels
11228
+ }) {
11229
+ return isMeaningMatch(content, terms) ? /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-muted-foreground", title: labels.meaningMatchTitle, children: labels.meaningMatch }) : null;
11230
+ }
11231
+ var FlatResultRow = memo(function FlatResultRow2({
11232
+ hit,
11233
+ position,
11234
+ terms,
11235
+ onOpen,
11236
+ labels
11237
+ }) {
11238
+ const handleClick = useCallback((e) => onOpen(e, hit), [onOpen, hit]);
11239
+ return /* @__PURE__ */ jsx(
11240
+ SearchHit,
11241
+ {
11242
+ active: useIsOpenPassage(hit.chunkExtId),
11243
+ header: (
11244
+ // No `onClick` on the pill: it would render a real <button> inside the card's
11245
+ // role="button" target (invalid nesting). The card already opens the passage.
11246
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
11247
+ /* @__PURE__ */ jsx(
11248
+ PassagePill,
11249
+ {
11250
+ icon: /* @__PURE__ */ jsx(HtmlIcon, { size: 16, className: "flex-shrink-0" }),
11251
+ documentTitle: hit.docTitle,
11252
+ pageNumber: hit.page,
11253
+ passageIndex: hit.index,
11254
+ showTooltip: false,
11255
+ labelMaxWidth: "max-w-none"
11256
+ }
11257
+ ),
11258
+ /* @__PURE__ */ jsx(MeaningMatchBadge, { content: hit.content, terms, labels })
11259
+ ] })
11260
+ ),
11261
+ content: hit.content,
11262
+ terms,
11263
+ renderSnippet: renderSearchSnippet,
11264
+ onClick: handleClick,
11265
+ testId: `search-result-flat-${position}`
11266
+ }
11267
+ );
11268
+ });
11269
+ var GroupedResultRow = memo(function GroupedResultRow2({
10704
11270
  hit,
10705
11271
  testId,
10706
11272
  terms,
@@ -13823,7 +14389,7 @@ function BookingPagesPanel({ defaultTimeZone, className }) {
13823
14389
  var selectClass4 = "h-9 w-full rounded-md border border-input bg-background px-2 text-sm text-foreground shadow-sm";
13824
14390
  function CreateRoomForm({ zone, onDone }) {
13825
14391
  const { notify: notify14 } = useBlocksEnvironment();
13826
- const create2 = useCreateRoom();
14392
+ const create3 = useCreateRoom();
13827
14393
  const [name, setName] = useState("");
13828
14394
  const [type, setType] = useState("room");
13829
14395
  const [capacity, setCapacity] = useState("");
@@ -13843,7 +14409,7 @@ function CreateRoomForm({ zone, onDone }) {
13843
14409
  const cap = capacity.trim() ? Number(capacity) : null;
13844
14410
  if (cap !== null && (!Number.isInteger(cap) || cap < 1))
13845
14411
  return setError("Capacity must be a whole number");
13846
- create2.mutate(
14412
+ create3.mutate(
13847
14413
  {
13848
14414
  name: name.trim(),
13849
14415
  type,
@@ -13934,8 +14500,8 @@ function CreateRoomForm({ zone, onDone }) {
13934
14500
  error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }),
13935
14501
  /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
13936
14502
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: onDone, children: "Cancel" }),
13937
- /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: create2.isPending, children: [
13938
- create2.isPending && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 size-4 animate-spin" }),
14503
+ /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: create3.isPending, children: [
14504
+ create3.isPending && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 size-4 animate-spin" }),
13939
14505
  "Create"
13940
14506
  ] })
13941
14507
  ] })
@@ -15014,7 +15580,7 @@ function subscribePhone(cb) {
15014
15580
  return () => mq?.removeEventListener?.("change", cb);
15015
15581
  }
15016
15582
  var isPhoneNow = () => typeof window !== "undefined" && !!window.matchMedia?.(PHONE_QUERY)?.matches;
15017
- function useIsPhone() {
15583
+ function useIsPhone2() {
15018
15584
  return useSyncExternalStore(subscribePhone, isPhoneNow, () => false);
15019
15585
  }
15020
15586
  var errorText3 = (err) => describeCalendarError(err);
@@ -15036,7 +15602,7 @@ function ConnectedCalendar({
15036
15602
  const arbi = useArbi();
15037
15603
  const fetchEvent = useFetchCalendarEvent();
15038
15604
  const notifyError = (prefix, err) => notify14(`${prefix}: ${errorText3(err)}`, "error");
15039
- const isPhone = useIsPhone();
15605
+ const isPhone = useIsPhone2();
15040
15606
  const { data: settingsData } = useCalendarSettings();
15041
15607
  const settings = settingsData ?? FALLBACK_SETTINGS;
15042
15608
  const zone = safeZone(settings.time_zone, detectTimeZone());
@@ -16251,6 +16817,1152 @@ function ConnectedPublicBooking({ slug, cancelToken }) {
16251
16817
  ] })
16252
16818
  ] });
16253
16819
  }
16820
+ var useFilesStore = create()(
16821
+ persist(
16822
+ (set) => ({
16823
+ pendingFolders: {},
16824
+ addPendingFolder: (workspaceId, path) => set((state) => {
16825
+ const current = state.pendingFolders[workspaceId] ?? [];
16826
+ if (current.includes(path)) return state;
16827
+ return { pendingFolders: { ...state.pendingFolders, [workspaceId]: [...current, path] } };
16828
+ }),
16829
+ removePendingFolder: (workspaceId, path) => set((state) => {
16830
+ const current = state.pendingFolders[workspaceId] ?? [];
16831
+ if (!current.includes(path)) return state;
16832
+ return {
16833
+ pendingFolders: {
16834
+ ...state.pendingFolders,
16835
+ [workspaceId]: current.filter((p) => p !== path)
16836
+ }
16837
+ };
16838
+ })
16839
+ }),
16840
+ {
16841
+ name: "arbi_explorer_pending_folders",
16842
+ storage: createJSONStorage(() => sessionStorage),
16843
+ partialize: (state) => ({ pendingFolders: state.pendingFolders })
16844
+ }
16845
+ )
16846
+ );
16847
+ var NONE = [];
16848
+ function usePendingFolders(workspaceId) {
16849
+ return useFilesStore(
16850
+ (state) => (workspaceId ? state.pendingFolders[workspaceId] : void 0) ?? NONE
16851
+ );
16852
+ }
16853
+ var FilesSupportContext = createContext({});
16854
+ function FilesSupportProvider({
16855
+ value,
16856
+ children
16857
+ }) {
16858
+ return /* @__PURE__ */ jsx(FilesSupportContext.Provider, { value, children });
16859
+ }
16860
+ function useFilesSupport() {
16861
+ return useContext(FilesSupportContext);
16862
+ }
16863
+ var NO_IDS3 = /* @__PURE__ */ new Set();
16864
+ function useOfflineDocumentIds() {
16865
+ const { useOfflineDocumentIds: useHostIds } = useFilesSupport();
16866
+ return useHostIds?.() ?? NO_IDS3;
16867
+ }
16868
+ function useViewingDocumentId() {
16869
+ const { useViewingDocumentId: useHostViewing } = useFilesSupport();
16870
+ return useHostViewing?.() ?? null;
16871
+ }
16872
+ function DocumentSyncGlyph({
16873
+ docExtId,
16874
+ size = 13,
16875
+ className,
16876
+ bgClassName = "bg-muted-foreground"
16877
+ }) {
16878
+ const isAvailableOffline = useOfflineDocumentIds().has(docExtId);
16879
+ if (!isAvailableOffline) return null;
16880
+ return /* @__PURE__ */ jsx(
16881
+ "span",
16882
+ {
16883
+ title: "Available offline (saved on this device)",
16884
+ "aria-label": "Available offline",
16885
+ "data-testid": `offline-available-badge-${docExtId}`,
16886
+ className: cn(
16887
+ "inline-flex items-center justify-center rounded-full text-white ring-1 ring-white",
16888
+ bgClassName,
16889
+ className
16890
+ ),
16891
+ style: { width: size, height: size },
16892
+ children: /* @__PURE__ */ jsx(ArrowDownToLine, { size: Math.round(size * 0.7), strokeWidth: 2.75 })
16893
+ }
16894
+ );
16895
+ }
16896
+ var ACTIVE_STATUSES = /* @__PURE__ */ new Set([
16897
+ "uploading",
16898
+ "queued",
16899
+ "parsing",
16900
+ "encrypting",
16901
+ "indexing",
16902
+ "analysing"
16903
+ ]);
16904
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "skipped", "empty", "low-content"]);
16905
+ var STATUS_LABEL = {
16906
+ uploading: "Uploading",
16907
+ queued: "Queued",
16908
+ parsing: "Parsing",
16909
+ encrypting: "Encrypting",
16910
+ indexing: "Indexing",
16911
+ analysing: "Analysing"
16912
+ };
16913
+ function isActiveDocumentStatus(status) {
16914
+ return ACTIVE_STATUSES.has(status.toLowerCase());
16915
+ }
16916
+ function useDocStatus(doc) {
16917
+ const liveStatus = useProcessingTask(doc.external_id)?.status;
16918
+ const rowStatus = (doc.status ?? "").toLowerCase();
16919
+ if (liveStatus && ACTIVE_STATUSES.has(liveStatus)) return liveStatus;
16920
+ if (TERMINAL_STATUSES.has(rowStatus)) return rowStatus;
16921
+ return liveStatus ?? rowStatus;
16922
+ }
16923
+ function TileStatusBadge({ doc, status }) {
16924
+ let tone = "neutral";
16925
+ let icon2 = null;
16926
+ let title = "";
16927
+ switch (status) {
16928
+ case "completed":
16929
+ tone = "success";
16930
+ icon2 = /* @__PURE__ */ jsx(CheckCircle, { className: "size-4 text-green-text", strokeWidth: 2.75 });
16931
+ title = "Ready";
16932
+ break;
16933
+ case "failed":
16934
+ tone = "danger";
16935
+ icon2 = /* @__PURE__ */ jsx(
16936
+ FailedStageIcon,
16937
+ {
16938
+ stage: doc.failed_stage ?? null,
16939
+ errorMessage: doc.error_message,
16940
+ className: "size-4 text-red-text"
16941
+ }
16942
+ );
16943
+ title = "Failed";
16944
+ break;
16945
+ case "skipped":
16946
+ icon2 = /* @__PURE__ */ jsx(SkipForward, { className: "size-4 text-muted-foreground", strokeWidth: 2.75 });
16947
+ title = "Skipped";
16948
+ break;
16949
+ case "empty":
16950
+ tone = "warning";
16951
+ icon2 = /* @__PURE__ */ jsx(FileX, { className: "size-4 text-yellow-text", strokeWidth: 2.75 });
16952
+ title = "Empty \u2014 no extractable content";
16953
+ break;
16954
+ case "low-content":
16955
+ tone = "warning";
16956
+ icon2 = /* @__PURE__ */ jsx(AlertTriangle, { className: "size-4 text-yellow-text", strokeWidth: 2.75 });
16957
+ title = "Low content \u2014 minimal extractable content";
16958
+ break;
16959
+ default:
16960
+ return null;
16961
+ }
16962
+ return /* @__PURE__ */ jsx(
16963
+ StatusPill,
16964
+ {
16965
+ tone,
16966
+ icon: icon2,
16967
+ title,
16968
+ testId: `explorer-file-status-${doc.external_id}`
16969
+ }
16970
+ );
16971
+ }
16972
+ function TileVisibilityBadge({ doc }) {
16973
+ const shared = !!doc.shared;
16974
+ return /* @__PURE__ */ jsx(
16975
+ StatusPill,
16976
+ {
16977
+ icon: /* @__PURE__ */ jsx(VisibilityIcon, { isShared: shared, className: "size-3.5", "aria-hidden": true }),
16978
+ label: shared ? getEntityLabel("WORKSPACE") : "Only you",
16979
+ title: visibilityAudience(shared),
16980
+ testId: `explorer-file-visibility-${doc.external_id}`
16981
+ }
16982
+ );
16983
+ }
16984
+ function MiniUserAvatar({ user }) {
16985
+ return /* @__PURE__ */ jsxs(Avatar, { className: "h-4 w-4 border border-white", children: [
16986
+ user.picture && /* @__PURE__ */ jsx(AvatarImage, { src: user.picture, alt: getUserDisplayName(user) }),
16987
+ /* @__PURE__ */ jsx(
16988
+ AvatarFallback,
16989
+ {
16990
+ className: cn(
16991
+ "text-xs font-semibold leading-none text-white",
16992
+ getUserAvatarColor(user.external_id)
16993
+ ),
16994
+ children: getUserInitials(user)
16995
+ }
16996
+ )
16997
+ ] });
16998
+ }
16999
+ function FileTooltipContent({
17000
+ doc,
17001
+ modifiedBy,
17002
+ tagNameById
17003
+ }) {
17004
+ const labels = (doc.doctags ?? []).map((dt) => tagNameById.get(dt.tag_ext_id)).filter((name) => !!name);
17005
+ return /* @__PURE__ */ jsx(
17006
+ ExplorerFileTooltip,
17007
+ {
17008
+ title: getDocumentTitle(doc, doc.file_name ?? ""),
17009
+ summary: getDocumentSubject(doc),
17010
+ modifiedLabel: doc.updated_at ? formatFriendlyDate(doc.updated_at, false) : null,
17011
+ modifiedByAvatar: modifiedBy && /* @__PURE__ */ jsx(MiniUserAvatar, { user: modifiedBy }),
17012
+ shared: !!doc.shared,
17013
+ visibilityIcon: /* @__PURE__ */ jsx(VisibilityIcon, { isShared: !!doc.shared, className: "h-3 w-3" }),
17014
+ labels,
17015
+ uiLabels: { shared: visibilityAudience(true), private: visibilityAudience(false) }
17016
+ }
17017
+ );
17018
+ }
17019
+ function startMenuItem(items) {
17020
+ if (items.length === 0) return [];
17021
+ return [
17022
+ {
17023
+ id: "start",
17024
+ label: "Start\u2026",
17025
+ icon: Sparkles,
17026
+ testId: "explorer-start-menu",
17027
+ onSelect: () => {
17028
+ },
17029
+ submenu: items.map((item) => ({
17030
+ id: item.id,
17031
+ label: item.title,
17032
+ icon: Play,
17033
+ onSelect: item.run,
17034
+ testId: `explorer-start-${item.id}`
17035
+ }))
17036
+ }
17037
+ ];
17038
+ }
17039
+ var FileTile = memo(function FileTile2({
17040
+ doc,
17041
+ density,
17042
+ selected,
17043
+ dragging,
17044
+ dndActive,
17045
+ renaming,
17046
+ canDelete,
17047
+ thumbnailUrl,
17048
+ modifiedBy,
17049
+ tagNameById,
17050
+ onSelect,
17051
+ onToggle,
17052
+ onOpen,
17053
+ onReview,
17054
+ onStartRename,
17055
+ onCommitRename,
17056
+ onCancelRename,
17057
+ onDownload,
17058
+ onMove,
17059
+ onDelete
17060
+ }) {
17061
+ const { attributes, listeners: listeners2, setNodeRef } = useDraggable({
17062
+ id: `doc:${doc.external_id}`,
17063
+ data: { type: "doc", docId: doc.external_id }
17064
+ });
17065
+ const { notify: notify14 } = useBlocksEnvironment();
17066
+ const { startItemsForFile, openDocumentChat, emailDocument } = useFilesSupport();
17067
+ const status = useDocStatus(doc);
17068
+ const isProcessing = ACTIVE_STATUSES.has(status);
17069
+ const isOffline = useOfflineDocumentIds().has(doc.external_id);
17070
+ const isViewing = useViewingDocumentId() === doc.external_id;
17071
+ const showStatusMarker = !isProcessing && TERMINAL_STATUSES.has(status);
17072
+ const Icon = getFileTypeIcon(doc.file_name, { mime: doc.file_type });
17073
+ const { data: mailPolicy } = useMailPolicy();
17074
+ const shareAsEmail = async () => {
17075
+ try {
17076
+ await emailDocument?.(doc);
17077
+ } catch (error) {
17078
+ notify14(error instanceof Error ? error.message : "Could not attach document", "error");
17079
+ }
17080
+ };
17081
+ const startItems = startItemsForFile?.(doc);
17082
+ const menuItems = [
17083
+ { id: "open", label: "Open", icon: SquarePen, onSelect: () => onOpen(doc) },
17084
+ ...onReview ? [{ id: "review", label: "Open in Review", icon: BookOpen, onSelect: () => onReview(doc) }] : [],
17085
+ ...openDocumentChat ? [
17086
+ {
17087
+ id: "chat",
17088
+ label: "Document chat",
17089
+ icon: MessagesSquare,
17090
+ onSelect: () => openDocumentChat(doc.external_id)
17091
+ }
17092
+ ] : [],
17093
+ ...startMenuItem(startItems ?? []),
17094
+ {
17095
+ id: "rename",
17096
+ label: "Rename",
17097
+ icon: Pencil,
17098
+ onSelect: () => onStartRename(doc),
17099
+ separatorBefore: true
17100
+ },
17101
+ { id: "download", label: "Download", icon: Download, onSelect: () => onDownload(doc) },
17102
+ ...emailDocument && mailPolicy?.enabled ? [
17103
+ {
17104
+ id: "email",
17105
+ label: "Send as email attachment",
17106
+ icon: Mail,
17107
+ onSelect: () => {
17108
+ void shareAsEmail();
17109
+ }
17110
+ }
17111
+ ] : [],
17112
+ ...onMove ? [{ id: "move", label: "Move to\u2026", icon: FolderInput, onSelect: () => onMove(doc) }] : [],
17113
+ ...canDelete ? [
17114
+ {
17115
+ id: "delete",
17116
+ label: "Delete",
17117
+ icon: Trash2,
17118
+ onSelect: () => onDelete(doc),
17119
+ destructive: true,
17120
+ separatorBefore: true
17121
+ }
17122
+ ] : []
17123
+ ];
17124
+ const tooltip = renaming || dndActive ? void 0 : /* @__PURE__ */ jsx(FileTooltipContent, { doc, modifiedBy, tagNameById });
17125
+ return /* @__PURE__ */ jsx(
17126
+ ExplorerFileTile,
17127
+ {
17128
+ id: doc.external_id,
17129
+ title: getDocumentTitle(doc, doc.file_name ?? ""),
17130
+ density,
17131
+ selected,
17132
+ dragging,
17133
+ renaming,
17134
+ thumbnailUrl,
17135
+ renderFileIcon: (size) => /* @__PURE__ */ jsx(Icon, { size, className: "drop-shadow-sm" }),
17136
+ progress: isProcessing ? { label: STATUS_LABEL[status] ?? status, queued: status === "queued" } : void 0,
17137
+ statusMarker: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
17138
+ /* @__PURE__ */ jsx(TileVisibilityBadge, { doc }),
17139
+ showStatusMarker && /* @__PURE__ */ jsx(TileStatusBadge, { doc, status })
17140
+ ] }),
17141
+ offlineMarker: isOffline ? /* @__PURE__ */ jsx(DocumentSyncGlyph, { docExtId: doc.external_id, size: 24, bgClassName: "bg-primary" }) : void 0,
17142
+ entity: /* @__PURE__ */ jsx(
17143
+ DocumentEntity,
17144
+ {
17145
+ doc,
17146
+ labelMaxWidth: "max-w-full",
17147
+ noClamp: true,
17148
+ labelClassName: "whitespace-normal line-clamp-2 break-words",
17149
+ viewing: isViewing,
17150
+ tooltipContent: /* @__PURE__ */ jsx(FileTooltipContent, { doc, modifiedBy, tagNameById }),
17151
+ className: cn(
17152
+ "w-full min-w-0 max-w-full bg-background/90 backdrop-blur-sm",
17153
+ density === "list" ? "text-xs" : void 0
17154
+ ),
17155
+ onClick: (e) => {
17156
+ e.stopPropagation();
17157
+ onOpen(doc);
17158
+ }
17159
+ }
17160
+ ),
17161
+ tooltip,
17162
+ menuItems,
17163
+ rootRef: setNodeRef,
17164
+ rootProps: { ...listeners2, ...attributes },
17165
+ onOpen: () => onOpen(doc),
17166
+ onSelect: (e) => onSelect(doc, e),
17167
+ onToggle: (e) => onToggle(doc, e),
17168
+ onCommitRename: (name) => onCommitRename(doc, name),
17169
+ onCancelRename
17170
+ }
17171
+ );
17172
+ });
17173
+ var FolderTile = memo(function FolderTile2({
17174
+ folder,
17175
+ density,
17176
+ editing,
17177
+ selected,
17178
+ partiallySelected,
17179
+ documents,
17180
+ onOpen,
17181
+ onToggleSelect,
17182
+ onCommitName,
17183
+ onCancelName,
17184
+ onStartRename,
17185
+ onDownload,
17186
+ onMove,
17187
+ onDelete
17188
+ }) {
17189
+ const { setNodeRef, isOver } = useDroppable({
17190
+ id: `folder:${folder.path.join("/")}`,
17191
+ data: { type: "folder", path: folder.path }
17192
+ });
17193
+ const { startItemsForFolder } = useFilesSupport();
17194
+ const tasks = useProcessingTasks();
17195
+ const processingCount = useMemo(
17196
+ () => documents.filter(
17197
+ (doc) => isActiveDocumentStatus(tasks[doc.external_id]?.status ?? doc.status ?? "")
17198
+ ).length,
17199
+ [documents, tasks]
17200
+ );
17201
+ const isEmpty = folder.count === 0;
17202
+ const iconSize = density === "list" ? 34 : 48;
17203
+ const menuItems = [
17204
+ ...onStartRename ? [{ id: "rename", label: "Rename", icon: Pencil, onSelect: () => onStartRename(folder) }] : [],
17205
+ ...!isEmpty ? startMenuItem(startItemsForFolder?.(folder.path.join("/")) ?? []) : [],
17206
+ ...onDownload && !isEmpty ? [{ id: "download", label: "Download", icon: Download, onSelect: () => onDownload(folder) }] : [],
17207
+ ...onMove && !isEmpty ? [{ id: "move", label: "Move to\u2026", icon: FolderInput, onSelect: () => onMove(folder) }] : [],
17208
+ ...onDelete ? [
17209
+ {
17210
+ id: "delete",
17211
+ label: "Delete",
17212
+ icon: Trash2,
17213
+ onSelect: () => onDelete(folder),
17214
+ destructive: true,
17215
+ separatorBefore: true
17216
+ }
17217
+ ] : []
17218
+ ];
17219
+ const displayName = folder.path.length === 1 ? formatFolderSegment(folder.name) : folder.name;
17220
+ const providerIcon = folder.path.length === 1 && hasFolderProviderIcon(folder.name) ? /* @__PURE__ */ jsx(
17221
+ "span",
17222
+ {
17223
+ className: "flex items-center justify-center drop-shadow-sm",
17224
+ style: { width: iconSize, height: iconSize },
17225
+ children: /* @__PURE__ */ jsx(FolderProviderIcon, { folderPath: folder.name, className: "h-full w-full" })
17226
+ }
17227
+ ) : void 0;
17228
+ return /* @__PURE__ */ jsx(
17229
+ ExplorerFolderTile,
17230
+ {
17231
+ name: folder.name,
17232
+ displayName: displayName === "" ? null : displayName,
17233
+ path: folder.path,
17234
+ count: folder.count,
17235
+ density,
17236
+ editing,
17237
+ selected,
17238
+ partiallySelected,
17239
+ processingCount,
17240
+ isOver,
17241
+ icon: providerIcon,
17242
+ menuItems,
17243
+ rootRef: setNodeRef,
17244
+ onOpen: () => onOpen(folder),
17245
+ onToggleSelect: onToggleSelect ? (e) => onToggleSelect(folder, e) : void 0,
17246
+ onCommitName: (name) => onCommitName?.(folder, name),
17247
+ onCancelName
17248
+ }
17249
+ );
17250
+ });
17251
+ var GRID_GAP = 12;
17252
+ var SCROLL_PADDING_X = 32;
17253
+ var PAGE_ASPECT = 1.3;
17254
+ var TILE_MIN = 88;
17255
+ var TILE_MAX = 232;
17256
+ var TILE_DEFAULT = 200;
17257
+ var COMPACT_BELOW = 132;
17258
+ var NO_DOCUMENTS = [];
17259
+ function useFilesWorkspace() {
17260
+ const { workspaceId, userId, live } = useConnectedWorkspace();
17261
+ const { members, role } = useConnectedWorkspaceRole();
17262
+ const { data: workspaces } = useWorkspaces({ enabled: live });
17263
+ const workspaceName = workspaces?.find((w) => w.external_id === workspaceId)?.name;
17264
+ return {
17265
+ workspaceId: workspaceId ?? "",
17266
+ workspaceName,
17267
+ userId,
17268
+ role: role ?? null,
17269
+ members
17270
+ };
17271
+ }
17272
+ var ConnectedFilesExplorer = forwardRef(function ConnectedFilesExplorer2({
17273
+ documents,
17274
+ isLoading,
17275
+ canUpload,
17276
+ selectedIds,
17277
+ onSelectionChange,
17278
+ onOrderedFilesChange,
17279
+ onOpenDocument,
17280
+ onUploadFiles,
17281
+ folderPath,
17282
+ onFolderPathChange,
17283
+ search = "",
17284
+ sort,
17285
+ sortReversed,
17286
+ tileSize,
17287
+ onMoveSelected,
17288
+ onFolderMove,
17289
+ onOpenInReview,
17290
+ emptyWorkspace
17291
+ }, ref) {
17292
+ const { confirm, notify: notify14 } = useBlocksEnvironment();
17293
+ const currentPath = folderPath;
17294
+ const density = tileSize < COMPACT_BELOW ? "list" : "grid";
17295
+ const [containerWidth, setContainerWidth] = useState(0);
17296
+ const [activeDragId, setActiveDragId] = useState(null);
17297
+ const [isFileDragOver, setIsFileDragOver] = useState(false);
17298
+ const [renamingId, setRenamingId] = useState(null);
17299
+ const scrollRef = useRef(null);
17300
+ const anchorRef = useRef(null);
17301
+ const dragIdsRef = useRef([]);
17302
+ const fileDragDepth = useRef(0);
17303
+ const marqueeOverlayRef = useRef(null);
17304
+ const marqueeRef = useRef(
17305
+ null
17306
+ );
17307
+ const didMarqueeRef = useRef(false);
17308
+ const updateDocument = useUpdateDocuments();
17309
+ const deleteDocuments = useDeleteDocuments();
17310
+ const renameFolder = useRenameFolder();
17311
+ const { workspaceId: wsId, workspaceName, userId, role, members } = useFilesWorkspace();
17312
+ const { downloadDocuments, downloadDocument } = useDownloadDocuments({ workspaceName });
17313
+ const { data: workspaceTags } = useTags();
17314
+ const userById = useMemo(() => {
17315
+ const map = /* @__PURE__ */ new Map();
17316
+ for (const member of members) map.set(member.user.external_id, member.user);
17317
+ return map;
17318
+ }, [members]);
17319
+ const tagNameById = useMemo(() => {
17320
+ const map = /* @__PURE__ */ new Map();
17321
+ for (const tag of workspaceTags ?? []) map.set(tag.external_id, tag.name);
17322
+ return map;
17323
+ }, [workspaceTags]);
17324
+ const pendingFolders = usePendingFolders(wsId);
17325
+ const addPendingFolder = useFilesStore((state) => state.addPendingFolder);
17326
+ const removePendingFolder = useFilesStore((state) => state.removePendingFolder);
17327
+ const [editingFolderPath, setEditingFolderPath] = useState(null);
17328
+ const isSearching = search.trim().length > 0;
17329
+ const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);
17330
+ const selectedIdsRef = useRef(selectedIds);
17331
+ selectedIdsRef.current = selectedIds;
17332
+ const { folders, files } = useMemo(() => {
17333
+ const result = isSearching ? {
17334
+ folders: [],
17335
+ files: searchExplorerDocuments(documents, search, sort)
17336
+ } : buildExplorerEntries(documents, currentPath, sort, [...pendingFolders]);
17337
+ if (sortReversed) result.files = [...result.files].reverse();
17338
+ return result;
17339
+ }, [documents, currentPath, sort, isSearching, search, sortReversed, pendingFolders]);
17340
+ const entries = useMemo(
17341
+ () => [...folders, ...files.map((doc) => ({ kind: "file", doc }))],
17342
+ [folders, files]
17343
+ );
17344
+ const orderedFileIds = useMemo(() => files.map((d) => d.external_id), [files]);
17345
+ const folderDocs = useMemo(() => {
17346
+ const map = /* @__PURE__ */ new Map();
17347
+ for (const f of folders) map.set(f.path.join("/"), collectFolderDocs(documents, f.path));
17348
+ return map;
17349
+ }, [folders, documents]);
17350
+ const folderSelection = useMemo(() => {
17351
+ const map = /* @__PURE__ */ new Map();
17352
+ for (const [key, docs] of folderDocs) {
17353
+ const sel = docs.reduce((n, d) => selectedSet.has(d.external_id) ? n + 1 : n, 0);
17354
+ map.set(key, { all: docs.length > 0 && sel === docs.length, some: sel > 0 });
17355
+ }
17356
+ return map;
17357
+ }, [folderDocs, selectedSet]);
17358
+ const contentWidth = Math.max(0, containerWidth - SCROLL_PADDING_X);
17359
+ const columnCount = Math.max(
17360
+ 1,
17361
+ Math.floor((contentWidth + GRID_GAP) / (tileSize + GRID_GAP)) || 1
17362
+ );
17363
+ const colWidth = contentWidth > 0 ? (contentWidth - (columnCount - 1) * GRID_GAP) / columnCount : tileSize;
17364
+ const tileHeight = Math.max(120, Math.round(colWidth * PAGE_ASPECT));
17365
+ const rowHeight = tileHeight + GRID_GAP;
17366
+ const rowCount = Math.ceil(entries.length / columnCount);
17367
+ const rowVirtualizer = useVirtualizer({
17368
+ count: rowCount,
17369
+ getScrollElement: () => scrollRef.current,
17370
+ estimateSize: () => rowHeight,
17371
+ overscan: 3
17372
+ });
17373
+ const virtualRows = rowVirtualizer.getVirtualItems();
17374
+ const firstRow = virtualRows.length > 0 ? virtualRows[0].index : 0;
17375
+ const lastRow = virtualRows.length > 0 ? virtualRows[virtualRows.length - 1].index : -1;
17376
+ const visibleFileIds = useMemo(() => {
17377
+ const ids = [];
17378
+ for (let r = firstRow; r <= lastRow; r++) {
17379
+ const start = r * columnCount;
17380
+ for (let c = 0; c < columnCount; c++) {
17381
+ const entry = entries[start + c];
17382
+ if (entry?.kind === "file") ids.push(entry.doc.external_id);
17383
+ }
17384
+ }
17385
+ return ids;
17386
+ }, [firstRow, lastRow, columnCount, entries]);
17387
+ const thumbnailsById = useDocumentThumbnails(visibleFileIds);
17388
+ useEffect(() => {
17389
+ rowVirtualizer.measure();
17390
+ }, [rowVirtualizer, columnCount, rowHeight, entries.length]);
17391
+ const isWholeWorkspaceEmpty = !isLoading && documents.length === 0 && canUpload && emptyWorkspace !== void 0;
17392
+ useLayoutEffect(() => {
17393
+ const el = scrollRef.current;
17394
+ if (!el) return;
17395
+ const apply = () => {
17396
+ const next = Math.round(el.clientWidth);
17397
+ setContainerWidth((prev) => prev === next ? prev : next);
17398
+ };
17399
+ apply();
17400
+ const ro = new ResizeObserver(apply);
17401
+ ro.observe(el);
17402
+ return () => ro.disconnect();
17403
+ }, [isWholeWorkspaceEmpty]);
17404
+ useEffect(() => {
17405
+ anchorRef.current = null;
17406
+ if (scrollRef.current) scrollRef.current.scrollTop = 0;
17407
+ }, [currentPath, search]);
17408
+ useEffect(() => {
17409
+ const MARQUEE_THRESHOLD = 5;
17410
+ let rafId = 0;
17411
+ let cur = null;
17412
+ const paint = () => {
17413
+ rafId = 0;
17414
+ const el = marqueeOverlayRef.current;
17415
+ if (!el || !cur) return;
17416
+ el.style.display = "block";
17417
+ el.style.left = `${cur.left}px`;
17418
+ el.style.top = `${cur.top}px`;
17419
+ el.style.width = `${cur.right - cur.left}px`;
17420
+ el.style.height = `${cur.bottom - cur.top}px`;
17421
+ };
17422
+ const onMove = (e) => {
17423
+ const m = marqueeRef.current;
17424
+ if (!m) return;
17425
+ if (!didMarqueeRef.current && Math.hypot(e.clientX - m.x, e.clientY - m.y) < MARQUEE_THRESHOLD)
17426
+ return;
17427
+ didMarqueeRef.current = true;
17428
+ const b = scrollRef.current?.getBoundingClientRect();
17429
+ const px = b ? Math.max(b.left, Math.min(e.clientX, b.right)) : e.clientX;
17430
+ const py = b ? Math.max(b.top, Math.min(e.clientY, b.bottom)) : e.clientY;
17431
+ cur = {
17432
+ left: Math.min(m.x, px),
17433
+ top: Math.min(m.y, py),
17434
+ right: Math.max(m.x, px),
17435
+ bottom: Math.max(m.y, py)
17436
+ };
17437
+ if (!rafId) rafId = requestAnimationFrame(paint);
17438
+ };
17439
+ const onUp = () => {
17440
+ const m = marqueeRef.current;
17441
+ if (!m) return;
17442
+ marqueeRef.current = null;
17443
+ if (rafId) cancelAnimationFrame(rafId);
17444
+ rafId = 0;
17445
+ const el = marqueeOverlayRef.current;
17446
+ if (el) el.style.display = "none";
17447
+ if (!didMarqueeRef.current || !cur) return;
17448
+ const box = cur;
17449
+ const hit = [];
17450
+ scrollRef.current?.querySelectorAll("[data-doc-id]").forEach((node) => {
17451
+ const r = node.getBoundingClientRect();
17452
+ if (r.left < box.right && r.right > box.left && r.top < box.bottom && r.bottom > box.top) {
17453
+ const id = node.getAttribute("data-doc-id");
17454
+ if (id) hit.push(id);
17455
+ }
17456
+ });
17457
+ onSelectionChange(m.additive ? Array.from(/* @__PURE__ */ new Set([...m.base, ...hit])) : hit);
17458
+ };
17459
+ window.addEventListener("pointermove", onMove);
17460
+ window.addEventListener("pointerup", onUp);
17461
+ return () => {
17462
+ window.removeEventListener("pointermove", onMove);
17463
+ window.removeEventListener("pointerup", onUp);
17464
+ if (rafId) cancelAnimationFrame(rafId);
17465
+ };
17466
+ }, [onSelectionChange]);
17467
+ const handleScrollPointerDown = useCallback((e) => {
17468
+ if (e.button !== 0) return;
17469
+ const el = e.target;
17470
+ if (el.closest(
17471
+ '[data-testid="explorer-file"],[data-testid="explorer-folder"],button,input,select,a,[role="button"]'
17472
+ )) {
17473
+ return;
17474
+ }
17475
+ const additive = e.shiftKey || e.metaKey || e.ctrlKey;
17476
+ marqueeRef.current = {
17477
+ x: e.clientX,
17478
+ y: e.clientY,
17479
+ additive,
17480
+ base: additive ? selectedIdsRef.current : []
17481
+ };
17482
+ didMarqueeRef.current = false;
17483
+ }, []);
17484
+ const openFolder = useCallback(
17485
+ (folder) => onFolderPathChange(folder.path),
17486
+ [onFolderPathChange]
17487
+ );
17488
+ const handleNewFolder = useCallback(() => {
17489
+ if (!wsId) return;
17490
+ const siblings = new Set(folders.map((f) => f.name.toLowerCase()));
17491
+ const base = "New folder";
17492
+ let name = base;
17493
+ let i = 2;
17494
+ while (siblings.has(name.toLowerCase())) name = `${base} ${i++}`;
17495
+ const path = joinFolderPath([...currentPath, name]);
17496
+ if (!path) return;
17497
+ addPendingFolder(wsId, path);
17498
+ setEditingFolderPath(path);
17499
+ }, [wsId, folders, currentPath, addPendingFolder]);
17500
+ const lastOrderedRef = useRef([]);
17501
+ useEffect(() => {
17502
+ const prev = lastOrderedRef.current;
17503
+ if (prev.length === orderedFileIds.length && prev.every((id, i) => id === orderedFileIds[i])) {
17504
+ return;
17505
+ }
17506
+ lastOrderedRef.current = orderedFileIds;
17507
+ onOrderedFilesChange?.(orderedFileIds);
17508
+ }, [onOrderedFilesChange, orderedFileIds]);
17509
+ const scrollToDocument = useCallback(
17510
+ (docId) => {
17511
+ const index = entries.findIndex((e) => e.kind === "file" && e.doc.external_id === docId);
17512
+ if (index < 0) return;
17513
+ rowVirtualizer.scrollToIndex(Math.floor(index / columnCount), { align: "center" });
17514
+ },
17515
+ [entries, columnCount, rowVirtualizer]
17516
+ );
17517
+ useImperativeHandle(ref, () => ({ triggerNewFolder: handleNewFolder, scrollToDocument }), [
17518
+ handleNewFolder,
17519
+ scrollToDocument
17520
+ ]);
17521
+ const commitFolderName = useCallback(
17522
+ async (folder, rawName) => {
17523
+ setEditingFolderPath(null);
17524
+ const name = rawName.trim();
17525
+ if (!wsId || !name || name === folder.name) return;
17526
+ const siblings = new Set(folders.filter((f) => f !== folder).map((f) => f.name.toLowerCase()));
17527
+ if (siblings.has(name.toLowerCase())) {
17528
+ notify14(`A folder named "${name}" already exists here.`, "error");
17529
+ return;
17530
+ }
17531
+ const oldPath = joinFolderPath(folder.path);
17532
+ const newPrefix = [...folder.path.slice(0, -1), name];
17533
+ const newPath = joinFolderPath(newPrefix);
17534
+ const docs = collectFolderDocs(documents, folder.path);
17535
+ try {
17536
+ if (await renameFolder(docs, folder.path, newPrefix) > 0) {
17537
+ notify14(`Renamed folder to "${name}"`, "success");
17538
+ }
17539
+ } catch {
17540
+ notify14("Failed to rename folder. Please try again.", "error");
17541
+ return;
17542
+ }
17543
+ if (oldPath) removePendingFolder(wsId, oldPath);
17544
+ if (docs.length === 0 && newPath) addPendingFolder(wsId, newPath);
17545
+ },
17546
+ [wsId, folders, documents, renameFolder, notify14, addPendingFolder, removePendingFolder]
17547
+ );
17548
+ const cancelFolderName = useCallback(() => setEditingFolderPath(null), []);
17549
+ const clearSelection = useCallback(() => {
17550
+ if (selectedIdsRef.current.length > 0) onSelectionChange([]);
17551
+ anchorRef.current = null;
17552
+ }, [onSelectionChange]);
17553
+ const handleSelect = useCallback(
17554
+ (doc, e) => {
17555
+ const id = doc.external_id;
17556
+ if (e.shiftKey) {
17557
+ const from = anchorRef.current ?? id;
17558
+ const a = orderedFileIds.indexOf(from);
17559
+ const b = orderedFileIds.indexOf(id);
17560
+ if (a !== -1 && b !== -1) {
17561
+ anchorRef.current = from;
17562
+ const [lo, hi] = a < b ? [a, b] : [b, a];
17563
+ onSelectionChange(orderedFileIds.slice(lo, hi + 1));
17564
+ return;
17565
+ }
17566
+ }
17567
+ if (e.metaKey || e.ctrlKey) {
17568
+ onSelectionChange(
17569
+ selectedSet.has(id) ? selectedIds.filter((s) => s !== id) : [...selectedIds, id]
17570
+ );
17571
+ anchorRef.current = id;
17572
+ return;
17573
+ }
17574
+ onSelectionChange([id]);
17575
+ anchorRef.current = id;
17576
+ },
17577
+ [onSelectionChange, orderedFileIds, selectedIds, selectedSet]
17578
+ );
17579
+ const handleToggle = useCallback(
17580
+ (doc, e) => {
17581
+ e.stopPropagation();
17582
+ const id = doc.external_id;
17583
+ onSelectionChange(
17584
+ selectedSet.has(id) ? selectedIds.filter((s) => s !== id) : [...selectedIds, id]
17585
+ );
17586
+ anchorRef.current = id;
17587
+ },
17588
+ [onSelectionChange, selectedIds, selectedSet]
17589
+ );
17590
+ const handleToggleFolder = useCallback(
17591
+ (folder, e) => {
17592
+ e.stopPropagation();
17593
+ const ids = collectFolderDocs(documents, folder.path).map((d) => d.external_id);
17594
+ if (ids.length === 0) return;
17595
+ if (ids.every((id) => selectedSet.has(id))) {
17596
+ const remove = new Set(ids);
17597
+ onSelectionChange(selectedIds.filter((id) => !remove.has(id)));
17598
+ } else {
17599
+ onSelectionChange(Array.from(/* @__PURE__ */ new Set([...selectedIds, ...ids])));
17600
+ }
17601
+ anchorRef.current = null;
17602
+ },
17603
+ [documents, onSelectionChange, selectedIds, selectedSet]
17604
+ );
17605
+ const handleOpenFile = useCallback(
17606
+ (doc) => onOpenDocument(doc.external_id),
17607
+ [onOpenDocument]
17608
+ );
17609
+ const handleOpenInReview = useMemo(
17610
+ () => onOpenInReview ? (doc) => onOpenInReview(doc.external_id) : void 0,
17611
+ [onOpenInReview]
17612
+ );
17613
+ const startRename = useCallback((doc) => setRenamingId(doc.external_id), []);
17614
+ const cancelRename = useCallback(() => setRenamingId(null), []);
17615
+ const commitRename = useCallback(
17616
+ async (doc, name) => {
17617
+ setRenamingId(null);
17618
+ try {
17619
+ await updateDocument.mutateAsync({
17620
+ documents: [
17621
+ { external_id: doc.external_id, doc_metadata: { ...doc.doc_metadata, title: name } }
17622
+ ]
17623
+ });
17624
+ notify14(`Renamed to "${name}"`, "success");
17625
+ } catch {
17626
+ notify14("Failed to rename. Please try again.", "error");
17627
+ }
17628
+ },
17629
+ [updateDocument, notify14]
17630
+ );
17631
+ useEffect(() => {
17632
+ const onKey = (e) => {
17633
+ if (e.key !== "F2" || renamingId) return;
17634
+ const target = e.target;
17635
+ if (target && /^(input|textarea|select)$/i.test(target.tagName)) return;
17636
+ if (selectedIdsRef.current.length === 1) setRenamingId(selectedIdsRef.current[0]);
17637
+ };
17638
+ window.addEventListener("keydown", onKey);
17639
+ return () => window.removeEventListener("keydown", onKey);
17640
+ }, [renamingId]);
17641
+ const handleDownload = useCallback(
17642
+ (doc) => void downloadDocument(doc),
17643
+ [downloadDocument]
17644
+ );
17645
+ const handleMove = useMemo(
17646
+ () => onMoveSelected ? (doc) => {
17647
+ onSelectionChange([doc.external_id]);
17648
+ onMoveSelected();
17649
+ } : void 0,
17650
+ [onSelectionChange, onMoveSelected]
17651
+ );
17652
+ const handleDelete = useCallback(
17653
+ async (doc) => {
17654
+ const name = getExplorerDisplayName(doc);
17655
+ const confirmed = await confirm({
17656
+ message: `Delete "${name}"? ${RECENTLY_DELETED_HINT}`,
17657
+ title: "Delete file"
17658
+ });
17659
+ if (!confirmed) return;
17660
+ try {
17661
+ await deleteDocuments.mutateAsync([doc.external_id]);
17662
+ onSelectionChange(selectedIdsRef.current.filter((id) => id !== doc.external_id));
17663
+ notify14(`Deleted "${name}"`, "success");
17664
+ } catch {
17665
+ notify14("Failed to delete. Please try again.", "error");
17666
+ }
17667
+ },
17668
+ [confirm, deleteDocuments, onSelectionChange, notify14]
17669
+ );
17670
+ const startFolderRename = useCallback(
17671
+ (folder) => setEditingFolderPath(folder.path.join("/")),
17672
+ []
17673
+ );
17674
+ const handleFolderDownload = useCallback(
17675
+ (folder) => {
17676
+ const docs = collectFolderDocs(documents, folder.path);
17677
+ if (docs.length > 0) void downloadDocuments(docs);
17678
+ },
17679
+ [documents, downloadDocuments]
17680
+ );
17681
+ const handleFolderMove = useMemo(() => {
17682
+ if (onFolderMove) return onFolderMove;
17683
+ if (!onMoveSelected) return void 0;
17684
+ return (folder) => {
17685
+ const ids = collectFolderDocs(documents, folder.path).map((d) => d.external_id);
17686
+ if (ids.length === 0) return;
17687
+ onSelectionChange(ids);
17688
+ onMoveSelected();
17689
+ };
17690
+ }, [documents, onFolderMove, onMoveSelected, onSelectionChange]);
17691
+ const handleFolderDelete = useCallback(
17692
+ async (folder) => {
17693
+ const docs = collectFolderDocs(documents, folder.path);
17694
+ if (docs.length === 0) {
17695
+ const path = joinFolderPath(folder.path);
17696
+ if (wsId && path) removePendingFolder(wsId, path);
17697
+ return;
17698
+ }
17699
+ const confirmed = await confirm({
17700
+ message: `Delete folder "${folder.name}" and its ${docs.length} item${docs.length === 1 ? "" : "s"}? ${RECENTLY_DELETED_HINT_PLURAL}`,
17701
+ title: "Delete folder"
17702
+ });
17703
+ if (!confirmed) return;
17704
+ try {
17705
+ const ids = docs.map((d) => d.external_id);
17706
+ await deleteDocuments.mutateAsync(ids);
17707
+ const removed = new Set(ids);
17708
+ onSelectionChange(selectedIdsRef.current.filter((id) => !removed.has(id)));
17709
+ notify14(`Deleted folder "${folder.name}"`, "success");
17710
+ } catch {
17711
+ notify14("Failed to delete folder. Please try again.", "error");
17712
+ }
17713
+ },
17714
+ [confirm, documents, deleteDocuments, onSelectionChange, removePendingFolder, wsId, notify14]
17715
+ );
17716
+ const canDeleteFolder = useCallback(
17717
+ (folder) => canUpload && collectFolderDocs(documents, folder.path).every(
17718
+ (doc) => canDeleteDocument(doc, userId, role)
17719
+ ),
17720
+ [canUpload, documents, userId, role]
17721
+ );
17722
+ const handleBackgroundClick = useCallback(
17723
+ (e) => {
17724
+ if (didMarqueeRef.current) {
17725
+ didMarqueeRef.current = false;
17726
+ return;
17727
+ }
17728
+ if (!e.target.closest('[data-testid^="explorer-"]')) clearSelection();
17729
+ },
17730
+ [clearSelection]
17731
+ );
17732
+ const moveDocs = useCallback(
17733
+ async (ids, folder) => {
17734
+ if (ids.length === 0) return;
17735
+ try {
17736
+ await updateDocument.mutateAsync({
17737
+ documents: ids.map((external_id) => ({ external_id, folder }))
17738
+ });
17739
+ clearSelection();
17740
+ notify14(
17741
+ `Moved ${ids.length} item${ids.length === 1 ? "" : "s"} to "${folder ?? "Home"}"`,
17742
+ "success"
17743
+ );
17744
+ } catch {
17745
+ notify14("Failed to move. Please try again.", "error");
17746
+ }
17747
+ },
17748
+ [updateDocument, clearSelection, notify14]
17749
+ );
17750
+ const sensors = useSensors(
17751
+ useSensor(MouseSensor, { activationConstraint: { distance: 6 } }),
17752
+ useSensor(TouchSensor, { activationConstraint: { delay: 150, tolerance: 8 } })
17753
+ );
17754
+ const collisionDetection = useCallback((args) => {
17755
+ const hits = pointerWithin(args);
17756
+ return hits.length > 0 ? hits : rectIntersection(args);
17757
+ }, []);
17758
+ const handleDragStart = useCallback(
17759
+ (event) => {
17760
+ const docId = event.active.data.current?.docId;
17761
+ if (!docId) return;
17762
+ setActiveDragId(docId);
17763
+ if (!selectedIdsRef.current.includes(docId)) {
17764
+ onSelectionChange([docId]);
17765
+ dragIdsRef.current = [docId];
17766
+ } else {
17767
+ dragIdsRef.current = selectedIdsRef.current;
17768
+ }
17769
+ },
17770
+ [onSelectionChange]
17771
+ );
17772
+ const handleDragEnd = useCallback(
17773
+ (event) => {
17774
+ setActiveDragId(null);
17775
+ const data = event.over?.data.current;
17776
+ if (data?.type === "folder" && data.path) {
17777
+ const targetFolder = joinFolderPath(data.path);
17778
+ const moving = dragIdsRef.current.filter((id) => {
17779
+ const doc = documents.find((d) => d.external_id === id);
17780
+ return doc && (doc.folder ?? null) !== targetFolder;
17781
+ });
17782
+ void moveDocs(moving, targetFolder);
17783
+ }
17784
+ },
17785
+ [documents, moveDocs]
17786
+ );
17787
+ const handleNativeDragOver = useCallback(
17788
+ (e) => {
17789
+ if (!canUpload) return;
17790
+ if (e.dataTransfer.types.includes("Files")) {
17791
+ e.preventDefault();
17792
+ e.dataTransfer.dropEffect = "copy";
17793
+ }
17794
+ },
17795
+ [canUpload]
17796
+ );
17797
+ const handleNativeDragEnter = useCallback(
17798
+ (e) => {
17799
+ if (!canUpload || !e.dataTransfer.types.includes("Files")) return;
17800
+ fileDragDepth.current += 1;
17801
+ setIsFileDragOver(true);
17802
+ },
17803
+ [canUpload]
17804
+ );
17805
+ const handleNativeDragLeave = useCallback(() => {
17806
+ fileDragDepth.current = Math.max(0, fileDragDepth.current - 1);
17807
+ if (fileDragDepth.current === 0) setIsFileDragOver(false);
17808
+ }, []);
17809
+ const handleNativeDrop = useCallback(
17810
+ (e) => {
17811
+ fileDragDepth.current = 0;
17812
+ setIsFileDragOver(false);
17813
+ if (!canUpload) return;
17814
+ const dropped = Array.from(e.dataTransfer.files ?? []);
17815
+ if (dropped.length > 0) {
17816
+ e.preventDefault();
17817
+ onUploadFiles(dropped, joinFolderPath(currentPath));
17818
+ }
17819
+ },
17820
+ [canUpload, currentPath, onUploadFiles]
17821
+ );
17822
+ const activeDoc = activeDragId ? documents.find((d) => d.external_id === activeDragId) : null;
17823
+ const dragCount = dragIdsRef.current.length || 1;
17824
+ if (isWholeWorkspaceEmpty) return /* @__PURE__ */ jsx(Fragment, { children: emptyWorkspace });
17825
+ return /* @__PURE__ */ jsxs(
17826
+ DndContext,
17827
+ {
17828
+ sensors,
17829
+ collisionDetection,
17830
+ onDragStart: handleDragStart,
17831
+ onDragEnd: handleDragEnd,
17832
+ onDragCancel: () => setActiveDragId(null),
17833
+ children: [
17834
+ /* @__PURE__ */ jsx(ExplorerFrame, { "data-testid": "documents-explorer", children: /* @__PURE__ */ jsxs(ExplorerStage, { children: [
17835
+ /* @__PURE__ */ jsx(
17836
+ ExplorerScroller,
17837
+ {
17838
+ ref: scrollRef,
17839
+ onClick: handleBackgroundClick,
17840
+ onPointerDown: handleScrollPointerDown,
17841
+ onDragEnter: handleNativeDragEnter,
17842
+ onDragOver: handleNativeDragOver,
17843
+ onDragLeave: handleNativeDragLeave,
17844
+ onDrop: handleNativeDrop,
17845
+ "data-testid": "explorer-scroll",
17846
+ children: isLoading && documents.length === 0 ? /* @__PURE__ */ jsx(ExplorerLoadingTiles, {}) : entries.length === 0 ? /* @__PURE__ */ jsx(
17847
+ ExplorerEmpty,
17848
+ {
17849
+ title: isSearching ? `No files match "${search.trim()}"` : "This folder is empty",
17850
+ description: isSearching ? "Try a different search." : canUpload ? "Drop files here or use Upload to add documents." : "No documents here yet."
17851
+ }
17852
+ ) : containerWidth === 0 ? (
17853
+ // Not measured yet: rendering now would flash one stretched column.
17854
+ /* @__PURE__ */ jsx(ExplorerLoadingTiles, {})
17855
+ ) : /* @__PURE__ */ jsx(
17856
+ "div",
17857
+ {
17858
+ style: {
17859
+ height: rowVirtualizer.getTotalSize(),
17860
+ position: "relative",
17861
+ width: "100%"
17862
+ },
17863
+ children: virtualRows.map((virtualRow) => {
17864
+ const start = virtualRow.index * columnCount;
17865
+ const rowEntries = entries.slice(start, start + columnCount);
17866
+ return /* @__PURE__ */ jsx(
17867
+ "div",
17868
+ {
17869
+ style: {
17870
+ position: "absolute",
17871
+ top: 0,
17872
+ left: 0,
17873
+ width: "100%",
17874
+ height: tileHeight,
17875
+ transform: `translateY(${virtualRow.start}px)`,
17876
+ display: "grid",
17877
+ gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`,
17878
+ columnGap: GRID_GAP
17879
+ },
17880
+ children: rowEntries.map((entry) => {
17881
+ if (entry.kind === "folder") {
17882
+ const key = entry.path.join("/");
17883
+ return /* @__PURE__ */ jsx(
17884
+ FolderTile,
17885
+ {
17886
+ folder: entry,
17887
+ density,
17888
+ editing: editingFolderPath === key,
17889
+ selected: folderSelection.get(key)?.all,
17890
+ partiallySelected: folderSelection.get(key)?.some,
17891
+ documents: folderDocs.get(key) ?? NO_DOCUMENTS,
17892
+ onOpen: openFolder,
17893
+ onToggleSelect: handleToggleFolder,
17894
+ onCommitName: commitFolderName,
17895
+ onCancelName: cancelFolderName,
17896
+ onStartRename: canUpload ? startFolderRename : void 0,
17897
+ onDownload: handleFolderDownload,
17898
+ onMove: canUpload ? handleFolderMove : void 0,
17899
+ onDelete: canDeleteFolder(entry) ? handleFolderDelete : void 0
17900
+ },
17901
+ `folder:${key}`
17902
+ );
17903
+ }
17904
+ const doc = entry.doc;
17905
+ return /* @__PURE__ */ jsx(
17906
+ FileTile,
17907
+ {
17908
+ doc,
17909
+ density,
17910
+ selected: selectedSet.has(doc.external_id),
17911
+ dragging: activeDragId === doc.external_id,
17912
+ dndActive: activeDragId !== null,
17913
+ renaming: renamingId === doc.external_id,
17914
+ canDelete: canDeleteDocument(doc, userId, role),
17915
+ thumbnailUrl: thumbnailsById.get(doc.external_id),
17916
+ modifiedBy: userById.get(
17917
+ doc.updated_by_ext_id ?? doc.created_by_ext_id
17918
+ ),
17919
+ tagNameById,
17920
+ onSelect: handleSelect,
17921
+ onToggle: handleToggle,
17922
+ onOpen: handleOpenFile,
17923
+ onReview: handleOpenInReview,
17924
+ onStartRename: startRename,
17925
+ onCommitRename: commitRename,
17926
+ onCancelRename: cancelRename,
17927
+ onDownload: handleDownload,
17928
+ onMove: canUpload ? handleMove : void 0,
17929
+ onDelete: handleDelete
17930
+ },
17931
+ doc.external_id
17932
+ );
17933
+ })
17934
+ },
17935
+ virtualRow.key
17936
+ );
17937
+ })
17938
+ }
17939
+ )
17940
+ }
17941
+ ),
17942
+ /* @__PURE__ */ jsx(ExplorerMarquee, { ref: marqueeOverlayRef, "data-testid": "explorer-marquee" }),
17943
+ isFileDragOver && /* @__PURE__ */ jsx(
17944
+ ExplorerDropOverlay,
17945
+ {
17946
+ label: `Drop to upload${currentPath.length > 0 ? ` to "${currentPath[currentPath.length - 1]}"` : ""}`
17947
+ }
17948
+ )
17949
+ ] }) }),
17950
+ /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: activeDoc ? /* @__PURE__ */ jsx(DragPreview, { doc: activeDoc, count: dragCount }) : null })
17951
+ ]
17952
+ }
17953
+ );
17954
+ });
17955
+ function DragPreview({ doc, count }) {
17956
+ const Icon = getFileTypeIcon(doc.file_name, { mime: doc.file_type });
17957
+ return /* @__PURE__ */ jsx(
17958
+ ExplorerDragPreview,
17959
+ {
17960
+ icon: /* @__PURE__ */ jsx(Icon, { size: 20 }),
17961
+ title: getDocumentTitle(doc, doc.file_name ?? "Untitled"),
17962
+ count
17963
+ }
17964
+ );
17965
+ }
16254
17966
  function ActiveChat({ agentId, workspaceId }) {
16255
17967
  const chat = useArbiChat({
16256
17968
  agentId,
@@ -16318,6 +18030,6 @@ function ConnectedAgentChatPanel({
16318
18030
  ] });
16319
18031
  }
16320
18032
 
16321
- export { AddAddonDialog, ArbiAppProvider, BACKFILL_OPTIONS, BillingProjectEntity, BillingSupportProvider, CAP_REACHED_MESSAGE, ChangePlanDialog, ConnectedAccountSettings, ConnectedAgentChatPanel, ConnectedAgentCreator, ConnectedAppsSettings, ConnectedCalendar, ConnectedDangerZone, ConnectedDriveSyncDialog, ConnectedFactsPage, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailInviteCard, ConnectedMailMessage, ConnectedMailSettings, ConnectedMailSetup, ConnectedMailbox, ConnectedMailboxSyncDialog, ConnectedNotificationCenter, ConnectedNotificationPreferences, ConnectedPasswordChangeDialog, ConnectedPremiumModels, ConnectedPrivacySettings, ConnectedProfileCard, ConnectedPublicBooking, ConnectedRequireVerified, ConnectedSearchPage, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSettingsPage, ConnectedSkillUpload, ConnectedSkillsLibrary, ConnectedTagTable, ConnectedTagsPage, ConnectedTaskAccessRequest, ConnectedTaskActivityDialog, ConnectedThreadFollow, ConnectedUpcomingEvents, ConnectedWorkflowHistory, ConnectedWorkflowRunner, ConnectedWorkspaceActivity, ConnectedWorkspaceNotificationMenu, ConnectedWorkspaceSettings, DEFAULT_CONNECTED_SEARCH_PAGE_LABELS, DEFAULT_CONNECTED_SKILL_UPLOAD_LABELS, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, DEFAULT_SEARCH_ROW_LABELS, DEFAULT_SETTINGS_SECTIONS, DEFAULT_TIMELINE_LABEL_WIDTH, FACTS_GRID_ID, InvoicesModal, NewProjectFlow, NotificationCenter, NotificationEvent, ProjectDetail, ProjectsTable, REFERRAL_OFFER, SETTINGS_SECTION_IDS, SessionStatusIcon, SharedProjects, StreamedArtifact, WorkflowRunThread, ZERO_BUDGET_MESSAGE, accountSettingsSection, aiUsageResetTimestamp, bookingCancelLink, buildNotificationCenterItems, chatSettingsSection, connectedAppsSettingsSection, countNotificationTypes, defaultUnmetPasswordRequirement, filterNotificationCenterItems, formatLastSync, formatMoney, formatResetDate, getQuotaItems, isAddonBudgetZero, isProProject, mailSettingsSection, openMeetingLink, planBadgeView, planName, privacySettingsSection, renderSessionStatusIcon, requestCalendarComposer, requestCalendarEvent, requestProjectBillingFocus, resolveMailbox, useAutoRefreshPendingProjects, useBillingSupport, useCalendarViewStore, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications, useConnectedWorkspaceSettings, useOpenBillingPortal, useProjectBillingFocus, useReferralReward, useRewardBalance, windowLabel, workflowRunView };
18033
+ export { AddAddonDialog, ArbiAppProvider, BACKFILL_OPTIONS, BillingProjectEntity, ChangePlanDialog, ConnectedAccountSettings, ConnectedAgentChatPanel, ConnectedAgentCreator, ConnectedAppsSettings, ConnectedCalendar, ConnectedDangerZone, ConnectedDriveSyncDialog, ConnectedFactsPage, ConnectedFilesExplorer, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailInviteCard, ConnectedMailMessage, ConnectedMailSettings, ConnectedMailSetup, ConnectedMailbox, ConnectedMailboxSyncDialog, ConnectedNotificationCenter, ConnectedNotificationPreferences, ConnectedPasswordChangeDialog, ConnectedPremiumModels, ConnectedPrivacySettings, ConnectedProfileCard, ConnectedProjectsPanel, ConnectedPublicBooking, ConnectedRequireVerified, ConnectedSearchPage, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSettingsPage, ConnectedSkillUpload, ConnectedSkillsLibrary, ConnectedTagTable, ConnectedTagsPage, ConnectedTaskAccessRequest, ConnectedTaskActivityDialog, ConnectedThreadFollow, ConnectedUpcomingEvents, ConnectedWorkflowHistory, ConnectedWorkflowRunner, ConnectedWorkspaceActivity, ConnectedWorkspaceDashboard, ConnectedWorkspaceList, ConnectedWorkspaceMembers, ConnectedWorkspaceNotificationMenu, ConnectedWorkspaceSettings, DEFAULT_CONNECTED_SEARCH_PAGE_LABELS, DEFAULT_CONNECTED_SKILL_UPLOAD_LABELS, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, DEFAULT_SEARCH_ROW_LABELS, DEFAULT_SETTINGS_SECTIONS, DEFAULT_TIMELINE_LABEL_WIDTH, DEFAULT_WORKSPACE_LIST_LABELS, DocumentSyncGlyph, FACTS_GRID_ID, FileTooltipContent, FilesSupportProvider, InvoicesModal, NewProjectFlow, NotificationCenter, NotificationEvent, PROJECTS_BASE_TAB_ID, ProjectDetail, ProjectsTable, REFERRAL_OFFER, SETTINGS_SECTION_IDS, SessionStatusIcon, SharedProjects, StreamedArtifact, TILE_DEFAULT, TILE_MAX, TILE_MIN, WORKSPACES_TAB_ID, WorkflowRunThread, accountSettingsSection, bookingCancelLink, buildNotificationCenterItems, chatSettingsSection, connectedAppsSettingsSection, countNotificationTypes, defaultUnmetPasswordRequirement, filterNotificationCenterItems, formatLastSync, isActiveDocumentStatus, mailSettingsSection, openMeetingLink, privacySettingsSection, renderSessionStatusIcon, requestCalendarComposer, requestCalendarEvent, requestProjectBillingFocus, resolveMailbox, startMenuItem, toWorkspaceSummary, useAutoRefreshPendingProjects, useCalendarViewStore, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications, useConnectedWorkspaceList, useConnectedWorkspaceSettings, useFilesStore, useFilesSupport, useOfflineDocumentIds, useOpenBillingPortal, usePendingFolders, useProjectBillingFocus, useReferralReward, useViewingDocumentId, windowLabel, workflowRunView };
16322
18034
  //# sourceMappingURL=index.js.map
16323
18035
  //# sourceMappingURL=index.js.map