@opengeni/react 5.1.0-canary.0 → 5.2.0-canary.1

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 (90) hide show
  1. package/README.md +12 -0
  2. package/dist/artifacts.d.ts +2 -0
  3. package/dist/artifacts.js +1391 -2
  4. package/dist/artifacts.js.map +1 -1
  5. package/dist/chunk-2TKI3WEG.js +73 -0
  6. package/dist/chunk-2TKI3WEG.js.map +1 -0
  7. package/dist/{chunk-M2HNWZYR.js → chunk-IHQII5WO.js} +69 -18
  8. package/dist/chunk-IHQII5WO.js.map +1 -0
  9. package/dist/{chunk-5PQJOCX5.js → chunk-KANFU34B.js} +6 -4
  10. package/dist/chunk-KANFU34B.js.map +1 -0
  11. package/dist/{chunk-WS6D5BHH.js → chunk-O4NQNMPG.js} +2393 -2123
  12. package/dist/chunk-O4NQNMPG.js.map +1 -0
  13. package/dist/{chunk-HUMIRRYI.js → chunk-PZ27MCZP.js} +54 -19
  14. package/dist/chunk-PZ27MCZP.js.map +1 -0
  15. package/dist/{chunk-BRFMGY4L.js → chunk-UGMKPUEM.js} +28 -3
  16. package/dist/chunk-UGMKPUEM.js.map +1 -0
  17. package/dist/{chunk-BV5A4OTE.js → chunk-YS24CFDI.js} +44 -10
  18. package/dist/chunk-YS24CFDI.js.map +1 -0
  19. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  20. package/dist/components/artifacts/published-html-artifact-frame.d.ts +2 -0
  21. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  22. package/dist/components/markdown.d.ts +13 -1
  23. package/dist/components/session-chrome.d.ts +6 -1
  24. package/dist/{computer-viewer-7TEZC6Y2.js → computer-viewer-CTFCJVS7.js} +2 -2
  25. package/dist/connect.js +1 -1
  26. package/dist/hooks/use-file-attachments.d.ts +2 -0
  27. package/dist/index.d.ts +6 -1
  28. package/dist/index.js +16 -5
  29. package/dist/index.js.map +1 -1
  30. package/dist/interaction.js +1 -1
  31. package/dist/{platform-activity-row-27LKAWZD.js → platform-activity-row-DUE2W74J.js} +6 -3
  32. package/dist/platform-activity-row-DUE2W74J.js.map +1 -0
  33. package/dist/session-ui.d.ts +2 -0
  34. package/dist/session-ui.js +7 -107
  35. package/dist/session-ui.js.map +1 -1
  36. package/dist/session.js +2 -2
  37. package/dist/sites-ui.d.ts +2 -1
  38. package/dist/sites.js +12 -4
  39. package/dist/sites.js.map +1 -1
  40. package/dist/timeline/activity-rail.d.ts +1 -0
  41. package/dist/timeline/genie-loading.d.ts +9 -1
  42. package/dist/timeline/index.d.ts +3 -1
  43. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  44. package/dist/timeline/platform-activity-row.d.ts +1 -2
  45. package/dist/timeline/retained-image.d.ts +16 -0
  46. package/dist/timeline/rolling-activity.d.ts +9 -0
  47. package/dist/timeline/shared.d.ts +4 -1
  48. package/dist/timeline/turn-summary.d.ts +4 -1
  49. package/dist/timeline/types.d.ts +13 -2
  50. package/package.json +4 -4
  51. package/src/artifacts.ts +2 -0
  52. package/src/components/artifacts/inline-html-document.ts +24 -0
  53. package/src/components/artifacts/published-html-artifact-frame.tsx +32 -2
  54. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  55. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  56. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  57. package/src/components/computer-viewer.tsx +11 -3
  58. package/src/components/copy-button.tsx +7 -2
  59. package/src/components/markdown.tsx +111 -34
  60. package/src/components/message-timeline.tsx +48 -3
  61. package/src/components/session-chrome.tsx +14 -2
  62. package/src/hooks/use-computer-frame-stream.ts +17 -5
  63. package/src/hooks/use-computer-session.ts +36 -1
  64. package/src/hooks/use-file-attachments.ts +4 -1
  65. package/src/hooks/use-goal.ts +3 -1
  66. package/src/index.ts +12 -1
  67. package/src/session-ui.ts +3 -0
  68. package/src/sites-ui.tsx +23 -6
  69. package/src/timeline/activity-rail.tsx +31 -4
  70. package/src/timeline/genie-loading.tsx +18 -4
  71. package/src/timeline/index.ts +4 -0
  72. package/src/timeline/knowledge-receipt.tsx +100 -0
  73. package/src/timeline/platform-activity-row.tsx +5 -3
  74. package/src/timeline/projection.ts +40 -0
  75. package/src/timeline/retained-image.ts +91 -0
  76. package/src/timeline/rolling-activity.tsx +83 -0
  77. package/src/timeline/shared.tsx +33 -12
  78. package/src/timeline/tool-renderers.tsx +55 -91
  79. package/src/timeline/turn-summary.tsx +21 -15
  80. package/src/timeline/types.ts +14 -0
  81. package/styles/compiled.css +122 -0
  82. package/styles/index.css +49 -0
  83. package/dist/chunk-5PQJOCX5.js.map +0 -1
  84. package/dist/chunk-BRFMGY4L.js.map +0 -1
  85. package/dist/chunk-BV5A4OTE.js.map +0 -1
  86. package/dist/chunk-HUMIRRYI.js.map +0 -1
  87. package/dist/chunk-M2HNWZYR.js.map +0 -1
  88. package/dist/chunk-WS6D5BHH.js.map +0 -1
  89. package/dist/platform-activity-row-27LKAWZD.js.map +0 -1
  90. /package/dist/{computer-viewer-7TEZC6Y2.js.map → computer-viewer-CTFCJVS7.js.map} +0 -0
@@ -11,7 +11,8 @@ import {
11
11
  isSiteHttpRequest,
12
12
  serveSiteHttp
13
13
  } from "@opengeni/sdk/site";
14
- import { useEffect, useRef } from "react";
14
+ import { useEffect, useRef, useState } from "react";
15
+ import { resolveSiteClientScript } from "@opengeni/sdk/site-document";
15
16
  import { jsx } from "react/jsx-runtime";
16
17
  var PUBLISHED_HTML_ARTIFACT_IFRAME_SANDBOX = [
17
18
  "allow-downloads",
@@ -31,6 +32,7 @@ var SITE_BRIDGE_BOOTSTRAP_SCRIPT = `<script>(()=>{const key=${JSON.stringify(
31
32
  )}||data.version!==${OPENGENI_SITE_BRIDGE_VERSION}||event.ports.length!==1)return;const port=event.ports[0];const previous=window[key]?.port;if(previous&&previous!==port)previous.close();Object.defineProperty(window,key,{configurable:true,value:{port}});port.start();});})();</script>`;
32
33
  function publishedHtmlArtifactDocument(html, toolBridgeEnabled) {
33
34
  if (!toolBridgeEnabled) return html;
35
+ html = resolveSiteClientScript(html);
34
36
  const doctypeEnd = leadingDoctypeEnd(html);
35
37
  if (doctypeEnd >= 0) {
36
38
  return `${html.slice(0, doctypeEnd + 1)}${SITE_BRIDGE_BOOTSTRAP_SCRIPT}${html.slice(doctypeEnd + 1)}`;
@@ -52,6 +54,25 @@ function leadingDoctypeEnd(html) {
52
54
  }
53
55
  function PublishedHtmlArtifactFrame(props) {
54
56
  const frameRef = useRef(null);
57
+ const [height, setHeight] = useState();
58
+ useEffect(() => {
59
+ if (!props.autoHeight) return;
60
+ const receive = (event) => {
61
+ if (event.source !== frameRef.current?.contentWindow || event.data?.type !== "opengeni.preview.height")
62
+ return;
63
+ const value = event.data.height;
64
+ if (typeof value === "number" && Number.isFinite(value))
65
+ setHeight(Math.min(1200, Math.max(80, value)));
66
+ };
67
+ window.addEventListener("message", receive);
68
+ return () => window.removeEventListener("message", receive);
69
+ }, [props.autoHeight]);
70
+ useEffect(() => {
71
+ frameRef.current?.contentWindow?.postMessage(
72
+ { type: "opengeni.preview.theme", theme: props.theme },
73
+ "*"
74
+ );
75
+ }, [props.theme]);
55
76
  const bridgeRef = useRef(props.toolBridge);
56
77
  const onFrameLoadRef = useRef(() => void 0);
57
78
  const frameLoadPendingRef = useRef(false);
@@ -161,11 +182,15 @@ function PublishedHtmlArtifactFrame(props) {
161
182
  referrerPolicy: "no-referrer",
162
183
  srcDoc: publishedHtmlArtifactDocument(props.html, toolBridgeEnabled),
163
184
  onLoad: () => {
185
+ frameRef.current?.contentWindow?.postMessage(
186
+ { type: "opengeni.preview.theme", theme: props.theme },
187
+ "*"
188
+ );
164
189
  frameLoadPendingRef.current = true;
165
190
  onFrameLoadRef.current();
166
191
  },
167
192
  className: props.className,
168
- style: props.style
193
+ style: { ...props.style, ...props.autoHeight && height ? { height } : {} }
169
194
  }
170
195
  );
171
196
  }
@@ -271,4 +296,4 @@ export {
271
296
  PublishedHtmlArtifactFrame,
272
297
  openGeniSiteBridgePortFromBootstrap
273
298
  };
274
- //# sourceMappingURL=chunk-BRFMGY4L.js.map
299
+ //# sourceMappingURL=chunk-UGMKPUEM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/artifacts/published-html-artifact-frame.tsx"],"sourcesContent":["import {\n OPENGENI_SITE_BRIDGE_BOOTSTRAP_GLOBAL,\n OPENGENI_SITE_BRIDGE_READY,\n OPENGENI_SITE_BRIDGE_RESPONSE,\n OPENGENI_SITE_BRIDGE_VERSION,\n isOpenGeniSiteBridgeCancelMessage,\n isOpenGeniSiteBridgeConnectMessage,\n isOpenGeniSiteBridgeRequestMessage,\n sanitizeOpenGeniSiteToolCallRequest,\n isSiteHttpRequest,\n serveSiteHttp,\n type SiteHttpRequest,\n type OpenGeniSiteBridgeResponseMessage,\n type OpenGeniSiteToolCatalog,\n} from \"@opengeni/sdk/site\";\nimport type {\n ToolGatewayCallRequest,\n ToolGatewayCallResponse,\n ToolGatewayDeclarationsResponse,\n} from \"@opengeni/sdk\";\nimport { useEffect, useRef, useState, type CSSProperties } from \"react\";\nimport { resolveSiteClientScript } from \"@opengeni/sdk/site-document\";\n\nexport const PUBLISHED_HTML_ARTIFACT_IFRAME_SANDBOX = [\n \"allow-downloads\",\n \"allow-forms\",\n \"allow-modals\",\n \"allow-orientation-lock\",\n \"allow-pointer-lock\",\n \"allow-popups\",\n \"allow-popups-to-escape-sandbox\",\n \"allow-presentation\",\n \"allow-scripts\",\n].join(\" \");\n\nexport type PublishedHtmlArtifactFrameProps = {\n html: string;\n autoHeight?: boolean;\n theme?: \"light\" | \"dark\";\n title: string;\n className?: string;\n style?: CSSProperties;\n toolBridge?: PublishedHtmlArtifactToolBridge;\n};\n\nexport type PublishedHtmlArtifactToolBridge = {\n fetch?: (request: SiteHttpRequest, signal: AbortSignal) => Promise<Response>;\n catalog: (options: { signal: AbortSignal }) => Promise<OpenGeniSiteToolCatalog>;\n call: (\n request: ToolGatewayCallRequest,\n options: { signal: AbortSignal },\n ) => Promise<ToolGatewayCallResponse>;\n declarations?: (options: { signal: AbortSignal }) => Promise<ToolGatewayDeclarationsResponse>;\n};\n\nconst SITE_BRIDGE_BOOTSTRAP_SCRIPT = `<script>(()=>{const key=${JSON.stringify(\n OPENGENI_SITE_BRIDGE_BOOTSTRAP_GLOBAL,\n)};const parentWindow=window.parent;if(parentWindow===window)return;window.addEventListener(\"message\",event=>{const data=event.data;if(event.source!==parentWindow||!data||data.type!==${JSON.stringify(\n OPENGENI_SITE_BRIDGE_READY,\n)}||data.version!==${OPENGENI_SITE_BRIDGE_VERSION}||event.ports.length!==1)return;const port=event.ports[0];const previous=window[key]?.port;if(previous&&previous!==port)previous.close();Object.defineProperty(window,key,{configurable:true,value:{port}});port.start();});})();</script>`;\n\nexport function publishedHtmlArtifactDocument(html: string, toolBridgeEnabled: boolean): string {\n if (!toolBridgeEnabled) return html;\n html = resolveSiteClientScript(html);\n const doctypeEnd = leadingDoctypeEnd(html);\n if (doctypeEnd >= 0) {\n return `${html.slice(0, doctypeEnd + 1)}${SITE_BRIDGE_BOOTSTRAP_SCRIPT}${html.slice(doctypeEnd + 1)}`;\n }\n return `${SITE_BRIDGE_BOOTSTRAP_SCRIPT}${html}`;\n}\n\nfunction leadingDoctypeEnd(html: string): number {\n const lower = html.toLowerCase();\n let cursor = html.charCodeAt(0) === 0xfeff ? 1 : 0;\n while (cursor < html.length) {\n while (/\\s/.test(html[cursor] ?? \"\")) cursor += 1;\n if (!html.startsWith(\"<!--\", cursor)) break;\n const commentEnd = html.indexOf(\"-->\", cursor + 4);\n if (commentEnd < 0) return -1;\n cursor = commentEnd + 3;\n }\n if (!lower.startsWith(\"<!doctype\", cursor)) return -1;\n return html.indexOf(\">\", cursor + 9);\n}\n\n/**\n * Render exact published HTML in an opaque-origin iframe. Artifact scripts,\n * external resources, forms, popups, downloads, and the host-filtered tool\n * bridge work without granting parent-origin authority, shared cookies/storage,\n * or top-level navigation.\n */\nexport function PublishedHtmlArtifactFrame(props: PublishedHtmlArtifactFrameProps) {\n const frameRef = useRef<HTMLIFrameElement | null>(null);\n const [height, setHeight] = useState<number>();\n useEffect(() => {\n if (!props.autoHeight) return;\n const receive = (event: MessageEvent) => {\n if (\n event.source !== frameRef.current?.contentWindow ||\n event.data?.type !== \"opengeni.preview.height\"\n )\n return;\n const value = event.data.height;\n if (typeof value === \"number\" && Number.isFinite(value))\n setHeight(Math.min(1200, Math.max(80, value)));\n };\n window.addEventListener(\"message\", receive);\n return () => window.removeEventListener(\"message\", receive);\n }, [props.autoHeight]);\n useEffect(() => {\n frameRef.current?.contentWindow?.postMessage(\n { type: \"opengeni.preview.theme\", theme: props.theme },\n \"*\",\n );\n }, [props.theme]);\n const bridgeRef = useRef(props.toolBridge);\n const onFrameLoadRef = useRef<() => void>(() => undefined);\n const frameLoadPendingRef = useRef(false);\n const toolBridgeEnabled = props.toolBridge !== undefined;\n useEffect(() => {\n bridgeRef.current = props.toolBridge;\n }, [props.toolBridge]);\n useEffect(() => {\n if (!toolBridgeEnabled || typeof window === \"undefined\") return;\n const requests = new SiteBridgeRequestRegistry();\n const attachToolPort = (data: unknown, ports: readonly MessagePort[]) => {\n const port = openGeniSiteBridgePortFromBootstrap(data, ports);\n if (!port) return;\n requests.addPort(port);\n port.addEventListener(\"message\", (portEvent: MessageEvent<unknown>) => {\n const message = portEvent.data;\n if (isOpenGeniSiteBridgeCancelMessage(message)) {\n requests.cancel(port, message.requestId);\n return;\n }\n if (isSiteHttpRequest(message) && portEvent.ports.length === 1) {\n const controller = requests.start(port, message.requestId);\n if (!controller) {\n portEvent.ports[0]!.close();\n return;\n }\n void serveSiteHttp(\n message,\n portEvent.ports[0]!,\n async (request, signal) => {\n const fetch = bridgeRef.current?.fetch;\n if (!fetch) throw new Error(\"This Site host does not support the session SDK\");\n return fetch(request, signal);\n },\n controller.signal,\n ).finally(() => requests.complete(port, message.requestId, controller));\n return;\n }\n if (!isOpenGeniSiteBridgeRequestMessage(message)) return;\n const controller = requests.start(port, message.requestId);\n if (!controller) {\n port.postMessage({\n type: OPENGENI_SITE_BRIDGE_RESPONSE,\n version: OPENGENI_SITE_BRIDGE_VERSION,\n requestId: message.requestId,\n ok: false,\n error: {\n code: \"duplicate_request_id\",\n message: \"A Site tool request with this id is already running\",\n retryable: false,\n },\n } satisfies OpenGeniSiteBridgeResponseMessage);\n return;\n }\n void handleSiteBridgeRequest(bridgeRef.current, message, controller.signal)\n .then((value) => {\n if (!controller.signal.aborted) {\n port.postMessage({\n type: OPENGENI_SITE_BRIDGE_RESPONSE,\n version: OPENGENI_SITE_BRIDGE_VERSION,\n requestId: message.requestId,\n ok: true,\n value,\n } satisfies OpenGeniSiteBridgeResponseMessage);\n }\n })\n .catch((error) => {\n if (!controller.signal.aborted) {\n port.postMessage({\n type: OPENGENI_SITE_BRIDGE_RESPONSE,\n version: OPENGENI_SITE_BRIDGE_VERSION,\n requestId: message.requestId,\n ok: false,\n error: siteBridgeError(error),\n } satisfies OpenGeniSiteBridgeResponseMessage);\n }\n })\n .finally(() => requests.complete(port, message.requestId, controller));\n });\n port.addEventListener(\"messageerror\", () => requests.closePort(port));\n port.start();\n port.postMessage({\n type: OPENGENI_SITE_BRIDGE_READY,\n version: OPENGENI_SITE_BRIDGE_VERSION,\n });\n };\n const documentLease = new SiteBridgeDocumentLease(attachToolPort, () => requests.closeAll());\n const onFrameLoad = () => {\n frameLoadPendingRef.current = false;\n const frameWindow = frameRef.current?.contentWindow ?? null;\n if (!frameWindow) return;\n documentLease.load(frameWindow);\n };\n onFrameLoadRef.current = onFrameLoad;\n if (frameLoadPendingRef.current) {\n frameLoadPendingRef.current = false;\n onFrameLoad();\n }\n return () => {\n onFrameLoadRef.current = () => undefined;\n documentLease.close();\n };\n }, [props.html, toolBridgeEnabled]);\n return (\n <iframe\n ref={frameRef}\n title={props.title}\n sandbox={PUBLISHED_HTML_ARTIFACT_IFRAME_SANDBOX}\n referrerPolicy=\"no-referrer\"\n srcDoc={publishedHtmlArtifactDocument(props.html, toolBridgeEnabled)}\n onLoad={() => {\n frameRef.current?.contentWindow?.postMessage(\n { type: \"opengeni.preview.theme\", theme: props.theme },\n \"*\",\n );\n frameLoadPendingRef.current = true;\n onFrameLoadRef.current();\n }}\n className={props.className}\n style={{ ...props.style, ...(props.autoHeight && height ? { height } : {}) }}\n />\n );\n}\n\nexport class SiteBridgeRequestRegistry {\n private readonly controllersByPort = new Map<MessagePort, Map<string, AbortController>>();\n\n addPort(port: MessagePort): void {\n // Several SDK clients may share one document bootstrap. Document teardown,\n // not another client connecting, revokes all of that document's ports.\n if (!this.controllersByPort.has(port)) this.controllersByPort.set(port, new Map());\n }\n\n start(port: MessagePort, requestId: string): AbortController | null {\n const controllers = this.controllersByPort.get(port);\n if (!controllers || controllers.has(requestId)) return null;\n const controller = new AbortController();\n controllers.set(requestId, controller);\n return controller;\n }\n\n cancel(port: MessagePort, requestId: string): void {\n const controllers = this.controllersByPort.get(port);\n const controller = controllers?.get(requestId);\n if (!controller) return;\n controller.abort(new Error(\"Site tool request cancelled\"));\n controllers!.delete(requestId);\n }\n\n complete(port: MessagePort, requestId: string, controller: AbortController): void {\n const controllers = this.controllersByPort.get(port);\n if (controllers?.get(requestId) === controller) controllers.delete(requestId);\n }\n\n closePort(port: MessagePort): void {\n const controllers = this.controllersByPort.get(port);\n if (!controllers) return;\n for (const controller of controllers.values()) {\n controller.abort(new Error(\"Site tool bridge port closed\"));\n }\n controllers.clear();\n this.controllersByPort.delete(port);\n port.close();\n }\n\n closeAll(): void {\n for (const port of [...this.controllersByPort.keys()]) this.closePort(port);\n }\n}\n\nexport class SiteBridgeDocumentLease {\n private loaded = false;\n private bootstrapPort: MessagePort | null = null;\n\n constructor(\n private readonly attachToolPort: (data: unknown, ports: readonly MessagePort[]) => void,\n private readonly closeActivePorts: () => void,\n private readonly createMessageChannel: () => MessageChannel = () => new MessageChannel(),\n ) {}\n\n load(frameWindow: Pick<Window, \"postMessage\">): boolean {\n if (this.loaded) {\n this.close();\n return false;\n }\n this.loaded = true;\n const channel = this.createMessageChannel();\n this.bootstrapPort = channel.port1;\n channel.port1.addEventListener(\"message\", (event: MessageEvent<unknown>) => {\n this.attachToolPort(event.data, event.ports);\n });\n channel.port1.start();\n frameWindow.postMessage(\n {\n type: OPENGENI_SITE_BRIDGE_READY,\n version: OPENGENI_SITE_BRIDGE_VERSION,\n },\n \"*\",\n [channel.port2],\n );\n return true;\n }\n\n close(): void {\n this.bootstrapPort?.close();\n this.bootstrapPort = null;\n this.closeActivePorts();\n }\n}\n\nexport function openGeniSiteBridgePortFromBootstrap(\n data: unknown,\n ports: readonly MessagePort[],\n): MessagePort | null {\n if (!isOpenGeniSiteBridgeConnectMessage(data)) return null;\n return ports.length === 1 ? ports[0]! : null;\n}\n\nasync function handleSiteBridgeRequest(\n bridge: PublishedHtmlArtifactToolBridge | undefined,\n message: Parameters<typeof isOpenGeniSiteBridgeRequestMessage>[0] & {\n method: \"catalog\" | \"call\" | \"declarations\";\n requestId: string;\n payload?: ToolGatewayCallRequest;\n },\n signal: AbortSignal,\n): Promise<OpenGeniSiteToolCatalog | ToolGatewayCallResponse | ToolGatewayDeclarationsResponse> {\n if (!bridge) throw new Error(\"Site tool bridge is unavailable\");\n if (message.method === \"catalog\") return await bridge.catalog({ signal });\n if (message.method === \"declarations\") {\n if (!bridge.declarations) throw new Error(\"Site tool declarations are unavailable\");\n return await bridge.declarations({ signal });\n }\n return await bridge.call(sanitizeOpenGeniSiteToolCallRequest(message.payload!), { signal });\n}\n\nexport function siteBridgeError(error: unknown): {\n code: string;\n message: string;\n retryable: boolean;\n outcomeUnknown: boolean;\n} {\n const candidate = error as {\n code?: unknown;\n message?: unknown;\n retryable?: unknown;\n outcomeUnknown?: unknown;\n };\n return {\n code: typeof candidate?.code === \"string\" ? candidate.code : \"site_tool_call_failed\",\n message:\n typeof candidate?.message === \"string\" ? candidate.message : \"Site tool request failed\",\n retryable: candidate?.retryable === true,\n outcomeUnknown: candidate?.outcomeUnknown === true,\n };\n}\n"],"mappings":"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","names":["controller"]}
@@ -144,9 +144,18 @@ function useComputerFrameStream(options) {
144
144
  socketHandlers = null;
145
145
  pendingFrame = null;
146
146
  };
147
+ const clearRecoveryTimers = () => {
148
+ if (reconnectTimer) clearTimeout(reconnectTimer);
149
+ if (expiryTimer) clearTimeout(expiryTimer);
150
+ reconnectTimer = null;
151
+ expiryTimer = null;
152
+ };
147
153
  const scheduleReconnect = () => {
148
154
  if (disposed || reconnectTimer) return;
149
- if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) return;
155
+ if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) {
156
+ clearRecoveryTimers();
157
+ return;
158
+ }
150
159
  const delay = Math.min(5e3, 250 * 2 ** Math.min(failures, 5));
151
160
  failures += 1;
152
161
  setResult((current) => ({ ...current, state: "reconnecting" }));
@@ -161,6 +170,7 @@ function useComputerFrameStream(options) {
161
170
  setResult((current) => ({ ...current, state: "error", error }));
162
171
  clearSocket();
163
172
  if (reconnectAutomatically) scheduleReconnect();
173
+ else clearRecoveryTimers();
164
174
  };
165
175
  const onOpen = (source) => {
166
176
  if (disposed || source !== socket) return;
@@ -264,8 +274,7 @@ function useComputerFrameStream(options) {
264
274
  };
265
275
  const onClose = (source) => {
266
276
  if (disposed || source !== socket) return;
267
- clearSocket();
268
- scheduleReconnect();
277
+ fail(new Error("Desktop view lost connection."));
269
278
  };
270
279
  const connectSocket = () => {
271
280
  if (disposed) return;
@@ -327,6 +336,7 @@ function useComputerFrameStream(options) {
327
336
  throw new Error("desktop attachment does not match the requested resource");
328
337
  }
329
338
  controllerGeneration = attachment.controllerGeneration;
339
+ latestRef.current = { key: "", sequence: -1 };
330
340
  activeAttachment = attachment;
331
341
  activeStream = attachment.stream;
332
342
  attachmentExpiresAt = Date.parse(attachment.expiresAt);
@@ -364,8 +374,7 @@ function useComputerFrameStream(options) {
364
374
  disposed = true;
365
375
  pendingFrame = null;
366
376
  attachmentAbort.abort();
367
- if (reconnectTimer) clearTimeout(reconnectTimer);
368
- if (expiryTimer) clearTimeout(expiryTimer);
377
+ clearRecoveryTimers();
369
378
  clearSocket(true);
370
379
  };
371
380
  }, [client, enabled, nonce, options.computerSessionId, options.targetId, workspaceId]);
@@ -454,6 +463,22 @@ function useComputerSession(options) {
454
463
  if (!mountedRef.current || requestRef.current.id !== id) return;
455
464
  const targets = sortComputerTargets(targetResponse.targets);
456
465
  const selected = chooseTarget(targets, selectedTargetIdRef.current, session.platform);
466
+ const previous = observationRef.current.observation;
467
+ const retainedObservation = selected && previous?.target.id === selected.id && previous.target.controllerGeneration === selected.controllerGeneration && previous.target.targetGeneration === selected.targetGeneration ? previous : null;
468
+ selectedTargetIdRef.current = selected?.id ?? null;
469
+ targetsRef.current = { computerSessionId, targets };
470
+ observationRef.current = { computerSessionId, observation: retainedObservation };
471
+ setState(
472
+ (current) => current.computerSessionId === computerSessionId ? {
473
+ ...current,
474
+ session,
475
+ targets,
476
+ selectedTargetId: selected?.id ?? null,
477
+ observation: retainedObservation,
478
+ loading: false,
479
+ error: null
480
+ } : current
481
+ );
457
482
  const observation = selected ? await client.observeComputerTarget(workspaceId, computerSessionId, selected.id, {
458
483
  signal: controller.signal
459
484
  }) : null;
@@ -602,7 +627,10 @@ function useComputerSession(options) {
602
627
  const currentObservation = observationRef.current.computerSessionId === computerSessionId ? observationRef.current.observation : null;
603
628
  const focusTarget = action.type === "focus" ? targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ?? null : null;
604
629
  const frameTarget = frame ? targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ?? (currentObservation?.target.id === frame.targetId ? currentObservation.target : null) : null;
605
- const target = frameTarget ?? focusTarget ?? currentObservation?.target ?? null;
630
+ const inputTarget = (action.type === "keyboard" || action.type === "clipboard") && targetsRef.current.computerSessionId === computerSessionId ? targetsRef.current.targets.find(
631
+ (candidate) => candidate.id === selectedTargetIdRef.current
632
+ ) : null;
633
+ const target = frameTarget ?? focusTarget ?? currentObservation?.target ?? inputTarget ?? null;
606
634
  if (!target) throw new Error("The desktop target is not ready for input.");
607
635
  if (frame && frame.targetId !== selectedTargetIdRef.current) {
608
636
  throw new Error("The displayed desktop frame is no longer selected.");
@@ -1254,7 +1282,12 @@ function ComputerViewer({
1254
1282
  if (relevant[0]) selectComputerSession({ sessionId: relevant[0].id, pinned: false });
1255
1283
  },
1256
1284
  onCreate: hideGenericCreate ? void 0 : createComputer,
1257
- onRefresh: () => void Promise.all([refreshRegistry(), refreshComputer()])
1285
+ onRefresh: () => {
1286
+ if (frames.state === "error" && !generationLossSelected && !generationLossFrames) {
1287
+ frames.reconnect();
1288
+ }
1289
+ void Promise.all([refreshRegistry(), refreshComputer()]);
1290
+ }
1258
1291
  }
1259
1292
  ),
1260
1293
  /* @__PURE__ */ jsx(
@@ -1581,7 +1614,8 @@ function ComputerViewport(props) {
1581
1614
  actionRef.current = props.onAction;
1582
1615
  readClipboardRef.current = props.onReadClipboard;
1583
1616
  errorRef.current = props.onError;
1584
- const rawInputEnabled = !props.backgroundActions || props.target?.kind === "screen" || props.target?.focused === true;
1617
+ const streamFailed = props.connectionState === "error";
1618
+ const rawInputEnabled = !streamFailed && !props.machineLocked && (!props.backgroundActions || props.target?.kind === "screen" || props.target?.focused === true);
1585
1619
  const paintQueuedFrames = useCallback4(() => {
1586
1620
  if (decodingFrameRef.current) return;
1587
1621
  decodingFrameRef.current = true;
@@ -1864,7 +1898,7 @@ function ComputerViewport(props) {
1864
1898
  }
1865
1899
  if (inputRef.current) inputRef.current.value = "";
1866
1900
  };
1867
- const showCanvas = props.frame !== null && !props.machineLocked;
1901
+ const showCanvas = props.frame !== null && !props.machineLocked && !streamFailed;
1868
1902
  return /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-black", children: [
1869
1903
  /* @__PURE__ */ jsx(
1870
1904
  "canvas",
@@ -2297,4 +2331,4 @@ export {
2297
2331
  ComputerViewer,
2298
2332
  computerKey
2299
2333
  };
2300
- //# sourceMappingURL=chunk-BV5A4OTE.js.map
2334
+ //# sourceMappingURL=chunk-YS24CFDI.js.map