From 3b44205a386d670e0d6971aa0e4ea83e7e591d17 Mon Sep 17 00:00:00 2001 From: Aarav Sharma Date: Sun, 10 May 2026 22:20:24 -0600 Subject: [PATCH 1/3] feat(cli): improve model info panel with modality and description support --- .../components/model-info-panel-utils.ts | 9 +- .../kilocode/components/model-info-panel.tsx | 137 ++++++++++++++---- 2 files changed, 113 insertions(+), 33 deletions(-) diff --git a/packages/opencode/src/kilocode/components/model-info-panel-utils.ts b/packages/opencode/src/kilocode/components/model-info-panel-utils.ts index 582db55650d..ee7c596e747 100644 --- a/packages/opencode/src/kilocode/components/model-info-panel-utils.ts +++ b/packages/opencode/src/kilocode/components/model-info-panel-utils.ts @@ -13,11 +13,11 @@ export function fmtPrice(n: number): string { return `$${n.toFixed(2)}/1M` } -export function fmtCachedPrice(cost: Cost): string { +export function fmtCachedPrice(cost: Cost): string | null { const read = cost.cache.read if (read > 0) return fmtPrice(read) if (cost.input === 0) return fmtPrice(0) - return "N/A" + return null } export function avgPrice(cost: Cost): number { @@ -31,3 +31,8 @@ export function fmtContext(n: number): string { if (n >= 1_000) return `${(n / 1_000).toFixed(n % 1_000 === 0 ? 0 : 1)}K` return String(n) } + +export function fmtDate(s: string): string { + const d = new Date(s) + return d.toLocaleDateString(undefined, { year: "numeric", month: "short" }) +} diff --git a/packages/opencode/src/kilocode/components/model-info-panel.tsx b/packages/opencode/src/kilocode/components/model-info-panel.tsx index ceb9e860445..c3d49b1ebcb 100644 --- a/packages/opencode/src/kilocode/components/model-info-panel.tsx +++ b/packages/opencode/src/kilocode/components/model-info-panel.tsx @@ -1,7 +1,9 @@ import { TextAttributes } from "@opentui/core" +import { useTerminalDimensions } from "@opentui/solid" +import { createMemo } from "solid-js" import { useTheme } from "@tui/context/theme" import type { Model } from "@kilocode/sdk/v2" -import { avgPrice, fmtCachedPrice, fmtContext, fmtPrice } from "./model-info-panel-utils" +import { avgPrice, fmtCachedPrice, fmtContext, fmtDate, fmtPrice } from "./model-info-panel-utils" import { Show } from "solid-js" interface Props { @@ -12,6 +14,40 @@ interface Props { export function ModelInfoPanel(props: Props) { const { theme } = useTheme() const m = () => props.model + const dimensions = useTerminalDimensions() + + const maxHeight = createMemo(() => Math.floor(dimensions().height / 2) - 3) + + const cost = () => m().cost + const cached = () => (cost() ? fmtCachedPrice(cost()) : null) + const avg = () => (cost() ? avgPrice(cost()) : undefined) + const caps = () => m().capabilities + const inputs = () => caps()?.input + const outputs = () => caps()?.output + const activeInputModalities = () => { + if (!inputs()) return [] as string[] + return Object.entries(inputs()) + .filter(([k, v]) => v && k !== "text") + .map(([k]) => k.charAt(0).toUpperCase() + k.slice(1)) + } + const activeOutputModalities = () => { + if (!outputs()) return [] as string[] + return Object.entries(outputs()) + .filter(([k, v]) => v && k !== "text") + .map(([k]) => k.charAt(0).toUpperCase() + k.slice(1)) + } + const inputLine = () => { + const mods = activeInputModalities() + return mods.length > 0 ? `In: ${mods.join(", ")}` : null + } + const outputLine = () => { + const mods = activeOutputModalities() + return mods.length > 0 ? `Out: ${mods.join(", ")}` : null + } + const desc = () => { + const d = m().options?.description + return typeof d === "string" && d.trim() ? d : null + } return ( - - - {m().name ?? m().id ?? "Model"} - - {props.provider ?? m().providerID ?? ""} - - + + + + {m().name ?? m().id ?? "Model"} + + {props.provider ?? m().providerID ?? ""} + - + Free - + - Input - {m() ? fmtPrice(m().cost.input) : "—"} - - - Output - {m() ? fmtPrice(m().cost.output) : "—"} - - - Cache Read - {m() ? fmtCachedPrice(m().cost) : "—"} - - - - Context Size - {m() ? fmtContext(m().limit.context) : "—"} - - - Average Cost - {m() ? fmtPrice(avgPrice(m().cost)) : "—"} + Family + {m().family!.charAt(0).toUpperCase() + m().family!.slice(1)} - + + + Released + {fmtDate(m().release_date)} + + + + + + Input + {m() ? fmtPrice(m().cost.input) : "—"} + + + Output + {m() ? fmtPrice(m().cost.output) : "—"} + + + + Cached + {cached()} + + + + + Avg Cost + {m() ? fmtPrice(avg()!) : "—"} + + + + Context + {m() ? fmtContext(m().limit.context) : "—"} + + + + + + + + [Reasoning] + + + {inputLine()} + + + {outputLine()} + + + + + + + {desc()} + + + ) -} +} \ No newline at end of file From 51b35d0b9490532a3f2174086f06340a89f99116 Mon Sep 17 00:00:00 2001 From: Aarav Sharma Date: Sun, 10 May 2026 22:29:08 -0600 Subject: [PATCH 2/3] fix(model-info-panel): resolve review comments - merge imports, guard fmtDate, use Show render-prop --- .../src/kilocode/components/model-info-panel-utils.ts | 1 + .../src/kilocode/components/model-info-panel.tsx | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/kilocode/components/model-info-panel-utils.ts b/packages/opencode/src/kilocode/components/model-info-panel-utils.ts index ee7c596e747..152c23a50e6 100644 --- a/packages/opencode/src/kilocode/components/model-info-panel-utils.ts +++ b/packages/opencode/src/kilocode/components/model-info-panel-utils.ts @@ -34,5 +34,6 @@ export function fmtContext(n: number): string { export function fmtDate(s: string): string { const d = new Date(s) + if (isNaN(d.getTime())) return s return d.toLocaleDateString(undefined, { year: "numeric", month: "short" }) } diff --git a/packages/opencode/src/kilocode/components/model-info-panel.tsx b/packages/opencode/src/kilocode/components/model-info-panel.tsx index c3d49b1ebcb..b726d606433 100644 --- a/packages/opencode/src/kilocode/components/model-info-panel.tsx +++ b/packages/opencode/src/kilocode/components/model-info-panel.tsx @@ -1,10 +1,9 @@ import { TextAttributes } from "@opentui/core" import { useTerminalDimensions } from "@opentui/solid" -import { createMemo } from "solid-js" +import { createMemo, Show } from "solid-js" import { useTheme } from "@tui/context/theme" import type { Model } from "@kilocode/sdk/v2" import { avgPrice, fmtCachedPrice, fmtContext, fmtDate, fmtPrice } from "./model-info-panel-utils" -import { Show } from "solid-js" interface Props { model: Model @@ -121,10 +120,10 @@ export function ModelInfoPanel(props: Props) { [Reasoning] - {inputLine()} + {(line) => {line()}} - {outputLine()} + {(line) => {line()}} @@ -137,4 +136,4 @@ export function ModelInfoPanel(props: Props) { ) -} \ No newline at end of file +} From 8e90f2969542e009481bf7fe0195f0e01f9f81bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Mon, 11 May 2026 09:50:01 -0300 Subject: [PATCH 3/3] fix(cli): improve model info panel scrollbar padding and capabilities display --- .../kilocode/components/model-info-panel.tsx | 25 ++++++++++++++----- .../kilocode/model-info-panel-utils.test.ts | 4 +-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/kilocode/components/model-info-panel.tsx b/packages/opencode/src/kilocode/components/model-info-panel.tsx index b726d606433..c228eb7e1f2 100644 --- a/packages/opencode/src/kilocode/components/model-info-panel.tsx +++ b/packages/opencode/src/kilocode/components/model-info-panel.tsx @@ -61,6 +61,7 @@ export function ModelInfoPanel(props: Props) { > @@ -114,22 +115,34 @@ export function ModelInfoPanel(props: Props) { - - [Reasoning] + + Reasoning + Yes + - {(line) => {line()}} + {(line) => ( + + Caps + {line().replace(/^In:\s*/, "")} + + )} - {(line) => {line()}} + {(line) => ( + + Out + {line().replace(/^Out:\s*/, "")} + + )} - - + {" "} + {desc()} diff --git a/packages/opencode/test/kilocode/model-info-panel-utils.test.ts b/packages/opencode/test/kilocode/model-info-panel-utils.test.ts index 07159e5bb22..8a07fcb2291 100644 --- a/packages/opencode/test/kilocode/model-info-panel-utils.test.ts +++ b/packages/opencode/test/kilocode/model-info-panel-utils.test.ts @@ -22,8 +22,8 @@ describe("model info panel price formatting", () => { expect(fmtCachedPrice({ input: 0, output: 0, cache: { read: 0, write: 0 } })).toBe("Free") }) - test("fmtCachedPrice returns N/A without cache read", () => { - expect(fmtCachedPrice({ input: 3, output: 15, cache: { read: 0, write: 0 } })).toBe("N/A") + test("fmtCachedPrice returns null without cache read", () => { + expect(fmtCachedPrice({ input: 3, output: 15, cache: { read: 0, write: 0 } })).toBeNull() }) test("avgPrice uses cache weighted formula when cache read exists", () => {