"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import type { OptionPrint } from "@islandflow/types"; const MAX_ITEMS = 60; const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1"]); type WsStatus = "connecting" | "connected" | "disconnected"; type OptionMessage = { type: "option-print"; payload: OptionPrint; }; const buildWsUrl = (): string => { const envBase = process.env.NEXT_PUBLIC_API_URL; if (envBase) { const url = new URL(envBase); url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; url.pathname = "/ws/options"; url.search = ""; url.hash = ""; return url.toString(); } const { protocol, hostname } = window.location; const wsProtocol = protocol === "https:" ? "wss" : "ws"; const isLocal = LOCAL_HOSTS.has(hostname); const host = isLocal ? `${hostname}:4000` : window.location.host; return `${wsProtocol}://${host}/ws/options`; }; const formatPrice = (price: number): string => { return price.toFixed(2); }; const formatSize = (size: number): string => { return size.toLocaleString(); }; const formatTime = (ts: number): string => { return new Date(ts).toLocaleTimeString(); }; export default function HomePage() { const [status, setStatus] = useState("connecting"); const [prints, setPrints] = useState([]); const [lastUpdate, setLastUpdate] = useState(null); const reconnectRef = useRef(null); const socketRef = useRef(null); const statusLabel = useMemo(() => { switch (status) { case "connected": return "Live"; case "connecting": return "Connecting"; case "disconnected": default: return "Disconnected"; } }, [status]); useEffect(() => { let active = true; const connect = () => { if (!active) { return; } setStatus("connecting"); const socket = new WebSocket(buildWsUrl()); socketRef.current = socket; socket.onopen = () => { if (!active) { return; } setStatus("connected"); }; socket.onmessage = (event) => { if (!active) { return; } try { const message = JSON.parse(event.data) as OptionMessage; if (message.type !== "option-print") { return; } setPrints((prev) => { const next = [message.payload, ...prev]; return next.slice(0, MAX_ITEMS); }); setLastUpdate(Date.now()); } catch (error) { console.warn("Failed to parse websocket payload", error); } }; socket.onclose = () => { if (!active) { return; } setStatus("disconnected"); reconnectRef.current = window.setTimeout(() => { connect(); }, 1000); }; socket.onerror = () => { if (!active) { return; } setStatus("disconnected"); socket.close(); }; }; connect(); return () => { active = false; if (reconnectRef.current !== null) { window.clearTimeout(reconnectRef.current); } if (socketRef.current) { socketRef.current.close(); } }; }, []); return (

Realtime flow workspace

Islandflow

Live option prints streaming from /ws/options.

{statusLabel} {lastUpdate ? ( Updated {formatTime(lastUpdate)} ) : ( Waiting for data )}

Options Tape

Newest prints first (max {MAX_ITEMS}).

Live
{prints.length === 0 ? (
No prints yet. Start ingest-options to populate the tape.
) : ( prints.map((print) => (
{print.option_contract_id}
${formatPrice(print.price)} {formatSize(print.size)}x {print.exchange} {print.conditions?.length ? ( {print.conditions.join(", ")} ) : null}
{formatTime(print.ts)}
)) )}
); }