@opengeni/react 1.4.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 (212) hide show
  1. package/README.md +8 -0
  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-MBMPHOMA.js → chunk-55DXD22O.js} +3 -2
  22. package/dist/{chunk-MBMPHOMA.js.map → chunk-55DXD22O.js.map} +1 -1
  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-LJ2UTJJE.js → chunk-CREMJMVN.js} +181 -96
  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-WZMYFJD3.js → chunk-G6YRCIYJ.js} +16 -7
  42. package/dist/{chunk-WZMYFJD3.js.map → chunk-G6YRCIYJ.js.map} +1 -1
  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-CJUO7J5Z.js → chunk-HXYOSSQO.js} +3 -2
  46. package/dist/{chunk-CJUO7J5Z.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-LN6TCXD3.js → chunk-IUHHPMAX.js} +184 -48
  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-OA6HFRQP.js → chunk-QMMCJGFI.js} +4 -3
  89. package/dist/{chunk-OA6HFRQP.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-AWZLIMKX.js → chunk-T46QZH3E.js} +3 -1
  97. package/dist/{chunk-AWZLIMKX.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-H5X55XH7.js → chunk-UDPH5SLV.js} +4 -3
  103. package/dist/{chunk-H5X55XH7.js.map → chunk-UDPH5SLV.js.map} +1 -1
  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 +4 -0
  122. package/dist/components/session-conversation.d.ts +6 -0
  123. package/dist/composer-voice-events.d.ts +8 -0
  124. package/dist/composer.js +15 -13
  125. package/dist/computer-viewer-46UIN7F3.js +20 -0
  126. package/dist/connect.js +15 -12
  127. package/dist/connect.js.map +1 -1
  128. package/dist/desktop.js +6 -5
  129. package/dist/desktop.js.map +1 -1
  130. package/dist/diffs.js +2 -1
  131. package/dist/diffs.js.map +1 -1
  132. package/dist/editor.js +4 -3
  133. package/dist/editor.js.map +1 -1
  134. package/dist/embedded-voice-NIL4RDX3.js +69 -0
  135. package/dist/embedded-voice-NIL4RDX3.js.map +1 -0
  136. package/dist/{fleet-decision-row-YZ3ZKWMM.js → fleet-decision-row-SI63O5V4.js} +3 -1
  137. package/dist/{fleet-decision-row-YZ3ZKWMM.js.map → fleet-decision-row-SI63O5V4.js.map} +1 -1
  138. package/dist/hooks/use-client-config-flags.d.ts +6 -0
  139. package/dist/hooks/use-realtime-voice-models.d.ts +9 -0
  140. package/dist/hooks/use-voice-input.d.ts +8 -1
  141. package/dist/index.js +47 -45
  142. package/dist/index.js.map +1 -1
  143. package/dist/interaction.js +14 -13
  144. package/dist/interaction.js.map +1 -1
  145. package/dist/{latest-question-GPYM2JNU.js → latest-question-2POFPYNB.js} +5 -4
  146. package/dist/{latest-question-GPYM2JNU.js.map → latest-question-2POFPYNB.js.map} +1 -1
  147. package/dist/machines.js +8 -7
  148. package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-DQ4DRNVT.js} +3 -1
  149. package/dist/{markdown-table-layout-QXSLK4BY.js.map → markdown-table-layout-DQ4DRNVT.js.map} +1 -1
  150. package/dist/model-policy-picker-menu-WRNSRFQC.js +15 -0
  151. package/dist/model-policy.js +2 -1
  152. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  153. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  154. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  155. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  156. package/dist/{platform-activity-row-627BFZ46.js → platform-activity-row-PJJVATTZ.js} +3 -1
  157. package/dist/{platform-activity-row-627BFZ46.js.map → platform-activity-row-PJJVATTZ.js.map} +1 -1
  158. package/dist/{queue-surface-implementation-ZHFI5JBP.js → queue-surface-implementation-67FS7FUI.js} +10 -9
  159. package/dist/{queue-surface-implementation-ZHFI5JBP.js.map → queue-surface-implementation-67FS7FUI.js.map} +1 -1
  160. package/dist/realtime/embedded-voice.d.ts +20 -0
  161. package/dist/realtime.js +22 -1353
  162. package/dist/realtime.js.map +1 -1
  163. package/dist/sandbox-files-34CJJNOY.js +21 -0
  164. package/dist/session-ui.js +32 -30
  165. package/dist/session-ui.js.map +1 -1
  166. package/dist/session.js +10 -9
  167. package/dist/sites.js +2 -1
  168. package/dist/terminal.js +6 -5
  169. package/dist/terminal.js.map +1 -1
  170. package/dist/{timeline-annotation-markers-GNDHTGBD.js → timeline-annotation-markers-U3G66KUD.js} +5 -4
  171. package/dist/{timeline-annotation-markers-GNDHTGBD.js.map → timeline-annotation-markers-U3G66KUD.js.map} +1 -1
  172. package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-BI7QTPTZ.js} +4 -3
  173. package/dist/{timeline-annotation-selection-DMLMM2TX.js.map → timeline-annotation-selection-BI7QTPTZ.js.map} +1 -1
  174. package/dist/{timeline-annotations-dialog-ELRDBHTS.js → timeline-annotations-dialog-FOMHSDTE.js} +7 -6
  175. package/dist/{timeline-annotations-dialog-ELRDBHTS.js.map → timeline-annotations-dialog-FOMHSDTE.js.map} +1 -1
  176. package/dist/usage.js +6 -5
  177. package/dist/usage.js.map +1 -1
  178. package/dist/voice-recording-owner.d.ts +7 -0
  179. package/package.json +3 -3
  180. package/src/components/composer-transcription-control.tsx +109 -27
  181. package/src/components/markdown.tsx +7 -2
  182. package/src/components/message-timeline.tsx +10 -1
  183. package/src/components/session-conversation.tsx +44 -0
  184. package/src/components/session-proxy-scope.tsx +26 -5
  185. package/src/composer-voice-events.ts +12 -0
  186. package/src/connection-discovery.tsx +4 -1
  187. package/src/hooks/use-client-config-flags.ts +8 -0
  188. package/src/hooks/use-goal.ts +12 -1
  189. package/src/hooks/use-realtime-voice-models.ts +67 -0
  190. package/src/hooks/use-voice-input.ts +109 -18
  191. package/src/realtime/embedded-voice.tsx +81 -0
  192. package/src/realtime/realtime-control.tsx +82 -17
  193. package/src/voice-recording-owner.ts +22 -0
  194. package/styles/compiled.css +72 -18
  195. package/styles/index.css +49 -0
  196. package/styles/responsive.css +61 -4
  197. package/styles/session-capability.css +1 -1
  198. package/dist/browser-viewer-SHYKQRNX.js +0 -16
  199. package/dist/chat-interactive-block-UGBTNB3L.js +0 -11
  200. package/dist/chunk-I7IWRQLN.js.map +0 -1
  201. package/dist/chunk-IWIDV7SB.js +0 -1
  202. package/dist/chunk-LJ2UTJJE.js.map +0 -1
  203. package/dist/chunk-LN6TCXD3.js.map +0 -1
  204. package/dist/computer-viewer-6SRGWRUX.js +0 -19
  205. package/dist/model-policy-picker-menu-DXHUL73H.js +0 -14
  206. package/dist/sandbox-files-GORZOUUJ.js +0 -20
  207. /package/dist/{browser-viewer-SHYKQRNX.js.map → browser-viewer-WGJ43YGI.js.map} +0 -0
  208. /package/dist/{chat-interactive-block-UGBTNB3L.js.map → chat-interactive-block-ZFHOKTMS.js.map} +0 -0
  209. /package/dist/{chunk-IWIDV7SB.js.map → chunk-M357MHH3.js.map} +0 -0
  210. /package/dist/{computer-viewer-6SRGWRUX.js.map → computer-viewer-46UIN7F3.js.map} +0 -0
  211. /package/dist/{model-policy-picker-menu-DXHUL73H.js.map → model-policy-picker-menu-WRNSRFQC.js.map} +0 -0
  212. /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";
@@ -3019,6 +3024,21 @@ async function createUnderlyingOwnerLease() {
3019
3024
  writeSessionOwnerId(ownerId);
3020
3025
  return { ownerId, release: () => void 0 };
3021
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
+ }
3022
3042
  function hasWebLocks() {
3023
3043
  return typeof navigator !== "undefined" && navigator.locks !== null && navigator.locks !== void 0 && typeof navigator.locks.request === "function";
3024
3044
  }
@@ -3140,6 +3160,7 @@ var VOICE_RECORDING_TIMESLICE_MILLISECONDS = 5e3;
3140
3160
  var VOICE_RECORDING_OWNER_HEARTBEAT_MILLISECONDS = 5e3;
3141
3161
  var VOICE_RECORDING_OWNER_STALE_MILLISECONDS = 3e4;
3142
3162
  var VOICE_RECORDING_CLIENT_MAX_DURATION_SECONDS = 600;
3163
+ var VOICE_RECORDING_DISCARD_ON_CANCEL_MAX_MILLISECONDS = 3e3;
3143
3164
  var VOICE_RECORDING_RESUMABLE_CLIENT_MAX_DURATION_SECONDS = 8 * 60 * 60;
3144
3165
  var VOICE_RECORDING_RECOVERY_STATUS_POLL_MILLISECONDS = 2e3;
3145
3166
  var VOICE_RECORDING_RECOVERY_MAX_MUTATION_DELAY_MILLISECONDS = 3e4;
@@ -3203,6 +3224,8 @@ function useVoiceInput({
3203
3224
  const recorderRef = useRef4(null);
3204
3225
  const streamRef = useRef4(null);
3205
3226
  const timerRef = useRef4(null);
3227
+ const holdOnStopRef = useRef4(false);
3228
+ const recordingStartedAtRef = useRef4(0);
3206
3229
  const automaticRetryTimerRef = useRef4(null);
3207
3230
  const automaticRetryAttemptRef = useRef4(0);
3208
3231
  const ownerHeartbeatTimerRef = useRef4(null);
@@ -3342,15 +3365,17 @@ function useVoiceInput({
3342
3365
  const active = () => generation === generationRef.current && workspaceIdRef.current === workspaceId;
3343
3366
  const [store, ownerId] = await Promise.all([ensureStore(), ensureOwnerId()]);
3344
3367
  if (!active()) return;
3345
- const staleBefore = new Date(
3368
+ const heartbeatStaleBefore = new Date(
3346
3369
  readNow().getTime() - VOICE_RECORDING_OWNER_STALE_MILLISECONDS
3347
3370
  ).toISOString();
3348
- await store.cleanupHandedOffManifests({ ownerId, staleBefore }).catch(() => void 0);
3371
+ await store.cleanupHandedOffManifests({ ownerId, staleBefore: heartbeatStaleBefore }).catch(() => void 0);
3349
3372
  if (!active()) return;
3350
- const manifests = await store.listRecoverableManifests(workspaceId, {
3351
- ownerId,
3352
- staleBefore
3353
- });
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
+ );
3354
3379
  if (!active()) return;
3355
3380
  for (const candidate of manifests) {
3356
3381
  try {
@@ -3372,10 +3397,13 @@ function useVoiceInput({
3372
3397
  }
3373
3398
  rememberManifest(claimed);
3374
3399
  beginOwnerHeartbeat(claimed);
3400
+ const transcriptReady = claimed.finalizationState === "transcript-ready" && claimed.transcriptText !== null;
3375
3401
  setStatus(
3376
- 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
3377
3406
  );
3378
- setError(null);
3379
3407
  return;
3380
3408
  } catch (reason) {
3381
3409
  if (reason instanceof VoiceRecordingOwnedError) continue;
@@ -3466,7 +3494,7 @@ function useVoiceInput({
3466
3494
  automaticRetryAttemptRef.current = 0;
3467
3495
  if (ready.handoffMode === "explicit") {
3468
3496
  setStatus("transcript-ready");
3469
- setError("handoff_uncertain");
3497
+ setError(null);
3470
3498
  focusInput();
3471
3499
  return;
3472
3500
  }
@@ -3742,6 +3770,15 @@ function useVoiceInput({
3742
3770
  await preserveForRetry(retained, attemptCaptureLimitError, generation);
3743
3771
  return;
3744
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
+ }
3745
3782
  await finalizePersistedRecording(generation);
3746
3783
  });
3747
3784
  };
@@ -3750,6 +3787,8 @@ function useVoiceInput({
3750
3787
  resolveCaptureSettledRef.current = resolve;
3751
3788
  });
3752
3789
  attemptRecordingStartedAt = readNow().getTime();
3790
+ recordingStartedAtRef.current = attemptRecordingStartedAt;
3791
+ holdOnStopRef.current = false;
3753
3792
  recorder.start(VOICE_RECORDING_TIMESLICE_MILLISECONDS);
3754
3793
  recorderStarted = true;
3755
3794
  setStatus("recording");
@@ -3951,7 +3990,21 @@ function useVoiceInput({
3951
3990
  workspaceId
3952
3991
  ]);
3953
3992
  const cancel = useCallback4(() => {
3954
- 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") {
3955
4008
  void discard();
3956
4009
  return;
3957
4010
  }
@@ -3975,7 +4028,7 @@ function useVoiceInput({
3975
4028
  await preserveForRetry(retained, null, generation);
3976
4029
  });
3977
4030
  }
3978
- }, [discard, preserveForRetry, status, updateManifestBestEffort]);
4031
+ }, [discard, preserveForRetry, readNow, status, updateManifestBestEffort]);
3979
4032
  useEffect3(() => {
3980
4033
  const current = manifestRef.current;
3981
4034
  const generation = ++generationRef.current;
@@ -4028,7 +4081,8 @@ function useVoiceInput({
4028
4081
  ]);
4029
4082
  useEffect3(() => {
4030
4083
  const onKeyDown = (event) => {
4031
- 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")) {
4032
4086
  event.preventDefault();
4033
4087
  cancel();
4034
4088
  }
@@ -4045,6 +4099,16 @@ function useVoiceInput({
4045
4099
  automaticRetryTimerRef.current = null;
4046
4100
  const manifest = manifestRef.current;
4047
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;
4048
4112
  stopOwnerHeartbeat();
4049
4113
  const recorder = recorderRef.current;
4050
4114
  let captureSettled = persistenceQueueRef.current;
@@ -4054,8 +4118,8 @@ function useVoiceInput({
4054
4118
  }
4055
4119
  clearCaptureRuntime();
4056
4120
  void captureSettled.catch(() => void 0).then(async () => {
4057
- const ownerId = ownerIdRef.current ?? (ownerReady ? await ownerReady.catch(() => null) : null);
4058
- const store = storeRef.current;
4121
+ const ownerId = knownOwnerId ?? (ownerReady ? await ownerReady.catch(() => null) : null);
4122
+ const store = closingStore;
4059
4123
  if (store && manifest && ownerId) {
4060
4124
  const wasProcessing = statusRef.current === "saving" || statusRef.current === "transcribing" || statusRef.current === "retrying";
4061
4125
  await store.updateManifest(
@@ -4075,10 +4139,9 @@ function useVoiceInput({
4075
4139
  ownerId
4076
4140
  ).catch(() => void 0);
4077
4141
  }
4078
- if (ownsStoreRef.current) await store?.close();
4142
+ if (ownsClosingStore) await store?.close();
4079
4143
  }).catch(() => void 0).finally(() => {
4080
- ownerLeaseRef.current?.release();
4081
- ownerLeaseRef.current = null;
4144
+ ownerLease?.release();
4082
4145
  });
4083
4146
  },
4084
4147
  [clearCaptureRuntime, readNow, stopOwnerHeartbeat]
@@ -4090,6 +4153,11 @@ function useVoiceInput({
4090
4153
  stream,
4091
4154
  recordingId,
4092
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,
4093
4161
  locallySaved,
4094
4162
  hasRecoverableRecording: manifestRef.current !== null && status !== "recording",
4095
4163
  savedTranscript: manifestRef.current?.finalizationState === "transcript-ready" ? manifestRef.current.transcriptText : null,
@@ -4338,31 +4406,33 @@ function Tip({ tip, children }) {
4338
4406
  var defaultMessages = {
4339
4407
  start: "Start voice input",
4340
4408
  stop: "Stop and transcribe",
4341
- cancel: "Cancel recording",
4409
+ cancel: "Stop without transcribing",
4342
4410
  retry: "Retry voice input",
4411
+ transcribeSaved: "Transcribe saved recording",
4412
+ timeRemaining: "Stops and transcribes in {seconds}s",
4343
4413
  pauseRetrying: "Pause automatic retry",
4344
4414
  requestingPermission: "Requesting microphone\u2026",
4345
- recording: "Recording. Press Escape to cancel.",
4415
+ recording: "Recording. Press Escape to stop without transcribing.",
4346
4416
  saving: "Saving audio locally\u2026",
4347
4417
  transcribing: "Transcribing\u2026",
4348
- retrying: "Recording saved. Retrying automatically\u2026",
4349
- recovered: "Recording recovered and saved locally.",
4350
- 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?",
4351
4421
  insertRecoveredTranscript: "Insert saved transcript",
4352
4422
  discardRecovered: "Discard saved recording",
4353
4423
  unavailableDisabled: "Voice input is unavailable while the composer is disabled.",
4354
4424
  unavailable: "Voice input is unavailable for this workspace.",
4355
- errorPermissionDenied: "Microphone permission was denied. Your draft was not changed.",
4425
+ errorPermissionDenied: "Microphone access is blocked. Allow it in site settings, then try again.",
4356
4426
  errorNotSupported: "Voice input is not supported on this device.",
4357
4427
  errorUnavailable: "Voice input is not configured.",
4358
- errorInsufficientCredits: "Voice input needs Opengeni credits. Add credits, then retry your saved recording.",
4359
- errorAllowanceExhausted: "Your usage limit was reached. Your recording is saved for later.",
4428
+ errorInsufficientCredits: "Voice input needs credits. Recording saved; retry after adding credits.",
4429
+ errorAllowanceExhausted: "Usage limit reached. Recording saved for later.",
4360
4430
  errorPolicyBlocked: "Voice input isn't allowed for this account or workspace.",
4361
4431
  errorTooLarge: "Recording is too large. Try a shorter message.",
4362
4432
  errorInvalidAudio: "The recording could not be read. Try again.",
4363
4433
  errorStorageUnavailable: "Voice input stopped because audio could not be saved safely.",
4364
- errorRetryable: "Recording is saved locally. Retry transcription when ready.",
4365
- 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.",
4366
4436
  errorUnknown: "Voice input could not start. Try again."
4367
4437
  };
4368
4438
  var WAVEFORM_BARS = 18;
@@ -4399,12 +4469,16 @@ function ComposerTranscriptionControl({
4399
4469
  const retrying = status === "retrying";
4400
4470
  const capturing = status === "requesting-permission" || status === "recording";
4401
4471
  const cancelTranscription = transcription.cancel;
4472
+ const discardTranscription = transcription.discard;
4473
+ const hasRecoverableRecording = transcription.hasRecoverableRecording;
4402
4474
  useEffect4(() => {
4403
4475
  if (!suppressed) return;
4404
4476
  if (status === "recording" || status === "requesting-permission") {
4405
4477
  cancelTranscription();
4478
+ } else if (status === "error" && !hasRecoverableRecording) {
4479
+ void discardTranscription();
4406
4480
  }
4407
- }, [cancelTranscription, status, suppressed]);
4481
+ }, [cancelTranscription, discardTranscription, hasRecoverableRecording, status, suppressed]);
4408
4482
  const active = capturing || status === "saving" || status === "transcribing";
4409
4483
  const recoverable = transcription.hasRecoverableRecording && (retrying || status === "recovered" || status === "transcript-ready" || status === "error");
4410
4484
  const savedTranscript = status === "transcript-ready" && transcription.savedTranscript !== null;
@@ -4412,11 +4486,15 @@ function ComposerTranscriptionControl({
4412
4486
  const idleLabel = unavailableMessage ?? (status === "error" ? messages.retry : messages.start);
4413
4487
  const errorMessage = transcription.error ? transcriptionErrorMessage(transcription.error, messages) : null;
4414
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;
4415
4490
  function start(event) {
4416
4491
  if (unavailableMessage) {
4417
4492
  event.preventDefault();
4418
4493
  return;
4419
4494
  }
4495
+ composerVoiceEventScope(event.currentTarget)?.dispatchEvent(
4496
+ new Event(COMPOSER_VOICE_INPUT_START_EVENT)
4497
+ );
4420
4498
  void transcription.start();
4421
4499
  }
4422
4500
  return /* @__PURE__ */ jsx4(AnimatePresence3, { initial: false, children: suppressed ? null : /* @__PURE__ */ jsx4(
@@ -4435,6 +4513,7 @@ function ComposerTranscriptionControl({
4435
4513
  ),
4436
4514
  "data-transcription-status": status,
4437
4515
  "data-transcription-capturing": capturing ? "" : void 0,
4516
+ "data-transcription-attention": !capturing && (recoverable || status === "error" && errorMessage) ? "" : void 0,
4438
4517
  children: /* @__PURE__ */ jsxs4("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [
4439
4518
  /* @__PURE__ */ jsx4(AnimatePresence3, { mode: "popLayout", initial: false, children: recoverable ? /* @__PURE__ */ jsxs4(
4440
4519
  motion3.span,
@@ -4448,18 +4527,25 @@ function ComposerTranscriptionControl({
4448
4527
  "bg-og-surface-2/70 pl-2 pr-1 pointer-coarse:h-11"
4449
4528
  ),
4450
4529
  children: [
4451
- /* @__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
+ ),
4452
4538
  /* @__PURE__ */ jsx4(
4453
4539
  Tip,
4454
4540
  {
4455
- tip: savedTranscript ? messages.insertRecoveredTranscript : retrying ? messages.pauseRetrying : messages.retry,
4541
+ tip: savedTranscript ? messages.insertRecoveredTranscript : retrying ? messages.pauseRetrying : status === "recovered" ? messages.transcribeSaved : messages.retry,
4456
4542
  children: /* @__PURE__ */ jsx4(
4457
4543
  "button",
4458
4544
  {
4459
4545
  type: "button",
4460
4546
  disabled: composer.disabled && !retrying,
4461
4547
  onClick: () => savedTranscript ? void transcription.insertSavedTranscript() : retrying ? transcription.cancel() : transcription.retry(),
4462
- "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,
4463
4549
  className: cn(
4464
4550
  "inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm",
4465
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",
@@ -4520,7 +4606,15 @@ function ComposerTranscriptionControl({
4520
4606
  stream: status === "recording" ? transcription.stream : null,
4521
4607
  mode: status === "recording" ? "recording" : "transcribing"
4522
4608
  }
4523
- )
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
4524
4618
  ] }),
4525
4619
  status === "recording" ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
4526
4620
  /* @__PURE__ */ jsx4(Tip, { tip: messages.cancel, children: /* @__PURE__ */ jsx4(
@@ -4577,14 +4671,26 @@ function ComposerTranscriptionControl({
4577
4671
  children: /* @__PURE__ */ jsx4(MicIcon, { className: "size-4" })
4578
4672
  }
4579
4673
  ) }, "idle") }),
4580
- status === "error" && errorMessage && !recoverable ? /* @__PURE__ */ jsx4(Tip, { tip: errorMessage, children: /* @__PURE__ */ jsx4(
4581
- "span",
4582
- {
4583
- "aria-hidden": "true",
4584
- className: "og-transcription-error-label max-w-40 truncate text-og-xs text-og-status-failed max-sm:max-w-24",
4585
- children: errorMessage
4586
- }
4587
- ) }) : 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,
4588
4694
  /* @__PURE__ */ jsx4(
4589
4695
  "span",
4590
4696
  {
@@ -4599,6 +4705,36 @@ function ComposerTranscriptionControl({
4599
4705
  "composer-dictate"
4600
4706
  ) });
4601
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
+ }
4602
4738
  function VoiceWaveform({
4603
4739
  stream,
4604
4740
  mode
@@ -4773,4 +4909,4 @@ export {
4773
4909
  useVoiceInput,
4774
4910
  ComposerTranscriptionControl
4775
4911
  };
4776
- //# sourceMappingURL=chunk-LN6TCXD3.js.map
4912
+ //# sourceMappingURL=chunk-IUHHPMAX.js.map