import { type Accessor, createMemo, createSignal, Match, Show, Switch } from "solid-js" import { useRouteData } from "@tui/context/route" import { useSync } from "@tui/context/sync" import { pipe, sumBy } from "remeda" import { useTheme } from "@tui/context/theme" import { SplitBorder } from "@tui/component/border" import type { AssistantMessage, Session } from "@opencode-ai/sdk/v2" import { useCommandDialog } from "@tui/component/dialog-command" import { useKeybind } from "../../context/keybind" import { Flag } from "@/flag/flag" import { useTerminalDimensions } from "@opentui/solid" const Title = (props: { session: Accessor }) => { const { theme } = useTheme() return ( # {props.session().title} ) } const ContextInfo = (props: { context: Accessor; cost: Accessor }) => { const { theme } = useTheme() return ( {props.context()} ({props.cost()}) ) } const WorkspaceInfo = (props: { workspace: Accessor }) => { const { theme } = useTheme() return ( {props.workspace()} ) } export function Header() { const route = useRouteData("session") const sync = useSync() const session = createMemo(() => sync.session.get(route.sessionID)!) const messages = createMemo(() => sync.data.message[route.sessionID] ?? []) const cost = createMemo(() => { const total = pipe( messages(), sumBy((x) => (x.role === "assistant" ? x.cost : 0)), ) return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(total) }) const context = createMemo(() => { const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage if (!last) return const total = last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID] let result = total.toLocaleString() if (model?.limit.context) { result += " " + Math.round((total / model.limit.context) * 100) + "%" } return result }) const workspace = createMemo(() => { const id = session()?.workspaceID if (!id) return "Workspace local" const info = sync.workspace.get(id) if (!info) return `Workspace ${id}` return `Workspace ${id} (${info.type})` }) const { theme } = useTheme() const keybind = useKeybind() const command = useCommandDialog() const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null) const dimensions = useTerminalDimensions() const narrow = createMemo(() => dimensions().width < 80) return ( {Flag.OPENCODE_EXPERIMENTAL_WORKSPACES_TUI ? ( Subagent session ) : ( Subagent session )} setHover("parent")} onMouseOut={() => setHover(null)} onMouseUp={() => command.trigger("session.parent")} backgroundColor={hover() === "parent" ? theme.backgroundElement : theme.backgroundPanel} > Parent {keybind.print("session_parent")} setHover("prev")} onMouseOut={() => setHover(null)} onMouseUp={() => command.trigger("session.child.previous")} backgroundColor={hover() === "prev" ? theme.backgroundElement : theme.backgroundPanel} > Prev {keybind.print("session_child_cycle_reverse")} setHover("next")} onMouseOut={() => setHover(null)} onMouseUp={() => command.trigger("session.child.next")} backgroundColor={hover() === "next" ? theme.backgroundElement : theme.backgroundPanel} > Next {keybind.print("session_child_cycle")} {Flag.OPENCODE_EXPERIMENTAL_WORKSPACES_TUI ? ( <WorkspaceInfo workspace={workspace} /> </box> ) : ( <Title session={session} /> )} <ContextInfo context={context} cost={cost} /> </box> </Match> </Switch> </box> </box> ) }