@opengeni/react 7.4.0 → 7.5.0-canary.36927100590001
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.
- package/README.md +124 -27
- package/dist/artifacts-document.js +3 -2
- package/dist/artifacts-presentation.js +3 -2
- package/dist/artifacts-spreadsheet.js +2 -2
- package/dist/artifacts.d.ts +1 -0
- package/dist/artifacts.js +516 -1413
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-2E6R2Y4W.js → browser-viewer-Z7S7QEJL.js} +6 -5
- package/dist/chat-interactive-block-UGBTNB3L.js +11 -0
- package/dist/{chunk-Y3QTZN2F.js → chunk-2VFNCX4E.js} +6 -6
- package/dist/{chunk-UDD6DLLT.js → chunk-3IJJEYW4.js} +15 -3
- package/dist/chunk-3IJJEYW4.js.map +1 -0
- package/dist/chunk-4RVYOY4O.js +379 -0
- package/dist/chunk-4RVYOY4O.js.map +1 -0
- package/dist/chunk-5CVXZA3Y.js +292 -0
- package/dist/chunk-5CVXZA3Y.js.map +1 -0
- package/dist/{chunk-5YK2ZDIG.js → chunk-5J4FOW6Q.js} +2 -10
- package/dist/chunk-5J4FOW6Q.js.map +1 -0
- package/dist/{chunk-KVERXCFY.js → chunk-5KYZIQFY.js} +40 -3
- package/dist/chunk-5KYZIQFY.js.map +1 -0
- package/dist/{chunk-KNDIXZN6.js → chunk-7UAMYXJY.js} +25 -18
- package/dist/{chunk-KNDIXZN6.js.map → chunk-7UAMYXJY.js.map} +1 -1
- package/dist/{chunk-SEEVCT36.js → chunk-A4UJB2MM.js} +2 -2
- package/dist/{chunk-X4HIN6TV.js → chunk-AFLPWUQ7.js} +71 -162
- package/dist/chunk-AFLPWUQ7.js.map +1 -0
- package/dist/chunk-B3V6TG77.js +41 -0
- package/dist/chunk-B3V6TG77.js.map +1 -0
- package/dist/{chunk-XEJEOAOV.js → chunk-B7WMS3CS.js} +51 -40
- package/dist/chunk-B7WMS3CS.js.map +1 -0
- package/dist/{chunk-S5JQ4R22.js → chunk-BDKR6BAR.js} +54 -24
- package/dist/chunk-BDKR6BAR.js.map +1 -0
- package/dist/{chunk-4MDLEHXL.js → chunk-DYWBUX5Q.js} +24 -12
- package/dist/chunk-DYWBUX5Q.js.map +1 -0
- package/dist/{chunk-YZ3YYMOM.js → chunk-E3G7SAYZ.js} +252 -624
- package/dist/chunk-E3G7SAYZ.js.map +1 -0
- package/dist/chunk-JT4JW7BC.js +54 -0
- package/dist/chunk-JT4JW7BC.js.map +1 -0
- package/dist/{chunk-5AZSK6MV.js → chunk-LWNLETQT.js} +5 -5
- package/dist/{chunk-USXVQACU.js → chunk-M7LBYP63.js} +5 -4
- package/dist/{chunk-USXVQACU.js.map → chunk-M7LBYP63.js.map} +1 -1
- package/dist/{chunk-L4OSXQ6H.js → chunk-NBOHNRLJ.js} +1289 -1060
- package/dist/chunk-NBOHNRLJ.js.map +1 -0
- package/dist/chunk-RHREZ7BO.js +11 -0
- package/dist/chunk-RHREZ7BO.js.map +1 -0
- package/dist/chunk-SCOTPE2I.js +589 -0
- package/dist/chunk-SCOTPE2I.js.map +1 -0
- package/dist/chunk-WEHMPOXK.js +39 -0
- package/dist/chunk-WEHMPOXK.js.map +1 -0
- package/dist/chunk-WRHIZYZF.js +988 -0
- package/dist/chunk-WRHIZYZF.js.map +1 -0
- package/dist/{chunk-3E2AITTZ.js → chunk-WUISZ3WU.js} +32 -28
- package/dist/{chunk-3E2AITTZ.js.map → chunk-WUISZ3WU.js.map} +1 -1
- package/dist/chunk-XCHWDJ22.js +107 -0
- package/dist/chunk-XCHWDJ22.js.map +1 -0
- package/dist/{chunk-DUEBTL3N.js → chunk-Y2PRMYLC.js} +4 -3
- package/dist/{chunk-DUEBTL3N.js.map → chunk-Y2PRMYLC.js.map} +1 -1
- package/dist/chunk-YBXQDQFX.js +1772 -0
- package/dist/chunk-YBXQDQFX.js.map +1 -0
- package/dist/chunk-YV4L3ILF.js +9 -0
- package/dist/chunk-YV4L3ILF.js.map +1 -0
- package/dist/{chunk-V2ABIVYJ.js → chunk-ZXSEDZP7.js} +70 -6
- package/dist/chunk-ZXSEDZP7.js.map +1 -0
- package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
- package/dist/components/artifacts/artifact-sandbox.d.ts +27 -0
- package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
- package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
- package/dist/components/artifacts/editable-artifact-view.d.ts +46 -0
- package/dist/components/artifacts/index.d.ts +7 -0
- package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
- package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
- package/dist/components/artifacts/site-view.d.ts +23 -0
- package/dist/components/browser-select-control.d.ts +3 -1
- package/dist/components/code-editor.d.ts +1 -4
- package/dist/components/desktop-viewer.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +20 -3
- package/dist/components/open-geni-links.d.ts +16 -0
- package/dist/components/session-conversation.d.ts +20 -1
- package/dist/components/timeline-pill-geometry.d.ts +8 -0
- package/dist/components/timeline-pill-placement.d.ts +24 -0
- package/dist/components/usage-limit-notice.d.ts +49 -0
- package/dist/components/usage-member-list.d.ts +91 -0
- package/dist/components/usage-meter.d.ts +73 -0
- package/dist/composer.js +5 -5
- package/dist/{computer-viewer-WMJE6OA3.js → computer-viewer-KJWLH3GI.js} +7 -5
- package/dist/connect.js +7 -6
- package/dist/connect.js.map +1 -1
- package/dist/conversation-timeline.d.ts +1 -1
- package/dist/desktop.d.ts +7 -0
- package/dist/desktop.js +26 -0
- package/dist/desktop.js.map +1 -0
- package/dist/editor.d.ts +7 -0
- package/dist/editor.js +21 -0
- package/dist/editor.js.map +1 -0
- package/dist/hooks/latest-question.d.ts +1 -1
- package/dist/hooks/use-composer.d.ts +4 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-usage.d.ts +40 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +460 -1386
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +8 -6
- package/dist/interaction.js.map +1 -1
- package/dist/{latest-question-QB4EUCW5.js → latest-question-IS445HOW.js} +3 -3
- package/dist/lib/format.d.ts +6 -1
- package/dist/lib/workbench-peers.d.ts +36 -0
- package/dist/machines.js +2 -2
- package/dist/{markdown-table-layout-PFUEZ2U4.js → markdown-table-layout-QXSLK4BY.js} +37 -5
- package/dist/markdown-table-layout-QXSLK4BY.js.map +1 -0
- package/dist/{queue-surface-implementation-IM2HD3SK.js → queue-surface-implementation-2CAQAIHM.js} +2 -2
- package/dist/realtime.js +3 -3
- package/dist/{sandbox-files-PXPIZAFG.js → sandbox-files-AEJTWGT4.js} +8 -5
- package/dist/sandbox-files-AEJTWGT4.js.map +1 -0
- package/dist/session-ui.d.ts +9 -2
- package/dist/session-ui.js +39 -20
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +4 -4
- package/dist/sites.js +4 -289
- package/dist/sites.js.map +1 -1
- package/dist/terminal.d.ts +8 -0
- package/dist/terminal.js +31 -0
- package/dist/terminal.js.map +1 -0
- package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
- package/dist/timeline/genie-loading.d.ts +3 -1
- package/dist/timeline/types.d.ts +8 -1
- package/dist/{timeline-annotation-selection-ODLVF4OI.js → timeline-annotation-selection-DMLMM2TX.js} +6 -1
- package/dist/timeline-annotation-selection-DMLMM2TX.js.map +1 -0
- package/dist/timeline-pill-geometry-Z23V243M.js +110 -0
- package/dist/timeline-pill-geometry-Z23V243M.js.map +1 -0
- package/dist/usage/allowance-copy.d.ts +25 -0
- package/dist/usage/summary.d.ts +35 -0
- package/dist/usage.d.ts +13 -0
- package/dist/usage.js +1073 -0
- package/dist/usage.js.map +1 -0
- package/package.json +19 -3
- package/src/artifacts.ts +37 -0
- package/src/components/artifacts/artifact-chrome.tsx +413 -0
- package/src/components/artifacts/artifact-sandbox.tsx +185 -0
- package/src/components/artifacts/chat-interactive-block.tsx +260 -0
- package/src/components/artifacts/deferred-chat-media.tsx +67 -0
- package/src/components/artifacts/editable-artifact-ui.tsx +3 -1
- package/src/components/artifacts/editable-artifact-view.tsx +187 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
- package/src/components/artifacts/editable-document.tsx +34 -36
- package/src/components/artifacts/editable-presentation.tsx +21 -16
- package/src/components/artifacts/index.ts +41 -0
- package/src/components/artifacts/latest-task-runner.ts +49 -0
- package/src/components/artifacts/session-artifact-viewer.tsx +376 -0
- package/src/components/artifacts/site-view.tsx +116 -0
- package/src/components/browser-select-control.tsx +4 -1
- package/src/components/browser-viewer.tsx +81 -22
- package/src/components/code-editor.tsx +13 -16
- package/src/components/computer-viewer.tsx +51 -23
- package/src/components/desktop-viewer.tsx +1 -1
- package/src/components/markdown-table-layout.ts +49 -5
- package/src/components/markdown.tsx +5 -2
- package/src/components/message-timeline.tsx +557 -516
- package/src/components/open-geni-links.tsx +24 -0
- package/src/components/sandbox-terminal.tsx +48 -11
- package/src/components/session-chrome.tsx +22 -3
- package/src/components/session-conversation.tsx +195 -102
- package/src/components/timeline-annotation-selection.tsx +7 -0
- package/src/components/timeline-pill-geometry.ts +169 -0
- package/src/components/timeline-pill-placement.ts +99 -0
- package/src/components/usage-limit-notice.tsx +179 -0
- package/src/components/usage-member-list.tsx +792 -0
- package/src/components/usage-meter.tsx +382 -0
- package/src/conversation-timeline.ts +10 -2
- package/src/desktop.ts +24 -0
- package/src/editor.ts +13 -0
- package/src/hooks/use-available-models.ts +1 -1
- package/src/hooks/use-composer.ts +57 -2
- package/src/hooks/use-desktop-stream.ts +19 -28
- package/src/hooks/use-usage.ts +100 -0
- package/src/index.ts +22 -0
- package/src/lib/format.ts +32 -6
- package/src/lib/workbench-peers.ts +62 -0
- package/src/session-ui.ts +22 -0
- package/src/terminal.ts +26 -0
- package/src/timeline/activity-rail.tsx +5 -3
- package/src/timeline/allowance-exhausted-row.tsx +86 -0
- package/src/timeline/genie-loading.tsx +16 -6
- package/src/timeline/projection.ts +78 -4
- package/src/timeline/types.ts +8 -0
- package/src/usage/allowance-copy.ts +64 -0
- package/src/usage/summary.ts +93 -0
- package/src/usage.ts +50 -0
- package/styles/compiled.css +325 -0
- package/dist/chunk-4MDLEHXL.js.map +0 -1
- package/dist/chunk-5YK2ZDIG.js.map +0 -1
- package/dist/chunk-KVERXCFY.js.map +0 -1
- package/dist/chunk-L4OSXQ6H.js.map +0 -1
- package/dist/chunk-S5JQ4R22.js.map +0 -1
- package/dist/chunk-UDD6DLLT.js.map +0 -1
- package/dist/chunk-UGMKPUEM.js +0 -299
- package/dist/chunk-UGMKPUEM.js.map +0 -1
- package/dist/chunk-V2ABIVYJ.js.map +0 -1
- package/dist/chunk-X4HIN6TV.js.map +0 -1
- package/dist/chunk-XEJEOAOV.js.map +0 -1
- package/dist/chunk-YZ3YYMOM.js.map +0 -1
- package/dist/markdown-table-layout-PFUEZ2U4.js.map +0 -1
- package/dist/timeline-annotation-selection-ODLVF4OI.js.map +0 -1
- /package/dist/{browser-viewer-2E6R2Y4W.js.map → browser-viewer-Z7S7QEJL.js.map} +0 -0
- /package/dist/{computer-viewer-WMJE6OA3.js.map → chat-interactive-block-UGBTNB3L.js.map} +0 -0
- /package/dist/{chunk-Y3QTZN2F.js.map → chunk-2VFNCX4E.js.map} +0 -0
- /package/dist/{chunk-SEEVCT36.js.map → chunk-A4UJB2MM.js.map} +0 -0
- /package/dist/{chunk-5AZSK6MV.js.map → chunk-LWNLETQT.js.map} +0 -0
- /package/dist/{sandbox-files-PXPIZAFG.js.map → computer-viewer-KJWLH3GI.js.map} +0 -0
- /package/dist/{latest-question-QB4EUCW5.js.map → latest-question-IS445HOW.js.map} +0 -0
- /package/dist/{queue-surface-implementation-IM2HD3SK.js.map → queue-surface-implementation-2CAQAIHM.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-terminal-stream.ts","../src/lib/xterm-theme.ts","../src/components/sandbox-terminal.tsx","../src/lib/terminal-capability.ts","../src/lib/xterm-renderer.ts"],"sourcesContent":["import {\n TTYD_SUBPROTOCOL,\n TerminalCapability,\n terminalSocketUrl,\n ttydAuthFrame,\n ttydInputFrame,\n ttydResizeFrame,\n TtydServerCommand,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n decodeStreamFrame,\n decodeStreamOpenAck,\n encodeStreamFrame,\n encodeStreamOpen,\n type RelayChannel,\n STREAM_KIND_PTY,\n STREAM_ROLE_CLIENT,\n} from \"../lib/relay-wire\";\n\n/** The ttyd connection lifecycle as surfaced to the component. */\nexport type TerminalStreamStatus = \"connecting\" | \"open\" | \"closed\" | \"error\";\n\nexport type UseTerminalStreamOptions = {\n /** The Terminal cell of the negotiated capabilities (`capabilities.Terminal`).\n * The stream connects ONLY when `transport === \"pty-ws\"` and `url` is set; on a\n * cold box (`transport === \"sse-events\"` / no url) it stays idle and the caller\n * falls back to the Channel-A read-only firehose. */\n capability: Pick<TerminalCapability, \"transport\" | \"url\" | \"token\" | \"expiresAt\"> | null;\n /** Called for each OUTPUT payload from ttyd (write verbatim into xterm). */\n onOutput?: ((data: string) => void) | undefined;\n /** Called when ttyd sends a SET_WINDOW_TITLE frame. */\n onTitle?: ((title: string) => void) | undefined;\n /** Requests a fresh negotiated terminal grant after an unexpected transport\n * failure. Intentional disconnects and effect cleanup never call it. */\n onReconnectNeeded?: (() => void) | undefined;\n /** Initial PTY size to seed the ttyd auth frame + first resize. */\n initialCols?: number | undefined;\n initialRows?: number | undefined;\n};\n\nexport type UseTerminalStreamResult = {\n /** True once the ttyd socket is open (and the auth frame has been sent). */\n connected: boolean;\n status: TerminalStreamStatus;\n /** Pipe a keystroke/paste to PTY stdin. Input during CONNECTING is bounded and\n * replayed only after ttyd authentication succeeds. */\n write: (data: string) => void;\n /** Tell ttyd the PTY window changed size (on xterm fit/resize). */\n resize: (cols: number, rows: number) => void;\n /** Tear the socket down (the effect also tears down on unmount / url change). */\n disconnect: () => void;\n};\n\n// Keystrokes entered during the websocket handshake are held until ttyd has\n// accepted its auth/resize frames. Bound the queue by UTF-16 code units (a hard\n// <=128 KiB string payload in current JS engines) so a paste cannot turn a slow\n// handshake into unbounded renderer memory. Overflow rejects the whole pending\n// input rather than ever executing a truncated shell command.\nexport const MAX_PENDING_TERMINAL_INPUT_CODE_UNITS = 64 * 1024;\nconst TERMINAL_CONNECT_EXPIRY_SKEW_MS = 5_000;\nconst TERMINAL_RECONNECT_INITIAL_MS = 100;\nconst TERMINAL_RECONNECT_MAX_MS = 3_000;\nconst RELAY_TAG_OPEN = 1;\nconst RELAY_TAG_OPEN_ACK = 2;\nconst RELAY_TAG_FRAME = 3;\n\nfunction relayDatagram(tag: number, body: Uint8Array): ArrayBuffer {\n const bytes = new Uint8Array(body.length + 1);\n bytes[0] = tag;\n bytes.set(body, 1);\n return bytes.buffer;\n}\n\nfunction relayChannelFromUrl(url: string): RelayChannel {\n const endpoint = new URL(url);\n const channel: RelayChannel = {\n channelId: endpoint.searchParams.get(\"channel\") ?? \"\",\n workspaceId: endpoint.searchParams.get(\"ws\") ?? \"\",\n agentId: endpoint.searchParams.get(\"agent\") ?? \"\",\n kind: STREAM_KIND_PTY,\n port: Number(endpoint.searchParams.get(\"port\") ?? \"0\"),\n };\n if (\n !channel.channelId ||\n !channel.workspaceId ||\n !channel.agentId ||\n !Number.isSafeInteger(channel.port) ||\n channel.port <= 0\n ) {\n throw new Error(\"terminal relay endpoint is incomplete\");\n }\n return channel;\n}\n\nfunction sendRelayInput(\n socket: WebSocket,\n channel: RelayChannel,\n sequence: bigint,\n data: string,\n): void {\n socket.send(\n relayDatagram(\n RELAY_TAG_FRAME,\n encodeStreamFrame({\n channelId: channel.channelId,\n seq: sequence.toString(),\n data: new TextEncoder().encode(data),\n producedAtMs: String(Date.now()),\n }),\n ),\n );\n}\n\n/** A bearer must remain valid long enough to finish a new websocket handshake.\n * Existing open sockets are unaffected: this is evaluated only when the\n * credential identity changes and the connection effect attempts a new socket. */\nexport function terminalStreamCredentialUsable(\n capability: Pick<TerminalCapability, \"transport\" | \"url\" | \"expiresAt\"> | null | undefined,\n nowMs = Date.now(),\n): boolean {\n if (\n (capability?.transport !== \"pty-ws\" && capability?.transport !== \"relay-pty\") ||\n !capability.url\n ) {\n return false;\n }\n if (!capability.expiresAt) return true;\n const expiresAt = Date.parse(capability.expiresAt);\n return Number.isFinite(expiresAt) && expiresAt > nowMs + TERMINAL_CONNECT_EXPIRY_SKEW_MS;\n}\n\n/** Decode an inbound ttyd frame's payload (everything after the 1-char command).\n * ttyd may send either a text frame (string) or a binary frame (ArrayBuffer);\n * for binary we slice off the first byte (the command) and utf-8 decode the rest. */\nfunction decodeFrame(data: string | ArrayBuffer): { command: string; payload: string } {\n if (typeof data === \"string\") {\n return { command: data.charAt(0), payload: data.slice(1) };\n }\n const bytes = new Uint8Array(data);\n const command = bytes.length > 0 ? String.fromCharCode(bytes[0]!) : \"\";\n const payload = bytes.length > 1 ? new TextDecoder().decode(bytes.subarray(1)) : \"\";\n return { command, payload };\n}\n\nfunction closeSocket(socket: WebSocket | null): void {\n try {\n socket?.close();\n } catch {\n // Already closed or rejected by the host WebSocket implementation.\n }\n}\n\n/**\n * Drive a ttyd PTY-over-websocket connection from a `pty-ws` Terminal capability,\n * symmetric with `use-desktop-stream` (the noVNC-over-tunnel hook). The scoped\n * stream token is already embedded in the minted tunnel `url`; the WebSocket is\n * opened with the REQUIRED ttyd subprotocol \"tty\".\n *\n * ttyd wire protocol (see `@opengeni/sdk/terminal`):\n * - first frame: `JSON.stringify({ AuthToken: \"\" })` (+ optional columns/rows).\n * - client→server: INPUT = \"0\"+data ; RESIZE = \"1\"+JSON({columns,rows}).\n * - server→client: \"0\" = OUTPUT (→ xterm) ; \"1\" = SET_WINDOW_TITLE ;\n * \"2\" = SET_PREFERENCES (ignored). Binary frames are decoded the same way.\n *\n * On a `url`/`token` rotation (a box rollover folds a fresh address into the cell)\n * the effect re-runs: the old socket closes and a fresh one connects — a brief\n * terminal blink, acceptable on rollover (mirrors the desktop's RFB hot-swap).\n * SSR-safe: the socket open lives in `useEffect`, so a server render is a no-op.\n */\nexport function useTerminalStream(options: UseTerminalStreamOptions): UseTerminalStreamResult {\n const { capability, onOutput, onTitle, onReconnectNeeded, initialCols, initialRows } = options;\n const [status, setStatus] = useState<TerminalStreamStatus>(\"closed\");\n const [reconnectGeneration, setReconnectGeneration] = useState(0);\n const wsRef = useRef<WebSocket | null>(null);\n const pendingInputRef = useRef(\"\");\n const socketFailedRef = useRef(false);\n const socketAuthenticatedRef = useRef(false);\n const relayChannelRef = useRef<RelayChannel | null>(null);\n const relayIdentityRef = useRef<string | null>(null);\n const relayInputSequenceRef = useRef(0n);\n const relayOutputSequenceRef = useRef(0n);\n const reconnectAttemptRef = useRef(0);\n const credentialIdentityRef = useRef<string | null>(null);\n const refreshRequestedRef = useRef(false);\n const intentionalCloseRef = useRef<WeakSet<WebSocket>>(new WeakSet());\n // Latest size, so a resize() before the socket opens is replayed on open, and a\n // reconnect seeds the right geometry.\n const sizeRef = useRef<{ cols: number; rows: number }>({\n cols: initialCols ?? 80,\n rows: initialRows ?? 24,\n });\n // Keep the callbacks current without re-running the connect effect on every\n // render (the parent passes fresh closures each time).\n const onOutputRef = useRef(onOutput);\n const onTitleRef = useRef(onTitle);\n const onReconnectNeededRef = useRef(onReconnectNeeded);\n onOutputRef.current = onOutput;\n onTitleRef.current = onTitle;\n onReconnectNeededRef.current = onReconnectNeeded;\n\n const transport = capability?.transport ?? null;\n const url = capability?.url ?? null;\n const token = capability?.token ?? null;\n const expiresAt = capability?.expiresAt ?? null;\n const transportRef = useRef(transport);\n transportRef.current = transport;\n\n useEffect(() => {\n // SSR / no WebSocket / not a live pty-ws cell: stay closed; the caller falls\n // back to the Channel-A read-only firehose.\n if (typeof window === \"undefined\" || typeof WebSocket === \"undefined\") return;\n const relay = transport === \"relay-pty\";\n if (transport !== \"pty-ws\" && !relay) {\n // A transport downgrade is a real semantic boundary. Do not replay input\n // captured for a PTY if this surface later changes back from firehose mode.\n pendingInputRef.current = \"\";\n socketAuthenticatedRef.current = false;\n relayIdentityRef.current = null;\n relayInputSequenceRef.current = 0n;\n relayOutputSequenceRef.current = 0n;\n reconnectAttemptRef.current = 0;\n credentialIdentityRef.current = null;\n refreshRequestedRef.current = false;\n setStatus(\"closed\");\n return;\n }\n const credentialIdentity = `${transport}\\u0000${url ?? \"\"}\\u0000${token ?? \"\"}\\u0000${expiresAt ?? \"\"}`;\n if (credentialIdentityRef.current !== credentialIdentity) {\n credentialIdentityRef.current = credentialIdentity;\n reconnectAttemptRef.current = 0;\n refreshRequestedRef.current = false;\n }\n // A changed/cleared credential is a new connection attempt boundary. It may\n // receive input while its exact grant is pending even if the prior socket\n // failed; the unchanged failed identity remains fenced because this effect\n // does not re-run for a status-only render.\n socketFailedRef.current = false;\n socketAuthenticatedRef.current = false;\n if (!url) {\n // Exact grants arrive asynchronously. Preserve the bounded first input\n // while the descriptor remains pty-ws, but never open without a credential.\n setStatus(\"closed\");\n return;\n }\n if (!terminalStreamCredentialUsable({ transport, url, expiresAt })) {\n // A descriptor from an older rolling server may still carry a bearer that\n // aged out before this surface mounted. Ask once for the exact fresh grant.\n setStatus(\"closed\");\n if (!refreshRequestedRef.current) {\n refreshRequestedRef.current = true;\n onReconnectNeededRef.current?.();\n }\n return;\n }\n\n let disposed = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let socket: WebSocket;\n let relayChannel: RelayChannel | null = null;\n const relayOutputDecoder = new TextDecoder();\n setStatus(\"connecting\");\n try {\n if (relay) {\n relayChannel = relayChannelFromUrl(url);\n const relayIdentity = [\n relayChannel.workspaceId,\n relayChannel.agentId,\n relayChannel.port,\n relayChannel.channelId,\n ].join(\"\\u0000\");\n if (relayIdentityRef.current !== relayIdentity) {\n relayIdentityRef.current = relayIdentity;\n relayInputSequenceRef.current = 0n;\n relayOutputSequenceRef.current = 0n;\n }\n relayChannelRef.current = relayChannel;\n socket = new WebSocket(url);\n } else {\n // The ttyd \"tty\" subprotocol is REQUIRED — ttyd rejects a handshake without\n // it. The scoped token is already in the tunnel `url`.\n socket = new WebSocket(terminalSocketUrl({ url }), TTYD_SUBPROTOCOL);\n }\n } catch {\n socketFailedRef.current = true;\n setStatus(\"error\");\n return;\n }\n socket.binaryType = \"arraybuffer\";\n wsRef.current = socket;\n\n const requestFreshGrant = () => {\n if (disposed || refreshRequestedRef.current || intentionalCloseRef.current.has(socket))\n return;\n refreshRequestedRef.current = true;\n onReconnectNeededRef.current?.();\n };\n\n const scheduleTransportReconnect = () => {\n if (disposed || reconnectTimer !== null || intentionalCloseRef.current.has(socket)) return;\n if (refreshRequestedRef.current) return;\n if (!terminalStreamCredentialUsable({ transport, url, expiresAt })) {\n requestFreshGrant();\n return;\n }\n const attempt = reconnectAttemptRef.current++;\n const delay = Math.min(\n TERMINAL_RECONNECT_INITIAL_MS * 2 ** Math.min(attempt, 8),\n TERMINAL_RECONNECT_MAX_MS,\n );\n setStatus(\"connecting\");\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n if (!disposed) setReconnectGeneration((generation) => generation + 1);\n }, delay);\n };\n\n socket.onopen = () => {\n if (disposed) return;\n if (relay && relayChannel) {\n try {\n socket.send(\n relayDatagram(\n RELAY_TAG_OPEN,\n encodeStreamOpen({\n channel: relayChannel,\n token: token ?? \"\",\n role: STREAM_ROLE_CLIENT,\n resumeFromSeq: relayOutputSequenceRef.current.toString(),\n }),\n ),\n );\n } catch {\n socketFailedRef.current = false;\n setStatus(\"connecting\");\n closeSocket(socket);\n }\n return;\n }\n // ttyd's required first frame: the auth message (empty token — the gate is\n // the tunnel url + scoped stream token, not a ttyd -c credential), seeded\n // with the current PTY geometry. Then an explicit resize to be safe.\n try {\n socket.send(ttydAuthFrame({ columns: sizeRef.current.cols, rows: sizeRef.current.rows }));\n socket.send(ttydResizeFrame(sizeRef.current.cols, sizeRef.current.rows));\n if (pendingInputRef.current.length > 0) {\n // One websocket frame makes the pre-open queue all-or-nothing from this\n // client's perspective; never execute a prefix of a buffered command.\n socket.send(ttydInputFrame(pendingInputRef.current));\n pendingInputRef.current = \"\";\n }\n socketAuthenticatedRef.current = true;\n } catch {\n socketFailedRef.current = false;\n setStatus(\"connecting\");\n closeSocket(socket);\n return;\n }\n setStatus(\"open\");\n reconnectAttemptRef.current = 0;\n refreshRequestedRef.current = false;\n };\n\n socket.onmessage = (ev: MessageEvent) => {\n if (disposed) return;\n if (relay) {\n if (!(ev.data instanceof ArrayBuffer) || !relayChannel) return;\n const bytes = new Uint8Array(ev.data);\n if (bytes.length < 1) return;\n const tag = bytes[0];\n const body = bytes.subarray(1);\n if (tag === RELAY_TAG_OPEN_ACK) {\n try {\n const ack = decodeStreamOpenAck(body);\n if (!ack.accepted) throw new Error(ack.error?.message ?? \"terminal relay rejected\");\n reconnectAttemptRef.current = 0;\n refreshRequestedRef.current = false;\n socketAuthenticatedRef.current = true;\n if (pendingInputRef.current.length > 0) {\n sendRelayInput(\n socket,\n relayChannel,\n relayInputSequenceRef.current++,\n pendingInputRef.current,\n );\n pendingInputRef.current = \"\";\n }\n setStatus(\"open\");\n } catch {\n socketFailedRef.current = true;\n setStatus(\"error\");\n requestFreshGrant();\n closeSocket(socket);\n }\n } else if (tag === RELAY_TAG_FRAME && socketAuthenticatedRef.current) {\n try {\n const frame = decodeStreamFrame(body);\n const sequence = BigInt(frame.seq);\n if (sequence < relayOutputSequenceRef.current) return;\n relayOutputSequenceRef.current = sequence + 1n;\n if (frame.data.length > 0) {\n onOutputRef.current?.(relayOutputDecoder.decode(frame.data, { stream: true }));\n }\n } catch {\n // Ignore one malformed frame; the live PTY remains usable.\n }\n }\n return;\n }\n const { command, payload } = decodeFrame(ev.data as string | ArrayBuffer);\n switch (command) {\n case TtydServerCommand.OUTPUT:\n onOutputRef.current?.(payload);\n break;\n case TtydServerCommand.SET_WINDOW_TITLE:\n onTitleRef.current?.(payload);\n break;\n // SET_PREFERENCES (\"2\") and anything else: ignored.\n default:\n break;\n }\n };\n\n socket.onerror = () => {\n if (!disposed) {\n // A network/relay outage is retryable with the same credential and PTY.\n // Keep accepting bounded input while the transport comes back.\n socketFailedRef.current = false;\n setStatus(\"connecting\");\n closeSocket(socket);\n }\n };\n socket.onclose = () => {\n if (wsRef.current === socket) wsRef.current = null;\n socketAuthenticatedRef.current = false;\n if (!disposed) {\n if (socketFailedRef.current) setStatus(\"error\");\n else scheduleTransportReconnect();\n }\n };\n\n return () => {\n disposed = true;\n wsRef.current = null;\n socketAuthenticatedRef.current = false;\n relayChannelRef.current = null;\n if (reconnectTimer !== null) clearTimeout(reconnectTimer);\n // Drop handlers so an in-flight close/error doesn't mutate state post-unmount.\n socket.onopen = null;\n socket.onmessage = null;\n socket.onerror = null;\n socket.onclose = null;\n closeSocket(socket);\n };\n // A url/token change (rotation) re-runs this effect → close old, open new.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [transport, url, token, expiresAt, reconnectGeneration]);\n\n const write = useCallback((data: string) => {\n const currentTransport = transportRef.current;\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {\n try {\n const relayChannel = relayChannelRef.current;\n if (currentTransport === \"relay-pty\" && relayChannel) {\n sendRelayInput(ws, relayChannel, relayInputSequenceRef.current++, data);\n } else {\n ws.send(ttydInputFrame(data));\n }\n } catch {\n socketFailedRef.current = false;\n setStatus(\"connecting\");\n closeSocket(ws);\n }\n } else if (\n (currentTransport === \"pty-ws\" || currentTransport === \"relay-pty\") &&\n !socketFailedRef.current\n ) {\n const nextSize = pendingInputRef.current.length + data.length;\n if (nextSize > MAX_PENDING_TERMINAL_INPUT_CODE_UNITS) {\n pendingInputRef.current = \"\";\n socketFailedRef.current = true;\n setStatus(\"error\");\n closeSocket(ws);\n return;\n }\n pendingInputRef.current += data;\n }\n }, []);\n const resize = useCallback((cols: number, rows: number) => {\n if (cols <= 0 || rows <= 0) return;\n sizeRef.current = { cols, rows };\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {\n try {\n // Self-hosted relay PTYs carry raw input/output frames. Resize remains\n // an out-of-band control operation; do not send ttyd framing into them.\n if (transportRef.current === \"pty-ws\") ws.send(ttydResizeFrame(cols, rows));\n } catch {\n // socket raced closed — geometry is replayed on the next open.\n }\n }\n }, []);\n const disconnect = useCallback(() => {\n const ws = wsRef.current;\n wsRef.current = null;\n pendingInputRef.current = \"\";\n socketFailedRef.current = false;\n socketAuthenticatedRef.current = false;\n relayChannelRef.current = null;\n setStatus(\"closed\");\n if (ws) intentionalCloseRef.current.add(ws);\n closeSocket(ws);\n }, []);\n\n return useMemo<UseTerminalStreamResult>(() => {\n return { connected: status === \"open\", status, write, resize, disconnect };\n }, [status, write, resize, disconnect]);\n}\n","import type { XtermTheme } from \"../components/sandbox-terminal\";\n\n/* ----------------------------------------------------------------------------\n xterm theming + font resolution from the `--og-*` token system.\n\n xterm paints glyphs into a canvas (WebGL / 2D) and builds its font strings by\n hand, so it can consume NEITHER CSS custom properties (`var(--og-font-mono)`\n never resolves in a canvas font string) NOR a partial palette (an unset ANSI\n slot falls back to xterm's stock VGA color). This module resolves both to\n concrete values: a full 16-color ANSI ITheme designed against the og palette\n for BOTH themes, and a concrete monospace family + px size.\n\n The color-building + font-resolution cores are pure over a token `read`\n function so they unit-test without a DOM; thin `*FromTokens` / `resolve*`\n wrappers do the `getComputedStyle` read on the client.\n -------------------------------------------------------------------------- */\n\nexport type ThemeMode = \"dark\" | \"light\";\n\n/** Reads a resolved token value (already `getComputedStyle`-flattened). */\nexport type TokenReader = (name: string) => string | undefined;\n\n/**\n * The 16 ANSI colors + selection/cursor treatment, designed against the og\n * OKLCH palette. Hues are borrowed from the og status ramp so terminal color\n * reads as part of the same system: red≈status-failed(22), green≈status-idle\n * (155), yellow≈status-running(80), magenta(305), cyan≈200. The ANSI blue and\n * magenta slots stay chromatic because program output means them; the app\n * chrome around the terminal is neutral. Dark uses lifted lightness on the\n * dark neutral ground; light uses deeper, more saturated inks on the\n * near-white ground. Bright variants add lightness + a touch of chroma.\n * `black`/`white` and the selection are neutral grey ramp stops (never pure\n * #000/#fff — that clashes with the calm neutral ground).\n */\nexport type AnsiPalette = {\n black: string;\n red: string;\n green: string;\n yellow: string;\n blue: string;\n magenta: string;\n cyan: string;\n white: string;\n brightBlack: string;\n brightRed: string;\n brightGreen: string;\n brightYellow: string;\n brightBlue: string;\n brightMagenta: string;\n brightCyan: string;\n brightWhite: string;\n /** Selection wash + its foreground; sits over the terminal ground. */\n selectionBackground: string;\n selectionForeground: string;\n selectionInactiveBackground: string;\n};\n\nconst DARK_ANSI: AnsiPalette = {\n black: \"oklch(0.3 0 0)\",\n red: \"oklch(0.68 0.17 22)\",\n green: \"oklch(0.75 0.13 155)\",\n yellow: \"oklch(0.8 0.11 80)\",\n blue: \"oklch(0.7 0.14 255)\",\n magenta: \"oklch(0.72 0.13 305)\",\n cyan: \"oklch(0.75 0.1 210)\",\n white: \"oklch(0.8 0 0)\",\n brightBlack: \"oklch(0.45 0 0)\",\n brightRed: \"oklch(0.76 0.17 22)\",\n brightGreen: \"oklch(0.82 0.14 155)\",\n brightYellow: \"oklch(0.86 0.12 85)\",\n brightBlue: \"oklch(0.78 0.13 255)\",\n brightMagenta: \"oklch(0.8 0.13 305)\",\n brightCyan: \"oklch(0.83 0.1 200)\",\n brightWhite: \"oklch(0.97 0 0)\",\n selectionBackground: \"oklch(0.8 0 0 / 0.3)\",\n selectionForeground: \"oklch(0.985 0 0)\",\n selectionInactiveBackground: \"oklch(0.72 0 0 / 0.2)\",\n};\n\nconst LIGHT_ANSI: AnsiPalette = {\n black: \"oklch(0.35 0 0)\",\n red: \"oklch(0.52 0.19 22)\",\n green: \"oklch(0.5 0.13 155)\",\n yellow: \"oklch(0.56 0.13 80)\",\n blue: \"oklch(0.52 0.18 255)\",\n magenta: \"oklch(0.52 0.16 305)\",\n cyan: \"oklch(0.52 0.1 210)\",\n white: \"oklch(0.7 0 0)\",\n brightBlack: \"oklch(0.5 0 0)\",\n brightRed: \"oklch(0.55 0.2 22)\",\n brightGreen: \"oklch(0.54 0.14 155)\",\n brightYellow: \"oklch(0.6 0.14 80)\",\n brightBlue: \"oklch(0.48 0.19 255)\",\n brightMagenta: \"oklch(0.54 0.17 305)\",\n brightCyan: \"oklch(0.54 0.11 210)\",\n brightWhite: \"oklch(0.3 0 0)\",\n selectionBackground: \"oklch(0.45 0 0 / 0.2)\",\n selectionForeground: \"oklch(0.21 0 0)\",\n selectionInactiveBackground: \"oklch(0.6 0 0 / 0.14)\",\n};\n\n/** The designed ANSI-16 palette for a theme mode. Pure. */\nexport function ogAnsiPalette(mode: ThemeMode): AnsiPalette {\n return mode === \"light\" ? LIGHT_ANSI : DARK_ANSI;\n}\n\n/**\n * Build a COMPLETE xterm ITheme from token values. Background/foreground/cursor\n * come from the live `--og-*` tokens (so an embedder's rebrand flows through);\n * the ANSI-16 + selection come from the designed palette for `mode`. Every ANSI\n * slot is always set — xterm never falls back to its stock VGA colors. Pure.\n */\nexport function buildXtermTheme(read: TokenReader, mode: ThemeMode): XtermTheme {\n const ansi = ogAnsiPalette(mode);\n const bgFallback = mode === \"light\" ? \"#ffffff\" : \"#202020\";\n const fgFallback = mode === \"light\" ? \"#242424\" : \"#e6e6e6\";\n const bg = read(\"--og-color-bg\") ?? bgFallback;\n const fg = read(\"--og-color-fg\") ?? fgFallback;\n const accent = read(\"--og-color-accent\") ?? ansi.brightBlue;\n return {\n // Match the dock surface the terminal paints on (`--og-color-bg`).\n background: bg,\n foreground: fg,\n cursor: accent,\n cursorAccent: bg,\n selectionBackground: ansi.selectionBackground,\n selectionForeground: ansi.selectionForeground,\n selectionInactiveBackground: ansi.selectionInactiveBackground,\n black: ansi.black,\n red: ansi.red,\n green: ansi.green,\n yellow: ansi.yellow,\n blue: ansi.blue,\n magenta: ansi.magenta,\n cyan: ansi.cyan,\n white: ansi.white,\n brightBlack: ansi.brightBlack,\n brightRed: ansi.brightRed,\n brightGreen: ansi.brightGreen,\n brightYellow: ansi.brightYellow,\n brightBlue: ansi.brightBlue,\n brightMagenta: ansi.brightMagenta,\n brightCyan: ansi.brightCyan,\n brightWhite: ansi.brightWhite,\n };\n}\n\n/** Which og theme is active for `el`, matching the `use-theme-type` convention\n * (nearest `data-og-theme` / `.og-light` ancestor; else the computed\n * `color-scheme`; else dark, the first-class default). */\nfunction detectMode(el: Element, style: CSSStyleDeclaration): ThemeMode {\n const closest = el.closest?.bind(el);\n if (closest) {\n if (closest('[data-og-theme=\"light\"]') || closest(\".og-light\")) return \"light\";\n const explicit = closest(\"[data-og-theme]\");\n if (explicit) return explicit.getAttribute(\"data-og-theme\") === \"light\" ? \"light\" : \"dark\";\n }\n const scheme = (\n style.getPropertyValue(\"color-scheme\").trim() ||\n style.colorScheme ||\n \"\"\n ).toLowerCase();\n if (scheme.includes(\"light\") && !scheme.includes(\"dark\")) return \"light\";\n return \"dark\";\n}\n\n/**\n * Derive a COMPLETE xterm `ITheme` from the live `--og-*` token system on the\n * DOM. Reads the COMPUTED values so xterm gets concrete colors, detects the\n * active theme mode, and fills every ANSI slot. Call on mount + re-derive on a\n * `data-og-theme` flip. SSR-safe: returns undefined off the DOM.\n *\n * `root` should be an element INSIDE the themed subtree (e.g. the terminal\n * container) so nested `[data-og-theme]` themes resolve correctly; when omitted\n * it falls back to the nearest `[data-og-theme]` element, then documentElement.\n */\nexport function xtermThemeFromTokens(root?: HTMLElement | null): XtermTheme | undefined {\n if (typeof window === \"undefined\" || typeof getComputedStyle === \"undefined\") return undefined;\n const el =\n root ??\n (typeof document !== \"undefined\"\n ? ((document.querySelector(\"[data-og-theme]\") as HTMLElement | null) ??\n document.documentElement)\n : null);\n if (!el) return undefined;\n const style = getComputedStyle(el);\n const read: TokenReader = (name) => {\n const value = style.getPropertyValue(name).trim();\n return value || undefined;\n };\n return buildXtermTheme(read, detectMode(el, style));\n}\n\n// ── Font resolution ──────────────────────────────────────────────────────────\n\nexport type ResolvedTerminalFont = { fontFamily: string; fontSize: number; lineHeight: number };\n\n/** Concrete fallback if the token is unset or (defensively) still a `var()`. */\nconst FALLBACK_MONO = \"ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace\";\n\nexport type FontOverrides = { fontFamily?: string | undefined; fontSize?: number | undefined };\n\n/**\n * Resolve xterm's font from token values. The critical bug this fixes:\n * `fontFamily: \"var(--og-font-mono)\"` is measured by xterm via a canvas font\n * string where `var()` does NOT resolve, so xterm silently mis-measures every\n * glyph. Here the family is a CONCRETE stack; a `var(` in the input is treated\n * as unresolved and replaced with the fallback. Pure over a token `read`.\n */\nexport function resolveTerminalFontFromReader(\n read: TokenReader,\n overrides?: FontOverrides,\n): ResolvedTerminalFont {\n const rawFamily = overrides?.fontFamily ?? read(\"--og-font-mono\");\n const fontFamily =\n rawFamily && rawFamily.trim() && !rawFamily.includes(\"var(\") ? rawFamily.trim() : FALLBACK_MONO;\n\n let fontSize = overrides?.fontSize;\n if (fontSize == null) {\n const raw = read(\"--og-code-font-size\");\n const parsed = raw ? Number.parseFloat(raw) : Number.NaN;\n fontSize = Number.isFinite(parsed) && parsed > 0 ? parsed : 13;\n }\n\n return { fontFamily, fontSize, lineHeight: 1.3 };\n}\n\n/** DOM wrapper: resolve the terminal font from an element's computed tokens. */\nexport function resolveTerminalFont(\n el: HTMLElement,\n overrides?: FontOverrides,\n): ResolvedTerminalFont {\n const style = getComputedStyle(el);\n const read: TokenReader = (name) => {\n const value = style.getPropertyValue(name).trim();\n return value || undefined;\n };\n return resolveTerminalFontFromReader(read, overrides);\n}\n","import type { TerminalCapability } from \"@opengeni/sdk\";\nimport {\n type ReactNode,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { type TerminalStreamStatus, useTerminalStream } from \"../hooks/use-terminal-stream\";\nimport type { UseSandboxTerminalResult } from \"../hooks/use-sandbox-terminal\";\nimport { resolveTerminalFont, xtermThemeFromTokens } from \"../lib/xterm-theme\";\nimport { sandboxAcceptsLiveIo } from \"../lib/sandbox-liveness\";\nimport { terminalCanAcquirePty } from \"../lib/terminal-capability\";\nimport { attachRenderer, type RendererLoaders, type RendererTier } from \"../lib/xterm-renderer\";\nimport { sandboxTerminalPeers } from \"../lib/workbench-peers\";\n\n/**\n * A COMPLETE xterm `ITheme` (subset by intent, but every field xterm colors a\n * cell with is present so it never falls back to its stock VGA palette). Built\n * from the og tokens via `xtermThemeFromTokens`.\n */\nexport type XtermTheme = {\n background?: string;\n foreground?: string;\n cursor?: string;\n cursorAccent?: string;\n selectionBackground?: string;\n selectionForeground?: string;\n selectionInactiveBackground?: string;\n black?: string;\n red?: string;\n green?: string;\n yellow?: string;\n blue?: string;\n magenta?: string;\n cyan?: string;\n white?: string;\n brightBlack?: string;\n brightRed?: string;\n brightGreen?: string;\n brightYellow?: string;\n brightBlue?: string;\n brightMagenta?: string;\n brightCyan?: string;\n brightWhite?: string;\n};\n\nexport type SandboxTerminalProps = {\n /** From `useSandboxTerminal(...)`. */\n result: UseSandboxTerminalResult;\n /**\n * The negotiated Terminal capability cell. When it advertises `transport:\n * \"pty-ws\"` + a live `url` (a warm box with a viewer attached), the terminal is\n * driven by a REAL bidirectional PTY over the Modal tunnel (ttyd-over-websocket)\n * INSTEAD of the broken ptyWrite-over-HTTP path: xterm input → the socket, the\n * socket's output → xterm, xterm resize → the socket. On a cold box / no url\n * (`transport: \"sse-events\"`) it stays on the read-only Channel-A firehose\n * (`result.chunks`). Omit to force the legacy firehose-only behavior.\n */\n terminalCapability?: TerminalCapability | null | undefined;\n /**\n * Full xterm theme. When omitted the terminal self-derives it from the `--og-*`\n * tokens of its own container (correct even inside a nested `data-og-theme`\n * subtree). Passing it lets the host drive re-theme on a theme flip.\n */\n theme?: XtermTheme | undefined;\n fontFamily?: string | undefined;\n fontSize?: number | undefined;\n /**\n * Lease liveness (`cold` | `warming` | `warm` | `draining`). Drives the\n * boot-in-terminal status lines: once the user has engaged the terminal and\n * the box is not yet warm, a styled \"● waking machine — Ns…\" line is rendered\n * INSIDE xterm (no overlay/spinner) until the live PTY connects.\n */\n liveness?: string | undefined;\n /**\n * Force read-only even when the PTY accepts stdin. Default: interactive\n * whenever a live pty-ws stream is connected OR `result.write !== null` (the box\n * advertises an interactive PTY); otherwise the read-only agent firehose.\n */\n readOnly?: boolean | undefined;\n /** Shown on the server / before xterm hydrates (SSR-safe placeholder). */\n placeholder?: ReactNode | undefined;\n /** Render the small status header (pty/shell + running dot + read-only pill). */\n showHeader?: boolean | undefined;\n /** Shell label for the header (e.g. `/bin/bash`). */\n shell?: string | undefined;\n /**\n * Fired the first time the user engages the terminal surface (focus or click).\n * The host wires this to warm the box for the REAL pty-ws terminal (the viewer\n * attach), so a cold box upgrades from the read-only firehose to a live PTY ON\n * INTERACT — never on mere mount (which would force a box spin-up and regress\n * the firehose-only default).\n */\n onActivate?: (() => void) | undefined;\n /** Re-negotiate the terminal capability after an unexpected socket failure. */\n onReconnectNeeded?: (() => void) | undefined;\n className?: string | undefined;\n};\n\n// The xterm.js handle the effect owns. `any`-free structural shape so we can\n// drive it without a hard type dep on the lazily-imported lib.\ntype XtermLike = {\n open: (el: HTMLElement) => void;\n focus: () => void;\n write: (data: string) => void;\n clear: () => void;\n onData: (cb: (data: string) => void) => { dispose: () => void };\n onResize: (cb: (size: { cols: number; rows: number }) => void) => { dispose: () => void };\n loadAddon: (addon: unknown) => void;\n dispose: () => void;\n cols: number;\n rows: number;\n options: {\n theme?: XtermTheme;\n disableStdin?: boolean;\n cursorBlink?: boolean;\n fontFamily?: string;\n fontSize?: number;\n };\n};\ntype FitAddonLike = { fit: () => void };\ntype TerminalPeerBundle = {\n Terminal: new (options: Record<string, unknown>) => XtermLike;\n FitAddon: new () => FitAddonLike;\n WebLinksAddon: new (handler: (event: MouseEvent, uri: string) => void) => unknown;\n loadWebgl?:\n | (() => Promise<{\n WebglAddon: new () => {\n dispose: () => void;\n onContextLoss?: (callback: () => void) => void;\n };\n }>)\n | undefined;\n};\n\nexport type TerminalSurfaceState =\n | \"loading\"\n | \"connecting\"\n | \"error\"\n | \"waking\"\n | \"output-only\"\n | \"interactive\";\n\n/**\n * Stable semantic state for hosts and browser acceptance. The xterm helper\n * textarea exists before the live PTY socket is ready, so its presence alone\n * must never be treated as proof that keystrokes can reach the sandbox.\n */\nexport function terminalSurfaceState(input: {\n ready: boolean;\n acceptsInput: boolean;\n inputPending: boolean;\n ptyStatus: TerminalStreamStatus;\n booting: boolean;\n}): TerminalSurfaceState {\n if (!input.ready) return \"loading\";\n if (input.acceptsInput) return \"interactive\";\n if (input.inputPending) return \"connecting\";\n if (input.ptyStatus === \"connecting\") return \"connecting\";\n if (input.ptyStatus === \"error\") return \"error\";\n if (input.booting) return \"waking\";\n return \"output-only\";\n}\n\nexport function subscribeTerminalInput(\n term: Pick<XtermLike, \"onData\"> | null,\n sink: ((data: string) => void) | null,\n enabled: boolean,\n): { dispose: () => void } | null {\n if (!enabled || !term || !sink) return null;\n return term.onData(sink);\n}\n\n/** Clear the transient wake line and put the first live prompt at cell 1,1.\n * `Terminal.clear()` removes scrollback but deliberately preserves the cursor,\n * which lets the first PTY frame append to the old wake message. */\nexport function clearTerminalBootStatus(term: Pick<XtermLike, \"write\">): void {\n term.write(\"\\r\\x1b[2K\\x1b[2J\\x1b[H\");\n}\n\n/** Debug seam (dev/evidence only, never a typed public API): a global hook the\n * screenshot harness reads to probe the settled renderer tier + resolved font\n * without mounting a real WebGL context in unit tests. */\ntype TerminalDebugInfo = {\n renderer: RendererTier | null;\n fontFamily: string | undefined;\n fontSize: number | undefined;\n hasVarInFont: boolean;\n theme: XtermTheme | undefined;\n /** The live xterm instance (opaque) — the evidence harness reads its buffer to\n * prove screen preservation (E5) and drive the burst-responsiveness probe. */\n term: unknown;\n};\ndeclare global {\n // eslint-disable-next-line no-var\n var __OG_TERMINAL_DEBUG__: ((info: TerminalDebugInfo) => void) | undefined;\n // A space/comma list of renderer tiers to force-fail, so the demo can prove the\n // WebGL→canvas→DOM fallback ladder without a real GPU context loss.\n // eslint-disable-next-line no-var\n var __OG_FORCE_RENDERER_FAIL__: string | undefined;\n}\n\n/** Which renderer tiers are force-failed (demo/evidence only). */\nfunction forcedRendererFailures(): Set<RendererTier> {\n const raw = typeof globalThis !== \"undefined\" ? globalThis.__OG_FORCE_RENDERER_FAIL__ : undefined;\n const set = new Set<RendererTier>();\n if (typeof raw === \"string\") {\n for (const tier of raw.split(/[\\s,]+/)) {\n if (tier === \"webgl\" || tier === \"canvas\") set.add(tier);\n }\n }\n return set;\n}\n\n/**\n * An xterm.js terminal fed by the Channel-A event projection\n * (`useSandboxTerminal`) or a live ttyd PTY. xterm + its addons are lazy-imported\n * inside an effect, so SSR renders the placeholder and the terminal mounts on\n * hydration.\n *\n * Renderer: WebGL (GPU-composited cells) with a canvas → DOM fallback ladder\n * (`attachRenderer`), so bursty output stays smooth but a lost/blocklisted\n * context degrades gracefully. Font + theme are resolved to CONCRETE values\n * from the og tokens BEFORE construction (xterm measures glyphs in a canvas\n * where `var()` never resolves, and colors every cell from a full ANSI palette).\n *\n * The terminal is mounted ONCE and its data source is swapped in place: the\n * read-only firehose (`result.chunks`) → the live PTY. Stdin is toggled via\n * `term.options.disableStdin` at runtime (not a remount), so scrollback and the\n * visible screen survive the projection→PTY handoff.\n *\n * Resizes are tracked with a `ResizeObserver` on the container (not just\n * `window.resize`) so dragging the dock handle refits the grid.\n */\nexport function SandboxTerminal({\n result,\n terminalCapability,\n theme,\n fontFamily,\n fontSize,\n liveness,\n readOnly,\n placeholder,\n showHeader,\n shell,\n onActivate,\n onReconnectNeeded,\n className,\n}: SandboxTerminalProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const termRef = useRef<XtermLike | null>(null);\n const fitRef = useRef<FitAddonLike | null>(null);\n const rendererRef = useRef<RendererTier | null>(null);\n const writtenRef = useRef<Set<string>>(new Set());\n const wroteFirehoseRef = useRef(false);\n const bootActiveRef = useRef(false);\n const [ready, setReady] = useState(false);\n const [loadError, setLoadError] = useState<Error | null>(null);\n const peerRevision = useSyncExternalStore(\n sandboxTerminalPeers.subscribe,\n sandboxTerminalPeers.revision,\n sandboxTerminalPeers.revision,\n );\n const [inputReady, setInputReady] = useState(false);\n const [activated, setActivated] = useState(false);\n\n // ── Interactive transport switch ─────────────────────────────────────────────\n const ptyDescriptor =\n terminalCapability?.transport === \"pty-ws\" || terminalCapability?.transport === \"relay-pty\";\n const ptyAttachable = terminalCanAcquirePty(terminalCapability);\n const {\n status: ptyStatus,\n write: ptyWrite,\n resize: resizePty,\n connected: ptyConnected,\n } = useTerminalStream({\n // Connect only after xterm exists. This makes the renderer the sole output\n // owner from the first websocket frame and removes an otherwise unbounded\n // pre-mount output queue during large command bursts.\n capability:\n ptyAttachable && ready\n ? {\n transport: terminalCapability!.transport,\n url: ptyDescriptor ? terminalCapability!.url : null,\n token: ptyDescriptor ? terminalCapability!.token : null,\n expiresAt: ptyDescriptor ? terminalCapability!.expiresAt : null,\n }\n : null,\n onOutput: (data) => {\n termRef.current?.write(data);\n },\n onReconnectNeeded,\n });\n\n // A pty-ws capability exclusively owns the screen even while connecting or\n // after a visible connection failure; never silently switch one terminal UI\n // between two PTYs. Input is enabled during the handshake because the stream\n // hook buffers it strictly and flushes only after ttyd auth succeeds.\n const ptyMode = ptyDescriptor;\n // Keep xterm stdin live for an attachable descriptor even before its URL has\n // arrived. Focus/click triggers the grant; any immediately following input is\n // bounded in the stream hook instead of disappearing in that HTTP interval.\n const ptyCapturesInput = ptyAttachable && ptyStatus !== \"error\";\n const interactive = !readOnly && (ptyAttachable ? ptyCapturesInput : result.write !== null);\n // Public \"interactive\" means the mounted input subscription can reach a live\n // PTY now. Before that point xterm still captures into the bounded queue, but\n // acceptance and embedders see an explicit connecting state.\n const acceptsInput = inputReady && (!ptyAttachable || ptyConnected);\n const inputPending = inputReady && ptyAttachable && !ptyConnected && activated;\n let terminalModeLabel: string | null;\n if (readOnly) terminalModeLabel = \"read-only\";\n else if (!ptyAttachable) terminalModeLabel = acceptsInput ? null : \"output only\";\n else if (ptyStatus === \"error\") terminalModeLabel = \"connection error\";\n else if (acceptsInput) terminalModeLabel = null;\n else terminalModeLabel = activated ? \"connecting\" : \"connect on input\";\n\n // Boot-in-terminal: after the user engages a not-yet-warm box, show styled\n // status lines INSIDE xterm instead of an overlay — but only when there is no\n // firehose transcript to show yet (otherwise the projected output is shown).\n const warm = sandboxAcceptsLiveIo(liveness);\n const booting =\n activated &&\n !ptyMode &&\n !warm &&\n liveness !== null &&\n liveness !== undefined &&\n result.chunks.length === 0;\n const surfaceState = terminalSurfaceState({\n ready,\n acceptsInput,\n inputPending,\n ptyStatus,\n booting,\n });\n\n function handleActivate() {\n // Capture-phase activation may synchronously negotiate a fresh terminal\n // capability and rerender the shell. Focus xterm before that state change so\n // the user's first keystroke or paste reaches its already-bounded input\n // queue instead of landing on the surrounding dock.\n const term = termRef.current;\n if (term && !readOnly && (ptyAttachable || result.write !== null)) {\n term.options.disableStdin = false;\n term.options.cursorBlink = true;\n }\n term?.focus();\n if (!activated) setActivated(true);\n onActivate?.();\n }\n\n // Mount xterm once (client-only). Never re-mounts on interactive/theme flips —\n // stdin + theme are runtime options synced by the effects below, so the visible\n // screen survives the projection→PTY handoff (E5).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const el = containerRef.current;\n if (!el) return;\n let disposed = false;\n\n // Wait for a real measured size before opening xterm so the first fit is at\n // the true width (belt-and-braces with the visibility gate below).\n const waitForSize = (node: HTMLElement) =>\n new Promise<void>((resolve) => {\n if (node.clientWidth > 0 && node.clientHeight > 0) return resolve();\n const ro = new ResizeObserver(() => {\n if (node.clientWidth > 0 && node.clientHeight > 0) {\n ro.disconnect();\n resolve();\n }\n });\n ro.observe(node);\n setTimeout(() => {\n ro.disconnect();\n resolve();\n }, 1000);\n });\n\n ensureXtermBaseCss();\n\n setLoadError(null);\n void (async () => {\n const { Terminal, FitAddon, WebLinksAddon, loadWebgl } =\n (await sandboxTerminalPeers.load()) as TerminalPeerBundle;\n if (disposed) return;\n await waitForSize(el);\n if (disposed) return;\n\n // Resolve font + theme to CONCRETE values BEFORE construction. `var()` in a\n // font family never resolves in xterm's canvas measurement, so we read the\n // computed `--og-font-mono` here; the theme is a full ANSI palette.\n const font = resolveTerminalFont(el, { fontFamily, fontSize });\n const initialTheme = theme ?? xtermThemeFromTokens(el);\n\n const term = new Terminal({\n convertEol: true,\n disableStdin: !interactive,\n cursorBlink: interactive,\n fontFamily: font.fontFamily,\n fontSize: font.fontSize,\n lineHeight: font.lineHeight,\n ...(initialTheme ? { theme: initialTheme } : {}),\n }) as unknown as XtermLike;\n const fit = new FitAddon() as unknown as FitAddonLike;\n term.loadAddon(fit);\n term.loadAddon(\n new WebLinksAddon((_e: MouseEvent, uri: string) =>\n window.open(uri, \"_blank\", \"noopener,noreferrer\"),\n ) as unknown,\n );\n term.open(el);\n termRef.current = term;\n fitRef.current = fit;\n\n // Attach the fastest available renderer. For xterm-6 the supported ladder\n // is WebGL → DOM (the standalone 2D-canvas addon targets xterm-5 internals,\n // so it is intentionally NOT in the shipped path). Init failure / context\n // loss steps down to xterm's built-in DOM renderer; the settled tier is\n // exposed for evidence.\n const failSet = forcedRendererFailures();\n const loaders: RendererLoaders = {\n webgl: async (onLoss) => {\n if (disposed) throw new Error(\"Terminal was disposed\");\n if (failSet.has(\"webgl\")) throw new Error(\"forced webgl failure\");\n if (!loadWebgl) throw new Error(\"WebGL terminal renderer is not enabled\");\n const { WebglAddon } = await loadWebgl();\n if (disposed) throw new Error(\"Terminal was disposed\");\n const addon = new WebglAddon() as unknown as {\n dispose: () => void;\n onContextLoss?: (cb: () => void) => void;\n };\n addon.onContextLoss?.(() => {\n if (disposed) return;\n try {\n addon.dispose();\n } catch {\n // already disposed\n }\n onLoss();\n });\n term.loadAddon(addon);\n return addon;\n },\n };\n await attachRenderer(\"webgl\", loaders, (tier) => {\n if (disposed) return;\n rendererRef.current = tier;\n el.setAttribute(\"data-og-term-renderer\", tier);\n });\n if (disposed) return;\n\n // Fit BEFORE reveal so the first visible frame is already correctly sized\n // (no 80×24 rasterize-then-reflow flash — the container stays hidden until\n // now via the `ready` visibility gate).\n const settle = () => {\n try {\n fit.fit();\n } catch {\n // ignore fit before layout\n }\n };\n settle();\n requestAnimationFrame(() => {\n if (disposed) return;\n settle();\n el.setAttribute(\"data-og-term-ready\", \"true\");\n if (typeof globalThis.__OG_TERMINAL_DEBUG__ === \"function\") {\n globalThis.__OG_TERMINAL_DEBUG__({\n renderer: rendererRef.current,\n fontFamily: term.options.fontFamily,\n fontSize: term.options.fontSize,\n hasVarInFont: String(term.options.fontFamily ?? \"\").includes(\"var(\"),\n theme: term.options.theme,\n term,\n });\n }\n setReady(true);\n });\n })().catch((cause: unknown) => {\n if (!disposed) setLoadError(cause instanceof Error ? cause : new Error(String(cause)));\n });\n\n return () => {\n disposed = true;\n termRef.current?.dispose();\n termRef.current = null;\n fitRef.current = null;\n rendererRef.current = null;\n writtenRef.current = new Set();\n wroteFirehoseRef.current = false;\n bootActiveRef.current = false;\n setReady(false);\n };\n // Mount once per peer registration; runtime options sync without a remount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [peerRevision]);\n\n // Sync stdin/cursor at runtime (NOT a remount) when interactivity flips, so the\n // projection→PTY handoff keeps the single Terminal instance + its scrollback.\n useLayoutEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n term.options.disableStdin = !interactive;\n term.options.cursorBlink = interactive;\n }, [ready, interactive]);\n\n // Re-theme live (dark↔light flip) without re-mounting / losing scrollback. Uses\n // the passed theme, or self-derives from the container's tokens.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n const next = theme ?? xtermThemeFromTokens(containerRef.current);\n if (next) term.options.theme = next;\n }, [ready, theme]);\n\n // Boot-in-terminal status lines. While `booting`, paint a styled \"● waking\n // machine — Ns…\" line and tick it. On exit, if those lines were the ONLY\n // content (no firehose transcript), clear them for the incoming live screen —\n // never clear a real transcript (E5 preservation).\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n if (!booting) {\n if (bootActiveRef.current) {\n bootActiveRef.current = false;\n if (!wroteFirehoseRef.current) clearTerminalBootStatus(term);\n }\n return;\n }\n bootActiveRef.current = true;\n const start = Date.now();\n const paint = () => {\n const s = Math.max(0, Math.round((Date.now() - start) / 1000));\n // Bright-blue dot (accent) + dim label; `\\r` + clear-line keeps it on one\n // line so the counter updates in place.\n term.write(`\\r\\x1b[2K\\x1b[94m●\\x1b[0m \\x1b[2mwaking machine — ${s}s…\\x1b[0m`);\n };\n term.write(\"\\r\\n\");\n paint();\n const id = setInterval(paint, 1000);\n return () => clearInterval(id);\n }, [ready, booting]);\n\n // Write new firehose chunks incrementally — ONLY in firehose mode. In PTY mode\n // the ttyd socket owns the screen. Records that a real transcript exists so the\n // boot cleanup never wipes it.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || ptyMode) return;\n for (const chunk of result.chunks) {\n if (writtenRef.current.has(chunk.id)) continue;\n writtenRef.current.add(chunk.id);\n wroteFirehoseRef.current = true;\n term.write(chunk.text);\n }\n }, [ready, result.chunks, ptyMode]);\n\n // Wire interactive input when allowed. `ptyWrite` is stable across socket\n // status changes, so this subscription does not tear down while a grant opens\n // or rotates and cannot create a first-keystroke gap.\n useLayoutEffect(() => {\n const term = termRef.current;\n if (!ready || !term || !interactive) {\n setInputReady(false);\n return;\n }\n const sink = ptyAttachable ? ptyWrite : result.write;\n if (!sink) {\n setInputReady(false);\n return;\n }\n const sub = subscribeTerminalInput(term, sink, true);\n setInputReady(sub !== null);\n return () => sub?.dispose();\n }, [ready, interactive, ptyAttachable, ptyWrite, result.write]);\n\n // In PTY mode, tell ttyd the window size on the xterm resize event.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || !ptyMode) return;\n resizePty(term.cols, term.rows);\n const sub = term.onResize(({ cols, rows }) => resizePty(cols, rows));\n return () => sub.dispose();\n }, [ready, ptyMode, ptyConnected, resizePty]);\n\n // Refit on container resize (dock drag) AND window resize.\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const refit = () => {\n try {\n fitRef.current?.fit();\n } catch {\n // ignore\n }\n };\n window.addEventListener(\"resize\", refit);\n let observer: ResizeObserver | null = null;\n const el = containerRef.current;\n if (el && typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(() => refit());\n observer.observe(el);\n }\n return () => {\n window.removeEventListener(\"resize\", refit);\n observer?.disconnect();\n };\n }, [ready]);\n\n return (\n <div className={cn(\"relative flex h-full w-full flex-col overflow-hidden\", className)}>\n {showHeader && (\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b border-og-border px-2 py-1 text-og-xs text-og-fg-subtle\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n result.running ? \"bg-og-status-running\" : \"bg-og-fg-subtle\",\n )}\n />\n <span className=\"truncate font-og-mono\">pty: {shell ?? \"shell\"}</span>\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {terminalModeLabel && (\n <span\n className=\"rounded-og-sm bg-og-surface-2 px-1.5 py-0.5 text-og-xs uppercase tracking-wide\"\n role=\"status\"\n >\n {terminalModeLabel}\n </span>\n )}\n <button\n type=\"button\"\n onClick={() => {\n termRef.current?.clear();\n writtenRef.current = new Set();\n wroteFirehoseRef.current = false;\n }}\n className=\"rounded-og-sm px-1.5 py-0.5 text-og-xs hover:text-og-fg pointer-coarse:min-h-11\"\n >\n Clear\n </button>\n </span>\n </div>\n )}\n {/* `onPointerDownCapture`/`onFocusCapture` fire on the FIRST user engagement\n with the terminal surface (capture so they win even though xterm's own\n listeners stop propagation). The host warms the box for pty-ws here. The\n 12px inset + `--og-color-bg` ground match the dock surface. */}\n <div\n className=\"relative min-h-0 flex-1 bg-og-bg p-3\"\n onPointerDownCapture={handleActivate}\n onFocusCapture={handleActivate}\n >\n {!ready &&\n (loadError ? (\n <p role=\"alert\">{loadError.message}</p>\n ) : (\n (placeholder ?? <TerminalPlaceholder />)\n ))}\n {/* Kept `visibility:hidden` until the first successful fit so the first\n VISIBLE frame is already correctly sized (no 80×24 flash — E4). */}\n <div\n ref={containerRef}\n className=\"h-full w-full\"\n style={{ visibility: ready ? \"visible\" : \"hidden\" }}\n data-opengeni-terminal\n data-opengeni-terminal-ready={ready ? \"true\" : \"false\"}\n data-opengeni-terminal-interactive={surfaceState === \"interactive\" ? \"true\" : \"false\"}\n data-opengeni-terminal-state={surfaceState}\n data-opengeni-terminal-status={ptyAttachable ? ptyStatus : \"firehose\"}\n />\n </div>\n </div>\n );\n}\n\n// xterm.js ships a stylesheet (`@xterm/xterm/css/xterm.css`) that is REQUIRED for\n// correct layout — without it the `.xterm-helper-textarea` (an off-screen input\n// xterm uses for IME/keystrokes) is NOT clipped and renders as a visible 1-line\n// box in the top-left (the \"weird input box\" bug), and the viewport/screen aren't\n// positioned. The host app never imports that CSS (no global stylesheet here), so\n// we inject the structural rules once, idempotently (keyed by id), the first time\n// any terminal mounts. Scoped under `.xterm` so it can't leak into host styles.\nconst XTERM_STYLE_ID = \"opengeni-xterm-base-css\";\nconst XTERM_BASE_CSS = `\n.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}\n.xterm.focus,.xterm:focus{outline:none}\n.xterm .xterm-helpers{position:absolute;top:0;z-index:5}\n.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}\n.xterm .composition-view{background:var(--og-color-bg);color:var(--og-color-fg);display:none;position:absolute;white-space:nowrap;z-index:1}\n.xterm .composition-view.active{display:block}\n.xterm .xterm-viewport{background-color:transparent;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}\n.xterm .xterm-screen{position:relative}\n.xterm .xterm-screen canvas{position:absolute;left:0;top:0}\n.xterm .xterm-scroll-area{visibility:hidden}\n.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}\n.xterm.enable-mouse-events{cursor:default}\n.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}\n.xterm.column-select.focus{cursor:crosshair}\n.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}\n.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}\n.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}\n.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}\n.xterm-dim{opacity:1!important}\n.xterm-underline-1{text-decoration:underline}\n.xterm-underline-2{text-decoration:double underline}\n.xterm-underline-3{text-decoration:wavy underline}\n.xterm-underline-4{text-decoration:dotted underline}\n.xterm-underline-5{text-decoration:dashed underline}\n.xterm-overline{text-decoration:overline}\n.xterm-strikethrough{text-decoration:line-through}\n.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}\n.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}\n.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}\n.xterm-decoration-top{z-index:2;position:relative}\n`;\n\nfunction ensureXtermBaseCss(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(XTERM_STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = XTERM_STYLE_ID;\n style.textContent = XTERM_BASE_CSS;\n document.head.appendChild(style);\n}\n\nfunction TerminalPlaceholder() {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center text-og-sm text-og-fg-subtle\">\n Loading terminal…\n </div>\n );\n}\n","import type { TerminalCapability } from \"@opengeni/sdk\";\n\n/** Whether an explicit viewer grant can upgrade this cell to a real PTY.\n * Keep acquisition feasibility separate from the current transport: a cold\n * real-PTY backend reports sse-events until the just-in-time grant warms it. */\nexport function terminalCanAcquirePty(cell: TerminalCapability | null | undefined): boolean {\n if (!cell?.ptyCapable) return false;\n if (cell.transport === \"pty-ws\" || cell.transport === \"relay-pty\") return true;\n return cell.reason === \"lease_cold\" || cell.reason === \"not_provisioned\";\n}\n","/* ----------------------------------------------------------------------------\n xterm renderer selection ladder: WebGL → 2D canvas → DOM.\n\n WebGL is the fast path (GPU-composited cells — the whole point of the burst\n responsiveness win). But a WebGL context can fail to init (headless / no GPU /\n blocklisted driver) or be LOST at runtime (GPU reset, tab backgrounded). On\n either, we dispose the addon and step down one tier so the terminal keeps\n painting via the 2D canvas addon, and failing that xterm's built-in DOM\n renderer (no addon).\n\n `attachRenderer` is pure over its `loaders` (each closes over the xterm\n instance and constructs+loads its addon, throwing on failure), so the ladder\n + context-loss downgrade unit-test without a real WebGL context.\n -------------------------------------------------------------------------- */\n\nexport type RendererTier = \"webgl\" | \"canvas\" | \"dom\";\n\nexport type RendererAddon = { dispose: () => void };\n\n/** A loader constructs + `loadAddon`s its renderer, wiring `onLoss` to the\n * addon's context-loss event, and throws if the renderer can't initialize. */\nexport type RendererLoaders = {\n webgl?: ((onLoss: () => void) => Promise<RendererAddon>) | undefined;\n canvas?: (() => Promise<RendererAddon>) | undefined;\n};\n\nconst ORDER: RendererTier[] = [\"webgl\", \"canvas\", \"dom\"];\n\n/** The next lower renderer tier, or null at the bottom (DOM). Pure. */\nexport function nextRendererTier(tier: RendererTier): RendererTier | null {\n const i = ORDER.indexOf(tier);\n return i >= 0 && i < ORDER.length - 1 ? ORDER[i + 1]! : null;\n}\n\n/**\n * Attach the highest renderer tier at or below `prefer` that initializes,\n * reporting the settled tier via `onTier`. On a WebGL/canvas context loss the\n * loader's `onLoss` re-enters at the next tier down, so `onTier` is called again\n * with the downgraded tier. Returns the tier chosen on this pass.\n */\nexport async function attachRenderer(\n prefer: RendererTier,\n loaders: RendererLoaders,\n onTier: (tier: RendererTier) => void,\n): Promise<RendererTier> {\n const start = Math.max(0, ORDER.indexOf(prefer));\n for (let i = start; i < ORDER.length; i++) {\n const tier = ORDER[i]!;\n if (tier === \"dom\") break;\n const loader = tier === \"webgl\" ? loaders.webgl : loaders.canvas;\n if (!loader) continue;\n try {\n const downgrade = nextRendererTier(tier) ?? \"dom\";\n await loader(() => {\n void attachRenderer(downgrade, loaders, onTier);\n });\n onTier(tier);\n return tier;\n } catch {\n // Initialization failed — fall through to the next tier down.\n }\n }\n onTier(\"dom\");\n return \"dom\";\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","useRef","useState","useEffect"]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useLatestTaskRunner
|
|
3
|
+
} from "./chunk-WEHMPOXK.js";
|
|
1
4
|
import {
|
|
2
5
|
ArtifactSurface,
|
|
3
6
|
EditableArtifactMessage,
|
|
@@ -6,7 +9,7 @@ import {
|
|
|
6
9
|
editableArtifactProjectionKey,
|
|
7
10
|
editableArtifactStatusLabel,
|
|
8
11
|
useEditableArtifactView
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-Y2PRMYLC.js";
|
|
10
13
|
import {
|
|
11
14
|
cn
|
|
12
15
|
} from "./chunk-22KP6LR5.js";
|
|
@@ -1784,6 +1787,7 @@ function EditableDocumentArtifactSurface({
|
|
|
1784
1787
|
error: null
|
|
1785
1788
|
});
|
|
1786
1789
|
const loadGeneration = useRef2(0);
|
|
1790
|
+
const scheduleLoad = useLatestTaskRunner(session);
|
|
1787
1791
|
const commandStateRef = useRef2(null);
|
|
1788
1792
|
if (commandStateRef.current?.session !== session) {
|
|
1789
1793
|
commandStateRef.current = {
|
|
@@ -1801,36 +1805,36 @@ function EditableDocumentArtifactSurface({
|
|
|
1801
1805
|
setState(
|
|
1802
1806
|
(current) => current.session === session ? { ...current, loading: true, error: null } : { session, projection: null, loading: true, error: null }
|
|
1803
1807
|
);
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
return;
|
|
1808
|
+
const superseded = () => cancelled || generation !== loadGeneration.current || commandStateRef.current !== commandState;
|
|
1809
|
+
scheduleLoad(async () => {
|
|
1810
|
+
if (superseded()) return;
|
|
1811
|
+
await composeDocumentEditorProjection(session).then(
|
|
1812
|
+
(projection2) => {
|
|
1813
|
+
if (superseded()) return;
|
|
1814
|
+
const nextParagraphText = paragraphTextById(projection2);
|
|
1815
|
+
for (const [localId, canonicalId] of commandState.optimisticParagraphIds) {
|
|
1816
|
+
if (nextParagraphText.has(canonicalId)) continue;
|
|
1817
|
+
const optimisticText = commandState.paragraphText.get(canonicalId) ?? commandState.paragraphText.get(localId);
|
|
1818
|
+
if (optimisticText !== void 0) nextParagraphText.set(canonicalId, optimisticText);
|
|
1819
|
+
}
|
|
1820
|
+
commandState.paragraphText = nextParagraphText;
|
|
1821
|
+
setState({ session, projection: projection2, loading: false, error: null });
|
|
1822
|
+
},
|
|
1823
|
+
(cause) => {
|
|
1824
|
+
if (superseded()) return;
|
|
1825
|
+
setState({
|
|
1826
|
+
session,
|
|
1827
|
+
projection: null,
|
|
1828
|
+
loading: false,
|
|
1829
|
+
error: asEditableArtifactError(cause, "Could not open this document")
|
|
1830
|
+
});
|
|
1821
1831
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
projection: null,
|
|
1825
|
-
loading: false,
|
|
1826
|
-
error: asEditableArtifactError(cause, "Could not open this document")
|
|
1827
|
-
});
|
|
1828
|
-
}
|
|
1829
|
-
);
|
|
1832
|
+
);
|
|
1833
|
+
});
|
|
1830
1834
|
return () => {
|
|
1831
1835
|
cancelled = true;
|
|
1832
1836
|
};
|
|
1833
|
-
}, [commandState, invalidator, retryEpoch, session]);
|
|
1837
|
+
}, [commandState, invalidator, retryEpoch, scheduleLoad, session]);
|
|
1834
1838
|
const refresh = useCallback2(() => setRetryEpoch((value) => value + 1), []);
|
|
1835
1839
|
const writable = !readOnly && view.writable;
|
|
1836
1840
|
const applyCommit = useCallback2(
|
|
@@ -2249,4 +2253,4 @@ export {
|
|
|
2249
2253
|
DocumentProjectionArtifactSurface,
|
|
2250
2254
|
EditableDocumentArtifactSurface
|
|
2251
2255
|
};
|
|
2252
|
-
//# sourceMappingURL=chunk-
|
|
2256
|
+
//# sourceMappingURL=chunk-WUISZ3WU.js.map
|