import type { DesktopConnectionState, DesktopStreamCapability } from "@opengeni/sdk"; import { type RefObject } from "react"; /** * The minimal WebSocket surface the frame renderer drives. Lets tests (and a * future transport swap) inject a fake without a live socket — the exact * analogue of `DesktopRfbFactory` for the noVNC path. Structurally a subset of * the browser `WebSocket`. */ export interface DesktopWebSocketLike { binaryType: string; send(data: ArrayBuffer): void; close(): void; addEventListener(type: "open" | "message" | "error" | "close", listener: (ev: { data?: unknown; } & Record) => void): void; removeEventListener(type: "open" | "message" | "error" | "close", listener: (ev: { data?: unknown; } & Record) => void): void; } export type DesktopWebSocketFactory = (url: string) => DesktopWebSocketLike; export type UseRelayFrameStreamOptions = { /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */ capability: DesktopStreamCapability | null; /** The mount target. A `` is appended here on connect. */ containerRef: RefObject; /** Custom socket factory (tests / a transport swap). Defaults to `new WebSocket(url)`. */ webSocketFactory?: DesktopWebSocketFactory | undefined; }; export type UseRelayFrameStreamResult = { state: DesktopConnectionState; error: Error | null; /** Tear down + reopen the socket (e.g. after a drop, once a fresh url arrives). */ reconnect: () => void; }; /** * VIEW-ONLY PNG-frame renderer for a SELF-HOSTED desktop stream — the relay * transport (`transport: "relay-frames"`, `client: "frames"`). The self-hosted * agent produces one PNG per frame as a protobuf datagram over the relay; there * is no RFB/noVNC here. This hook opens the relay DESKTOP channel as a CLIENT * (mirroring the proven diagnostic wire protocol), decodes each PNG onto a * `` mounted into `containerRef`, and drives the connection state * machine: idle → connecting (ws opening) → connected (first painted frame) → * error (ws error/close/ack rejection). * * It is DORMANT for any other transport (or no url): it stays idle and touches * nothing, so the noVNC path owns the surface. SSR-safe: the socket + DOM attach * live inside `useEffect`. * * Backpressure (critical — frames are ~1.8MB at ~10fps): at most one frame is * decoded at a time; a frame arriving mid-decode replaces the pending one * (latest-wins), so a slow decode can never build an unbounded queue. */ export declare function useRelayFrameStream(options: UseRelayFrameStreamOptions): UseRelayFrameStreamResult;