diff options
| author | Adam Malczewski <[email protected]> | 2026-06-10 16:48:30 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-10 16:48:30 +0900 |
| commit | 7c4038c9851a6acf4494a2d9555830d7bee9c7b9 (patch) | |
| tree | f517ac90a185c9d2febb9772f4ab5ad22b9ea79e /src/features/chat/model-select.ts | |
| parent | 55f635bea24f673f6d0242c2c7b749b4fd5329a2 (diff) | |
| download | dispatch-web-7c4038c9851a6acf4494a2d9555830d7bee9c7b9.tar.gz dispatch-web-7c4038c9851a6acf4494a2d9555830d7bee9c7b9.zip | |
feat(chat,app): Model view in sidebar + split key/model selectors
- move the model picker out of the chat header into a dedicated "Model" sidebar
view; sidebar now seeds two default panels (Model on top, Extensions below)
- split the single model dropdown into two stacked selects: a key selector
(distinct credential keys) + a model selector (models under the current key)
- pure model-select helpers (splitModelName/joinModelName/modelKeys/modelsForKey),
split on the FIRST slash so multi-slash model names stay intact
- onSelect still emits the full `<key>/<model>` string (ChatRequest.model unchanged)
Diffstat (limited to 'src/features/chat/model-select.ts')
| -rw-r--r-- | src/features/chat/model-select.ts | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/src/features/chat/model-select.ts b/src/features/chat/model-select.ts new file mode 100644 index 0000000..b1d70b9 --- /dev/null +++ b/src/features/chat/model-select.ts @@ -0,0 +1,49 @@ +/** + * Pure helpers for the two-step model picker. + * + * Models arrive from `GET /models` as `<key>/<model>` strings, where `key` is + * the credential name (the part before the FIRST slash) and `model` is the rest. + * These pure functions split that into a key selector + a model selector and + * recombine the choice — zero DOM, zero Svelte. + */ + +export interface SplitModel { + readonly key: string; + readonly model: string; +} + +/** Split `<key>/<model>` on the first slash. A slashless name is all-key. */ +export function splitModelName(full: string): SplitModel { + const i = full.indexOf("/"); + if (i === -1) return { key: full, model: "" }; + return { key: full.slice(0, i), model: full.slice(i + 1) }; +} + +/** Recombine a key + model into a `<key>/<model>` name (key-only if no model). */ +export function joinModelName(key: string, model: string): string { + return model === "" ? key : `${key}/${model}`; +} + +/** Distinct keys across all models, in first-seen order. */ +export function modelKeys(models: readonly string[]): string[] { + const seen = new Set<string>(); + const out: string[] = []; + for (const full of models) { + const { key } = splitModelName(full); + if (!seen.has(key)) { + seen.add(key); + out.push(key); + } + } + return out; +} + +/** The model suffixes available under a given key, in order. */ +export function modelsForKey(models: readonly string[], key: string): string[] { + const out: string[] = []; + for (const full of models) { + const split = splitModelName(full); + if (split.key === key) out.push(split.model); + } + return out; +} |
