@opengeni/react 1.3.0 → 1.4.2

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 (240) hide show
  1. package/README.md +29 -1
  2. package/dist/accounts.js +2 -0
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/artifacts-document.js +5 -4
  5. package/dist/artifacts-presentation.js +5 -4
  6. package/dist/artifacts-spreadsheet.js +4 -3
  7. package/dist/artifacts.js +14 -13
  8. package/dist/artifacts.js.map +1 -1
  9. package/dist/browser-viewer-WGJ43YGI.js +17 -0
  10. package/dist/chat-interactive-block-ZFHOKTMS.js +12 -0
  11. package/dist/{chunk-WEHMPOXK.js → chunk-2DDJ6YH6.js} +3 -1
  12. package/dist/{chunk-WEHMPOXK.js.map → chunk-2DDJ6YH6.js.map} +1 -1
  13. package/dist/{chunk-C5X5AJXK.js → chunk-2HVLQ26H.js} +5 -4
  14. package/dist/{chunk-C5X5AJXK.js.map → chunk-2HVLQ26H.js.map} +1 -1
  15. package/dist/{chunk-A4UJB2MM.js → chunk-32CGLXW2.js} +4 -3
  16. package/dist/{chunk-A4UJB2MM.js.map → chunk-32CGLXW2.js.map} +1 -1
  17. package/dist/{chunk-5CVXZA3Y.js → chunk-45NUOSXQ.js} +3 -2
  18. package/dist/{chunk-5CVXZA3Y.js.map → chunk-45NUOSXQ.js.map} +1 -1
  19. package/dist/{chunk-WUISZ3WU.js → chunk-53KJBGPZ.js} +5 -4
  20. package/dist/{chunk-WUISZ3WU.js.map → chunk-53KJBGPZ.js.map} +1 -1
  21. package/dist/{chunk-TMYGL6X5.js → chunk-55DXD22O.js} +73 -7
  22. package/dist/chunk-55DXD22O.js.map +1 -0
  23. package/dist/{chunk-JT4JW7BC.js → chunk-5I4VRWBB.js} +3 -1
  24. package/dist/{chunk-JT4JW7BC.js.map → chunk-5I4VRWBB.js.map} +1 -1
  25. package/dist/{chunk-UK4TINKT.js → chunk-B27E3V37.js} +3 -1
  26. package/dist/{chunk-UK4TINKT.js.map → chunk-B27E3V37.js.map} +1 -1
  27. package/dist/{chunk-J6DBS4T3.js → chunk-BNKV5ZLF.js} +5 -4
  28. package/dist/{chunk-J6DBS4T3.js.map → chunk-BNKV5ZLF.js.map} +1 -1
  29. package/dist/{chunk-YM2DE7BQ.js → chunk-C5BF4L26.js} +13 -12
  30. package/dist/{chunk-YM2DE7BQ.js.map → chunk-C5BF4L26.js.map} +1 -1
  31. package/dist/{chunk-YBXQDQFX.js → chunk-C7CMD56T.js} +6 -5
  32. package/dist/{chunk-YBXQDQFX.js.map → chunk-C7CMD56T.js.map} +1 -1
  33. package/dist/{chunk-UGLEYEMP.js → chunk-CM7LVUKC.js} +3 -1
  34. package/dist/{chunk-UGLEYEMP.js.map → chunk-CM7LVUKC.js.map} +1 -1
  35. package/dist/{chunk-PGSX7UCP.js → chunk-CREMJMVN.js} +1502 -833
  36. package/dist/chunk-CREMJMVN.js.map +1 -0
  37. package/dist/{chunk-JVYQRRC4.js → chunk-CVYCLFMH.js} +3 -1
  38. package/dist/{chunk-JVYQRRC4.js.map → chunk-CVYCLFMH.js.map} +1 -1
  39. package/dist/{chunk-Y2PRMYLC.js → chunk-G3U2A6GS.js} +3 -2
  40. package/dist/{chunk-Y2PRMYLC.js.map → chunk-G3U2A6GS.js.map} +1 -1
  41. package/dist/{chunk-WWEUO2PL.js → chunk-G6YRCIYJ.js} +202 -14
  42. package/dist/chunk-G6YRCIYJ.js.map +1 -0
  43. package/dist/{chunk-B3V6TG77.js → chunk-GC3PH6N7.js} +3 -1
  44. package/dist/{chunk-B3V6TG77.js.map → chunk-GC3PH6N7.js.map} +1 -1
  45. package/dist/{chunk-FZCP527Y.js → chunk-HXYOSSQO.js} +3 -2
  46. package/dist/{chunk-FZCP527Y.js.map → chunk-HXYOSSQO.js.map} +1 -1
  47. package/dist/{chunk-BRM6FVZA.js → chunk-IEE7WCLN.js} +4 -3
  48. package/dist/{chunk-BRM6FVZA.js.map → chunk-IEE7WCLN.js.map} +1 -1
  49. package/dist/{chunk-DYWBUX5Q.js → chunk-IMSUVYQB.js} +6 -5
  50. package/dist/{chunk-DYWBUX5Q.js.map → chunk-IMSUVYQB.js.map} +1 -1
  51. package/dist/{chunk-CY7WK5FD.js → chunk-IUHHPMAX.js} +203 -46
  52. package/dist/chunk-IUHHPMAX.js.map +1 -0
  53. package/dist/{chunk-SGKD2OCN.js → chunk-IYQCWGU5.js} +6 -5
  54. package/dist/{chunk-SGKD2OCN.js.map → chunk-IYQCWGU5.js.map} +1 -1
  55. package/dist/{chunk-J2QWVSQY.js → chunk-JH7V627O.js} +8 -7
  56. package/dist/{chunk-J2QWVSQY.js.map → chunk-JH7V627O.js.map} +1 -1
  57. package/dist/{chunk-EESD3PY3.js → chunk-JHTIMBOL.js} +6 -5
  58. package/dist/{chunk-EESD3PY3.js.map → chunk-JHTIMBOL.js.map} +1 -1
  59. package/dist/{chunk-LNIRAOA3.js → chunk-L2J3PU7A.js} +7 -6
  60. package/dist/{chunk-LNIRAOA3.js.map → chunk-L2J3PU7A.js.map} +1 -1
  61. package/dist/{chunk-ZYRGEKWS.js → chunk-LJUMYUBO.js} +3 -1
  62. package/dist/{chunk-ZYRGEKWS.js.map → chunk-LJUMYUBO.js.map} +1 -1
  63. package/dist/{chunk-4RVYOY4O.js → chunk-LSW2OJVV.js} +4 -3
  64. package/dist/{chunk-4RVYOY4O.js.map → chunk-LSW2OJVV.js.map} +1 -1
  65. package/dist/{chunk-UTRTG6LH.js → chunk-LVS3MH5T.js} +3 -1
  66. package/dist/{chunk-UTRTG6LH.js.map → chunk-LVS3MH5T.js.map} +1 -1
  67. package/dist/chunk-M357MHH3.js +2 -0
  68. package/dist/{chunk-GYVRFZDY.js → chunk-MDIXFKA3.js} +9 -8
  69. package/dist/{chunk-GYVRFZDY.js.map → chunk-MDIXFKA3.js.map} +1 -1
  70. package/dist/{chunk-WRHIZYZF.js → chunk-MEQM4LI3.js} +6 -5
  71. package/dist/{chunk-WRHIZYZF.js.map → chunk-MEQM4LI3.js.map} +1 -1
  72. package/dist/chunk-NX4ADH73.js +14 -0
  73. package/dist/chunk-NX4ADH73.js.map +1 -0
  74. package/dist/{chunk-XCHWDJ22.js → chunk-OGTTUYNT.js} +3 -1
  75. package/dist/{chunk-XCHWDJ22.js.map → chunk-OGTTUYNT.js.map} +1 -1
  76. package/dist/{chunk-GIMRELBW.js → chunk-OYQQJL3J.js} +3 -1
  77. package/dist/{chunk-GIMRELBW.js.map → chunk-OYQQJL3J.js.map} +1 -1
  78. package/dist/{chunk-RSI7XYXL.js → chunk-PBCZL4JP.js} +6 -5
  79. package/dist/{chunk-RSI7XYXL.js.map → chunk-PBCZL4JP.js.map} +1 -1
  80. package/dist/{chunk-I7IWRQLN.js → chunk-PHBFJCEU.js} +17 -11
  81. package/dist/chunk-PHBFJCEU.js.map +1 -0
  82. package/dist/{chunk-3CTRFMAK.js → chunk-PKCAYHDC.js} +5 -4
  83. package/dist/{chunk-3CTRFMAK.js.map → chunk-PKCAYHDC.js.map} +1 -1
  84. package/dist/{chunk-K3HQAKQX.js → chunk-PMV53IMM.js} +3 -1
  85. package/dist/{chunk-K3HQAKQX.js.map → chunk-PMV53IMM.js.map} +1 -1
  86. package/dist/{chunk-YQEAA6LT.js → chunk-QHUE7E4N.js} +3 -2
  87. package/dist/{chunk-YQEAA6LT.js.map → chunk-QHUE7E4N.js.map} +1 -1
  88. package/dist/{chunk-RC6BZ44Q.js → chunk-QMMCJGFI.js} +4 -3
  89. package/dist/{chunk-RC6BZ44Q.js.map → chunk-QMMCJGFI.js.map} +1 -1
  90. package/dist/{chunk-YV4L3ILF.js → chunk-QV3DJVRZ.js} +3 -1
  91. package/dist/{chunk-YV4L3ILF.js.map → chunk-QV3DJVRZ.js.map} +1 -1
  92. package/dist/{chunk-447JLCBU.js → chunk-S2QH6EZ5.js} +3 -1
  93. package/dist/{chunk-447JLCBU.js.map → chunk-S2QH6EZ5.js.map} +1 -1
  94. package/dist/{chunk-PNY3JKBJ.js → chunk-SFH43JT2.js} +5 -4
  95. package/dist/{chunk-PNY3JKBJ.js.map → chunk-SFH43JT2.js.map} +1 -1
  96. package/dist/{chunk-ERGYJIBB.js → chunk-T46QZH3E.js} +4 -2
  97. package/dist/{chunk-ERGYJIBB.js.map → chunk-T46QZH3E.js.map} +1 -1
  98. package/dist/{chunk-5J4FOW6Q.js → chunk-TNEZNMSN.js} +3 -1
  99. package/dist/{chunk-5J4FOW6Q.js.map → chunk-TNEZNMSN.js.map} +1 -1
  100. package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
  101. package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
  102. package/dist/{chunk-742CZ3L3.js → chunk-UDPH5SLV.js} +19 -202
  103. package/dist/chunk-UDPH5SLV.js.map +1 -0
  104. package/dist/{chunk-HDJWDK7W.js → chunk-VCSERYXH.js} +10 -9
  105. package/dist/{chunk-HDJWDK7W.js.map → chunk-VCSERYXH.js.map} +1 -1
  106. package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
  107. package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
  108. package/dist/{chunk-5F4WR7X7.js → chunk-XS67YG5R.js} +3 -1
  109. package/dist/{chunk-5F4WR7X7.js.map → chunk-XS67YG5R.js.map} +1 -1
  110. package/dist/{chunk-TKOTKRWY.js → chunk-XTD7TZHW.js} +3 -1
  111. package/dist/{chunk-TKOTKRWY.js.map → chunk-XTD7TZHW.js.map} +1 -1
  112. package/dist/{chunk-A2PI6UMY.js → chunk-YSRBFJMQ.js} +3 -1
  113. package/dist/{chunk-A2PI6UMY.js.map → chunk-YSRBFJMQ.js.map} +1 -1
  114. package/dist/chunk-YVFRBXST.js +1420 -0
  115. package/dist/chunk-YVFRBXST.js.map +1 -0
  116. package/dist/{chunk-SCOTPE2I.js → chunk-YYLBXXTE.js} +3 -1
  117. package/dist/{chunk-SCOTPE2I.js.map → chunk-YYLBXXTE.js.map} +1 -1
  118. package/dist/{chunk-RHREZ7BO.js → chunk-ZUWF4NF7.js} +3 -1
  119. package/dist/{chunk-RHREZ7BO.js.map → chunk-ZUWF4NF7.js.map} +1 -1
  120. package/dist/clipboard.js +3 -2
  121. package/dist/components/composer-transcription-control.d.ts +7 -0
  122. package/dist/components/open-geni-chat.d.ts +6 -2
  123. package/dist/components/session-conversation.d.ts +31 -19
  124. package/dist/components/session-list.d.ts +4 -1
  125. package/dist/components/tool-action-review.d.ts +29 -2
  126. package/dist/components/tool-review-history.d.ts +8 -4
  127. package/dist/composer-voice-events.d.ts +8 -0
  128. package/dist/composer.js +15 -13
  129. package/dist/computer-viewer-46UIN7F3.js +20 -0
  130. package/dist/connect.js +15 -12
  131. package/dist/connect.js.map +1 -1
  132. package/dist/desktop.js +6 -5
  133. package/dist/desktop.js.map +1 -1
  134. package/dist/diffs.js +2 -1
  135. package/dist/diffs.js.map +1 -1
  136. package/dist/editor.js +4 -3
  137. package/dist/editor.js.map +1 -1
  138. package/dist/embedded-voice-NIL4RDX3.js +69 -0
  139. package/dist/embedded-voice-NIL4RDX3.js.map +1 -0
  140. package/dist/{fleet-decision-row-YZ3ZKWMM.js → fleet-decision-row-SI63O5V4.js} +3 -1
  141. package/dist/{fleet-decision-row-YZ3ZKWMM.js.map → fleet-decision-row-SI63O5V4.js.map} +1 -1
  142. package/dist/hooks/use-client-config-flags.d.ts +43 -0
  143. package/dist/hooks/use-realtime-voice-models.d.ts +9 -0
  144. package/dist/hooks/use-voice-input.d.ts +8 -1
  145. package/dist/index.d.ts +3 -3
  146. package/dist/index.js +60 -48
  147. package/dist/index.js.map +1 -1
  148. package/dist/interaction.js +14 -13
  149. package/dist/interaction.js.map +1 -1
  150. package/dist/{latest-question-YTOBHCWR.js → latest-question-2POFPYNB.js} +5 -4
  151. package/dist/{latest-question-YTOBHCWR.js.map → latest-question-2POFPYNB.js.map} +1 -1
  152. package/dist/machines.js +8 -7
  153. package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-DQ4DRNVT.js} +3 -1
  154. package/dist/{markdown-table-layout-QXSLK4BY.js.map → markdown-table-layout-DQ4DRNVT.js.map} +1 -1
  155. package/dist/model-policy-picker-menu-WRNSRFQC.js +15 -0
  156. package/dist/model-policy.js +2 -1
  157. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  158. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  159. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  160. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  161. package/dist/{platform-activity-row-627BFZ46.js → platform-activity-row-PJJVATTZ.js} +3 -1
  162. package/dist/{platform-activity-row-627BFZ46.js.map → platform-activity-row-PJJVATTZ.js.map} +1 -1
  163. package/dist/{queue-surface-implementation-ZHFI5JBP.js → queue-surface-implementation-67FS7FUI.js} +10 -9
  164. package/dist/{queue-surface-implementation-ZHFI5JBP.js.map → queue-surface-implementation-67FS7FUI.js.map} +1 -1
  165. package/dist/realtime/embedded-voice.d.ts +20 -0
  166. package/dist/realtime/realtime-control.d.ts +2 -0
  167. package/dist/realtime.js +22 -1332
  168. package/dist/realtime.js.map +1 -1
  169. package/dist/sandbox-files-34CJJNOY.js +21 -0
  170. package/dist/session-capability-policy.d.ts +20 -1
  171. package/dist/session-ui.js +32 -30
  172. package/dist/session-ui.js.map +1 -1
  173. package/dist/session.js +12 -11
  174. package/dist/sites.js +2 -1
  175. package/dist/terminal.js +6 -5
  176. package/dist/terminal.js.map +1 -1
  177. package/dist/timeline/index.d.ts +2 -0
  178. package/dist/timeline/retained-loaders.d.ts +27 -0
  179. package/dist/{timeline-annotation-markers-GNDHTGBD.js → timeline-annotation-markers-U3G66KUD.js} +5 -4
  180. package/dist/{timeline-annotation-markers-GNDHTGBD.js.map → timeline-annotation-markers-U3G66KUD.js.map} +1 -1
  181. package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-BI7QTPTZ.js} +4 -3
  182. package/dist/{timeline-annotation-selection-DMLMM2TX.js.map → timeline-annotation-selection-BI7QTPTZ.js.map} +1 -1
  183. package/dist/{timeline-annotations-dialog-ELRDBHTS.js → timeline-annotations-dialog-FOMHSDTE.js} +7 -6
  184. package/dist/{timeline-annotations-dialog-ELRDBHTS.js.map → timeline-annotations-dialog-FOMHSDTE.js.map} +1 -1
  185. package/dist/usage.js +6 -5
  186. package/dist/usage.js.map +1 -1
  187. package/dist/voice-recording-owner.d.ts +7 -0
  188. package/package.json +3 -3
  189. package/src/components/approval-surface.tsx +190 -79
  190. package/src/components/artifacts/session-artifact-viewer.tsx +4 -2
  191. package/src/components/composer-transcription-control.tsx +121 -25
  192. package/src/components/composer.tsx +1 -0
  193. package/src/components/markdown.tsx +7 -2
  194. package/src/components/message-timeline.tsx +10 -1
  195. package/src/components/open-geni-chat.tsx +53 -13
  196. package/src/components/session-conversation.tsx +273 -63
  197. package/src/components/session-list.tsx +9 -2
  198. package/src/components/session-proxy-scope.tsx +26 -5
  199. package/src/components/tool-action-review.tsx +502 -239
  200. package/src/components/tool-review-history.tsx +18 -43
  201. package/src/composer-voice-events.ts +12 -0
  202. package/src/connection-discovery.tsx +4 -1
  203. package/src/hooks/use-client-config-flags.ts +110 -0
  204. package/src/hooks/use-goal.ts +12 -1
  205. package/src/hooks/use-realtime-voice-models.ts +67 -0
  206. package/src/hooks/use-session-events.ts +19 -16
  207. package/src/hooks/use-transcription.ts +6 -0
  208. package/src/hooks/use-voice-input.ts +115 -18
  209. package/src/index.ts +9 -0
  210. package/src/realtime/embedded-voice.tsx +81 -0
  211. package/src/realtime/realtime-control.tsx +109 -17
  212. package/src/session-capability-policy.ts +119 -6
  213. package/src/timeline/index.ts +10 -0
  214. package/src/timeline/retained-loaders.ts +86 -0
  215. package/src/timeline/tool-display-name.ts +5 -1
  216. package/src/timeline/tool-renderers.tsx +143 -10
  217. package/src/voice-recording-owner.ts +22 -0
  218. package/styles/compiled.css +135 -37
  219. package/styles/index.css +49 -0
  220. package/styles/responsive.css +61 -4
  221. package/styles/session-capability.css +1 -1
  222. package/styles/tokens.css +1 -1
  223. package/dist/browser-viewer-SHYKQRNX.js +0 -16
  224. package/dist/chat-interactive-block-UGBTNB3L.js +0 -11
  225. package/dist/chunk-742CZ3L3.js.map +0 -1
  226. package/dist/chunk-CY7WK5FD.js.map +0 -1
  227. package/dist/chunk-I7IWRQLN.js.map +0 -1
  228. package/dist/chunk-IWIDV7SB.js +0 -1
  229. package/dist/chunk-PGSX7UCP.js.map +0 -1
  230. package/dist/chunk-TMYGL6X5.js.map +0 -1
  231. package/dist/chunk-WWEUO2PL.js.map +0 -1
  232. package/dist/computer-viewer-6SRGWRUX.js +0 -19
  233. package/dist/model-policy-picker-menu-DXHUL73H.js +0 -14
  234. package/dist/sandbox-files-GORZOUUJ.js +0 -20
  235. /package/dist/{browser-viewer-SHYKQRNX.js.map → browser-viewer-WGJ43YGI.js.map} +0 -0
  236. /package/dist/{chat-interactive-block-UGBTNB3L.js.map → chat-interactive-block-ZFHOKTMS.js.map} +0 -0
  237. /package/dist/{chunk-IWIDV7SB.js.map → chunk-M357MHH3.js.map} +0 -0
  238. /package/dist/{computer-viewer-6SRGWRUX.js.map → computer-viewer-46UIN7F3.js.map} +0 -0
  239. /package/dist/{model-policy-picker-menu-DXHUL73H.js.map → model-policy-picker-menu-WRNSRFQC.js.map} +0 -0
  240. /package/dist/{sandbox-files-GORZOUUJ.js.map → sandbox-files-34CJJNOY.js.map} +0 -0
@@ -1,31 +1,36 @@
1
+ "use client";
1
2
  import {
2
3
  shouldSteerOnKey,
3
4
  shouldSubmitOnKey
4
- } from "./chunk-RSI7XYXL.js";
5
+ } from "./chunk-PBCZL4JP.js";
5
6
  import {
6
7
  Tooltip,
7
8
  TooltipContent,
8
9
  TooltipProvider,
9
10
  TooltipTrigger,
10
11
  useLightboxOptional
11
- } from "./chunk-BRM6FVZA.js";
12
+ } from "./chunk-IEE7WCLN.js";
13
+ import {
14
+ COMPOSER_VOICE_INPUT_START_EVENT,
15
+ composerVoiceEventScope
16
+ } from "./chunk-NX4ADH73.js";
12
17
  import {
13
18
  groupPickerRowsByBillingClass
14
- } from "./chunk-5F4WR7X7.js";
19
+ } from "./chunk-XS67YG5R.js";
15
20
  import {
16
21
  ComposerResponsiveContext
17
- } from "./chunk-VZTQ73XT.js";
22
+ } from "./chunk-XEA5BCMA.js";
18
23
  import {
19
24
  composerSubmissionErrorMessage,
20
25
  formatBytes,
21
26
  formatRelativeTime
22
- } from "./chunk-UTRTG6LH.js";
27
+ } from "./chunk-LVS3MH5T.js";
23
28
  import {
24
29
  useErrorMessage
25
- } from "./chunk-UGLEYEMP.js";
30
+ } from "./chunk-CM7LVUKC.js";
26
31
  import {
27
32
  cn
28
- } from "./chunk-22KP6LR5.js";
33
+ } from "./chunk-TXS65BB2.js";
29
34
 
30
35
  // src/workstream-control-event.ts
31
36
  var OPEN_WORKSTREAM_CONTROL_EVENT = "opengeni:open-workstream-control";
@@ -1229,6 +1234,7 @@ var Surface = forwardRef(function ComposerSurface({ className, children, ...prop
1229
1234
  return /* @__PURE__ */ jsxs3(
1230
1235
  "div",
1231
1236
  {
1237
+ "data-og-composer-surface": "",
1232
1238
  ...props,
1233
1239
  ref,
1234
1240
  onDragOver: controller.attachments ? controller.handleDragOver : void 0,
@@ -2850,6 +2856,12 @@ function classifyStartError(error) {
2850
2856
  }
2851
2857
  function normalizeTranscriptionErrorCode(code) {
2852
2858
  switch (code) {
2859
+ case "insufficient_credits":
2860
+ case "allowance_exhausted":
2861
+ case "monthly_model_cost_limit":
2862
+ case "unavailable":
2863
+ case "too_large":
2864
+ case "invalid_audio":
2853
2865
  case "permission_denied":
2854
2866
  case "not_supported":
2855
2867
  case "network":
@@ -3012,6 +3024,21 @@ async function createUnderlyingOwnerLease() {
3012
3024
  writeSessionOwnerId(ownerId);
3013
3025
  return { ownerId, release: () => void 0 };
3014
3026
  }
3027
+ async function liveVoiceRecordingOwnerIds() {
3028
+ if (!hasWebLocks() || typeof navigator.locks.query !== "function") return null;
3029
+ try {
3030
+ const snapshot = await navigator.locks.query();
3031
+ const live = /* @__PURE__ */ new Set();
3032
+ for (const lock of [...snapshot.held ?? [], ...snapshot.pending ?? []]) {
3033
+ if (lock.name?.startsWith(VOICE_RECORDING_OWNER_LOCK_PREFIX)) {
3034
+ live.add(lock.name.slice(VOICE_RECORDING_OWNER_LOCK_PREFIX.length));
3035
+ }
3036
+ }
3037
+ return live;
3038
+ } catch {
3039
+ return null;
3040
+ }
3041
+ }
3015
3042
  function hasWebLocks() {
3016
3043
  return typeof navigator !== "undefined" && navigator.locks !== null && navigator.locks !== void 0 && typeof navigator.locks.request === "function";
3017
3044
  }
@@ -3133,6 +3160,7 @@ var VOICE_RECORDING_TIMESLICE_MILLISECONDS = 5e3;
3133
3160
  var VOICE_RECORDING_OWNER_HEARTBEAT_MILLISECONDS = 5e3;
3134
3161
  var VOICE_RECORDING_OWNER_STALE_MILLISECONDS = 3e4;
3135
3162
  var VOICE_RECORDING_CLIENT_MAX_DURATION_SECONDS = 600;
3163
+ var VOICE_RECORDING_DISCARD_ON_CANCEL_MAX_MILLISECONDS = 3e3;
3136
3164
  var VOICE_RECORDING_RESUMABLE_CLIENT_MAX_DURATION_SECONDS = 8 * 60 * 60;
3137
3165
  var VOICE_RECORDING_RECOVERY_STATUS_POLL_MILLISECONDS = 2e3;
3138
3166
  var VOICE_RECORDING_RECOVERY_MAX_MUTATION_DELAY_MILLISECONDS = 3e4;
@@ -3196,6 +3224,8 @@ function useVoiceInput({
3196
3224
  const recorderRef = useRef4(null);
3197
3225
  const streamRef = useRef4(null);
3198
3226
  const timerRef = useRef4(null);
3227
+ const holdOnStopRef = useRef4(false);
3228
+ const recordingStartedAtRef = useRef4(0);
3199
3229
  const automaticRetryTimerRef = useRef4(null);
3200
3230
  const automaticRetryAttemptRef = useRef4(0);
3201
3231
  const ownerHeartbeatTimerRef = useRef4(null);
@@ -3335,15 +3365,17 @@ function useVoiceInput({
3335
3365
  const active = () => generation === generationRef.current && workspaceIdRef.current === workspaceId;
3336
3366
  const [store, ownerId] = await Promise.all([ensureStore(), ensureOwnerId()]);
3337
3367
  if (!active()) return;
3338
- const staleBefore = new Date(
3368
+ const heartbeatStaleBefore = new Date(
3339
3369
  readNow().getTime() - VOICE_RECORDING_OWNER_STALE_MILLISECONDS
3340
3370
  ).toISOString();
3341
- await store.cleanupHandedOffManifests({ ownerId, staleBefore }).catch(() => void 0);
3371
+ await store.cleanupHandedOffManifests({ ownerId, staleBefore: heartbeatStaleBefore }).catch(() => void 0);
3342
3372
  if (!active()) return;
3343
- const manifests = await store.listRecoverableManifests(workspaceId, {
3344
- ownerId,
3345
- staleBefore
3346
- });
3373
+ const liveOwners = createOwnerIdRef.current === void 0 ? await liveVoiceRecordingOwnerIds() : null;
3374
+ if (!active()) return;
3375
+ const staleBefore = liveOwners ? readNow().toISOString() : heartbeatStaleBefore;
3376
+ const manifests = (await store.listRecoverableManifests(workspaceId, { ownerId, staleBefore })).filter(
3377
+ (manifest) => !liveOwners || !manifest.ownerId || manifest.ownerId === ownerId || !liveOwners.has(manifest.ownerId) || !manifest.ownerHeartbeatAt || manifest.ownerHeartbeatAt <= heartbeatStaleBefore
3378
+ );
3347
3379
  if (!active()) return;
3348
3380
  for (const candidate of manifests) {
3349
3381
  try {
@@ -3365,10 +3397,13 @@ function useVoiceInput({
3365
3397
  }
3366
3398
  rememberManifest(claimed);
3367
3399
  beginOwnerHeartbeat(claimed);
3400
+ const transcriptReady = claimed.finalizationState === "transcript-ready" && claimed.transcriptText !== null;
3368
3401
  setStatus(
3369
- claimed.finalizationState === "transcript-ready" && claimed.transcriptText !== null ? "transcript-ready" : claimed.recoveryMode === "automatic" ? "retrying" : "recovered"
3402
+ transcriptReady ? "transcript-ready" : claimed.recoveryMode === "automatic" ? "retrying" : "recovered"
3403
+ );
3404
+ setError(
3405
+ transcriptReady && candidate.handoffMode === "append" ? "handoff_uncertain" : null
3370
3406
  );
3371
- setError(null);
3372
3407
  return;
3373
3408
  } catch (reason) {
3374
3409
  if (reason instanceof VoiceRecordingOwnedError) continue;
@@ -3459,7 +3494,7 @@ function useVoiceInput({
3459
3494
  automaticRetryAttemptRef.current = 0;
3460
3495
  if (ready.handoffMode === "explicit") {
3461
3496
  setStatus("transcript-ready");
3462
- setError("handoff_uncertain");
3497
+ setError(null);
3463
3498
  focusInput();
3464
3499
  return;
3465
3500
  }
@@ -3735,6 +3770,15 @@ function useVoiceInput({
3735
3770
  await preserveForRetry(retained, attemptCaptureLimitError, generation);
3736
3771
  return;
3737
3772
  }
3773
+ if (holdOnStopRef.current) {
3774
+ holdOnStopRef.current = false;
3775
+ const retained = await updateManifestBestEffort(stopped, {
3776
+ recoveryMode: "manual",
3777
+ handoffMode: "explicit"
3778
+ });
3779
+ await preserveForRetry(retained, null, generation);
3780
+ return;
3781
+ }
3738
3782
  await finalizePersistedRecording(generation);
3739
3783
  });
3740
3784
  };
@@ -3743,6 +3787,8 @@ function useVoiceInput({
3743
3787
  resolveCaptureSettledRef.current = resolve;
3744
3788
  });
3745
3789
  attemptRecordingStartedAt = readNow().getTime();
3790
+ recordingStartedAtRef.current = attemptRecordingStartedAt;
3791
+ holdOnStopRef.current = false;
3746
3792
  recorder.start(VOICE_RECORDING_TIMESLICE_MILLISECONDS);
3747
3793
  recorderStarted = true;
3748
3794
  setStatus("recording");
@@ -3944,7 +3990,21 @@ function useVoiceInput({
3944
3990
  workspaceId
3945
3991
  ]);
3946
3992
  const cancel = useCallback4(() => {
3947
- if (status === "recording" || status === "requesting-permission") {
3993
+ if (status === "recording") {
3994
+ const recorder = recorderRef.current;
3995
+ const elapsed = readNow().getTime() - recordingStartedAtRef.current;
3996
+ if (recorder && recorder.state !== "inactive" && elapsed >= VOICE_RECORDING_DISCARD_ON_CANCEL_MAX_MILLISECONDS) {
3997
+ if (timerRef.current) clearTimeout(timerRef.current);
3998
+ timerRef.current = null;
3999
+ holdOnStopRef.current = true;
4000
+ setStatus("saving");
4001
+ recorder.stop();
4002
+ return;
4003
+ }
4004
+ void discard();
4005
+ return;
4006
+ }
4007
+ if (status === "requesting-permission") {
3948
4008
  void discard();
3949
4009
  return;
3950
4010
  }
@@ -3968,7 +4028,7 @@ function useVoiceInput({
3968
4028
  await preserveForRetry(retained, null, generation);
3969
4029
  });
3970
4030
  }
3971
- }, [discard, preserveForRetry, status, updateManifestBestEffort]);
4031
+ }, [discard, preserveForRetry, readNow, status, updateManifestBestEffort]);
3972
4032
  useEffect3(() => {
3973
4033
  const current = manifestRef.current;
3974
4034
  const generation = ++generationRef.current;
@@ -4021,7 +4081,8 @@ function useVoiceInput({
4021
4081
  ]);
4022
4082
  useEffect3(() => {
4023
4083
  const onKeyDown = (event) => {
4024
- if (event.key === "Escape" && (status === "requesting-permission" || status === "recording" || status === "saving" || status === "transcribing" || status === "retrying")) {
4084
+ if (event.key === "Escape" && // A dialog, menu, or popover that handled Escape owns it.
4085
+ !event.defaultPrevented && (status === "requesting-permission" || status === "recording" || status === "saving" || status === "transcribing" || status === "retrying")) {
4025
4086
  event.preventDefault();
4026
4087
  cancel();
4027
4088
  }
@@ -4038,6 +4099,16 @@ function useVoiceInput({
4038
4099
  automaticRetryTimerRef.current = null;
4039
4100
  const manifest = manifestRef.current;
4040
4101
  const ownerReady = ownerIdPromiseRef.current;
4102
+ const knownOwnerId = ownerIdRef.current;
4103
+ const ownerLease = ownerLeaseRef.current;
4104
+ const closingStore = storeRef.current;
4105
+ const ownsClosingStore = ownsStoreRef.current;
4106
+ storeRef.current = null;
4107
+ storePromiseRef.current = null;
4108
+ ownerIdRef.current = null;
4109
+ ownerIdPromiseRef.current = null;
4110
+ ownerLeaseRef.current = null;
4111
+ manifestRef.current = null;
4041
4112
  stopOwnerHeartbeat();
4042
4113
  const recorder = recorderRef.current;
4043
4114
  let captureSettled = persistenceQueueRef.current;
@@ -4047,8 +4118,8 @@ function useVoiceInput({
4047
4118
  }
4048
4119
  clearCaptureRuntime();
4049
4120
  void captureSettled.catch(() => void 0).then(async () => {
4050
- const ownerId = ownerIdRef.current ?? (ownerReady ? await ownerReady.catch(() => null) : null);
4051
- const store = storeRef.current;
4121
+ const ownerId = knownOwnerId ?? (ownerReady ? await ownerReady.catch(() => null) : null);
4122
+ const store = closingStore;
4052
4123
  if (store && manifest && ownerId) {
4053
4124
  const wasProcessing = statusRef.current === "saving" || statusRef.current === "transcribing" || statusRef.current === "retrying";
4054
4125
  await store.updateManifest(
@@ -4068,10 +4139,9 @@ function useVoiceInput({
4068
4139
  ownerId
4069
4140
  ).catch(() => void 0);
4070
4141
  }
4071
- if (ownsStoreRef.current) await store?.close();
4142
+ if (ownsClosingStore) await store?.close();
4072
4143
  }).catch(() => void 0).finally(() => {
4073
- ownerLeaseRef.current?.release();
4074
- ownerLeaseRef.current = null;
4144
+ ownerLease?.release();
4075
4145
  });
4076
4146
  },
4077
4147
  [clearCaptureRuntime, readNow, stopOwnerHeartbeat]
@@ -4083,6 +4153,11 @@ function useVoiceInput({
4083
4153
  stream,
4084
4154
  recordingId,
4085
4155
  durationSeconds,
4156
+ recordingStartedAt: status === "recording" ? recordingStartedAtRef.current || null : null,
4157
+ maxRecordingSeconds: capability ? Math.min(
4158
+ capability.resumable && isResumableVoiceInputClient(client) ? capability.resumable.maxDurationSeconds : capability.maxDurationSeconds,
4159
+ capability.resumable && isResumableVoiceInputClient(client) ? VOICE_RECORDING_RESUMABLE_CLIENT_MAX_DURATION_SECONDS : VOICE_RECORDING_CLIENT_MAX_DURATION_SECONDS
4160
+ ) : null,
4086
4161
  locallySaved,
4087
4162
  hasRecoverableRecording: manifestRef.current !== null && status !== "recording",
4088
4163
  savedTranscript: manifestRef.current?.finalizationState === "transcript-ready" ? manifestRef.current.transcriptText : null,
@@ -4295,6 +4370,10 @@ function errorCode(error) {
4295
4370
  return "unknown";
4296
4371
  }
4297
4372
  function isRetryableVoiceInputError(error) {
4373
+ if (["insufficient_credits", "allowance_exhausted", "monthly_model_cost_limit"].includes(
4374
+ errorCode(error)
4375
+ ))
4376
+ return false;
4298
4377
  if (error instanceof OpenGeniApiError) {
4299
4378
  return error.retryable && error.status !== 409;
4300
4379
  }
@@ -4327,28 +4406,33 @@ function Tip({ tip, children }) {
4327
4406
  var defaultMessages = {
4328
4407
  start: "Start voice input",
4329
4408
  stop: "Stop and transcribe",
4330
- cancel: "Cancel recording",
4409
+ cancel: "Stop without transcribing",
4331
4410
  retry: "Retry voice input",
4411
+ transcribeSaved: "Transcribe saved recording",
4412
+ timeRemaining: "Stops and transcribes in {seconds}s",
4332
4413
  pauseRetrying: "Pause automatic retry",
4333
4414
  requestingPermission: "Requesting microphone\u2026",
4334
- recording: "Recording. Press Escape to cancel.",
4415
+ recording: "Recording. Press Escape to stop without transcribing.",
4335
4416
  saving: "Saving audio locally\u2026",
4336
4417
  transcribing: "Transcribing\u2026",
4337
- retrying: "Recording saved. Retrying automatically\u2026",
4338
- recovered: "Recording recovered and saved locally.",
4339
- recoveredTranscript: "Transcript saved locally. Check your draft before inserting.",
4418
+ retrying: "Saved. Retrying transcription\u2026",
4419
+ recovered: "Recording saved on this device.",
4420
+ recoveredTranscript: "Transcript saved. Insert it into your draft?",
4340
4421
  insertRecoveredTranscript: "Insert saved transcript",
4341
4422
  discardRecovered: "Discard saved recording",
4342
4423
  unavailableDisabled: "Voice input is unavailable while the composer is disabled.",
4343
4424
  unavailable: "Voice input is unavailable for this workspace.",
4344
- errorPermissionDenied: "Microphone permission was denied. Your draft was not changed.",
4425
+ errorPermissionDenied: "Microphone access is blocked. Allow it in site settings, then try again.",
4345
4426
  errorNotSupported: "Voice input is not supported on this device.",
4346
4427
  errorUnavailable: "Voice input is not configured.",
4428
+ errorInsufficientCredits: "Voice input needs credits. Recording saved; retry after adding credits.",
4429
+ errorAllowanceExhausted: "Usage limit reached. Recording saved for later.",
4430
+ errorPolicyBlocked: "Voice input isn't allowed for this account or workspace.",
4347
4431
  errorTooLarge: "Recording is too large. Try a shorter message.",
4348
4432
  errorInvalidAudio: "The recording could not be read. Try again.",
4349
4433
  errorStorageUnavailable: "Voice input stopped because audio could not be saved safely.",
4350
- errorRetryable: "Recording is saved locally. Retry transcription when ready.",
4351
- errorHandoffUncertain: "Transcript is saved. Check your draft before inserting it again.",
4434
+ errorRetryable: "Saved. Transcription failed, retry when ready.",
4435
+ errorHandoffUncertain: "Transcript saved. Check your draft before inserting again.",
4352
4436
  errorUnknown: "Voice input could not start. Try again."
4353
4437
  };
4354
4438
  var WAVEFORM_BARS = 18;
@@ -4385,12 +4469,16 @@ function ComposerTranscriptionControl({
4385
4469
  const retrying = status === "retrying";
4386
4470
  const capturing = status === "requesting-permission" || status === "recording";
4387
4471
  const cancelTranscription = transcription.cancel;
4472
+ const discardTranscription = transcription.discard;
4473
+ const hasRecoverableRecording = transcription.hasRecoverableRecording;
4388
4474
  useEffect4(() => {
4389
4475
  if (!suppressed) return;
4390
4476
  if (status === "recording" || status === "requesting-permission") {
4391
4477
  cancelTranscription();
4478
+ } else if (status === "error" && !hasRecoverableRecording) {
4479
+ void discardTranscription();
4392
4480
  }
4393
- }, [cancelTranscription, status, suppressed]);
4481
+ }, [cancelTranscription, discardTranscription, hasRecoverableRecording, status, suppressed]);
4394
4482
  const active = capturing || status === "saving" || status === "transcribing";
4395
4483
  const recoverable = transcription.hasRecoverableRecording && (retrying || status === "recovered" || status === "transcript-ready" || status === "error");
4396
4484
  const savedTranscript = status === "transcript-ready" && transcription.savedTranscript !== null;
@@ -4398,11 +4486,15 @@ function ComposerTranscriptionControl({
4398
4486
  const idleLabel = unavailableMessage ?? (status === "error" ? messages.retry : messages.start);
4399
4487
  const errorMessage = transcription.error ? transcriptionErrorMessage(transcription.error, messages) : null;
4400
4488
  const announcement = status === "requesting-permission" ? messages.requestingPermission : status === "recording" ? messages.recording : status === "saving" ? messages.saving : status === "transcribing" ? messages.transcribing : retrying ? messages.retrying : status === "recovered" ? messages.recovered : status === "transcript-ready" ? messages.recoveredTranscript : status === "error" ? errorMessage ?? messages.errorUnknown : unavailableMessage;
4489
+ const recoveredLabel = savedTranscript ? errorMessage ?? messages.recoveredTranscript : retrying ? messages.retrying : status === "error" ? errorMessage ?? messages.errorRetryable : messages.recovered;
4401
4490
  function start(event) {
4402
4491
  if (unavailableMessage) {
4403
4492
  event.preventDefault();
4404
4493
  return;
4405
4494
  }
4495
+ composerVoiceEventScope(event.currentTarget)?.dispatchEvent(
4496
+ new Event(COMPOSER_VOICE_INPUT_START_EVENT)
4497
+ );
4406
4498
  void transcription.start();
4407
4499
  }
4408
4500
  return /* @__PURE__ */ jsx4(AnimatePresence3, { initial: false, children: suppressed ? null : /* @__PURE__ */ jsx4(
@@ -4421,6 +4513,7 @@ function ComposerTranscriptionControl({
4421
4513
  ),
4422
4514
  "data-transcription-status": status,
4423
4515
  "data-transcription-capturing": capturing ? "" : void 0,
4516
+ "data-transcription-attention": !capturing && (recoverable || status === "error" && errorMessage) ? "" : void 0,
4424
4517
  children: /* @__PURE__ */ jsxs4("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [
4425
4518
  /* @__PURE__ */ jsx4(AnimatePresence3, { mode: "popLayout", initial: false, children: recoverable ? /* @__PURE__ */ jsxs4(
4426
4519
  motion3.span,
@@ -4434,18 +4527,25 @@ function ComposerTranscriptionControl({
4434
4527
  "bg-og-surface-2/70 pl-2 pr-1 pointer-coarse:h-11"
4435
4528
  ),
4436
4529
  children: [
4437
- /* @__PURE__ */ jsx4("span", { className: "og-transcription-recovered-label max-w-44 truncate text-og-xs text-og-fg-muted max-sm:max-w-28", children: savedTranscript ? errorMessage ?? messages.recoveredTranscript : retrying ? messages.retrying : status === "error" ? errorMessage ?? messages.errorRetryable : messages.recovered }),
4530
+ /* @__PURE__ */ jsx4(
4531
+ "span",
4532
+ {
4533
+ title: recoveredLabel,
4534
+ className: "og-transcription-recovered-label max-w-56 line-clamp-2 text-og-xs leading-tight text-og-fg-muted max-sm:max-w-32",
4535
+ children: recoveredLabel
4536
+ }
4537
+ ),
4438
4538
  /* @__PURE__ */ jsx4(
4439
4539
  Tip,
4440
4540
  {
4441
- tip: savedTranscript ? messages.insertRecoveredTranscript : retrying ? messages.pauseRetrying : messages.retry,
4541
+ tip: savedTranscript ? messages.insertRecoveredTranscript : retrying ? messages.pauseRetrying : status === "recovered" ? messages.transcribeSaved : messages.retry,
4442
4542
  children: /* @__PURE__ */ jsx4(
4443
4543
  "button",
4444
4544
  {
4445
4545
  type: "button",
4446
4546
  disabled: composer.disabled && !retrying,
4447
4547
  onClick: () => savedTranscript ? void transcription.insertSavedTranscript() : retrying ? transcription.cancel() : transcription.retry(),
4448
- "aria-label": savedTranscript ? messages.insertRecoveredTranscript : retrying ? messages.pauseRetrying : messages.retry,
4548
+ "aria-label": savedTranscript ? messages.insertRecoveredTranscript : retrying ? messages.pauseRetrying : status === "recovered" ? messages.transcribeSaved : messages.retry,
4449
4549
  className: cn(
4450
4550
  "inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm",
4451
4551
  "border border-og-primary-border bg-og-primary text-og-primary-fg hover:bg-og-primary-hover transition-colors duration-150 motion-reduce:transition-none",
@@ -4506,7 +4606,15 @@ function ComposerTranscriptionControl({
4506
4606
  stream: status === "recording" ? transcription.stream : null,
4507
4607
  mode: status === "recording" ? "recording" : "transcribing"
4508
4608
  }
4509
- )
4609
+ ),
4610
+ status === "recording" && transcription.recordingStartedAt !== null ? /* @__PURE__ */ jsx4(
4611
+ RecordingClock,
4612
+ {
4613
+ startedAt: transcription.recordingStartedAt,
4614
+ maxSeconds: transcription.maxRecordingSeconds,
4615
+ remainingLabel: messages.timeRemaining
4616
+ }
4617
+ ) : null
4510
4618
  ] }),
4511
4619
  status === "recording" ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
4512
4620
  /* @__PURE__ */ jsx4(Tip, { tip: messages.cancel, children: /* @__PURE__ */ jsx4(
@@ -4563,14 +4671,26 @@ function ComposerTranscriptionControl({
4563
4671
  children: /* @__PURE__ */ jsx4(MicIcon, { className: "size-4" })
4564
4672
  }
4565
4673
  ) }, "idle") }),
4566
- status === "error" && errorMessage && !recoverable ? /* @__PURE__ */ jsx4(Tip, { tip: errorMessage, children: /* @__PURE__ */ jsx4(
4567
- "span",
4568
- {
4569
- "aria-hidden": "true",
4570
- className: "og-transcription-error-label max-w-40 truncate text-og-xs text-og-status-failed max-sm:max-w-24",
4571
- children: errorMessage
4572
- }
4573
- ) }) : null,
4674
+ status === "error" && errorMessage && !recoverable ? (
4675
+ // Nothing is saved in this state, so dismissing loses no speech.
4676
+ /* @__PURE__ */ jsxs4(
4677
+ "button",
4678
+ {
4679
+ type: "button",
4680
+ title: errorMessage,
4681
+ "aria-label": `Dismiss: ${errorMessage}`,
4682
+ onClick: () => void transcription.discard(),
4683
+ className: cn(
4684
+ "inline-flex min-w-0 items-center gap-1 rounded-og-sm text-left outline-hidden",
4685
+ "text-og-status-failed focus-visible:ring-2 focus-visible:ring-og-accent/45"
4686
+ ),
4687
+ children: [
4688
+ /* @__PURE__ */ jsx4("span", { className: "og-transcription-error-label max-w-56 line-clamp-2 text-og-xs leading-tight max-sm:max-w-32", children: errorMessage }),
4689
+ /* @__PURE__ */ jsx4(XIcon2, { className: "size-3 shrink-0 opacity-70", "aria-hidden": true })
4690
+ ]
4691
+ }
4692
+ )
4693
+ ) : null,
4574
4694
  /* @__PURE__ */ jsx4(
4575
4695
  "span",
4576
4696
  {
@@ -4585,6 +4705,36 @@ function ComposerTranscriptionControl({
4585
4705
  "composer-dictate"
4586
4706
  ) });
4587
4707
  }
4708
+ function RecordingClock({
4709
+ startedAt,
4710
+ maxSeconds,
4711
+ remainingLabel
4712
+ }) {
4713
+ const [now, setNow] = useState5(() => Date.now());
4714
+ useEffect4(() => {
4715
+ const timer = setInterval(() => setNow(Date.now()), 500);
4716
+ return () => clearInterval(timer);
4717
+ }, []);
4718
+ const elapsed = Math.max(0, Math.floor((now - startedAt) / 1e3));
4719
+ const remaining = maxSeconds === null ? null : Math.max(0, maxSeconds - elapsed);
4720
+ const warn = remaining !== null && remaining <= 15;
4721
+ const label = `${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, "0")}`;
4722
+ return /* @__PURE__ */ jsxs4(
4723
+ "span",
4724
+ {
4725
+ "data-recording-clock": true,
4726
+ title: warn ? remainingLabel.replace("{seconds}", String(remaining)) : void 0,
4727
+ className: cn(
4728
+ "min-w-[2.25rem] text-right text-og-xs tabular-nums",
4729
+ warn ? "font-medium text-og-status-failed" : "text-og-fg-muted"
4730
+ ),
4731
+ children: [
4732
+ label,
4733
+ warn ? /* @__PURE__ */ jsx4("span", { className: "sr-only", children: remainingLabel.replace("{seconds}", String(remaining)) }) : null
4734
+ ]
4735
+ }
4736
+ );
4737
+ }
4588
4738
  function VoiceWaveform({
4589
4739
  stream,
4590
4740
  mode
@@ -4672,6 +4822,13 @@ function restingLevels(count) {
4672
4822
  }
4673
4823
  function transcriptionErrorMessage(code, messages) {
4674
4824
  switch (code) {
4825
+ case "insufficient_credits":
4826
+ return messages.errorInsufficientCredits;
4827
+ case "allowance_exhausted":
4828
+ case "monthly_model_cost_limit":
4829
+ return messages.errorAllowanceExhausted;
4830
+ case "policy_blocked":
4831
+ return messages.errorPolicyBlocked;
4675
4832
  case "permission_denied":
4676
4833
  return messages.errorPermissionDenied;
4677
4834
  case "not_supported":
@@ -4752,4 +4909,4 @@ export {
4752
4909
  useVoiceInput,
4753
4910
  ComposerTranscriptionControl
4754
4911
  };
4755
- //# sourceMappingURL=chunk-CY7WK5FD.js.map
4912
+ //# sourceMappingURL=chunk-IUHHPMAX.js.map