@arbidocs/blocks 0.5.118 → 0.5.120

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 (145) hide show
  1. package/dist/{ConnectedCallDock-E7RMQMMT.cjs → ConnectedCallDock-WER7R222.cjs} +12 -12
  2. package/dist/{ConnectedCallDock-E7RMQMMT.cjs.map → ConnectedCallDock-WER7R222.cjs.map} +1 -1
  3. package/dist/{ConnectedCallRoom-FF7X6LQ2.cjs → ConnectedCallRoom-T7OBQTYP.cjs} +12 -12
  4. package/dist/{ConnectedCallRoom-FF7X6LQ2.cjs.map → ConnectedCallRoom-T7OBQTYP.cjs.map} +1 -1
  5. package/dist/{ConnectedNativeDocxComparison-PB2LXT5U.cjs → ConnectedNativeDocxComparison-SGE4R3M4.cjs} +11 -11
  6. package/dist/{ConnectedNativeDocxComparison-PB2LXT5U.cjs.map → ConnectedNativeDocxComparison-SGE4R3M4.cjs.map} +1 -1
  7. package/dist/{ConnectedStreamedWordArtifact-S7ICDK5T.cjs → ConnectedStreamedWordArtifact-6MCLJRIT.cjs} +11 -11
  8. package/dist/{ConnectedStreamedWordArtifact-S7ICDK5T.cjs.map → ConnectedStreamedWordArtifact-6MCLJRIT.cjs.map} +1 -1
  9. package/dist/{DocumentReviewCard-L472OMQS.cjs → DocumentReviewCard-BPIAJJED.cjs} +17 -17
  10. package/dist/{DocumentReviewCard-L472OMQS.cjs.map → DocumentReviewCard-BPIAJJED.cjs.map} +1 -1
  11. package/dist/{FilesViewerDialog-JK25HKKF.cjs → FilesViewerDialog-67WK3TZC.cjs} +26 -26
  12. package/dist/{FilesViewerDialog-JK25HKKF.cjs.map → FilesViewerDialog-67WK3TZC.cjs.map} +1 -1
  13. package/dist/{WorkspaceTableView-JCTDEMIL.cjs → WorkspaceTableView-J3C6VH4T.cjs} +8 -8
  14. package/dist/{WorkspaceTableView-JCTDEMIL.cjs.map → WorkspaceTableView-J3C6VH4T.cjs.map} +1 -1
  15. package/dist/agents/index.cjs +187 -187
  16. package/dist/agents/tasks/ConversationListView.js +10 -2
  17. package/dist/agents/tasks/ConversationListView.js.map +2 -2
  18. package/dist/agents/tasks/TaskTable.js +5 -4
  19. package/dist/agents/tasks/TaskTable.js.map +2 -2
  20. package/dist/agents/tasks/WorkflowsTable.js +4 -5
  21. package/dist/agents/tasks/WorkflowsTable.js.map +2 -2
  22. package/dist/agents/tasks/taskColumns.js +1 -1
  23. package/dist/agents/tasks/taskColumns.js.map +1 -1
  24. package/dist/agents/tasks/taskTableLabels.js +1 -1
  25. package/dist/agents/tasks/taskTableLabels.js.map +1 -1
  26. package/dist/agents/tasks/workflowTableLabels.js +1 -1
  27. package/dist/agents/tasks/workflowTableLabels.js.map +1 -1
  28. package/dist/{ai-OBS67QV4.cjs → ai-IM4A23YT.cjs} +16 -16
  29. package/dist/ai-IM4A23YT.cjs.map +1 -0
  30. package/dist/calls/index.cjs +2 -2
  31. package/dist/{chunk-P77YTTJ3.cjs → chunk-3GWWOYGS.cjs} +4 -4
  32. package/dist/{chunk-P77YTTJ3.cjs.map → chunk-3GWWOYGS.cjs.map} +1 -1
  33. package/dist/{chunk-QOXK5O6M.cjs → chunk-557MJ4IH.cjs} +12 -12
  34. package/dist/{chunk-QOXK5O6M.cjs.map → chunk-557MJ4IH.cjs.map} +1 -1
  35. package/dist/{chunk-YDPLSXHB.cjs → chunk-5IZFSAEZ.cjs} +4 -4
  36. package/dist/{chunk-YDPLSXHB.cjs.map → chunk-5IZFSAEZ.cjs.map} +1 -1
  37. package/dist/{chunk-JSQQV23D.cjs → chunk-6UTKM5AB.cjs} +3 -3
  38. package/dist/{chunk-JSQQV23D.cjs.map → chunk-6UTKM5AB.cjs.map} +1 -1
  39. package/dist/{chunk-PVPRVI54.cjs → chunk-775S43W7.cjs} +21 -12
  40. package/dist/chunk-775S43W7.cjs.map +1 -0
  41. package/dist/{chunk-3LNUQYAF.cjs → chunk-B3LZITVF.cjs} +3 -3
  42. package/dist/{chunk-3LNUQYAF.cjs.map → chunk-B3LZITVF.cjs.map} +1 -1
  43. package/dist/{chunk-SUYPXUE6.cjs → chunk-BP3IJ34D.cjs} +6 -6
  44. package/dist/{chunk-SUYPXUE6.cjs.map → chunk-BP3IJ34D.cjs.map} +1 -1
  45. package/dist/{chunk-TT4IF52H.cjs → chunk-BUVIBHFU.cjs} +37 -38
  46. package/dist/chunk-BUVIBHFU.cjs.map +1 -0
  47. package/dist/{chunk-OPH4UZQN.cjs → chunk-CKCG4NDB.cjs} +33 -33
  48. package/dist/{chunk-OPH4UZQN.cjs.map → chunk-CKCG4NDB.cjs.map} +1 -1
  49. package/dist/{chunk-7IPIKMCY.cjs → chunk-CPR6EHG7.cjs} +3 -3
  50. package/dist/{chunk-7IPIKMCY.cjs.map → chunk-CPR6EHG7.cjs.map} +1 -1
  51. package/dist/{chunk-YGXOKB5Z.cjs → chunk-DFBDQKGE.cjs} +4 -4
  52. package/dist/{chunk-YGXOKB5Z.cjs.map → chunk-DFBDQKGE.cjs.map} +1 -1
  53. package/dist/{chunk-K6AUKKUN.cjs → chunk-ECLUFKYR.cjs} +6 -6
  54. package/dist/{chunk-K6AUKKUN.cjs.map → chunk-ECLUFKYR.cjs.map} +1 -1
  55. package/dist/{chunk-CPG7II4E.cjs → chunk-HA66DHW6.cjs} +28 -28
  56. package/dist/{chunk-CPG7II4E.cjs.map → chunk-HA66DHW6.cjs.map} +1 -1
  57. package/dist/{chunk-P2MOLUFO.cjs → chunk-KTQOGT2L.cjs} +6 -6
  58. package/dist/{chunk-P2MOLUFO.cjs.map → chunk-KTQOGT2L.cjs.map} +1 -1
  59. package/dist/{chunk-57NHXEU2.cjs → chunk-MXBXZXXQ.cjs} +5 -5
  60. package/dist/{chunk-57NHXEU2.cjs.map → chunk-MXBXZXXQ.cjs.map} +1 -1
  61. package/dist/{chunk-L6APDO3Y.cjs → chunk-NJCK6V6U.cjs} +3 -3
  62. package/dist/{chunk-L6APDO3Y.cjs.map → chunk-NJCK6V6U.cjs.map} +1 -1
  63. package/dist/{chunk-NNUK5ZM7.cjs → chunk-NLSWDP42.cjs} +9 -17
  64. package/dist/chunk-NLSWDP42.cjs.map +1 -0
  65. package/dist/{chunk-ZCIQBHH5.cjs → chunk-Q6CLWOP2.cjs} +36 -36
  66. package/dist/{chunk-ZCIQBHH5.cjs.map → chunk-Q6CLWOP2.cjs.map} +1 -1
  67. package/dist/{chunk-XZYIS2JZ.cjs → chunk-QQWHG3KF.cjs} +6 -7
  68. package/dist/chunk-QQWHG3KF.cjs.map +1 -0
  69. package/dist/{chunk-KHAO7ZTO.cjs → chunk-T23UTIRI.cjs} +5 -5
  70. package/dist/{chunk-KHAO7ZTO.cjs.map → chunk-T23UTIRI.cjs.map} +1 -1
  71. package/dist/{chunk-O4F77VSO.cjs → chunk-TDO5UWXF.cjs} +5 -5
  72. package/dist/{chunk-O4F77VSO.cjs.map → chunk-TDO5UWXF.cjs.map} +1 -1
  73. package/dist/{chunk-VLCY46K6.cjs → chunk-TG2GM7WJ.cjs} +4 -3
  74. package/dist/chunk-TG2GM7WJ.cjs.map +1 -0
  75. package/dist/{chunk-5XAJ4N2S.cjs → chunk-VZCPMXWC.cjs} +4 -4
  76. package/dist/{chunk-5XAJ4N2S.cjs.map → chunk-VZCPMXWC.cjs.map} +1 -1
  77. package/dist/{chunk-MMVDVHKE.cjs → chunk-W6INT5GS.cjs} +5 -5
  78. package/dist/{chunk-MMVDVHKE.cjs.map → chunk-W6INT5GS.cjs.map} +1 -1
  79. package/dist/{chunk-ZPPMAL5P.cjs → chunk-WQMR3O2D.cjs} +6 -10
  80. package/dist/chunk-WQMR3O2D.cjs.map +1 -0
  81. package/dist/{chunk-CB3YYZAU.cjs → chunk-X36UR4A7.cjs} +175 -58
  82. package/dist/chunk-X36UR4A7.cjs.map +1 -0
  83. package/dist/{chunk-6ULADDBW.cjs → chunk-YYSBWMDJ.cjs} +4 -4
  84. package/dist/{chunk-6ULADDBW.cjs.map → chunk-YYSBWMDJ.cjs.map} +1 -1
  85. package/dist/collaboration/index.cjs +3 -3
  86. package/dist/collaboration/sharing/ShareControl.js +10 -2
  87. package/dist/collaboration/sharing/ShareControl.js.map +2 -2
  88. package/dist/components/SharedCell.d.ts +2 -4
  89. package/dist/components/SharedCell.js +4 -8
  90. package/dist/components/SharedCell.js.map +2 -2
  91. package/dist/connect/docx.cjs +14 -14
  92. package/dist/connect/index.cjs +965 -937
  93. package/dist/connect/index.cjs.map +1 -1
  94. package/dist/connect/viewer.cjs +16 -16
  95. package/dist/connection/facts/factPageColumns.js +3 -9
  96. package/dist/connection/facts/factPageColumns.js.map +2 -2
  97. package/dist/connection/files/grid/cells.js +1 -3
  98. package/dist/connection/files/grid/cells.js.map +2 -2
  99. package/dist/connection/files/grid/documentColumns.js +3 -2
  100. package/dist/connection/files/grid/documentColumns.js.map +2 -2
  101. package/dist/connection/sharing/WorkspaceMembersList.d.ts +4 -0
  102. package/dist/connection/sharing/WorkspaceMembersList.js +34 -0
  103. package/dist/connection/sharing/WorkspaceMembersList.js.map +7 -0
  104. package/dist/connection/shell/ConnectedAppShell.js +4 -0
  105. package/dist/connection/shell/ConnectedAppShell.js.map +2 -2
  106. package/dist/connection/viewer/ConnectedDocumentViewer.js +3 -2
  107. package/dist/connection/viewer/ConnectedDocumentViewer.js.map +2 -2
  108. package/dist/connection/viewer/ViewerPdfPane.js +20 -2
  109. package/dist/connection/viewer/ViewerPdfPane.js.map +2 -2
  110. package/dist/connection/viewer/pdf/PdfPassageHighlights.d.ts +12 -0
  111. package/dist/connection/viewer/pdf/PdfPassageHighlights.js +36 -0
  112. package/dist/connection/viewer/pdf/PdfPassageHighlights.js.map +7 -0
  113. package/dist/connection/viewer/pdf/passageHighlights.d.ts +26 -0
  114. package/dist/connection/viewer/pdf/passageHighlights.js +40 -0
  115. package/dist/connection/viewer/pdf/passageHighlights.js.map +7 -0
  116. package/dist/connection/viewer/pdf/usePdfPassageHighlights.d.ts +9 -0
  117. package/dist/connection/viewer/pdf/usePdfPassageHighlights.js +47 -0
  118. package/dist/connection/viewer/pdf/usePdfPassageHighlights.js.map +7 -0
  119. package/dist/connection/workflows/workflowsPageLabels.js +1 -1
  120. package/dist/connection/workflows/workflowsPageLabels.js.map +1 -1
  121. package/dist/dashboards/index.cjs +1 -1
  122. package/dist/embed/index.cjs +26 -26
  123. package/dist/facts/factColumns.js +3 -3
  124. package/dist/facts/factColumns.js.map +1 -1
  125. package/dist/facts/index.cjs +42 -42
  126. package/dist/files/HtmlViewerParts.js +7 -15
  127. package/dist/files/HtmlViewerParts.js.map +2 -2
  128. package/dist/files/index.cjs +223 -223
  129. package/dist/index.cjs +321 -321
  130. package/dist/settings/index.cjs +147 -147
  131. package/dist/shell/index.cjs +35 -35
  132. package/dist/studio/index.cjs +8 -8
  133. package/dist/styles.css +1 -1
  134. package/dist/workflow-canvas/index.cjs +36 -36
  135. package/dist/workflow-canvas/index.cjs.map +1 -1
  136. package/node_modules/@arbidocs/coedit/package.json +1 -1
  137. package/package.json +5 -5
  138. package/dist/ai-OBS67QV4.cjs.map +0 -1
  139. package/dist/chunk-CB3YYZAU.cjs.map +0 -1
  140. package/dist/chunk-NNUK5ZM7.cjs.map +0 -1
  141. package/dist/chunk-PVPRVI54.cjs.map +0 -1
  142. package/dist/chunk-TT4IF52H.cjs.map +0 -1
  143. package/dist/chunk-VLCY46K6.cjs.map +0 -1
  144. package/dist/chunk-XZYIS2JZ.cjs.map +0 -1
  145. package/dist/chunk-ZPPMAL5P.cjs.map +0 -1
@@ -6,9 +6,9 @@ var chunk4PA6VCCR_cjs = require('./chunk-4PA6VCCR.cjs');
6
6
  var chunkRL2HI6NP_cjs = require('./chunk-RL2HI6NP.cjs');
7
7
  var chunk6UZC4XVV_cjs = require('./chunk-6UZC4XVV.cjs');
8
8
  var chunkE7TE2WVT_cjs = require('./chunk-E7TE2WVT.cjs');
9
- var chunkIPSHAMO2_cjs = require('./chunk-IPSHAMO2.cjs');
10
9
  var chunkSJSGSHIT_cjs = require('./chunk-SJSGSHIT.cjs');
11
10
  var chunkZW6FPIFC_cjs = require('./chunk-ZW6FPIFC.cjs');
11
+ var chunkIPSHAMO2_cjs = require('./chunk-IPSHAMO2.cjs');
12
12
  var chunkPLIF5CUV_cjs = require('./chunk-PLIF5CUV.cjs');
13
13
  var ui = require('@arbidocs/react/ui');
14
14
  var jsxRuntime = require('react/jsx-runtime');
@@ -253,7 +253,7 @@ var DEFAULT_WORKFLOW_LIST_LABELS = {
253
253
  colAction: "What it does",
254
254
  colAgent: "Agent",
255
255
  colStatus: "Active",
256
- colShared: "Shared",
256
+ colShared: "Sharing",
257
257
  colLastRun: "Last run",
258
258
  colNextRun: "Next run",
259
259
  shared: "Shared with the workspace",
@@ -432,13 +432,12 @@ function WorkflowsTable(props) {
432
432
  if (!p.onToggleShared || chunk3YEXZX4H_cjs.isSkillRow(w)) return null;
433
433
  const isCreator = w.is_owner !== false;
434
434
  return /* @__PURE__ */ jsxRuntime.jsx(
435
- ui.VisibilityToggle,
435
+ ui.SharingEntity,
436
436
  {
437
- items: { shared: w.shared ?? false },
437
+ shared: w.shared ?? false,
438
438
  onToggle: (share) => latest.current.props.onToggleShared?.(w, share),
439
439
  disabled: !isCreator || p.busyId === w.external_id,
440
440
  disabledTooltip: lb.sharedOwnerOnly,
441
- showLabel: false,
442
441
  "data-testid": `workflow-shared-${w.external_id}`
443
442
  }
444
443
  );
@@ -805,5 +804,5 @@ exports.WORKFLOW_COLUMN_ORDER = WORKFLOW_COLUMN_ORDER;
805
804
  exports.WorkflowCatalogueSections = WorkflowCatalogueSections;
806
805
  exports.WorkflowsTable = WorkflowsTable;
807
806
  exports.createWorkflowColumns = createWorkflowColumns;
808
- //# sourceMappingURL=chunk-XZYIS2JZ.cjs.map
809
- //# sourceMappingURL=chunk-XZYIS2JZ.cjs.map
807
+ //# sourceMappingURL=chunk-QQWHG3KF.cjs.map
808
+ //# sourceMappingURL=chunk-QQWHG3KF.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/tasks/PersonPill.tsx","../src/agents/tasks/TaskTriggerChip.tsx","../src/agents/tasks/workflowColumns.tsx","../src/agents/tasks/WorkflowCatalogueSections.tsx","../src/agents/tasks/workflowTableLabels.ts","../src/agents/tasks/WorkflowsTable.tsx"],"names":["jsx","UserEntity","initials","getEntityColorStyles","Link2","MousePointerClick","Clock","Bell","jsxs","testIdOr","workflowStatus","Fragment","Card","Workflow","Badge","Button","PencilRuler","Play","useState","useRef","isSkillRow","isScheduledWorkflow","useMemo","workflowStepCount","cn","workflowTriggerType","Switch","hasTrigger","SharingEntity","listedRunStatus","workflowRunStatusLabel","badgeVariants","DropdownMenu","DropdownMenuTrigger","MoreHorizontal","DropdownMenuContent","DropdownMenuItem","History","Pencil","DropdownMenuSeparator","Trash2","useEffect","EmptyState","GridView","FilterInput","TriStateFilterMenu","TabHeader","SelectionBar","SelectionBarAction"],"mappings":"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","file":"chunk-QQWHG3KF.cjs","sourcesContent":["import { UserEntity, getEntityColorStyles } from '@arbidocs/react/ui'\nimport { initials } from '../../lib/format'\n\n/** A person (or agent) as the table shows them. */\nexport interface TaskPerson {\n /** The user's external id; absent when the person is not known (the pill then reads as their label). */\n id?: string | null\n label: string\n /** The viewer: shown as \"You\" by the host's pill, and as the label here. */\n isMe?: boolean\n isAgent?: boolean\n}\n\nexport interface PersonPillProps {\n person: TaskPerson\n testId?: string\n}\n\n/**\n * A person as the app's own user pill: the same avatar, initials and tint as everywhere else. This is\n * the default; a host that knows more (presence, a direct-message click, the rich tooltip) hands the\n * table its own `renderPerson` instead.\n */\nexport function PersonPill({ person, testId }: PersonPillProps) {\n return (\n <span data-testid={testId} className=\"inline-flex min-w-0\">\n <UserEntity\n variant=\"pill\"\n size=\"sm\"\n interactive={false}\n showTooltip={false}\n initials={initials(person.label)}\n displayName={person.label}\n avatarStyle={person.id ? getEntityColorStyles(person.id) : undefined}\n isAgent={person.isAgent}\n isMe={person.isMe}\n />\n </span>\n )\n}\n","import { Bell, Clock, Link2, MousePointerClick } from 'lucide-react'\nimport { testIdOr } from '../../chat/test-id'\nimport type { WorkflowTriggerType } from './taskLabels'\n\nexport interface TaskTriggerChipLabels {\n /** \"After “X” is done\" — the workflow waits on a task. */\n after: (taskName: string) => string\n /** The workflow waits on a task that is not visible to the viewer. */\n afterUnknown: string\n /** No trigger: it runs when someone presses Run. */\n onDemand: string\n}\n\nexport const DEFAULT_TASK_TRIGGER_CHIP_LABELS: TaskTriggerChipLabels = {\n after: (name) => `After “${name}” is done`,\n afterUnknown: 'After another task is done',\n onDemand: 'Manual',\n}\n\nexport interface TaskTriggerChipProps {\n /** Workflow id: keys the test id (`task-trigger-{id}`). */\n id: string\n triggerType: WorkflowTriggerType\n /** Host-formatted summary for time and event triggers (\"Every weekday at 9:00\", \"On document uploaded\"). */\n summary: string\n /** Set when this workflow waits on a task; the name it shows (or undefined if not visible). */\n afterTaskId?: string | null\n afterTaskName?: string\n testId?: string\n labels?: Partial<TaskTriggerChipLabels>\n}\n\nfunction TriggerIcon({ type, chained }: { type: WorkflowTriggerType; chained: boolean }) {\n const cls = 'size-3.5 shrink-0 text-muted-foreground'\n if (chained) return <Link2 className={cls} aria-hidden />\n switch (type) {\n case 'on_demand':\n return <MousePointerClick className={cls} aria-hidden />\n case 'recurring':\n return <Clock className={cls} aria-hidden />\n case 'event':\n return <Bell className={cls} aria-hidden />\n default:\n return <Clock className={cls} aria-hidden />\n }\n}\n\n/** What starts a workflow, in one line: a time, a repeat, an event, or a task finishing. */\nexport function TaskTriggerChip({\n id,\n triggerType,\n summary,\n afterTaskId,\n afterTaskName,\n testId,\n labels,\n}: TaskTriggerChipProps) {\n const l = { ...DEFAULT_TASK_TRIGGER_CHIP_LABELS, ...labels }\n const chained = Boolean(afterTaskId)\n const text = chained\n ? afterTaskName\n ? l.after(afterTaskName)\n : l.afterUnknown\n : triggerType === 'on_demand'\n ? l.onDemand\n : summary\n return (\n <span\n className=\"inline-flex min-w-0 max-w-full items-center gap-1.5 text-sm font-normal text-foreground\"\n data-testid={testIdOr(testId, { testId: `task-trigger-${id}` })}\n data-trigger={chained ? 'task' : triggerType}\n >\n <TriggerIcon type={triggerType} chained={chained} />\n <span className=\"truncate\">{text}</span>\n </span>\n )\n}\n","/**\n * Column definitions for the Workflows grid. Every column has an explicit `colId` (AG Grid persists\n * column state by id: see CLAUDE.md). The table supplies the cells that need behaviour through slots,\n * which must be stable functions so the definitions are built once.\n */\nimport type { ReactNode } from 'react'\nimport type { ColDef, ICellRendererParams } from 'ag-grid-community'\nimport { workflowStatus, type WorkflowListRow } from './workflowRows'\nimport type { WorkflowListLabels } from './workflowTableLabels'\n\nexport const WORKFLOW_COLUMN_ORDER = [\n 'name',\n 'trigger',\n 'last_run',\n 'result',\n 'status',\n 'shared',\n 'actions',\n] as const\n\nexport interface CreateWorkflowColumnsOptions<T extends WorkflowListRow> {\n labels: WorkflowListLabels\n renderName: (workflow: T) => ReactNode\n renderTrigger: (workflow: T) => ReactNode\n renderStatus: (workflow: T) => ReactNode\n renderShared: (workflow: T) => ReactNode\n renderLastRun: (workflow: T) => ReactNode\n renderResult: (workflow: T) => ReactNode\n renderActions: (workflow: T) => ReactNode\n /** Plain text of the trigger cell, for sorting and the clipboard. */\n getTriggerText: (workflow: T) => string\n}\n\nfunction cell<T extends WorkflowListRow>(render: (workflow: T) => ReactNode) {\n return (params: ICellRendererParams<T>): ReactNode => (params.data ? render(params.data) : null)\n}\n\nconst time = (iso?: string | null): number => (iso ? Date.parse(iso) : Number.POSITIVE_INFINITY)\n\nexport function createWorkflowColumns<T extends WorkflowListRow>(\n o: CreateWorkflowColumnsOptions<T>\n): ColDef<T>[] {\n const l = o.labels\n const base: Partial<ColDef<T>> = {\n sortable: false,\n cellStyle: { display: 'flex', alignItems: 'center' },\n }\n return [\n {\n ...base,\n colId: 'name',\n sortable: true,\n headerName: l.colName,\n flex: 3,\n minWidth: 210,\n valueGetter: (p) => p.data?.name.toLowerCase() ?? '',\n cellRenderer: cell(o.renderName),\n },\n {\n ...base,\n colId: 'trigger',\n headerName: l.colTrigger,\n flex: 2.4,\n minWidth: 185,\n valueGetter: (p) => (p.data ? o.getTriggerText(p.data) : ''),\n cellRenderer: cell(o.renderTrigger),\n },\n {\n ...base,\n colId: 'last_run',\n sortable: true,\n headerName: l.colLastRun,\n flex: 1.4,\n minWidth: 140,\n cellStyle: { display: 'flex', alignItems: 'center', padding: 0 },\n valueGetter: (p) => time(p.data?.last_fired_at),\n cellRenderer: cell(o.renderLastRun),\n },\n {\n ...base,\n colId: 'result',\n headerName: 'Result',\n width: 130,\n valueGetter: (p) => p.data?.last_run_status ?? '',\n cellStyle: { display: 'flex', alignItems: 'center', padding: 0 },\n cellRenderer: cell(o.renderResult),\n },\n {\n ...base,\n colId: 'status',\n sortable: true,\n headerName: l.colStatus,\n width: 120,\n valueGetter: (p) => (p.data ? workflowStatus(p.data) : ''),\n cellRenderer: cell(o.renderStatus),\n },\n {\n ...base,\n colId: 'shared',\n headerName: l.colShared,\n headerTooltip: l.shared,\n width: 96,\n cellStyle: { display: 'flex', alignItems: 'center', justifyContent: 'center' },\n valueGetter: (p) => p.data?.shared ?? false,\n cellRenderer: cell(o.renderShared),\n },\n {\n ...base,\n colId: 'actions',\n headerName: '',\n width: 180,\n minWidth: 180,\n cellStyle: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end' },\n cellRenderer: cell(o.renderActions),\n },\n ]\n}\n","import type { ReactNode } from 'react'\nimport { PencilRuler, Play, Workflow } from 'lucide-react'\nimport { Badge, Button, Card } from '@arbidocs/react/ui'\n\n/** A host catalogue entry as the category view lists it: a workflow to start from or to run. */\nexport interface WorkflowCatalogueRow {\n id: string\n title: string\n description?: string\n /** A key into the host's icon renderer. */\n icon?: string\n /** The section it is listed in: the host's word (a practice, a team, a kind of work). */\n category: string\n /** How many named steps it has. */\n stepCount: number\n /** A short note beside it (e.g. what starts it). */\n note?: string\n /** It can be started from here (a Run button). */\n runnable?: boolean\n}\n\nexport interface WorkflowCatalogueLabels {\n /** Opens the entry in the builder, as a template. */\n openInBuilder: string\n run: string\n /** How many steps an entry has. */\n stepCount: (count: number) => string\n}\n\nexport interface WorkflowCatalogueSectionsProps {\n rows: WorkflowCatalogueRow[]\n /** The order of the sections (and the ones shown). Absent: in order of first appearance. */\n categories?: string[]\n labels: WorkflowCatalogueLabels\n renderIcon?: (icon: string) => ReactNode\n onOpen?: (id: string) => void\n onRun?: (id: string) => void\n}\n\nconst slug = (text: string) => text.toLowerCase().replace(/[^a-z0-9]+/g, '-')\n\n/** The categories in order: the host's, or as the entries first name them. */\nfunction catalogueCategories(rows: WorkflowCatalogueRow[], categories?: string[]) {\n return categories ?? [...new Set(rows.map((row) => row.category))]\n}\n\n/** A host's catalogue in sections, one per category, each entry a card with Builder and Run. */\nexport function WorkflowCatalogueSections({\n rows,\n categories,\n labels,\n renderIcon,\n onOpen,\n onRun,\n}: WorkflowCatalogueSectionsProps) {\n return (\n <>\n {catalogueCategories(rows, categories).map((category) => {\n const entries = rows.filter((row) => row.category === category)\n if (!entries.length) return null\n return (\n <section\n key={category}\n className=\"space-y-3\"\n data-testid={`workflow-category-${slug(category)}`}\n >\n <h2 className=\"text-sm font-semibold text-foreground\">{category}</h2>\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {entries.map((row) => (\n <Card\n key={row.id}\n className=\"flex flex-col gap-3 p-4\"\n data-testid={`workflow-template-${row.id}`}\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary [&_svg]:size-4\">\n {(row.icon && renderIcon?.(row.icon)) || <Workflow aria-hidden />}\n </span>\n <div className=\"min-w-0\">\n <p className=\"font-medium text-foreground\">{row.title}</p>\n {row.description && (\n <p className=\"text-xs text-muted-foreground\">{row.description}</p>\n )}\n </div>\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\">{labels.stepCount(row.stepCount)}</Badge>\n {row.note && <Badge variant=\"secondary\">{row.note}</Badge>}\n <span className=\"flex-1\" />\n {onOpen && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onOpen(row.id)}\n data-testid={`workflow-template-open-${row.id}`}\n >\n <PencilRuler className=\"size-4\" aria-hidden /> {labels.openInBuilder}\n </Button>\n )}\n {onRun && row.runnable !== false && (\n <Button\n variant=\"accent\"\n size=\"sm\"\n onClick={() => onRun(row.id)}\n data-testid={`workflow-template-run-${row.id}`}\n >\n <Play className=\"size-4\" aria-hidden /> {labels.run}\n </Button>\n )}\n </div>\n </Card>\n ))}\n </div>\n </section>\n )\n })}\n </>\n )\n}\n","import type { WorkflowCatalogueLabels } from './WorkflowCatalogueSections'\n\nexport interface WorkflowListLabels {\n colName: string\n colType: string\n colTrigger: string\n colAction: string\n colAgent: string\n colStatus: string\n colShared: string\n colLastRun: string\n colNextRun: string\n shared: string\n sharedOwnerOnly: string\n deleteOwnerOnly: string\n typeSkill: string\n typeWorkflow: string\n statusActive: string\n statusPaused: string\n statusOnDemand: string\n activeSwitch: (name: string) => string\n agent: string\n myAssistant: string\n me: string\n someone: string\n never: string\n none: string\n reminder: string\n /** What a stepped workflow does: its number of steps. */\n steps: (count: number) => string\n failed: string\n lastRunFailed: string\n openLastRun: string\n editWorkflow: string\n run: string\n runLater: string\n runNow: string\n runs: string\n delete: string\n moreActions: string\n searchLabel: string\n searchPlaceholder: string\n emptyTitle: string\n emptyMessage: string\n firstRun: string\n noMatches: string\n filterAll: string\n filterSkills: string\n filterWorkflows: string\n filterActive: string\n filterPaused: string\n /** Saved workflows that start on a date or a repeat. */\n filterScheduled: string\n /** The category view's heading over the saved workflows. */\n savedWorkflows: string\n makeActive: string\n makePaused: string\n selectAll: string\n selectWorkflow: (name: string) => string\n}\n\nexport const DEFAULT_WORKFLOW_LIST_LABELS: WorkflowListLabels = {\n colName: 'Name',\n colType: 'Type',\n colTrigger: 'When',\n colAction: 'What it does',\n colAgent: 'Agent',\n colStatus: 'Active',\n colShared: 'Sharing',\n colLastRun: 'Last run',\n colNextRun: 'Next run',\n shared: 'Shared with the workspace',\n sharedOwnerOnly: 'Only the creator can change who can see this',\n deleteOwnerOnly: 'Only the creator or workspace owner can delete this',\n typeSkill: 'Skill',\n typeWorkflow: 'Workflow',\n statusActive: 'Active',\n statusPaused: 'Inactive',\n statusOnDemand: 'On demand',\n activeSwitch: (name) => `Active: “${name}”`,\n agent: 'Agent',\n myAssistant: 'My assistant',\n me: 'Me',\n someone: 'Someone',\n never: 'Never',\n none: '—',\n reminder: 'Reminder',\n steps: (count) => `Workflow · ${count} ${count === 1 ? 'step' : 'steps'}`,\n failed: 'Failed',\n lastRunFailed: 'Last run failed',\n openLastRun: 'Open the task this run created',\n editWorkflow: 'Edit',\n run: 'Run',\n runLater: 'Run later',\n runNow: 'Run now',\n runs: 'Runs',\n delete: 'Delete',\n moreActions: 'More actions',\n searchLabel: 'Search workflows',\n searchPlaceholder: 'Search workflows and skills…',\n emptyTitle: 'Nothing to run yet',\n emptyMessage: 'Skills and workflows you can run will appear here.',\n firstRun: 'Run a skill now, or choose Run later to save it as a workflow that runs on its own.',\n noMatches: 'Nothing in this view.',\n filterAll: 'All',\n filterSkills: 'Skills',\n filterWorkflows: 'Workflows',\n filterActive: 'Active',\n filterPaused: 'Inactive',\n filterScheduled: 'Scheduled',\n savedWorkflows: 'Saved workflows',\n makeActive: 'Make active',\n makePaused: 'Deactivate',\n selectAll: 'Select all',\n selectWorkflow: (name) => `Select ${name}`,\n}\n\n/** The category view's catalogue cards. */\nexport const DEFAULT_WORKFLOW_CATALOGUE_LABELS: WorkflowCatalogueLabels = {\n openInBuilder: 'Builder',\n run: 'Run',\n stepCount: (count) => `${count} ${count === 1 ? 'step' : 'steps'}`,\n}\n","import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport type { GridApi } from 'ag-grid-community'\nimport { History, MoreHorizontal, Pencil, Play, Trash2, Workflow } from 'lucide-react'\nimport {\n Button,\n badgeVariants,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Switch,\n cn,\n SharingEntity,\n} from '@arbidocs/react/ui'\nimport { TabHeader, type Tab } from '../../shell/TabHeader'\nimport { FilterInput } from '../../shell/FilterInput'\nimport { TriStateFilterMenu, type TriState } from '../../settings/TriStateFilterMenu'\nimport { EmptyState } from '../../components/EmptyState'\nimport { GridView } from '../../files/GridView'\nimport { SelectionBar, SelectionBarAction } from '../../files/SelectionBar'\nimport { testIdOr } from '../../chat/test-id'\nimport { PersonPill, type TaskPerson } from './PersonPill'\nimport { TaskTriggerChip, type TaskTriggerChipLabels } from './TaskTriggerChip'\nimport { createWorkflowColumns } from './workflowColumns'\nimport {\n WorkflowCatalogueSections,\n type WorkflowCatalogueLabels,\n type WorkflowCatalogueRow,\n} from './WorkflowCatalogueSections'\nimport {\n hasTrigger,\n isScheduledWorkflow,\n isSkillRow,\n workflowStatus,\n listedRunStatus,\n workflowRunStatusLabel,\n workflowStepCount,\n workflowTriggerType,\n type WorkflowListRow,\n} from './workflowRows'\nimport {\n DEFAULT_WORKFLOW_CATALOGUE_LABELS,\n DEFAULT_WORKFLOW_LIST_LABELS,\n type WorkflowListLabels,\n} from './workflowTableLabels'\n\nexport interface WorkflowsTableProps<T extends WorkflowListRow> {\n /** Everything the viewer can run: saved workflows, and skills as virtual rows (`kind: 'skill'`). */\n workflows: T[]\n isLoading?: boolean\n /** This page contains saved automations only; skills stay in their own library. */\n automationsOnly?: boolean\n /** Human summary of when a workflow starts (\"Every weekday at 9:00\", \"On document uploaded\"). */\n describeTrigger: (workflow: T) => string\n /** Friendly display of an ISO timestamp. */\n formatDate: (iso: string) => string\n /** Pause or resume a workflow that starts by itself (an on-demand one has no switch). */\n onToggleActive: (workflow: T, active: boolean) => void\n /** Share a workflow with the workspace or make it private (the toggle appears with it). */\n onToggleShared?: (workflow: T, shouldShare: boolean) => void\n /** Open the workflow in the composer (workflow-edit mode). */\n onEdit: (workflow: T) => void\n /**\n * Run it. A saved workflow just runs (its inputs are stored); a skill needs its inputs, so the host\n * opens the run dialog with that skill selected.\n */\n onRun: (row: T) => void\n /** Run a skill later: the run dialog, on its schedule view. Offered on skill rows. */\n onRunLater?: (skill: T) => void\n onDelete: (workflow: T) => void\n /** Show the tasks this workflow created. */\n onViewRuns?: (workflow: T) => void\n /** Click on a row (not on one of its controls): a skill's detail, a workflow's editor. */\n onOpenRow?: (row: T) => void\n /** Open the task a workflow's last run created. */\n onOpenThread?: (conversationId: string) => void\n /** Each workflow's latest run: the newest task it created, by workflow id. */\n /** Each automation's latest run thread, with that thread's status (it corrects a lagging result). */\n lastRuns?: Record<string, { conversationId: string; at: string; threadStatus?: string | null }>\n /** Bulk actions on the selected workflows (skills are skipped). */\n onDeleteMany?: (workflows: T[]) => void\n onToggleActiveMany?: (workflows: T[], active: boolean) => void\n /** Agent names by id, for the Agent column (falls back to \"Agent\"). */\n agentNames?: Record<string, string>\n /** Skill names by `skill_ref`, for the action cell (falls back to the reference). */\n skillNames?: Record<string, string>\n /** Task names by id, for an \"after <task>\" trigger. */\n taskNames?: Record<string, string>\n /** How an agent appears in the Agent column; defaults to the app's user pill. */\n renderPerson?: (person: TaskPerson) => ReactNode\n /**\n * The viewer edits the workspace (owner or collaborator): they may then edit, pause, resume and run a\n * workflow shared with it. A guest leaves this off.\n */\n canEditShared?: boolean\n /** The viewer owns the workspace: with the creator, the only one who deletes a workflow. Who can see it stays the creator's alone. */\n isWorkspaceOwner?: boolean\n /** Workflow id with a request in flight: its row disables its controls. */\n busyId?: string | null\n tabs?: Tab[]\n activeTabId?: string\n onTabChange?: (id: string) => void\n content?: ReactNode\n /** Actions at the end of the tab row; a function is told when the row is narrow (a phone). */\n header?: ReactNode | ((isCompact: boolean) => ReactNode)\n gridId?: string\n labels?: Partial<WorkflowListLabels>\n triggerChipLabels?: Partial<TaskTriggerChipLabels>\n testId?: string\n /**\n * The host's catalogue: when it has entries, they are listed in sections by category (each\n * entry opens in the builder or runs), then the saved workflows. Absent or empty: the saved\n * workflows alone.\n */\n catalogue?: WorkflowCatalogueRow[]\n /** The order of the categories (and the ones shown). Absent: as the entries first name them. */\n categories?: string[]\n /** Open a catalogue entry in the builder, as a template. */\n onOpenTemplate?: (id: string) => void\n /** Run a catalogue entry. Absent: no Run on the entries. */\n onRunTemplate?: (id: string) => void\n /** Draws a catalogue entry's icon name. */\n renderIcon?: (icon: string) => ReactNode\n catalogueLabels?: Partial<WorkflowCatalogueLabels>\n}\n\ntype Filter = 'all' | 'skills' | 'workflows' | 'active' | 'paused' | 'scheduled'\n/** Each group narrows on its own: kinds, then states, then how it starts. */\nconst FILTER_GROUPS: Filter[][] = [['skills', 'workflows'], ['active', 'paused'], ['scheduled']]\nconst NO_CATALOGUE: WorkflowCatalogueRow[] = []\nconst ROW_HEIGHT = 40\n\n/**\n * Everything the viewer can run, in one list: skills (they run on demand) and saved workflows (they start\n * on a date, a repeat, an event or after a task, or on demand). Each row has its type, when it starts, what\n * it does, who runs it, its status (Active or Paused with a switch; On demand has none), when it last ran\n * (linking to the task that run created) and runs next, and a visible Run. The same grid as every other\n * list in the app.\n */\nexport function WorkflowsTable<T extends WorkflowListRow>(props: WorkflowsTableProps<T>) {\n const { workflows, isLoading, busyId, header, testId } = props\n const l: WorkflowListLabels = { ...DEFAULT_WORKFLOW_LIST_LABELS, ...props.labels }\n const [filterStates, setFilterStates] = useState<Map<Filter, TriState>>(new Map())\n const [filterOpen, setFilterOpen] = useState(false)\n const [query, setQuery] = useState('')\n const [selected, setSelected] = useState<T[]>([])\n const apiRef = useRef<GridApi<T> | null>(null)\n\n const q = query.trim().toLowerCase()\n const matches = (w: T) =>\n !q ||\n `${w.name} ${w.description ?? ''} ${w.prompt ?? ''} ${w.skill_ref ?? ''}`\n .toLowerCase()\n .includes(q)\n const inFilter = (w: T, f: Filter) =>\n f === 'all' ||\n (f === 'skills' && isSkillRow(w)) ||\n (f === 'workflows' && !isSkillRow(w)) ||\n (f === 'active' && workflowStatus(w) === 'active') ||\n (f === 'paused' && workflowStatus(w) === 'paused') ||\n (f === 'scheduled' && !isSkillRow(w) && isScheduledWorkflow(w))\n const counts = useMemo(() => {\n const out = { all: 0, skills: 0, workflows: 0, active: 0, paused: 0, scheduled: 0 }\n for (const w of workflows) {\n if (!matches(w)) continue\n for (const f of Object.keys(out) as Filter[]) if (inFilter(w, f)) out[f] += 1\n }\n return out\n // matches/inFilter read only `q` and the row.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [workflows, q])\n const shown = useMemo(\n () =>\n workflows.filter((w) => {\n if (!matches(w)) return false\n for (const group of FILTER_GROUPS) {\n const included = group.filter((f) => filterStates.get(f) === 'include')\n if (included.length && !included.some((f) => inFilter(w, f))) return false\n if (group.some((f) => filterStates.get(f) === 'exclude' && inFilter(w, f))) return false\n }\n return true\n }),\n [workflows, filterStates, q]\n )\n\n const renderPerson = props.renderPerson ?? ((p: TaskPerson) => <PersonPill person={p} />)\n const actionText = (w: T): string => {\n if (w.action)\n return w.action.type === 'email_documents' ? 'Email latest documents' : 'AI Review'\n if (isSkillRow(w))\n return w.description || (w.skill_ref && props.skillNames?.[w.skill_ref]) || w.name\n if (w.skill_ref) return props.skillNames?.[w.skill_ref] ?? w.skill_ref\n // A stepped workflow runs its plan: it is no reminder, even with no prompt of its own.\n const steps = workflowStepCount(w)\n if (steps > 0) return w.prompt?.trim() || l.steps(steps)\n return w.prompt?.trim() || w.plan?.goal?.trim() || l.reminder\n }\n\n // The cells read the latest handlers and lookups through this ref, so the column definitions are\n // built once and an interaction never makes the grid rebuild them.\n const latest = useRef({ props, l, renderPerson, actionText })\n latest.current = { props, l, renderPerson, actionText }\n\n const columnDefs = useMemo(\n () =>\n createWorkflowColumns<T>({\n labels: l,\n getTriggerText: (w) =>\n isSkillRow(w) ? l.statusOnDemand : latest.current.props.describeTrigger(w),\n renderName: (w) => {\n const { l: lb } = latest.current\n const skill = isSkillRow(w)\n const text = skill\n ? latest.current.actionText(w)\n : w.description || latest.current.actionText(w)\n // A name made from the instruction is not said again under itself.\n const stem = w.name.replace(/…$/, '').trim()\n const about = text && text.trim() !== w.name.trim() && text.trim() !== stem ? text : ''\n return (\n <div className=\"min-w-0 leading-tight\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <span\n className={cn(\n 'truncate text-sm font-normal',\n workflowStatus(w) === 'paused' && 'text-muted-foreground'\n )}\n data-testid={`workflow-name-${w.external_id}`}\n >\n {w.name}\n </span>\n {skill && (\n <span\n className=\"shrink-0 rounded-full border border-border px-1.5 text-[11px] text-muted-foreground\"\n data-testid={`workflow-type-${w.external_id}`}\n >\n {lb.typeSkill}\n </span>\n )}\n </span>\n {about && !latest.current.props.automationsOnly && (\n <p\n className=\"truncate text-xs text-muted-foreground\"\n data-testid={`workflow-action-${w.external_id}`}\n >\n {about}\n </p>\n )}\n </div>\n )\n },\n renderTrigger: (w) => {\n const { props: p } = latest.current\n const after = w.after_conversation_ext_id\n const skill = isSkillRow(w)\n return (\n <div className=\"min-w-0 leading-tight\">\n <TaskTriggerChip\n id={w.external_id}\n testId={`workflow-trigger-${w.external_id}`}\n triggerType={workflowTriggerType(w)}\n summary={skill ? '' : p.describeTrigger(w)}\n afterTaskId={after}\n afterTaskName={after ? p.taskNames?.[after] : undefined}\n labels={p.triggerChipLabels}\n />\n </div>\n )\n },\n renderStatus: (w) => {\n const { props: p, l: lb } = latest.current\n const canEdit = w.is_owner !== false || Boolean(p.canEditShared)\n return (\n <Switch\n checked={hasTrigger(w) && !isSkillRow(w) && Boolean(w.active)}\n disabled={!hasTrigger(w) || isSkillRow(w) || !canEdit || p.busyId === w.external_id}\n onCheckedChange={(next) => p.onToggleActive(w, next)}\n className=\"disabled:data-[state=unchecked]:bg-muted dark:disabled:data-[state=unchecked]:bg-muted\"\n aria-label={lb.activeSwitch(w.name)}\n data-testid={`workflow-active-${w.external_id}`}\n />\n )\n },\n renderShared: (w) => {\n const { props: p, l: lb } = latest.current\n if (!p.onToggleShared || isSkillRow(w)) return null\n // Who can see it is the creator's alone: unsharing hides it from everyone else.\n const isCreator = w.is_owner !== false\n return (\n <SharingEntity\n shared={w.shared ?? false}\n onToggle={(share) => latest.current.props.onToggleShared?.(w, share)}\n disabled={!isCreator || p.busyId === w.external_id}\n disabledTooltip={lb.sharedOwnerOnly}\n data-testid={`workflow-shared-${w.external_id}`}\n />\n )\n },\n renderLastRun: (w) => {\n const { props: p, l: lb } = latest.current\n const at = w.last_fired_at ?? p.lastRuns?.[w.external_id]?.at\n return (\n <button\n type=\"button\"\n className=\"h-full w-full truncate px-4 text-left text-sm hover:text-primary\"\n onClick={() => p.onViewRuns?.(w)}\n title=\"View run history\"\n data-testid={`workflow-last-run-${w.external_id}`}\n >\n {at ? p.formatDate(at) : lb.never}\n </button>\n )\n },\n renderResult: (w) => {\n const status = listedRunStatus(\n w.last_run_status,\n latest.current.props.lastRuns?.[w.external_id]?.threadStatus\n )\n const label = workflowRunStatusLabel(status)\n return (\n <button\n type=\"button\"\n onClick={() => latest.current.props.onViewRuns?.(w)}\n title=\"View run history\"\n className={cn(\n badgeVariants({ variant: 'outline' }),\n 'mx-4 cursor-pointer text-xs font-medium',\n status === 'failed'\n ? 'border-red-border bg-red-bg text-red-text hover:bg-red-bg/80'\n : status === 'completed' || status === 'success'\n ? 'border-green-border bg-green-bg text-green-text hover:bg-green-bg/80'\n : status\n ? 'border-primary/20 bg-primary/10 text-primary hover:bg-primary/15'\n : 'border-border bg-muted text-muted-foreground hover:bg-muted/80'\n )}\n data-testid={`workflow-result-${w.external_id}`}\n >\n {label}\n </button>\n )\n },\n renderActions: (w) => {\n const { props: p, l: lb } = latest.current\n const id = w.external_id\n const skill = isSkillRow(w)\n const busy = p.busyId === id\n const isOwner = w.is_owner !== false\n const canEdit = isOwner || Boolean(p.canEditShared)\n const canAdmin = isOwner || Boolean(p.isWorkspaceOwner)\n return (\n <div className=\"flex items-center justify-end gap-1\">\n {(skill || canEdit) && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-8 gap-1.5 pointer-coarse:h-11\"\n disabled={busy}\n onClick={() => p.onRun(w)}\n data-testid={`workflow-run-btn-${id}`}\n >\n <Play className=\"size-3.5\" aria-hidden />\n {lb.run}\n </Button>\n )}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 pointer-coarse:size-11\"\n aria-label={lb.moreActions}\n data-testid={`workflow-menu-${id}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {skill && p.onRunLater && (\n <DropdownMenuItem\n onSelect={() => p.onRunLater?.(w)}\n data-testid={`workflow-run-later-${id}`}\n >\n <History className=\"mr-2 size-4\" aria-hidden />\n {lb.runLater}\n </DropdownMenuItem>\n )}\n {!skill && canEdit && (\n <DropdownMenuItem\n onSelect={() => p.onEdit(w)}\n data-testid={`workflow-edit-${id}`}\n >\n <Pencil className=\"mr-2 size-4\" aria-hidden />\n {lb.editWorkflow}\n </DropdownMenuItem>\n )}\n {!skill && p.onViewRuns && (\n <DropdownMenuItem\n onSelect={() => p.onViewRuns?.(w)}\n data-testid={`workflow-runs-${id}`}\n >\n <History className=\"mr-2 size-4\" aria-hidden />\n {lb.runs}\n </DropdownMenuItem>\n )}\n {!skill && canEdit && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n disabled={busy || !canAdmin}\n onSelect={() => p.onDelete(w)}\n title={canAdmin ? undefined : lb.deleteOwnerOnly}\n className=\"text-destructive focus:text-destructive\"\n data-testid={`workflow-delete-${id}`}\n >\n <Trash2 className=\"mr-2 size-4\" aria-hidden />\n {lb.delete}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n },\n }),\n // Stable on purpose: only the header texts rebuild the columns (see `latest`).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [\n l.colName,\n l.colType,\n l.colTrigger,\n l.colAction,\n l.colAgent,\n l.colStatus,\n l.colShared,\n l.shared,\n l.colLastRun,\n l.colNextRun,\n ]\n )\n\n // A change in the lookups the cells draw from redraws them once.\n useEffect(() => {\n apiRef.current?.refreshCells({ force: true })\n }, [\n busyId,\n props.agentNames,\n props.skillNames,\n props.taskNames,\n props.lastRuns,\n props.renderPerson,\n props.labels,\n props.canEditShared,\n props.isWorkspaceOwner,\n ])\n\n const mine = selected.filter((w) => !isSkillRow(w))\n const adminSelected = mine.filter((w) => w.is_owner !== false || props.isWorkspaceOwner)\n const editableSelected = mine.filter(\n (w) => hasTrigger(w) && (w.is_owner !== false || props.canEditShared)\n )\n const clearSelection = () => setSelected([])\n\n // The category view: the catalogue (narrowed by the text filter; a state filter is about saved\n // workflows only, so it hides the catalogue), then the saved workflows.\n const byCategory = (props.catalogue?.length ?? 0) > 0\n const stateFiltered = [...filterStates.values()].some((state) => state === 'include')\n const catalogueLabels = { ...DEFAULT_WORKFLOW_CATALOGUE_LABELS, ...props.catalogueLabels }\n const catalogue = useMemo(\n () =>\n (props.catalogue ?? NO_CATALOGUE).filter(\n (row) => !q || `${row.title} ${row.description ?? ''}`.toLowerCase().includes(q)\n ),\n [props.catalogue, q]\n )\n const list =\n workflows.length === 0 && !isLoading ? (\n <EmptyState\n icon={Workflow}\n title={l.emptyTitle}\n description={l.emptyMessage}\n testId=\"workflow-list-empty\"\n />\n ) : (\n <div className=\"min-h-0 flex-1\">\n <GridView<T>\n testId=\"workflow-grid\"\n gridId={props.gridId ?? 'workflows-grid-v2'}\n items={shown}\n columnDefs={columnDefs}\n getItemId={(w) => w.external_id}\n getRowTestId={(w) => `workflow-row-${w.external_id}`}\n isLoading={isLoading}\n showEmpty={shown.length === 0}\n emptyState={{ title: l.noMatches }}\n rowHeight={ROW_HEIGHT}\n height=\"100%\"\n onGridReady={({ api }) => {\n apiRef.current = api\n }}\n onRowClick={(row) => props.onOpenRow?.(row)}\n selection={{ mode: 'multiple', selected, onSelectionChange: setSelected }}\n gridOptions={GRID_OPTIONS as never}\n />\n </div>\n )\n\n // The search box with its state filter: in the tab row, or (narrow) across a row of its own.\n const filterBox = (className?: string) => (\n <FilterInput\n className={className}\n value={query}\n onChange={setQuery}\n fields={['text']}\n onFieldsChange={() => {}}\n hideFieldPicker\n ariaLabel={l.searchLabel}\n testIds={{ input: 'workflow-search' }}\n filter={\n <TriStateFilterMenu\n testId=\"automations-filter-button\"\n open={filterOpen}\n onOpenChange={setFilterOpen}\n filterStates={filterStates}\n onFiltersChange={setFilterStates}\n triggerClassName=\"flex h-5 items-center gap-0.5 rounded px-1 text-muted-foreground hover:text-foreground pointer-coarse:h-11! pointer-coarse:min-w-11 pointer-coarse:justify-center\"\n renderChevron\n disableHoverOpen\n options={[\n ...(!props.automationsOnly\n ? [\n { id: 'skills' as const, label: l.filterSkills, count: counts.skills },\n {\n id: 'workflows' as const,\n label: l.filterWorkflows,\n count: counts.workflows,\n },\n ]\n : []),\n { id: 'active', label: l.filterActive, count: counts.active },\n { id: 'paused', label: l.filterPaused, count: counts.paused },\n { id: 'scheduled', label: l.filterScheduled, count: counts.scheduled },\n ]}\n />\n }\n />\n )\n\n return (\n <div\n className=\"flex min-h-0 flex-1 flex-col\"\n data-testid={testIdOr(testId, { testId: 'workflow-list' })}\n >\n <TabHeader\n testId=\"automations-tab-header\"\n tabs={[\n {\n id: 'automations',\n label: '',\n count: shown.length,\n countIcon: <Workflow className=\"size-3.5\" />,\n countPlacement: 'leading',\n presentation: 'count',\n hasActiveFilters: !!q || [...filterStates.values()].some(Boolean),\n },\n ...(props.tabs ?? []),\n ]}\n activeTabId={props.activeTabId ?? 'automations'}\n onTabChange={props.onTabChange ?? (() => {})}\n // A narrow row (a phone) gives the filter a row of its own under the tabs, so the host's\n // tabs and the actions keep their room in the tab row.\n firstTabContent={(isCompact) => (isCompact ? null : filterBox())}\n belowContent={(isCompact) => (isCompact ? filterBox('w-full') : null)}\n rightContent={header}\n />\n {props.content ? (\n props.content\n ) : byCategory ? (\n <div\n className=\"min-h-0 flex-1 space-y-8 overflow-auto p-4\"\n data-testid=\"workflow-categories\"\n >\n {!stateFiltered && (\n <WorkflowCatalogueSections\n rows={catalogue}\n categories={props.categories}\n labels={catalogueLabels}\n renderIcon={props.renderIcon}\n onOpen={props.onOpenTemplate}\n onRun={props.onRunTemplate}\n />\n )}\n <section className=\"space-y-3\" data-testid=\"workflow-category-saved\">\n <h2 className=\"flex items-center gap-2 text-sm font-semibold text-foreground\">\n <Workflow className=\"size-4 text-primary\" aria-hidden /> {l.savedWorkflows}\n </h2>\n <div className=\"flex h-[min(32rem,70dvh)] min-h-64 flex-col\">{list}</div>\n </section>\n </div>\n ) : (\n list\n )}\n {!props.content && (\n <SelectionBar count={selected.length} onClear={clearSelection}>\n {(isCompact) => (\n <>\n {props.onToggleActiveMany && (\n <>\n <SelectionBarAction\n icon={Play}\n label={l.makeActive}\n onClick={() => {\n props.onToggleActiveMany?.(editableSelected, true)\n clearSelection()\n }}\n disabled={editableSelected.length === 0}\n isCompact={isCompact}\n testId=\"workflow-bulk-activate\"\n />\n <SelectionBarAction\n icon={Workflow}\n label={l.makePaused}\n onClick={() => {\n props.onToggleActiveMany?.(editableSelected, false)\n clearSelection()\n }}\n disabled={editableSelected.length === 0}\n isCompact={isCompact}\n testId=\"workflow-bulk-pause\"\n />\n </>\n )}\n {props.onDeleteMany && (\n <SelectionBarAction\n icon={Trash2}\n label={l.delete}\n onClick={() => {\n props.onDeleteMany?.(adminSelected)\n clearSelection()\n }}\n disabled={adminSelected.length === 0}\n isCompact={isCompact}\n className=\"border-red-border text-red-text hover:bg-red-bg hover:text-red-text\"\n testId=\"workflow-bulk-delete\"\n />\n )}\n </>\n )}\n </SelectionBar>\n )}\n </div>\n )\n}\n\nconst GRID_OPTIONS = { suppressClickEdit: true }\n"]}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkQOXK5O6M_cjs = require('./chunk-QOXK5O6M.cjs');
4
+ var chunk557MJ4IH_cjs = require('./chunk-557MJ4IH.cjs');
5
5
  var chunk7JLJH5CW_cjs = require('./chunk-7JLJH5CW.cjs');
6
6
  var chunk5F4DCIHS_cjs = require('./chunk-5F4DCIHS.cjs');
7
7
  var chunk75BNMCUX_cjs = require('./chunk-75BNMCUX.cjs');
@@ -34,8 +34,8 @@ function useSlashField({
34
34
  send
35
35
  }) {
36
36
  const { openSkillsBrowser } = chunkTKHCIHE2_cjs.useBlocksEnvironment();
37
- const { slashToken, filteredItems, isOpen, canBrowseSkills } = chunkQOXK5O6M_cjs.useSlashItems(text, chunk7JLJH5CW_cjs.BUILTIN_SKILLS);
38
- const slashWorkflows = chunkQOXK5O6M_cjs.useSlashSkillWorkflows({ landing, send });
37
+ const { slashToken, filteredItems, isOpen, canBrowseSkills } = chunk557MJ4IH_cjs.useSlashItems(text, chunk7JLJH5CW_cjs.BUILTIN_SKILLS);
38
+ const slashWorkflows = chunk557MJ4IH_cjs.useSlashSkillWorkflows({ landing, send });
39
39
  const insert = (item) => {
40
40
  if (slashWorkflows.openFor(item)) {
41
41
  setText("");
@@ -86,5 +86,5 @@ exports.DEFAULT_ASK_SUGGESTIONS = DEFAULT_ASK_SUGGESTIONS;
86
86
  exports.DEFAULT_DOCUMENT_VIEWER_LABELS = DEFAULT_DOCUMENT_VIEWER_LABELS;
87
87
  exports.DEFAULT_DOCUMENT_VIEWER_TABS = DEFAULT_DOCUMENT_VIEWER_TABS;
88
88
  exports.useSlashField = useSlashField;
89
- //# sourceMappingURL=chunk-KHAO7ZTO.cjs.map
90
- //# sourceMappingURL=chunk-KHAO7ZTO.cjs.map
89
+ //# sourceMappingURL=chunk-T23UTIRI.cjs.map
90
+ //# sourceMappingURL=chunk-T23UTIRI.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/connection/viewer/documentViewerTabs.ts","../src/connection/ask/useSlashField.tsx"],"names":["useBlocksEnvironment","useSlashItems","BUILTIN_SKILLS","useSlashSkillWorkflows","useListKeyNav","jsx","SlashMenu"],"mappings":";;;;;;;;;;AASO,IAAM,4BAAA,GAA6D;AAAA,EACxE,QAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF;AAaO,IAAM,8BAAA,GAAuE;AAAA,EAClF,MAAA,EAAQ,QAAA;AAAA,EACR,QAAA,EAAU,UAAA;AAAA,EACV,GAAA,EAAK,QAAA;AAAA,EACL,QAAA,EACE,4GAAA;AAAA,EACF,cAAA,EAAgB;AAClB;AAEO,IAAM,uBAAA,GAA6C;AAAA,EACxD,0BAAA;AAAA,EACA,0CAAA;AAAA,EACA;AACF;ACHO,SAAS,aAAA,CAAc;AAAA,EAC5B,IAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAqC;AACnC,EAAA,MAAM,EAAE,iBAAA,EAAkB,GAAIA,sCAAA,EAAqB;AACnD,EAAA,MAAM,EAAE,YAAY,aAAA,EAAe,MAAA,EAAQ,iBAAgB,GAAIC,+BAAA,CAAc,MAAMC,gCAAc,CAAA;AACjG,EAAA,MAAM,cAAA,GAAiBC,wCAAA,CAAuB,EAAE,OAAA,EAAS,MAAM,CAAA;AAE/D,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAAwB;AACtC,IAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,IAAI,CAAA,EAAG;AAChC,MAAA,OAAA,CAAQ,EAAE,CAAA;AACV,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,CAAA;AAC1B,IAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,IAAA,qBAAA,CAAsB,MAAM;AAC1B,MAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,KAAA,CAAM,KAAA,EAAM;AACZ,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,MAAM,CAAA;AAAA,IAClD,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,WAAWC,+BAAA,CAAc;AAAA,IAC7B,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,aAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW,MAAM,OAAA,CAAQ,EAAE,CAAA;AAAA,IAC3B,QAAA,EAAU;AAAA,GACX,CAAA;AAED,EAAA,OAAO;AAAA,IACL,UAAU,cAAA,CAAe,QAAA;AAAA,IACzB,WAAW,CAAC,KAAA,KAAsC,KAAK,QAAA,CAAS,cAAc,KAAK,CAAA;AAAA,IACnF,eAAe,QAAA,CAAS,aAAA;AAAA,IACxB,WAAA,EAAa,CAAC,QAAA,KAA8B;AAC1C,MAAA,IAAI,CAAC,cAAA,CAAe,YAAA,CAAa,QAAQ,GAAG,OAAO,KAAA;AACnD,MAAA,OAAA,CAAQ,EAAE,CAAA;AACV,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAA,kBACEC,cAAA;AAAA,MAACC,2BAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,MAAA;AAAA,QACN,SAAA,EAAU,OAAA;AAAA,QACV,KAAA,EAAO,aAAA;AAAA,QACP,eAAe,QAAA,CAAS,aAAA;AAAA,QACxB,kBAAkB,QAAA,CAAS,gBAAA;AAAA,QAC3B,WAAW,QAAA,CAAS,SAAA;AAAA,QACpB,QAAA,EAAU,MAAA;AAAA,QACV,QAAA,EAAU,eAAA,IAAmB,iBAAA,GAAoB,MAAM,mBAAkB,GAAI;AAAA;AAAA;AAC/E,GAEJ;AACF","file":"chunk-KHAO7ZTO.cjs","sourcesContent":["/** The views of the document dialog (`DocumentViewerDialog`, the files page's own viewer). */\n\n/**\n * - `review`: the parsed document with its AI findings, a cited passage marked;\n * - `document`: the whole viewer (PDF and text, markup, signing, versions, sharing);\n * - `ask`: a question about the document, which starts a thread about it.\n */\nexport type DocumentViewerTab = 'review' | 'document' | 'ask'\n\nexport const DEFAULT_DOCUMENT_VIEWER_TABS: readonly DocumentViewerTab[] = [\n 'review',\n 'document',\n 'ask',\n]\n\n/** The dialog's words; each has a neutral default. */\nexport interface DocumentViewerDialogLabels {\n review?: string\n document?: string\n ask?: string\n /** Above the ask bar. */\n askIntro?: string\n /** The ask bar's placeholder; `{title}` is the document's title. */\n askPlaceholder?: string\n}\n\nexport const DEFAULT_DOCUMENT_VIEWER_LABELS: Required<DocumentViewerDialogLabels> = {\n review: 'Review',\n document: 'Document',\n ask: 'Ask AI',\n askIntro:\n 'Your question starts a new thread about this document. The answer cites its passages and takes follow-ups.',\n askPlaceholder: 'Ask about {title}…',\n}\n\nexport const DEFAULT_ASK_SUGGESTIONS: readonly string[] = [\n 'Summarise the key points',\n 'List the dates and deadlines it mentions',\n 'Who are the people and organisations named, and what are their roles?',\n]\n","/**\n * The `/` menu of a one-line ask field (the workspace ask field, the document ask), the chat\n * composer's rules: `/` opens the menu over the same rows, arrows move, Enter/Tab insert the slug,\n * Escape clears the slash. A skill (picked, or typed as `/slug text` and submitted) opens its form\n * instead (`useSlashSkillWorkflows`).\n */\nimport type { KeyboardEvent, ReactNode, RefObject } from 'react'\nimport { SlashMenu, type SlashMenuItem } from '../../chat/SlashMenu'\nimport type { WorkflowLanding } from '../../agents/workflows/types'\nimport { useListKeyNav, type UseListKeyNav } from '../../lib/useListKeyNav'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useSlashItems } from '../conversation/useSlashItems'\nimport { BUILTIN_SKILLS } from '../../agents/workflows/catalogue/seed'\nimport { useSlashSkillWorkflows } from '../workflows/useSlashSkillWorkflows'\n\nexport interface UseSlashFieldOptions {\n text: string\n setText: (text: string) => void\n inputRef: RefObject<HTMLInputElement | null>\n /** Where a skill's run lands (`useSlashSkillWorkflows`). */\n landing: WorkflowLanding\n /** A skill run's own send, for a field that asks its own way (`useSlashSkillWorkflows`). */\n send?: (prompt: string) => void\n}\n\nexport interface SlashField {\n /** Render once beside the field (the skill's form). */\n launcher: ReactNode\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void\n comboboxProps: UseListKeyNav['comboboxProps']\n /** A submitted question: true when it named a skill (its form opened), so nothing is asked. */\n startsSkill: (question: string) => boolean\n /** The menu, for the field's `menu` slot (it opens below the field). */\n menu: ReactNode\n}\n\nexport function useSlashField({\n text,\n setText,\n inputRef,\n landing,\n send,\n}: UseSlashFieldOptions): SlashField {\n const { openSkillsBrowser } = useBlocksEnvironment()\n const { slashToken, filteredItems, isOpen, canBrowseSkills } = useSlashItems(text, BUILTIN_SKILLS)\n const slashWorkflows = useSlashSkillWorkflows({ landing, send })\n\n const insert = (item: SlashMenuItem) => {\n if (slashWorkflows.openFor(item)) {\n setText('')\n return\n }\n const next = `/${item.name} `\n setText(next)\n requestAnimationFrame(() => {\n const input = inputRef.current\n if (!input) return\n input.focus()\n input.setSelectionRange(next.length, next.length)\n })\n }\n\n const slashNav = useListKeyNav({\n open: isOpen,\n items: filteredItems,\n onPick: insert,\n onDismiss: () => setText(''),\n resetKey: slashToken,\n })\n\n return {\n launcher: slashWorkflows.launcher,\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void slashNav.handleKeyDown(event),\n comboboxProps: slashNav.comboboxProps,\n startsSkill: (question: string): boolean => {\n if (!slashWorkflows.openForDraft(question)) return false\n setText('')\n return true\n },\n menu: (\n <SlashMenu\n open={isOpen}\n placement=\"below\"\n items={filteredItems}\n selectedIndex={slashNav.selectedIndex}\n setSelectedIndex={slashNav.setSelectedIndex}\n listboxId={slashNav.listboxId}\n onSelect={insert}\n onBrowse={canBrowseSkills && openSkillsBrowser ? () => openSkillsBrowser() : undefined}\n />\n ),\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/connection/viewer/documentViewerTabs.ts","../src/connection/ask/useSlashField.tsx"],"names":["useBlocksEnvironment","useSlashItems","BUILTIN_SKILLS","useSlashSkillWorkflows","useListKeyNav","jsx","SlashMenu"],"mappings":";;;;;;;;;;AASO,IAAM,4BAAA,GAA6D;AAAA,EACxE,QAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF;AAaO,IAAM,8BAAA,GAAuE;AAAA,EAClF,MAAA,EAAQ,QAAA;AAAA,EACR,QAAA,EAAU,UAAA;AAAA,EACV,GAAA,EAAK,QAAA;AAAA,EACL,QAAA,EACE,4GAAA;AAAA,EACF,cAAA,EAAgB;AAClB;AAEO,IAAM,uBAAA,GAA6C;AAAA,EACxD,0BAAA;AAAA,EACA,0CAAA;AAAA,EACA;AACF;ACHO,SAAS,aAAA,CAAc;AAAA,EAC5B,IAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAqC;AACnC,EAAA,MAAM,EAAE,iBAAA,EAAkB,GAAIA,sCAAA,EAAqB;AACnD,EAAA,MAAM,EAAE,YAAY,aAAA,EAAe,MAAA,EAAQ,iBAAgB,GAAIC,+BAAA,CAAc,MAAMC,gCAAc,CAAA;AACjG,EAAA,MAAM,cAAA,GAAiBC,wCAAA,CAAuB,EAAE,OAAA,EAAS,MAAM,CAAA;AAE/D,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAAwB;AACtC,IAAA,IAAI,cAAA,CAAe,OAAA,CAAQ,IAAI,CAAA,EAAG;AAChC,MAAA,OAAA,CAAQ,EAAE,CAAA;AACV,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,CAAA;AAC1B,IAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,IAAA,qBAAA,CAAsB,MAAM;AAC1B,MAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,KAAA,CAAM,KAAA,EAAM;AACZ,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,MAAM,CAAA;AAAA,IAClD,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,WAAWC,+BAAA,CAAc;AAAA,IAC7B,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,aAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW,MAAM,OAAA,CAAQ,EAAE,CAAA;AAAA,IAC3B,QAAA,EAAU;AAAA,GACX,CAAA;AAED,EAAA,OAAO;AAAA,IACL,UAAU,cAAA,CAAe,QAAA;AAAA,IACzB,WAAW,CAAC,KAAA,KAAsC,KAAK,QAAA,CAAS,cAAc,KAAK,CAAA;AAAA,IACnF,eAAe,QAAA,CAAS,aAAA;AAAA,IACxB,WAAA,EAAa,CAAC,QAAA,KAA8B;AAC1C,MAAA,IAAI,CAAC,cAAA,CAAe,YAAA,CAAa,QAAQ,GAAG,OAAO,KAAA;AACnD,MAAA,OAAA,CAAQ,EAAE,CAAA;AACV,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAA,kBACEC,cAAA;AAAA,MAACC,2BAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,MAAA;AAAA,QACN,SAAA,EAAU,OAAA;AAAA,QACV,KAAA,EAAO,aAAA;AAAA,QACP,eAAe,QAAA,CAAS,aAAA;AAAA,QACxB,kBAAkB,QAAA,CAAS,gBAAA;AAAA,QAC3B,WAAW,QAAA,CAAS,SAAA;AAAA,QACpB,QAAA,EAAU,MAAA;AAAA,QACV,QAAA,EAAU,eAAA,IAAmB,iBAAA,GAAoB,MAAM,mBAAkB,GAAI;AAAA;AAAA;AAC/E,GAEJ;AACF","file":"chunk-T23UTIRI.cjs","sourcesContent":["/** The views of the document dialog (`DocumentViewerDialog`, the files page's own viewer). */\n\n/**\n * - `review`: the parsed document with its AI findings, a cited passage marked;\n * - `document`: the whole viewer (PDF and text, markup, signing, versions, sharing);\n * - `ask`: a question about the document, which starts a thread about it.\n */\nexport type DocumentViewerTab = 'review' | 'document' | 'ask'\n\nexport const DEFAULT_DOCUMENT_VIEWER_TABS: readonly DocumentViewerTab[] = [\n 'review',\n 'document',\n 'ask',\n]\n\n/** The dialog's words; each has a neutral default. */\nexport interface DocumentViewerDialogLabels {\n review?: string\n document?: string\n ask?: string\n /** Above the ask bar. */\n askIntro?: string\n /** The ask bar's placeholder; `{title}` is the document's title. */\n askPlaceholder?: string\n}\n\nexport const DEFAULT_DOCUMENT_VIEWER_LABELS: Required<DocumentViewerDialogLabels> = {\n review: 'Review',\n document: 'Document',\n ask: 'Ask AI',\n askIntro:\n 'Your question starts a new thread about this document. The answer cites its passages and takes follow-ups.',\n askPlaceholder: 'Ask about {title}…',\n}\n\nexport const DEFAULT_ASK_SUGGESTIONS: readonly string[] = [\n 'Summarise the key points',\n 'List the dates and deadlines it mentions',\n 'Who are the people and organisations named, and what are their roles?',\n]\n","/**\n * The `/` menu of a one-line ask field (the workspace ask field, the document ask), the chat\n * composer's rules: `/` opens the menu over the same rows, arrows move, Enter/Tab insert the slug,\n * Escape clears the slash. A skill (picked, or typed as `/slug text` and submitted) opens its form\n * instead (`useSlashSkillWorkflows`).\n */\nimport type { KeyboardEvent, ReactNode, RefObject } from 'react'\nimport { SlashMenu, type SlashMenuItem } from '../../chat/SlashMenu'\nimport type { WorkflowLanding } from '../../agents/workflows/types'\nimport { useListKeyNav, type UseListKeyNav } from '../../lib/useListKeyNav'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useSlashItems } from '../conversation/useSlashItems'\nimport { BUILTIN_SKILLS } from '../../agents/workflows/catalogue/seed'\nimport { useSlashSkillWorkflows } from '../workflows/useSlashSkillWorkflows'\n\nexport interface UseSlashFieldOptions {\n text: string\n setText: (text: string) => void\n inputRef: RefObject<HTMLInputElement | null>\n /** Where a skill's run lands (`useSlashSkillWorkflows`). */\n landing: WorkflowLanding\n /** A skill run's own send, for a field that asks its own way (`useSlashSkillWorkflows`). */\n send?: (prompt: string) => void\n}\n\nexport interface SlashField {\n /** Render once beside the field (the skill's form). */\n launcher: ReactNode\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void\n comboboxProps: UseListKeyNav['comboboxProps']\n /** A submitted question: true when it named a skill (its form opened), so nothing is asked. */\n startsSkill: (question: string) => boolean\n /** The menu, for the field's `menu` slot (it opens below the field). */\n menu: ReactNode\n}\n\nexport function useSlashField({\n text,\n setText,\n inputRef,\n landing,\n send,\n}: UseSlashFieldOptions): SlashField {\n const { openSkillsBrowser } = useBlocksEnvironment()\n const { slashToken, filteredItems, isOpen, canBrowseSkills } = useSlashItems(text, BUILTIN_SKILLS)\n const slashWorkflows = useSlashSkillWorkflows({ landing, send })\n\n const insert = (item: SlashMenuItem) => {\n if (slashWorkflows.openFor(item)) {\n setText('')\n return\n }\n const next = `/${item.name} `\n setText(next)\n requestAnimationFrame(() => {\n const input = inputRef.current\n if (!input) return\n input.focus()\n input.setSelectionRange(next.length, next.length)\n })\n }\n\n const slashNav = useListKeyNav({\n open: isOpen,\n items: filteredItems,\n onPick: insert,\n onDismiss: () => setText(''),\n resetKey: slashToken,\n })\n\n return {\n launcher: slashWorkflows.launcher,\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void slashNav.handleKeyDown(event),\n comboboxProps: slashNav.comboboxProps,\n startsSkill: (question: string): boolean => {\n if (!slashWorkflows.openForDraft(question)) return false\n setText('')\n return true\n },\n menu: (\n <SlashMenu\n open={isOpen}\n placement=\"below\"\n items={filteredItems}\n selectedIndex={slashNav.selectedIndex}\n setSelectedIndex={slashNav.setSelectedIndex}\n listboxId={slashNav.listboxId}\n onSelect={insert}\n onBrowse={canBrowseSkills && openSkillsBrowser ? () => openSkillsBrowser() : undefined}\n />\n ),\n }\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var chunkE5TPRRAW_cjs = require('./chunk-E5TPRRAW.cjs');
5
- var chunkL6APDO3Y_cjs = require('./chunk-L6APDO3Y.cjs');
5
+ var chunkNJCK6V6U_cjs = require('./chunk-NJCK6V6U.cjs');
6
6
  var chunkORELHENJ_cjs = require('./chunk-ORELHENJ.cjs');
7
7
  var chunk3YZKDCWY_cjs = require('./chunk-3YZKDCWY.cjs');
8
8
  var react = require('@arbidocs/react');
@@ -350,7 +350,7 @@ function planBadgeView(project) {
350
350
  }
351
351
  function projectGroupBilling(project, { isOwned, stripeEnabled }) {
352
352
  if (!project) return {};
353
- const planBadge = isOwned && stripeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(chunkL6APDO3Y_cjs.PlanBadge, { ...planBadgeView(project) }) : void 0;
353
+ const planBadge = isOwned && stripeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(chunkNJCK6V6U_cjs.PlanBadge, { ...planBadgeView(project) }) : void 0;
354
354
  const quotaItems = project.quotas ? getQuotaItems(project.quotas) : [];
355
355
  const readout = project.quotas ? /* @__PURE__ */ jsxRuntime.jsx(
356
356
  chunkE5TPRRAW_cjs.ProjectReadout,
@@ -360,7 +360,7 @@ function projectGroupBilling(project, { isOwned, stripeEnabled }) {
360
360
  testId: `project-usage-${project.external_id}`,
361
361
  // Bare numbers without plans for sale: a glance beside the name, not the Plan & Usage detail.
362
362
  node: /* @__PURE__ */ jsxRuntime.jsx(
363
- chunkL6APDO3Y_cjs.QuotaMeter,
363
+ chunkNJCK6V6U_cjs.QuotaMeter,
364
364
  {
365
365
  items: quotaItems,
366
366
  size: "sm",
@@ -451,5 +451,5 @@ exports.totalWithTaxWord = totalWithTaxWord;
451
451
  exports.useBillingSupport = useBillingSupport;
452
452
  exports.useRewardBalance = useRewardBalance;
453
453
  exports.useSelfExtId = useSelfExtId;
454
- //# sourceMappingURL=chunk-O4F77VSO.cjs.map
455
- //# sourceMappingURL=chunk-O4F77VSO.cjs.map
454
+ //# sourceMappingURL=chunk-TDO5UWXF.cjs.map
455
+ //# sourceMappingURL=chunk-TDO5UWXF.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/connection/billing/reward-balance.ts","../src/connection/billing/support.ts","../src/connection/billing/addon-pricing.ts","../src/connection/billing/plan-constants.ts","../src/connection/billing/quota-items.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":["useUserSettings","useHost","useArbi","formatGbp","PoundSterling","HardDrive","Users","gbp","jsx","PlanBadge","ProjectReadout","QuotaMeter","ProjectBreakdown","UsageMeter"],"mappings":"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","file":"chunk-O4F77VSO.cjs","sourcesContent":["/**\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({ refetchOnMount: 'always' })\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","/**\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\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","/** 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. */\nfunction 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 credits\" / \"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 credits\", \"1 × £10 + 1 × £25 of AI credits\", \"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 credits' : ''}`\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 = 0\n return { min, current, next: pending ?? current }\n}\n\n/** One block's monthly-equivalent price ex VAT: AI credits 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 ex VAT\" or \"£225/year ex VAT\". */\nfunction blockRenewalShort(kind: AddonKind, addons: ProjectAddons, isAnnual: boolean): string {\n return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? 'year' : 'month'} ex VAT`\n}\n\n/** The extra line annual plans show beside an AI credits 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 = 'Auto-upgrade limit'\n\n/** Automatic-upgrade spending is separate from explicitly selected allowances. */\nexport const USAGE_CAP_HELP =\n 'Your auto-upgrade limit covers extra monthly spend beyond your selected plan and add-ons. ' +\n '£0 turns automatic upgrades off. You can adjust usage add-ons with + and −. ' +\n 'Additions show the charge before you confirm; reductions apply next billing period, ' +\n 'provided your storage and collaborators fit. Prices are ex VAT.'\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 credits / 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 = blockMonthlyExVatPence(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 credits\" / \"25 GB\". */\nexport function stepLabel(kind: AddonKind, size: number): string {\n return kind === 'ai_usage' ? `${formatGbp(size)} of AI credits` : 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","/** 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\nexport const PRO_COLLABORATORS = 1\nexport const COLLABORATOR_BLOCK_SIZE = 5\n\n/**\n * Add-on block sizes (£25 of AI credits 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 credits (£) 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 dev). */\nexport const UNLIMITED_COLLABORATORS = 999\n\n/**\n * Suggested initial daily per-user AI credits 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 credits 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 credits 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 credits 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 credits',\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 * 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 credits + 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'\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 ex VAT\". */\nexport function plusVat(exVatPence: number, currency = 'gbp'): string {\n return `${formatMoney(exVatPence, currency)} ex 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/** What a checkout buys: \"Pro\", \"Pro + 2 × £25 of AI credits + 1 × 25 GB\". */\nexport function checkoutItemsLabel(\n aiBlocks: number,\n storageBlocks: number,\n collaboratorBlocks = 0,\n price?: ProductPrice\n): string {\n const ai = addonPrice(price, 'ai_usage')?.ai_gbp\n const storage = addonPrice(price, 'storage')?.storage_gb\n const collaborators = price?.addon_prices?.find((p) => p.kind === 'collaborators')?.collaborators\n return [\n 'Pro',\n aiBlocks > 0 &&\n (ai != null\n ? `${aiBlocks} × ${formatGbp(ai)} of AI credits`\n : `${aiBlocks} AI credits add-ons`),\n storageBlocks > 0 &&\n (storage != null ? `${storageBlocks} × ${storage} GB` : `${storageBlocks} storage add-ons`),\n collaboratorBlocks > 0 &&\n (collaborators != null\n ? `${collaboratorBlocks * collaborators} extra collaborators`\n : `${collaboratorBlocks} collaborator add-ons`),\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 ex VAT each\", annual: \"£225/year ex 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 ex VAT each`\n : `${formatMoney(block.amount_ex_vat, currency)}/month ex 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 (project.subscription === 'suspended') return 'Subscription ended'\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 === 'suspended'\n ? 'Subscription ended'\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 === 'suspended' || 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/reward-balance.ts","../src/connection/billing/support.ts","../src/connection/billing/addon-pricing.ts","../src/connection/billing/plan-constants.ts","../src/connection/billing/quota-items.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":["useUserSettings","useHost","useArbi","formatGbp","PoundSterling","HardDrive","Users","gbp","jsx","PlanBadge","ProjectReadout","QuotaMeter","ProjectBreakdown","UsageMeter"],"mappings":"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","file":"chunk-TDO5UWXF.cjs","sourcesContent":["/**\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({ refetchOnMount: 'always' })\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","/**\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\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","/** 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. */\nfunction 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 credits\" / \"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 credits\", \"1 × £10 + 1 × £25 of AI credits\", \"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 credits' : ''}`\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 = 0\n return { min, current, next: pending ?? current }\n}\n\n/** One block's monthly-equivalent price ex VAT: AI credits 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 ex VAT\" or \"£225/year ex VAT\". */\nfunction blockRenewalShort(kind: AddonKind, addons: ProjectAddons, isAnnual: boolean): string {\n return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? 'year' : 'month'} ex VAT`\n}\n\n/** The extra line annual plans show beside an AI credits 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 = 'Auto-upgrade limit'\n\n/** Automatic-upgrade spending is separate from explicitly selected allowances. */\nexport const USAGE_CAP_HELP =\n 'Your auto-upgrade limit covers extra monthly spend beyond your selected plan and add-ons. ' +\n '£0 turns automatic upgrades off. You can adjust usage add-ons with + and −. ' +\n 'Additions show the charge before you confirm; reductions apply next billing period, ' +\n 'provided your storage and collaborators fit. Prices are ex VAT.'\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 credits / 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 = blockMonthlyExVatPence(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 credits\" / \"25 GB\". */\nexport function stepLabel(kind: AddonKind, size: number): string {\n return kind === 'ai_usage' ? `${formatGbp(size)} of AI credits` : 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","/** 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\nexport const PRO_COLLABORATORS = 1\nexport const COLLABORATOR_BLOCK_SIZE = 5\n\n/**\n * Add-on block sizes (£25 of AI credits 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 credits (£) 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 dev). */\nexport const UNLIMITED_COLLABORATORS = 999\n\n/**\n * Suggested initial daily per-user AI credits 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 credits 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 credits 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 credits 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 credits',\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 * 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 credits + 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'\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 ex VAT\". */\nexport function plusVat(exVatPence: number, currency = 'gbp'): string {\n return `${formatMoney(exVatPence, currency)} ex 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/** What a checkout buys: \"Pro\", \"Pro + 2 × £25 of AI credits + 1 × 25 GB\". */\nexport function checkoutItemsLabel(\n aiBlocks: number,\n storageBlocks: number,\n collaboratorBlocks = 0,\n price?: ProductPrice\n): string {\n const ai = addonPrice(price, 'ai_usage')?.ai_gbp\n const storage = addonPrice(price, 'storage')?.storage_gb\n const collaborators = price?.addon_prices?.find((p) => p.kind === 'collaborators')?.collaborators\n return [\n 'Pro',\n aiBlocks > 0 &&\n (ai != null\n ? `${aiBlocks} × ${formatGbp(ai)} of AI credits`\n : `${aiBlocks} AI credits add-ons`),\n storageBlocks > 0 &&\n (storage != null ? `${storageBlocks} × ${storage} GB` : `${storageBlocks} storage add-ons`),\n collaboratorBlocks > 0 &&\n (collaborators != null\n ? `${collaboratorBlocks * collaborators} extra collaborators`\n : `${collaboratorBlocks} collaborator add-ons`),\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 ex VAT each\", annual: \"£225/year ex 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 ex VAT each`\n : `${formatMoney(block.amount_ex_vat, currency)}/month ex 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 (project.subscription === 'suspended') return 'Subscription ended'\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 === 'suspended'\n ? 'Subscription ended'\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 === 'suspended' || 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"]}