summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/model-select.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-10 16:48:30 +0900
committerAdam Malczewski <[email protected]>2026-06-10 16:48:30 +0900
commitb3f7ba523f644224364d155b575fa3f9f13c5eb9 (patch)
tree1d131f624fe2e78c3a8ee050d4888b5ddec3f2cc /src/features/chat/model-select.ts
parent871957b930203c019e631c4606cfdf8266d222fa (diff)
downloaddispatch-web-b3f7ba523f644224364d155b575fa3f9f13c5eb9.tar.gz
dispatch-web-b3f7ba523f644224364d155b575fa3f9f13c5eb9.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.ts49
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;
+}