summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/ui/ModelSelector.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-08-12 22:01:59 +0900
committerAdam Malczewski <[email protected]>2026-08-12 22:01:59 +0900
commit5f10a40b26dbfdc10b2c02cdf23ec3a7edfa9443 (patch)
tree2f1d5ee2674d1dcc9909e89f06a82a7b10b4191a /src/features/chat/ui/ModelSelector.svelte
parent774519e052fd869952cc5e05aca159e4bf978fa1 (diff)
downloaddispatch-web-5f10a40b26dbfdc10b2c02cdf23ec3a7edfa9443.tar.gz
dispatch-web-5f10a40b26dbfdc10b2c02cdf23ec3a7edfa9443.zip
feat(models): numbered OpenCode credential selection, labels, migration
Adopt the numbered OPENCODE_API_KEY_N scheme in the model picker and concurrency surfaces. - model-select: numeric sort of opencode-<n> keys (#2 before #10); friendly OpenCode #N labels (raw opencode-N/<model> value still selected/persisted); migrateLegacyModelName rewrites opencode/<model> -> opencode-1/<model>. - ModelSelector: renders friendly key labels; surfaces a disappeared saved credential as UNAVAILABLE (disabled option + notice) without silently switching to another model. - app store: DEFAULT_MODEL -> opencode-1/...; catalogs no longer auto-fallback the active model when the selected credential is absent; boot restore migrates persisted legacy opencode/<model> tab models idempotently. - concurrency: providerLabel renders opencode-<n> as OpenCode #<n> in the limit rows and Add dropdown (raw provider id preserved on the wire). - Tests: parsing/sorting/labels/migration, unavailable-state selector behavior, no-silent-fallback, and concurrency provider label; fixtures updated to the numbered scheme. Verification: bun install, bun run typecheck (0 errors), bun run test (1179 passed), bun run check (clean, exit 0), bun run build (vite OK).
Diffstat (limited to 'src/features/chat/ui/ModelSelector.svelte')
-rw-r--r--src/features/chat/ui/ModelSelector.svelte31
1 files changed, 29 insertions, 2 deletions
diff --git a/src/features/chat/ui/ModelSelector.svelte b/src/features/chat/ui/ModelSelector.svelte
index 11b9feb..1d06848 100644
--- a/src/features/chat/ui/ModelSelector.svelte
+++ b/src/features/chat/ui/ModelSelector.svelte
@@ -1,6 +1,13 @@
<script lang="ts">
import type { ModelMetadata } from "@dispatch/transport-contract";
- import { isVisionModel, joinModelName, modelKeys, modelsForKey, splitModelName } from "../model-select";
+ import {
+ isVisionModel,
+ joinModelName,
+ modelKeyLabel,
+ modelKeys,
+ modelsForKey,
+ splitModelName,
+ } from "../model-select";
let {
models,
@@ -27,8 +34,17 @@
// Whether the currently-selected full model name is vision-capable.
const selectedVision = $derived(isVisionModel(modelInfo, selected));
+ // Whether the currently-selected full name is PRESENT in the catalog. When a
+ // saved credential/model disappears, we show it as unavailable rather than
+ // silently switching to something else.
+ const selectedUnavailable = $derived(selected !== "" && !models.includes(selected));
+ // The key part is itself missing from the catalog (e.g. a whole numbered
+ // OpenCode credential was removed).
+ const keyUnavailable = $derived(selected !== "" && !keys.includes(current.key));
+
// Switching key jumps to the first model available under it.
function selectKey(key: string): void {
+ if (key === current.key) return;
const first = modelsForKey(models, key)[0] ?? "";
onSelect(joinModelName(key, first));
}
@@ -50,8 +66,11 @@
onchange={(e) => selectKey(e.currentTarget.value)}
aria-label="Key selector"
>
+ {#if keyUnavailable}
+ <option value={current.key} disabled>{modelKeyLabel(current.key)} (unavailable)</option>
+ {/if}
{#each keys as key (key)}
- <option value={key}>{key}</option>
+ <option value={key}>{modelKeyLabel(key)}</option>
{/each}
</select>
<select
@@ -65,7 +84,15 @@
{model}{#if isVisionModel(modelInfo, fullNameFor(model))} ยท vision{/if}
</option>
{/each}
+ {#if selectedUnavailable && !keyUnavailable}
+ <option value={current.model} disabled>{current.model} (unavailable)</option>
+ {/if}
</select>
+ {#if selectedUnavailable}
+ <div class="text-xs text-warning" role="status">
+ Selected model โ€œ{selected}โ€ is unavailable โ€” choose another credential.
+ </div>
+ {/if}
{#if selectedVision}
<div class="flex items-center gap-1 text-xs text-base-content/60">
<svg