From e0d8663103e8fc0b0cdaf523ca3f48849848ed69 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Sat, 23 May 2026 17:16:33 +0900 Subject: feat: fallback model range slider with live label, model-changed event - Added model-changed event: backend emits it on fallback, frontend updates tab keyId/modelId - Range slider embedded inside active agent card when >1 model configured - Live label updates on drag (oninput), backend call only on release (onchange) - Slider auto-positions when fallback occurs via model-changed WS event --- packages/api/src/agent-manager.ts | 4 ++ packages/core/src/types/index.ts | 1 + .../src/lib/components/ModelSelector.svelte | 61 ++++++++++++++++++++-- packages/frontend/src/lib/tabs.svelte.ts | 9 ++++ packages/frontend/src/lib/types.ts | 1 + 5 files changed, 72 insertions(+), 4 deletions(-) diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts index 828855c..5b19eb3 100644 --- a/packages/api/src/agent-manager.ts +++ b/packages/api/src/agent-manager.ts @@ -984,6 +984,10 @@ export class AgentManager { `Falling back to "${nextEntry.key_id}" (model: ${nextEntry.model_id})...`; console.warn(`[dispatch] ${fallbackMsg}`); this.emit({ type: "notice", message: fallbackMsg }, tabId); + this.emit( + { type: "model-changed", keyId: nextEntry.key_id, modelId: nextEntry.model_id }, + tabId, + ); tabAgent.agent = null; continue; } diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index b17a720..1c3090f 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -38,6 +38,7 @@ export type AgentEvent = | { type: "shell-output"; data: string; stream: "stdout" | "stderr" } | { type: "error"; error: string; statusCode?: number } | { type: "notice"; message: string } + | { type: "model-changed"; keyId: string; modelId: string } | { type: "done"; message: ChatMessage } | { type: "task-list-update"; tasks: TaskItem[] } | { type: "config-reload" } diff --git a/packages/frontend/src/lib/components/ModelSelector.svelte b/packages/frontend/src/lib/components/ModelSelector.svelte index 19ce818..bbbf036 100644 --- a/packages/frontend/src/lib/components/ModelSelector.svelte +++ b/packages/frontend/src/lib/components/ModelSelector.svelte @@ -61,6 +61,7 @@ const modelCache = new Map(); let availableModels = $state([]); let loadingModels = $state(false); let modelError = $state(null); + let sliderDragging = $state(null); let cwdExists = $state(null); let cwdCheckTimer: ReturnType | null = null; @@ -265,28 +266,80 @@ const modelCache = new Map(); {:else}
{#each visibleAgents as agent (agent.slug + ":" + agent.scope)} - + {#if isActive && hasMultipleModels} + {@const displayIdx = sliderDragging !== null ? sliderDragging : (currentIdx >= 0 ? currentIdx : 0)} + {@const displayModel = agent.models[displayIdx]} +
+
+ {displayModel ? `${displayModel.key_id} / ${displayModel.model_id}` : `${activeKeyId} / ${activeModelId}`} +
+ = 0 ? currentIdx : 0} + class="range range-xs" + step="1" + oninput={(e) => { + sliderDragging = Number(e.currentTarget.value); + }} + onchange={(e) => { + const idx = Number(e.currentTarget.value); + const m = agent.models[idx]; + if (m) onModelChange(m.key_id, m.model_id); + sliderDragging = null; + }} + onclick={(e) => e.stopPropagation()} + onkeydown={(e) => e.stopPropagation()} + /> +
+ {#each agent.models as _, i} + {i + 1} + {/each} +
+
+ {/if} +
{/each} {/if} +