anvilsign in

collin/strudel-claude

1// Model picker. The list of models is the backend's to decide — this only
2// remembers which of the offered ones the user prefers, and sends its id along
3// with each request. An unrecognised id is refused server-side, so a stale
4// choice is cleared here as soon as we learn what's actually on offer.
5import { getConfig } from './config.js';
6import { bindPanel } from './panel.js';
7
8const KEY = 'strudel-claude-model';
9
10const stored = () => {
11 try {
12 return localStorage.getItem(KEY) || '';
13 } catch {
14 return '';
15 }
16};
17
18const store = (id) => {
19 try {
20 if (id) localStorage.setItem(KEY, id);
21 else localStorage.removeItem(KEY);
22 } catch {
23 /* storage blocked — the choice just won't survive a reload */
24 }
25};
26
27// What goes in the request body. Empty until the backend has answered, which
28// means "use whatever you consider the default" — the same thing an old client
29// that doesn't know about models sends.
30let chosen = '';
31let models = [];
32
33export const getModel = () => chosen;
34
35export function initModelUI() {
36 const button = document.getElementById('model-btn');
37 const label = document.getElementById('model-state');
38 const panel = document.getElementById('model-panel');
39 const select = document.getElementById('model-select');
40 const note = document.getElementById('model-note');
41
42 const current = () => models.find((m) => m.id === chosen);
43
44 const render = () => {
45 const model = current();
46 label.textContent = model ? model.label : 'Model';
47 note.textContent = model?.blurb || '';
48 select.value = chosen;
49 // Nothing to choose between until the backend answers, or if it only
50 // offers one model.
51 button.disabled = models.length < 2;
52 };
53
54 bindPanel(button, panel, () => select.focus());
55
56 select.addEventListener('change', () => {
57 chosen = select.value;
58 store(chosen);
59 render();
60 openPanel(false);
61 });
62
63 render();
64
65 getConfig().then((cfg) => {
66 if (!Array.isArray(cfg?.models) || !cfg.models.length) return;
67 models = cfg.models;
68
69 // Honour the saved choice only if this backend still offers it; otherwise
70 // fall back to its default rather than sending an id it will refuse.
71 const saved = stored();
72 chosen = models.some((m) => m.id === saved) ? saved : cfg.defaultModel || models[0].id;
73 if (chosen !== saved) store(chosen);
74
75 select.replaceChildren(
76 ...models.map((m) => {
77 const opt = document.createElement('option');
78 opt.value = m.id;
79 opt.textContent = m.label;
80 return opt;
81 }),
82 );
83 render();
84 });
85}