From 6433cc42de1ceca7210e2b64ad3b98b3a5ce7d02 Mon Sep 17 00:00:00 2001
From: Adam Malczewski <github@tradam.dev>
Date: Tue, 2 Jun 2026 13:25:23 +0900
Subject: feat(context-window): show current/max context usage per tab/model

Add a 'Context Window' sidebar view showing the live context occupancy
(latest request's input+output) against the model's maximum context
window, resolved dynamically from the models.dev catalog.

- core: models.dev catalog module (resolveContextLimit) with disk cache,
  TTL, stale-fallback + offline penalty memo; null for unknown models.
- api: GET /models/context-limit?provider=&modelId=.
- frontend: ContextWindowPanel + computeContextUsage helper; App resolves
  + caches the active model's max (anthropic/opencode-anthropic only);
  percent shown to 2 decimals; degrades to bare token count when max
  unknown.
- tests: core catalog (13), api route (3), frontend helper (6).
---
 packages/frontend/src/lib/components/SidebarPanel.svelte | 11 +++++++++++
 1 file changed, 11 insertions(+)

(limited to 'packages/frontend/src/lib/components/SidebarPanel.svelte')

diff --git a/packages/frontend/src/lib/components/SidebarPanel.svelte b/packages/frontend/src/lib/components/SidebarPanel.svelte
index 491b1bd..573a6fc 100644
--- a/packages/frontend/src/lib/components/SidebarPanel.svelte
+++ b/packages/frontend/src/lib/components/SidebarPanel.svelte
@@ -4,6 +4,7 @@ import type { CacheStats, KeyInfo, LogEntry, TaskItem } from "../types.js";
 import CacheRatePanel from "./CacheRatePanel.svelte";
 import ClaudeReset from "./ClaudeReset.svelte";
 import ConfigPanel from "./ConfigPanel.svelte";
+import ContextWindowPanel from "./ContextWindowPanel.svelte";
 import DebugPanel from "./DebugPanel.svelte";
 import KeyUsage from "./KeyUsage.svelte";
 import ModelSelector from "./ModelSelector.svelte";
@@ -27,6 +28,7 @@ const {
 	tasks = [],
 	cacheStats = null,
 	cacheTabTitle = null,
+	contextLimit = null,
 	permissionLog = [],
 	apiBase = "",
 	activeKeyId = null,
@@ -47,6 +49,7 @@ const {
 	tasks?: TaskItem[];
 	cacheStats?: CacheStats | null;
 	cacheTabTitle?: string | null;
+	contextLimit?: number | null;
 	permissionLog?: LogEntry[];
 	apiBase?: string;
 	activeKeyId?: string | null;
@@ -89,6 +92,7 @@ const viewOptions = [
 	"Chat Settings",
 	"Key Usage",
 	"Cache Rate",
+	"Context Window",
 	"Claude Reset",
 	"Model Status",
 	"Tasks",
@@ -170,6 +174,13 @@ function contentClass(_selected: string): string {
 					<KeyUsage {keys} {apiBase} />
 				{:else if panel.selected === "Cache Rate"}
 					<CacheRatePanel {cacheStats} tabTitle={cacheTabTitle} />
+				{:else if panel.selected === "Context Window"}
+					<ContextWindowPanel
+						{cacheStats}
+						{contextLimit}
+						tabTitle={cacheTabTitle}
+						modelId={activeModelId}
+					/>
 				{:else if panel.selected === "Claude Reset"}
 					<ClaudeReset {apiBase} />
 				{:else if panel.selected === "Model Status"}
-- 
cgit v1.2.3

