@arbidocs/blocks 0.4.163 → 0.4.165

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 (186) hide show
  1. package/bin/arbidocs-copy-pdf-assets.mjs +6 -29
  2. package/bin/pdfjs-shims.mjs +54 -0
  3. package/dist/{ConnectedNativeDocxComparison-LKIMKOTA.js → ConnectedNativeDocxComparison-7WXIDRDY.js} +17 -17
  4. package/dist/{ConnectedStreamedWordArtifact-432HT7DK.js.map → ConnectedNativeDocxComparison-7WXIDRDY.js.map} +1 -1
  5. package/dist/{ConnectedNativeDocxComparison-GLHOGUP6.cjs → ConnectedNativeDocxComparison-XV2UHRBQ.cjs} +18 -18
  6. package/dist/{ConnectedNativeDocxComparison-GLHOGUP6.cjs.map → ConnectedNativeDocxComparison-XV2UHRBQ.cjs.map} +1 -1
  7. package/dist/{ConnectedNativeDocxEditor-6LNYWZ3P.js → ConnectedNativeDocxEditor-X3FEGMRT.js} +15 -15
  8. package/dist/{ConnectedNativeDocxEditor-6LNYWZ3P.js.map → ConnectedNativeDocxEditor-X3FEGMRT.js.map} +1 -1
  9. package/dist/{ConnectedNativeDocxEditor-M3UGWL6K.cjs → ConnectedNativeDocxEditor-Z7KSH5RK.cjs} +17 -17
  10. package/dist/{ConnectedNativeDocxEditor-M3UGWL6K.cjs.map → ConnectedNativeDocxEditor-Z7KSH5RK.cjs.map} +1 -1
  11. package/dist/{ConnectedStreamedWordArtifact-432HT7DK.js → ConnectedStreamedWordArtifact-JV56WYWK.js} +17 -17
  12. package/dist/{ConnectedNativeDocxComparison-LKIMKOTA.js.map → ConnectedStreamedWordArtifact-JV56WYWK.js.map} +1 -1
  13. package/dist/{ConnectedStreamedWordArtifact-COEXL5IT.cjs → ConnectedStreamedWordArtifact-UNZIAYWQ.cjs} +18 -18
  14. package/dist/ConnectedStreamedWordArtifact-UNZIAYWQ.cjs.map +1 -0
  15. package/dist/{NativeDocxEditor-T2PJIZMU.cjs → NativeDocxEditor-WAUI2YRU.cjs} +8 -8
  16. package/dist/{NativeDocxEditor-T2PJIZMU.cjs.map → NativeDocxEditor-WAUI2YRU.cjs.map} +1 -1
  17. package/dist/{NativeDocxEditor-TBTBD6TC.js → NativeDocxEditor-XEDC4UFE.js} +4 -4
  18. package/dist/{NativeDocxEditor-TBTBD6TC.js.map → NativeDocxEditor-XEDC4UFE.js.map} +1 -1
  19. package/dist/{WorkspaceTableView-AQTF5F6T.cjs → WorkspaceTableView-OMBC2G5K.cjs} +12 -12
  20. package/dist/{WorkspaceTableView-AQTF5F6T.cjs.map → WorkspaceTableView-OMBC2G5K.cjs.map} +1 -1
  21. package/dist/{WorkspaceTableView-PTB5BPVA.js → WorkspaceTableView-QVOLTH5Z.js} +10 -10
  22. package/dist/{WorkspaceTableView-PTB5BPVA.js.map → WorkspaceTableView-QVOLTH5Z.js.map} +1 -1
  23. package/dist/agents/index.cjs +269 -269
  24. package/dist/agents/index.js +12 -12
  25. package/dist/auth/index.cjs +30 -30
  26. package/dist/auth/index.js +1 -1
  27. package/dist/calls/index.cjs +3 -3
  28. package/dist/calls/index.js +2 -2
  29. package/dist/chat/index.cjs +140 -140
  30. package/dist/chat/index.js +6 -6
  31. package/dist/{chunk-ANLTECZ6.cjs → chunk-2MX3RAF4.cjs} +13 -13
  32. package/dist/{chunk-ANLTECZ6.cjs.map → chunk-2MX3RAF4.cjs.map} +1 -1
  33. package/dist/{chunk-K7CZFNJ2.js → chunk-2NTQH63V.js} +13 -13
  34. package/dist/{chunk-K7CZFNJ2.js.map → chunk-2NTQH63V.js.map} +1 -1
  35. package/dist/{chunk-2AMYT2HQ.js → chunk-4Q4W5WEO.js} +31 -7
  36. package/dist/chunk-4Q4W5WEO.js.map +1 -0
  37. package/dist/{chunk-WKSP73RU.js → chunk-5BKVKWKQ.js} +7 -7
  38. package/dist/{chunk-WKSP73RU.js.map → chunk-5BKVKWKQ.js.map} +1 -1
  39. package/dist/{chunk-FMJGVAWX.js → chunk-6RKMFTUJ.js} +8 -8
  40. package/dist/{chunk-FMJGVAWX.js.map → chunk-6RKMFTUJ.js.map} +1 -1
  41. package/dist/{chunk-3PQIWOTZ.js → chunk-6SOWJFWJ.js} +4 -4
  42. package/dist/{chunk-3PQIWOTZ.js.map → chunk-6SOWJFWJ.js.map} +1 -1
  43. package/dist/{chunk-NOXGEQQR.js → chunk-A7HNGO5F.js} +3 -3
  44. package/dist/{chunk-NOXGEQQR.js.map → chunk-A7HNGO5F.js.map} +1 -1
  45. package/dist/{chunk-4WWM3GQ5.js → chunk-ABEKUV6L.js} +11 -6
  46. package/dist/chunk-ABEKUV6L.js.map +1 -0
  47. package/dist/{chunk-TNESYDBR.cjs → chunk-AXCTDWFH.cjs} +3 -3
  48. package/dist/{chunk-TNESYDBR.cjs.map → chunk-AXCTDWFH.cjs.map} +1 -1
  49. package/dist/{chunk-5C7DRQQE.cjs → chunk-C3GS4WQE.cjs} +4 -4
  50. package/dist/{chunk-5C7DRQQE.cjs.map → chunk-C3GS4WQE.cjs.map} +1 -1
  51. package/dist/{chunk-5A2QQAF5.cjs → chunk-C45WOKZW.cjs} +3 -3
  52. package/dist/{chunk-5A2QQAF5.cjs.map → chunk-C45WOKZW.cjs.map} +1 -1
  53. package/dist/{chunk-AVXRXPII.js → chunk-EJHJQKUF.js} +3 -3
  54. package/dist/{chunk-AVXRXPII.js.map → chunk-EJHJQKUF.js.map} +1 -1
  55. package/dist/chunk-EQ3WRY2N.js +49 -0
  56. package/dist/chunk-EQ3WRY2N.js.map +1 -0
  57. package/dist/{chunk-WU34CHU5.js → chunk-FPFMKSSB.js} +3 -3
  58. package/dist/{chunk-WU34CHU5.js.map → chunk-FPFMKSSB.js.map} +1 -1
  59. package/dist/{chunk-ZW2AWACP.js → chunk-GMJZ4FO7.js} +4 -4
  60. package/dist/{chunk-ZW2AWACP.js.map → chunk-GMJZ4FO7.js.map} +1 -1
  61. package/dist/{chunk-4GITK3Q5.cjs → chunk-HCAXURWO.cjs} +3 -3
  62. package/dist/{chunk-4GITK3Q5.cjs.map → chunk-HCAXURWO.cjs.map} +1 -1
  63. package/dist/{chunk-6XIXNH26.js → chunk-HDJSU5IS.js} +3 -3
  64. package/dist/{chunk-6XIXNH26.js.map → chunk-HDJSU5IS.js.map} +1 -1
  65. package/dist/{chunk-OHCYJFV2.cjs → chunk-HHNYVJV3.cjs} +7 -7
  66. package/dist/{chunk-OHCYJFV2.cjs.map → chunk-HHNYVJV3.cjs.map} +1 -1
  67. package/dist/{chunk-5WLORPGX.js → chunk-HQYN5EFX.js} +3 -3
  68. package/dist/{chunk-5WLORPGX.js.map → chunk-HQYN5EFX.js.map} +1 -1
  69. package/dist/{chunk-BGXWPVHR.js → chunk-HZJQQVKL.js} +3 -3
  70. package/dist/{chunk-BGXWPVHR.js.map → chunk-HZJQQVKL.js.map} +1 -1
  71. package/dist/{chunk-UORU5S7Y.js → chunk-I4HFM5HB.js} +3 -3
  72. package/dist/{chunk-UORU5S7Y.js.map → chunk-I4HFM5HB.js.map} +1 -1
  73. package/dist/{chunk-LQYPOWKN.cjs → chunk-IHPQO2TG.cjs} +5 -5
  74. package/dist/{chunk-LQYPOWKN.cjs.map → chunk-IHPQO2TG.cjs.map} +1 -1
  75. package/dist/{chunk-NUAC5GTQ.cjs → chunk-JB4EXO3V.cjs} +6 -6
  76. package/dist/{chunk-NUAC5GTQ.cjs.map → chunk-JB4EXO3V.cjs.map} +1 -1
  77. package/dist/{chunk-GWSGIZQ5.js → chunk-KCIYZ76I.js} +3 -3
  78. package/dist/{chunk-GWSGIZQ5.js.map → chunk-KCIYZ76I.js.map} +1 -1
  79. package/dist/{chunk-I6I34FDU.cjs → chunk-M7C4FHLU.cjs} +12 -12
  80. package/dist/{chunk-I6I34FDU.cjs.map → chunk-M7C4FHLU.cjs.map} +1 -1
  81. package/dist/{chunk-7RGLTF76.cjs → chunk-MEMWZ72Y.cjs} +11 -6
  82. package/dist/chunk-MEMWZ72Y.cjs.map +1 -0
  83. package/dist/{chunk-ETJFC6MO.cjs → chunk-MPXUHCEV.cjs} +4 -4
  84. package/dist/{chunk-ETJFC6MO.cjs.map → chunk-MPXUHCEV.cjs.map} +1 -1
  85. package/dist/{chunk-3UDVQTXA.js → chunk-NKT6CO5F.js} +4 -4
  86. package/dist/{chunk-3UDVQTXA.js.map → chunk-NKT6CO5F.js.map} +1 -1
  87. package/dist/{chunk-X3XPXEIM.cjs → chunk-OGQSYL3P.cjs} +13 -13
  88. package/dist/{chunk-X3XPXEIM.cjs.map → chunk-OGQSYL3P.cjs.map} +1 -1
  89. package/dist/chunk-OSWZ4K34.cjs +18 -0
  90. package/dist/{chunk-VYTK2WJB.cjs.map → chunk-OSWZ4K34.cjs.map} +1 -1
  91. package/dist/chunk-PMHAJRKF.cjs +18 -0
  92. package/dist/{chunk-AR6A2PMV.cjs.map → chunk-PMHAJRKF.cjs.map} +1 -1
  93. package/dist/{chunk-J7YHBMBB.js → chunk-QARJQJAX.js} +3 -3
  94. package/dist/{chunk-J7YHBMBB.js.map → chunk-QARJQJAX.js.map} +1 -1
  95. package/dist/{chunk-3VW7ZXQ6.cjs → chunk-R2N5VIVI.cjs} +4 -4
  96. package/dist/{chunk-3VW7ZXQ6.cjs.map → chunk-R2N5VIVI.cjs.map} +1 -1
  97. package/dist/{chunk-JTJCCL2E.js → chunk-SLLNLFFP.js} +4 -4
  98. package/dist/{chunk-JTJCCL2E.js.map → chunk-SLLNLFFP.js.map} +1 -1
  99. package/dist/chunk-TM2D4XT4.cjs +52 -0
  100. package/dist/chunk-TM2D4XT4.cjs.map +1 -0
  101. package/dist/{chunk-KNCJSPXV.js → chunk-UKKTT3JE.js} +6 -6
  102. package/dist/{chunk-KNCJSPXV.js.map → chunk-UKKTT3JE.js.map} +1 -1
  103. package/dist/{chunk-6UTIAHQG.cjs → chunk-V24UHHL4.cjs} +67 -67
  104. package/dist/chunk-V24UHHL4.cjs.map +1 -0
  105. package/dist/{chunk-57OTWUBC.cjs → chunk-VDGLAVVM.cjs} +3 -3
  106. package/dist/chunk-VDGLAVVM.cjs.map +1 -0
  107. package/dist/{chunk-TQUYWUEE.cjs → chunk-VIV56KDN.cjs} +7 -7
  108. package/dist/{chunk-TQUYWUEE.cjs.map → chunk-VIV56KDN.cjs.map} +1 -1
  109. package/dist/{chunk-XER3JLC7.js → chunk-VTTDCHBZ.js} +3 -3
  110. package/dist/{chunk-XER3JLC7.js.map → chunk-VTTDCHBZ.js.map} +1 -1
  111. package/dist/{chunk-HRDVQERS.cjs → chunk-W7IFDFOZ.cjs} +31 -5
  112. package/dist/chunk-W7IFDFOZ.cjs.map +1 -0
  113. package/dist/{chunk-B3FMUFAW.cjs → chunk-WE5DABTC.cjs} +4 -4
  114. package/dist/{chunk-B3FMUFAW.cjs.map → chunk-WE5DABTC.cjs.map} +1 -1
  115. package/dist/{chunk-NS7NVX4E.cjs → chunk-WLFMHGHK.cjs} +6 -6
  116. package/dist/{chunk-NS7NVX4E.cjs.map → chunk-WLFMHGHK.cjs.map} +1 -1
  117. package/dist/{chunk-O5E3NKPI.cjs → chunk-X6Z6IWRY.cjs} +6 -6
  118. package/dist/{chunk-O5E3NKPI.cjs.map → chunk-X6Z6IWRY.cjs.map} +1 -1
  119. package/dist/{chunk-KQWC4YBX.js → chunk-XA6QWXOQ.js} +4 -4
  120. package/dist/{chunk-KQWC4YBX.js.map → chunk-XA6QWXOQ.js.map} +1 -1
  121. package/dist/{chunk-ZDOKJD44.js → chunk-XBTOPW6B.js} +4 -4
  122. package/dist/{chunk-ZDOKJD44.js.map → chunk-XBTOPW6B.js.map} +1 -1
  123. package/dist/{chunk-PCL7IEZO.js → chunk-YG3BXG5M.js} +4 -4
  124. package/dist/{chunk-PCL7IEZO.js.map → chunk-YG3BXG5M.js.map} +1 -1
  125. package/dist/{chunk-AYJZRBHG.cjs → chunk-YMWMVV4H.cjs} +4 -4
  126. package/dist/{chunk-AYJZRBHG.cjs.map → chunk-YMWMVV4H.cjs.map} +1 -1
  127. package/dist/{chunk-GFVIFXWT.cjs → chunk-YNLWAD57.cjs} +5 -5
  128. package/dist/{chunk-GFVIFXWT.cjs.map → chunk-YNLWAD57.cjs.map} +1 -1
  129. package/dist/{chunk-RY2SODRN.js → chunk-ZSDL36BI.js} +4 -4
  130. package/dist/{chunk-RY2SODRN.js.map → chunk-ZSDL36BI.js.map} +1 -1
  131. package/dist/citations/index.cjs +1 -1
  132. package/dist/citations/index.js +1 -1
  133. package/dist/components/ErrorBoundary.d.ts +33 -0
  134. package/dist/connect/docx.cjs +21 -21
  135. package/dist/connect/docx.js +17 -17
  136. package/dist/connect/index.cjs +688 -688
  137. package/dist/connect/index.cjs.map +1 -1
  138. package/dist/connect/index.js +36 -36
  139. package/dist/connect/index.js.map +1 -1
  140. package/dist/connect/viewer.cjs +82 -72
  141. package/dist/connect/viewer.cjs.map +1 -1
  142. package/dist/connect/viewer.js +30 -20
  143. package/dist/connect/viewer.js.map +1 -1
  144. package/dist/document-tools/index.cjs +20 -20
  145. package/dist/document-tools/index.js +3 -3
  146. package/dist/docx/index.cjs +51 -51
  147. package/dist/docx/index.js +4 -4
  148. package/dist/facts/index.cjs +58 -58
  149. package/dist/facts/index.js +8 -8
  150. package/dist/files/index.cjs +241 -241
  151. package/dist/files/index.js +9 -9
  152. package/dist/home/index.cjs +30 -30
  153. package/dist/home/index.js +5 -5
  154. package/dist/index.cjs +975 -958
  155. package/dist/index.cjs.map +1 -1
  156. package/dist/index.d.ts +1 -0
  157. package/dist/index.js +26 -25
  158. package/dist/index.js.map +1 -1
  159. package/dist/lib/start-inset.d.ts +13 -0
  160. package/dist/retrieval/index.cjs +1 -1
  161. package/dist/retrieval/index.js +1 -1
  162. package/dist/settings/index.cjs +174 -174
  163. package/dist/settings/index.js +5 -5
  164. package/dist/shell/InstallPrompt.d.ts +2 -1
  165. package/dist/shell/index.cjs +62 -54
  166. package/dist/shell/index.d.ts +1 -0
  167. package/dist/shell/index.js +5 -5
  168. package/dist/studio/index.cjs +7 -7
  169. package/dist/studio/index.js +6 -6
  170. package/dist/workflow-canvas/index.cjs +25 -25
  171. package/dist/workflow-canvas/index.js +12 -12
  172. package/dist/workspaces/index.cjs +2 -2
  173. package/dist/workspaces/index.js +2 -2
  174. package/node_modules/@arbidocs/coedit/package.json +1 -1
  175. package/node_modules/@docx-editor.dev/core/dist/chunk-LWDSDCMO.js +1 -1
  176. package/node_modules/@docx-editor.dev/core/dist/chunk-RCHS4ZEH.cjs +1 -1
  177. package/package.json +5 -5
  178. package/dist/ConnectedStreamedWordArtifact-COEXL5IT.cjs.map +0 -1
  179. package/dist/chunk-2AMYT2HQ.js.map +0 -1
  180. package/dist/chunk-4WWM3GQ5.js.map +0 -1
  181. package/dist/chunk-57OTWUBC.cjs.map +0 -1
  182. package/dist/chunk-6UTIAHQG.cjs.map +0 -1
  183. package/dist/chunk-7RGLTF76.cjs.map +0 -1
  184. package/dist/chunk-AR6A2PMV.cjs +0 -18
  185. package/dist/chunk-HRDVQERS.cjs.map +0 -1
  186. package/dist/chunk-VYTK2WJB.cjs +0 -18
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/home/FoldSection.tsx","../src/home/AddFilesFirst.tsx","../src/home/QuickActionDialogHeader.tsx","../src/lib/useScopeSelection.ts","../src/home/ScopePicker.tsx","../src/home/sections.tsx","../src/lib/balanced-columns.ts","../src/home/StartTiles.tsx"],"names":["jsxs","jsx","cn","useMemo","useState"],"mappings":";;;;;;;;;AAsBO,SAAS,WAAA,CAAY;AAAA,EAC1B,KAAA;AAAA,EACA,KAAA,GAAQ,CAAA;AAAA,EACR,IAAA;AAAA,EACA,IAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,EAAqB;AACnB,EAAA,uBACE,IAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAY,YAAA,EAA4B,aAAU,cAAA,EAC7D,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,kBAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,wMAAA;AAAA,QACV,aAAA,EAAa,MAAA;AAAA,QAEb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,WAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,EAAA,CAAG,sCAAA,EAAwC,CAAC,QAAQ,YAAY;AAAA;AAAA,WAC7E;AAAA,0BACA,GAAA,CAAC,UAAM,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,UACZ,QAAQ,CAAA,oBAAK,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,8CAA8C,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,UACjF,QAAQ,CAAC,IAAA,wBACP,MAAA,EAAA,EAAK,SAAA,EAAU,8DAA8D,QAAA,EAAA,IAAA,EAAK;AAAA;AAAA;AAAA,KAEvF;AAAA,oBACA,GAAA,CAAC,sBAAoB,QAAA,EAAS;AAAA,GAAA,EAChC,CAAA;AAEJ;ACzCO,IAAM,8BAAA,GAAsD;AAAA,EACjE,OAAA,EAAS;AACX;AAkBO,SAAS,cAAc,EAAE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,QAAO,EAAuB;AACpF,EAAA,MAAM,CAAA,GAAI,EAAE,GAAG,8BAAA,EAAgC,GAAG,MAAA,EAAO;AACzD,EAAA,uBACEA,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,0GAAA;AAAA,MACV,WAAA,EAAU,iBAAA;AAAA,MACV,aAAA,EAAa,MAAA;AAAA,MAEb,QAAA,EAAA;AAAA,wBAAAC,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,8EAAA,EACd,QAAA,kBAAAA,GAAAA,CAAC,UAAA,EAAA,EAAW,SAAA,EAAU,QAAA,EAAS,aAAA,EAAY,MAAA,EAAO,CAAA,EACpD,CAAA;AAAA,wBACAD,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACb,QAAA,EAAA;AAAA,0BAAAC,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,uCAAA,EAAyC,YAAE,OAAA,EAAQ,CAAA;AAAA,0BAChEA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,iDAAiD,QAAA,EAAA,MAAA,EAAO;AAAA,SAAA,EACvE,CAAA;AAAA,QACC;AAAA;AAAA;AAAA,GACH;AAEJ;ACvBO,SAAS,uBAAA,CAAwB;AAAA,EACtC,IAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAA,EAAiC;AAC/B,EAAA,uBACED,IAAAA,CAAC,YAAA,EAAA,EAAa,SAAA,EAAU,qBAAA,EAAsB,aAAU,4BAAA,EACtD,QAAA,EAAA;AAAA,oBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EACb,QAAA,EAAA;AAAA,sBAAAC,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uFAAA,EACZ,QAAA,EAAA,IAAA,EACH,CAAA;AAAA,sBACAA,GAAAA,CAAC,WAAA,EAAA,EAAY,SAAA,EAAU,uCAAuC,QAAA,EAAA,KAAA,EAAM;AAAA,KAAA,EACtE,CAAA;AAAA,oBACAA,GAAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,iEAAA;AAAA,QACV,aAAA,EAAa,iBAAA;AAAA,QAEZ,QAAA,EAAA;AAAA;AAAA;AACH,GAAA,EACF,CAAA;AAEJ;ACjBO,SAAS,iBAAA,CAAkB;AAAA,EAChC,WAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,EAA6B;AAC3B,EAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,MAAM,IAAI,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA,EAAG,CAAC,KAAA,CAAM,OAAO,CAAC,CAAA;AAC7E,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,MAAM,IAAI,GAAA,CAAI,KAAA,CAAM,MAAM,CAAA,EAAG,CAAC,KAAA,CAAM,MAAM,CAAC,CAAA;AAG1E,EAAA,MAAM,WAAA,GAAc,OAAA;AAAA,IAClB,MAAM,CAAC,GAAI,UAAA,GAAa,CAAC,EAAE,CAAA,GAAI,EAAC,EAAI,GAAG,WAAW,CAAA;AAAA,IAClD,CAAC,aAAa,UAAU;AAAA,GAC1B;AACA,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,MAAA,GAAS,CAAA,IAAK,WAAA,CAAY,KAAA,CAAM,CAAC,CAAA,KAAM,eAAA,CAAgB,GAAA,CAAI,CAAC,CAAC,CAAA;AAC7F,EAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,IAAA,GAAO,CAAA,IAAK,CAAC,WAAA;AAElD,EAAA,MAAM,WAAA,GAAc,CAAC,IAAA,KAAiB,WAAA,CAAY,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,UAAA,CAAW,IAAA,GAAO,GAAG,CAAC,CAAA;AAExF,EAAA,MAAM,YAAA,GAAe,CAAC,IAAA,KAAiB;AACrC,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,eAAe,CAAA;AACpC,IAAA,MAAM,SAAS,CAAC,IAAA,EAAM,GAAG,WAAA,CAAY,IAAI,CAAC,CAAA;AAC1C,IAAA,IAAI,IAAA,CAAK,IAAI,IAAI,CAAA,aAAc,CAAA,IAAK,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA;AAAA,SACpD,KAAA,MAAW,CAAA,IAAK,MAAA,EAAQ,IAAA,CAAK,IAAI,CAAC,CAAA;AACvC,IAAA,aAAA,CAAc,EAAE,SAAS,CAAC,GAAG,IAAI,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAQ,CAAA;AAAA,EAC5D,CAAA;AAEA,EAAA,MAAM,mBAAmB,MAAM;AAC7B,IAAA,aAAA,CAAc,EAAE,SAAS,WAAA,GAAc,KAAK,WAAA,EAAa,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAQ,CAAA;AAAA,EACjF,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,OAAA,KAAoB;AACvC,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,cAAc,CAAA;AACnC,IAAA,IAAI,KAAK,GAAA,CAAI,OAAO,CAAA,EAAG,IAAA,CAAK,OAAO,OAAO,CAAA;AAAA,SACrC,IAAA,CAAK,IAAI,OAAO,CAAA;AACrB,IAAA,aAAA,CAAc,EAAE,SAAS,KAAA,CAAM,OAAA,EAAS,QAAQ,CAAC,GAAG,IAAI,CAAA,EAAG,CAAA;AAAA,EAC7D,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,eAAA;AAAA,IACA,cAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAc,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA,IAAK,KAAA,CAAM,OAAO,MAAA,GAAS,CAAA;AAAA,IAChE,YAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACF;AACF;AC5CA,IAAM,SAAA,GAAY,CAAC,CAAA,EAAW,IAAA,KAAiB,CAAA,EAAG,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,EAAE,CAAA,CAAA;AAEzE,IAAM,2BAAA,GAAiD;AAAA,EAC5D,OAAA,EAAS,SAAA;AAAA,EACT,aAAA,EAAe,QAAA;AAAA,EACf,GAAA,EAAK,KAAA;AAAA,EACL,OAAA,EAAS,SAAA;AAAA,EACT,YAAA,EAAc,gBAAA;AAAA,EACd,WAAA,EAAa,6EAAA;AAAA,EACb,cAAA,EAAgB,kBAAA;AAAA,EAChB,cAAA,EAAgB,CAAC,OAAA,EAAS,KAAA,KACxB,GAAG,OAAO,CAAA,IAAA,EAAO,WAAA,CAAY,KAAA,EAAO,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,CAAC,CAAA,CAAA;AAAA,EACtE,cAAA,EAAgB,WAAA;AAAA,EAChB,WAAA,EAAa,CAAC,KAAA,KAAU,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EACjD,UAAA,EAAY,CAAC,KAAA,KAAU,SAAA,CAAU,OAAO,OAAO,CAAA;AAAA,EAC/C,IAAA,EAAM,CAAC,KAAA,KAAU,CAAA,EAAG,KAAK,CAAA,KAAA,CAAA;AAAA,EACzB,UAAU,CAAC,KAAA,KAAU,OAAO,SAAA,CAAU,KAAA,EAAO,MAAM,CAAC,CAAA,CAAA;AAAA,EACpD,cAAA,EAAgB;AAClB;AAmDA,SAAS,SAAA,CAAU;AAAA,EACjB,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,EAAmB;AACjB,EAAA,uBACED,IAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAS,QAAA;AAAA,MACT,IAAA,EAAK,UAAA;AAAA,MACL,cAAA,EAAc,gBAAgB,OAAA,GAAU,QAAA;AAAA,MACxC,OAAO,EAAE,WAAA,EAAa,GAAG,CAAA,GAAI,KAAA,GAAQ,EAAE,CAAA,EAAA,CAAA,EAAK;AAAA,MAC5C,SAAA,EAAWE,EAAAA;AAAA,QACT,mMAAA;AAAA,QACA,QAAA,IAAY,eAAA;AAAA,QACZ,KAAA,IAAS,CAAC,QAAA,IAAY;AAAA,OACxB;AAAA,MACA,WAAA,EAAU,kBAAA;AAAA,MAEV,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,+CAAA,EACd,QAAA,kBAAAA,GAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAWC,EAAAA;AAAA,cACT,kFAAA;AAAA,cACA,QAAA,IAAY,gBACR,mDAAA,GACA;AAAA,aACN;AAAA,YAEC,QAAA,EAAA,aAAA,mBACCD,GAAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAU,UAAA,EAAW,CAAA,GAE5B,QAAA,oBAAYA,GAAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAU,UAAA,EAAW;AAAA;AAAA,SAE5C,EACF,CAAA;AAAA,QACC,KAAA,KAAU,EAAA,oBACTA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAWC,EAAAA,CAAG,kBAAA,EAAoB,QAAA,IAAY,4BAA4B,CAAA,EAC7E,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,wBAEFD,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,4DAA4D,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA;AAAA,GACpF;AAEJ;AAQO,SAAS,WAAA,CAAY;AAAA,EAC1B,OAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,KAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,CAAA,GAAI,EAAE,GAAG,2BAAA,EAA6B,GAAG,QAAA,EAAS;AACxD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAS,KAAK,CAAA;AAElD,EAAA,MAAM,WAAA,GAAcE,OAAAA,CAAQ,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AACvE,EAAA,MAAM,YAAY,iBAAA,CAAkB;AAAA,IAClC,WAAA;AAAA,IACA,YAAY,YAAA,GAAe,CAAA;AAAA,IAC3B,KAAA;AAAA,IACA;AAAA,GACD,CAAA;AACD,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,MAAA,GAAS,CAAA,IAAK,YAAA,GAAe,CAAA;AAErD,EAAA,uBACEH,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EAAsB,aAAU,cAAA,EAC7C,QAAA,EAAA;AAAA,oBAAAC,GAAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,OAAO,CAAA,CAAE,OAAA;AAAA,QACT,KAAA,EAAO,MAAM,OAAA,CAAQ,MAAA;AAAA,QACrB,IAAA,EAAM,WAAA;AAAA,QACN,YAAA,EAAc,cAAA;AAAA,QACd,QAAQ,QAAA,CAAS,OAAA,EAAS,eAAe,EAAE,MAAA,EAAQ,+BAA+B,CAAA;AAAA,QAElF,QAAA,kBAAAD,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mEAAA,EACZ,QAAA,EAAA;AAAA,UAAA,OAAA,oBACCA,KAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,4BAAAC,GAAAA;AAAA,cAAC,SAAA;AAAA,cAAA;AAAA,gBACC,OAAO,CAAA,CAAE,GAAA;AAAA,gBACT,KAAA,EAAO,UAAA;AAAA,gBACP,KAAA,EAAO,CAAA;AAAA,gBACP,UAAU,SAAA,CAAU,WAAA;AAAA,gBACpB,eAAe,SAAA,CAAU,YAAA;AAAA,gBACzB,UAAU,SAAA,CAAU;AAAA;AAAA,aACtB;AAAA,4BACAA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAAA,EAA8B;AAAA,WAAA,EAC/C,CAAA;AAAA,UAED,YAAA,GAAe,CAAA,oBACdD,IAAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,4BAAAC,GAAAA;AAAA,cAAC,SAAA;AAAA,cAAA;AAAA,gBACC,OAAO,CAAA,CAAE,OAAA;AAAA,gBACT,KAAA,EAAO,YAAA;AAAA,gBACP,KAAA,EAAO,CAAA;AAAA,gBACP,QAAA,EAAU,SAAA,CAAU,eAAA,CAAgB,GAAA,CAAI,EAAE,CAAA;AAAA,gBAC1C,KAAA,EAAK,IAAA;AAAA,gBACL,QAAA,EAAU,MAAM,SAAA,CAAU,YAAA,CAAa,EAAE;AAAA;AAAA,aAC3C;AAAA,YACC,QAAQ,MAAA,GAAS,CAAA,oBAAKA,GAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,6BAAA,EAA8B;AAAA,WAAA,EACtE,CAAA;AAAA,UAED,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,qBACZA,GAAAA;AAAA,YAAC,SAAA;AAAA,YAAA;AAAA,cAEC,OAAO,MAAA,CAAO,KAAA;AAAA,cACd,OAAO,MAAA,CAAO,KAAA;AAAA,cACd,OAAO,MAAA,CAAO,KAAA;AAAA,cACd,QAAA,EAAU,SAAA,CAAU,eAAA,CAAgB,GAAA,CAAI,OAAO,IAAI,CAAA;AAAA,cACnD,QAAA,EAAU,MAAM,SAAA,CAAU,YAAA,CAAa,OAAO,IAAI;AAAA,aAAA;AAAA,YAL7C,MAAA,CAAO;AAAA,WAOf,CAAA;AAAA,UACA,CAAC,2BAAWA,GAAAA,CAAC,OAAE,SAAA,EAAU,yCAAA,EAA2C,YAAE,YAAA,EAAa;AAAA,SAAA,EACtF;AAAA;AAAA,KACF;AAAA,oBAIAA,GAAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,OAAO,CAAA,CAAE,aAAA;AAAA,QACT,KAAA,EAAO,MAAM,MAAA,CAAO,MAAA;AAAA,QACpB,IAAA,EAAM,UAAA;AAAA,QACN,YAAA,EAAc,aAAA;AAAA,QACd,QAAQ,QAAA,CAAS,OAAA,EAAS,cAAc,EAAE,MAAA,EAAQ,8BAA8B,CAAA;AAAA,QAE/E,QAAA,EAAA,MAAA,CAAO,MAAA,KAAW,CAAA,mBACjBA,GAAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,yCAAA;AAAA,YACV,eAAa,QAAA,CAAS,OAAA,EAAS,UAAU,EAAE,MAAA,EAAQ,0BAA0B,CAAA;AAAA,YAE5E,QAAA,EAAA,CAAA,CAAE;AAAA;AAAA,SACL,mBAEAA,GAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wBAAA,EACZ,QAAA,EAAA,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,KAAU;AACrB,UAAA,MAAM,QAAA,GAAW,SAAA,CAAU,cAAA,CAAe,GAAA,CAAI,MAAM,EAAE,CAAA;AACtD,UAAA,uBACED,IAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cAEC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAS,MAAM,SAAA,CAAU,WAAA,CAAY,MAAM,EAAE,CAAA;AAAA,cAC7C,cAAA,EAAc,QAAA;AAAA,cACd,aAAA,EAAa,cAAA,CAAe,OAAA,EAAS,SAAA,EAAW,MAAM,EAAA,EAAI;AAAA,gBACxD,MAAA,EAAQ,CAAA,wBAAA,EAA2B,KAAA,CAAM,EAAE,CAAA;AAAA,eAC5C,CAAA;AAAA,cACD,SAAA,EAAWE,EAAAA;AAAA,gBACT,wLAAA;AAAA,gBACA,WACI,8CAAA,GACA;AAAA,eACN;AAAA,cAEA,QAAA,EAAA;AAAA,gCAAAD,GAAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,gCAAA;AAAA,oBACV,KAAA,EAAO,EAAE,eAAA,EAAiB,KAAA,CAAM,KAAA;AAAM;AAAA,iBACxC;AAAA,gCACAA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAY,gBAAM,IAAA,EAAK,CAAA;AAAA,gCACvCD,IAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,wCAAA,EAAyC,QAAA,EAAA;AAAA,kBAAA,GAAA;AAAA,kBAAE,KAAA,CAAM,KAAA;AAAA,kBAAM;AAAA,iBAAA,EAAC,CAAA;AAAA,gBACvE,QAAA,oBAAYC,GAAAA,CAAC,KAAA,EAAA,EAAM,WAAU,mBAAA,EAAoB;AAAA;AAAA,aAAA;AAAA,YApB7C,KAAA,CAAM;AAAA,WAqBb;AAAA,QAEJ,CAAC,CAAA,EACH;AAAA;AAAA,KAEJ;AAAA,oBAEAD,IAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,yBAAA;AAAA,QACV,eAAa,QAAA,CAAS,OAAA,EAAS,SAAS,EAAE,MAAA,EAAQ,wBAAwB,CAAA;AAAA,QAE1E,QAAA,EAAA;AAAA,0BAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,0EAAA,EACb,QAAA,EAAA;AAAA,4BAAAC,GAAAA,CAAC,UAAA,EAAA,EAAW,SAAA,EAAU,QAAA,EAAS,CAAA;AAAA,YAC9B,CAAA,CAAE;AAAA,WAAA,EACL,CAAA;AAAA,UACC,UAAU,YAAA,mBACTD,IAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,+BAAA,EACb,QAAA,EAAA;AAAA,4BAAAC,GAAAA,CAAC,UAAK,SAAA,EAAU,yCAAA,EACb,YAAE,cAAA,CAAe,YAAA,EAAc,UAAU,CAAA,EAC5C,CAAA;AAAA,YACC,CAAA,CAAE,cAAA;AAAA,YACF,MAAM,OAAA,CAAQ,MAAA,GAAS,CAAA,oBAAKD,KAAC,MAAA,EAAA,EAAK,QAAA,EAAA;AAAA,cAAA,UAAA;AAAA,cAAI,CAAA,CAAE,WAAA,CAAY,KAAA,CAAM,OAAA,CAAQ,MAAM;AAAA,aAAA,EAAE,CAAA;AAAA,YAC1E,KAAA,CAAM,OAAA,CAAQ,MAAA,GAAS,CAAA,IAAK,KAAA,CAAM,MAAA,CAAO,MAAA,GAAS,CAAA,oBAAKC,GAAAA,CAAC,MAAA,EAAA,EAAK,QAAA,EAAA,IAAA,EAAE,CAAA;AAAA,YAC/D,KAAA,CAAM,MAAA,CAAO,MAAA,GAAS,CAAA,oBAAKA,GAAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,CAAA,CAAE,UAAA,CAAW,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,EAAE,CAAA;AAAA,4BACrED,IAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EACb,QAAA,EAAA;AAAA,cAAA,QAAA;AAAA,cACA,CAAA,CAAE,IAAA,CAAK,UAAA,GAAa,YAAY;AAAA,aAAA,EACnC;AAAA,WAAA,EACF,CAAA,mBAEAA,IAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,+BAAA,EACb,QAAA,EAAA;AAAA,4BAAAC,IAAC,MAAA,EAAA,EAAK,SAAA,EAAU,2CACb,QAAA,EAAA,CAAA,CAAE,QAAA,CAAS,UAAU,CAAA,EACxB,CAAA;AAAA,YACC,CAAA,CAAE;AAAA,WAAA,EACL;AAAA;AAAA;AAAA;AAEJ,GAAA,EACF,CAAA;AAEJ;ACnTO,SAAS,YAAA,CAAa;AAAA,EAC3B,QAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,uBACEA,GAAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAWC,EAAAA;AAAA,QACT,uGAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,WAAA,EAAU,eAAA;AAAA,MACV,aAAA,EAAa,MAAA;AAAA,MAEZ;AAAA;AAAA,GACH;AAEJ;AAkBO,SAAS,aAAA,CAAc;AAAA,EAC5B,IAAA,EAAM,IAAA;AAAA,EACN,KAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,EAAuB;AACrB,EAAA,uBACEF,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAWE,EAAAA,CAAG,yCAAA,EAA2C,SAAS,CAAA;AAAA,MAClE,WAAA,EAAU,gBAAA;AAAA,MAEV,QAAA,EAAA;AAAA,wBAAAF,IAAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAM,QAAA;AAAA,YACN,QAAA,EAAU,MAAA;AAAA,YACV,QAAA,EAAU,CAAC,MAAA,IAAU,CAAC,QAAA;AAAA,YACtB,SAAA,EAAU,sRAAA;AAAA,YACV,aAAA,EAAa,UAAA;AAAA,YAEZ,QAAA,EAAA;AAAA,cAAA,IAAA,oBAAQC,GAAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,uCAAA,EAAwC,eAAY,MAAA,EAAO,CAAA;AAAA,8BACpFA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,2DAA2D,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,cAChF,UAAU,MAAA,oBACTA,IAAC,MAAA,EAAA,EAAK,SAAA,EAAU,8CAA8C,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA;AAAA,SAExE;AAAA,QACC,0BAAUA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,YAAY,QAAA,EAAA,MAAA,EAAO;AAAA;AAAA;AAAA,GAC/C;AAEJ;AAWO,SAAS,UAAA,CAAW;AAAA,EACzB,MAAM,IAAA,GAAO,IAAA;AAAA,EACb,KAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAAoB;AAClB,EAAA,uBACED,IAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAS,QAAA;AAAA,MACT,UAAU,CAAC,QAAA;AAAA,MACX,SAAA,EAAU,4UAAA;AAAA,MACV,WAAA,EAAU,aAAA;AAAA,MACV,aAAA,EAAa,MAAA;AAAA,MAEb,QAAA,EAAA;AAAA,wBAAAC,GAAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,uCAAA,EAAwC,eAAY,MAAA,EAAO,CAAA;AAAA,wBAC3ED,IAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gBAAA,EACd,QAAA,EAAA;AAAA,0BAAAC,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,6CAAA,EAA+C,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,UACpE,+BACCA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,oDAAoD,QAAA,EAAA,WAAA,EAAY;AAAA,SAAA,EAEpF;AAAA;AAAA;AAAA,GACF;AAEJ;;;ACzGO,SAAS,gBAAgB,KAAA,EAAe,KAAA,EAAe,OAAA,GAAU,GAAA,EAAK,MAAM,EAAA,EAAY;AAC7F,EAAA,IAAI,KAAA,IAAS,GAAG,OAAO,CAAA;AACvB,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,KAAA,EAAO,IAAA,CAAK,KAAA,CAAA,CAAO,KAAA,GAAQ,GAAA,KAAQ,OAAA,GAAU,GAAA,CAAI,CAAC,CAAC,CAAA;AACpF,EAAA,IAAI,GAAA,GAAM,GAAG,OAAO,CAAA;AACpB,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,IAAI,YAAY,MAAA,CAAO,iBAAA;AACvB,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,GAAA,EAAK,OAAA,EAAA,EAAW;AAC/C,IAAA,MAAM,KAAA,GAAA,CAAS,OAAA,GAAW,KAAA,GAAQ,OAAA,IAAY,OAAA;AAC9C,IAAA,IAAI,KAAA,GAAQ,SAAA,IAAc,KAAA,KAAU,SAAA,IAAa,UAAU,IAAA,EAAO;AAChE,MAAA,IAAA,GAAO,OAAA;AACP,MAAA,SAAA,GAAY,KAAA;AAAA,IACd;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;ACmBO,SAAS,WAAW,EAAE,KAAA,EAAO,SAAS,aAAA,EAAe,SAAA,EAAW,QAAO,EAAoB;AAGhG,EAAA,MAAM,OAAA,GAAU,OAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIG,SAAwB,IAAI,CAAA;AAC1D,EAAA,MAAM,QAAQ,KAAA,CAAM,MAAA;AACpB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,IAAQ,KAAA,KAAU,CAAA,EAAG;AAC1B,IAAA,MAAM,UAAU,MAAM,UAAA,CAAW,gBAAgB,KAAA,EAAO,IAAA,CAAK,WAAW,CAAC,CAAA;AACzE,IAAA,OAAA,EAAQ;AACR,IAAA,OAAO,aAAA,CAAc,MAAM,OAAO,CAAA;AAAA,EACpC,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,IAAI,KAAA,KAAU,GAAG,OAAO,IAAA;AACxB,EAAA,uBACEJ,IAAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAWE,EAAAA,CAAG,qBAAA,EAAuB,SAAS,CAAA;AAAA,MAC9C,WAAA,EAAU,aAAA;AAAA,MACV,aAAA,EAAa,MAAA;AAAA,MAEZ,QAAA,EAAA;AAAA,QAAA,OAAA,oBAAWD,GAAAA,CAAC,YAAA,EAAA,EAAa,MAAA,EAAQ,eAAgB,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,wBAC1DA,GAAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,OAAA;AAAA,YACL,WAAA,EAAU,kBAAA;AAAA,YACV,gBAAc,OAAA,IAAW,MAAA;AAAA,YACzB,SAAA,EAAU,YAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,mBAAA,EACE,OAAA,KAAY,IAAA,GACR,sCAAA,GACA,UAAU,OAAO,CAAA,iBAAA;AAAA,aACzB;AAAA,YAEC,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACnB,cAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,cAAA,uBACED,IAAAA;AAAA,gBAAC,UAAA;AAAA,gBAAA;AAAA,kBAEC,MAAM,IAAA,CAAK,IAAA;AAAA,kBACX,UAAU,IAAA,CAAK,QAAA;AAAA,kBACf,UAAU,IAAA,CAAK,QAAA;AAAA,kBACf,KAAA,EAAO,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,cAAA,GAAiB,MAAA;AAAA,kBAC7C,SAAA,EAAWE,EAAAA;AAAA,oBACT,iEAAA;AAAA,oBACA,kGAAA;AAAA,oBACA,yEAAA;AAAA,oBACA,iGAAA;AAAA,oBACA;AAAA,mBACF;AAAA,kBACA,WAAA,EAAU,YAAA;AAAA,kBACV,eAAa,IAAA,CAAK,MAAA;AAAA,kBAEjB,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAK,2BACJD,GAAAA,CAAC,UAAK,SAAA,EAAU,qCAAA,EAAsC,eAAY,MAAA,EAC/D,QAAA,EAAA,IAAA,CAAK,QAAA,EACR,CAAA,GAEA,wBAAQA,GAAAA,CAAC,QAAK,SAAA,EAAU,6BAAA,EAA8B,eAAY,MAAA,EAAO,CAAA;AAAA,oCAE3ED,IAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EACd,QAAA,EAAA;AAAA,sCAAAC,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,6CAAA,EAA+C,eAAK,KAAA,EAAM,CAAA;AAAA,sCAC1EA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uDAAA,EACb,QAAA,EAAA,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,WAAA,EACrE;AAAA,qBAAA,EACF;AAAA;AAAA,iBAAA;AAAA,gBA3BK,IAAA,CAAK;AAAA,eA4BZ;AAAA,YAEJ,CAAC;AAAA;AAAA;AACH;AAAA;AAAA,GACF;AAEJ","file":"chunk-UORU5S7Y.js","sourcesContent":["import type { ReactNode } from 'react'\nimport { ChevronDown } from 'lucide-react'\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger, cn } from '@arbidocs/react/ui'\n\nexport interface FoldSectionProps {\n label: string\n /** Shown as plain muted text beside the name; omit or pass 0 to hide. */\n count?: number\n /** A word or two of state for sections a count cannot summarise (shown while shut). */\n hint?: string\n open: boolean\n onOpenChange: (open: boolean) => void\n /** Trigger test id; the caller owns the naming. */\n testId: string\n children: ReactNode\n}\n\n/**\n * One foldable block of a dialog. The chevron and the name are the whole hit\n * target (44px on touch), and the count keeps the selection readable while the\n * section is shut, so you need not re-open it to check.\n */\nexport function FoldSection({\n label,\n count = 0,\n hint,\n open,\n onOpenChange,\n testId,\n children,\n}: FoldSectionProps) {\n return (\n <Collapsible open={open} onOpenChange={onOpenChange} data-slot=\"fold-section\">\n <CollapsibleTrigger\n className=\"mb-1.5 flex w-full items-center gap-1.5 rounded-md text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&:hover]:text-muted-foreground pointer-coarse:min-h-11\"\n data-testid={testId}\n >\n <ChevronDown\n className={cn('size-4 shrink-0 transition-transform', !open && '-rotate-90')}\n />\n <span>{label}</span>\n {count > 0 && <span className=\"text-xs tabular-nums text-muted-foreground\">{count}</span>}\n {hint && !open && (\n <span className=\"min-w-0 truncate text-xs font-normal text-muted-foreground\">{hint}</span>\n )}\n </CollapsibleTrigger>\n <CollapsibleContent>{children}</CollapsibleContent>\n </Collapsible>\n )\n}\n","import type { ReactNode } from 'react'\nimport { FolderPlus } from 'lucide-react'\n\n/** Fixed strings of the empty-files state. */\nexport interface AddFilesFirstLabels {\n heading: string\n}\n\nexport const DEFAULT_ADD_FILES_FIRST_LABELS: AddFilesFirstLabels = {\n heading: 'Add files first',\n}\n\nexport interface AddFilesFirstProps {\n /** Why this action needs files, in the action's own words. */\n reason: string\n /** The host's own \"Add\" control. */\n action: ReactNode\n testId?: string\n labels?: Partial<AddFilesFirstLabels>\n}\n\n/**\n * What a quick-action dialog shows when the workspace has no files yet.\n *\n * Greying the action out says you cannot do it without saying how to get\n * there. Offering the same \"Add\" menu the Files view carries turns the dead\n * end into the first step: add files here, then run the action.\n */\nexport function AddFilesFirst({ reason, action, testId, labels }: AddFilesFirstProps) {\n const l = { ...DEFAULT_ADD_FILES_FIRST_LABELS, ...labels }\n return (\n <div\n className=\"flex flex-col items-center gap-3 rounded-lg border border-border bg-card shadow-xs px-4 py-8 text-center\"\n data-slot=\"add-files-first\"\n data-testid={testId}\n >\n <span className=\"flex size-11 items-center justify-center rounded-full bg-ai-soft text-ai-ink\">\n <FolderPlus className=\"size-5\" aria-hidden=\"true\" />\n </span>\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-semibold text-foreground\">{l.heading}</p>\n <p className=\"text-xs leading-relaxed text-muted-foreground\">{reason}</p>\n </div>\n {action}\n </div>\n )\n}\n","import type { ReactNode } from 'react'\nimport { DialogDescription, DialogHeader, DialogTitle } from '@arbidocs/react/ui'\n\nexport interface QuickActionDialogHeaderProps {\n /** The tile's glyph, sized by the caller at `size-5`. */\n icon: ReactNode\n title: string\n /** One sentence on what the dialog does. Keep it to a line or two. */\n description: ReactNode\n /** Test id for the description, where a test asserts on the copy. */\n descriptionTestId?: string\n}\n\n/**\n * The header every \"Start\" quick-action dialog wears: the tile's own mark in\n * its disc, the action's name, and one line saying what the dialog will do.\n *\n * - The disc centres on the TITLE row, so it reads as the title's mark.\n * - The description keeps the title's left edge (`pl-13` = disc + gap), so the\n * header is one text column beside the mark rather than an L.\n * - `max-w-prose` + `text-pretty` cap the measure and stop two-word orphans.\n * - `pr-8` keeps the copy clear of the close button.\n */\nexport function QuickActionDialogHeader({\n icon,\n title,\n description,\n descriptionTestId,\n}: QuickActionDialogHeaderProps) {\n return (\n <DialogHeader className=\"space-y-0 text-left\" data-slot=\"quick-action-dialog-header\">\n <div className=\"flex items-center gap-3 pr-8\">\n <div className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-ai-soft text-ai-ink\">\n {icon}\n </div>\n <DialogTitle className=\"text-xl font-semibold leading-tight\">{title}</DialogTitle>\n </div>\n <DialogDescription\n className=\"mt-2 max-w-prose pl-13 pr-8 text-sm leading-relaxed text-pretty\"\n data-testid={descriptionTestId}\n >\n {description}\n </DialogDescription>\n </DialogHeader>\n )\n}\n","import { useMemo } from 'react'\n\n/** Which folders and labels a run is limited to. Empty = everything. */\nexport interface DocumentScope {\n /** Folder paths; `''` is the \"unfiled\" bucket. */\n folders: string[]\n /** Label (tag) ids. */\n labels: string[]\n}\n\nexport interface UseScopeSelectionOptions {\n /** Every folder path in the tree (not including the unfiled bucket). */\n folderPaths: string[]\n /** Whether the unfiled bucket is shown as a row. */\n hasUnfiled: boolean\n scope: DocumentScope\n onScopeChange: (scope: DocumentScope) => void\n}\n\n/**\n * Headless selection logic for a folder + label scope picker.\n *\n * Folders use hierarchical selection: ticking \"contracts\" also ticks\n * \"contracts/2024\", and unticking removes the whole subtree. \"All\" ticks every\n * row, which is NOT the same as an empty scope: empty means \"the whole\n * workspace, whatever lands in it later\", all-ticked means \"these folders, as\n * they are now\".\n */\nexport function useScopeSelection({\n folderPaths,\n hasUnfiled,\n scope,\n onScopeChange,\n}: UseScopeSelectionOptions) {\n const selectedFolders = useMemo(() => new Set(scope.folders), [scope.folders])\n const selectedLabels = useMemo(() => new Set(scope.labels), [scope.labels])\n\n /** Every row the folder list shows, \"unfiled\" included. */\n const allRowPaths = useMemo(\n () => [...(hasUnfiled ? [''] : []), ...folderPaths],\n [folderPaths, hasUnfiled]\n )\n const allSelected = allRowPaths.length > 0 && allRowPaths.every((p) => selectedFolders.has(p))\n const someSelected = selectedFolders.size > 0 && !allSelected\n\n const descendants = (path: string) => folderPaths.filter((p) => p.startsWith(path + '/'))\n\n const toggleFolder = (path: string) => {\n const next = new Set(selectedFolders)\n const family = [path, ...descendants(path)]\n if (next.has(path)) for (const p of family) next.delete(p)\n else for (const p of family) next.add(p)\n onScopeChange({ folders: [...next], labels: scope.labels })\n }\n\n const toggleAllFolders = () => {\n onScopeChange({ folders: allSelected ? [] : allRowPaths, labels: scope.labels })\n }\n\n const toggleLabel = (labelId: string) => {\n const next = new Set(selectedLabels)\n if (next.has(labelId)) next.delete(labelId)\n else next.add(labelId)\n onScopeChange({ folders: scope.folders, labels: [...next] })\n }\n\n return {\n selectedFolders,\n selectedLabels,\n allSelected,\n someSelected,\n hasSelection: scope.folders.length > 0 || scope.labels.length > 0,\n toggleFolder,\n toggleAllFolders,\n toggleLabel,\n }\n}\n","import { useMemo, useState } from 'react'\nimport { Check, FileCheck2, Minus } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { prefixedTestId, testIdOr } from '../chat/test-id'\nimport { useScopeSelection, type DocumentScope } from '../lib/useScopeSelection'\nimport { formatCount } from '../lib/format'\nimport { FoldSection } from './FoldSection'\n\n/**\n * Fixed strings of the picker. Passed as `uiLabels`: the `labels` prop is already the list of\n * document labels to scope by.\n */\nexport interface ScopePickerLabels {\n folders: string\n /** The Labels section heading. */\n labelsHeading: string\n all: string\n unfiled: string\n noFoldersYet: string\n noLabelsYet: string\n filesToInclude: string\n /** \"3 of 10 files\". */\n matchedOfTotal: (matched: number, total: number) => string\n selectedSuffix: string\n folderCount: (count: number) => string\n labelCount: (count: number) => string\n left: (count: number) => string\n /** \"All 10 files\". */\n allFiles: (total: number) => string\n includedSuffix: string\n}\n\nconst pluralize = (n: number, word: string) => `${n} ${word}${n !== 1 ? 's' : ''}`\n\nexport const DEFAULT_SCOPE_PICKER_LABELS: ScopePickerLabels = {\n folders: 'Folders',\n labelsHeading: 'Labels',\n all: 'All',\n unfiled: 'Unfiled',\n noFoldersYet: 'No folders yet',\n noLabelsYet: 'No labels yet — label files from the Files page to scope by them here.',\n filesToInclude: 'Files to include',\n matchedOfTotal: (matched, total) =>\n `${matched} of ${formatCount(total, { one: 'file', other: 'files' })}`,\n selectedSuffix: ' selected',\n folderCount: (count) => pluralize(count, 'folder'),\n labelCount: (count) => pluralize(count, 'label'),\n left: (count) => `${count} left`,\n allFiles: (total) => `All ${pluralize(total, 'file')}`,\n includedSuffix: ' in the workspace will be included',\n}\n\nexport interface ScopeFolder {\n /** Full path, e.g. `contracts/2024`. */\n path: string\n /** Last segment, shown. */\n label: string\n /** Documents under this folder. */\n count: number\n depth: number\n}\n\nexport interface ScopeLabel {\n id: string\n name: string\n /** Any CSS colour for the dot. */\n color: string\n /** Documents carrying this label. */\n count: number\n}\n\nexport interface ScopePickerProps {\n folders: ScopeFolder[]\n /** Documents with no folder; the \"Unfiled\" row is hidden at 0. */\n unfiledCount: number\n labels: ScopeLabel[]\n /** All documents the picker is scoping. */\n totalCount: number\n /** Documents the current scope matches (computed by the caller). */\n matchedCount: number\n scope: DocumentScope\n onScopeChange: (scope: DocumentScope) => void\n /** Reword the picker's fixed strings. */\n uiLabels?: Partial<ScopePickerLabels>\n /** Sub-element ids; defaults are the current ids. `labelChip` is a prefix (`<prefix>-<label id>`). */\n testIds?: Partial<\n Record<'foldersToggle' | 'labelsToggle' | 'noLabels' | 'labelChip' | 'summary', string>\n >\n}\n\ninterface FolderRowProps {\n label: string\n count: number\n depth: number\n isActive: boolean\n indeterminate?: boolean\n muted?: boolean\n onToggle: () => void\n}\n\n/** A checkbox row: the row is the checkbox (a nested real one would swallow the click). */\nfunction FolderRow({\n label,\n count,\n depth,\n isActive,\n indeterminate,\n muted,\n onToggle,\n}: FolderRowProps) {\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n role=\"checkbox\"\n aria-checked={indeterminate ? 'mixed' : isActive}\n style={{ paddingLeft: `${4 + depth * 14}px` }}\n className={cn(\n 'flex w-full items-center gap-1 rounded-sm py-1 pr-2 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&:hover]:bg-accent pointer-coarse:min-h-11',\n isActive && 'bg-primary/10',\n muted && !isActive && 'text-muted-foreground'\n )}\n data-slot=\"scope-folder-row\"\n >\n <span className=\"flex w-4 shrink-0 items-center justify-center\">\n <span\n className={cn(\n 'flex size-3.5 items-center justify-center rounded-[3px] border transition-colors',\n isActive || indeterminate\n ? 'border-primary bg-primary text-primary-foreground'\n : 'border-muted-foreground/40'\n )}\n >\n {indeterminate ? (\n <Minus className=\"size-2.5\" />\n ) : (\n isActive && <Check className=\"size-2.5\" />\n )}\n </span>\n </span>\n {label !== '' && (\n <span className={cn('min-w-0 truncate', isActive && 'font-semibold text-primary')}>\n {label}\n </span>\n )}\n <span className=\"ml-auto pl-2 text-sm font-semibold text-muted-foreground\">{count}</span>\n </button>\n )\n}\n\n/**\n * Pick a folder + label scope for a run over documents. Controlled: the caller\n * owns the scope and computes `matchedCount`, so the submit button lives with\n * the caller. Both sections start shut: the default (every file) is what most\n * runs want, and the summary states it plainly.\n */\nexport function ScopePicker({\n folders,\n unfiledCount,\n labels,\n totalCount,\n matchedCount,\n scope,\n onScopeChange,\n uiLabels,\n testIds,\n}: ScopePickerProps) {\n const l = { ...DEFAULT_SCOPE_PICKER_LABELS, ...uiLabels }\n const [foldersOpen, setFoldersOpen] = useState(false)\n const [labelsOpen, setLabelsOpen] = useState(false)\n\n const folderPaths = useMemo(() => folders.map((f) => f.path), [folders])\n const selection = useScopeSelection({\n folderPaths,\n hasUnfiled: unfiledCount > 0,\n scope,\n onScopeChange,\n })\n const hasRows = folders.length > 0 || unfiledCount > 0\n\n return (\n <div className=\"flex flex-col gap-4\" data-slot=\"scope-picker\">\n <FoldSection\n label={l.folders}\n count={scope.folders.length}\n open={foldersOpen}\n onOpenChange={setFoldersOpen}\n testId={testIdOr(testIds?.foldersToggle, { testId: 'scope-picker-folders-toggle' })}\n >\n <div className=\"max-h-[180px] overflow-y-auto rounded-md border border-border p-1\">\n {hasRows && (\n <>\n <FolderRow\n label={l.all}\n count={totalCount}\n depth={0}\n isActive={selection.allSelected}\n indeterminate={selection.someSelected}\n onToggle={selection.toggleAllFolders}\n />\n <div className=\"my-1 border-t border-border\" />\n </>\n )}\n {unfiledCount > 0 && (\n <>\n <FolderRow\n label={l.unfiled}\n count={unfiledCount}\n depth={0}\n isActive={selection.selectedFolders.has('')}\n muted\n onToggle={() => selection.toggleFolder('')}\n />\n {folders.length > 0 && <div className=\"my-1 border-t border-border\" />}\n </>\n )}\n {folders.map((folder) => (\n <FolderRow\n key={folder.path}\n label={folder.label}\n count={folder.count}\n depth={folder.depth}\n isActive={selection.selectedFolders.has(folder.path)}\n onToggle={() => selection.toggleFolder(folder.path)}\n />\n ))}\n {!hasRows && <p className=\"px-2 py-1 text-sm text-muted-foreground\">{l.noFoldersYet}</p>}\n </div>\n </FoldSection>\n\n {/* Always present, like Folders: a workspace with no labels yet reads as\n \"you have not labelled anything\", not \"this cannot scope by label\". */}\n <FoldSection\n label={l.labelsHeading}\n count={scope.labels.length}\n open={labelsOpen}\n onOpenChange={setLabelsOpen}\n testId={testIdOr(testIds?.labelsToggle, { testId: 'scope-picker-labels-toggle' })}\n >\n {labels.length === 0 ? (\n <p\n className=\"px-2 py-1 text-sm text-muted-foreground\"\n data-testid={testIdOr(testIds?.noLabels, { testId: 'scope-picker-no-labels' })}\n >\n {l.noLabelsYet}\n </p>\n ) : (\n <div className=\"flex flex-wrap gap-1.5\">\n {labels.map((label) => {\n const isActive = selection.selectedLabels.has(label.id)\n return (\n <button\n key={label.id}\n type=\"button\"\n onClick={() => selection.toggleLabel(label.id)}\n aria-pressed={isActive}\n data-testid={prefixedTestId(testIds?.labelChip, label.id, {\n testId: `scope-picker-label-chip-${label.id}`,\n })}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11',\n isActive\n ? 'border-primary/40 bg-primary/10 text-primary'\n : 'border-border [&:hover]:bg-accent [&:hover]:text-accent-foreground'\n )}\n >\n <span\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: label.color }}\n />\n <span className=\"truncate\">{label.name}</span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">({label.count})</span>\n {isActive && <Check className=\"size-3.5 shrink-0\" />}\n </button>\n )\n })}\n </div>\n )}\n </FoldSection>\n\n <div\n className=\"rounded-lg bg-muted p-3\"\n data-testid={testIdOr(testIds?.summary, { testId: 'scope-picker-summary' })}\n >\n <div className=\"mb-2 flex items-center gap-1.5 text-xs font-medium text-muted-foreground\">\n <FileCheck2 className=\"size-4\" />\n {l.filesToInclude}\n </div>\n {selection.hasSelection ? (\n <div className=\"text-sm text-muted-foreground\">\n <span className=\"text-base font-semibold text-foreground\">\n {l.matchedOfTotal(matchedCount, totalCount)}\n </span>\n {l.selectedSuffix}\n {scope.folders.length > 0 && <span> — {l.folderCount(scope.folders.length)}</span>}\n {scope.folders.length > 0 && scope.labels.length > 0 && <span>, </span>}\n {scope.labels.length > 0 && <span>{l.labelCount(scope.labels.length)}</span>}\n <span className=\"text-xs\">\n {' · '}\n {l.left(totalCount - matchedCount)}\n </span>\n </div>\n ) : (\n <div className=\"text-sm text-muted-foreground\">\n <span className=\"text-base font-semibold text-foreground\">\n {l.allFiles(totalCount)}\n </span>\n {l.includedSuffix}\n </div>\n )}\n </div>\n </div>\n )\n}\n","import type { ReactNode } from 'react'\nimport { Plus, type LucideIcon } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { ActionLink } from '../components/ActionLink'\n\n/** The small label that names a band of a page (\"Start\", \"Recent\"). */\nexport function SectionLabel({\n children,\n className,\n testId,\n}: {\n children: ReactNode\n className?: string\n testId?: string\n}) {\n return (\n <p\n className={cn(\n 'flex items-center gap-3 text-sm font-semibold text-foreground after:h-px after:flex-1 after:bg-border',\n className\n )}\n data-slot=\"section-label\"\n data-testid={testId}\n >\n {children}\n </p>\n )\n}\n\nexport interface SectionHeaderProps {\n icon?: LucideIcon\n title: string\n count?: number\n /** Makes the title a button (open the full list). Omit for a static title. */\n onOpen?: () => void\n /** Real link target of the title; `onOpen` still fires on a plain click. */\n openHref?: string\n /** Test id of the title button. */\n openTestId?: string\n /** The section's one quiet action, rendered at the trailing edge. */\n action?: ReactNode\n className?: string\n}\n\n/** Title + count + one quiet action: the head of a list section. */\nexport function SectionHeader({\n icon: Icon,\n title,\n count,\n onOpen,\n openHref,\n openTestId,\n action,\n className,\n}: SectionHeaderProps) {\n return (\n <div\n className={cn('flex items-center justify-between gap-2', className)}\n data-slot=\"section-header\"\n >\n <ActionLink\n href={openHref}\n onSelect={onOpen}\n disabled={!onOpen && !openHref}\n className=\"flex min-w-0 cursor-pointer items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:text-foreground pointer-coarse:min-h-11\"\n data-testid={openTestId}\n >\n {Icon && <Icon className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />}\n <span className=\"whitespace-nowrap text-sm font-semibold text-foreground\">{title}</span>\n {count !== undefined && (\n <span className=\"text-sm tabular-nums text-muted-foreground\">{count}</span>\n )}\n </ActionLink>\n {action && <div className=\"shrink-0\">{action}</div>}\n </div>\n )\n}\n\nexport interface EmptyStripProps {\n icon?: LucideIcon\n label: ReactNode\n description?: ReactNode\n onSelect?: () => void\n testId?: string\n}\n\n/** One tonal strip that stands in for an empty section: an invitation, not a void. */\nexport function EmptyStrip({\n icon: Icon = Plus,\n label,\n description,\n onSelect,\n testId,\n}: EmptyStripProps) {\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n disabled={!onSelect}\n className=\"flex w-full items-center gap-3 rounded-lg px-4 py-3 text-left border border-border bg-card shadow-xs transition-colors hover:border-ring/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:bg-card disabled:hover:border-border pointer-coarse:min-h-11\"\n data-slot=\"empty-strip\"\n data-testid={testId}\n >\n <Icon className=\"size-5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-sm font-semibold text-foreground\">{label}</span>\n {description && (\n <span className=\"block text-sm leading-snug text-muted-foreground\">{description}</span>\n )}\n </span>\n </button>\n )\n}\n","/**\n * The column count for a grid of `count` equal tiles in `width` pixels, chosen so the last row is\n * never left with a lone orphan: of the counts that fit (each column at least `minTile` wide),\n * take the one that leaves the fewest empty cells in the last row, preferring more columns on a\n * tie (never fewer than two columns when two fit). Four tiles resolve to 2x2 where three would fit\n * and 4 across when there is room for four; six resolve to 3x2.\n */\nexport function balancedColumns(count: number, width: number, minTile = 200, gap = 12): number {\n if (count <= 1) return 1\n const fit = Math.max(1, Math.min(count, Math.floor((width + gap) / (minTile + gap))))\n if (fit < 2) return 1\n let best = 2\n let bestEmpty = Number.POSITIVE_INFINITY\n for (let columns = 2; columns <= fit; columns++) {\n const empty = (columns - (count % columns)) % columns\n if (empty < bestEmpty || (empty === bestEmpty && columns > best)) {\n best = columns\n bestEmpty = empty\n }\n }\n return best\n}\n","import { useEffect, useRef, useState, type ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { ActionLink } from '../components/ActionLink'\nimport { balancedColumns } from '../lib/balanced-columns'\nimport { observeResize } from '../lib/observe-resize'\nimport { SectionLabel } from './sections'\n\n/** One \"start something\" tile: an AI-driven action, one click from underway. */\nexport interface StartTile {\n id: string\n /** A Lucide glyph; `iconNode` wins when both are given. */\n icon?: LucideIcon\n /** A ready-made mark (host icon maps), wearing the same AI ink. */\n iconNode?: ReactNode\n title: string\n description: string\n onSelect: () => void\n /** Real link target (ctrl-click / new tab, in-SPA through the host environment's `linkComponent`). `onSelect` still fires on a plain click. */\n href?: string\n /** Rendered non-interactive and dimmed; `disabledReason` replaces the description. */\n disabled?: boolean\n disabledReason?: string\n testId?: string\n}\n\nexport interface StartTilesProps {\n items: StartTile[]\n /** Optional band label above the tiles (\"Start\"). */\n caption?: string\n captionTestId?: string\n className?: string\n testId?: string\n}\n\n/**\n * A band of tonal action tiles. The glyph wears the AI ink, because these are\n * the AI-driven starts. Nothing is hover-only: tiles are always rendered, hover\n * only nudges tone, and touch targets are at least 44px.\n */\nexport function StartTiles({ items, caption, captionTestId, className, testId }: StartTilesProps) {\n // The tiles reflow by their own container width (a docked tablet sidebar narrows it without\n // changing the viewport), choosing a column count that never leaves one tile alone on a row.\n const gridRef = useRef<HTMLDivElement>(null)\n const [columns, setColumns] = useState<number | null>(null)\n const count = items.length\n useEffect(() => {\n const grid = gridRef.current\n if (!grid || count === 0) return\n const measure = () => setColumns(balancedColumns(count, grid.clientWidth))\n measure()\n return observeResize(grid, measure)\n }, [count])\n\n if (count === 0) return null\n return (\n <section\n className={cn('flex flex-col gap-3', className)}\n data-slot=\"start-tiles\"\n data-testid={testId}\n >\n {caption && <SectionLabel testId={captionTestId}>{caption}</SectionLabel>}\n <div\n ref={gridRef}\n data-slot=\"start-tiles-grid\"\n data-columns={columns ?? undefined}\n className=\"grid gap-3\"\n style={{\n gridTemplateColumns:\n columns === null\n ? 'repeat(auto-fit, minmax(200px, 1fr))'\n : `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {items.map((item) => {\n const Icon = item.icon\n return (\n <ActionLink\n key={item.id}\n href={item.href}\n onSelect={item.onSelect}\n disabled={item.disabled}\n title={item.disabled ? item.disabledReason : undefined}\n className={cn(\n 'flex h-full flex-col items-start gap-3 rounded-lg p-4 text-left',\n 'border border-border bg-card shadow-xs transition-colors hover:border-ring/50 hover:bg-accent/40',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'disabled:cursor-default disabled:opacity-55 disabled:hover:bg-card disabled:hover:border-border',\n 'pointer-coarse:min-h-11'\n )}\n data-slot=\"start-tile\"\n data-testid={item.testId}\n >\n {item.iconNode ? (\n <span className=\"shrink-0 text-ai-ink [&>svg]:size-5\" aria-hidden=\"true\">\n {item.iconNode}\n </span>\n ) : (\n Icon && <Icon className=\"size-5 shrink-0 text-ai-ink\" aria-hidden=\"true\" />\n )}\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-semibold text-foreground\">{item.title}</span>\n <span className=\"mt-1 block text-sm leading-snug text-muted-foreground\">\n {item.disabled && item.disabledReason ? item.disabledReason : item.description}\n </span>\n </span>\n </ActionLink>\n )\n })}\n </div>\n </section>\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/home/FoldSection.tsx","../src/home/AddFilesFirst.tsx","../src/home/QuickActionDialogHeader.tsx","../src/lib/useScopeSelection.ts","../src/home/ScopePicker.tsx","../src/home/sections.tsx","../src/lib/balanced-columns.ts","../src/home/StartTiles.tsx"],"names":["jsxs","jsx","cn","useMemo","useState"],"mappings":"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","file":"chunk-I4HFM5HB.js","sourcesContent":["import type { ReactNode } from 'react'\nimport { ChevronDown } from 'lucide-react'\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger, cn } from '@arbidocs/react/ui'\n\nexport interface FoldSectionProps {\n label: string\n /** Shown as plain muted text beside the name; omit or pass 0 to hide. */\n count?: number\n /** A word or two of state for sections a count cannot summarise (shown while shut). */\n hint?: string\n open: boolean\n onOpenChange: (open: boolean) => void\n /** Trigger test id; the caller owns the naming. */\n testId: string\n children: ReactNode\n}\n\n/**\n * One foldable block of a dialog. The chevron and the name are the whole hit\n * target (44px on touch), and the count keeps the selection readable while the\n * section is shut, so you need not re-open it to check.\n */\nexport function FoldSection({\n label,\n count = 0,\n hint,\n open,\n onOpenChange,\n testId,\n children,\n}: FoldSectionProps) {\n return (\n <Collapsible open={open} onOpenChange={onOpenChange} data-slot=\"fold-section\">\n <CollapsibleTrigger\n className=\"mb-1.5 flex w-full items-center gap-1.5 rounded-md text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&:hover]:text-muted-foreground pointer-coarse:min-h-11\"\n data-testid={testId}\n >\n <ChevronDown\n className={cn('size-4 shrink-0 transition-transform', !open && '-rotate-90')}\n />\n <span>{label}</span>\n {count > 0 && <span className=\"text-xs tabular-nums text-muted-foreground\">{count}</span>}\n {hint && !open && (\n <span className=\"min-w-0 truncate text-xs font-normal text-muted-foreground\">{hint}</span>\n )}\n </CollapsibleTrigger>\n <CollapsibleContent>{children}</CollapsibleContent>\n </Collapsible>\n )\n}\n","import type { ReactNode } from 'react'\nimport { FolderPlus } from 'lucide-react'\n\n/** Fixed strings of the empty-files state. */\nexport interface AddFilesFirstLabels {\n heading: string\n}\n\nexport const DEFAULT_ADD_FILES_FIRST_LABELS: AddFilesFirstLabels = {\n heading: 'Add files first',\n}\n\nexport interface AddFilesFirstProps {\n /** Why this action needs files, in the action's own words. */\n reason: string\n /** The host's own \"Add\" control. */\n action: ReactNode\n testId?: string\n labels?: Partial<AddFilesFirstLabels>\n}\n\n/**\n * What a quick-action dialog shows when the workspace has no files yet.\n *\n * Greying the action out says you cannot do it without saying how to get\n * there. Offering the same \"Add\" menu the Files view carries turns the dead\n * end into the first step: add files here, then run the action.\n */\nexport function AddFilesFirst({ reason, action, testId, labels }: AddFilesFirstProps) {\n const l = { ...DEFAULT_ADD_FILES_FIRST_LABELS, ...labels }\n return (\n <div\n className=\"flex flex-col items-center gap-3 rounded-lg border border-border bg-card shadow-xs px-4 py-8 text-center\"\n data-slot=\"add-files-first\"\n data-testid={testId}\n >\n <span className=\"flex size-11 items-center justify-center rounded-full bg-ai-soft text-ai-ink\">\n <FolderPlus className=\"size-5\" aria-hidden=\"true\" />\n </span>\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-semibold text-foreground\">{l.heading}</p>\n <p className=\"text-xs leading-relaxed text-muted-foreground\">{reason}</p>\n </div>\n {action}\n </div>\n )\n}\n","import type { ReactNode } from 'react'\nimport { DialogDescription, DialogHeader, DialogTitle } from '@arbidocs/react/ui'\n\nexport interface QuickActionDialogHeaderProps {\n /** The tile's glyph, sized by the caller at `size-5`. */\n icon: ReactNode\n title: string\n /** One sentence on what the dialog does. Keep it to a line or two. */\n description: ReactNode\n /** Test id for the description, where a test asserts on the copy. */\n descriptionTestId?: string\n}\n\n/**\n * The header every \"Start\" quick-action dialog wears: the tile's own mark in\n * its disc, the action's name, and one line saying what the dialog will do.\n *\n * - The disc centres on the TITLE row, so it reads as the title's mark.\n * - The description keeps the title's left edge (`pl-13` = disc + gap), so the\n * header is one text column beside the mark rather than an L.\n * - `max-w-prose` + `text-pretty` cap the measure and stop two-word orphans.\n * - `pr-8` keeps the copy clear of the close button.\n */\nexport function QuickActionDialogHeader({\n icon,\n title,\n description,\n descriptionTestId,\n}: QuickActionDialogHeaderProps) {\n return (\n <DialogHeader className=\"space-y-0 text-left\" data-slot=\"quick-action-dialog-header\">\n <div className=\"flex items-center gap-3 pr-8\">\n <div className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-ai-soft text-ai-ink\">\n {icon}\n </div>\n <DialogTitle className=\"text-xl font-semibold leading-tight\">{title}</DialogTitle>\n </div>\n <DialogDescription\n className=\"mt-2 max-w-prose pl-13 pr-8 text-sm leading-relaxed text-pretty\"\n data-testid={descriptionTestId}\n >\n {description}\n </DialogDescription>\n </DialogHeader>\n )\n}\n","import { useMemo } from 'react'\n\n/** Which folders and labels a run is limited to. Empty = everything. */\nexport interface DocumentScope {\n /** Folder paths; `''` is the \"unfiled\" bucket. */\n folders: string[]\n /** Label (tag) ids. */\n labels: string[]\n}\n\nexport interface UseScopeSelectionOptions {\n /** Every folder path in the tree (not including the unfiled bucket). */\n folderPaths: string[]\n /** Whether the unfiled bucket is shown as a row. */\n hasUnfiled: boolean\n scope: DocumentScope\n onScopeChange: (scope: DocumentScope) => void\n}\n\n/**\n * Headless selection logic for a folder + label scope picker.\n *\n * Folders use hierarchical selection: ticking \"contracts\" also ticks\n * \"contracts/2024\", and unticking removes the whole subtree. \"All\" ticks every\n * row, which is NOT the same as an empty scope: empty means \"the whole\n * workspace, whatever lands in it later\", all-ticked means \"these folders, as\n * they are now\".\n */\nexport function useScopeSelection({\n folderPaths,\n hasUnfiled,\n scope,\n onScopeChange,\n}: UseScopeSelectionOptions) {\n const selectedFolders = useMemo(() => new Set(scope.folders), [scope.folders])\n const selectedLabels = useMemo(() => new Set(scope.labels), [scope.labels])\n\n /** Every row the folder list shows, \"unfiled\" included. */\n const allRowPaths = useMemo(\n () => [...(hasUnfiled ? [''] : []), ...folderPaths],\n [folderPaths, hasUnfiled]\n )\n const allSelected = allRowPaths.length > 0 && allRowPaths.every((p) => selectedFolders.has(p))\n const someSelected = selectedFolders.size > 0 && !allSelected\n\n const descendants = (path: string) => folderPaths.filter((p) => p.startsWith(path + '/'))\n\n const toggleFolder = (path: string) => {\n const next = new Set(selectedFolders)\n const family = [path, ...descendants(path)]\n if (next.has(path)) for (const p of family) next.delete(p)\n else for (const p of family) next.add(p)\n onScopeChange({ folders: [...next], labels: scope.labels })\n }\n\n const toggleAllFolders = () => {\n onScopeChange({ folders: allSelected ? [] : allRowPaths, labels: scope.labels })\n }\n\n const toggleLabel = (labelId: string) => {\n const next = new Set(selectedLabels)\n if (next.has(labelId)) next.delete(labelId)\n else next.add(labelId)\n onScopeChange({ folders: scope.folders, labels: [...next] })\n }\n\n return {\n selectedFolders,\n selectedLabels,\n allSelected,\n someSelected,\n hasSelection: scope.folders.length > 0 || scope.labels.length > 0,\n toggleFolder,\n toggleAllFolders,\n toggleLabel,\n }\n}\n","import { useMemo, useState } from 'react'\nimport { Check, FileCheck2, Minus } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { prefixedTestId, testIdOr } from '../chat/test-id'\nimport { useScopeSelection, type DocumentScope } from '../lib/useScopeSelection'\nimport { formatCount } from '../lib/format'\nimport { FoldSection } from './FoldSection'\n\n/**\n * Fixed strings of the picker. Passed as `uiLabels`: the `labels` prop is already the list of\n * document labels to scope by.\n */\nexport interface ScopePickerLabels {\n folders: string\n /** The Labels section heading. */\n labelsHeading: string\n all: string\n unfiled: string\n noFoldersYet: string\n noLabelsYet: string\n filesToInclude: string\n /** \"3 of 10 files\". */\n matchedOfTotal: (matched: number, total: number) => string\n selectedSuffix: string\n folderCount: (count: number) => string\n labelCount: (count: number) => string\n left: (count: number) => string\n /** \"All 10 files\". */\n allFiles: (total: number) => string\n includedSuffix: string\n}\n\nconst pluralize = (n: number, word: string) => `${n} ${word}${n !== 1 ? 's' : ''}`\n\nexport const DEFAULT_SCOPE_PICKER_LABELS: ScopePickerLabels = {\n folders: 'Folders',\n labelsHeading: 'Labels',\n all: 'All',\n unfiled: 'Unfiled',\n noFoldersYet: 'No folders yet',\n noLabelsYet: 'No labels yet — label files from the Files page to scope by them here.',\n filesToInclude: 'Files to include',\n matchedOfTotal: (matched, total) =>\n `${matched} of ${formatCount(total, { one: 'file', other: 'files' })}`,\n selectedSuffix: ' selected',\n folderCount: (count) => pluralize(count, 'folder'),\n labelCount: (count) => pluralize(count, 'label'),\n left: (count) => `${count} left`,\n allFiles: (total) => `All ${pluralize(total, 'file')}`,\n includedSuffix: ' in the workspace will be included',\n}\n\nexport interface ScopeFolder {\n /** Full path, e.g. `contracts/2024`. */\n path: string\n /** Last segment, shown. */\n label: string\n /** Documents under this folder. */\n count: number\n depth: number\n}\n\nexport interface ScopeLabel {\n id: string\n name: string\n /** Any CSS colour for the dot. */\n color: string\n /** Documents carrying this label. */\n count: number\n}\n\nexport interface ScopePickerProps {\n folders: ScopeFolder[]\n /** Documents with no folder; the \"Unfiled\" row is hidden at 0. */\n unfiledCount: number\n labels: ScopeLabel[]\n /** All documents the picker is scoping. */\n totalCount: number\n /** Documents the current scope matches (computed by the caller). */\n matchedCount: number\n scope: DocumentScope\n onScopeChange: (scope: DocumentScope) => void\n /** Reword the picker's fixed strings. */\n uiLabels?: Partial<ScopePickerLabels>\n /** Sub-element ids; defaults are the current ids. `labelChip` is a prefix (`<prefix>-<label id>`). */\n testIds?: Partial<\n Record<'foldersToggle' | 'labelsToggle' | 'noLabels' | 'labelChip' | 'summary', string>\n >\n}\n\ninterface FolderRowProps {\n label: string\n count: number\n depth: number\n isActive: boolean\n indeterminate?: boolean\n muted?: boolean\n onToggle: () => void\n}\n\n/** A checkbox row: the row is the checkbox (a nested real one would swallow the click). */\nfunction FolderRow({\n label,\n count,\n depth,\n isActive,\n indeterminate,\n muted,\n onToggle,\n}: FolderRowProps) {\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n role=\"checkbox\"\n aria-checked={indeterminate ? 'mixed' : isActive}\n style={{ paddingLeft: `${4 + depth * 14}px` }}\n className={cn(\n 'flex w-full items-center gap-1 rounded-sm py-1 pr-2 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&:hover]:bg-accent pointer-coarse:min-h-11',\n isActive && 'bg-primary/10',\n muted && !isActive && 'text-muted-foreground'\n )}\n data-slot=\"scope-folder-row\"\n >\n <span className=\"flex w-4 shrink-0 items-center justify-center\">\n <span\n className={cn(\n 'flex size-3.5 items-center justify-center rounded-[3px] border transition-colors',\n isActive || indeterminate\n ? 'border-primary bg-primary text-primary-foreground'\n : 'border-muted-foreground/40'\n )}\n >\n {indeterminate ? (\n <Minus className=\"size-2.5\" />\n ) : (\n isActive && <Check className=\"size-2.5\" />\n )}\n </span>\n </span>\n {label !== '' && (\n <span className={cn('min-w-0 truncate', isActive && 'font-semibold text-primary')}>\n {label}\n </span>\n )}\n <span className=\"ml-auto pl-2 text-sm font-semibold text-muted-foreground\">{count}</span>\n </button>\n )\n}\n\n/**\n * Pick a folder + label scope for a run over documents. Controlled: the caller\n * owns the scope and computes `matchedCount`, so the submit button lives with\n * the caller. Both sections start shut: the default (every file) is what most\n * runs want, and the summary states it plainly.\n */\nexport function ScopePicker({\n folders,\n unfiledCount,\n labels,\n totalCount,\n matchedCount,\n scope,\n onScopeChange,\n uiLabels,\n testIds,\n}: ScopePickerProps) {\n const l = { ...DEFAULT_SCOPE_PICKER_LABELS, ...uiLabels }\n const [foldersOpen, setFoldersOpen] = useState(false)\n const [labelsOpen, setLabelsOpen] = useState(false)\n\n const folderPaths = useMemo(() => folders.map((f) => f.path), [folders])\n const selection = useScopeSelection({\n folderPaths,\n hasUnfiled: unfiledCount > 0,\n scope,\n onScopeChange,\n })\n const hasRows = folders.length > 0 || unfiledCount > 0\n\n return (\n <div className=\"flex flex-col gap-4\" data-slot=\"scope-picker\">\n <FoldSection\n label={l.folders}\n count={scope.folders.length}\n open={foldersOpen}\n onOpenChange={setFoldersOpen}\n testId={testIdOr(testIds?.foldersToggle, { testId: 'scope-picker-folders-toggle' })}\n >\n <div className=\"max-h-[180px] overflow-y-auto rounded-md border border-border p-1\">\n {hasRows && (\n <>\n <FolderRow\n label={l.all}\n count={totalCount}\n depth={0}\n isActive={selection.allSelected}\n indeterminate={selection.someSelected}\n onToggle={selection.toggleAllFolders}\n />\n <div className=\"my-1 border-t border-border\" />\n </>\n )}\n {unfiledCount > 0 && (\n <>\n <FolderRow\n label={l.unfiled}\n count={unfiledCount}\n depth={0}\n isActive={selection.selectedFolders.has('')}\n muted\n onToggle={() => selection.toggleFolder('')}\n />\n {folders.length > 0 && <div className=\"my-1 border-t border-border\" />}\n </>\n )}\n {folders.map((folder) => (\n <FolderRow\n key={folder.path}\n label={folder.label}\n count={folder.count}\n depth={folder.depth}\n isActive={selection.selectedFolders.has(folder.path)}\n onToggle={() => selection.toggleFolder(folder.path)}\n />\n ))}\n {!hasRows && <p className=\"px-2 py-1 text-sm text-muted-foreground\">{l.noFoldersYet}</p>}\n </div>\n </FoldSection>\n\n {/* Always present, like Folders: a workspace with no labels yet reads as\n \"you have not labelled anything\", not \"this cannot scope by label\". */}\n <FoldSection\n label={l.labelsHeading}\n count={scope.labels.length}\n open={labelsOpen}\n onOpenChange={setLabelsOpen}\n testId={testIdOr(testIds?.labelsToggle, { testId: 'scope-picker-labels-toggle' })}\n >\n {labels.length === 0 ? (\n <p\n className=\"px-2 py-1 text-sm text-muted-foreground\"\n data-testid={testIdOr(testIds?.noLabels, { testId: 'scope-picker-no-labels' })}\n >\n {l.noLabelsYet}\n </p>\n ) : (\n <div className=\"flex flex-wrap gap-1.5\">\n {labels.map((label) => {\n const isActive = selection.selectedLabels.has(label.id)\n return (\n <button\n key={label.id}\n type=\"button\"\n onClick={() => selection.toggleLabel(label.id)}\n aria-pressed={isActive}\n data-testid={prefixedTestId(testIds?.labelChip, label.id, {\n testId: `scope-picker-label-chip-${label.id}`,\n })}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11',\n isActive\n ? 'border-primary/40 bg-primary/10 text-primary'\n : 'border-border [&:hover]:bg-accent [&:hover]:text-accent-foreground'\n )}\n >\n <span\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: label.color }}\n />\n <span className=\"truncate\">{label.name}</span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">({label.count})</span>\n {isActive && <Check className=\"size-3.5 shrink-0\" />}\n </button>\n )\n })}\n </div>\n )}\n </FoldSection>\n\n <div\n className=\"rounded-lg bg-muted p-3\"\n data-testid={testIdOr(testIds?.summary, { testId: 'scope-picker-summary' })}\n >\n <div className=\"mb-2 flex items-center gap-1.5 text-xs font-medium text-muted-foreground\">\n <FileCheck2 className=\"size-4\" />\n {l.filesToInclude}\n </div>\n {selection.hasSelection ? (\n <div className=\"text-sm text-muted-foreground\">\n <span className=\"text-base font-semibold text-foreground\">\n {l.matchedOfTotal(matchedCount, totalCount)}\n </span>\n {l.selectedSuffix}\n {scope.folders.length > 0 && <span> — {l.folderCount(scope.folders.length)}</span>}\n {scope.folders.length > 0 && scope.labels.length > 0 && <span>, </span>}\n {scope.labels.length > 0 && <span>{l.labelCount(scope.labels.length)}</span>}\n <span className=\"text-xs\">\n {' · '}\n {l.left(totalCount - matchedCount)}\n </span>\n </div>\n ) : (\n <div className=\"text-sm text-muted-foreground\">\n <span className=\"text-base font-semibold text-foreground\">\n {l.allFiles(totalCount)}\n </span>\n {l.includedSuffix}\n </div>\n )}\n </div>\n </div>\n )\n}\n","import type { ReactNode } from 'react'\nimport { Plus, type LucideIcon } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { ActionLink } from '../components/ActionLink'\n\n/** The small label that names a band of a page (\"Start\", \"Recent\"). */\nexport function SectionLabel({\n children,\n className,\n testId,\n}: {\n children: ReactNode\n className?: string\n testId?: string\n}) {\n return (\n <p\n className={cn(\n 'flex items-center gap-3 text-sm font-semibold text-foreground after:h-px after:flex-1 after:bg-border',\n className\n )}\n data-slot=\"section-label\"\n data-testid={testId}\n >\n {children}\n </p>\n )\n}\n\nexport interface SectionHeaderProps {\n icon?: LucideIcon\n title: string\n count?: number\n /** Makes the title a button (open the full list). Omit for a static title. */\n onOpen?: () => void\n /** Real link target of the title; `onOpen` still fires on a plain click. */\n openHref?: string\n /** Test id of the title button. */\n openTestId?: string\n /** The section's one quiet action, rendered at the trailing edge. */\n action?: ReactNode\n className?: string\n}\n\n/** Title + count + one quiet action: the head of a list section. */\nexport function SectionHeader({\n icon: Icon,\n title,\n count,\n onOpen,\n openHref,\n openTestId,\n action,\n className,\n}: SectionHeaderProps) {\n return (\n <div\n className={cn('flex items-center justify-between gap-2', className)}\n data-slot=\"section-header\"\n >\n <ActionLink\n href={openHref}\n onSelect={onOpen}\n disabled={!onOpen && !openHref}\n className=\"flex min-w-0 cursor-pointer items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:text-foreground pointer-coarse:min-h-11\"\n data-testid={openTestId}\n >\n {Icon && <Icon className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />}\n <span className=\"whitespace-nowrap text-sm font-semibold text-foreground\">{title}</span>\n {count !== undefined && (\n <span className=\"text-sm tabular-nums text-muted-foreground\">{count}</span>\n )}\n </ActionLink>\n {action && <div className=\"shrink-0\">{action}</div>}\n </div>\n )\n}\n\nexport interface EmptyStripProps {\n icon?: LucideIcon\n label: ReactNode\n description?: ReactNode\n onSelect?: () => void\n testId?: string\n}\n\n/** One tonal strip that stands in for an empty section: an invitation, not a void. */\nexport function EmptyStrip({\n icon: Icon = Plus,\n label,\n description,\n onSelect,\n testId,\n}: EmptyStripProps) {\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n disabled={!onSelect}\n className=\"flex w-full items-center gap-3 rounded-lg px-4 py-3 text-left border border-border bg-card shadow-xs transition-colors hover:border-ring/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:bg-card disabled:hover:border-border pointer-coarse:min-h-11\"\n data-slot=\"empty-strip\"\n data-testid={testId}\n >\n <Icon className=\"size-5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-sm font-semibold text-foreground\">{label}</span>\n {description && (\n <span className=\"block text-sm leading-snug text-muted-foreground\">{description}</span>\n )}\n </span>\n </button>\n )\n}\n","/**\n * The column count for a grid of `count` equal tiles in `width` pixels, chosen so the last row is\n * never left with a lone orphan: of the counts that fit (each column at least `minTile` wide),\n * take the one that leaves the fewest empty cells in the last row, preferring more columns on a\n * tie (never fewer than two columns when two fit). Four tiles resolve to 2x2 where three would fit\n * and 4 across when there is room for four; six resolve to 3x2.\n */\nexport function balancedColumns(count: number, width: number, minTile = 200, gap = 12): number {\n if (count <= 1) return 1\n const fit = Math.max(1, Math.min(count, Math.floor((width + gap) / (minTile + gap))))\n if (fit < 2) return 1\n let best = 2\n let bestEmpty = Number.POSITIVE_INFINITY\n for (let columns = 2; columns <= fit; columns++) {\n const empty = (columns - (count % columns)) % columns\n if (empty < bestEmpty || (empty === bestEmpty && columns > best)) {\n best = columns\n bestEmpty = empty\n }\n }\n return best\n}\n","import { useEffect, useRef, useState, type ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { ActionLink } from '../components/ActionLink'\nimport { balancedColumns } from '../lib/balanced-columns'\nimport { observeResize } from '../lib/observe-resize'\nimport { SectionLabel } from './sections'\n\n/** One \"start something\" tile: an AI-driven action, one click from underway. */\nexport interface StartTile {\n id: string\n /** A Lucide glyph; `iconNode` wins when both are given. */\n icon?: LucideIcon\n /** A ready-made mark (host icon maps), wearing the same AI ink. */\n iconNode?: ReactNode\n title: string\n description: string\n onSelect: () => void\n /** Real link target (ctrl-click / new tab, in-SPA through the host environment's `linkComponent`). `onSelect` still fires on a plain click. */\n href?: string\n /** Rendered non-interactive and dimmed; `disabledReason` replaces the description. */\n disabled?: boolean\n disabledReason?: string\n testId?: string\n}\n\nexport interface StartTilesProps {\n items: StartTile[]\n /** Optional band label above the tiles (\"Start\"). */\n caption?: string\n captionTestId?: string\n className?: string\n testId?: string\n}\n\n/**\n * A band of tonal action tiles. The glyph wears the AI ink, because these are\n * the AI-driven starts. Nothing is hover-only: tiles are always rendered, hover\n * only nudges tone, and touch targets are at least 44px.\n */\nexport function StartTiles({ items, caption, captionTestId, className, testId }: StartTilesProps) {\n // The tiles reflow by their own container width (a docked tablet sidebar narrows it without\n // changing the viewport), choosing a column count that never leaves one tile alone on a row.\n const gridRef = useRef<HTMLDivElement>(null)\n const [columns, setColumns] = useState<number | null>(null)\n const count = items.length\n useEffect(() => {\n const grid = gridRef.current\n if (!grid || count === 0) return\n const measure = () => setColumns(balancedColumns(count, grid.clientWidth))\n measure()\n return observeResize(grid, measure)\n }, [count])\n\n if (count === 0) return null\n return (\n <section\n className={cn('flex flex-col gap-3', className)}\n data-slot=\"start-tiles\"\n data-testid={testId}\n >\n {caption && <SectionLabel testId={captionTestId}>{caption}</SectionLabel>}\n <div\n ref={gridRef}\n data-slot=\"start-tiles-grid\"\n data-columns={columns ?? undefined}\n className=\"grid gap-3\"\n style={{\n gridTemplateColumns:\n columns === null\n ? 'repeat(auto-fit, minmax(200px, 1fr))'\n : `repeat(${columns}, minmax(0, 1fr))`,\n }}\n >\n {items.map((item) => {\n const Icon = item.icon\n return (\n <ActionLink\n key={item.id}\n href={item.href}\n onSelect={item.onSelect}\n disabled={item.disabled}\n title={item.disabled ? item.disabledReason : undefined}\n className={cn(\n 'flex h-full flex-col items-start gap-3 rounded-lg p-4 text-left',\n 'border border-border bg-card shadow-xs transition-colors hover:border-ring/50 hover:bg-accent/40',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n 'disabled:cursor-default disabled:opacity-55 disabled:hover:bg-card disabled:hover:border-border',\n 'pointer-coarse:min-h-11'\n )}\n data-slot=\"start-tile\"\n data-testid={item.testId}\n >\n {item.iconNode ? (\n <span className=\"shrink-0 text-ai-ink [&>svg]:size-5\" aria-hidden=\"true\">\n {item.iconNode}\n </span>\n ) : (\n Icon && <Icon className=\"size-5 shrink-0 text-ai-ink\" aria-hidden=\"true\" />\n )}\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-semibold text-foreground\">{item.title}</span>\n <span className=\"mt-1 block text-sm leading-snug text-muted-foreground\">\n {item.disabled && item.disabledReason ? item.disabledReason : item.description}\n </span>\n </span>\n </ActionLink>\n )\n })}\n </div>\n </section>\n )\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var chunkOU3RGQFX_cjs = require('./chunk-OU3RGQFX.cjs');
5
- var chunkTQUYWUEE_cjs = require('./chunk-TQUYWUEE.cjs');
5
+ var chunkVIV56KDN_cjs = require('./chunk-VIV56KDN.cjs');
6
6
  var chunkQZTNTP75_cjs = require('./chunk-QZTNTP75.cjs');
7
7
  var chunkWP4VIOOA_cjs = require('./chunk-WP4VIOOA.cjs');
8
8
  var react = require('@arbidocs/react');
@@ -376,7 +376,7 @@ function planBadgeView(project) {
376
376
  }
377
377
  function projectGroupBilling(project, { isOwned, stripeEnabled }) {
378
378
  if (!project) return {};
379
- const planBadge = isOwned && stripeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(chunkTQUYWUEE_cjs.PlanBadge, { ...planBadgeView(project) }) : void 0;
379
+ const planBadge = isOwned && stripeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(chunkVIV56KDN_cjs.PlanBadge, { ...planBadgeView(project) }) : void 0;
380
380
  const quotaItems = project.quotas ? getQuotaItems(project.quotas) : [];
381
381
  const readout = project.quotas ? /* @__PURE__ */ jsxRuntime.jsx(
382
382
  chunkOU3RGQFX_cjs.ProjectReadout,
@@ -386,7 +386,7 @@ function projectGroupBilling(project, { isOwned, stripeEnabled }) {
386
386
  testId: `project-usage-${project.external_id}`,
387
387
  // Bare numbers without plans for sale: a glance beside the name, not the Plan & Usage detail.
388
388
  node: /* @__PURE__ */ jsxRuntime.jsx(
389
- chunkTQUYWUEE_cjs.QuotaMeter,
389
+ chunkVIV56KDN_cjs.QuotaMeter,
390
390
  {
391
391
  items: quotaItems,
392
392
  size: "sm",
@@ -485,5 +485,5 @@ exports.totalWithTaxWord = totalWithTaxWord;
485
485
  exports.useBillingSupport = useBillingSupport;
486
486
  exports.useRewardBalance = useRewardBalance;
487
487
  exports.useSelfExtId = useSelfExtId;
488
- //# sourceMappingURL=chunk-LQYPOWKN.cjs.map
489
- //# sourceMappingURL=chunk-LQYPOWKN.cjs.map
488
+ //# sourceMappingURL=chunk-IHPQO2TG.cjs.map
489
+ //# sourceMappingURL=chunk-IHPQO2TG.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/connection/billing/support.ts","../src/connection/billing/plan-constants.ts","../src/connection/billing/quota-items.ts","../src/connection/billing/reward-balance.ts","../src/connection/billing/addon-pricing.ts","../src/connection/billing/api-error.ts","../src/connection/billing/tax-quote.ts","../src/connection/billing/plan-pricing.ts","../src/connection/billing/plan-badge.ts","../src/connection/workspace-list/projectBilling.tsx"],"names":["useHost","useArbi","PoundSterling","formatGbp","HardDrive","Users","useUserSettings","gbp","jsx","PlanBadge","ProjectReadout","QuotaMeter","ProjectBreakdown","UsageMeter"],"mappings":"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","file":"chunk-LQYPOWKN.cjs","sourcesContent":["/**\n * What a host adds to the billing kit. Selling plans needs Stripe, which stays the host's: whether\n * this deployment sells plans (a publishable key is configured), and its embedded checkout. It is the\n * host configuration's `billing`; without it nothing is for sale: projects show their limits and\n * usage only.\n */\nimport type { ReactNode } from 'react'\nimport { useArbi } from '@arbidocs/react'\nimport { useHost } from '../../provider/host'\n\nexport interface BillingCheckoutSupport {\n /** Load the host's Stripe (lazily, when a checkout opens); `null` when it cannot. */\n load(): Promise<unknown> | null\n /** Stripe's embedded checkout for a session, on the loaded Stripe. */\n render(stripe: unknown, session: { clientSecret: string; onComplete: () => void }): ReactNode\n}\n\nexport interface BillingSupport {\n /** This deployment sells plans (Stripe is configured). */\n stripeEnabled: boolean\n checkout?: BillingCheckoutSupport\n /**\n * Add-on blocks the user just bought here, so the host does not announce them later as having\n * arrived on their own.\n */\n onAddonBlocksBought?: (projectExtId: string, blocks: { ai: number; storage: number }) => void\n}\n\nconst NOT_FOR_SALE: BillingSupport = { stripeEnabled: false }\n\nexport function useBillingSupport(): BillingSupport {\n return useHost().billing ?? NOT_FOR_SALE\n}\n\n/** The signed-in user's id (`usr-…`), to tell the projects they own from the others. */\nexport function useSelfExtId(): string | undefined {\n const arbi = useArbi()\n return arbi.isLoggedIn ? (arbi.user.identity()?.externalId ?? undefined) : undefined\n}\n","/** Pro's allowance per month (one licence) — must match the backend. Legacy multi-pack plans get it per pack.\n * The live numbers for an existing project come from `project.addons`; these are only for\n * describing the plan before a project exists (new-project dialog, upgrade picker). */\nexport const PRO_AI_GBP = 10\nexport const PRO_STORAGE_GB = 5\n\n/**\n * Add-on block sizes (£25 of AI usage or 25 GB), the same on every plan. Their prices come from the\n * products API (`addon_prices`) before a project exists, and from `project.addons` after.\n */\nexport const ADDON_BLOCK_AI_GBP = 25\nexport const STORAGE_BLOCK_GB = 25\n\n/** Default monthly usage cap for a new Pro project (pence); it limits automatic top-ups only. 0 = no automatic\n * top-ups, null = no limit. */\nexport const DEFAULT_SPEND_CAP_PENCE = 10000\n\n/** The automatic add-on triggers when less than this much AI usage (£) remains; the backend minimum. */\nexport const MIN_AUTO_TOPUP_THRESHOLD_GBP = 1\n\n/** Free-tier quotas — must match backend FREE_* constants in src/constants.py\n * (FREE_AI_GBP=1, FREE_STORAGE_GB=0.05 → 50 MB, FREE_COLLABORATORS=0). */\nexport const FREE_AI_GBP = 1\nexport const FREE_STORAGE_MB = 50\nexport const FREE_COLLABORATORS = 0\n\n/** Collaborator limits at or above this are \"unlimited\" (the backend reports 999 for Pro/dev). */\nexport const UNLIMITED_COLLABORATORS = 999\n\n/**\n * Suggested initial daily per-user AI usage cap (£), pre-filled when creating\n * a new project. The user can clear it to create a project with no cap.\n * Backend treats null as \"no enforcement\" — there is no server-side default.\n */\nexport const SUGGESTED_DAILY_CAP_PER_USER = 10\n","/**\n * Quota display — single source of truth.\n *\n * Builds the icon / label / formatted used-of-limit strings for a project's\n * quotas (AI usage in £, storage, collaborators). Shared by QuotaBadges and the\n * project tooltip so the numbers and units can never disagree (storage in GB on\n * plans of 1 GB or more, MB below).\n */\n\nimport { HardDrive, PoundSterling, Users } from 'lucide-react'\nimport { formatGbp } from '@arbidocs/react'\nimport type { QuotaMeterItem } from '../../settings'\nimport type { components } from '@arbidocs/client'\nimport { UNLIMITED_COLLABORATORS } from './plan-constants'\n\ntype Quotas = components['schemas']['ProjectResponse']['quotas']\n\nconst fmt = (v: number) => Math.round(v).toLocaleString('en-US')\n\n/** \"May 31, 2025\" — the one format every AI-usage-reset mention uses. */\nexport function formatResetDate(unixTimestamp: number | null | undefined): string | null {\n if (!unixTimestamp) return null\n const date = new Date(unixTimestamp * 1000)\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\n/**\n * When the project's AI usage allowance next resets, as a unix timestamp.\n * Pro projects report it directly (`addons.ai_usage_reset_at` — monthly, even on an\n * annual plan); monthly subscriptions otherwise reset with the billing period\n * (`current_period_end`); free projects reset on the backend-stamped signup\n * anniversary (`quotas.budget_reset_at`). Only a FUTURE date is returned — a stale\n * past stamp would read as a bug, not a promise.\n */\nexport function aiUsageResetTimestamp(project: {\n quotas?: { budget_reset_at?: number | null } | null\n current_period_end?: number | null\n plan_interval?: string | null\n addons?: { ai_usage_reset_at?: number | null } | null\n}): number | null {\n // An annual period end is a year away — never a monthly AI usage reset.\n const periodEnd = project.plan_interval === 'year' ? null : project.current_period_end\n const ts =\n project.addons?.ai_usage_reset_at ?? periodEnd ?? project.quotas?.budget_reset_at ?? null\n return ts && ts * 1000 > Date.now() ? ts : null\n}\n\n// Defensive: a quota field may be missing on partial/empty payloads — treat it\n// as 0 / 0 rather than crashing the whole row.\nconst quota = (q?: { used?: number; limit?: number }) => ({\n used: q?.used ?? 0,\n limit: q?.limit ?? 0,\n})\n/** GB from 1 GB plans up (\"2.4 GB\", \"5 GB\"), MB below (\"50 MB\"); `unit: false` drops the unit. */\nfunction storageText(gbValue: number, limitGb: number, unit = true): string {\n if (limitGb >= 1) {\n const n = Math.round(gbValue * 10) / 10\n return `${n.toLocaleString('en-US')}${unit ? ' GB' : ''}`\n }\n return `${fmt(gbValue * 1000)}${unit ? ' MB' : ''}`\n}\nconst pctOf = (used: number, limit: number) => (limit > 0 ? (used / limit) * 100 : 0)\n\nexport function getQuotaItems(quotas: Quotas): QuotaMeterItem[] {\n const aiUsage = quota(quotas?.ai_usage)\n const storage = quota(quotas?.storage_gb)\n const collaborators = quota(quotas?.collaborators)\n const unlimitedCollaborators = collaborators.limit >= UNLIMITED_COLLABORATORS\n return [\n {\n key: 'ai-usage',\n icon: PoundSterling,\n label: 'AI usage',\n used: formatGbp(aiUsage.used),\n usedOfLimit: `${formatGbp(aiUsage.used)} / ${formatGbp(aiUsage.limit)}`,\n pct: pctOf(aiUsage.used, aiUsage.limit),\n },\n {\n key: 'storage',\n icon: HardDrive,\n label: 'Storage',\n used: storageText(storage.used, storage.limit),\n usedOfLimit: `${storageText(storage.used, storage.limit, false)} / ${storageText(\n storage.limit,\n storage.limit\n )}`,\n pct: pctOf(storage.used, storage.limit),\n },\n {\n key: 'collaborators',\n icon: Users,\n label: 'Collaborators',\n used: fmt(collaborators.used),\n usedOfLimit: unlimitedCollaborators\n ? `${fmt(collaborators.used)} / Unlimited`\n : `${fmt(collaborators.used)} / ${fmt(collaborators.limit)}`,\n // Zero seats (Free) IS the cap: 0 / 0 shows a full destructive bar,\n // because you can't add anyone — the truth the upgrade door answers.\n // (Only when the quota is actually present; a missing field on a\n // partial payload stays an empty bar, not a false alarm.)\n pct: unlimitedCollaborators\n ? 0\n : quotas?.collaborators && collaborators.limit === 0\n ? 100\n : pctOf(collaborators.used, collaborators.limit),\n },\n ]\n}\n","/**\n * Reward-balance logic (hook + money formatter).\n *\n * A user's reward balance is their available account balance — a single GLOBAL per-user\n * number, the Stripe customer account balance mirrored read-only into\n * `settings.extra_discount`. It auto-applies to the user's NEXT invoice.\n *\n * \"Rewards\" is the broad concept: the balance can be fed by several sources (referral\n * reward is just one). The referral pricing surfaces of the billing kit\n * speak to the referral source specifically; this module owns the balance itself.\n */\n\nimport { useUserSettings } from '@arbidocs/react'\n\nexport interface RewardBalance {\n amount: number // minor units (e.g. pennies) of available account balance\n currency: string\n}\n\n/** The user's available reward balance (all sources), or null if none. */\nexport function useRewardBalance(): RewardBalance | null {\n const { data } = useUserSettings()\n // Backend mirrors this read-only as { amount, currency }; schema types it as a loose object.\n const ed = data?.extra_discount as { amount?: number; currency?: string } | null | undefined\n if (!ed || !ed.amount || ed.amount <= 0) return null\n return { amount: ed.amount, currency: ed.currency || 'gbp' }\n}\n\n/** Format minor units as a localized money string, e.g. `£20` (whole) or `£29.99`. */\nexport function formatMoney(cents: number, currency = 'gbp'): string {\n const value = cents / 100\n const isWhole = Number.isInteger(value)\n const c = currency.toLowerCase()\n const symbol =\n c === 'usd' ? '$' : c === 'gbp' ? '£' : c === 'eur' ? '€' : currency.toUpperCase() + ' '\n return `${symbol}${value.toLocaleString('en-US', {\n minimumFractionDigits: isWhole ? 0 : 2,\n maximumFractionDigits: isWhole ? 0 : 2,\n })}`\n}\n","/** Add-on block price helpers (VAT-inclusive pence from `project.addons`). */\n\nimport { formatGbp } from '@arbidocs/react'\nimport type { components } from '@arbidocs/client'\nimport type { AddonKind } from './useAddonPurchase'\nimport { formatMoney } from './reward-balance'\n\ntype ProjectAddons = components['schemas']['ProjectAddons']\n\nconst gbp = (pence: number) => formatMoney(pence, 'gbp')\n\n/** \"3 Nov 2026\". */\nexport function formatPeriodEnd(unix: number | null | undefined): string | null {\n if (!unix) return null\n return new Date(unix * 1000).toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n })\n}\n\n/** What one more block of this kind costs right now, incl. UK VAT. */\nexport function addonPricePence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage' ? addons.ai_block_price_pence : addons.storage_block_price_pence\n}\n\n/** The same price ex VAT. */\nexport function addonPriceExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_price_ex_vat_pence\n : addons.storage_block_price_ex_vat_pence\n}\n\n/**\n * The PUT /addons/settings body (the monthly usage cap and the automatic add-on trigger) from the current add-on\n * state with the given changes applied.\n */\nexport function addonSettingsBody(\n addons: ProjectAddons,\n change: Partial<{\n spend_cap_pence: number | null\n auto_topup_threshold_gbp: number\n }> = {}\n) {\n return {\n spend_cap_pence: addons.spend_cap_pence ?? null,\n auto_topup_threshold_gbp: addons.auto_topup_threshold_gbp,\n ...change,\n }\n}\n\n/**\n * Of `count` blocks of a kind, how many are on an earlier catalogue's price (legacy, at their old size: £10 of AI\n * usage or 10 GB) and how many on today's. A reduction removes today's blocks first, so the legacy ones are the\n * last kept.\n */\nexport function splitBlocks(\n kind: AddonKind,\n count: number,\n addons: ProjectAddons\n): { legacy: number; current: number } {\n const granted =\n (kind === 'ai_usage' ? addons.legacy_ai_blocks : addons.legacy_storage_blocks) ?? 0\n const legacy = Math.max(0, Math.min(granted, count))\n return { legacy, current: count - legacy }\n}\n\n/** One block's size: \"£25 of AI usage\" / \"25 GB\" (legacy: \"£10\" / \"10 GB\"). */\nfunction blockSize(kind: AddonKind, addons: ProjectAddons, legacy: boolean): string {\n if (kind === 'ai_usage') {\n return formatGbp(legacy ? (addons.legacy_block_ai_gbp ?? 10) : addons.block_ai_gbp)\n }\n return `${legacy ? (addons.legacy_block_storage_gb ?? 10) : addons.block_storage_gb} GB`\n}\n\n/**\n * \"2 × £25 of AI usage\", \"1 × £10 + 1 × £25 of AI usage\", \"1 × 10 GB + 2 × 25 GB\": `count` blocks of a kind, the\n * legacy ones (kept last on a reduction) at their old size.\n */\nexport function blocksLabel(kind: AddonKind, count: number, addons: ProjectAddons): string {\n const { legacy, current } = splitBlocks(kind, count, addons)\n const parts = [\n ...(legacy > 0 ? [`${legacy} × ${blockSize(kind, addons, true)}`] : []),\n ...(current > 0 || legacy === 0 ? [`${current} × ${blockSize(kind, addons, false)}`] : []),\n ]\n return `${parts.join(' + ')}${kind === 'ai_usage' ? ' of AI usage' : ''}`\n}\n\n/**\n * Bounds for \"Reduce from next period\": keep `min`..`current` blocks. `next` is what renews\n * now (a pending reduction, or the current count). Storage can't go below what usage needs.\n */\nexport function reduceBounds(\n kind: AddonKind,\n addons: ProjectAddons\n): { min: number; current: number; next: number } {\n const current = kind === 'ai_usage' ? addons.ai_blocks : addons.storage_blocks\n const pending = kind === 'ai_usage' ? addons.ai_blocks_next : addons.storage_blocks_next\n const min = kind === 'ai_usage' ? 0 : Math.min(addons.storage_blocks_min, current)\n return { min, current, next: pending ?? current }\n}\n\n/** One block's monthly-equivalent price ex VAT: AI usage and storage are priced separately. */\nfunction blockMonthlyExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_monthly_ex_vat_pence\n : addons.storage_block_monthly_ex_vat_pence\n}\n\n/** What one block renews at per billing period, ex VAT (annual: 12 × the monthly price). */\nfunction blockPeriodExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_period_ex_vat_pence\n : addons.storage_block_period_ex_vat_pence\n}\n\n/** What one add-on renews at: \"£25/month + VAT\" or \"£225/year + VAT\". */\nexport function blockRenewalShort(\n kind: AddonKind,\n addons: ProjectAddons,\n isAnnual: boolean\n): string {\n return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? 'year' : 'month'} + VAT`\n}\n\n/** The extra line annual plans show beside an AI usage add-on or upgrade price. */\nexport const ANNUAL_PREMIUM_AI_NOTE = 'No markup on Premium AI on annual plans'\n\n/** The confirm dialog's figures: charged now (incl. / ex VAT), what it covers until, then what renews. */\nexport function addonCharge(\n kind: AddonKind,\n addons: ProjectAddons,\n isAnnual: boolean,\n periodEnd: number | null | undefined\n): { now: string; nowExVat: string; nowVat: string; until: string | null; then: string } {\n return {\n now: gbp(addonPricePence(kind, addons)),\n nowExVat: gbp(addonPriceExVatPence(kind, addons)),\n nowVat: gbp(addonPricePence(kind, addons) - addonPriceExVatPence(kind, addons)),\n until: isAnnual ? formatPeriodEnd(periodEnd) : null,\n then: blockRenewalShort(kind, addons, isAnnual),\n }\n}\n\n/** Why automatic top-ups stopped (one line; the price of a step is on the Add button). */\nexport const CAP_REACHED_MESSAGE = 'Usage cap reached: no more automatic top-ups this month.'\n\n/** The £0 usage cap: no automatic top-ups (adding by hand still works). */\nexport const ZERO_BUDGET_MESSAGE = 'Automatic top-ups are off (£0 usage cap).'\n\n/** The usage cap's heading wherever it is edited or shown. */\nexport const USAGE_CAP_LABEL = 'Usage cap'\n\n/** What the usage cap means (in \"More info\"): £0 and \"No limit\" explained. */\nexport const USAGE_CAP_HELP =\n 'The most automatic top-ups (AI usage added when it runs low, storage added when an upload needs it) may ' +\n 'cost in a month. Adding or removing by hand is never limited by it; the plan and renewals are not counted. ' +\n '£0 = no automatic top-ups: AI usage and uploads stop at your allowance unless you add more. ' +\n 'No limit = no cap.'\n\n/** A £0 cap turns automatic top-ups off (manual adds still work). */\nexport function isAddonBudgetZero(addons: Pick<ProjectAddons, 'spend_cap_pence'>): boolean {\n return addons.spend_cap_pence === 0\n}\n\n/**\n * How much more of a resource automatic top-ups may add this month before the usage cap stops them, in the\n * resource's unit (£ of AI usage / GB): the whole blocks the cap left pays for at today's ex-VAT price. `null` =\n * no limit; 0 with a £0 cap or when not even one block fits.\n */\nexport function autoRoom(kind: AddonKind, addons: ProjectAddons): number | null {\n const left = addons.cap_left_pence\n if (left == null) return addons.spend_cap_pence === 0 ? 0 : null\n const price = addonPriceExVatPence(kind, addons)\n if (price <= 0) return 0\n const blocks = Math.floor(left / price)\n return blocks * (kind === 'ai_usage' ? addons.block_ai_gbp : addons.block_storage_gb)\n}\n\n/** The size of the block a one-step reduction removes from what renews (today's blocks go first). */\nexport function nextRemovedBlockSize(kind: AddonKind, addons: ProjectAddons): number {\n const { next } = reduceBounds(kind, addons)\n const { current } = splitBlocks(kind, next, addons)\n if (kind === 'ai_usage') {\n return current > 0 ? addons.block_ai_gbp : (addons.legacy_block_ai_gbp ?? 10)\n }\n return current > 0 ? addons.block_storage_gb : (addons.legacy_block_storage_gb ?? 10)\n}\n\n/**\n * Pounds typed into a usage cap field → pence. '' → the default (null); invalid → undefined.\n * Equal to the default is also sent as null (\"keep the default\").\n */\nexport function parseBudgetPounds(value: string): number | null | undefined {\n if (value.trim() === '') return null\n const n = Number(value)\n if (!Number.isFinite(n) || n < 0) return undefined\n return Math.round(n * 100)\n}\n\n/** Toast after saving the usage cap. */\nexport function budgetSavedMessage(capPence: number | null): string {\n if (capPence == null) return 'Usage cap: no limit'\n if (capPence === 0) return 'Usage cap: £0 (no automatic top-ups)'\n return `Usage cap: ${gbp(capPence)} a month`\n}\n\nconst gbText = (n: number) => `${Math.round(n * 10) / 10} GB`\n\n/** \"£25 of AI usage\" / \"25 GB\". */\nexport function stepLabel(kind: AddonKind, size: number): string {\n return kind === 'ai_usage' ? `${formatGbp(size)} of AI usage` : gbText(size)\n}\n\n/** The PUT /addons/next-period body that renews `keep` blocks of `kind` (the other kind unchanged). */\nexport function nextPeriodBody(kind: AddonKind, keep: number) {\n return kind === 'ai_usage'\n ? { ai_blocks: keep, storage_blocks: null }\n : { ai_blocks: null, storage_blocks: keep }\n}\n\n/**\n * The \"+\" step's price line, ex VAT: \"£25/month · today £25\" (annual: \"£18.75/month · today £150\");\n * storage, charged for the rest of the period: \"£25/month · prorated today £8.33\".\n */\nexport function addStepCaption(kind: AddonKind, addons: ProjectAddons): string {\n const now = gbp(addonPriceExVatPence(kind, addons))\n const today = kind === 'storage' ? `prorated today ${now}` : `today ${now}`\n return `${gbp(blockMonthlyExVatPence(kind, addons))}/month · ${today}`\n}\n","/**\n * The `detail` of a refused billing call: the response body's `detail` (a string, or the\n * `{code, message}` of a payment refusal), whether the error is an SDK `ArbiApiError` (body in\n * `apiError`) or the body itself.\n */\nexport function apiErrorDetail(error: unknown): unknown {\n const body = (error as { apiError?: unknown } | null)?.apiError ?? error\n return (body as { detail?: unknown } | null)?.detail\n}\n","/**\n * Tax-inclusive totals from the backend's Stripe Tax quotes (`TaxQuoteResponse`, plan-change\n * previews, add-on quotes): \"£48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\", \"£40.00 · no VAT\n * (reverse charge)\", \"£40.00 · no tax\".\n */\n\nimport type { components } from '@arbidocs/client'\nimport { apiErrorDetail } from './api-error'\n\nexport type TaxQuote = components['schemas']['TaxQuoteResponse']\n\n/** Countries whose tax depends on the postcode: the field is shown up front. */\nexport const POSTCODE_COUNTRIES: readonly string[] = ['US', 'CA']\n\n/** The quote error that asks for a ZIP / postal code. */\nexport const TAX_LOCATION_INVALID = 'TAX_LOCATION_INVALID'\n\n/** \"£42.00\" — always pence, so the parts of a total add up on screen. */\nexport function money2(pence: number, currency = 'gbp'): string {\n const symbol = { gbp: '£', usd: '$', eur: '€' }[currency.toLowerCase()] ?? `${currency} `\n return `${symbol}${(pence / 100).toLocaleString('en-GB', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}`\n}\n\nexport interface TaxParts {\n subtotal: number\n tax: number\n total: number\n /** \"VAT 20%\"; null when no tax is due. */\n label?: string | null\n reverseCharge?: boolean\n}\n\n/** \"VAT\" for a VAT label, else \"tax\". */\nfunction taxKind(label: string | null | undefined): string {\n return /^vat\\b/i.test(label ?? '') ? 'VAT' : 'tax'\n}\n\n/** What follows the total: \"incl. VAT (£40.00 + £8.00 VAT 20%)\", \"· no VAT (reverse charge)\", \"· no tax\". */\nexport function taxDetail({ subtotal, tax, label, reverseCharge }: TaxParts): string {\n if (reverseCharge) return '· no VAT (reverse charge)'\n if (tax <= 0) return '· no tax'\n return `incl. ${taxKind(label)} (${money2(subtotal)} + ${money2(tax)} ${label ?? 'tax'})`\n}\n\n/** \"£48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\", \"£40.00 · no VAT (reverse charge)\", \"£40.00 · no tax\". */\nexport function taxSummary(parts: TaxParts): string {\n return `${money2(parts.total)} ${taxDetail(parts)}`\n}\n\n/** \"£21.00 incl. VAT\" / \"£17.50\" — a total with only the word for its tax. */\nexport function totalWithTaxWord({ total, tax, label }: TaxParts): string {\n return tax > 0 ? `${money2(total)} incl. ${taxKind(label)}` : money2(total)\n}\n\n/** The parts of a quote response. */\nexport function quoteParts(quote: TaxQuote): TaxParts {\n return {\n subtotal: quote.subtotal_pence,\n tax: quote.tax_pence,\n total: quote.total_pence,\n label: quote.tax_label,\n reverseCharge: quote.reverse_charge,\n }\n}\n\n/** \"Total today £48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\". */\nexport function quoteTotalLine(quote: TaxQuote): string {\n return `Total today ${taxSummary(quoteParts(quote))}`\n}\n\n/** The API error code (`detail.code`) and message, if any. */\nexport function quoteError(error: unknown): { code?: string; message?: string } {\n const detail = apiErrorDetail(error)\n if (detail && typeof detail === 'object') {\n const { code, message } = detail as { code?: unknown; message?: unknown }\n return {\n code: typeof code === 'string' ? code : undefined,\n message: typeof message === 'string' ? message : undefined,\n }\n }\n return { message: typeof detail === 'string' ? detail : undefined }\n}\n","/**\n * Pro plan price helpers. Prices come from Stripe via GET /v1/user/products (source of truth).\n * One plan is sold: Pro, one licence (£10 of AI usage + 5 GB a month). Each price lists\n * `pack_prices` for that one licence — `amount` incl. UK VAT and `amount_ex_vat` — and\n * `addon_prices` (one add-on block on that interval). Display them as-is.\n * Legacy subscriptions with several packs keep working; their name and price come from the project.\n */\n\nimport type { components } from '@arbidocs/client'\nimport { formatGbp } from '@arbidocs/react'\nimport { formatMoney } from './reward-balance'\nimport { formatPeriodEnd } from './addon-pricing'\nimport { taxSummary } from './tax-quote'\nimport { ADDON_BLOCK_AI_GBP, STORAGE_BLOCK_GB } from './plan-constants'\n\ntype PlanChangePreview = components['schemas']['PlanChangePreview']\ntype ProductPrice = components['schemas']['ProductPrice']\ntype ProductResponse = components['schemas']['ProductResponse']\ntype PackPrice = components['schemas']['PackPrice']\ntype AddonPrice = components['schemas']['AddonPrice']\n\nexport type PlanInterval = 'month' | 'year'\n\nconst gbp = (pence: number) => formatMoney(pence, 'gbp')\n\n/** The headline price: \"£40 + VAT\". */\nexport function plusVat(exVatPence: number, currency = 'gbp'): string {\n return `${formatMoney(exVatPence, currency)} + VAT`\n}\n\n/** The one licence Pro sells. Other pack counts exist only on legacy subscriptions (displayed, not sold). */\nexport const PRO_PACKS = 1\n\n/** A plan the pickers offer. */\nexport type PlanChoice = 'free' | 'pro'\n\n/** Packs per plan choice (0 = Free). */\nexport const PLAN_CHOICE_PACKS: Record<PlanChoice, number> = {\n free: 0,\n pro: PRO_PACKS,\n}\n\n/** \"Free\" (0), \"Pro\" (1), otherwise \"Pro ×N\" (a legacy multi-pack plan; the API's own name wins). */\nexport function planNameForPacks(packs: number | null | undefined): string {\n const n = packs ?? 0\n if (n <= 0) return 'Free'\n if (n === PRO_PACKS) return 'Pro'\n return `Pro ×${n}`\n}\n\n/** The plan-change panel's fixed lines, each with a test id. */\nexport interface PlanChangeLine {\n id: 'pay-today' | 'from' | 'current-until' | 'no-change' | 'awaiting-bank'\n text: string\n}\n\n/**\n * What a plan change costs, from the backend's Stripe invoice preview (taxed for the customer's\n * address), as fixed lines:\n * - \"Pay today £42.00 incl. VAT (£35.00 + £7.00 VAT 20%)\" (\"Pay today £0\" for a change that waits)\n * - \"From 16 Nov 2026: £210.00 incl. VAT (£175.00 + £35.00 VAT 20%) a month\"\n * - \"Current plan until 16 Nov 2026\" for a change that waits for the period end.\n * \"No change\" when the selection is what the project already has; \"Waiting for your bank\" while\n * an earlier upgrade payment awaits 3-D Secure.\n */\nexport function planPreviewLines(preview: PlanChangePreview): PlanChangeLine[] {\n if (preview.unchanged) return [{ id: 'no-change', text: 'No change' }]\n const date = formatPeriodEnd(preview.next_renewal_at)\n const cadence = preview.interval === 'year' ? 'year' : 'month'\n const lines: PlanChangeLine[] = [\n preview.awaiting_confirmation\n ? { id: 'awaiting-bank', text: 'Waiting for your bank' }\n : {\n id: 'pay-today',\n text: preview.effective_now\n ? `Pay today ${taxSummary({\n subtotal: preview.due_now_ex_vat_pence,\n tax: preview.due_now_tax_pence,\n total: preview.due_now_pence,\n label: preview.tax_label,\n })}`\n : `Pay today ${gbp(0)}`,\n },\n {\n id: 'from',\n text: `${date ? `From ${date}` : 'From renewal'}: ${taxSummary({\n subtotal: preview.next_renewal_ex_vat_pence,\n tax: preview.next_renewal_tax_pence,\n total: preview.next_renewal_pence,\n label: preview.tax_label,\n })} a ${cadence}`,\n },\n ]\n if (!preview.effective_now) {\n lines.push({ id: 'current-until', text: `Current plan until ${date ?? 'renewal'}` })\n }\n return lines\n}\n\n/** Stand-in for \"Pay today\" when the preview failed for an upgrade. */\nexport const PAY_TODAY_UNPRICED = 'Pay today: the difference for the rest of this period'\n\n/** One add-on block's price on this interval's plan price (`addon_prices`), if listed. */\nexport function addonPrice(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage' = 'ai_usage'\n): AddonPrice | undefined {\n return price?.addon_prices?.find((p) => p.kind === kind)\n}\n\n/** Per add-on block of `kind` per month, ex VAT (annual plans: the monthly equivalent). */\nexport function addonMonthlyExVat(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage' = 'ai_usage'\n): number | undefined {\n return addonPrice(price, kind)?.monthly_ex_vat\n}\n\n/**\n * The add-on offer on this interval's price, each block with its own price:\n * \"£25 of AI usage for £25/month + VAT or 25 GB for £25/month + VAT\". Null when unlisted.\n */\nexport function addonOfferText(price: ProductPrice | undefined): string | null {\n const ai = addonPrice(price, 'ai_usage')\n const storage = addonPrice(price, 'storage')\n const parts = [\n ai && `${formatGbp(ai.ai_gbp)} of AI usage for ${gbp(ai.monthly_ex_vat)}/month + VAT`,\n storage && `${storage.storage_gb} GB for ${gbp(storage.monthly_ex_vat)}/month + VAT`,\n ].filter(Boolean)\n return parts.length ? parts.join(' or ') : null\n}\n\n/** What a checkout buys: \"Pro\", \"Pro + 2 × £25 of AI usage + 1 × 25 GB\". */\nexport function checkoutItemsLabel(aiBlocks: number, storageBlocks: number): string {\n return [\n 'Pro',\n aiBlocks > 0 && `${aiBlocks} × ${formatGbp(ADDON_BLOCK_AI_GBP)} of AI usage`,\n storageBlocks > 0 && `${storageBlocks} × ${STORAGE_BLOCK_GB} GB`,\n ]\n .filter(Boolean)\n .join(' + ')\n}\n\n/** The price of `packs` packs on this interval's price, if Stripe lists it (only one licence is listed). */\nexport function packPrice(\n price: ProductPrice | undefined,\n packs: number = PRO_PACKS\n): PackPrice | undefined {\n return price?.pack_prices?.find((p) => p.packs === packs)\n}\n\nexport interface ProPrices {\n monthly?: ProductPrice\n annual?: ProductPrice\n}\n\n/** The Pro product's monthly and annual prices (default price per interval wins). */\nexport function findProPrices(products: ProductResponse[] | undefined): ProPrices {\n const prices = products?.[0]?.prices ?? []\n const pick = (interval: PlanInterval) =>\n prices.find((p) => p.interval === interval && p.default_price) ??\n prices.find((p) => p.interval === interval)\n return { monthly: pick('month'), annual: pick('year') }\n}\n\n/** Annual savings vs 12 × monthly, as a whole percent (e.g. £360 vs £480 → 25). */\nexport function calcSavingsPercent(\n monthlyPrice: ProductPrice | undefined,\n annualPrice: ProductPrice | undefined\n): number | null {\n const monthly = packPrice(monthlyPrice)?.amount ?? monthlyPrice?.amount\n const annual = packPrice(annualPrice)?.amount ?? annualPrice?.amount\n if (monthly == null || annual == null || monthly === 0) return null\n const savings = Math.round(((monthly * 12 - annual) / (monthly * 12)) * 100)\n return savings > 0 ? savings : null\n}\n\n/** Most add-on blocks of each kind a checkout may include (the backend's limit). */\nexport const MAX_CHECKOUT_BLOCKS = 100\n\n/** \"£25/month + VAT each\", annual: \"£225/year + VAT each\": one block on the chosen interval. */\nexport function checkoutBlockPriceText(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage'\n): string | null {\n const block = addonPrice(price, kind)\n if (!block) return null\n const currency = price?.currency ?? 'gbp'\n return price?.interval === 'year'\n ? `${formatMoney(block.amount_ex_vat, currency)}/year + VAT each`\n : `${formatMoney(block.amount_ex_vat, currency)}/month + VAT each`\n}\n","import type { PlanBadgeProps } from '../../settings'\nimport type { components } from '@arbidocs/client'\nimport { planNameForPacks } from './plan-pricing'\n\ntype ProjectBillingFields = Pick<\n components['schemas']['ProjectResponse'],\n 'subscription' | 'plan_interval'\n> &\n Partial<Pick<components['schemas']['ProjectResponse'], 'packs' | 'addons'>>\n\n/** A project is on Pro when it has a billing interval (null = Free or checkout pending). */\nexport function isProProject(project: Pick<ProjectBillingFields, 'plan_interval'>): boolean {\n return project.plan_interval != null\n}\n\n/** \"Pro\", or a legacy multi-pack plan's name (\"Pro ×N\") — the API's name when it sends one (owner view). */\nexport function proTierName(project: ProjectBillingFields): string {\n return project.addons?.plan_name || planNameForPacks(Math.max(project.packs ?? 1, 1))\n}\n\n/** \"Free\", \"Pro monthly\", \"Pro annual\", \"Pro ×3 monthly\" (legacy) or \"Dev\" — the full plan name. */\nexport function planName(project: ProjectBillingFields): string {\n if (project.subscription === 'dev') return 'Dev'\n if (!isProProject(project)) return 'Free'\n return `${proTierName(project)} ${project.plan_interval === 'year' ? 'annual' : 'monthly'}`\n}\n\n/**\n * Maps a project's billing state to the `PlanBadge` block's label + tone.\n * Pro reads as accent, Free/dev as neutral, payment trouble or cancelling as pending.\n */\nexport function planBadgeView(\n project: ProjectBillingFields\n): Pick<PlanBadgeProps, 'label' | 'tone'> {\n const { subscription } = project\n const label =\n subscription === 'canceled'\n ? 'Cancelling'\n : subscription === 'dev'\n ? 'Dev'\n : isProProject(project)\n ? proTierName(project)\n : 'Free'\n\n const tone =\n subscription === 'dev'\n ? 'dev'\n : subscription === 'canceled' || subscription === 'past_due'\n ? 'pending'\n : isProProject(project)\n ? 'paid'\n : 'free'\n\n return { label, tone }\n}\n","/**\n * What a project's group in the workspace list says about the project itself: its plan badge (own\n * projects, where plans are sold), the usage readout beside its name, and the plan and limits\n * breakdown behind the name. One source for the cards' bands and the table's headings.\n */\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport type { components } from '@arbidocs/client'\nimport { UsageMeter } from '../../components/UsageMeter'\nimport { PlanBadge, QuotaMeter } from '../../settings'\nimport { ProjectBreakdown, ProjectReadout } from '../../home/ProjectBandParts'\nimport { planBadgeView } from '../billing/plan-badge'\nimport { getQuotaItems } from '../billing/quota-items'\n\ntype Project = components['schemas']['ProjectResponse']\n\nexport interface ProjectGroupBilling {\n /** The plan badge: own projects only, where plans are sold. */\n planBadge?: ReactNode\n /** Plan and usage, right after the name (projects with quotas). */\n readout?: ReactNode\n /** The plan and limits panel behind the name; meters for own projects only. */\n breakdown?: ReactNode\n}\n\nexport function projectGroupBilling(\n project: Project | undefined,\n { isOwned, stripeEnabled }: { isOwned: boolean; stripeEnabled: boolean }\n): ProjectGroupBilling {\n if (!project) return {}\n const planBadge = isOwned && stripeEnabled ? <PlanBadge {...planBadgeView(project)} /> : undefined\n const quotaItems = project.quotas ? getQuotaItems(project.quotas) : []\n const readout = project.quotas ? (\n <ProjectReadout\n plan={\n planBadge ? { badge: planBadge, testId: `project-tier-${project.external_id}` } : undefined\n }\n usage={{\n testId: `project-usage-${project.external_id}`,\n // Bare numbers without plans for sale: a glance beside the name, not the Plan & Usage detail.\n node: (\n <QuotaMeter\n items={quotaItems}\n size=\"sm\"\n showTooltips={false}\n showBars={stripeEnabled}\n showPercent={false}\n />\n ),\n }}\n />\n ) : (\n planBadge\n )\n const breakdown = (\n <ProjectBreakdown\n name={project.name}\n badge={planBadge}\n meters={\n isOwned\n ? quotaItems.map(({ key, icon, label, usedOfLimit, pct }) => (\n <UsageMeter\n key={key}\n icon={icon as LucideIcon}\n label={label}\n value={usedOfLimit}\n pct={pct}\n />\n ))\n : undefined\n }\n dailyCap={{\n label: 'Daily cap',\n value:\n project.daily_cap_per_user == null\n ? 'No limit'\n : project.daily_cap_per_user.toLocaleString('en-US'),\n }}\n />\n )\n return { planBadge, readout, breakdown }\n}\n"]}
1
+ {"version":3,"sources":["../src/connection/billing/support.ts","../src/connection/billing/plan-constants.ts","../src/connection/billing/quota-items.ts","../src/connection/billing/reward-balance.ts","../src/connection/billing/addon-pricing.ts","../src/connection/billing/api-error.ts","../src/connection/billing/tax-quote.ts","../src/connection/billing/plan-pricing.ts","../src/connection/billing/plan-badge.ts","../src/connection/workspace-list/projectBilling.tsx"],"names":["useHost","useArbi","PoundSterling","formatGbp","HardDrive","Users","useUserSettings","gbp","jsx","PlanBadge","ProjectReadout","QuotaMeter","ProjectBreakdown","UsageMeter"],"mappings":"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","file":"chunk-IHPQO2TG.cjs","sourcesContent":["/**\n * What a host adds to the billing kit. Selling plans needs Stripe, which stays the host's: whether\n * this deployment sells plans (a publishable key is configured), and its embedded checkout. It is the\n * host configuration's `billing`; without it nothing is for sale: projects show their limits and\n * usage only.\n */\nimport type { ReactNode } from 'react'\nimport { useArbi } from '@arbidocs/react'\nimport { useHost } from '../../provider/host'\n\nexport interface BillingCheckoutSupport {\n /** Load the host's Stripe (lazily, when a checkout opens); `null` when it cannot. */\n load(): Promise<unknown> | null\n /** Stripe's embedded checkout for a session, on the loaded Stripe. */\n render(stripe: unknown, session: { clientSecret: string; onComplete: () => void }): ReactNode\n}\n\nexport interface BillingSupport {\n /** This deployment sells plans (Stripe is configured). */\n stripeEnabled: boolean\n checkout?: BillingCheckoutSupport\n /**\n * Add-on blocks the user just bought here, so the host does not announce them later as having\n * arrived on their own.\n */\n onAddonBlocksBought?: (projectExtId: string, blocks: { ai: number; storage: number }) => void\n}\n\nconst NOT_FOR_SALE: BillingSupport = { stripeEnabled: false }\n\nexport function useBillingSupport(): BillingSupport {\n return useHost().billing ?? NOT_FOR_SALE\n}\n\n/** The signed-in user's id (`usr-…`), to tell the projects they own from the others. */\nexport function useSelfExtId(): string | undefined {\n const arbi = useArbi()\n return arbi.isLoggedIn ? (arbi.user.identity()?.externalId ?? undefined) : undefined\n}\n","/** Pro's allowance per month (one licence) — must match the backend. Legacy multi-pack plans get it per pack.\n * The live numbers for an existing project come from `project.addons`; these are only for\n * describing the plan before a project exists (new-project dialog, upgrade picker). */\nexport const PRO_AI_GBP = 10\nexport const PRO_STORAGE_GB = 5\n\n/**\n * Add-on block sizes (£25 of AI usage or 25 GB), the same on every plan. Their prices come from the\n * products API (`addon_prices`) before a project exists, and from `project.addons` after.\n */\nexport const ADDON_BLOCK_AI_GBP = 25\nexport const STORAGE_BLOCK_GB = 25\n\n/** Default monthly usage cap for a new Pro project (pence); it limits automatic top-ups only. 0 = no automatic\n * top-ups, null = no limit. */\nexport const DEFAULT_SPEND_CAP_PENCE = 10000\n\n/** The automatic add-on triggers when less than this much AI usage (£) remains; the backend minimum. */\nexport const MIN_AUTO_TOPUP_THRESHOLD_GBP = 1\n\n/** Free-tier quotas — must match backend FREE_* constants in src/constants.py\n * (FREE_AI_GBP=1, FREE_STORAGE_GB=0.05 → 50 MB, FREE_COLLABORATORS=0). */\nexport const FREE_AI_GBP = 1\nexport const FREE_STORAGE_MB = 50\nexport const FREE_COLLABORATORS = 0\n\n/** Collaborator limits at or above this are \"unlimited\" (the backend reports 999 for Pro/dev). */\nexport const UNLIMITED_COLLABORATORS = 999\n\n/**\n * Suggested initial daily per-user AI usage cap (£), pre-filled when creating\n * a new project. The user can clear it to create a project with no cap.\n * Backend treats null as \"no enforcement\" — there is no server-side default.\n */\nexport const SUGGESTED_DAILY_CAP_PER_USER = 10\n","/**\n * Quota display — single source of truth.\n *\n * Builds the icon / label / formatted used-of-limit strings for a project's\n * quotas (AI usage in £, storage, collaborators). Shared by QuotaBadges and the\n * project tooltip so the numbers and units can never disagree (storage in GB on\n * plans of 1 GB or more, MB below).\n */\n\nimport { HardDrive, PoundSterling, Users } from 'lucide-react'\nimport { formatGbp } from '@arbidocs/react'\nimport type { QuotaMeterItem } from '../../settings'\nimport type { components } from '@arbidocs/client'\nimport { UNLIMITED_COLLABORATORS } from './plan-constants'\n\ntype Quotas = components['schemas']['ProjectResponse']['quotas']\n\nconst fmt = (v: number) => Math.round(v).toLocaleString('en-US')\n\n/** \"May 31, 2025\" — the one format every AI-usage-reset mention uses. */\nexport function formatResetDate(unixTimestamp: number | null | undefined): string | null {\n if (!unixTimestamp) return null\n const date = new Date(unixTimestamp * 1000)\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\n/**\n * When the project's AI usage allowance next resets, as a unix timestamp.\n * Pro projects report it directly (`addons.ai_usage_reset_at` — monthly, even on an\n * annual plan); monthly subscriptions otherwise reset with the billing period\n * (`current_period_end`); free projects reset on the backend-stamped signup\n * anniversary (`quotas.budget_reset_at`). Only a FUTURE date is returned — a stale\n * past stamp would read as a bug, not a promise.\n */\nexport function aiUsageResetTimestamp(project: {\n quotas?: { budget_reset_at?: number | null } | null\n current_period_end?: number | null\n plan_interval?: string | null\n addons?: { ai_usage_reset_at?: number | null } | null\n}): number | null {\n // An annual period end is a year away — never a monthly AI usage reset.\n const periodEnd = project.plan_interval === 'year' ? null : project.current_period_end\n const ts =\n project.addons?.ai_usage_reset_at ?? periodEnd ?? project.quotas?.budget_reset_at ?? null\n return ts && ts * 1000 > Date.now() ? ts : null\n}\n\n// Defensive: a quota field may be missing on partial/empty payloads — treat it\n// as 0 / 0 rather than crashing the whole row.\nconst quota = (q?: { used?: number; limit?: number }) => ({\n used: q?.used ?? 0,\n limit: q?.limit ?? 0,\n})\n/** GB from 1 GB plans up (\"2.4 GB\", \"5 GB\"), MB below (\"50 MB\"); `unit: false` drops the unit. */\nfunction storageText(gbValue: number, limitGb: number, unit = true): string {\n if (limitGb >= 1) {\n const n = Math.round(gbValue * 10) / 10\n return `${n.toLocaleString('en-US')}${unit ? ' GB' : ''}`\n }\n return `${fmt(gbValue * 1000)}${unit ? ' MB' : ''}`\n}\nconst pctOf = (used: number, limit: number) => (limit > 0 ? (used / limit) * 100 : 0)\n\nexport function getQuotaItems(quotas: Quotas): QuotaMeterItem[] {\n const aiUsage = quota(quotas?.ai_usage)\n const storage = quota(quotas?.storage_gb)\n const collaborators = quota(quotas?.collaborators)\n const unlimitedCollaborators = collaborators.limit >= UNLIMITED_COLLABORATORS\n return [\n {\n key: 'ai-usage',\n icon: PoundSterling,\n label: 'AI usage',\n used: formatGbp(aiUsage.used),\n usedOfLimit: `${formatGbp(aiUsage.used)} / ${formatGbp(aiUsage.limit)}`,\n pct: pctOf(aiUsage.used, aiUsage.limit),\n },\n {\n key: 'storage',\n icon: HardDrive,\n label: 'Storage',\n used: storageText(storage.used, storage.limit),\n usedOfLimit: `${storageText(storage.used, storage.limit, false)} / ${storageText(\n storage.limit,\n storage.limit\n )}`,\n pct: pctOf(storage.used, storage.limit),\n },\n {\n key: 'collaborators',\n icon: Users,\n label: 'Collaborators',\n used: fmt(collaborators.used),\n usedOfLimit: unlimitedCollaborators\n ? `${fmt(collaborators.used)} / Unlimited`\n : `${fmt(collaborators.used)} / ${fmt(collaborators.limit)}`,\n // Zero seats (Free) IS the cap: 0 / 0 shows a full destructive bar,\n // because you can't add anyone — the truth the upgrade door answers.\n // (Only when the quota is actually present; a missing field on a\n // partial payload stays an empty bar, not a false alarm.)\n pct: unlimitedCollaborators\n ? 0\n : quotas?.collaborators && collaborators.limit === 0\n ? 100\n : pctOf(collaborators.used, collaborators.limit),\n },\n ]\n}\n","/**\n * Reward-balance logic (hook + money formatter).\n *\n * A user's reward balance is their available account balance — a single GLOBAL per-user\n * number, the Stripe customer account balance mirrored read-only into\n * `settings.extra_discount`. It auto-applies to the user's NEXT invoice.\n *\n * \"Rewards\" is the broad concept: the balance can be fed by several sources (referral\n * reward is just one). The referral pricing surfaces of the billing kit\n * speak to the referral source specifically; this module owns the balance itself.\n */\n\nimport { useUserSettings } from '@arbidocs/react'\n\nexport interface RewardBalance {\n amount: number // minor units (e.g. pennies) of available account balance\n currency: string\n}\n\n/** The user's available reward balance (all sources), or null if none. */\nexport function useRewardBalance(): RewardBalance | null {\n const { data } = useUserSettings()\n // Backend mirrors this read-only as { amount, currency }; schema types it as a loose object.\n const ed = data?.extra_discount as { amount?: number; currency?: string } | null | undefined\n if (!ed || !ed.amount || ed.amount <= 0) return null\n return { amount: ed.amount, currency: ed.currency || 'gbp' }\n}\n\n/** Format minor units as a localized money string, e.g. `£20` (whole) or `£29.99`. */\nexport function formatMoney(cents: number, currency = 'gbp'): string {\n const value = cents / 100\n const isWhole = Number.isInteger(value)\n const c = currency.toLowerCase()\n const symbol =\n c === 'usd' ? '$' : c === 'gbp' ? '£' : c === 'eur' ? '€' : currency.toUpperCase() + ' '\n return `${symbol}${value.toLocaleString('en-US', {\n minimumFractionDigits: isWhole ? 0 : 2,\n maximumFractionDigits: isWhole ? 0 : 2,\n })}`\n}\n","/** Add-on block price helpers (VAT-inclusive pence from `project.addons`). */\n\nimport { formatGbp } from '@arbidocs/react'\nimport type { components } from '@arbidocs/client'\nimport type { AddonKind } from './useAddonPurchase'\nimport { formatMoney } from './reward-balance'\n\ntype ProjectAddons = components['schemas']['ProjectAddons']\n\nconst gbp = (pence: number) => formatMoney(pence, 'gbp')\n\n/** \"3 Nov 2026\". */\nexport function formatPeriodEnd(unix: number | null | undefined): string | null {\n if (!unix) return null\n return new Date(unix * 1000).toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n })\n}\n\n/** What one more block of this kind costs right now, incl. UK VAT. */\nexport function addonPricePence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage' ? addons.ai_block_price_pence : addons.storage_block_price_pence\n}\n\n/** The same price ex VAT. */\nexport function addonPriceExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_price_ex_vat_pence\n : addons.storage_block_price_ex_vat_pence\n}\n\n/**\n * The PUT /addons/settings body (the monthly usage cap and the automatic add-on trigger) from the current add-on\n * state with the given changes applied.\n */\nexport function addonSettingsBody(\n addons: ProjectAddons,\n change: Partial<{\n spend_cap_pence: number | null\n auto_topup_threshold_gbp: number\n }> = {}\n) {\n return {\n spend_cap_pence: addons.spend_cap_pence ?? null,\n auto_topup_threshold_gbp: addons.auto_topup_threshold_gbp,\n ...change,\n }\n}\n\n/**\n * Of `count` blocks of a kind, how many are on an earlier catalogue's price (legacy, at their old size: £10 of AI\n * usage or 10 GB) and how many on today's. A reduction removes today's blocks first, so the legacy ones are the\n * last kept.\n */\nexport function splitBlocks(\n kind: AddonKind,\n count: number,\n addons: ProjectAddons\n): { legacy: number; current: number } {\n const granted =\n (kind === 'ai_usage' ? addons.legacy_ai_blocks : addons.legacy_storage_blocks) ?? 0\n const legacy = Math.max(0, Math.min(granted, count))\n return { legacy, current: count - legacy }\n}\n\n/** One block's size: \"£25 of AI usage\" / \"25 GB\" (legacy: \"£10\" / \"10 GB\"). */\nfunction blockSize(kind: AddonKind, addons: ProjectAddons, legacy: boolean): string {\n if (kind === 'ai_usage') {\n return formatGbp(legacy ? (addons.legacy_block_ai_gbp ?? 10) : addons.block_ai_gbp)\n }\n return `${legacy ? (addons.legacy_block_storage_gb ?? 10) : addons.block_storage_gb} GB`\n}\n\n/**\n * \"2 × £25 of AI usage\", \"1 × £10 + 1 × £25 of AI usage\", \"1 × 10 GB + 2 × 25 GB\": `count` blocks of a kind, the\n * legacy ones (kept last on a reduction) at their old size.\n */\nexport function blocksLabel(kind: AddonKind, count: number, addons: ProjectAddons): string {\n const { legacy, current } = splitBlocks(kind, count, addons)\n const parts = [\n ...(legacy > 0 ? [`${legacy} × ${blockSize(kind, addons, true)}`] : []),\n ...(current > 0 || legacy === 0 ? [`${current} × ${blockSize(kind, addons, false)}`] : []),\n ]\n return `${parts.join(' + ')}${kind === 'ai_usage' ? ' of AI usage' : ''}`\n}\n\n/**\n * Bounds for \"Reduce from next period\": keep `min`..`current` blocks. `next` is what renews\n * now (a pending reduction, or the current count). Storage can't go below what usage needs.\n */\nexport function reduceBounds(\n kind: AddonKind,\n addons: ProjectAddons\n): { min: number; current: number; next: number } {\n const current = kind === 'ai_usage' ? addons.ai_blocks : addons.storage_blocks\n const pending = kind === 'ai_usage' ? addons.ai_blocks_next : addons.storage_blocks_next\n const min = kind === 'ai_usage' ? 0 : Math.min(addons.storage_blocks_min, current)\n return { min, current, next: pending ?? current }\n}\n\n/** One block's monthly-equivalent price ex VAT: AI usage and storage are priced separately. */\nfunction blockMonthlyExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_monthly_ex_vat_pence\n : addons.storage_block_monthly_ex_vat_pence\n}\n\n/** What one block renews at per billing period, ex VAT (annual: 12 × the monthly price). */\nfunction blockPeriodExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_period_ex_vat_pence\n : addons.storage_block_period_ex_vat_pence\n}\n\n/** What one add-on renews at: \"£25/month + VAT\" or \"£225/year + VAT\". */\nexport function blockRenewalShort(\n kind: AddonKind,\n addons: ProjectAddons,\n isAnnual: boolean\n): string {\n return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? 'year' : 'month'} + VAT`\n}\n\n/** The extra line annual plans show beside an AI usage add-on or upgrade price. */\nexport const ANNUAL_PREMIUM_AI_NOTE = 'No markup on Premium AI on annual plans'\n\n/** The confirm dialog's figures: charged now (incl. / ex VAT), what it covers until, then what renews. */\nexport function addonCharge(\n kind: AddonKind,\n addons: ProjectAddons,\n isAnnual: boolean,\n periodEnd: number | null | undefined\n): { now: string; nowExVat: string; nowVat: string; until: string | null; then: string } {\n return {\n now: gbp(addonPricePence(kind, addons)),\n nowExVat: gbp(addonPriceExVatPence(kind, addons)),\n nowVat: gbp(addonPricePence(kind, addons) - addonPriceExVatPence(kind, addons)),\n until: isAnnual ? formatPeriodEnd(periodEnd) : null,\n then: blockRenewalShort(kind, addons, isAnnual),\n }\n}\n\n/** Why automatic top-ups stopped (one line; the price of a step is on the Add button). */\nexport const CAP_REACHED_MESSAGE = 'Usage cap reached: no more automatic top-ups this month.'\n\n/** The £0 usage cap: no automatic top-ups (adding by hand still works). */\nexport const ZERO_BUDGET_MESSAGE = 'Automatic top-ups are off (£0 usage cap).'\n\n/** The usage cap's heading wherever it is edited or shown. */\nexport const USAGE_CAP_LABEL = 'Usage cap'\n\n/** What the usage cap means (in \"More info\"): £0 and \"No limit\" explained. */\nexport const USAGE_CAP_HELP =\n 'The most automatic top-ups (AI usage added when it runs low, storage added when an upload needs it) may ' +\n 'cost in a month. Adding or removing by hand is never limited by it; the plan and renewals are not counted. ' +\n '£0 = no automatic top-ups: AI usage and uploads stop at your allowance unless you add more. ' +\n 'No limit = no cap.'\n\n/** A £0 cap turns automatic top-ups off (manual adds still work). */\nexport function isAddonBudgetZero(addons: Pick<ProjectAddons, 'spend_cap_pence'>): boolean {\n return addons.spend_cap_pence === 0\n}\n\n/**\n * How much more of a resource automatic top-ups may add this month before the usage cap stops them, in the\n * resource's unit (£ of AI usage / GB): the whole blocks the cap left pays for at today's ex-VAT price. `null` =\n * no limit; 0 with a £0 cap or when not even one block fits.\n */\nexport function autoRoom(kind: AddonKind, addons: ProjectAddons): number | null {\n const left = addons.cap_left_pence\n if (left == null) return addons.spend_cap_pence === 0 ? 0 : null\n const price = addonPriceExVatPence(kind, addons)\n if (price <= 0) return 0\n const blocks = Math.floor(left / price)\n return blocks * (kind === 'ai_usage' ? addons.block_ai_gbp : addons.block_storage_gb)\n}\n\n/** The size of the block a one-step reduction removes from what renews (today's blocks go first). */\nexport function nextRemovedBlockSize(kind: AddonKind, addons: ProjectAddons): number {\n const { next } = reduceBounds(kind, addons)\n const { current } = splitBlocks(kind, next, addons)\n if (kind === 'ai_usage') {\n return current > 0 ? addons.block_ai_gbp : (addons.legacy_block_ai_gbp ?? 10)\n }\n return current > 0 ? addons.block_storage_gb : (addons.legacy_block_storage_gb ?? 10)\n}\n\n/**\n * Pounds typed into a usage cap field → pence. '' → the default (null); invalid → undefined.\n * Equal to the default is also sent as null (\"keep the default\").\n */\nexport function parseBudgetPounds(value: string): number | null | undefined {\n if (value.trim() === '') return null\n const n = Number(value)\n if (!Number.isFinite(n) || n < 0) return undefined\n return Math.round(n * 100)\n}\n\n/** Toast after saving the usage cap. */\nexport function budgetSavedMessage(capPence: number | null): string {\n if (capPence == null) return 'Usage cap: no limit'\n if (capPence === 0) return 'Usage cap: £0 (no automatic top-ups)'\n return `Usage cap: ${gbp(capPence)} a month`\n}\n\nconst gbText = (n: number) => `${Math.round(n * 10) / 10} GB`\n\n/** \"£25 of AI usage\" / \"25 GB\". */\nexport function stepLabel(kind: AddonKind, size: number): string {\n return kind === 'ai_usage' ? `${formatGbp(size)} of AI usage` : gbText(size)\n}\n\n/** The PUT /addons/next-period body that renews `keep` blocks of `kind` (the other kind unchanged). */\nexport function nextPeriodBody(kind: AddonKind, keep: number) {\n return kind === 'ai_usage'\n ? { ai_blocks: keep, storage_blocks: null }\n : { ai_blocks: null, storage_blocks: keep }\n}\n\n/**\n * The \"+\" step's price line, ex VAT: \"£25/month · today £25\" (annual: \"£18.75/month · today £150\");\n * storage, charged for the rest of the period: \"£25/month · prorated today £8.33\".\n */\nexport function addStepCaption(kind: AddonKind, addons: ProjectAddons): string {\n const now = gbp(addonPriceExVatPence(kind, addons))\n const today = kind === 'storage' ? `prorated today ${now}` : `today ${now}`\n return `${gbp(blockMonthlyExVatPence(kind, addons))}/month · ${today}`\n}\n","/**\n * The `detail` of a refused billing call: the response body's `detail` (a string, or the\n * `{code, message}` of a payment refusal), whether the error is an SDK `ArbiApiError` (body in\n * `apiError`) or the body itself.\n */\nexport function apiErrorDetail(error: unknown): unknown {\n const body = (error as { apiError?: unknown } | null)?.apiError ?? error\n return (body as { detail?: unknown } | null)?.detail\n}\n","/**\n * Tax-inclusive totals from the backend's Stripe Tax quotes (`TaxQuoteResponse`, plan-change\n * previews, add-on quotes): \"£48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\", \"£40.00 · no VAT\n * (reverse charge)\", \"£40.00 · no tax\".\n */\n\nimport type { components } from '@arbidocs/client'\nimport { apiErrorDetail } from './api-error'\n\nexport type TaxQuote = components['schemas']['TaxQuoteResponse']\n\n/** Countries whose tax depends on the postcode: the field is shown up front. */\nexport const POSTCODE_COUNTRIES: readonly string[] = ['US', 'CA']\n\n/** The quote error that asks for a ZIP / postal code. */\nexport const TAX_LOCATION_INVALID = 'TAX_LOCATION_INVALID'\n\n/** \"£42.00\" — always pence, so the parts of a total add up on screen. */\nexport function money2(pence: number, currency = 'gbp'): string {\n const symbol = { gbp: '£', usd: '$', eur: '€' }[currency.toLowerCase()] ?? `${currency} `\n return `${symbol}${(pence / 100).toLocaleString('en-GB', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}`\n}\n\nexport interface TaxParts {\n subtotal: number\n tax: number\n total: number\n /** \"VAT 20%\"; null when no tax is due. */\n label?: string | null\n reverseCharge?: boolean\n}\n\n/** \"VAT\" for a VAT label, else \"tax\". */\nfunction taxKind(label: string | null | undefined): string {\n return /^vat\\b/i.test(label ?? '') ? 'VAT' : 'tax'\n}\n\n/** What follows the total: \"incl. VAT (£40.00 + £8.00 VAT 20%)\", \"· no VAT (reverse charge)\", \"· no tax\". */\nexport function taxDetail({ subtotal, tax, label, reverseCharge }: TaxParts): string {\n if (reverseCharge) return '· no VAT (reverse charge)'\n if (tax <= 0) return '· no tax'\n return `incl. ${taxKind(label)} (${money2(subtotal)} + ${money2(tax)} ${label ?? 'tax'})`\n}\n\n/** \"£48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\", \"£40.00 · no VAT (reverse charge)\", \"£40.00 · no tax\". */\nexport function taxSummary(parts: TaxParts): string {\n return `${money2(parts.total)} ${taxDetail(parts)}`\n}\n\n/** \"£21.00 incl. VAT\" / \"£17.50\" — a total with only the word for its tax. */\nexport function totalWithTaxWord({ total, tax, label }: TaxParts): string {\n return tax > 0 ? `${money2(total)} incl. ${taxKind(label)}` : money2(total)\n}\n\n/** The parts of a quote response. */\nexport function quoteParts(quote: TaxQuote): TaxParts {\n return {\n subtotal: quote.subtotal_pence,\n tax: quote.tax_pence,\n total: quote.total_pence,\n label: quote.tax_label,\n reverseCharge: quote.reverse_charge,\n }\n}\n\n/** \"Total today £48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\". */\nexport function quoteTotalLine(quote: TaxQuote): string {\n return `Total today ${taxSummary(quoteParts(quote))}`\n}\n\n/** The API error code (`detail.code`) and message, if any. */\nexport function quoteError(error: unknown): { code?: string; message?: string } {\n const detail = apiErrorDetail(error)\n if (detail && typeof detail === 'object') {\n const { code, message } = detail as { code?: unknown; message?: unknown }\n return {\n code: typeof code === 'string' ? code : undefined,\n message: typeof message === 'string' ? message : undefined,\n }\n }\n return { message: typeof detail === 'string' ? detail : undefined }\n}\n","/**\n * Pro plan price helpers. Prices come from Stripe via GET /v1/user/products (source of truth).\n * One plan is sold: Pro, one licence (£10 of AI usage + 5 GB a month). Each price lists\n * `pack_prices` for that one licence — `amount` incl. UK VAT and `amount_ex_vat` — and\n * `addon_prices` (one add-on block on that interval). Display them as-is.\n * Legacy subscriptions with several packs keep working; their name and price come from the project.\n */\n\nimport type { components } from '@arbidocs/client'\nimport { formatGbp } from '@arbidocs/react'\nimport { formatMoney } from './reward-balance'\nimport { formatPeriodEnd } from './addon-pricing'\nimport { taxSummary } from './tax-quote'\nimport { ADDON_BLOCK_AI_GBP, STORAGE_BLOCK_GB } from './plan-constants'\n\ntype PlanChangePreview = components['schemas']['PlanChangePreview']\ntype ProductPrice = components['schemas']['ProductPrice']\ntype ProductResponse = components['schemas']['ProductResponse']\ntype PackPrice = components['schemas']['PackPrice']\ntype AddonPrice = components['schemas']['AddonPrice']\n\nexport type PlanInterval = 'month' | 'year'\n\nconst gbp = (pence: number) => formatMoney(pence, 'gbp')\n\n/** The headline price: \"£40 + VAT\". */\nexport function plusVat(exVatPence: number, currency = 'gbp'): string {\n return `${formatMoney(exVatPence, currency)} + VAT`\n}\n\n/** The one licence Pro sells. Other pack counts exist only on legacy subscriptions (displayed, not sold). */\nexport const PRO_PACKS = 1\n\n/** A plan the pickers offer. */\nexport type PlanChoice = 'free' | 'pro'\n\n/** Packs per plan choice (0 = Free). */\nexport const PLAN_CHOICE_PACKS: Record<PlanChoice, number> = {\n free: 0,\n pro: PRO_PACKS,\n}\n\n/** \"Free\" (0), \"Pro\" (1), otherwise \"Pro ×N\" (a legacy multi-pack plan; the API's own name wins). */\nexport function planNameForPacks(packs: number | null | undefined): string {\n const n = packs ?? 0\n if (n <= 0) return 'Free'\n if (n === PRO_PACKS) return 'Pro'\n return `Pro ×${n}`\n}\n\n/** The plan-change panel's fixed lines, each with a test id. */\nexport interface PlanChangeLine {\n id: 'pay-today' | 'from' | 'current-until' | 'no-change' | 'awaiting-bank'\n text: string\n}\n\n/**\n * What a plan change costs, from the backend's Stripe invoice preview (taxed for the customer's\n * address), as fixed lines:\n * - \"Pay today £42.00 incl. VAT (£35.00 + £7.00 VAT 20%)\" (\"Pay today £0\" for a change that waits)\n * - \"From 16 Nov 2026: £210.00 incl. VAT (£175.00 + £35.00 VAT 20%) a month\"\n * - \"Current plan until 16 Nov 2026\" for a change that waits for the period end.\n * \"No change\" when the selection is what the project already has; \"Waiting for your bank\" while\n * an earlier upgrade payment awaits 3-D Secure.\n */\nexport function planPreviewLines(preview: PlanChangePreview): PlanChangeLine[] {\n if (preview.unchanged) return [{ id: 'no-change', text: 'No change' }]\n const date = formatPeriodEnd(preview.next_renewal_at)\n const cadence = preview.interval === 'year' ? 'year' : 'month'\n const lines: PlanChangeLine[] = [\n preview.awaiting_confirmation\n ? { id: 'awaiting-bank', text: 'Waiting for your bank' }\n : {\n id: 'pay-today',\n text: preview.effective_now\n ? `Pay today ${taxSummary({\n subtotal: preview.due_now_ex_vat_pence,\n tax: preview.due_now_tax_pence,\n total: preview.due_now_pence,\n label: preview.tax_label,\n })}`\n : `Pay today ${gbp(0)}`,\n },\n {\n id: 'from',\n text: `${date ? `From ${date}` : 'From renewal'}: ${taxSummary({\n subtotal: preview.next_renewal_ex_vat_pence,\n tax: preview.next_renewal_tax_pence,\n total: preview.next_renewal_pence,\n label: preview.tax_label,\n })} a ${cadence}`,\n },\n ]\n if (!preview.effective_now) {\n lines.push({ id: 'current-until', text: `Current plan until ${date ?? 'renewal'}` })\n }\n return lines\n}\n\n/** Stand-in for \"Pay today\" when the preview failed for an upgrade. */\nexport const PAY_TODAY_UNPRICED = 'Pay today: the difference for the rest of this period'\n\n/** One add-on block's price on this interval's plan price (`addon_prices`), if listed. */\nexport function addonPrice(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage' = 'ai_usage'\n): AddonPrice | undefined {\n return price?.addon_prices?.find((p) => p.kind === kind)\n}\n\n/** Per add-on block of `kind` per month, ex VAT (annual plans: the monthly equivalent). */\nexport function addonMonthlyExVat(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage' = 'ai_usage'\n): number | undefined {\n return addonPrice(price, kind)?.monthly_ex_vat\n}\n\n/**\n * The add-on offer on this interval's price, each block with its own price:\n * \"£25 of AI usage for £25/month + VAT or 25 GB for £25/month + VAT\". Null when unlisted.\n */\nexport function addonOfferText(price: ProductPrice | undefined): string | null {\n const ai = addonPrice(price, 'ai_usage')\n const storage = addonPrice(price, 'storage')\n const parts = [\n ai && `${formatGbp(ai.ai_gbp)} of AI usage for ${gbp(ai.monthly_ex_vat)}/month + VAT`,\n storage && `${storage.storage_gb} GB for ${gbp(storage.monthly_ex_vat)}/month + VAT`,\n ].filter(Boolean)\n return parts.length ? parts.join(' or ') : null\n}\n\n/** What a checkout buys: \"Pro\", \"Pro + 2 × £25 of AI usage + 1 × 25 GB\". */\nexport function checkoutItemsLabel(aiBlocks: number, storageBlocks: number): string {\n return [\n 'Pro',\n aiBlocks > 0 && `${aiBlocks} × ${formatGbp(ADDON_BLOCK_AI_GBP)} of AI usage`,\n storageBlocks > 0 && `${storageBlocks} × ${STORAGE_BLOCK_GB} GB`,\n ]\n .filter(Boolean)\n .join(' + ')\n}\n\n/** The price of `packs` packs on this interval's price, if Stripe lists it (only one licence is listed). */\nexport function packPrice(\n price: ProductPrice | undefined,\n packs: number = PRO_PACKS\n): PackPrice | undefined {\n return price?.pack_prices?.find((p) => p.packs === packs)\n}\n\nexport interface ProPrices {\n monthly?: ProductPrice\n annual?: ProductPrice\n}\n\n/** The Pro product's monthly and annual prices (default price per interval wins). */\nexport function findProPrices(products: ProductResponse[] | undefined): ProPrices {\n const prices = products?.[0]?.prices ?? []\n const pick = (interval: PlanInterval) =>\n prices.find((p) => p.interval === interval && p.default_price) ??\n prices.find((p) => p.interval === interval)\n return { monthly: pick('month'), annual: pick('year') }\n}\n\n/** Annual savings vs 12 × monthly, as a whole percent (e.g. £360 vs £480 → 25). */\nexport function calcSavingsPercent(\n monthlyPrice: ProductPrice | undefined,\n annualPrice: ProductPrice | undefined\n): number | null {\n const monthly = packPrice(monthlyPrice)?.amount ?? monthlyPrice?.amount\n const annual = packPrice(annualPrice)?.amount ?? annualPrice?.amount\n if (monthly == null || annual == null || monthly === 0) return null\n const savings = Math.round(((monthly * 12 - annual) / (monthly * 12)) * 100)\n return savings > 0 ? savings : null\n}\n\n/** Most add-on blocks of each kind a checkout may include (the backend's limit). */\nexport const MAX_CHECKOUT_BLOCKS = 100\n\n/** \"£25/month + VAT each\", annual: \"£225/year + VAT each\": one block on the chosen interval. */\nexport function checkoutBlockPriceText(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage'\n): string | null {\n const block = addonPrice(price, kind)\n if (!block) return null\n const currency = price?.currency ?? 'gbp'\n return price?.interval === 'year'\n ? `${formatMoney(block.amount_ex_vat, currency)}/year + VAT each`\n : `${formatMoney(block.amount_ex_vat, currency)}/month + VAT each`\n}\n","import type { PlanBadgeProps } from '../../settings'\nimport type { components } from '@arbidocs/client'\nimport { planNameForPacks } from './plan-pricing'\n\ntype ProjectBillingFields = Pick<\n components['schemas']['ProjectResponse'],\n 'subscription' | 'plan_interval'\n> &\n Partial<Pick<components['schemas']['ProjectResponse'], 'packs' | 'addons'>>\n\n/** A project is on Pro when it has a billing interval (null = Free or checkout pending). */\nexport function isProProject(project: Pick<ProjectBillingFields, 'plan_interval'>): boolean {\n return project.plan_interval != null\n}\n\n/** \"Pro\", or a legacy multi-pack plan's name (\"Pro ×N\") — the API's name when it sends one (owner view). */\nexport function proTierName(project: ProjectBillingFields): string {\n return project.addons?.plan_name || planNameForPacks(Math.max(project.packs ?? 1, 1))\n}\n\n/** \"Free\", \"Pro monthly\", \"Pro annual\", \"Pro ×3 monthly\" (legacy) or \"Dev\" — the full plan name. */\nexport function planName(project: ProjectBillingFields): string {\n if (project.subscription === 'dev') return 'Dev'\n if (!isProProject(project)) return 'Free'\n return `${proTierName(project)} ${project.plan_interval === 'year' ? 'annual' : 'monthly'}`\n}\n\n/**\n * Maps a project's billing state to the `PlanBadge` block's label + tone.\n * Pro reads as accent, Free/dev as neutral, payment trouble or cancelling as pending.\n */\nexport function planBadgeView(\n project: ProjectBillingFields\n): Pick<PlanBadgeProps, 'label' | 'tone'> {\n const { subscription } = project\n const label =\n subscription === 'canceled'\n ? 'Cancelling'\n : subscription === 'dev'\n ? 'Dev'\n : isProProject(project)\n ? proTierName(project)\n : 'Free'\n\n const tone =\n subscription === 'dev'\n ? 'dev'\n : subscription === 'canceled' || subscription === 'past_due'\n ? 'pending'\n : isProProject(project)\n ? 'paid'\n : 'free'\n\n return { label, tone }\n}\n","/**\n * What a project's group in the workspace list says about the project itself: its plan badge (own\n * projects, where plans are sold), the usage readout beside its name, and the plan and limits\n * breakdown behind the name. One source for the cards' bands and the table's headings.\n */\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport type { components } from '@arbidocs/client'\nimport { UsageMeter } from '../../components/UsageMeter'\nimport { PlanBadge, QuotaMeter } from '../../settings'\nimport { ProjectBreakdown, ProjectReadout } from '../../home/ProjectBandParts'\nimport { planBadgeView } from '../billing/plan-badge'\nimport { getQuotaItems } from '../billing/quota-items'\n\ntype Project = components['schemas']['ProjectResponse']\n\nexport interface ProjectGroupBilling {\n /** The plan badge: own projects only, where plans are sold. */\n planBadge?: ReactNode\n /** Plan and usage, right after the name (projects with quotas). */\n readout?: ReactNode\n /** The plan and limits panel behind the name; meters for own projects only. */\n breakdown?: ReactNode\n}\n\nexport function projectGroupBilling(\n project: Project | undefined,\n { isOwned, stripeEnabled }: { isOwned: boolean; stripeEnabled: boolean }\n): ProjectGroupBilling {\n if (!project) return {}\n const planBadge = isOwned && stripeEnabled ? <PlanBadge {...planBadgeView(project)} /> : undefined\n const quotaItems = project.quotas ? getQuotaItems(project.quotas) : []\n const readout = project.quotas ? (\n <ProjectReadout\n plan={\n planBadge ? { badge: planBadge, testId: `project-tier-${project.external_id}` } : undefined\n }\n usage={{\n testId: `project-usage-${project.external_id}`,\n // Bare numbers without plans for sale: a glance beside the name, not the Plan & Usage detail.\n node: (\n <QuotaMeter\n items={quotaItems}\n size=\"sm\"\n showTooltips={false}\n showBars={stripeEnabled}\n showPercent={false}\n />\n ),\n }}\n />\n ) : (\n planBadge\n )\n const breakdown = (\n <ProjectBreakdown\n name={project.name}\n badge={planBadge}\n meters={\n isOwned\n ? quotaItems.map(({ key, icon, label, usedOfLimit, pct }) => (\n <UsageMeter\n key={key}\n icon={icon as LucideIcon}\n label={label}\n value={usedOfLimit}\n pct={pct}\n />\n ))\n : undefined\n }\n dailyCap={{\n label: 'Daily cap',\n value:\n project.daily_cap_per_user == null\n ? 'No limit'\n : project.daily_cap_per_user.toLocaleString('en-US'),\n }}\n />\n )\n return { planBadge, readout, breakdown }\n}\n"]}
@@ -3,16 +3,16 @@
3
3
 
4
4
  var chunk223D4727_cjs = require('./chunk-223D4727.cjs');
5
5
  var chunk5Y6V7F6W_cjs = require('./chunk-5Y6V7F6W.cjs');
6
- var chunkVLCY46K6_cjs = require('./chunk-VLCY46K6.cjs');
7
6
  var chunkDVUZTGVL_cjs = require('./chunk-DVUZTGVL.cjs');
8
- var chunkHUNX4CQG_cjs = require('./chunk-HUNX4CQG.cjs');
9
7
  var chunkV4ISCZJ7_cjs = require('./chunk-V4ISCZJ7.cjs');
10
8
  var chunk2EI7NCUA_cjs = require('./chunk-2EI7NCUA.cjs');
11
- var chunkC2CWWEHO_cjs = require('./chunk-C2CWWEHO.cjs');
9
+ var chunkHUNX4CQG_cjs = require('./chunk-HUNX4CQG.cjs');
12
10
  var chunk32HW2Q33_cjs = require('./chunk-32HW2Q33.cjs');
11
+ var chunkKDG6FER6_cjs = require('./chunk-KDG6FER6.cjs');
12
+ var chunkVLCY46K6_cjs = require('./chunk-VLCY46K6.cjs');
13
+ var chunkC2CWWEHO_cjs = require('./chunk-C2CWWEHO.cjs');
13
14
  var chunkMHVNT4J3_cjs = require('./chunk-MHVNT4J3.cjs');
14
15
  var chunkULWRNGGE_cjs = require('./chunk-ULWRNGGE.cjs');
15
- var chunkKDG6FER6_cjs = require('./chunk-KDG6FER6.cjs');
16
16
  var lucideReact = require('lucide-react');
17
17
  var ui = require('@arbidocs/react/ui');
18
18
  var jsxRuntime = require('react/jsx-runtime');
@@ -6227,5 +6227,5 @@ exports.visibilityAudience = visibilityAudience;
6227
6227
  exports.visibilityToggleTooltip = visibilityToggleTooltip;
6228
6228
  exports.visibleColumns = visibleColumns;
6229
6229
  exports.widthsMatch = widthsMatch;
6230
- //# sourceMappingURL=chunk-NUAC5GTQ.cjs.map
6231
- //# sourceMappingURL=chunk-NUAC5GTQ.cjs.map
6230
+ //# sourceMappingURL=chunk-JB4EXO3V.cjs.map
6231
+ //# sourceMappingURL=chunk-JB4EXO3V.cjs.map