@opengeni/react 7.1.0 → 7.1.2-canary.36079201993001

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 (78) hide show
  1. package/dist/artifacts-document.js +2 -2
  2. package/dist/artifacts-presentation.js +2 -2
  3. package/dist/artifacts-spreadsheet.js +2 -2
  4. package/dist/artifacts.js +4 -4
  5. package/dist/{browser-viewer-MN7MOYQT.js → browser-viewer-K2H3SLIW.js} +2 -2
  6. package/dist/{chunk-4ACTWOOW.js → chunk-6N3AZUOV.js} +31 -14
  7. package/dist/chunk-6N3AZUOV.js.map +1 -0
  8. package/dist/{chunk-KCGXPP5V.js → chunk-6P7T4RPE.js} +7 -3
  9. package/dist/chunk-6P7T4RPE.js.map +1 -0
  10. package/dist/{chunk-SHRCATV6.js → chunk-7J4DUR3E.js} +12 -5
  11. package/dist/chunk-7J4DUR3E.js.map +1 -0
  12. package/dist/{chunk-ZC2DK5SC.js → chunk-AWIX4UAC.js} +88 -7
  13. package/dist/chunk-AWIX4UAC.js.map +1 -0
  14. package/dist/{chunk-UQS7BGNQ.js → chunk-ETA37UZ7.js} +82 -11
  15. package/dist/chunk-ETA37UZ7.js.map +1 -0
  16. package/dist/{chunk-VR4Z73EU.js → chunk-G2SCXNXW.js} +9 -2
  17. package/dist/chunk-G2SCXNXW.js.map +1 -0
  18. package/dist/{chunk-5UONOQ3Y.js → chunk-K3MQZO7R.js} +150 -67
  19. package/dist/chunk-K3MQZO7R.js.map +1 -0
  20. package/dist/{chunk-OWHPKAOV.js → chunk-L6MYP3QU.js} +4 -2
  21. package/dist/{chunk-OWHPKAOV.js.map → chunk-L6MYP3QU.js.map} +1 -1
  22. package/dist/{chunk-VI7HELBI.js → chunk-SJ5HE4ZA.js} +21 -2
  23. package/dist/chunk-SJ5HE4ZA.js.map +1 -0
  24. package/dist/{chunk-XMXRI5JK.js → chunk-ZZ4744OZ.js} +128 -71
  25. package/dist/chunk-ZZ4744OZ.js.map +1 -0
  26. package/dist/components/artifacts/artifact-surface.d.ts +3 -1
  27. package/dist/components/artifacts/document-editor.d.ts +4 -2
  28. package/dist/components/artifacts/editable-document.d.ts +1 -1
  29. package/dist/components/artifacts/editable-presentation.d.ts +1 -1
  30. package/dist/components/artifacts/editable-spreadsheet.d.ts +2 -1
  31. package/dist/components/artifacts/presentation-editor.d.ts +4 -2
  32. package/dist/components/browser-viewer.d.ts +4 -1
  33. package/dist/components/markdown.d.ts +2 -0
  34. package/dist/components/session-capability-frame.d.ts +5 -1
  35. package/dist/composer.js +1 -1
  36. package/dist/connect.js +2 -2
  37. package/dist/hooks/use-browser-session.d.ts +3 -0
  38. package/dist/index.js +7 -6
  39. package/dist/index.js.map +1 -1
  40. package/dist/interaction.js +1 -1
  41. package/dist/session-ui.js +4 -4
  42. package/dist/session.js +1 -1
  43. package/dist/timeline/types.d.ts +2 -0
  44. package/package.json +3 -3
  45. package/src/components/artifacts/artifact-surface.tsx +39 -19
  46. package/src/components/artifacts/document-editor.tsx +77 -9
  47. package/src/components/artifacts/editable-document.tsx +3 -0
  48. package/src/components/artifacts/editable-presentation.tsx +3 -0
  49. package/src/components/artifacts/editable-spreadsheet.tsx +3 -0
  50. package/src/components/artifacts/presentation-editor.tsx +6 -0
  51. package/src/components/browser-viewer.tsx +11 -5
  52. package/src/components/composer-transcription-control.tsx +4 -2
  53. package/src/components/composer.tsx +20 -9
  54. package/src/components/markdown.tsx +9 -2
  55. package/src/components/sandbox-workspace.tsx +1 -0
  56. package/src/components/session-capability-frame.tsx +11 -3
  57. package/src/components/session-mcp-capability-card.tsx +174 -70
  58. package/src/hooks/use-browser-frame-stream.ts +3 -1
  59. package/src/hooks/use-browser-session.ts +107 -3
  60. package/src/hooks/use-voice-input.ts +12 -1
  61. package/src/timeline/compaction-copy.ts +2 -0
  62. package/src/timeline/parsers.ts +2 -2
  63. package/src/timeline/projection.ts +31 -5
  64. package/src/timeline/tool-renderers.tsx +105 -16
  65. package/src/timeline/turn-summary.tsx +10 -2
  66. package/src/timeline/types.ts +2 -0
  67. package/styles/compiled.css +6 -0
  68. package/styles/session-capability.css +2 -0
  69. package/dist/chunk-4ACTWOOW.js.map +0 -1
  70. package/dist/chunk-5UONOQ3Y.js.map +0 -1
  71. package/dist/chunk-KCGXPP5V.js.map +0 -1
  72. package/dist/chunk-SHRCATV6.js.map +0 -1
  73. package/dist/chunk-UQS7BGNQ.js.map +0 -1
  74. package/dist/chunk-VI7HELBI.js.map +0 -1
  75. package/dist/chunk-VR4Z73EU.js.map +0 -1
  76. package/dist/chunk-XMXRI5JK.js.map +0 -1
  77. package/dist/chunk-ZC2DK5SC.js.map +0 -1
  78. /package/dist/{browser-viewer-MN7MOYQT.js.map → browser-viewer-K2H3SLIW.js.map} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  useBrowserSession,
8
8
  useBrowserSessions,
9
9
  useSiteAuthConnections
10
- } from "./chunk-UQS7BGNQ.js";
10
+ } from "./chunk-ETA37UZ7.js";
11
11
  import {
12
12
  ComputerViewer,
13
13
  useComputerFrameStream,
@@ -19,22 +19,22 @@ import {
19
19
  setStartupDetails,
20
20
  useStartupDetails,
21
21
  userMessageLikelyNeedsDisclosure
22
- } from "./chunk-ZC2DK5SC.js";
22
+ } from "./chunk-AWIX4UAC.js";
23
23
  import "./chunk-GEN54DU5.js";
24
24
  import "./chunk-GBPCZH3Y.js";
25
25
  import "./chunk-ZYRGEKWS.js";
26
26
  import "./chunk-WYWRYUNQ.js";
27
27
  import {
28
28
  createOlderHistoryLoadReceipt
29
- } from "./chunk-VI7HELBI.js";
29
+ } from "./chunk-SJ5HE4ZA.js";
30
30
  import "./chunk-PAJU5KKL.js";
31
- import "./chunk-4ACTWOOW.js";
31
+ import "./chunk-6N3AZUOV.js";
32
32
  import "./chunk-3LL47F4U.js";
33
33
  import "./chunk-AKOMC3VO.js";
34
34
  import "./chunk-IOLPHOF4.js";
35
35
  import "./chunk-KZ73RL76.js";
36
36
  import "./chunk-NEXLLTP4.js";
37
- import "./chunk-SHRCATV6.js";
37
+ import "./chunk-7J4DUR3E.js";
38
38
  import "./chunk-2VHARTBL.js";
39
39
  import "./chunk-BRM6FVZA.js";
40
40
  import "./chunk-VZTQ73XT.js";
package/dist/session.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  useSession,
30
30
  useSessionEvents,
31
31
  useTurnQueue
32
- } from "./chunk-VI7HELBI.js";
32
+ } from "./chunk-SJ5HE4ZA.js";
33
33
  import {
34
34
  FILE_ONLY_MESSAGE_TEXT,
35
35
  composeSendInput,
@@ -375,6 +375,8 @@ export type AuthNeededItem = {
375
375
  authorizationUrl: string | null;
376
376
  /** Agent-selected catalog recommendation. The host still owns authorization. */
377
377
  capability?: NonNullable<ToolAuthNeededPayload["capability"]> | null | undefined;
378
+ /** Human-reviewed custom integration proposal. Never treated as catalog authority. */
379
+ setupRequest?: ToolAuthNeededPayload["setupRequest"] | null | undefined;
378
380
  occurredAt: string;
379
381
  };
380
382
  export type TurnOutcome = "complete" | "failed" | "cancelled";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/react",
3
- "version": "7.1.0",
3
+ "version": "7.1.2-canary.36079201993001",
4
4
  "description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -133,8 +133,8 @@
133
133
  "@dnd-kit/utilities": "3.2.2",
134
134
  "@fontsource-variable/noto-sans-arabic": "5.2.10",
135
135
  "@fontsource-variable/noto-sans-jp": "5.2.10",
136
- "@opengeni/connect": "^0.3.0",
137
- "@opengeni/sdk": "^7.1.0",
136
+ "@opengeni/connect": "^0.3.0-canary.36079201993001",
137
+ "@opengeni/sdk": "^7.1.1-canary.36079201993001",
138
138
  "clsx": "^2.1.1",
139
139
  "dequal": "2.0.3",
140
140
  "lucide-react": "^1.8.0",
@@ -13,6 +13,8 @@ export type ArtifactModality = "spreadsheet" | "document" | "presentation";
13
13
  export type ArtifactSurfaceProps = {
14
14
  modality: ArtifactModality;
15
15
  title: string;
16
+ /** An embedding host may already provide the artifact title and navigation. */
17
+ showHeader?: boolean | undefined;
16
18
  subtitle?: ReactNode | undefined;
17
19
  actions?: ReactNode | undefined;
18
20
  children: ReactNode;
@@ -46,6 +48,7 @@ function ModalityIcon({ modality }: { modality: ArtifactModality }) {
46
48
  export function ArtifactSurface({
47
49
  modality,
48
50
  title,
51
+ showHeader = true,
49
52
  subtitle,
50
53
  actions,
51
54
  children,
@@ -63,25 +66,42 @@ export function ArtifactSurface({
63
66
  className,
64
67
  )}
65
68
  >
66
- <header className="flex min-h-11 shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-2 px-3">
67
- <span
68
- aria-hidden
69
- className="grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-surface-3 text-og-fg-muted"
70
- >
71
- <ModalityIcon modality={modality} />
72
- </span>
73
- <div className="min-w-0 flex-1">
74
- <h2 className="truncate text-og-base font-medium text-og-fg">{title}</h2>
75
- {subtitle ? <div className="truncate text-og-xs text-og-fg-muted">{subtitle}</div> : null}
76
- </div>
77
- {busy ? (
78
- <LoaderCircleIcon
79
- aria-label="Updating artifact"
80
- className="size-4 shrink-0 animate-spin text-og-fg-muted"
81
- />
82
- ) : null}
83
- {actions ? <div className="flex shrink-0 items-center gap-1">{actions}</div> : null}
84
- </header>
69
+ {showHeader ? (
70
+ <header className="flex min-h-11 shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-2 px-3">
71
+ <span
72
+ aria-hidden
73
+ className="grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-surface-3 text-og-fg-muted"
74
+ >
75
+ <ModalityIcon modality={modality} />
76
+ </span>
77
+ <div className="min-w-0 flex-1">
78
+ <h2 className="truncate text-og-base font-medium text-og-fg">{title}</h2>
79
+ {subtitle ? (
80
+ <div className="truncate text-og-xs text-og-fg-muted">{subtitle}</div>
81
+ ) : null}
82
+ </div>
83
+ {busy ? (
84
+ <LoaderCircleIcon
85
+ aria-label="Updating artifact"
86
+ className="size-4 shrink-0 animate-spin text-og-fg-muted"
87
+ />
88
+ ) : null}
89
+ {actions ? <div className="flex shrink-0 items-center gap-1">{actions}</div> : null}
90
+ </header>
91
+ ) : (
92
+ <>
93
+ {typeof subtitle === "string" || typeof subtitle === "number" ? (
94
+ <span role="status" className="sr-only">
95
+ {subtitle}
96
+ </span>
97
+ ) : null}
98
+ {actions ? (
99
+ <div className="flex shrink-0 items-center justify-end gap-1 border-b border-og-border px-3 py-1">
100
+ {actions}
101
+ </div>
102
+ ) : null}
103
+ </>
104
+ )}
85
105
 
86
106
  <div className="min-h-0 min-w-0 flex-1">{children}</div>
87
107
 
@@ -204,6 +204,7 @@ export type DocumentProjectionArtifactSurfaceProps = Omit<
204
204
  "ariaLabel" | "className"
205
205
  > & {
206
206
  title: string;
207
+ showHeader?: boolean | undefined;
207
208
  subtitle?: ReactNode | undefined;
208
209
  busy?: boolean | undefined;
209
210
  className?: string | undefined;
@@ -212,6 +213,7 @@ export type DocumentProjectionArtifactSurfaceProps = Omit<
212
213
 
213
214
  export type DocumentArtifactSurfaceProps = Omit<DocumentEditorProps, "ariaLabel" | "className"> & {
214
215
  title: string;
216
+ showHeader?: boolean | undefined;
215
217
  subtitle?: ReactNode | undefined;
216
218
  busy?: boolean | undefined;
217
219
  className?: string | undefined;
@@ -907,10 +909,27 @@ function DocumentEditorCore({
907
909
  </div>
908
910
  </div>
909
911
  ) : (
910
- <div className="relative min-w-full" style={{ height: axis.total }}>
912
+ <div
913
+ className="relative min-w-full"
914
+ style={{
915
+ height: axis.total,
916
+ minWidth:
917
+ layout === "paginated"
918
+ ? pages.reduce(
919
+ (width, page) =>
920
+ Math.max(
921
+ width,
922
+ page.page.widthPt * POINT_TO_CSS_PIXEL * page.renderScale + 32,
923
+ ),
924
+ 0,
925
+ )
926
+ : undefined,
927
+ }}
928
+ >
911
929
  {mountedIndexes.map((pageIndex) => {
912
930
  const page = pages[pageIndex]!;
913
931
  const top = axis.offsets[pageIndex]!;
932
+ const pageWidth = page.page.widthPt * POINT_TO_CSS_PIXEL * page.renderScale;
914
933
  return (
915
934
  <article
916
935
  key={page.key}
@@ -923,19 +942,23 @@ function DocumentEditorCore({
923
942
  className={cn(
924
943
  "absolute overflow-hidden",
925
944
  layout === "paginated"
926
- ? "left-1/2 -translate-x-1/2 border shadow-og-sm"
945
+ ? "border shadow-og-sm"
927
946
  : "left-0 right-0 border-b border-og-border bg-og-surface-1 text-og-fg",
928
947
  )}
929
948
  style={{
930
949
  top,
950
+ // Center against the visible viewport, not the shared width
951
+ // reserved for the widest section in this document.
952
+ left:
953
+ layout === "paginated"
954
+ ? Math.max(16, (measuredViewport.width - pageWidth) / 2)
955
+ : undefined,
931
956
  color: layout === "paginated" ? "#171717" : undefined,
932
957
  backgroundColor: layout === "paginated" ? "#fff" : undefined,
933
958
  borderColor: layout === "paginated" ? "#0000001a" : undefined,
934
- width:
935
- layout === "paginated"
936
- ? page.page.widthPt * POINT_TO_CSS_PIXEL * page.renderScale
937
- : undefined,
959
+ width: layout === "paginated" ? pageWidth : undefined,
938
960
  height: page.height,
961
+ containerType: layout === "continuous" ? "inline-size" : undefined,
939
962
  }}
940
963
  >
941
964
  <MeasuredPageBody
@@ -998,6 +1021,7 @@ function DocumentEditorCore({
998
1021
  export function DocumentArtifactSurface({
999
1022
  document,
1000
1023
  title,
1024
+ showHeader,
1001
1025
  subtitle,
1002
1026
  busy,
1003
1027
  className,
@@ -1008,6 +1032,7 @@ export function DocumentArtifactSurface({
1008
1032
  <ArtifactSurface
1009
1033
  modality="document"
1010
1034
  title={title}
1035
+ showHeader={showHeader}
1011
1036
  subtitle={subtitle}
1012
1037
  busy={busy}
1013
1038
  className={className}
@@ -1026,6 +1051,7 @@ export function DocumentArtifactSurface({
1026
1051
  export function DocumentProjectionArtifactSurface({
1027
1052
  projection,
1028
1053
  title,
1054
+ showHeader,
1029
1055
  subtitle,
1030
1056
  busy,
1031
1057
  className,
@@ -1036,6 +1062,7 @@ export function DocumentProjectionArtifactSurface({
1036
1062
  <ArtifactSurface
1037
1063
  modality="document"
1038
1064
  title={title}
1065
+ showHeader={showHeader}
1039
1066
  subtitle={subtitle}
1040
1067
  busy={busy}
1041
1068
  className={className}
@@ -1382,11 +1409,12 @@ function DocumentTableView({
1382
1409
  viewBlockId: string;
1383
1410
  }) {
1384
1411
  const columnWidths = table.style.columnWidthsPt;
1412
+ const headerColor = tableHeaderTextColor(table.style.headerFill);
1385
1413
  return (
1386
1414
  <div
1387
1415
  data-og-document-block={viewBlockId}
1388
1416
  data-og-block-kind="table"
1389
- className="overflow-x-auto py-2"
1417
+ className="w-full max-w-full overflow-x-auto py-2"
1390
1418
  >
1391
1419
  <table
1392
1420
  className="w-full border-collapse text-left"
@@ -1422,6 +1450,8 @@ function DocumentTableView({
1422
1450
  style={{
1423
1451
  borderColor: table.style.borderColor ?? "#d1d5db",
1424
1452
  background: header ? (table.style.headerFill ?? "#f3f4f6") : undefined,
1453
+ // An explicitly colored run still owns its authored text style.
1454
+ color: header ? headerColor : undefined,
1425
1455
  padding: `${table.style.cellPaddingPt ?? 6}pt`,
1426
1456
  }}
1427
1457
  >
@@ -1443,6 +1473,42 @@ function DocumentTableView({
1443
1473
  );
1444
1474
  }
1445
1475
 
1476
+ /** Choose a theme-independent header color only when both light and dark backdrops allow one. */
1477
+ function tableHeaderTextColor(fill: string | undefined): string | undefined {
1478
+ if (!fill) return "#171717";
1479
+ const hex = /^#([\da-f]{3}|[\da-f]{6}|[\da-f]{8})$/i.exec(fill)?.[1];
1480
+ if (!hex) return undefined;
1481
+ const channels =
1482
+ hex.length === 3
1483
+ ? [...hex].map((digit) => Number.parseInt(digit + digit, 16))
1484
+ : [0, 2, 4].map((index) => Number.parseInt(hex.slice(index, index + 2), 16));
1485
+ const alpha = hex.length === 8 ? Number.parseInt(hex.slice(6), 16) / 255 : 1;
1486
+ const luminanceOn = (backdrop: number) => {
1487
+ const linear = channels.map((channel) => {
1488
+ const normalized = (channel * alpha + backdrop * (1 - alpha)) / 255;
1489
+ return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
1490
+ });
1491
+ return linear[0]! * 0.2126 + linear[1]! * 0.7152 + linear[2]! * 0.0722;
1492
+ };
1493
+ // A paginated page is white; a continuous page follows its theme. For alpha
1494
+ // fills, choose a fixed foreground only if it contrasts on both extremes.
1495
+ const backdrops = alpha < 1 ? [luminanceOn(0), luminanceOn(255)] : [luminanceOn(255)];
1496
+ const darkTextLuminance = ((23 / 255 + 0.055) / 1.055) ** 2.4;
1497
+ const whiteContrast = Math.min(...backdrops.map((value) => 1.05 / (value + 0.05)));
1498
+ const darkContrast = Math.min(
1499
+ ...backdrops.map(
1500
+ (value) =>
1501
+ (Math.max(value, darkTextLuminance) + 0.05) / (Math.min(value, darkTextLuminance) + 0.05),
1502
+ ),
1503
+ );
1504
+ if (Math.max(whiteContrast, darkContrast) < 4.5) {
1505
+ // Opaque midtones need pure black where the softer #171717 cannot meet
1506
+ // normal-text contrast. Translucent fills inherit the theme's text color.
1507
+ return alpha < 1 ? undefined : "#000";
1508
+ }
1509
+ return whiteContrast > darkContrast ? "#fff" : "#171717";
1510
+ }
1511
+
1446
1512
  function ToolbarButton({
1447
1513
  label,
1448
1514
  shortcut,
@@ -1633,7 +1699,9 @@ function responsiveDocumentPageScale(
1633
1699
  ): number {
1634
1700
  if (viewportWidth <= 0) return 1;
1635
1701
  const naturalWidth = page.widthPt * POINT_TO_CSS_PIXEL;
1636
- return Math.min(1, Math.max(0.1, (viewportWidth - 32) / naturalWidth));
1702
+ // Below this scale the page text becomes too small to read in a narrow dock.
1703
+ // The viewport can pan horizontally without changing the chosen layout.
1704
+ return Math.min(1, Math.max(0.75, (viewportWidth - 32) / naturalWidth));
1637
1705
  }
1638
1706
 
1639
1707
  function pageContentStyle(
@@ -1642,7 +1710,7 @@ function pageContentStyle(
1642
1710
  ): CSSProperties {
1643
1711
  if (layout === "continuous") {
1644
1712
  return {
1645
- padding: "24px clamp(24px, 8vw, 96px)",
1713
+ padding: "24px clamp(12px, 4cqw, 72px)",
1646
1714
  };
1647
1715
  }
1648
1716
  return {
@@ -70,6 +70,7 @@ type DocumentCommandState = {
70
70
  export function EditableDocumentArtifactSurface({
71
71
  session,
72
72
  title,
73
+ showHeader,
73
74
  subtitle,
74
75
  readOnly = false,
75
76
  onCommit,
@@ -260,6 +261,7 @@ export function EditableDocumentArtifactSurface({
260
261
  <DocumentProjectionArtifactSurface
261
262
  {...surfaceProps}
262
263
  title={title}
264
+ showHeader={showHeader}
263
265
  subtitle={
264
266
  subtitle ??
265
267
  `${projection.blocks.length} block${projection.blocks.length === 1 ? "" : "s"}${writable ? "" : " · Read only"}`
@@ -274,6 +276,7 @@ export function EditableDocumentArtifactSurface({
274
276
  <ArtifactSurface
275
277
  modality="document"
276
278
  title={title}
279
+ showHeader={showHeader}
277
280
  subtitle={subtitle ?? status}
278
281
  busy={!accessRevoked && !state.error}
279
282
  className={surfaceProps.className}
@@ -89,6 +89,7 @@ type PresentationProjectionState = {
89
89
  export function EditablePresentationArtifactSurface({
90
90
  session,
91
91
  title = "Presentation",
92
+ showHeader,
92
93
  subtitle,
93
94
  readOnly = false,
94
95
  onCommit,
@@ -266,6 +267,7 @@ export function EditablePresentationArtifactSurface({
266
267
  <PresentationProjectionArtifactSurface
267
268
  {...surfaceProps}
268
269
  title={title}
270
+ showHeader={showHeader}
269
271
  subtitle={
270
272
  subtitle ??
271
273
  `${projection.slides.length} slide${projection.slides.length === 1 ? "" : "s"}${writable ? "" : " · Read only"}`
@@ -280,6 +282,7 @@ export function EditablePresentationArtifactSurface({
280
282
  <ArtifactSurface
281
283
  modality="presentation"
282
284
  title={title}
285
+ showHeader={showHeader}
283
286
  subtitle={subtitle ?? status}
284
287
  actions={surfaceProps.actions}
285
288
  busy={!accessRevoked && !state.error}
@@ -62,6 +62,7 @@ export type EditableSpreadsheetArtifactSurfaceProps = Omit<
62
62
  "metadataRevision" | "sheet"
63
63
  > & {
64
64
  title?: string | undefined;
65
+ showHeader?: boolean | undefined;
65
66
  subtitle?: ReactNode | undefined;
66
67
  actions?: ReactNode | undefined;
67
68
  initialSheetId?: string | undefined;
@@ -311,6 +312,7 @@ export function EditableSpreadsheetGrid({
311
312
  export function EditableSpreadsheetArtifactSurface({
312
313
  session,
313
314
  title = "Workbook",
315
+ showHeader,
314
316
  subtitle,
315
317
  actions,
316
318
  initialSheetId,
@@ -392,6 +394,7 @@ export function EditableSpreadsheetArtifactSurface({
392
394
  <ArtifactSurface
393
395
  modality="spreadsheet"
394
396
  title={title}
397
+ showHeader={showHeader}
395
398
  subtitle={
396
399
  subtitle ??
397
400
  (accessRevoked
@@ -334,6 +334,7 @@ export type PresentationProjectionEditorProps = Omit<
334
334
 
335
335
  export type PresentationProjectionArtifactSurfaceProps = PresentationProjectionEditorProps & {
336
336
  title?: string | undefined;
337
+ showHeader?: boolean | undefined;
337
338
  subtitle?: ReactNode | undefined;
338
339
  actions?: ReactNode | undefined;
339
340
  busy?: boolean | undefined;
@@ -360,6 +361,7 @@ export type PresentationEditorProps = {
360
361
 
361
362
  export type PresentationArtifactSurfaceProps = PresentationEditorProps & {
362
363
  title?: string | undefined;
364
+ showHeader?: boolean | undefined;
363
365
  subtitle?: ReactNode | undefined;
364
366
  actions?: ReactNode | undefined;
365
367
  busy?: boolean | undefined;
@@ -1819,6 +1821,7 @@ function PresentationEditorCore({
1819
1821
  export function PresentationArtifactSurface({
1820
1822
  presentation,
1821
1823
  title = "Presentation",
1824
+ showHeader,
1822
1825
  subtitle,
1823
1826
  actions,
1824
1827
  busy,
@@ -1830,6 +1833,7 @@ export function PresentationArtifactSurface({
1830
1833
  <ArtifactSurface
1831
1834
  modality="presentation"
1832
1835
  title={title}
1836
+ showHeader={showHeader}
1833
1837
  subtitle={subtitle ?? `${count} slide${count === 1 ? "" : "s"}`}
1834
1838
  actions={actions}
1835
1839
  busy={busy}
@@ -1844,6 +1848,7 @@ export function PresentationArtifactSurface({
1844
1848
  export function PresentationProjectionArtifactSurface({
1845
1849
  projection,
1846
1850
  title = "Presentation",
1851
+ showHeader,
1847
1852
  subtitle,
1848
1853
  actions,
1849
1854
  busy,
@@ -1855,6 +1860,7 @@ export function PresentationProjectionArtifactSurface({
1855
1860
  <ArtifactSurface
1856
1861
  modality="presentation"
1857
1862
  title={title}
1863
+ showHeader={showHeader}
1858
1864
  subtitle={subtitle ?? `${count} slide${count === 1 ? "" : "s"}`}
1859
1865
  actions={actions}
1860
1866
  busy={busy}
@@ -82,6 +82,9 @@ export type BrowserViewerProps = EmbeddedBrowserInteractionClientOverride & {
82
82
  /** The selected OpenGeni agent/session. Peer BrowserSessions stay discoverable. */
83
83
  sessionId: string;
84
84
  enabled?: boolean | undefined;
85
+ /** Whether the Browser tab is selected. Keeps the mounted media stream warm
86
+ * while pausing semantic reads behind other dock tabs. */
87
+ active?: boolean | undefined;
85
88
  className?: string | undefined;
86
89
  onNotify?: ((notification: BrowserViewerNotification) => void) | undefined;
87
90
  /** Tests/demos only. Production uses the browser's native WebSocket. */
@@ -139,6 +142,7 @@ type BrowserDiagnosticsView = {
139
142
  export function BrowserViewer({
140
143
  sessionId,
141
144
  enabled = true,
145
+ active = true,
142
146
  className,
143
147
  onNotify,
144
148
  webSocketFactory,
@@ -287,6 +291,7 @@ export function BrowserViewer({
287
291
  );
288
292
  const controllerReady = selectedRegistrySession?.lifecycle === "active";
289
293
  const resumeSession = registry.resume;
294
+ const [hasLiveFrame, setHasLiveFrame] = useState(false);
290
295
 
291
296
  const wakeBrowser = useCallback(
292
297
  async (session: BrowserSession, retry = false): Promise<void> => {
@@ -337,17 +342,14 @@ export function BrowserViewer({
337
342
  setResumeAttempt((visible) =>
338
343
  visible?.sessionId === selectedRegistrySession.id ? null : visible,
339
344
  );
340
- return;
341
- }
342
- if (selectedRegistrySession.lifecycle === "suspended") {
343
- void wakeBrowser(selectedRegistrySession);
344
345
  }
345
- }, [selectedRegistrySession, wakeBrowser]);
346
+ }, [selectedRegistrySession]);
346
347
 
347
348
  const browser = useBrowserSession({
348
349
  ...override,
349
350
  browserSessionId: selection?.sessionId ?? null,
350
351
  enabled: enabled && selection !== null && controllerReady,
352
+ semanticObservationEnabled: active && !hasLiveFrame,
351
353
  });
352
354
  useEffect(() => {
353
355
  if (!isSourcePlacementChangedError(browser.error, "browser_session")) return;
@@ -413,6 +415,10 @@ export function BrowserViewer({
413
415
  )
414
416
  ? frames.frame
415
417
  : null;
418
+ const frameIsLive = frames.state === "live" && displayedFrame !== null;
419
+ useEffect(() => {
420
+ setHasLiveFrame(frameIsLive);
421
+ }, [frameIsLive]);
416
422
  const supportsLiveFrames = browser.session?.capabilities.liveFrames === true;
417
423
  const displayConnectionState = supportsLiveFrames
418
424
  ? frames.state === "live" && !displayedFrame
@@ -248,6 +248,7 @@ export function ComposerTranscriptionControl({
248
248
  >
249
249
  <button
250
250
  type="button"
251
+ disabled={composer.disabled && !retrying}
251
252
  onClick={() =>
252
253
  savedTranscript
253
254
  ? void transcription.insertSavedTranscript()
@@ -265,7 +266,7 @@ export function ComposerTranscriptionControl({
265
266
  className={cn(
266
267
  "inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm",
267
268
  "bg-og-fg text-og-bg transition-colors duration-150 motion-reduce:transition-none",
268
- "hover:bg-og-fg-muted pointer-coarse:size-11",
269
+ "hover:bg-og-fg-muted disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-11",
269
270
  )}
270
271
  >
271
272
  {savedTranscript ? (
@@ -280,12 +281,13 @@ export function ComposerTranscriptionControl({
280
281
  <Tip tip={messages.discardRecovered}>
281
282
  <button
282
283
  type="button"
284
+ disabled={composer.disabled}
283
285
  onClick={() => void transcription.discard()}
284
286
  aria-label={messages.discardRecovered}
285
287
  className={cn(
286
288
  "inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm",
287
289
  "text-og-fg-muted transition-colors duration-150 motion-reduce:transition-none",
288
- "hover:bg-og-surface-3 hover:text-og-status-failed pointer-coarse:size-11",
290
+ "hover:bg-og-surface-3 hover:text-og-status-failed disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-11",
289
291
  )}
290
292
  >
291
293
  <Trash2Icon className="size-3.5" />
@@ -457,9 +457,9 @@ export function useChatComposerController({
457
457
  (event: DragEvent<HTMLDivElement>) => {
458
458
  if (!attachments || !dragCarriesFiles(event)) return;
459
459
  event.preventDefault();
460
- setDragging(true);
460
+ if (!disabled) setDragging(true);
461
461
  },
462
- [attachments],
462
+ [attachments, disabled],
463
463
  );
464
464
  const handleDragLeave = useCallback(
465
465
  (event: DragEvent<HTMLDivElement>) => {
@@ -474,11 +474,16 @@ export function useChatComposerController({
474
474
  if (!attachments || !dragCarriesFiles(event)) return;
475
475
  event.preventDefault();
476
476
  setDragging(false);
477
- if (event.dataTransfer.files.length > 0) attachments.addFiles(event.dataTransfer.files);
477
+ if (!disabled && event.dataTransfer.files.length > 0)
478
+ attachments.addFiles(event.dataTransfer.files);
478
479
  },
479
- [attachments],
480
+ [attachments, disabled],
480
481
  );
481
482
 
483
+ useEffect(() => {
484
+ if (disabled) setDragging(false);
485
+ }, [disabled]);
486
+
482
487
  const [notice, setNotice] = useState<Notice | null>(null);
483
488
  const [helpOpen, setHelpOpen] = useState(false);
484
489
  const [confirmState, setConfirmState] = useState<ConfirmState>(null);
@@ -624,17 +629,18 @@ export function useChatComposerController({
624
629
  );
625
630
  const handlePaste = useCallback(
626
631
  (event: ClipboardEvent<HTMLTextAreaElement>) => {
632
+ if (disabled) return;
627
633
  onPaste?.(event);
628
634
  attachments?.addFromPaste(event);
629
635
  },
630
- [attachments, onPaste],
636
+ [attachments, disabled, onPaste],
631
637
  );
632
638
  const handleFileChange = useCallback(
633
639
  (event: ChangeEvent<HTMLInputElement>) => {
634
- if (event.target.files) attachments?.addFiles(event.target.files);
640
+ if (!disabled && event.target.files) attachments?.addFiles(event.target.files);
635
641
  event.target.value = "";
636
642
  },
637
- [attachments],
643
+ [attachments, disabled],
638
644
  );
639
645
 
640
646
  const helpCommands = useMemo(
@@ -975,6 +981,7 @@ export function Attachments() {
975
981
  <AttachmentChips
976
982
  attachments={controller.attachments.attachments}
977
983
  messages={controller.messages}
984
+ disabled={controller.disabled}
978
985
  onRemove={controller.attachments.remove}
979
986
  onRetry={controller.attachments.retry}
980
987
  onRetainPreview={controller.attachments.retainPreview}
@@ -1679,6 +1686,7 @@ function ConfirmBar({
1679
1686
  function AttachmentChips({
1680
1687
  attachments,
1681
1688
  messages,
1689
+ disabled,
1682
1690
  onRemove,
1683
1691
  onRetry,
1684
1692
  onRetainPreview,
@@ -1686,6 +1694,7 @@ function AttachmentChips({
1686
1694
  }: {
1687
1695
  attachments: UseFileAttachmentsResult["attachments"];
1688
1696
  messages: ChatComposerMessages;
1697
+ disabled: boolean;
1689
1698
  onRemove: (id: string) => void;
1690
1699
  onRetry?: ((id: string) => void) | undefined;
1691
1700
  onRetainPreview: UseFileAttachmentsResult["retainPreview"];
@@ -1837,7 +1846,8 @@ function AttachmentChips({
1837
1846
  <button
1838
1847
  type="button"
1839
1848
  onClick={() => onRetry(attachment.id)}
1840
- className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg pointer-coarse:size-10"
1849
+ disabled={disabled}
1850
+ className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-10"
1841
1851
  aria-label={messages.retryAttachment(attachment.name)}
1842
1852
  >
1843
1853
  <RotateCwIcon className="size-3.5" />
@@ -1847,7 +1857,8 @@ function AttachmentChips({
1847
1857
  <button
1848
1858
  type="button"
1849
1859
  onClick={() => onRemove(attachment.id)}
1850
- className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg pointer-coarse:size-10"
1860
+ disabled={disabled}
1861
+ className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-10"
1851
1862
  aria-label={messages.removeAttachment(attachment.name)}
1852
1863
  >
1853
1864
  <XIcon className="size-3.5" />