@opengeni/react 7.4.0-canary.36855269964001 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +3 -48
  2. package/dist/artifacts-document.js +2 -3
  3. package/dist/artifacts-presentation.js +2 -3
  4. package/dist/artifacts-spreadsheet.js +2 -2
  5. package/dist/artifacts.d.ts +0 -1
  6. package/dist/artifacts.js +1402 -505
  7. package/dist/artifacts.js.map +1 -1
  8. package/dist/{browser-viewer-R6KK5VKR.js → browser-viewer-2E6R2Y4W.js} +4 -4
  9. package/dist/{chunk-WUISZ3WU.js → chunk-3E2AITTZ.js} +28 -32
  10. package/dist/{chunk-WUISZ3WU.js.map → chunk-3E2AITTZ.js.map} +1 -1
  11. package/dist/{chunk-PWUILT2U.js → chunk-5AZSK6MV.js} +2 -2
  12. package/dist/{chunk-Y2PRMYLC.js → chunk-DUEBTL3N.js} +3 -4
  13. package/dist/{chunk-Y2PRMYLC.js.map → chunk-DUEBTL3N.js.map} +1 -1
  14. package/dist/{chunk-7UAMYXJY.js → chunk-KNDIXZN6.js} +18 -25
  15. package/dist/{chunk-7UAMYXJY.js.map → chunk-KNDIXZN6.js.map} +1 -1
  16. package/dist/{chunk-P4G6K2E2.js → chunk-L4OSXQ6H.js} +884 -1119
  17. package/dist/chunk-L4OSXQ6H.js.map +1 -0
  18. package/dist/{chunk-LLIH7Q4B.js → chunk-S5JQ4R22.js} +19 -47
  19. package/dist/chunk-S5JQ4R22.js.map +1 -0
  20. package/dist/{chunk-A4UJB2MM.js → chunk-SEEVCT36.js} +2 -2
  21. package/dist/{chunk-UXJCQ5GH.js → chunk-UDD6DLLT.js} +1 -6
  22. package/dist/chunk-UDD6DLLT.js.map +1 -0
  23. package/dist/chunk-UGMKPUEM.js +299 -0
  24. package/dist/chunk-UGMKPUEM.js.map +1 -0
  25. package/dist/{chunk-ZCZHKJKZ.js → chunk-USXVQACU.js} +3 -3
  26. package/dist/{chunk-VABBF52G.js → chunk-V2ABIVYJ.js} +6 -70
  27. package/dist/chunk-V2ABIVYJ.js.map +1 -0
  28. package/dist/{chunk-TVO6GXR6.js → chunk-X4HIN6TV.js} +162 -71
  29. package/dist/chunk-X4HIN6TV.js.map +1 -0
  30. package/dist/{chunk-IIDEIBMC.js → chunk-XEJEOAOV.js} +38 -49
  31. package/dist/chunk-XEJEOAOV.js.map +1 -0
  32. package/dist/{chunk-ZKLCPBWN.js → chunk-Y3QTZN2F.js} +5 -5
  33. package/dist/{chunk-73BUNGLF.js → chunk-YZ3YYMOM.js} +1 -1
  34. package/dist/components/artifacts/editable-artifact-ui.d.ts +1 -2
  35. package/dist/components/artifacts/index.d.ts +0 -7
  36. package/dist/components/browser-select-control.d.ts +1 -3
  37. package/dist/components/message-timeline.d.ts +1 -18
  38. package/dist/components/open-geni-links.d.ts +0 -16
  39. package/dist/components/session-conversation.d.ts +1 -20
  40. package/dist/composer.js +3 -3
  41. package/dist/{computer-viewer-DJHPSP2J.js → computer-viewer-WMJE6OA3.js} +3 -3
  42. package/dist/connect.js +6 -7
  43. package/dist/connect.js.map +1 -1
  44. package/dist/hooks/latest-question.d.ts +1 -1
  45. package/dist/index.d.ts +2 -5
  46. package/dist/index.js +28 -39
  47. package/dist/index.js.map +1 -1
  48. package/dist/interaction.js +4 -4
  49. package/dist/{latest-question-KGCDK3XX.js → latest-question-QB4EUCW5.js} +3 -3
  50. package/dist/lib/format.d.ts +0 -3
  51. package/dist/machines.js +2 -2
  52. package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-PFUEZ2U4.js} +5 -37
  53. package/dist/markdown-table-layout-PFUEZ2U4.js.map +1 -0
  54. package/dist/{sandbox-files-4PMXDSDV.js → sandbox-files-PXPIZAFG.js} +2 -2
  55. package/dist/session-ui.d.ts +2 -5
  56. package/dist/session-ui.js +13 -23
  57. package/dist/session-ui.js.map +1 -1
  58. package/dist/session.js +3 -3
  59. package/dist/sites.js +289 -4
  60. package/dist/sites.js.map +1 -1
  61. package/dist/timeline/types.d.ts +1 -7
  62. package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-ODLVF4OI.js} +1 -6
  63. package/dist/timeline-annotation-selection-ODLVF4OI.js.map +1 -0
  64. package/package.json +3 -7
  65. package/src/artifacts.ts +0 -37
  66. package/src/components/artifacts/editable-artifact-ui.tsx +1 -3
  67. package/src/components/artifacts/editable-artifact-workbench.tsx +2 -5
  68. package/src/components/artifacts/editable-document.tsx +36 -34
  69. package/src/components/artifacts/editable-presentation.tsx +16 -21
  70. package/src/components/artifacts/index.ts +0 -41
  71. package/src/components/browser-select-control.tsx +1 -4
  72. package/src/components/browser-viewer.tsx +22 -81
  73. package/src/components/computer-viewer.tsx +23 -51
  74. package/src/components/markdown-table-layout.ts +5 -49
  75. package/src/components/markdown.tsx +2 -5
  76. package/src/components/message-timeline.tsx +434 -515
  77. package/src/components/open-geni-links.tsx +0 -24
  78. package/src/components/session-conversation.tsx +20 -131
  79. package/src/components/timeline-annotation-selection.tsx +0 -7
  80. package/src/hooks/use-available-models.ts +1 -1
  81. package/src/index.ts +0 -9
  82. package/src/lib/format.ts +0 -13
  83. package/src/session-ui.ts +0 -9
  84. package/src/timeline/projection.ts +4 -78
  85. package/src/timeline/types.ts +0 -7
  86. package/styles/compiled.css +0 -325
  87. package/dist/chat-interactive-block-VAGABN7J.js +0 -11
  88. package/dist/chunk-5CVXZA3Y.js +0 -292
  89. package/dist/chunk-5CVXZA3Y.js.map +0 -1
  90. package/dist/chunk-B3V6TG77.js +0 -41
  91. package/dist/chunk-B3V6TG77.js.map +0 -1
  92. package/dist/chunk-IIDEIBMC.js.map +0 -1
  93. package/dist/chunk-LLIH7Q4B.js.map +0 -1
  94. package/dist/chunk-P4G6K2E2.js.map +0 -1
  95. package/dist/chunk-RAIQYITC.js +0 -1772
  96. package/dist/chunk-RAIQYITC.js.map +0 -1
  97. package/dist/chunk-SCOTPE2I.js +0 -589
  98. package/dist/chunk-SCOTPE2I.js.map +0 -1
  99. package/dist/chunk-TVO6GXR6.js.map +0 -1
  100. package/dist/chunk-UXJCQ5GH.js.map +0 -1
  101. package/dist/chunk-VABBF52G.js.map +0 -1
  102. package/dist/chunk-WEHMPOXK.js +0 -39
  103. package/dist/chunk-WEHMPOXK.js.map +0 -1
  104. package/dist/chunk-XCHWDJ22.js +0 -107
  105. package/dist/chunk-XCHWDJ22.js.map +0 -1
  106. package/dist/components/artifacts/artifact-chrome.d.ts +0 -120
  107. package/dist/components/artifacts/artifact-sandbox.d.ts +0 -27
  108. package/dist/components/artifacts/chat-interactive-block.d.ts +0 -29
  109. package/dist/components/artifacts/deferred-chat-media.d.ts +0 -15
  110. package/dist/components/artifacts/editable-artifact-view.d.ts +0 -46
  111. package/dist/components/artifacts/latest-task-runner.d.ts +0 -15
  112. package/dist/components/artifacts/session-artifact-viewer.d.ts +0 -39
  113. package/dist/components/artifacts/site-view.d.ts +0 -23
  114. package/dist/components/timeline-pill-geometry.d.ts +0 -6
  115. package/dist/components/timeline-pill-placement.d.ts +0 -18
  116. package/dist/components/usage-limit-notice.d.ts +0 -49
  117. package/dist/components/usage-member-list.d.ts +0 -91
  118. package/dist/components/usage-meter.d.ts +0 -73
  119. package/dist/hooks/use-usage.d.ts +0 -40
  120. package/dist/markdown-table-layout-QXSLK4BY.js.map +0 -1
  121. package/dist/sandbox-files-4PMXDSDV.js.map +0 -1
  122. package/dist/timeline/allowance-exhausted-row.d.ts +0 -24
  123. package/dist/timeline-annotation-selection-DMLMM2TX.js.map +0 -1
  124. package/dist/timeline-pill-geometry-2G6EK47V.js +0 -73
  125. package/dist/timeline-pill-geometry-2G6EK47V.js.map +0 -1
  126. package/dist/usage/allowance-copy.d.ts +0 -25
  127. package/dist/usage/summary.d.ts +0 -35
  128. package/dist/usage.d.ts +0 -13
  129. package/dist/usage.js +0 -1073
  130. package/dist/usage.js.map +0 -1
  131. package/src/components/artifacts/artifact-chrome.tsx +0 -413
  132. package/src/components/artifacts/artifact-sandbox.tsx +0 -185
  133. package/src/components/artifacts/chat-interactive-block.tsx +0 -260
  134. package/src/components/artifacts/deferred-chat-media.tsx +0 -67
  135. package/src/components/artifacts/editable-artifact-view.tsx +0 -187
  136. package/src/components/artifacts/latest-task-runner.ts +0 -49
  137. package/src/components/artifacts/session-artifact-viewer.tsx +0 -376
  138. package/src/components/artifacts/site-view.tsx +0 -116
  139. package/src/components/timeline-pill-geometry.ts +0 -112
  140. package/src/components/timeline-pill-placement.ts +0 -81
  141. package/src/components/usage-limit-notice.tsx +0 -179
  142. package/src/components/usage-member-list.tsx +0 -792
  143. package/src/components/usage-meter.tsx +0 -382
  144. package/src/hooks/use-usage.ts +0 -100
  145. package/src/timeline/allowance-exhausted-row.tsx +0 -86
  146. package/src/usage/allowance-copy.ts +0 -64
  147. package/src/usage/summary.ts +0 -93
  148. package/src/usage.ts +0 -50
  149. /package/dist/{browser-viewer-R6KK5VKR.js.map → browser-viewer-2E6R2Y4W.js.map} +0 -0
  150. /package/dist/{chunk-PWUILT2U.js.map → chunk-5AZSK6MV.js.map} +0 -0
  151. /package/dist/{chunk-A4UJB2MM.js.map → chunk-SEEVCT36.js.map} +0 -0
  152. /package/dist/{chunk-ZCZHKJKZ.js.map → chunk-USXVQACU.js.map} +0 -0
  153. /package/dist/{chunk-ZKLCPBWN.js.map → chunk-Y3QTZN2F.js.map} +0 -0
  154. /package/dist/{chunk-73BUNGLF.js.map → chunk-YZ3YYMOM.js.map} +0 -0
  155. /package/dist/{chat-interactive-block-VAGABN7J.js.map → computer-viewer-WMJE6OA3.js.map} +0 -0
  156. /package/dist/{latest-question-KGCDK3XX.js.map → latest-question-QB4EUCW5.js.map} +0 -0
  157. /package/dist/{computer-viewer-DJHPSP2J.js.map → sandbox-files-PXPIZAFG.js.map} +0 -0
@@ -208,11 +208,6 @@ export function BrowserViewer({
208
208
  () => registry.relevantSessions.filter((session) => isLiveBrowser(session)),
209
209
  [registry.relevantSessions],
210
210
  );
211
- const latestRelevantBrowser = registry.relevantSessions[0];
212
- const unavailableBrowser =
213
- latestRelevantBrowser?.lifecycle === "lost" || latestRelevantBrowser?.lifecycle === "failed"
214
- ? latestRelevantBrowser
215
- : undefined;
216
211
  const [selection, setSelection] = useState<BrowserSelection>(() =>
217
212
  initialBrowserSessionId ? { sessionId: initialBrowserSessionId, pinned: true } : null,
218
213
  );
@@ -774,8 +769,7 @@ export function BrowserViewer({
774
769
  );
775
770
  }
776
771
  if (liveSessions.length === 0) {
777
- if (renderEmpty && !unavailableBrowser)
778
- return <>{renderEmpty(() => createBrowser(), creating)}</>;
772
+ if (renderEmpty) return <>{renderEmpty(() => createBrowser(), creating)}</>;
779
773
  return (
780
774
  <div
781
775
  className={cn("flex h-full min-h-0 items-center justify-center bg-og-bg p-6", className)}
@@ -784,22 +778,16 @@ export function BrowserViewer({
784
778
  <span className="mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted">
785
779
  <Globe2Icon className="size-4.5" />
786
780
  </span>
787
- {unavailableBrowser && !attachedGenerationLoss ? (
788
- <BrowserUnavailableNotice session={unavailableBrowser} />
789
- ) : (
790
- <>
791
- <p className="mt-3 text-og-menu font-medium text-og-fg">
792
- {attachedGenerationLoss
793
- ? "Chrome reconnected—open a fresh browser/desktop."
794
- : "No browser open"}
795
- </p>
796
- <p className="mt-1 text-og-control leading-5 text-og-fg-muted">
797
- {attachedGenerationLoss
798
- ? "This Chrome profile is live again, but the previous browser cannot move to the new connection. Use Browser → New browser → Connected Chrome."
799
- : "A browser appears here when this agent—or another agent in the workspace—opens one."}
800
- </p>
801
- </>
802
- )}
781
+ <p className="mt-3 text-og-menu font-medium text-og-fg">
782
+ {attachedGenerationLoss
783
+ ? "Chrome reconnected—open a fresh browser/desktop."
784
+ : "No browser open"}
785
+ </p>
786
+ <p className="mt-1 text-og-control leading-5 text-og-fg-muted">
787
+ {attachedGenerationLoss
788
+ ? "This Chrome profile is live again, but the previous browser cannot move to the new connection. Use Browser → New browser → Connected Chrome."
789
+ : "A browser appears here when this agent—or another agent in the workspace—opens one."}
790
+ </p>
803
791
  <BrowserLaunchMenu
804
792
  attachedBridges={onlineAttachedBridges}
805
793
  attachedDevices={attached.devices}
@@ -867,7 +855,6 @@ export function BrowserViewer({
867
855
  />
868
856
  {!selectedRegistrySession ? (
869
857
  <BrowserUnselectedPanel
870
- unavailableBrowser={unavailableBrowser}
871
858
  peerCount={liveSessions.length}
872
859
  creating={creating}
873
860
  onCreate={() => createBrowser()}
@@ -1199,7 +1186,6 @@ function BrowserToolbar(props: {
1199
1186
  }
1200
1187
 
1201
1188
  function BrowserUnselectedPanel(props: {
1202
- unavailableBrowser: BrowserSession | undefined;
1203
1189
  peerCount: number;
1204
1190
  creating: boolean;
1205
1191
  onCreate: () => void;
@@ -1210,11 +1196,7 @@ function BrowserUnselectedPanel(props: {
1210
1196
  <span className="mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted">
1211
1197
  <Globe2Icon className="size-4.5" />
1212
1198
  </span>
1213
- {props.unavailableBrowser ? (
1214
- <BrowserUnavailableNotice session={props.unavailableBrowser} />
1215
- ) : (
1216
- <p className="mt-3 text-og-menu font-medium text-og-fg">No browser for this agent</p>
1217
- )}
1199
+ <p className="mt-3 text-og-menu font-medium text-og-fg">No browser for this agent</p>
1218
1200
  <p className="mt-1 text-og-control leading-5 text-og-fg-muted">
1219
1201
  {props.peerCount === 1
1220
1202
  ? "One workspace browser is available from the browser menu."
@@ -1234,25 +1216,6 @@ function BrowserUnselectedPanel(props: {
1234
1216
  );
1235
1217
  }
1236
1218
 
1237
- function BrowserUnavailableNotice({ session }: { session: BrowserSession }) {
1238
- const reason =
1239
- session.failureCode === "provider_deadline_rotation"
1240
- ? "The computer running it reached its time limit."
1241
- : session.failureCode === "source_placement_changed"
1242
- ? "This chat moved to another computer."
1243
- : session.lifecycle === "failed"
1244
- ? "It stopped unexpectedly."
1245
- : "The connection to it was lost.";
1246
- return (
1247
- <div role="status">
1248
- <p className="mt-3 text-og-menu font-medium text-og-fg">Browser unavailable</p>
1249
- <p className="mt-1 break-words text-og-control leading-5 text-og-fg-muted">
1250
- {session.name}. {reason} Open a new browser to continue.
1251
- </p>
1252
- </div>
1253
- );
1254
- }
1255
-
1256
1219
  function BrowserSessionGroup(props: {
1257
1220
  label: string;
1258
1221
  sessions: BrowserSession[];
@@ -1756,8 +1719,7 @@ function BrowserProfileMenu(props: {
1756
1719
  ) : null}
1757
1720
  {canSave ? (
1758
1721
  <p className="mt-2 text-[10px] leading-4 text-og-fg-subtle">
1759
- Saving restarts this browser. Save unfinished work on its pages first; unsaved form
1760
- entries may be lost. Other open browsers are unchanged.
1722
+ Saving briefly restarts this browser. Other open browsers are unchanged.
1761
1723
  </p>
1762
1724
  ) : null}
1763
1725
  </div>
@@ -1948,7 +1910,6 @@ function BrowserViewport(props: {
1948
1910
  controllerGeneration: string;
1949
1911
  targetId: string;
1950
1912
  fencedInputBatches?: true | undefined;
1951
- focusedInputObservations?: true | undefined;
1952
1913
  expiresAt: string;
1953
1914
  } | null;
1954
1915
  activityLabel?: string | undefined;
@@ -2001,7 +1962,6 @@ function BrowserViewport(props: {
2001
1962
  } | null>(null);
2002
1963
  const actionRef = useRef(props.onAction);
2003
1964
  const readClipboardRef = useRef(props.onReadClipboard);
2004
- const observeInputRef = useRef(props.onObserveForInput);
2005
1965
  const errorRef = useRef(props.onError);
2006
1966
  const actionTailRef = useRef<Promise<void>>(Promise.resolve());
2007
1967
  const actionQueueEpochRef = useRef(0);
@@ -2018,7 +1978,6 @@ function BrowserViewport(props: {
2018
1978
  const mountedRef = useRef(true);
2019
1979
  actionRef.current = props.onAction;
2020
1980
  readClipboardRef.current = props.onReadClipboard;
2021
- observeInputRef.current = props.onObserveForInput;
2022
1981
  errorRef.current = props.onError;
2023
1982
 
2024
1983
  const clearBufferedInput = useCallback(() => {
@@ -2195,23 +2154,8 @@ function BrowserViewport(props: {
2195
2154
  // frame from that render even when its input fence contains no bytes.
2196
2155
  const receipt = await actionRef.current(dispatchedAction, frame);
2197
2156
  if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
2198
- // Alt+Down explicitly opens a native dropdown from the keyboard.
2199
- // Existing controllers return focused-input metadata for clicks only,
2200
- // so use the ordinary authorized read for this explicit gesture.
2201
- const keyboardSelect =
2202
- dispatchedAction.type === "press" && dispatchedAction.key === "Alt+ArrowDown";
2203
- if ((selectAnchor || keyboardSelect) && inputSequence === inputSequenceRef.current) {
2204
- const observation = keyboardSelect
2205
- ? await observeInputRef.current()
2206
- : receipt.observation;
2207
- if (
2208
- mountedRef.current &&
2209
- epoch === actionQueueEpochRef.current &&
2210
- inputSequence === inputSequenceRef.current &&
2211
- observation
2212
- ) {
2213
- setSelectPopup({ observation, anchor: selectAnchor ?? null });
2214
- }
2157
+ if (selectAnchor && receipt.observation && inputSequence === inputSequenceRef.current) {
2158
+ setSelectPopup({ observation: receipt.observation, anchor: selectAnchor });
2215
2159
  }
2216
2160
  await after?.(receipt);
2217
2161
  })
@@ -2504,7 +2448,6 @@ function BrowserViewport(props: {
2504
2448
  onPaste={paste}
2505
2449
  className="pointer-events-none absolute left-1/2 top-1/2 size-px resize-none overflow-hidden opacity-0"
2506
2450
  aria-label="Browser keyboard input"
2507
- aria-keyshortcuts="Alt+ArrowDown"
2508
2451
  autoCapitalize="off"
2509
2452
  autoCorrect="off"
2510
2453
  spellCheck={false}
@@ -2524,7 +2467,6 @@ function BrowserViewport(props: {
2524
2467
  ) : null}
2525
2468
  {showCanvas ? (
2526
2469
  <BrowserSelectControl
2527
- manualFallback={props.inputBatchAttachment?.focusedInputObservations !== true}
2528
2470
  activation={selectPopup}
2529
2471
  onDismiss={() => inputRef.current?.focus({ preventScroll: true })}
2530
2472
  observe={async () => {
@@ -2798,14 +2740,13 @@ function BrowserDiagnosticsDrawer(props: {
2798
2740
  id="browser-page-title"
2799
2741
  className="text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle"
2800
2742
  >
2801
- Tab diagnostics
2743
+ Current page
2802
2744
  </h3>
2803
- <p className="mt-1 text-og-xs text-og-fg-subtle">Includes earlier pages in this tab.</p>
2804
2745
  <dl className="mt-2 grid grid-cols-2 gap-2">
2805
- <DiagnosticCount label="Console errors" value={diagnostics?.consoleErrorCount} />
2806
- <DiagnosticCount label="Page errors" value={diagnostics?.pageErrorCount} />
2807
- <DiagnosticCount label="Failed requests" value={diagnostics?.failedRequestCount} />
2808
- <DiagnosticCount label="Downloads" value={diagnostics?.downloadCount} />
2746
+ <DiagnosticCount label="Console errors" value={diagnostics?.consoleErrorCount ?? 0} />
2747
+ <DiagnosticCount label="Page errors" value={diagnostics?.pageErrorCount ?? 0} />
2748
+ <DiagnosticCount label="Failed requests" value={diagnostics?.failedRequestCount ?? 0} />
2749
+ <DiagnosticCount label="Downloads" value={diagnostics?.downloadCount ?? 0} />
2809
2750
  </dl>
2810
2751
  {session?.failureCode ? (
2811
2752
  <p className="mt-2 rounded-og-sm border border-og-status-error/30 bg-og-status-error/5 px-2.5 py-2 text-og-xs text-og-danger">
@@ -3103,7 +3044,7 @@ function DiagnosticFact(props: { label: string; value: string }) {
3103
3044
  );
3104
3045
  }
3105
3046
 
3106
- function DiagnosticCount(props: { label: string; value: number | undefined }) {
3047
+ function DiagnosticCount(props: { label: string; value: number }) {
3107
3048
  return (
3108
3049
  <div className="rounded-og-sm border border-og-border bg-og-bg px-2.5 py-2">
3109
3050
  <dt className="text-[10px] text-og-fg-subtle">{props.label}</dt>
@@ -3113,7 +3054,7 @@ function DiagnosticCount(props: { label: string; value: number | undefined }) {
3113
3054
  props.value ? "text-og-fg" : "text-og-fg-muted",
3114
3055
  )}
3115
3056
  >
3116
- {props.value ?? "Unavailable"}
3057
+ {props.value}
3117
3058
  </dd>
3118
3059
  </div>
3119
3060
  );
@@ -289,19 +289,13 @@ export function ComputerViewer({
289
289
  : null;
290
290
  const rfbStream =
291
291
  frames.attachment?.stream.kind === "direct_rfb" ? frames.attachment.stream : null;
292
- const machineLocked = selectedRegistrySession?.failureCode === "machine_locked";
293
- // RFB has one combined input switch; partial native input stays view-only.
294
- const rfbInputEnabled =
295
- !machineLocked &&
296
- computer.session?.capabilities?.pointerInput === true &&
297
- computer.session?.capabilities?.keyboardInput === true;
298
292
  const rfbCapability = useMemo<DesktopStreamCapability | null>(() => {
299
293
  if (!rfbStream || !frames.attachment) return null;
300
294
  const bounds = computer.selectedTarget?.bounds;
301
295
  return {
302
296
  transport: "vnc-ws",
303
297
  client: "novnc",
304
- mode: rfbInputEnabled ? "interactive" : "read-only",
298
+ mode: "interactive",
305
299
  url: rfbStream.url,
306
300
  token: null,
307
301
  expiresAt: frames.attachment.expiresAt,
@@ -313,7 +307,8 @@ export function ComputerViewer({
313
307
  sharedSessionIds: [],
314
308
  reason: null,
315
309
  };
316
- }, [computer.selectedTarget?.bounds, frames.attachment, rfbInputEnabled, rfbStream]);
310
+ }, [computer.selectedTarget?.bounds, frames.attachment, rfbStream]);
311
+ const machineLocked = selectedRegistrySession?.failureCode === "machine_locked";
317
312
  const currentIds = useMemo(() => new Set(relevant.map((session) => session.id)), [relevant]);
318
313
 
319
314
  useEffect(() => {
@@ -457,8 +452,7 @@ export function ComputerViewer({
457
452
 
458
453
  const copyFromRfb = useCallback(
459
454
  (event: ClipboardEvent<HTMLDivElement>) => {
460
- if (!rfbInputEnabled || !rfbStream || computer.session?.capabilities?.clipboard !== true)
461
- return;
455
+ if (!rfbStream || computer.session?.capabilities?.clipboard !== true) return;
462
456
  event.preventDefault();
463
457
  event.stopPropagation();
464
458
  void perform({ type: "clipboard", operation: "copy" }, null)
@@ -470,13 +464,12 @@ export function ComputerViewer({
470
464
  })
471
465
  .catch((cause) => notifyError(cause, "Could not copy from the desktop."));
472
466
  },
473
- [computer, notifyError, perform, rfbInputEnabled, rfbStream],
467
+ [computer, notifyError, perform, rfbStream],
474
468
  );
475
469
 
476
470
  const pasteIntoRfb = useCallback(
477
471
  (text: string): boolean => {
478
- if (!rfbInputEnabled || !rfbStream || computer.session?.capabilities?.clipboard !== true)
479
- return false;
472
+ if (!rfbStream || computer.session?.capabilities?.clipboard !== true) return false;
480
473
  // Keep paste on the canonical ComputerSession action path. RFB
481
474
  // ClientCutText synchronization varies by server and can acknowledge a
482
475
  // local paste without ever updating the remote graphical seat. These two
@@ -491,7 +484,7 @@ export function ComputerViewer({
491
484
  })().catch((cause) => notifyError(cause, "Could not paste into the desktop."));
492
485
  return true;
493
486
  },
494
- [computer, notifyError, perform, rfbInputEnabled, rfbStream],
487
+ [computer, notifyError, perform, rfbStream],
495
488
  );
496
489
 
497
490
  const hideGenericCreate = suppressGenericCreate && liveRelevant.length === 0;
@@ -630,18 +623,13 @@ export function ComputerViewer({
630
623
  <div className="relative min-h-0 flex-1 bg-black" onCopyCapture={copyFromRfb}>
631
624
  <DesktopViewer
632
625
  capability={rfbCapability}
633
- interactive={rfbInputEnabled}
626
+ interactive
634
627
  showControlToggle={false}
635
628
  webSocketProtocols={rfbStream.protocols}
636
629
  onPasteText={pasteIntoRfb}
637
630
  targetPlatform={computer.session?.platform ?? null}
638
631
  className="h-full"
639
632
  />
640
- {!rfbInputEnabled ? (
641
- <div className="pointer-events-none absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full border border-white/10 bg-black/70 px-3 py-1 text-[11px] text-white/80 backdrop-blur">
642
- View only · direct input unavailable
643
- </div>
644
- ) : null}
645
633
  </div>
646
634
  ) : (
647
635
  <ComputerViewport
@@ -660,8 +648,6 @@ export function ComputerViewer({
660
648
  mutating={computer.mutating}
661
649
  backgroundActions={computer.session?.capabilities?.backgroundActions === true}
662
650
  clipboardEnabled={computer.session?.capabilities?.clipboard === true}
663
- pointerInput={computer.session?.capabilities?.pointerInput === true}
664
- keyboardInput={computer.session?.capabilities?.keyboardInput === true}
665
651
  onAction={perform}
666
652
  onReadClipboard={computer.readClipboard}
667
653
  onReconnect={
@@ -1025,8 +1011,6 @@ function ComputerViewport(props: {
1025
1011
  mutating: boolean;
1026
1012
  backgroundActions: boolean;
1027
1013
  clipboardEnabled: boolean;
1028
- pointerInput: boolean;
1029
- keyboardInput: boolean;
1030
1014
  onAction: (action: ComputerAction, frame: ComputerFrame | null) => Promise<void>;
1031
1015
  onReadClipboard: () => Promise<ComputerClipboard>;
1032
1016
  onReconnect: () => void;
@@ -1086,9 +1070,6 @@ function ComputerViewport(props: {
1086
1070
  if (inputRef.current) inputRef.current.value = "";
1087
1071
  }, []);
1088
1072
 
1089
- const pointerInputEnabled = rawInputEnabled && props.pointerInput;
1090
- const keyboardInputEnabled = rawInputEnabled && props.keyboardInput;
1091
-
1092
1073
  const paintQueuedFrames = useCallback(() => {
1093
1074
  if (decodingFrameRef.current) return;
1094
1075
  decodingFrameRef.current = true;
@@ -1178,11 +1159,11 @@ function ComputerViewport(props: {
1178
1159
  }, [clearBufferedInput, paintQueuedFrames, props.frame]);
1179
1160
 
1180
1161
  useLayoutEffect(() => {
1181
- if (!rawInputEnabled || !props.pointerInput || !props.keyboardInput) {
1162
+ if (!rawInputEnabled) {
1182
1163
  actionQueueEpochRef.current += 1;
1183
1164
  clearBufferedInput();
1184
1165
  }
1185
- }, [clearBufferedInput, props.keyboardInput, props.pointerInput, rawInputEnabled]);
1166
+ }, [clearBufferedInput, rawInputEnabled]);
1186
1167
 
1187
1168
  const enqueue = useCallback(
1188
1169
  (
@@ -1267,8 +1248,6 @@ function ComputerViewport(props: {
1267
1248
  if (!frame || props.mutating || !rawInputEnabled || event.button !== 0) return;
1268
1249
  // Preserve the keyboard sink's focus through the canvas pointer default action.
1269
1250
  event.preventDefault();
1270
- if (keyboardInputEnabled) inputRef.current?.focus({ preventScroll: true });
1271
- if (!pointerInputEnabled) return;
1272
1251
  flushPendingWheel();
1273
1252
  flushPendingText();
1274
1253
  pointerStartRef.current = {
@@ -1278,10 +1257,11 @@ function ComputerViewport(props: {
1278
1257
  frame,
1279
1258
  };
1280
1259
  event.currentTarget.setPointerCapture?.(event.pointerId);
1260
+ inputRef.current?.focus({ preventScroll: true });
1281
1261
  };
1282
1262
 
1283
1263
  const pointerUp = (event: PointerEvent<HTMLCanvasElement>) => {
1284
- if (!pointerInputEnabled) return;
1264
+ if (!rawInputEnabled) return;
1285
1265
  const start = pointerStartRef.current;
1286
1266
  pointerStartRef.current = null;
1287
1267
  if (!start || start.pointerId !== event.pointerId) return;
@@ -1346,7 +1326,7 @@ function ComputerViewport(props: {
1346
1326
 
1347
1327
  const contextMenu = (event: MouseEvent<HTMLCanvasElement>) => {
1348
1328
  event.preventDefault();
1349
- if (!pointerInputEnabled) return;
1329
+ if (!rawInputEnabled) return;
1350
1330
  const frame = paintedFrameRef.current;
1351
1331
  if (!frame) return;
1352
1332
  flushPendingWheel();
@@ -1369,7 +1349,7 @@ function ComputerViewport(props: {
1369
1349
  };
1370
1350
 
1371
1351
  const wheel = (event: WheelEvent<HTMLCanvasElement>) => {
1372
- if (!pointerInputEnabled) return;
1352
+ if (!rawInputEnabled) return;
1373
1353
  const frame = paintedFrameRef.current;
1374
1354
  if (!frame) return;
1375
1355
  const at = point(frame, event.clientX, event.clientY);
@@ -1397,7 +1377,7 @@ function ComputerViewport(props: {
1397
1377
  };
1398
1378
 
1399
1379
  const keyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
1400
- if (!keyboardInputEnabled) return;
1380
+ if (!rawInputEnabled) return;
1401
1381
  flushPendingWheel();
1402
1382
  const command = event.metaKey || event.ctrlKey;
1403
1383
  if (
@@ -1418,7 +1398,7 @@ function ComputerViewport(props: {
1418
1398
  };
1419
1399
 
1420
1400
  const copy = (event: ClipboardEvent<HTMLTextAreaElement>) => {
1421
- if (!props.clipboardEnabled || !keyboardInputEnabled) return;
1401
+ if (!props.clipboardEnabled || !rawInputEnabled) return;
1422
1402
  event.preventDefault();
1423
1403
  flushPendingWheel();
1424
1404
  flushPendingText();
@@ -1435,7 +1415,7 @@ function ComputerViewport(props: {
1435
1415
  };
1436
1416
 
1437
1417
  const paste = (event: ClipboardEvent<HTMLTextAreaElement>) => {
1438
- if (!props.clipboardEnabled || !keyboardInputEnabled) return;
1418
+ if (!props.clipboardEnabled || !rawInputEnabled) return;
1439
1419
  event.preventDefault();
1440
1420
  flushPendingWheel();
1441
1421
  flushPendingText();
@@ -1452,7 +1432,7 @@ function ComputerViewport(props: {
1452
1432
  };
1453
1433
 
1454
1434
  const input = (value: string) => {
1455
- if (!value || !keyboardInputEnabled) return;
1435
+ if (!value || !rawInputEnabled) return;
1456
1436
  flushPendingWheel();
1457
1437
  flushPendingClick();
1458
1438
  const pending = pendingTextRef.current;
@@ -1482,7 +1462,7 @@ function ComputerViewport(props: {
1482
1462
  className={cn(
1483
1463
  "absolute inset-0 m-auto max-h-full max-w-full touch-none focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
1484
1464
  !showCanvas && "invisible",
1485
- !pointerInputEnabled && "cursor-default",
1465
+ !rawInputEnabled && "cursor-default",
1486
1466
  )}
1487
1467
  onPointerDown={pointerDown}
1488
1468
  onPointerUp={pointerUp}
@@ -1505,7 +1485,7 @@ function ComputerViewport(props: {
1505
1485
  autoCapitalize="off"
1506
1486
  autoCorrect="off"
1507
1487
  spellCheck={false}
1508
- disabled={!keyboardInputEnabled}
1488
+ disabled={!rawInputEnabled}
1509
1489
  />
1510
1490
  {!showCanvas ? (
1511
1491
  <ComputerViewportFallback
@@ -1522,18 +1502,10 @@ function ComputerViewport(props: {
1522
1502
  <LoaderCircleIcon className="size-3 animate-spin" /> Acting
1523
1503
  </div>
1524
1504
  ) : null}
1525
- {showCanvas && (!rawInputEnabled || !props.pointerInput || !props.keyboardInput) ? (
1505
+ {showCanvas && !rawInputEnabled ? (
1526
1506
  <div className="absolute bottom-3 left-1/2 flex -translate-x-1/2 items-center gap-2 rounded-full border border-white/10 bg-black/70 py-1 pl-3 pr-1 text-[11px] text-white/80 backdrop-blur">
1527
- <span>
1528
- {!rawInputEnabled && (props.pointerInput || props.keyboardInput)
1529
- ? "Background view · use native controls"
1530
- : !props.pointerInput && !props.keyboardInput
1531
- ? "View only · mouse and keyboard unavailable"
1532
- : props.keyboardInput
1533
- ? "Keyboard only · mouse unavailable"
1534
- : "Mouse only · keyboard unavailable"}
1535
- </span>
1536
- {!rawInputEnabled && props.target && (props.pointerInput || props.keyboardInput) ? (
1507
+ <span>Background view · use native controls</span>
1508
+ {props.target ? (
1537
1509
  <button
1538
1510
  type="button"
1539
1511
  disabled={props.mutating}
@@ -16,37 +16,6 @@ export function markdownTableWidth({
16
16
  return Math.max(columnWidth, Math.min(preferredWidth, available));
17
17
  }
18
18
 
19
- function measurementCopy(source: Node, document: Document): Node {
20
- if (source.nodeType === Node.ELEMENT_NODE) {
21
- const element = source as Element;
22
- // Inert prevents focus, not custom-element construction, media loading or
23
- // embedded browsing contexts. Do not clone those host-rendered surfaces.
24
- if (
25
- element.localName.includes("-") ||
26
- ["iframe", "object", "embed", "video", "audio", "canvas", "script"].includes(
27
- element.localName,
28
- )
29
- ) {
30
- const bounds = element.getBoundingClientRect();
31
- const placeholder = document.createElement("span");
32
- placeholder.style.display = "inline-block";
33
- placeholder.style.width = `${bounds.width}px`;
34
- placeholder.style.height = `${bounds.height}px`;
35
- return placeholder;
36
- }
37
- }
38
- const copy = document.importNode(source, false);
39
- if (copy.nodeType === Node.ELEMENT_NODE) {
40
- const element = copy as Element;
41
- element.removeAttribute("id");
42
- for (const attribute of [...element.attributes]) {
43
- if (attribute.name.startsWith("on")) element.removeAttribute(attribute.name);
44
- }
45
- }
46
- for (const child of source.childNodes) copy.appendChild(measurementCopy(child, document));
47
- return copy;
48
- }
49
-
50
19
  /** Loaded only for assistant tables. Keep prose and nested scroll owners intact. */
51
20
  export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLTableElement) {
52
21
  const body = wrapper.parentElement;
@@ -55,8 +24,6 @@ export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLT
55
24
  ?.closest<HTMLElement>("[data-og-timeline-scroller]");
56
25
  if (!body?.classList.contains("og-markdown-body") || !scroller) return;
57
26
 
58
- // Build the static copy without a browsing context before inserting its probe.
59
- const measurementDocument = table.ownerDocument.implementation.createHTMLDocument("");
60
27
  let width: number | null = null;
61
28
  const reset = () => {
62
29
  width = null;
@@ -83,25 +50,14 @@ export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLT
83
50
  const right =
84
51
  panel.left + scroller.clientLeft + scroller.clientWidth - parseFloat(panelStyle.paddingRight);
85
52
 
86
- // Even a temporary max-content width on the live table changes its height
87
- // during forced layout. Browser scroll anchoring can retain that displacement
88
- // after the width is restored. Measure a copy without touching live geometry.
89
- const probe = table.ownerDocument.createElement("div");
90
- probe.setAttribute("aria-hidden", "true");
91
- probe.inert = true;
92
- // A zero-sized, contained box cannot extend the scroller's overflow area,
93
- // including inside transformed ancestors. Keep the same inherited styling.
94
- probe.style.cssText =
95
- "position:absolute;width:0;height:0;overflow:hidden;contain:layout size paint;visibility:hidden;pointer-events:none";
96
- const copy = measurementCopy(table, measurementDocument) as HTMLTableElement;
97
- copy.style.width = "max-content";
98
- probe.append(copy);
53
+ // Measure the original table, preserving its wrapping and TSV copy DOM.
54
+ const previousWidth = table.style.width;
99
55
  let preferred: number;
100
56
  try {
101
- table.parentElement!.append(probe);
102
- preferred = copy.getBoundingClientRect().width;
57
+ table.style.width = "max-content";
58
+ preferred = table.getBoundingClientRect().width;
103
59
  } finally {
104
- probe.remove();
60
+ table.style.width = previousWidth;
105
61
  }
106
62
  const next = markdownTableWidth({
107
63
  columnLeft: column.left,
@@ -391,15 +391,12 @@ function ActionMarkdownLink({
391
391
  type="button"
392
392
  className={cn(
393
393
  MARKDOWN_LINK_CLASS,
394
- "inline-flex min-h-7 cursor-pointer items-center aria-disabled:cursor-wait aria-disabled:opacity-70 pointer-coarse:min-h-11",
394
+ "inline-flex min-h-7 cursor-pointer items-center disabled:cursor-wait disabled:opacity-70 pointer-coarse:min-h-11",
395
395
  )}
396
- // Not `disabled`: disabling the focused control drops focus to the page,
397
- // so a host viewer could not return focus to the link that opened it.
398
- aria-disabled={state === "loading" || undefined}
396
+ disabled={state === "loading"}
399
397
  aria-busy={state === "loading"}
400
398
  title={state === "error" ? "Couldn't open this file. Select to retry." : title}
401
399
  onClick={() => {
402
- if (state === "loading") return;
403
400
  setState("loading");
404
401
  void Promise.resolve()
405
402
  .then(onOpen)