Skip to content

Latest commit

 

History

History
243 lines (202 loc) · 8.79 KB

File metadata and controls

243 lines (202 loc) · 8.79 KB

Embedding

There are two distinct dimensions: embedding the frontend into your app, and embedding blit server into your own service.

Your app, our components: @blit-sh/react / @blit-sh/solid

@blit-sh/react and @blit-sh/solid are workspace-first. Both are thin wrappers over @blit-sh/core's BlitTerminalSurface. A BlitWorkspace owns connections, each connection owns terminals, and each BlitTerminal renders a terminal by ID.

import {
  BlitTerminal,
  BlitWorkspaceProvider,
  useBlitFocusedSession,
  useBlitSessions,
  useBlitWorkspace,
} from "@blit-sh/react";
import { BlitWorkspace, WebSocketTransport } from "@blit-sh/core";
import { useEffect, useMemo } from "react";

function EmbeddedBlit({ wasm, passphrase }: { wasm: any; passphrase: string }) {
  const transport = useMemo(
    () => new WebSocketTransport("wss://example.com/blit", passphrase),
    [passphrase],
  );

  const workspace = useMemo(
    () =>
      new BlitWorkspace({
        wasm,
        connections: [{ id: "default", transport }],
      }),
    [transport, wasm],
  );

  useEffect(() => () => workspace.dispose(), [workspace]);

  return (
    <BlitWorkspaceProvider workspace={workspace}>
      <TerminalScreen />
    </BlitWorkspaceProvider>
  );
}

function TerminalScreen() {
  const workspace = useBlitWorkspace();
  const sessions = useBlitSessions();
  const focusedSession = useBlitFocusedSession();

  useEffect(() => {
    if (sessions.length > 0) return;
    void workspace.createSession({
      connectionId: "default",
      rows: 24,
      cols: 80,
    });
  }, [sessions.length, workspace]);

  return (
    <BlitTerminal
      sessionId={focusedSession?.id ?? null}
      style={{ width: "100%", height: "100vh" }}
    />
  );
}

React API

API Purpose
new BlitWorkspace({ wasm, connections }) Create a workspace with one or more transports
BlitWorkspaceProvider Put the workspace, palette, and font settings in context
useBlitWorkspace() Get the imperative workspace object
useBlitWorkspaceState() Read the full reactive workspace snapshot
useBlitConnection(connectionId?) Read one connection snapshot
useBlitSessions() Read all terminals
useBlitFocusedSession() Read the currently focused terminal
useBlitWorkspaceConnection(workspace, id, transport) Manage a connection lifecycle with cleanup
BlitTerminal Render one terminal by sessionId

Solid API

import {
  BlitTerminal,
  BlitWorkspaceProvider,
  createBlitWorkspace,
  createBlitWorkspaceState,
  createBlitSessions,
  useBlitFocusedSession,
} from "@blit-sh/solid";
import { BlitWorkspace } from "@blit-sh/core";
import { createSignal, onCleanup, createEffect } from "solid-js";

function EmbeddedBlit(props: { wasm: any; passphrase: string }) {
  const workspace = new BlitWorkspace({
    wasm: props.wasm,
    connections: [
      {
        id: "default",
        transport: {
          type: "websocket",
          url: "wss://example.com/blit",
          passphrase: props.passphrase,
        },
      },
    ],
  });
  onCleanup(() => workspace.dispose());

  return (
    <BlitWorkspaceProvider workspace={workspace}>
      <TerminalScreen />
    </BlitWorkspaceProvider>
  );
}

function TerminalScreen() {
  const workspace = createBlitWorkspace();
  const sessions = createBlitSessions();
  const focusedSession = () => useBlitFocusedSession(workspace);

  createEffect(() => {
    if (sessions().length > 0) return;
    workspace.createSession({ connectionId: "default", rows: 24, cols: 80 });
  });

  return (
    <BlitTerminal
      sessionId={focusedSession()?.id ?? null}
      style={{ width: "100%", height: "100vh" }}
    />
  );
}
API Purpose
new BlitWorkspace({ wasm, connections }) Create a workspace with one or more transports
BlitWorkspaceProvider Put the workspace, palette, and font settings in context
createBlitWorkspace() Get the imperative workspace object from context
createBlitWorkspaceState(workspace?) Reactive signal tracking the workspace snapshot
createBlitSessions(workspace?) Reactive signal tracking all terminals
useBlitSession(workspace, sessionId) Look up a single terminal by ID (non-reactive)
useBlitFocusedSession(workspace) Look up the focused terminal (non-reactive)
useBlitConnection(workspace, sessionId) Look up a connection snapshot (non-reactive)
createBlitWorkspaceConnection(workspace, id, transport) Manage a connection lifecycle with onCleanup
BlitTerminal Render one terminal by sessionId

Wayland surface rendering (experimental)

BlitSurfaceView renders a single Wayland surface from a terminal's compositor. The server encodes each surface as H.264 or AV1; the component decodes via WebCodecs and draws to a canvas.

import { BlitSurfaceView } from "@blit-sh/react";

function AppWindow({
  connectionId,
  surfaceId,
}: {
  connectionId: string;
  surfaceId: number;
}) {
  return (
    <BlitSurfaceView
      connectionId={connectionId}
      surfaceId={surfaceId}
      style={{ width: 800, height: 600 }}
    />
  );
}

Every terminal has an experimental Wayland compositor available. Any command — shell, TUI, or GUI — can open Wayland surfaces:

workspace.createSession({
  connectionId: "default",
  rows: 24,
  cols: 80,
  command: "my-gui-app",
});

Surfaces created by the terminal appear in the connection's surfaceStore, keyed by the terminal's PTY ID. Each surface has a surfaceId, parentId, title, appId, width, and height.

Workspace operations

  • createSession({ connectionId, rows, cols, tag?, command?, cwdFromSessionId? })
  • closeSession(sessionId)
  • restartSession(sessionId)
  • focusSession(sessionId | null)
  • search(query, { connectionId? })
  • setVisibleSessions(sessionIds)
  • addConnection(...) / removeConnection(connectionId) / reconnectConnection(connectionId)

Transports

All transports share a common set of options (BlitTransportOptions):

Option Default Description
reconnect true Auto-reconnect on disconnect
reconnectDelay 500 Initial reconnect delay (ms)
maxReconnectDelay 10000 Maximum reconnect delay (ms)
reconnectBackoff 1.5 Backoff multiplier
connectTimeoutMs none (WS) / 10000 (WT, WebRTC) Connection timeout (ms)
// WebSocket
new WebSocketTransport(url, passphrase, { reconnect, reconnectDelay, connectTimeoutMs, ... })

// WebTransport (QUIC/HTTP3)
new WebTransportTransport(url, passphrase, { serverCertificateHash, ... })

// WebRTC data channel
createWebRtcDataChannelTransport(peerConnection, { label, displayRateFps, ... })

Or implement your own:

interface BlitTransport {
  connect(): void;
  send(data: Uint8Array): void;
  close(): void;
  readonly status: ConnectionStatus;
  readonly authRejected: boolean;
  readonly lastError: string | null;
  addEventListener(type: "message" | "statuschange", listener: Function): void;
  removeEventListener(
    type: "message" | "statuschange",
    listener: Function,
  ): void;
}

Your service, our server: fd-channel mode

fd-channel lets an external process own blit server's lifecycle and control which clients connect via SCM_RIGHTS fd passing. See ARCHITECTURE.md for the protocol details and the working examples: