import { createComponent as _$createComponent } from "opentui:runtime-module:%40opentui%2Fsolid"; import { createTextNode as _$createTextNode } from "opentui:runtime-module:%40opentui%2Fsolid"; import { insertNode as _$insertNode } from "opentui:runtime-module:%40opentui%2Fsolid"; import { memo as _$memo } from "opentui:runtime-module:%40opentui%2Fsolid"; import { setProp as _$setProp } from "opentui:runtime-module:%40opentui%2Fsolid"; import { insert as _$insert } from "opentui:runtime-module:%40opentui%2Fsolid"; import { createElement as _$createElement } from "opentui:runtime-module:%40opentui%2Fsolid"; /** @jsxImportSource @opentui/solid */ // @ts-nocheck import { createEffect, createSignal, onCleanup } from "opentui:runtime-module:solid-js"; import { coerceAftStatus, formatStatusDialogMessage } from "../shared/status"; import { AftRpc } from "./aft-rpc"; import { formatAftStatusSegment, summarizeAftSidebar } from "./v2-status"; function eventMatchesSession(eventSessionID, sessionID) { return eventSessionID === undefined || eventSessionID === sessionID; } async function fetchStatus(context, rpc, sessionID, signal) { const response = await rpc.getStatus(sessionID ? { sessionID } : {}, { ...(context.location ? { location: context.location } : {}), ...(signal ? { signal } : {}) }); return coerceAftStatus(response); } export function subscribeV2StatusRefresh(rpc, sessionID, refresh) { const unsubscribes = [rpc.events.on("statusInvalidated", event => { if (eventMatchesSession(event.data.sessionID, sessionID())) refresh(); }), rpc.events.on("indexProgress", event => { if (eventMatchesSession(event.data.sessionID, sessionID())) refresh(); })]; return () => { for (const unsubscribe of unsubscribes) unsubscribe(); }; } function useAftStatus(context, rpc, sessionID) { const [status, setStatus] = createSignal(null); let refreshGeneration = 0; let refreshController; const refresh = async () => { const requestedSessionID = sessionID(); const generation = ++refreshGeneration; refreshController?.abort(); const controller = new AbortController(); refreshController = controller; try { const next = await fetchStatus(context, rpc, requestedSessionID, controller.signal); if (controller.signal.aborted || generation !== refreshGeneration) return; if (sessionID() !== requestedSessionID) return; setStatus(next); } catch { // Status is best-effort UI data; the next typed invalidation retries it. } }; createEffect(() => { sessionID(); void refresh(); }); const unsubscribe = subscribeV2StatusRefresh(rpc, sessionID, () => { void refresh(); }); onCleanup(() => { refreshGeneration += 1; refreshController?.abort(); unsubscribe(); }); return status; } function FooterStatus(props) { const status = useAftStatus(props.context, props.rpc, () => props.sessionID); return (() => { var _el$ = _$createElement("text"); _$insert(_el$, () => formatAftStatusSegment(status())); return _el$; })(); } function SidebarStatus(props) { const status = useAftStatus(props.context, props.rpc, () => props.sessionID); const summary = () => summarizeAftSidebar(status()); return (() => { var _el$2 = _$createElement("box"), _el$3 = _$createElement("text"), _el$4 = _$createElement("b"), _el$5 = _$createElement("text"), _el$6 = _$createTextNode(`Search: `), _el$7 = _$createElement("text"), _el$8 = _$createTextNode(`Semantic: `), _el$9 = _$createElement("text"), _el$0 = _$createTextNode(`Health: `); _$insertNode(_el$2, _el$3); _$insertNode(_el$2, _el$5); _$insertNode(_el$2, _el$7); _$insertNode(_el$2, _el$9); _$setProp(_el$2, "width", "100%"); _$setProp(_el$2, "flexDirection", "column"); _$insertNode(_el$3, _el$4); _$insert(_el$4, () => summary().title); _$insert(_el$3, (() => { var _c$ = _$memo(() => !!summary().version); return () => _c$() ? ` v${summary().version}` : ""; })(), null); _$insertNode(_el$5, _el$6); _$insert(_el$5, () => summary().search, null); _$insertNode(_el$7, _el$8); _$insert(_el$7, () => summary().semantic, null); _$insertNode(_el$9, _el$0); _$insert(_el$9, () => summary().health, null); return _el$2; })(); } function activeSessionID(context) { const route = context.ui.router.current(); return route.type === "session" ? route.sessionID : undefined; } async function showStatusDialog(context, rpc, sessionID) { try { const status = await fetchStatus(context, rpc, sessionID); await context.ui.dialog.alert({ title: "AFT Status", message: formatStatusDialogMessage(status) }); } catch { await context.ui.dialog.alert({ title: "AFT Status", message: "AFT is starting up. Status will refresh automatically." }); } } export async function setupV2Tui(context) { const rpc = context.client.rpc(AftRpc); const controller = new AbortController(); const slotCleanups = [context.ui.slot({ append: "prompt.footer.status", render: input => _$createComponent(FooterStatus, { context: context, rpc: rpc, get sessionID() { return input.sessionID; } }) }), context.ui.slot({ append: "sidebar.content", render: input => _$createComponent(SidebarStatus, { context: context, rpc: rpc, get sessionID() { return input.sessionID; } }) })]; const stopDialogEvents = rpc.events.on("showStatusDialog", event => showStatusDialog(context, rpc, event.data.sessionID ?? activeSessionID(context)), { signal: controller.signal }); context.keymap.layer(() => ({ commands: [{ id: "aft.status", title: "AFT: Status", description: "Show AFT status, index health, and cache usage", group: "AFT", palette: true, // V2's KeymapCommand contract (plugin/tui/context.ts:379-385) dispatches // slash entries unless `arguments: true`; omitting it prevents this text // from remaining in the prompt or being submitted to the model. slash: { name: "aft-status" }, run: () => showStatusDialog(context, rpc, activeSessionID(context)) }], bindings: [] })); return () => { controller.abort(); stopDialogEvents(); for (const cleanup of slotCleanups) cleanup(); }; }