@stigmer/react 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/billing/AutoRechargeCard.d.ts +38 -0
  2. package/billing/AutoRechargeCard.d.ts.map +1 -0
  3. package/billing/AutoRechargeCard.js +90 -0
  4. package/billing/AutoRechargeCard.js.map +1 -0
  5. package/billing/BillingSection.d.ts +32 -0
  6. package/billing/BillingSection.d.ts.map +1 -0
  7. package/billing/BillingSection.js +81 -0
  8. package/billing/BillingSection.js.map +1 -0
  9. package/billing/CreditBalanceCard.d.ts +25 -0
  10. package/billing/CreditBalanceCard.d.ts.map +1 -0
  11. package/billing/CreditBalanceCard.js +28 -0
  12. package/billing/CreditBalanceCard.js.map +1 -0
  13. package/billing/CreditLedgerTable.d.ts +22 -0
  14. package/billing/CreditLedgerTable.d.ts.map +1 -0
  15. package/billing/CreditLedgerTable.js +75 -0
  16. package/billing/CreditLedgerTable.js.map +1 -0
  17. package/billing/CreditPackGrid.d.ts +31 -0
  18. package/billing/CreditPackGrid.d.ts.map +1 -0
  19. package/billing/CreditPackGrid.js +35 -0
  20. package/billing/CreditPackGrid.js.map +1 -0
  21. package/billing/LowBalanceBanner.d.ts +26 -0
  22. package/billing/LowBalanceBanner.d.ts.map +1 -0
  23. package/billing/LowBalanceBanner.js +33 -0
  24. package/billing/LowBalanceBanner.js.map +1 -0
  25. package/billing/PaymentMethodCard.d.ts +35 -0
  26. package/billing/PaymentMethodCard.d.ts.map +1 -0
  27. package/billing/PaymentMethodCard.js +48 -0
  28. package/billing/PaymentMethodCard.js.map +1 -0
  29. package/billing/credit-packs.d.ts +25 -0
  30. package/billing/credit-packs.d.ts.map +1 -0
  31. package/billing/credit-packs.js +39 -0
  32. package/billing/credit-packs.js.map +1 -0
  33. package/billing/format.d.ts +39 -0
  34. package/billing/format.d.ts.map +1 -0
  35. package/billing/format.js +90 -0
  36. package/billing/format.js.map +1 -0
  37. package/billing/index.d.ts +32 -0
  38. package/billing/index.d.ts.map +1 -0
  39. package/billing/index.js +21 -0
  40. package/billing/index.js.map +1 -0
  41. package/billing/useBillingAccount.d.ts +40 -0
  42. package/billing/useBillingAccount.d.ts.map +1 -0
  43. package/billing/useBillingAccount.js +35 -0
  44. package/billing/useBillingAccount.js.map +1 -0
  45. package/billing/useBillingUsageReport.d.ts +42 -0
  46. package/billing/useBillingUsageReport.d.ts.map +1 -0
  47. package/billing/useBillingUsageReport.js +43 -0
  48. package/billing/useBillingUsageReport.js.map +1 -0
  49. package/billing/useCreateBillingPortalSession.d.ts +35 -0
  50. package/billing/useCreateBillingPortalSession.d.ts.map +1 -0
  51. package/billing/useCreateBillingPortalSession.js +50 -0
  52. package/billing/useCreateBillingPortalSession.js.map +1 -0
  53. package/billing/useCreateCheckoutSession.d.ts +54 -0
  54. package/billing/useCreateCheckoutSession.d.ts.map +1 -0
  55. package/billing/useCreateCheckoutSession.js +58 -0
  56. package/billing/useCreateCheckoutSession.js.map +1 -0
  57. package/billing/useCreditLedger.d.ts +48 -0
  58. package/billing/useCreditLedger.d.ts.map +1 -0
  59. package/billing/useCreditLedger.js +39 -0
  60. package/billing/useCreditLedger.js.map +1 -0
  61. package/billing/useCustomerModelPricing.d.ts +41 -0
  62. package/billing/useCustomerModelPricing.d.ts.map +1 -0
  63. package/billing/useCustomerModelPricing.js +37 -0
  64. package/billing/useCustomerModelPricing.js.map +1 -0
  65. package/billing/useSetAutoRechargeConfig.d.ts +50 -0
  66. package/billing/useSetAutoRechargeConfig.d.ts.map +1 -0
  67. package/billing/useSetAutoRechargeConfig.js +53 -0
  68. package/billing/useSetAutoRechargeConfig.js.map +1 -0
  69. package/composer/ComposerToolbar.js +1 -1
  70. package/composer/ComposerToolbar.js.map +1 -1
  71. package/composer/SessionComposer.d.ts +1 -1
  72. package/composer/SessionComposer.d.ts.map +1 -1
  73. package/composer/SessionComposer.js +19 -4
  74. package/composer/SessionComposer.js.map +1 -1
  75. package/composer/__tests__/SessionComposer-memo.test.d.ts +2 -0
  76. package/composer/__tests__/SessionComposer-memo.test.d.ts.map +1 -0
  77. package/composer/__tests__/SessionComposer-memo.test.js +23 -0
  78. package/composer/__tests__/SessionComposer-memo.test.js.map +1 -0
  79. package/execution/ApprovalCard.d.ts +5 -1
  80. package/execution/ApprovalCard.d.ts.map +1 -1
  81. package/execution/ApprovalCard.js +7 -3
  82. package/execution/ApprovalCard.js.map +1 -1
  83. package/execution/ExecutionPhaseBadge.d.ts +1 -1
  84. package/execution/ExecutionPhaseBadge.d.ts.map +1 -1
  85. package/execution/ExecutionPhaseBadge.js +3 -2
  86. package/execution/ExecutionPhaseBadge.js.map +1 -1
  87. package/execution/MessageEntry.d.ts +7 -3
  88. package/execution/MessageEntry.d.ts.map +1 -1
  89. package/execution/MessageEntry.js +19 -8
  90. package/execution/MessageEntry.js.map +1 -1
  91. package/execution/MessageThread.d.ts +84 -3
  92. package/execution/MessageThread.d.ts.map +1 -1
  93. package/execution/MessageThread.js +113 -65
  94. package/execution/MessageThread.js.map +1 -1
  95. package/execution/SetupProgress.d.ts +1 -1
  96. package/execution/SetupProgress.d.ts.map +1 -1
  97. package/execution/SetupProgress.js +3 -3
  98. package/execution/SetupProgress.js.map +1 -1
  99. package/execution/SubAgentSection.d.ts +5 -1
  100. package/execution/SubAgentSection.d.ts.map +1 -1
  101. package/execution/SubAgentSection.js +13 -7
  102. package/execution/SubAgentSection.js.map +1 -1
  103. package/execution/ThreadSkeleton.d.ts +22 -0
  104. package/execution/ThreadSkeleton.d.ts.map +1 -0
  105. package/execution/ThreadSkeleton.js +26 -0
  106. package/execution/ThreadSkeleton.js.map +1 -0
  107. package/execution/ToolCallGroup.d.ts +16 -1
  108. package/execution/ToolCallGroup.d.ts.map +1 -1
  109. package/execution/ToolCallGroup.js +31 -3
  110. package/execution/ToolCallGroup.js.map +1 -1
  111. package/execution/UsageWidget.d.ts +1 -1
  112. package/execution/__tests__/message-entry.test.d.ts +2 -0
  113. package/execution/__tests__/message-entry.test.d.ts.map +1 -0
  114. package/execution/__tests__/message-entry.test.js +178 -0
  115. package/execution/__tests__/message-entry.test.js.map +1 -0
  116. package/execution/__tests__/thread-keys.test.d.ts +2 -0
  117. package/execution/__tests__/thread-keys.test.d.ts.map +1 -0
  118. package/execution/__tests__/thread-keys.test.js +289 -0
  119. package/execution/__tests__/thread-keys.test.js.map +1 -0
  120. package/execution/__tests__/thread-memoization.test.d.ts +2 -0
  121. package/execution/__tests__/thread-memoization.test.d.ts.map +1 -0
  122. package/execution/__tests__/thread-memoization.test.js +262 -0
  123. package/execution/__tests__/thread-memoization.test.js.map +1 -0
  124. package/execution/__tests__/thread-skeleton.test.d.ts +2 -0
  125. package/execution/__tests__/thread-skeleton.test.d.ts.map +1 -0
  126. package/execution/__tests__/thread-skeleton.test.js +35 -0
  127. package/execution/__tests__/thread-skeleton.test.js.map +1 -0
  128. package/execution/__tests__/useExecutionStream.test.js +73 -10
  129. package/execution/__tests__/useExecutionStream.test.js.map +1 -1
  130. package/execution/__tests__/useSessionVariables-stability.test.d.ts +2 -0
  131. package/execution/__tests__/useSessionVariables-stability.test.d.ts.map +1 -0
  132. package/execution/__tests__/useSessionVariables-stability.test.js +69 -0
  133. package/execution/__tests__/useSessionVariables-stability.test.js.map +1 -0
  134. package/execution/__tests__/virtualized-thread.test.d.ts +2 -0
  135. package/execution/__tests__/virtualized-thread.test.d.ts.map +1 -0
  136. package/execution/__tests__/virtualized-thread.test.js +274 -0
  137. package/execution/__tests__/virtualized-thread.test.js.map +1 -0
  138. package/execution/index.d.ts +2 -0
  139. package/execution/index.d.ts.map +1 -1
  140. package/execution/index.js +1 -0
  141. package/execution/index.js.map +1 -1
  142. package/execution/useExecutionStream.d.ts +35 -10
  143. package/execution/useExecutionStream.d.ts.map +1 -1
  144. package/execution/useExecutionStream.js +79 -40
  145. package/execution/useExecutionStream.js.map +1 -1
  146. package/execution/useSessionVariables.d.ts.map +1 -1
  147. package/execution/useSessionVariables.js +4 -3
  148. package/execution/useSessionVariables.js.map +1 -1
  149. package/github/useGitHubConnection.d.ts.map +1 -1
  150. package/github/useGitHubConnection.js +5 -4
  151. package/github/useGitHubConnection.js.map +1 -1
  152. package/identity-account/index.d.ts +2 -0
  153. package/identity-account/index.d.ts.map +1 -0
  154. package/identity-account/index.js +2 -0
  155. package/identity-account/index.js.map +1 -0
  156. package/identity-account/useIdentityAccountGate.d.ts +81 -0
  157. package/identity-account/useIdentityAccountGate.d.ts.map +1 -0
  158. package/identity-account/useIdentityAccountGate.js +100 -0
  159. package/identity-account/useIdentityAccountGate.js.map +1 -0
  160. package/index.d.ts +10 -4
  161. package/index.d.ts.map +1 -1
  162. package/index.js +8 -2
  163. package/index.js.map +1 -1
  164. package/internal/FetchCacheProvider.d.ts +44 -0
  165. package/internal/FetchCacheProvider.d.ts.map +1 -0
  166. package/internal/FetchCacheProvider.js +61 -0
  167. package/internal/FetchCacheProvider.js.map +1 -0
  168. package/internal/JumpToLatestButton.d.ts +14 -0
  169. package/internal/JumpToLatestButton.d.ts.map +1 -0
  170. package/internal/JumpToLatestButton.js +19 -0
  171. package/internal/JumpToLatestButton.js.map +1 -0
  172. package/internal/ThreadItemWrapper.d.ts +20 -0
  173. package/internal/ThreadItemWrapper.d.ts.map +1 -0
  174. package/internal/ThreadItemWrapper.js +44 -0
  175. package/internal/ThreadItemWrapper.js.map +1 -0
  176. package/internal/VirtualizedThread.d.ts +25 -0
  177. package/internal/VirtualizedThread.d.ts.map +1 -0
  178. package/internal/VirtualizedThread.js +58 -0
  179. package/internal/VirtualizedThread.js.map +1 -0
  180. package/internal/__tests__/fetch-cache.test.d.ts +2 -0
  181. package/internal/__tests__/fetch-cache.test.d.ts.map +1 -0
  182. package/internal/__tests__/fetch-cache.test.js +182 -0
  183. package/internal/__tests__/fetch-cache.test.js.map +1 -0
  184. package/internal/__tests__/stream-controller.test.d.ts +2 -0
  185. package/internal/__tests__/stream-controller.test.d.ts.map +1 -0
  186. package/internal/__tests__/stream-controller.test.js +294 -0
  187. package/internal/__tests__/stream-controller.test.js.map +1 -0
  188. package/internal/__tests__/thread-animation.test.d.ts +2 -0
  189. package/internal/__tests__/thread-animation.test.d.ts.map +1 -0
  190. package/internal/__tests__/thread-animation.test.js +79 -0
  191. package/internal/__tests__/thread-animation.test.js.map +1 -0
  192. package/internal/__tests__/useAutoScroll.test.d.ts +2 -0
  193. package/internal/__tests__/useAutoScroll.test.d.ts.map +1 -0
  194. package/internal/__tests__/useAutoScroll.test.js +188 -0
  195. package/internal/__tests__/useAutoScroll.test.js.map +1 -0
  196. package/internal/__tests__/useFetch-cache.test.d.ts +2 -0
  197. package/internal/__tests__/useFetch-cache.test.d.ts.map +1 -0
  198. package/internal/__tests__/useFetch-cache.test.js +137 -0
  199. package/internal/__tests__/useFetch-cache.test.js.map +1 -0
  200. package/internal/dev/__tests__/use-key-stability.test.d.ts +2 -0
  201. package/internal/dev/__tests__/use-key-stability.test.d.ts.map +1 -0
  202. package/internal/dev/__tests__/use-key-stability.test.js +72 -0
  203. package/internal/dev/__tests__/use-key-stability.test.js.map +1 -0
  204. package/internal/dev/__tests__/use-render-tracer.test.d.ts +2 -0
  205. package/internal/dev/__tests__/use-render-tracer.test.d.ts.map +1 -0
  206. package/internal/dev/__tests__/use-render-tracer.test.js +55 -0
  207. package/internal/dev/__tests__/use-render-tracer.test.js.map +1 -0
  208. package/internal/dev/dom-counter.d.ts +14 -0
  209. package/internal/dev/dom-counter.d.ts.map +1 -0
  210. package/internal/dev/dom-counter.js +39 -0
  211. package/internal/dev/dom-counter.js.map +1 -0
  212. package/internal/dev/index.d.ts +6 -0
  213. package/internal/dev/index.d.ts.map +1 -0
  214. package/internal/dev/index.js +6 -0
  215. package/internal/dev/index.js.map +1 -0
  216. package/internal/dev/profiler-wrapper.d.ts +16 -0
  217. package/internal/dev/profiler-wrapper.d.ts.map +1 -0
  218. package/internal/dev/profiler-wrapper.js +31 -0
  219. package/internal/dev/profiler-wrapper.js.map +1 -0
  220. package/internal/dev/use-key-stability.d.ts +22 -0
  221. package/internal/dev/use-key-stability.d.ts.map +1 -0
  222. package/internal/dev/use-key-stability.js +67 -0
  223. package/internal/dev/use-key-stability.js.map +1 -0
  224. package/internal/dev/use-render-tracer.d.ts +13 -0
  225. package/internal/dev/use-render-tracer.d.ts.map +1 -0
  226. package/internal/dev/use-render-tracer.js +57 -0
  227. package/internal/dev/use-render-tracer.js.map +1 -0
  228. package/internal/dev/use-stream-rate.d.ts +23 -0
  229. package/internal/dev/use-stream-rate.d.ts.map +1 -0
  230. package/internal/dev/use-stream-rate.js +94 -0
  231. package/internal/dev/use-stream-rate.js.map +1 -0
  232. package/internal/fetch-cache.d.ts +72 -0
  233. package/internal/fetch-cache.d.ts.map +1 -0
  234. package/internal/fetch-cache.js +118 -0
  235. package/internal/fetch-cache.js.map +1 -0
  236. package/internal/store/__tests__/conversation-store.test.d.ts +2 -0
  237. package/internal/store/__tests__/conversation-store.test.d.ts.map +1 -0
  238. package/internal/store/__tests__/conversation-store.test.js +200 -0
  239. package/internal/store/__tests__/conversation-store.test.js.map +1 -0
  240. package/internal/store/__tests__/structural-share.test.d.ts +2 -0
  241. package/internal/store/__tests__/structural-share.test.d.ts.map +1 -0
  242. package/internal/store/__tests__/structural-share.test.js +368 -0
  243. package/internal/store/__tests__/structural-share.test.js.map +1 -0
  244. package/internal/store/conversation-store.d.ts +62 -0
  245. package/internal/store/conversation-store.d.ts.map +1 -0
  246. package/internal/store/conversation-store.js +95 -0
  247. package/internal/store/conversation-store.js.map +1 -0
  248. package/internal/store/index.d.ts +31 -0
  249. package/internal/store/index.d.ts.map +1 -0
  250. package/internal/store/index.js +54 -0
  251. package/internal/store/index.js.map +1 -0
  252. package/internal/store/structural-share.d.ts +13 -0
  253. package/internal/store/structural-share.d.ts.map +1 -0
  254. package/internal/store/structural-share.js +240 -0
  255. package/internal/store/structural-share.js.map +1 -0
  256. package/internal/stream-controller.d.ts +85 -0
  257. package/internal/stream-controller.d.ts.map +1 -0
  258. package/internal/stream-controller.js +146 -0
  259. package/internal/stream-controller.js.map +1 -0
  260. package/internal/useAutoScroll.d.ts +32 -0
  261. package/internal/useAutoScroll.d.ts.map +1 -0
  262. package/internal/useAutoScroll.js +97 -0
  263. package/internal/useAutoScroll.js.map +1 -0
  264. package/internal/useFetch.d.ts +14 -0
  265. package/internal/useFetch.d.ts.map +1 -1
  266. package/internal/useFetch.js +32 -2
  267. package/internal/useFetch.js.map +1 -1
  268. package/package.json +7 -5
  269. package/session/__tests__/useNewSessionFlow.test.js +16 -0
  270. package/session/__tests__/useNewSessionFlow.test.js.map +1 -1
  271. package/session/__tests__/usePersistedModel.test.d.ts +2 -0
  272. package/session/__tests__/usePersistedModel.test.d.ts.map +1 -0
  273. package/session/__tests__/usePersistedModel.test.js +82 -0
  274. package/session/__tests__/usePersistedModel.test.js.map +1 -0
  275. package/session/__tests__/useSession.test.d.ts +2 -0
  276. package/session/__tests__/useSession.test.d.ts.map +1 -0
  277. package/session/__tests__/useSession.test.js +130 -0
  278. package/session/__tests__/useSession.test.js.map +1 -0
  279. package/session/useNewSessionFlow.d.ts.map +1 -1
  280. package/session/useNewSessionFlow.js +12 -6
  281. package/session/useNewSessionFlow.js.map +1 -1
  282. package/session/usePersistedModel.d.ts +3 -0
  283. package/session/usePersistedModel.d.ts.map +1 -1
  284. package/session/usePersistedModel.js +27 -2
  285. package/session/usePersistedModel.js.map +1 -1
  286. package/session/useSession.d.ts.map +1 -1
  287. package/session/useSession.js +1 -1
  288. package/session/useSession.js.map +1 -1
  289. package/session/useSessionConversation.d.ts.map +1 -1
  290. package/session/useSessionConversation.js +9 -1
  291. package/session/useSessionConversation.js.map +1 -1
  292. package/session/useSessionExecutions.d.ts.map +1 -1
  293. package/session/useSessionExecutions.js +1 -1
  294. package/session/useSessionExecutions.js.map +1 -1
  295. package/session/useSessionPageFlow.js +1 -1
  296. package/session/useSessionPageFlow.js.map +1 -1
  297. package/session/useSessionUsage.d.ts +24 -40
  298. package/session/useSessionUsage.d.ts.map +1 -1
  299. package/session/useSessionUsage.js +64 -97
  300. package/session/useSessionUsage.js.map +1 -1
  301. package/settings/BillingSection.d.ts +3 -0
  302. package/settings/BillingSection.d.ts.map +1 -0
  303. package/settings/BillingSection.js +3 -0
  304. package/settings/BillingSection.js.map +1 -0
  305. package/settings/index.d.ts +2 -0
  306. package/settings/index.d.ts.map +1 -1
  307. package/settings/index.js +1 -0
  308. package/settings/index.js.map +1 -1
  309. package/settings/settings-nav.js +1 -1
  310. package/settings/settings-nav.js.map +1 -1
  311. package/src/billing/AutoRechargeCard.tsx +274 -0
  312. package/src/billing/BillingSection.tsx +255 -0
  313. package/src/billing/CreditBalanceCard.tsx +81 -0
  314. package/src/billing/CreditLedgerTable.tsx +281 -0
  315. package/src/billing/CreditPackGrid.tsx +132 -0
  316. package/src/billing/LowBalanceBanner.tsx +67 -0
  317. package/src/billing/PaymentMethodCard.tsx +133 -0
  318. package/src/billing/credit-packs.ts +54 -0
  319. package/src/billing/format.ts +97 -0
  320. package/src/billing/index.ts +51 -0
  321. package/src/billing/useBillingAccount.ts +64 -0
  322. package/src/billing/useBillingUsageReport.ts +73 -0
  323. package/src/billing/useCreateBillingPortalSession.ts +76 -0
  324. package/src/billing/useCreateCheckoutSession.ts +101 -0
  325. package/src/billing/useCreditLedger.ts +79 -0
  326. package/src/billing/useCustomerModelPricing.ts +67 -0
  327. package/src/billing/useSetAutoRechargeConfig.ts +90 -0
  328. package/src/composer/ComposerToolbar.tsx +1 -1
  329. package/src/composer/SessionComposer.tsx +22 -4
  330. package/src/composer/__tests__/SessionComposer-memo.test.ts +26 -0
  331. package/src/execution/ApprovalCard.tsx +7 -3
  332. package/src/execution/ExecutionPhaseBadge.tsx +3 -2
  333. package/src/execution/MessageEntry.tsx +27 -16
  334. package/src/execution/MessageThread.tsx +308 -131
  335. package/src/execution/SetupProgress.tsx +3 -3
  336. package/src/execution/SubAgentSection.tsx +14 -6
  337. package/src/execution/ThreadSkeleton.tsx +73 -0
  338. package/src/execution/ToolCallGroup.tsx +36 -3
  339. package/src/execution/UsageWidget.tsx +1 -1
  340. package/src/execution/__tests__/message-entry.test.tsx +236 -0
  341. package/src/execution/__tests__/thread-keys.test.ts +409 -0
  342. package/src/execution/__tests__/thread-memoization.test.ts +320 -0
  343. package/src/execution/__tests__/thread-skeleton.test.tsx +44 -0
  344. package/src/execution/__tests__/useExecutionStream.test.tsx +109 -12
  345. package/src/execution/__tests__/useSessionVariables-stability.test.ts +95 -0
  346. package/src/execution/__tests__/virtualized-thread.test.tsx +401 -0
  347. package/src/execution/index.ts +3 -0
  348. package/src/execution/useExecutionStream.ts +123 -48
  349. package/src/execution/useSessionVariables.ts +17 -12
  350. package/src/github/useGitHubConnection.ts +18 -13
  351. package/src/identity-account/index.ts +5 -0
  352. package/src/identity-account/useIdentityAccountGate.ts +163 -0
  353. package/src/index.ts +73 -0
  354. package/src/internal/FetchCacheProvider.tsx +74 -0
  355. package/src/internal/JumpToLatestButton.tsx +61 -0
  356. package/src/internal/ThreadItemWrapper.tsx +65 -0
  357. package/src/internal/VirtualizedThread.tsx +162 -0
  358. package/src/internal/__tests__/fetch-cache.test.ts +230 -0
  359. package/src/internal/__tests__/stream-controller.test.ts +395 -0
  360. package/src/internal/__tests__/thread-animation.test.tsx +121 -0
  361. package/src/internal/__tests__/useAutoScroll.test.tsx +261 -0
  362. package/src/internal/__tests__/useFetch-cache.test.ts +214 -0
  363. package/src/internal/dev/__tests__/use-key-stability.test.ts +124 -0
  364. package/src/internal/dev/__tests__/use-render-tracer.test.ts +78 -0
  365. package/src/internal/dev/dom-counter.ts +47 -0
  366. package/src/internal/dev/index.ts +5 -0
  367. package/src/internal/dev/profiler-wrapper.tsx +52 -0
  368. package/src/internal/dev/use-key-stability.ts +86 -0
  369. package/src/internal/dev/use-render-tracer.ts +70 -0
  370. package/src/internal/dev/use-stream-rate.ts +138 -0
  371. package/src/internal/fetch-cache.ts +155 -0
  372. package/src/internal/store/__tests__/conversation-store.test.ts +257 -0
  373. package/src/internal/store/__tests__/structural-share.test.ts +454 -0
  374. package/src/internal/store/conversation-store.ts +128 -0
  375. package/src/internal/store/index.ts +68 -0
  376. package/src/internal/store/structural-share.ts +318 -0
  377. package/src/internal/stream-controller.ts +201 -0
  378. package/src/internal/useAutoScroll.ts +121 -0
  379. package/src/internal/useFetch.ts +51 -2
  380. package/src/session/__tests__/useNewSessionFlow.test.tsx +22 -0
  381. package/src/session/__tests__/usePersistedModel.test.tsx +117 -0
  382. package/src/session/__tests__/useSession.test.tsx +187 -0
  383. package/src/session/useNewSessionFlow.ts +12 -6
  384. package/src/session/usePersistedModel.ts +28 -2
  385. package/src/session/useSession.ts +1 -0
  386. package/src/session/useSessionConversation.ts +11 -2
  387. package/src/session/useSessionExecutions.ts +1 -0
  388. package/src/session/useSessionPageFlow.ts +1 -1
  389. package/src/session/useSessionUsage.ts +102 -123
  390. package/src/settings/BillingSection.tsx +4 -0
  391. package/src/settings/index.ts +2 -0
  392. package/src/settings/settings-nav.ts +1 -1
  393. package/src/styles.css +31 -0
  394. package/src/usage/AgentBreakdownList.tsx +147 -0
  395. package/src/usage/CreditRunwayIndicator.tsx +71 -0
  396. package/src/usage/ExportButton.tsx +115 -0
  397. package/src/usage/HarnessSplitCard.tsx +103 -0
  398. package/src/usage/OrgUsagePanel.tsx +109 -45
  399. package/src/usage/index.ts +15 -0
  400. package/src/usage/useExportCSV.ts +115 -0
  401. package/src/usage/useOrgUsageReport.ts +2 -1
  402. package/src/workspace/__tests__/useWorkspaceEntries-stability.test.ts +76 -0
  403. package/src/workspace/useWorkspaceEntries.ts +16 -11
  404. package/styles.css +1 -1
  405. package/usage/AgentBreakdownList.d.ts +21 -0
  406. package/usage/AgentBreakdownList.d.ts.map +1 -0
  407. package/usage/AgentBreakdownList.js +44 -0
  408. package/usage/AgentBreakdownList.js.map +1 -0
  409. package/usage/CreditRunwayIndicator.d.ts +21 -0
  410. package/usage/CreditRunwayIndicator.d.ts.map +1 -0
  411. package/usage/CreditRunwayIndicator.js +38 -0
  412. package/usage/CreditRunwayIndicator.js.map +1 -0
  413. package/usage/ExportButton.d.ts +20 -0
  414. package/usage/ExportButton.d.ts.map +1 -0
  415. package/usage/ExportButton.js +36 -0
  416. package/usage/ExportButton.js.map +1 -0
  417. package/usage/HarnessSplitCard.d.ts +17 -0
  418. package/usage/HarnessSplitCard.d.ts.map +1 -0
  419. package/usage/HarnessSplitCard.js +38 -0
  420. package/usage/HarnessSplitCard.js.map +1 -0
  421. package/usage/OrgUsagePanel.d.ts.map +1 -1
  422. package/usage/OrgUsagePanel.js +30 -22
  423. package/usage/OrgUsagePanel.js.map +1 -1
  424. package/usage/index.d.ts +10 -0
  425. package/usage/index.d.ts.map +1 -1
  426. package/usage/index.js +5 -0
  427. package/usage/index.js.map +1 -1
  428. package/usage/useExportCSV.d.ts +23 -0
  429. package/usage/useExportCSV.d.ts.map +1 -0
  430. package/usage/useExportCSV.js +81 -0
  431. package/usage/useExportCSV.js.map +1 -0
  432. package/usage/useOrgUsageReport.d.ts +2 -1
  433. package/usage/useOrgUsageReport.d.ts.map +1 -1
  434. package/usage/useOrgUsageReport.js +2 -1
  435. package/usage/useOrgUsageReport.js.map +1 -1
  436. package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts +2 -0
  437. package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts.map +1 -0
  438. package/workspace/__tests__/useWorkspaceEntries-stability.test.js +57 -0
  439. package/workspace/__tests__/useWorkspaceEntries-stability.test.js.map +1 -0
  440. package/workspace/useWorkspaceEntries.d.ts.map +1 -1
  441. package/workspace/useWorkspaceEntries.js +5 -4
  442. package/workspace/useWorkspaceEntries.js.map +1 -1
@@ -0,0 +1,281 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { cn } from "@stigmer/theme";
5
+ import { getUserMessage } from "@stigmer/sdk";
6
+ import type { CreditLedgerEntry } from "@stigmer/protos/ai/stigmer/billing/v1/credit_pb";
7
+ import {
8
+ formatLedgerAmount,
9
+ formatCreditBalance,
10
+ formatLedgerDate,
11
+ ledgerEntryLabel,
12
+ isCredit,
13
+ isHold,
14
+ } from "./format";
15
+ import { useCreditLedger, type UseCreditLedgerOptions } from "./useCreditLedger";
16
+
17
+ /** Props for {@link CreditLedgerTable}. */
18
+ export interface CreditLedgerTableProps {
19
+ /** Organization ID to fetch ledger entries for. */
20
+ readonly orgId: string;
21
+ /** Additional CSS class names. */
22
+ readonly className?: string;
23
+ }
24
+
25
+ /**
26
+ * Paginated transaction history table showing credit ledger entries.
27
+ *
28
+ * Displays entries with type badges, signed amounts, and running
29
+ * balance. Supports pagination with simple prev/next controls.
30
+ * Uses semantic colors: green for credits, red for debits, gray
31
+ * for holds and releases.
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * <CreditLedgerTable orgId={activeOrg.metadata.id} />
36
+ * ```
37
+ */
38
+ export function CreditLedgerTable({
39
+ orgId,
40
+ className,
41
+ }: CreditLedgerTableProps) {
42
+ const [pageNum, setPageNum] = useState(1);
43
+ const options: UseCreditLedgerOptions = { pageNum, pageSize: 10 };
44
+ const { ledger, isLoading, error } = useCreditLedger(orgId, options);
45
+
46
+ if (isLoading) {
47
+ return (
48
+ <div className={cn("space-y-2", className)} aria-busy="true">
49
+ <div className="h-4 w-32 animate-pulse rounded bg-muted-subtle" />
50
+ {Array.from({ length: 5 }, (_, i) => (
51
+ <div
52
+ key={i}
53
+ className="h-10 animate-pulse rounded-lg bg-muted-subtle"
54
+ />
55
+ ))}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ if (error) {
61
+ return (
62
+ <p className={cn("text-destructive text-xs", className)} role="alert">
63
+ {getUserMessage(error)}
64
+ </p>
65
+ );
66
+ }
67
+
68
+ const entries = ledger?.entries ?? [];
69
+ const totalPages = ledger?.totalPages ?? 0;
70
+
71
+ return (
72
+ <div className={className}>
73
+ <h3 className="mb-2 text-xs font-semibold text-foreground">
74
+ Transaction History
75
+ </h3>
76
+
77
+ {entries.length === 0 ? (
78
+ <EmptyLedger />
79
+ ) : (
80
+ <>
81
+ <div
82
+ className="rounded-lg border border-border bg-card"
83
+ role="table"
84
+ aria-label="Credit ledger"
85
+ >
86
+ <LedgerHeader />
87
+ {entries.map((entry) => (
88
+ <LedgerRow key={entry.entryId} entry={entry} />
89
+ ))}
90
+ </div>
91
+
92
+ {totalPages > 1 && (
93
+ <Pagination
94
+ pageNum={pageNum}
95
+ totalPages={totalPages}
96
+ onPageChange={setPageNum}
97
+ />
98
+ )}
99
+ </>
100
+ )}
101
+ </div>
102
+ );
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // LedgerHeader (internal)
107
+ // ---------------------------------------------------------------------------
108
+
109
+ function LedgerHeader() {
110
+ return (
111
+ <div
112
+ role="row"
113
+ className="grid grid-cols-[1fr_auto_auto] gap-x-4 border-b border-border px-3.5 py-2 text-[0.65rem] font-medium uppercase tracking-wider text-muted-foreground sm:grid-cols-[auto_1fr_auto_auto]"
114
+ >
115
+ <span role="columnheader" className="hidden sm:block">
116
+ Date
117
+ </span>
118
+ <span role="columnheader">Type</span>
119
+ <span role="columnheader" className="text-right">
120
+ Amount
121
+ </span>
122
+ <span role="columnheader" className="text-right">
123
+ Balance
124
+ </span>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ // ---------------------------------------------------------------------------
130
+ // LedgerRow (internal)
131
+ // ---------------------------------------------------------------------------
132
+
133
+ function LedgerRow({ entry }: { entry: CreditLedgerEntry }) {
134
+ const entryType = entry.type;
135
+ const credit = isCredit(entryType);
136
+ const hold = isHold(entryType);
137
+
138
+ return (
139
+ <div
140
+ role="row"
141
+ className="grid grid-cols-[1fr_auto_auto] items-center gap-x-4 border-b border-border-muted px-3.5 py-2.5 last:border-b-0 sm:grid-cols-[auto_1fr_auto_auto]"
142
+ >
143
+ <span
144
+ role="cell"
145
+ className="hidden text-xs tabular-nums text-muted-foreground sm:block sm:w-36"
146
+ >
147
+ {entry.createdAt
148
+ ? formatLedgerDate(entry.createdAt.seconds)
149
+ : "\u2014"}
150
+ </span>
151
+
152
+ <div role="cell" className="min-w-0">
153
+ <span
154
+ className={cn(
155
+ "inline-block rounded-full px-2 py-0.5 text-[0.65rem] font-medium",
156
+ credit
157
+ ? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
158
+ : hold
159
+ ? "bg-muted text-muted-foreground"
160
+ : "bg-destructive/10 text-destructive",
161
+ )}
162
+ >
163
+ {ledgerEntryLabel(entryType)}
164
+ </span>
165
+ {entry.source?.description && (
166
+ <span className="ml-2 text-xs text-muted-foreground">
167
+ {entry.source.description}
168
+ </span>
169
+ )}
170
+ {entry.createdAt && (
171
+ <span className="mt-0.5 block text-[0.6rem] tabular-nums text-muted-foreground sm:hidden">
172
+ {formatLedgerDate(entry.createdAt.seconds)}
173
+ </span>
174
+ )}
175
+ </div>
176
+
177
+ <span
178
+ role="cell"
179
+ className={cn(
180
+ "text-right text-xs font-medium tabular-nums",
181
+ credit
182
+ ? "text-emerald-600 dark:text-emerald-400"
183
+ : hold
184
+ ? "text-muted-foreground"
185
+ : "text-destructive",
186
+ )}
187
+ >
188
+ {formatLedgerAmount(entry.amountMicros)}
189
+ </span>
190
+
191
+ <span
192
+ role="cell"
193
+ className="text-right text-xs tabular-nums text-muted-foreground"
194
+ >
195
+ {formatCreditBalance(entry.balanceAfterMicros)}
196
+ </span>
197
+ </div>
198
+ );
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Pagination (internal)
203
+ // ---------------------------------------------------------------------------
204
+
205
+ function Pagination({
206
+ pageNum,
207
+ totalPages,
208
+ onPageChange,
209
+ }: {
210
+ pageNum: number;
211
+ totalPages: number;
212
+ onPageChange: (page: number) => void;
213
+ }) {
214
+ return (
215
+ <div
216
+ className="mt-3 flex items-center justify-between"
217
+ role="navigation"
218
+ aria-label="Ledger pagination"
219
+ >
220
+ <button
221
+ type="button"
222
+ disabled={pageNum <= 1}
223
+ onClick={() => onPageChange(pageNum - 1)}
224
+ className="rounded-md px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-40"
225
+ >
226
+ Previous
227
+ </button>
228
+ <span className="text-xs tabular-nums text-muted-foreground">
229
+ Page {pageNum} of {totalPages}
230
+ </span>
231
+ <button
232
+ type="button"
233
+ disabled={pageNum >= totalPages}
234
+ onClick={() => onPageChange(pageNum + 1)}
235
+ className="rounded-md px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-40"
236
+ >
237
+ Next
238
+ </button>
239
+ </div>
240
+ );
241
+ }
242
+
243
+ // ---------------------------------------------------------------------------
244
+ // EmptyLedger (internal)
245
+ // ---------------------------------------------------------------------------
246
+
247
+ function EmptyLedger() {
248
+ return (
249
+ <div className="flex flex-col items-center justify-center rounded-lg border border-border bg-card py-10 text-center">
250
+ <ReceiptIcon className="text-muted-foreground mb-3 size-8" />
251
+ <p className="text-sm font-medium text-foreground">
252
+ No transactions yet
253
+ </p>
254
+ <p className="mt-1 max-w-xs text-xs text-muted-foreground">
255
+ Credit purchases and usage charges will appear here as your
256
+ organization runs agent executions.
257
+ </p>
258
+ </div>
259
+ );
260
+ }
261
+
262
+ function ReceiptIcon({ className }: { className?: string }) {
263
+ return (
264
+ <svg
265
+ width="24"
266
+ height="24"
267
+ viewBox="0 0 24 24"
268
+ fill="none"
269
+ stroke="currentColor"
270
+ strokeWidth="1.5"
271
+ strokeLinecap="round"
272
+ strokeLinejoin="round"
273
+ className={className}
274
+ aria-hidden="true"
275
+ >
276
+ <path d="M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1Z" />
277
+ <path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8" />
278
+ <path d="M12 17.5v.5M12 6v.5" />
279
+ </svg>
280
+ );
281
+ }
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+ import { BillingAccountStatus } from "@stigmer/protos/ai/stigmer/billing/v1/enum_pb";
5
+ import {
6
+ CREDIT_PACKS,
7
+ formatPackPrice,
8
+ formatCreditCount,
9
+ type CreditPackInfo,
10
+ } from "./credit-packs";
11
+
12
+ /** Props for {@link CreditPackGrid}. */
13
+ export interface CreditPackGridProps {
14
+ /** Account status — purchases disabled when suspended or closed. */
15
+ readonly accountStatus: BillingAccountStatus;
16
+ /** ID of the pack currently being purchased (shows loading state). */
17
+ readonly purchasingPackId?: string | null;
18
+ /** Called when the user clicks a pack's buy button. */
19
+ readonly onPurchase: (packId: string) => void;
20
+ /** Additional CSS class names. */
21
+ readonly className?: string;
22
+ }
23
+
24
+ /**
25
+ * Grid of credit pack cards with purchase buttons.
26
+ *
27
+ * Displays the 3 self-serve credit packs (Starter, Growth, Team)
28
+ * in a responsive grid. Each card shows the pack name, price,
29
+ * credit count, and a buy button. Buttons are disabled when the
30
+ * account is suspended/closed or a purchase is in progress.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * <CreditPackGrid
35
+ * accountStatus={account.status}
36
+ * purchasingPackId={isSubmitting ? activePackId : null}
37
+ * onPurchase={(packId) => createSession({ orgId, packId, ... })}
38
+ * />
39
+ * ```
40
+ */
41
+ export function CreditPackGrid({
42
+ accountStatus,
43
+ purchasingPackId,
44
+ onPurchase,
45
+ className,
46
+ }: CreditPackGridProps) {
47
+ const isAccountActive =
48
+ accountStatus === BillingAccountStatus.billing_account_active;
49
+
50
+ return (
51
+ <div>
52
+ <h3 className="mb-2 text-xs font-semibold text-foreground">
53
+ Purchase Credits
54
+ </h3>
55
+ <div
56
+ className={cn("grid gap-3 sm:grid-cols-3", className)}
57
+ role="group"
58
+ aria-label="Credit packs"
59
+ >
60
+ {CREDIT_PACKS.map((pack) => (
61
+ <PackCard
62
+ key={pack.packId}
63
+ pack={pack}
64
+ isPurchasing={purchasingPackId === pack.packId}
65
+ isDisabled={!isAccountActive || purchasingPackId != null}
66
+ onPurchase={onPurchase}
67
+ />
68
+ ))}
69
+ </div>
70
+ {!isAccountActive && (
71
+ <p className="mt-2 text-xs text-muted-foreground">
72
+ Credit purchases are unavailable while your billing account is{" "}
73
+ {accountStatus === BillingAccountStatus.billing_account_suspended
74
+ ? "suspended"
75
+ : "closed"}
76
+ .
77
+ </p>
78
+ )}
79
+ </div>
80
+ );
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // PackCard (internal)
85
+ // ---------------------------------------------------------------------------
86
+
87
+ function PackCard({
88
+ pack,
89
+ isPurchasing,
90
+ isDisabled,
91
+ onPurchase,
92
+ }: {
93
+ pack: CreditPackInfo;
94
+ isPurchasing: boolean;
95
+ isDisabled: boolean;
96
+ onPurchase: (packId: string) => void;
97
+ }) {
98
+ return (
99
+ <div className="flex flex-col rounded-lg border border-border bg-card px-4 py-4">
100
+ <div className="text-sm font-semibold text-foreground">
101
+ {pack.displayName}
102
+ </div>
103
+ <div className="mt-0.5 text-xs text-muted-foreground">
104
+ {pack.description}
105
+ </div>
106
+
107
+ <div className="mt-3 flex items-baseline gap-1">
108
+ <span className="text-xl font-bold tabular-nums text-foreground">
109
+ {formatPackPrice(pack.priceCents)}
110
+ </span>
111
+ </div>
112
+ <div className="mt-0.5 text-xs tabular-nums text-muted-foreground">
113
+ {formatCreditCount(pack.credits)} credits
114
+ </div>
115
+
116
+ <button
117
+ type="button"
118
+ disabled={isDisabled}
119
+ onClick={() => onPurchase(pack.packId)}
120
+ className={cn(
121
+ "mt-4 w-full rounded-md px-3 py-1.5 text-xs font-medium transition-colors",
122
+ "bg-primary text-primary-foreground",
123
+ "hover:bg-primary/90",
124
+ "disabled:pointer-events-none disabled:opacity-50",
125
+ )}
126
+ aria-busy={isPurchasing}
127
+ >
128
+ {isPurchasing ? "Redirecting\u2026" : "Buy"}
129
+ </button>
130
+ </div>
131
+ );
132
+ }
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+ import { AlertTriangle } from "lucide-react";
5
+ import { formatCreditBalance } from "./format";
6
+
7
+ /** Props for {@link LowBalanceBanner}. */
8
+ export interface LowBalanceBannerProps {
9
+ /** Available balance in micro-USD. */
10
+ readonly availableMicros: bigint;
11
+ /** Threshold in micro-USD below which the warning is shown. */
12
+ readonly thresholdMicros: bigint;
13
+ /** Additional CSS class names. */
14
+ readonly className?: string;
15
+ }
16
+
17
+ /**
18
+ * Conditional banner that warns when the available credit balance
19
+ * is below the configured threshold.
20
+ *
21
+ * Renders nothing when the balance is healthy. Not dismissible —
22
+ * it reflects a real account state that the user should address.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <LowBalanceBanner
27
+ * availableMicros={account.balance.availableMicros}
28
+ * thresholdMicros={account.lowBalanceThresholdMicros}
29
+ * />
30
+ * ```
31
+ */
32
+ export function LowBalanceBanner({
33
+ availableMicros,
34
+ thresholdMicros,
35
+ className,
36
+ }: LowBalanceBannerProps) {
37
+ if (availableMicros >= thresholdMicros) return null;
38
+
39
+ const isZeroOrNegative = availableMicros <= BigInt(0);
40
+
41
+ return (
42
+ <div
43
+ role="alert"
44
+ className={cn(
45
+ "flex items-start gap-2.5 rounded-lg border px-3.5 py-3 text-xs",
46
+ isZeroOrNegative
47
+ ? "border-destructive/30 bg-destructive/5 text-destructive"
48
+ : "border-warning/30 bg-warning/5 text-warning-foreground",
49
+ className,
50
+ )}
51
+ >
52
+ <AlertTriangle className="mt-0.5 size-3.5 shrink-0" aria-hidden="true" />
53
+ <div>
54
+ <p className="font-medium">
55
+ {isZeroOrNegative
56
+ ? "Credit balance exhausted"
57
+ : "Low credit balance"}
58
+ </p>
59
+ <p className="mt-0.5 opacity-80">
60
+ {isZeroOrNegative
61
+ ? "Your credit balance is zero. Purchase credits to continue running agent executions."
62
+ : `Your balance (${formatCreditBalance(availableMicros)}) is below the warning threshold. Consider purchasing additional credits.`}
63
+ </p>
64
+ </div>
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,133 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+ import type { PaymentMethodSummary } from "@stigmer/protos/ai/stigmer/billing/v1/billing_account_pb";
5
+ import { BillingAccountStatus } from "@stigmer/protos/ai/stigmer/billing/v1/enum_pb";
6
+
7
+ /** Props for {@link PaymentMethodCard}. */
8
+ export interface PaymentMethodCardProps {
9
+ /** The saved payment method, or undefined if none is on file. */
10
+ readonly paymentMethod?: PaymentMethodSummary;
11
+ /** Account status — portal button disabled when suspended or closed. */
12
+ readonly accountStatus: BillingAccountStatus;
13
+ /** `true` while the portal session is being created. */
14
+ readonly isPortalLoading?: boolean;
15
+ /** Called when the user clicks "Manage payment methods". */
16
+ readonly onManage: () => void;
17
+ /** Additional CSS class names. */
18
+ readonly className?: string;
19
+ }
20
+
21
+ const BRAND_DISPLAY: Record<string, string> = {
22
+ visa: "Visa",
23
+ mastercard: "Mastercard",
24
+ amex: "American Express",
25
+ discover: "Discover",
26
+ diners: "Diners Club",
27
+ jcb: "JCB",
28
+ unionpay: "UnionPay",
29
+ };
30
+
31
+ function formatBrand(brand: string): string {
32
+ return BRAND_DISPLAY[brand.toLowerCase()] ?? brand;
33
+ }
34
+
35
+ function formatExpiry(month: number, year: number): string {
36
+ const m = String(month).padStart(2, "0");
37
+ const y = String(year).slice(-2);
38
+ return `${m}/${y}`;
39
+ }
40
+
41
+ /**
42
+ * Displays the saved payment method with a button to open the
43
+ * Stripe Customer Portal for management.
44
+ *
45
+ * Shows card brand, last 4 digits, and expiry when a payment method
46
+ * is on file. Shows an empty state prompt when none exists. The
47
+ * "Manage" button opens the Stripe Customer Portal where users can
48
+ * add, update, or remove payment methods.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * <PaymentMethodCard
53
+ * paymentMethod={account.defaultPaymentMethod}
54
+ * accountStatus={account.status}
55
+ * onManage={() => openPortal(orgId)}
56
+ * />
57
+ * ```
58
+ */
59
+ export function PaymentMethodCard({
60
+ paymentMethod,
61
+ accountStatus,
62
+ isPortalLoading,
63
+ onManage,
64
+ className,
65
+ }: PaymentMethodCardProps) {
66
+ const isAccountActive =
67
+ accountStatus === BillingAccountStatus.billing_account_active;
68
+ const hasPm =
69
+ paymentMethod != null && paymentMethod.paymentMethodId !== "";
70
+
71
+ return (
72
+ <div
73
+ className={cn(
74
+ "rounded-lg border border-border bg-card px-4 py-4",
75
+ className,
76
+ )}
77
+ >
78
+ <div className="flex items-center justify-between">
79
+ <h3 className="text-xs font-semibold text-foreground">
80
+ Payment Method
81
+ </h3>
82
+ {hasPm && (
83
+ <button
84
+ type="button"
85
+ disabled={!isAccountActive || isPortalLoading}
86
+ onClick={onManage}
87
+ className={cn(
88
+ "rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
89
+ "text-muted-foreground hover:bg-accent hover:text-foreground",
90
+ "disabled:pointer-events-none disabled:opacity-50",
91
+ )}
92
+ aria-busy={isPortalLoading}
93
+ >
94
+ {isPortalLoading ? "Opening\u2026" : "Manage"}
95
+ </button>
96
+ )}
97
+ </div>
98
+
99
+ {hasPm ? (
100
+ <div className="mt-2 flex items-center gap-3">
101
+ <CardBrandIcon brand={paymentMethod.brand} />
102
+ <div>
103
+ <div className="text-sm font-medium text-foreground">
104
+ {formatBrand(paymentMethod.brand)} ····{" "}
105
+ {paymentMethod.last4}
106
+ </div>
107
+ <div className="text-xs text-muted-foreground">
108
+ Expires{" "}
109
+ {formatExpiry(paymentMethod.expMonth, paymentMethod.expYear)}
110
+ </div>
111
+ </div>
112
+ </div>
113
+ ) : (
114
+ <div className="mt-2">
115
+ <p className="text-xs text-muted-foreground">
116
+ No payment method on file. A card will be saved automatically
117
+ when you purchase your first credit pack.
118
+ </p>
119
+ </div>
120
+ )}
121
+ </div>
122
+ );
123
+ }
124
+
125
+ function CardBrandIcon({ brand }: { brand: string }) {
126
+ return (
127
+ <div className="flex size-9 items-center justify-center rounded-md border border-border bg-background">
128
+ <span className="text-[0.6rem] font-bold uppercase text-muted-foreground">
129
+ {brand.slice(0, 4)}
130
+ </span>
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,54 @@
1
+ /** Metadata for a self-serve credit pack. */
2
+ export interface CreditPackInfo {
3
+ /** Stable identifier matching the backend catalog (e.g., "starter"). */
4
+ readonly packId: string;
5
+ /** Display name shown in the UI. */
6
+ readonly displayName: string;
7
+ /** Brief description of the pack's positioning. */
8
+ readonly description: string;
9
+ /** Price in USD cents (e.g., 1000 = $10.00). */
10
+ readonly priceCents: number;
11
+ /** Number of credits granted (1 credit = $0.01 USD). */
12
+ readonly credits: number;
13
+ }
14
+
15
+ /**
16
+ * Static credit pack catalog matching the backend `CreditPackCatalog`.
17
+ *
18
+ * Packs are static product entries, not database-backed resources.
19
+ * At launch, credits equal the dollar value (no volume bonus).
20
+ */
21
+ export const CREDIT_PACKS: readonly CreditPackInfo[] = [
22
+ {
23
+ packId: "starter",
24
+ displayName: "Starter",
25
+ description: "For trying things out",
26
+ priceCents: 1_000,
27
+ credits: 1_000,
28
+ },
29
+ {
30
+ packId: "growth",
31
+ displayName: "Growth",
32
+ description: "For growing teams",
33
+ priceCents: 5_000,
34
+ credits: 5_000,
35
+ },
36
+ {
37
+ packId: "team",
38
+ displayName: "Team",
39
+ description: "For production workloads",
40
+ priceCents: 20_000,
41
+ credits: 20_000,
42
+ },
43
+ ];
44
+
45
+ /** Format a cent amount as a dollar string (e.g., 1000 -> "$10"). */
46
+ export function formatPackPrice(cents: number): string {
47
+ const dollars = cents / 100;
48
+ return dollars % 1 === 0 ? `$${dollars}` : `$${dollars.toFixed(2)}`;
49
+ }
50
+
51
+ /** Format a credit count with commas (e.g., 5000 -> "5,000"). */
52
+ export function formatCreditCount(credits: number): string {
53
+ return credits.toLocaleString("en-US");
54
+ }