@opengeni/react 0.48.0 → 0.57.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 (325) hide show
  1. package/README.md +138 -2
  2. package/dist/artifacts-document.d.ts +3 -0
  3. package/dist/artifacts-document.js +17 -0
  4. package/dist/artifacts-document.js.map +1 -0
  5. package/dist/artifacts-presentation.d.ts +3 -0
  6. package/dist/artifacts-presentation.js +17 -0
  7. package/dist/artifacts-presentation.js.map +1 -0
  8. package/dist/artifacts-spreadsheet.d.ts +3 -0
  9. package/dist/artifacts-spreadsheet.js +17 -0
  10. package/dist/artifacts-spreadsheet.js.map +1 -0
  11. package/dist/artifacts.d.ts +2 -0
  12. package/dist/artifacts.js +199 -0
  13. package/dist/artifacts.js.map +1 -0
  14. package/dist/{chunk-NJ7M2B6Z.js → chunk-5UQPNLIO.js} +16 -8
  15. package/dist/{chunk-NJ7M2B6Z.js.map → chunk-5UQPNLIO.js.map} +1 -1
  16. package/dist/{chunk-LHLYWG5R.js → chunk-6KLX2RH5.js} +461 -38
  17. package/dist/chunk-6KLX2RH5.js.map +1 -0
  18. package/dist/chunk-7WCLAEFV.js +2828 -0
  19. package/dist/chunk-7WCLAEFV.js.map +1 -0
  20. package/dist/{chunk-I4MPFDRP.js → chunk-B6WPZSTT.js} +104 -104
  21. package/dist/{chunk-I4MPFDRP.js.map → chunk-B6WPZSTT.js.map} +1 -1
  22. package/dist/{chunk-EKZH6IDG.js → chunk-C5ZPSSS5.js} +111 -1
  23. package/dist/chunk-C5ZPSSS5.js.map +1 -0
  24. package/dist/{chunk-OL6QVXGF.js → chunk-HA46CYQK.js} +152 -8
  25. package/dist/chunk-HA46CYQK.js.map +1 -0
  26. package/dist/chunk-HUIKIDM5.js +2148 -0
  27. package/dist/chunk-HUIKIDM5.js.map +1 -0
  28. package/dist/{chunk-FWXGSNXT.js → chunk-IGC6A2ZI.js} +585 -34
  29. package/dist/chunk-IGC6A2ZI.js.map +1 -0
  30. package/dist/chunk-JVYQRRC4.js +53 -0
  31. package/dist/chunk-JVYQRRC4.js.map +1 -0
  32. package/dist/chunk-KCGXPP5V.js +157 -0
  33. package/dist/chunk-KCGXPP5V.js.map +1 -0
  34. package/dist/chunk-LLOLUHVG.js +5937 -0
  35. package/dist/chunk-LLOLUHVG.js.map +1 -0
  36. package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
  37. package/dist/chunk-MKPIAGVL.js.map +1 -0
  38. package/dist/chunk-PEI6RANI.js +3234 -0
  39. package/dist/chunk-PEI6RANI.js.map +1 -0
  40. package/dist/chunk-R3AZOAB5.js +933 -0
  41. package/dist/chunk-R3AZOAB5.js.map +1 -0
  42. package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
  43. package/dist/chunk-WSK7G5LE.js.map +1 -0
  44. package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
  45. package/dist/chunk-XBZWOM2K.js.map +1 -0
  46. package/dist/{chunk-XCNTXWBB.js → chunk-Y7OEDUJ5.js} +856 -338
  47. package/dist/chunk-Y7OEDUJ5.js.map +1 -0
  48. package/dist/client.d.ts +10 -2
  49. package/dist/clipboard.d.ts +1 -0
  50. package/dist/clipboard.js +7 -0
  51. package/dist/clipboard.js.map +1 -0
  52. package/dist/commands/index.d.ts +3 -3
  53. package/dist/commands/registry.d.ts +1 -1
  54. package/dist/commands/types.d.ts +1 -1
  55. package/dist/components/approval-surface.d.ts +2 -2
  56. package/dist/components/artifacts/artifact-surface.d.ts +18 -0
  57. package/dist/components/artifacts/document-editor.d.ts +172 -0
  58. package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
  59. package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
  60. package/dist/components/artifacts/editable-document.d.ts +24 -0
  61. package/dist/components/artifacts/editable-presentation.d.ts +24 -0
  62. package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
  63. package/dist/components/artifacts/index.d.ts +9 -0
  64. package/dist/components/artifacts/presentation-editor.d.ts +297 -0
  65. package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
  66. package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
  67. package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
  68. package/dist/components/browser-input.d.ts +7 -0
  69. package/dist/components/browser-viewer.d.ts +30 -0
  70. package/dist/components/chat-composer.d.ts +8 -8
  71. package/dist/components/code-editor.d.ts +25 -3
  72. package/dist/components/command-palette.d.ts +2 -2
  73. package/dist/components/composer-transcription-control.d.ts +2 -2
  74. package/dist/components/composer.d.ts +17 -17
  75. package/dist/components/computer-viewer.d.ts +24 -0
  76. package/dist/components/copy-button.d.ts +2 -2
  77. package/dist/components/desktop-viewer.d.ts +50 -22
  78. package/dist/components/diff-view.d.ts +1 -1
  79. package/dist/components/enrollment-consent.d.ts +1 -1
  80. package/dist/components/enrollment-device-flow.d.ts +1 -1
  81. package/dist/components/file-browser.d.ts +2 -2
  82. package/dist/components/fleet-tile.d.ts +1 -1
  83. package/dist/components/generated-video-player.d.ts +10 -0
  84. package/dist/components/human-input-form.d.ts +1 -1
  85. package/dist/components/human-input-surface.d.ts +2 -2
  86. package/dist/components/interaction-intervention-banner.d.ts +11 -0
  87. package/dist/components/interactive-workbench-viewers.d.ts +2 -0
  88. package/dist/components/machine-card.d.ts +2 -2
  89. package/dist/components/machine-dock-bar.d.ts +3 -3
  90. package/dist/components/machine-health-pill.d.ts +2 -2
  91. package/dist/components/machine-input-display.d.ts +1 -1
  92. package/dist/components/machine-metrics.d.ts +2 -2
  93. package/dist/components/machine-status-pill.d.ts +4 -4
  94. package/dist/components/machines/health.d.ts +1 -1
  95. package/dist/components/machines/machine-detail.d.ts +3 -3
  96. package/dist/components/machines/metric-history-chart.d.ts +1 -1
  97. package/dist/components/machines/metric-sparkline.d.ts +2 -2
  98. package/dist/components/machines/series.d.ts +2 -2
  99. package/dist/components/machines-dashboard.d.ts +2 -2
  100. package/dist/components/markdown.d.ts +1 -1
  101. package/dist/components/message-timeline.d.ts +12 -5
  102. package/dist/components/model-picker.d.ts +2 -2
  103. package/dist/components/model-policy-picker.d.ts +7 -7
  104. package/dist/components/pierre-diff.d.ts +1 -1
  105. package/dist/components/pierre-file.d.ts +1 -1
  106. package/dist/components/queue-draft-policy.d.ts +1 -1
  107. package/dist/components/queue-surface-implementation.d.ts +3 -3
  108. package/dist/components/queue-surface-state.d.ts +4 -4
  109. package/dist/components/queue-surface.d.ts +5 -5
  110. package/dist/components/sandbox-files.d.ts +3 -3
  111. package/dist/components/sandbox-terminal.d.ts +24 -4
  112. package/dist/components/sandbox-workspace.d.ts +28 -9
  113. package/dist/components/session-chrome.d.ts +4 -4
  114. package/dist/components/session-status.d.ts +2 -2
  115. package/dist/components/timeline-annotation-selection.d.ts +9 -0
  116. package/dist/components/timeline-annotations-dialog.d.ts +16 -0
  117. package/dist/components/timeline-annotations.d.ts +13 -0
  118. package/dist/components/tooltip.d.ts +4 -4
  119. package/dist/components/user-message-body.d.ts +2 -2
  120. package/dist/components/workbench-changes.d.ts +2 -2
  121. package/dist/components/workspace-dock.d.ts +1 -1
  122. package/dist/composer.d.ts +11 -11
  123. package/dist/composer.js +7 -7
  124. package/dist/hooks/internal.d.ts +1 -1
  125. package/dist/hooks/use-attached-browsers.d.ts +21 -0
  126. package/dist/hooks/use-auth-runs.d.ts +32 -0
  127. package/dist/hooks/use-available-models.d.ts +2 -2
  128. package/dist/hooks/use-billing-usage.d.ts +1 -1
  129. package/dist/hooks/use-browser-downloads.d.ts +25 -0
  130. package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
  131. package/dist/hooks/use-browser-identities.d.ts +30 -0
  132. package/dist/hooks/use-browser-session.d.ts +29 -0
  133. package/dist/hooks/use-browser-sessions.d.ts +32 -0
  134. package/dist/hooks/use-channels.d.ts +26 -0
  135. package/dist/hooks/use-codex-accounts.d.ts +2 -2
  136. package/dist/hooks/use-composer.d.ts +36 -9
  137. package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
  138. package/dist/hooks/use-computer-session.d.ts +29 -0
  139. package/dist/hooks/use-computer-sessions.d.ts +28 -0
  140. package/dist/hooks/use-desktop-stream.d.ts +5 -1
  141. package/dist/hooks/use-environments.d.ts +1 -1
  142. package/dist/hooks/use-file-attachments.d.ts +1 -1
  143. package/dist/hooks/use-goal.d.ts +2 -2
  144. package/dist/hooks/use-human-input.d.ts +2 -2
  145. package/dist/hooks/use-interaction-interventions.d.ts +27 -0
  146. package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
  147. package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
  148. package/dist/hooks/use-machine-chip.d.ts +2 -2
  149. package/dist/hooks/use-machines.d.ts +4 -4
  150. package/dist/hooks/use-network-routes.d.ts +25 -0
  151. package/dist/hooks/use-packs.d.ts +7 -2
  152. package/dist/hooks/use-rigs.d.ts +1 -1
  153. package/dist/hooks/use-sandbox-files.d.ts +6 -4
  154. package/dist/hooks/use-sandbox-git.d.ts +6 -5
  155. package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
  156. package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
  157. package/dist/hooks/use-session-capabilities.d.ts +5 -4
  158. package/dist/hooks/use-session-control.d.ts +1 -1
  159. package/dist/hooks/use-session-events.d.ts +2 -2
  160. package/dist/hooks/use-session-lineage.d.ts +1 -1
  161. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  162. package/dist/hooks/use-session.d.ts +2 -2
  163. package/dist/hooks/use-site-auth-connections.d.ts +25 -0
  164. package/dist/hooks/use-slash-commands.d.ts +1 -1
  165. package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
  166. package/dist/hooks/use-terminal-stream.d.ts +11 -2
  167. package/dist/hooks/use-turn-queue.d.ts +2 -2
  168. package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
  169. package/dist/hooks/use-voice-input.d.ts +1 -1
  170. package/dist/hooks/use-workspace-capture.d.ts +1 -1
  171. package/dist/hooks/use-workspace-edit.d.ts +1 -1
  172. package/dist/hooks/use-workspace-sessions.d.ts +1 -1
  173. package/dist/hooks/use-workspaces.d.ts +1 -1
  174. package/dist/index.d.ts +162 -155
  175. package/dist/index.js +1822 -1703
  176. package/dist/index.js.map +1 -1
  177. package/dist/interaction.d.ts +33 -0
  178. package/dist/interaction.js +550 -0
  179. package/dist/interaction.js.map +1 -0
  180. package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
  181. package/dist/interactive-workbench-viewers-TOHBXJEE.js.map +1 -0
  182. package/dist/lib/host-platform.d.ts +5 -0
  183. package/dist/lib/relay-wire.d.ts +24 -1
  184. package/dist/lib/sandbox-liveness.d.ts +9 -0
  185. package/dist/lib/terminal-capability.d.ts +5 -0
  186. package/dist/lib/xterm-theme.d.ts +1 -1
  187. package/dist/machines.d.ts +30 -30
  188. package/dist/machines.js +2 -2
  189. package/dist/model-policy.d.ts +1 -1
  190. package/dist/model-policy.js +1 -1
  191. package/dist/provider.d.ts +6 -5
  192. package/dist/{queue-surface-implementation-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
  193. package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
  194. package/dist/realtime/dropdown-menu.d.ts +8 -8
  195. package/dist/realtime/realtime-control.d.ts +8 -8
  196. package/dist/realtime.d.ts +4 -4
  197. package/dist/realtime.js +38 -11
  198. package/dist/realtime.js.map +1 -1
  199. package/dist/session-context.d.ts +28 -2
  200. package/dist/session-ui.d.ts +14 -14
  201. package/dist/session-ui.js +5 -4
  202. package/dist/session.d.ts +28 -28
  203. package/dist/session.js +10 -10
  204. package/dist/timeline/activity-rail.d.ts +5 -3
  205. package/dist/timeline/disclosure-context.d.ts +1 -1
  206. package/dist/timeline/fleet-decision-projection.d.ts +1 -1
  207. package/dist/timeline/fleet-decision-row.d.ts +3 -3
  208. package/dist/timeline/index.d.ts +19 -19
  209. package/dist/timeline/parsers.d.ts +4 -1
  210. package/dist/timeline/projection.d.ts +3 -3
  211. package/dist/timeline/registry.d.ts +8 -2
  212. package/dist/timeline/screenshot-lightbox.d.ts +2 -2
  213. package/dist/timeline/shared.d.ts +21 -10
  214. package/dist/timeline/tool-diff.d.ts +2 -2
  215. package/dist/timeline/tool-renderers.d.ts +1 -1
  216. package/dist/timeline/turn-summary.d.ts +3 -3
  217. package/dist/timeline/types.d.ts +44 -3
  218. package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
  219. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
  220. package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
  221. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
  222. package/package.json +36 -7
  223. package/src/artifacts-document.ts +31 -0
  224. package/src/artifacts-presentation.ts +36 -0
  225. package/src/artifacts-spreadsheet.ts +21 -0
  226. package/src/artifacts.ts +89 -0
  227. package/src/client.ts +157 -1
  228. package/src/clipboard.ts +2 -0
  229. package/src/components/artifacts/artifact-surface.tsx +93 -0
  230. package/src/components/artifacts/document-editor.tsx +2255 -0
  231. package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
  232. package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
  233. package/src/components/artifacts/editable-document.tsx +657 -0
  234. package/src/components/artifacts/editable-presentation.tsx +1020 -0
  235. package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
  236. package/src/components/artifacts/index.ts +104 -0
  237. package/src/components/artifacts/presentation-editor.tsx +3148 -0
  238. package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
  239. package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
  240. package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
  241. package/src/components/browser-input.ts +65 -0
  242. package/src/components/browser-viewer.tsx +2702 -0
  243. package/src/components/chat-composer.tsx +13 -0
  244. package/src/components/code-editor.tsx +89 -30
  245. package/src/components/composer-transcription-control.tsx +10 -6
  246. package/src/components/computer-viewer.tsx +1636 -0
  247. package/src/components/desktop-viewer.tsx +215 -74
  248. package/src/components/generated-video-player.tsx +89 -0
  249. package/src/components/interaction-intervention-banner.tsx +99 -0
  250. package/src/components/interactive-workbench-viewers.ts +2 -0
  251. package/src/components/machine-input-display.ts +3 -0
  252. package/src/components/machines/machine-detail.tsx +1 -1
  253. package/src/components/message-timeline.tsx +289 -32
  254. package/src/components/model-policy-picker.tsx +15 -0
  255. package/src/components/queue-surface-implementation.tsx +116 -60
  256. package/src/components/sandbox-files.tsx +2 -2
  257. package/src/components/sandbox-terminal.tsx +128 -46
  258. package/src/components/sandbox-workspace.tsx +256 -90
  259. package/src/components/session-chrome.tsx +53 -13
  260. package/src/components/timeline-annotation-selection.tsx +221 -0
  261. package/src/components/timeline-annotations-dialog.tsx +213 -0
  262. package/src/components/timeline-annotations.tsx +84 -0
  263. package/src/components/workbench-changes.tsx +188 -31
  264. package/src/components/workspace-dock.tsx +59 -5
  265. package/src/hooks/use-attached-browsers.ts +154 -0
  266. package/src/hooks/use-auth-runs.ts +278 -0
  267. package/src/hooks/use-browser-downloads.ts +218 -0
  268. package/src/hooks/use-browser-frame-stream.ts +425 -0
  269. package/src/hooks/use-browser-identities.ts +266 -0
  270. package/src/hooks/use-browser-session.ts +543 -0
  271. package/src/hooks/use-browser-sessions.ts +286 -0
  272. package/src/hooks/use-channels.ts +99 -0
  273. package/src/hooks/use-composer.ts +769 -24
  274. package/src/hooks/use-computer-frame-stream.ts +434 -0
  275. package/src/hooks/use-computer-session.ts +462 -0
  276. package/src/hooks/use-computer-sessions.ts +251 -0
  277. package/src/hooks/use-desktop-stream.ts +39 -7
  278. package/src/hooks/use-interaction-interventions.ts +242 -0
  279. package/src/hooks/use-interaction-invalidation.ts +66 -0
  280. package/src/hooks/use-machine-chip.ts +15 -3
  281. package/src/hooks/use-machines.ts +33 -3
  282. package/src/hooks/use-network-routes.ts +214 -0
  283. package/src/hooks/use-packs.ts +51 -0
  284. package/src/hooks/use-sandbox-files.ts +98 -55
  285. package/src/hooks/use-sandbox-git.ts +93 -38
  286. package/src/hooks/use-sandbox-terminal.ts +3 -2
  287. package/src/hooks/use-session-capabilities.ts +59 -46
  288. package/src/hooks/use-site-auth-connections.ts +226 -0
  289. package/src/hooks/use-supergrok-accounts.ts +81 -0
  290. package/src/hooks/use-terminal-stream.ts +359 -47
  291. package/src/hooks/use-video-artifact-playback.ts +28 -0
  292. package/src/hooks/use-workspace-edit.ts +2 -1
  293. package/src/index.ts +18 -0
  294. package/src/interaction.ts +101 -0
  295. package/src/lib/host-platform.ts +10 -0
  296. package/src/lib/relay-wire.ts +47 -4
  297. package/src/lib/sandbox-liveness.ts +11 -0
  298. package/src/lib/terminal-capability.ts +10 -0
  299. package/src/model-policy.ts +19 -4
  300. package/src/provider.tsx +133 -1
  301. package/src/realtime/realtime-control.tsx +34 -6
  302. package/src/session-context.ts +156 -1
  303. package/src/timeline/activity-rail.tsx +25 -3
  304. package/src/timeline/index.ts +3 -0
  305. package/src/timeline/parsers.ts +14 -1
  306. package/src/timeline/projection.ts +269 -14
  307. package/src/timeline/registry.ts +13 -1
  308. package/src/timeline/screenshot-lightbox.tsx +19 -9
  309. package/src/timeline/shared.tsx +56 -8
  310. package/src/timeline/tool-renderers.tsx +303 -50
  311. package/src/timeline/types.ts +57 -3
  312. package/styles/compiled.css +312 -21
  313. package/styles/index.css +15 -0
  314. package/styles/responsive.css +13 -1
  315. package/dist/chunk-23EJ676W.js.map +0 -1
  316. package/dist/chunk-EKZH6IDG.js.map +0 -1
  317. package/dist/chunk-FWXGSNXT.js.map +0 -1
  318. package/dist/chunk-LHLYWG5R.js.map +0 -1
  319. package/dist/chunk-O4VXOBGF.js.map +0 -1
  320. package/dist/chunk-OL6QVXGF.js.map +0 -1
  321. package/dist/chunk-WYA65Y3F.js.map +0 -1
  322. package/dist/chunk-XCNTXWBB.js.map +0 -1
  323. package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
  324. package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
  325. package/dist/queue-surface-implementation-U5JFG7HT.js.map +0 -1
@@ -35,6 +35,7 @@ import {
35
35
  ComposerTranscriptionControl,
36
36
  type ComposerTranscriptionControlProps,
37
37
  } from "./composer-transcription-control";
38
+ import { TimelineAnnotationsChip } from "./timeline-annotations";
38
39
 
39
40
  export { OPEN_WORKSTREAM_CONTROL_EVENT };
40
41
 
@@ -155,6 +156,18 @@ export function ChatComposer({
155
156
  <RestoredResources />
156
157
  <Attachments />
157
158
  {header}
159
+ {composer.annotations && composer.annotations.length > 0 ? (
160
+ <div className="px-3 pt-2 sm:px-4">
161
+ <TimelineAnnotationsChip
162
+ annotations={composer.annotations}
163
+ editable
164
+ focusAnnotationId={composer.annotationReviewTargetId}
165
+ onFocusConsumed={composer.clearAnnotationReviewTarget}
166
+ onUpdate={composer.updateAnnotation}
167
+ onRemove={composer.removeAnnotation}
168
+ />
169
+ </div>
170
+ ) : null}
158
171
  <Input placeholder={placeholder} autoFocus={autoFocus} />
159
172
  {controller.confirmState ? (
160
173
  <Confirmation />
@@ -95,8 +95,13 @@ export type CodeEditorProps = {
95
95
  path: string;
96
96
  /** The decoded text contents to seed the editor with. */
97
97
  initialContents: string;
98
- /** Persist the current buffer. Resolves when the write lands; rejects to surface an error. */
99
- onSave: (contents: string) => Promise<unknown>;
98
+ /**
99
+ * Persist the current buffer against the exact contents it was edited from.
100
+ * The second argument is the immutable compare-and-swap baseline; hosts must
101
+ * not replace it with a newer remote snapshot or concurrent writes can be
102
+ * silently overwritten.
103
+ */
104
+ onSave: (contents: string, expectedContents: string) => Promise<unknown>;
100
105
  /** Explicitly overwrite after a visible expected-content conflict. */
101
106
  onOverwrite?: ((contents: string) => Promise<unknown>) | undefined;
102
107
  /** Re-read the selected file after a visible save conflict. */
@@ -115,6 +120,41 @@ export type CodeEditorProps = {
115
120
  className?: string | undefined;
116
121
  };
117
122
 
123
+ export type EditorBufferState = {
124
+ path: string;
125
+ value: string;
126
+ baseline: string;
127
+ };
128
+
129
+ /**
130
+ * Reconcile a remote file snapshot without destroying unsaved local work.
131
+ *
132
+ * A different path is a new document and always resets. For the same path, a
133
+ * clean editor follows the remote snapshot while a dirty editor retains both
134
+ * its local value and the original CAS baseline. The eventual save will then
135
+ * either succeed against that baseline or surface a typed conflict.
136
+ */
137
+ export function reconcileEditorBuffer(
138
+ current: EditorBufferState,
139
+ incoming: { path: string; contents: string },
140
+ ): EditorBufferState {
141
+ if (current.path === incoming.path && current.value !== current.baseline) {
142
+ return current;
143
+ }
144
+ if (
145
+ current.path === incoming.path &&
146
+ current.value === incoming.contents &&
147
+ current.baseline === incoming.contents
148
+ ) {
149
+ return current;
150
+ }
151
+ return {
152
+ path: incoming.path,
153
+ value: incoming.contents,
154
+ baseline: incoming.contents,
155
+ };
156
+ }
157
+
118
158
  /**
119
159
  * The EDITABLE single-file pane: CodeMirror 6 with a per-language grammar chosen
120
160
  * from the filename, og-* themed, with dirty tracking and a save path wired to
@@ -143,42 +183,45 @@ export function CodeEditor({
143
183
  const [failed, setFailed] = useState(false);
144
184
  const [bundle, setBundle] = useState<EditorBundle | null>(null);
145
185
 
146
- // The live buffer + the baseline it's compared against for dirtiness. The
147
- // baseline resets whenever a *new* file is loaded (path/initialContents change)
148
- // or after a successful save.
149
- const [value, setValue] = useState(initialContents);
150
- const [baseline, setBaseline] = useState(initialContents);
186
+ // The live buffer and its compare-and-swap baseline are one state value so a
187
+ // remote refresh cannot update one without the other.
188
+ const [buffer, setBuffer] = useState<EditorBufferState>(() => ({
189
+ path,
190
+ value: initialContents,
191
+ baseline: initialContents,
192
+ }));
193
+ const { value, baseline } = buffer;
151
194
  const [saving, setSaving] = useState(false);
152
195
  const [saveError, setSaveError] = useState<Error | null>(null);
153
196
  const [savedTick, setSavedTick] = useState(false);
197
+ const saveInFlightRef = useRef(false);
154
198
 
155
199
  const dirty = value !== baseline;
156
200
  const saveConflict =
157
201
  saveError !== null && (saveError as Error & { code?: unknown }).code === "file_write_conflict";
158
202
 
159
- // Reload the buffer when the file identity changes. Guard on initialContents
160
- // too so an external refresh of the SAME path (e.g. fs.changed re-read) reseeds
161
- // a clean buffer but only when the user hasn't got unsaved edits in flight.
162
- // Fire the wake-on-edit intent at most once per opened file — reset on re-seed.
203
+ // Follow path changes and clean same-file refreshes. Dirty same-file buffers
204
+ // intentionally ignore remote snapshots: the original baseline must survive
205
+ // until guarded save, explicit reload, or navigation.
163
206
  const editIntentFiredRef = useRef(false);
164
- const lastSeed = useRef<{ path: string; contents: string }>({ path, contents: initialContents });
165
207
  useEffect(() => {
166
- const seedChanged =
167
- lastSeed.current.path !== path || lastSeed.current.contents !== initialContents;
168
- if (!seedChanged) return;
169
- lastSeed.current = { path, contents: initialContents };
208
+ setBuffer((current) => reconcileEditorBuffer(current, { path, contents: initialContents }));
209
+ }, [path, initialContents]);
210
+
211
+ // A new document is a new edit-intent/error lifecycle. The normal
212
+ // SandboxFiles host also keys the component by path; this keeps standalone
213
+ // consumers correct without relying on that implementation detail.
214
+ useEffect(() => {
170
215
  editIntentFiredRef.current = false;
171
- setValue(initialContents);
172
- setBaseline(initialContents);
173
216
  setSaveError(null);
174
217
  setSavedTick(false);
175
- }, [path, initialContents]);
218
+ }, [path]);
176
219
 
177
220
  // Record a buffer change: the FIRST divergence from the baseline is the edit
178
221
  // intent that warms the box (idempotent — latched per opened file).
179
222
  const noteEdit = useCallback(
180
223
  (next: string) => {
181
- setValue(next);
224
+ setBuffer((current) => ({ ...current, value: next }));
182
225
  setSavedTick(false);
183
226
  if (!editIntentFiredRef.current && next !== baseline) {
184
227
  editIntentFiredRef.current = true;
@@ -246,40 +289,46 @@ export function CodeEditor({
246
289
  const saveRef = useRef<() => void>(() => {});
247
290
 
248
291
  const save = useCallback(async () => {
249
- if (readOnly) return;
292
+ if (readOnly || saveInFlightRef.current) return;
250
293
  // Snapshot the buffer at call time so an in-flight edit can't race the write.
251
294
  const snapshot = value;
252
295
  if (snapshot === baseline) return; // nothing to persist
296
+ saveInFlightRef.current = true;
253
297
  setSaving(true);
254
298
  setSaveError(null);
255
299
  setSavedTick(false);
256
300
  try {
257
- await onSave(snapshot);
258
- setBaseline(snapshot);
259
- lastSeed.current = { path, contents: snapshot };
301
+ await onSave(snapshot, baseline);
302
+ setBuffer((current) =>
303
+ current.path === path ? { ...current, baseline: snapshot } : current,
304
+ );
260
305
  setSavedTick(true);
261
306
  } catch (cause) {
262
307
  setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
263
308
  } finally {
309
+ saveInFlightRef.current = false;
264
310
  setSaving(false);
265
311
  }
266
312
  }, [readOnly, value, baseline, onSave, path]);
267
313
 
268
314
  const overwrite = useCallback(async () => {
269
- if (readOnly || !onOverwrite) return;
315
+ if (readOnly || !onOverwrite || saveInFlightRef.current) return;
270
316
  const snapshot = value;
271
317
  if (snapshot === baseline) return;
318
+ saveInFlightRef.current = true;
272
319
  setSaving(true);
273
320
  setSaveError(null);
274
321
  setSavedTick(false);
275
322
  try {
276
323
  await onOverwrite(snapshot);
277
- setBaseline(snapshot);
278
- lastSeed.current = { path, contents: snapshot };
324
+ setBuffer((current) =>
325
+ current.path === path ? { ...current, baseline: snapshot } : current,
326
+ );
279
327
  setSavedTick(true);
280
328
  } catch (cause) {
281
329
  setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
282
330
  } finally {
331
+ saveInFlightRef.current = false;
283
332
  setSaving(false);
284
333
  }
285
334
  }, [readOnly, onOverwrite, value, baseline, path]);
@@ -306,10 +355,19 @@ export function CodeEditor({
306
355
 
307
356
  const Editor = bundle?.Editor;
308
357
 
358
+ const reload = useCallback(() => {
359
+ setBuffer({ path, value: initialContents, baseline: initialContents });
360
+ editIntentFiredRef.current = false;
361
+ setSaveError(null);
362
+ setSavedTick(false);
363
+ onReload?.();
364
+ }, [initialContents, onReload, path]);
365
+
309
366
  return (
310
367
  <div
311
368
  className={cn("flex h-full min-h-0 flex-col", className)}
312
369
  data-opengeni-code-editor
370
+ data-opengeni-editor-dirty={dirty ? "true" : "false"}
313
371
  style={editorVars}
314
372
  >
315
373
  {/* Save bar: dirty indicator + status + the explicit Save button. */}
@@ -334,7 +392,9 @@ export function CodeEditor({
334
392
  {saveError.message}
335
393
  </span>
336
394
  )}
337
- {!saveError && savedTick && <span className="text-og-xs text-og-status-idle">Saved</span>}
395
+ {!saveError && savedTick && !dirty && (
396
+ <span className="text-og-xs text-og-status-idle">Saved</span>
397
+ )}
338
398
  {readOnly ? (
339
399
  <span className="text-og-xs uppercase tracking-wide text-og-fg-subtle">Read-only</span>
340
400
  ) : !saveConflict ? (
@@ -375,8 +435,7 @@ export function CodeEditor({
375
435
  <button
376
436
  type="button"
377
437
  onClick={() => {
378
- setSaveError(null);
379
- onReload();
438
+ reload();
380
439
  }}
381
440
  className="min-h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-og-xs font-medium text-og-fg hover:bg-og-surface-2 pointer-coarse:min-h-11"
382
441
  >
@@ -143,6 +143,7 @@ export function ComposerTranscriptionControl({
143
143
  });
144
144
  const { status } = transcription;
145
145
  const retrying = status === "retrying";
146
+ const capturing = status === "requesting-permission" || status === "recording";
146
147
 
147
148
  const cancelTranscription = transcription.cancel;
148
149
  useEffect(() => {
@@ -151,11 +152,7 @@ export function ComposerTranscriptionControl({
151
152
  cancelTranscription();
152
153
  }
153
154
  }, [cancelTranscription, status, suppressed]);
154
- const active =
155
- status === "requesting-permission" ||
156
- status === "recording" ||
157
- status === "saving" ||
158
- status === "transcribing";
155
+ const active = capturing || status === "saving" || status === "transcribing";
159
156
  const recoverable =
160
157
  transcription.hasRecoverableRecording &&
161
158
  (retrying || status === "recovered" || status === "transcript-ready" || status === "error");
@@ -207,8 +204,15 @@ export function ComposerTranscriptionControl({
207
204
  animate={{ opacity: 1, width: "auto" }}
208
205
  exit={{ opacity: 0, width: 0 }}
209
206
  transition={{ duration: 0.36, ease: [0.22, 1, 0.36, 1] }}
210
- className={cn("inline-flex min-w-0 overflow-hidden", className)}
207
+ className={cn(
208
+ "inline-flex min-w-0 overflow-hidden",
209
+ // Mobile capture mode reserves the footer for this control. Keep the
210
+ // shell itself from shrinking and clipping its trailing actions.
211
+ capturing && "shrink-0",
212
+ className,
213
+ )}
211
214
  data-transcription-status={status}
215
+ data-transcription-capturing={capturing ? "" : undefined}
212
216
  >
213
217
  <span className="inline-flex min-w-0 items-center gap-1.5">
214
218
  <AnimatePresence mode="popLayout" initial={false}>