Example Board (Server Runtime)

Server runtime mode: board initializes on first connect and streams updates via SSE. Source execution and event processing happen on the server side.

Advanced Composition Model

This demo still mounts the full board, but the same client now exposes the shared runtime session needed for filtered or projected live views. Host applications do not need to reimplement SSE, client identity, notification reduction, or card chat subscribe or unsubscribe.

Active board: default
Shared client id: pending
Session state: not connected

What is public now

  • createBoardRuntimeSession()
  • createDerivedBoardRuntime()
  • applyBoardNotifications()
  • serverPayloadToBoardState()
  • pickBoardState()
  • subtractBoardState()
  • deriveBoardState()
  • session.attachProvidedState()
  • session.subscribeCardChats()
  • session.unsubscribeCardChats()

Recommended shape

  1. Create one shared runtime session per logical board.
  2. Mount the full board or any number of derived views from that same session.
  3. Reuse the shared client id for all chat subscribe or unsubscribe calls.
  4. Use the public reducer helpers instead of mutating projected state by hand.

When to use this

  • split views over one board
  • plugin or secondary mounts
  • filtered card subsets
  • provided-state boot with live SSE attached afterward
1. Share one session
const client = BoardLiveCardsClient.createBoardRuntimeClient({
  fetchServer,
  boardPaths,
  getServerOrigin,
});

await client.bootstrapBoard({
  boardId: 'default',
  rootElement: boardRoot,
});

const session = client.getRuntimeSession();
2. Mount derived live views
const alertsView = client.createDerivedRuntime({
  includeCard: (model) => model.id.startsWith('alert-'),
});

alertsView.mountBoard({
  rootElement: alertsRoot,
  mode: 'board',
});

// shares SSE + chat session with the main board
3. Start from provided state
const session = BoardLiveCardsClient.createBoardRuntimeSession({
  fetchServer,
  boardPaths,
  getServerOrigin,
});

session.attachProvidedState({ boardId, payload });
await session.bootstrap({ boardId, skipInitBoard: true });

const view = BoardLiveCardsClient.createDerivedBoardRuntime({
  session,
  includeCard: (model) => model.id !== 'hidden-card',
});
Reducer helpers for host-managed state
const state = BoardLiveCardsClient.serverPayloadToBoardState(payload);

const nextState = BoardLiveCardsClient.applyBoardNotifications(
  state,
  notifications,
  () => payload,
);

const leftPane = BoardLiveCardsClient.pickBoardState(nextState, ['card-a']);
const rightPane = BoardLiveCardsClient.subtractBoardState(nextState, ['card-a']);
Chat lifecycle stays with the session
const session = client.getRuntimeSession();

await session.subscribeCardChats(cardId);
await session.unsubscribeCardChats(cardId);

// same shared client id is reused automatically
console.log(session.getClientId());

// incoming notifications keep mounted chat UIs synchronized
Loading...

Initializing board...