@kevin5251984/guild 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  <link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32" />
10
10
  <link rel="icon" href="/favicon-16.png" type="image/png" sizes="16x16" />
11
11
  <title>Guild — 外出</title>
12
- <link rel="stylesheet" href="/mobile.css?v=away" />
12
+ <link rel="stylesheet" href="/mobile.css?v=html-preview" />
13
13
  <script src="/i18n.js"></script>
14
14
  <script src="/md.js"></script>
15
15
  </head>
@@ -39,17 +39,33 @@
39
39
  <div id="mention-results"></div>
40
40
  </div>
41
41
  <textarea id="draft" rows="1" placeholder="Ask anything" data-i18n-placeholder="askAnything"></textarea>
42
+ <div class="composer-steer" id="composer-steer" hidden></div>
42
43
  <div class="composer-actions">
43
44
  <button type="submit" class="send" id="send" data-i18n="send">送出</button>
44
45
  <button type="button" class="stop-all" id="stop" hidden data-i18n="live.stop">停止</button>
46
+ <button type="button" class="steer" id="steer" hidden data-i18n="steer.insert">插入引導</button>
45
47
  </div>
46
48
  </div>
47
49
  </form>
48
50
  </section>
49
- <div class="flash" id="flash" hidden></div>
51
+ <div class="flash" id="flash" role="status" aria-live="polite" hidden></div>
52
+ <div class="html-zoom" id="html-zoom" hidden>
53
+ <div class="html-zoom-bar">
54
+ <strong data-i18n="html.zoom">HTML 預覽</strong>
55
+ <button type="button" class="html-zoom-close" id="html-zoom-close" title="關閉" aria-label="關閉" data-i18n-title="close" data-i18n-aria="close">×</button>
56
+ </div>
57
+ <iframe class="html-zoom-frame" id="html-zoom-frame" sandbox="allow-scripts" title="HTML preview large"></iframe>
58
+ </div>
50
59
  <script>
51
60
  (function () {
52
61
  const COLORS = ["#7c6af7", "#5b8def", "#2ea887", "#e07a3d", "#d4537e"];
62
+ const SEAT_HUE = {
63
+ infra: "#5b8def",
64
+ pm: "#2ea887",
65
+ rd: "#e07a3d",
66
+ design: "#7c6af7",
67
+ marketing: "#d4537e",
68
+ };
53
69
  const state = {
54
70
  channels: [],
55
71
  bots: [],
@@ -64,6 +80,8 @@
64
80
  };
65
81
 
66
82
  function hue(key) {
83
+ const seat = SEAT_HUE[String(key).toLowerCase()];
84
+ if (seat) return seat;
67
85
  let n = 0;
68
86
  for (const ch of String(key)) n = (n + ch.charCodeAt(0) * 17) % COLORS.length;
69
87
  return COLORS[n];
@@ -80,9 +98,18 @@
80
98
  .trim()
81
99
  .split(/\s+/);
82
100
  if (parts[0] && /[A-Za-z]/.test(parts[0][0])) {
83
- return parts[0][0].toUpperCase();
101
+ const letters = parts.slice(0, 2).map((part) => part[0]).filter(Boolean);
102
+ if (
103
+ letters.length === 2 &&
104
+ /[A-Za-z]/.test(letters[0]) &&
105
+ /[A-Za-z]/.test(letters[1])
106
+ ) {
107
+ return letters.join("").toUpperCase();
108
+ }
109
+ return letters[0].toUpperCase();
84
110
  }
85
- return Array.from(String(name).replace(/\s+/g, ""))[0] || "?";
111
+ const glyph = Array.from(String(name).replace(/\s+/g, ""))[0] || "?";
112
+ return glyph;
86
113
  }
87
114
  function portraitUrl(bot) {
88
115
  const url = bot && typeof bot.portrait === "string" ? bot.portrait.trim() : "";
@@ -121,6 +148,27 @@
121
148
  ? "/dms/" + encodeURIComponent(r.id) + "/abort"
122
149
  : "/channels/" + encodeURIComponent(r.id) + "/abort";
123
150
  }
151
+ function pauseUrl() {
152
+ const r = currentRoomRef();
153
+ if (!r) return "";
154
+ return r.kind === "dm"
155
+ ? "/dms/" + encodeURIComponent(r.id) + "/pause"
156
+ : "/channels/" + encodeURIComponent(r.id) + "/pause";
157
+ }
158
+ function continueUrl() {
159
+ const r = currentRoomRef();
160
+ if (!r) return "";
161
+ return r.kind === "dm"
162
+ ? "/dms/" + encodeURIComponent(r.id) + "/continue"
163
+ : "/channels/" + encodeURIComponent(r.id) + "/continue";
164
+ }
165
+ function steerUrl() {
166
+ const r = currentRoomRef();
167
+ if (!r) return "";
168
+ return r.kind === "dm"
169
+ ? "/dms/" + encodeURIComponent(r.id) + "/steer"
170
+ : "/channels/" + encodeURIComponent(r.id) + "/steer";
171
+ }
124
172
  function parseHash() {
125
173
  const raw = location.hash.replace(/^#/, "");
126
174
  if (raw.startsWith("d/")) {
@@ -143,15 +191,17 @@
143
191
  const h = window.visualViewport ? window.visualViewport.height : window.innerHeight;
144
192
  document.documentElement.style.setProperty("--vvh", h + "px");
145
193
  }
146
- function toast(text) {
194
+ function toast(text, opts) {
147
195
  const el = document.getElementById("flash");
196
+ const ok = Boolean(opts && opts.ok);
148
197
  el.hidden = false;
149
198
  el.textContent = text;
199
+ el.classList.toggle("err", !ok);
150
200
  el.classList.add("on");
151
201
  clearTimeout(toast.timer);
152
202
  toast.timer = setTimeout(() => {
153
203
  el.classList.remove("on");
154
- }, 1800);
204
+ }, (opts && opts.ms) || 1800);
155
205
  }
156
206
  function relTime(iso) {
157
207
  if (!iso) return t("noActivity");
@@ -196,6 +246,7 @@
196
246
  thinking: row.thinking || "",
197
247
  steps: Array.isArray(row.steps) ? row.steps.slice(-5) : [],
198
248
  startedAt: row.startedAt || "",
249
+ paused: Boolean(row.paused),
199
250
  }));
200
251
  }
201
252
  function isBusy() {
@@ -206,17 +257,54 @@
206
257
  .replace(/```[\s\S]*?```/g, " ")
207
258
  .replace(/`[^`]*`/g, " ");
208
259
  }
260
+ function deferredHandles(text) {
261
+ const raw = String(text || "");
262
+ const out = [];
263
+ const add = (name) => {
264
+ const key = String(name || "").toLowerCase();
265
+ if (key && out.indexOf(key) < 0) out.push(key);
266
+ };
267
+ const scan = raw.replace(/```[\s\S]*?```/g, " ");
268
+ const cue =
269
+ /(?:完成後|通過後|最後(?:才|由)?|才需要|才叫|才交)[^@\n]{0,24}@([A-Za-z0-9_-]+)/gi;
270
+ const bare =
271
+ /(?:完成後|才需要|才叫)\s*(?:call|ask|由)\s*`?@?([A-Za-z0-9_-]+)`?/gi;
272
+ let row;
273
+ while ((row = cue.exec(scan))) add(row[1]);
274
+ while ((row = bare.exec(scan))) add(row[1]);
275
+ const later =
276
+ /^[ \t]*(?:\d+[.)、]\s*|[-*•]\s+)(?:通過後|最後|然後|之後|完成後)\s*`?@([A-Za-z0-9_-]+)`?/i;
277
+ const lines = raw.split(/\r?\n/);
278
+ for (let i = 0; i < lines.length; i++) {
279
+ const hit = lines[i].match(later);
280
+ if (hit) add(hit[1]);
281
+ }
282
+ return out;
283
+ }
209
284
  function summonedBotIds(text) {
210
285
  const raw = String(text || "");
286
+ const deferred = {};
287
+ deferredHandles(raw).forEach((handle) => {
288
+ deferred[handle] = true;
289
+ });
290
+ const members = new Set(
291
+ state.kind === "channel"
292
+ ? ((currentChannel() && currentChannel().memberIds) || [])
293
+ : state.kind === "dm"
294
+ ? [state.id]
295
+ : [],
296
+ );
211
297
  const known = new Map(
212
- (state.bots || []).map((bot) => [
213
- String(bot.handle).toLowerCase(),
214
- bot.id,
215
- ]),
298
+ (state.bots || [])
299
+ .filter((bot) => !members.size || members.has(bot.id))
300
+ .map((bot) => [
301
+ String(bot.handle).toLowerCase(),
302
+ bot.id,
303
+ ]),
216
304
  );
217
305
  const out = [];
218
- const push = (id) => {
219
- if (id && out.indexOf(id) < 0) out.push(id);
306
+ const push = (id, handle) => {
307
+ if (id && !deferred[handle] && out.indexOf(id) < 0) out.push(id);
220
308
  };
221
309
  let fence = false;
222
310
  const lines = raw.split(/\r?\n/);
@@ -233,7 +321,8 @@
233
321
  if (!m) continue;
234
322
  const names = m[1].match(/@([A-Za-z0-9_-]+)/g) || [];
235
323
  for (let n = 0; n < names.length; n++) {
236
- push(known.get(names[n].slice(1).toLowerCase()));
324
+ const handle = names[n].slice(1).toLowerCase();
325
+ push(known.get(handle), handle);
237
326
  }
238
327
  }
239
328
  if (out.length) return out;
@@ -241,7 +330,8 @@
241
330
  const re = /(?:^|\s)@([A-Za-z0-9_-]+)\b/g;
242
331
  let row;
243
332
  while ((row = re.exec(scan))) {
244
- push(known.get(String(row[1]).toLowerCase()));
333
+ const handle = String(row[1]).toLowerCase();
334
+ push(known.get(handle), handle);
245
335
  }
246
336
  return out;
247
337
  }
@@ -274,10 +364,11 @@
274
364
  }
275
365
  function formatBody(text) {
276
366
  const cleaned = visibleAssistantText(text);
277
- if (cleaned.length > 8000) return escapeHtml(cleaned.slice(-8000));
367
+ const clipped =
368
+ cleaned.length > 48000 ? cleaned.slice(0, 48000) + "\n…" : cleaned;
278
369
  return typeof renderMarkdown === "function"
279
- ? renderMarkdown(cleaned)
280
- : escapeHtml(cleaned);
370
+ ? renderMarkdown(clipped)
371
+ : escapeHtml(clipped);
281
372
  }
282
373
  function liveStepLabel(name) {
283
374
  if (name === "think") return t("think");
@@ -341,15 +432,35 @@
341
432
  '</span><span class="handle">@' +
342
433
  escapeHtml(bot.handle) +
343
434
  '</span></div><div class="turn-live"><div class="turn-status">' +
344
- t("deepDiving") +
435
+ (live.paused ? t("live.paused") : t("deepDiving")) +
345
436
  clock +
346
437
  "</div>" +
347
438
  liveStepsHtml(live.steps) +
348
- '<div class="turn-actions"><button type="button" class="turn-stop" data-live-stop="' +
439
+ '<div class="turn-actions">' +
440
+ (live.paused
441
+ ? '<button type="button" class="turn-continue" data-live-continue="' +
442
+ escapeHtml(bot.id) +
443
+ '">' +
444
+ t("live.continue") +
445
+ "</button>"
446
+ : '<button type="button" class="turn-pause" data-live-pause="' +
447
+ escapeHtml(bot.id) +
448
+ '">' +
449
+ t("live.pause") +
450
+ "</button>") +
451
+ '<button type="button" class="turn-stop" data-live-stop="' +
349
452
  escapeHtml(bot.id) +
350
453
  '">' +
351
454
  t("live.stop") +
352
- "</button></div></div></div></article>"
455
+ "</button>" +
456
+ (live.paused
457
+ ? ""
458
+ : '<button type="button" class="turn-steer" data-live-steer="' +
459
+ escapeHtml(bot.id) +
460
+ '">' +
461
+ t("live.steer") +
462
+ "</button>") +
463
+ "</div></div></div></article>"
353
464
  );
354
465
  })
355
466
  .join("");
@@ -410,12 +521,46 @@
410
521
  "</div>"
411
522
  : "";
412
523
  root.innerHTML = notice + rows.map(msgHtml).join("") + turnStatusHtml();
524
+ if (typeof hydrateHtmlPreviews === "function") {
525
+ hydrateHtmlPreviews(root);
526
+ }
413
527
  if (pin) root.scrollTop = root.scrollHeight;
414
528
  }
529
+ function openHtmlZoom(raw) {
530
+ const wrap = document.getElementById("html-zoom");
531
+ const frame = document.getElementById("html-zoom-frame");
532
+ if (!wrap || !frame) return;
533
+ frame.srcdoc = raw || "";
534
+ wrap.hidden = false;
535
+ }
536
+ function closeHtmlZoom() {
537
+ const wrap = document.getElementById("html-zoom");
538
+ const frame = document.getElementById("html-zoom-frame");
539
+ if (frame) frame.srcdoc = "";
540
+ if (wrap) wrap.hidden = true;
541
+ }
542
+ function steerModLabel() {
543
+ return /Mac|iPhone|iPad/.test(navigator.platform || "") ? "\u2318" : "Ctrl+";
544
+ }
545
+ function syncSteerHint() {
546
+ const hint = document.getElementById("composer-steer");
547
+ if (!hint) return;
548
+ if (!isBusy()) {
549
+ hint.hidden = true;
550
+ hint.textContent = "";
551
+ return;
552
+ }
553
+ const hintKey = t("steer.hint", { mod: steerModLabel() });
554
+ hint.hidden = false;
555
+ hint.textContent = hintKey === "steer.hint" ? t("live.steer") : hintKey;
556
+ }
415
557
  function syncComposer() {
416
558
  const busy = isBusy();
417
559
  document.getElementById("send").hidden = busy;
418
560
  document.getElementById("stop").hidden = !busy;
561
+ const steer = document.getElementById("steer");
562
+ if (steer) steer.hidden = !busy;
563
+ syncSteerHint();
419
564
  }
420
565
  function navRow(opts) {
421
566
  const live = opts.busy
@@ -681,6 +826,105 @@
681
826
  renderThread();
682
827
  await loadMessages().catch(() => {});
683
828
  }
829
+ async function pauseTurn(botId) {
830
+ try {
831
+ await fetch(pauseUrl(), {
832
+ method: "POST",
833
+ headers: { "content-type": "application/json" },
834
+ body: JSON.stringify(botId ? { botId: botId } : {}),
835
+ });
836
+ } catch {
837
+ /* ignore */
838
+ }
839
+ (state.lives || []).forEach((row) => {
840
+ if (!botId || row.botId === botId) row.paused = true;
841
+ });
842
+ renderThread();
843
+ await pollLive().catch(() => {});
844
+ }
845
+ async function continueTurn(botId) {
846
+ (state.lives || []).forEach((row) => {
847
+ if (row.botId === botId) row.paused = false;
848
+ });
849
+ state.posting = true;
850
+ syncComposer();
851
+ renderThread();
852
+ try {
853
+ const res = await fetch(continueUrl(), {
854
+ method: "POST",
855
+ headers: { "content-type": "application/json" },
856
+ body: JSON.stringify({ botId: botId }),
857
+ });
858
+ const body = await res.json().catch(() => ({}));
859
+ if (!res.ok) throw new Error(body.error || "continue failed");
860
+ await loadMessages();
861
+ await pollLive();
862
+ } catch (err) {
863
+ toast(err.message || String(err));
864
+ await pollLive().catch(() => {});
865
+ } finally {
866
+ state.posting = false;
867
+ syncComposer();
868
+ schedule();
869
+ }
870
+ }
871
+ async function sendSteer(raw, botId) {
872
+ const text = String(raw || "").trim();
873
+ if (!text || !state.kind) return;
874
+ const payload = { body: text };
875
+ if (botId) payload.botId = botId;
876
+ else {
877
+ const ids = botsFromSend(text);
878
+ if (ids.length === 1) payload.botId = ids[0];
879
+ }
880
+ const res = await fetch(steerUrl(), {
881
+ method: "POST",
882
+ headers: { "content-type": "application/json" },
883
+ body: JSON.stringify(payload),
884
+ });
885
+ const body = await res.json().catch(() => ({}));
886
+ if (!res.ok) throw new Error(body.error || "steer failed");
887
+ await loadMessages({ merge: true }).catch(() => {});
888
+ await pollLive();
889
+ const who = body.message && body.message.steerBotId
890
+ ? botById(body.message.steerBotId)
891
+ : null;
892
+ toast(
893
+ who ? t("steer.tagTo", { who: who.name || who.handle }) : t("steer.tag"),
894
+ { ok: true, ms: 2000 },
895
+ );
896
+ }
897
+ async function steerDraft(botId) {
898
+ const draft = document.getElementById("draft");
899
+ const raw = draft.value.trim();
900
+ if (!raw) {
901
+ draft.focus();
902
+ toast(t("live.steer"), { ok: true, ms: 1400 });
903
+ return;
904
+ }
905
+ draft.value = "";
906
+ fitDraft();
907
+ hideMention();
908
+ state.messages = state.messages.concat([
909
+ {
910
+ id: "pending-steer-" + Date.now(),
911
+ author: "you",
912
+ body: raw,
913
+ createdAt: new Date().toISOString(),
914
+ },
915
+ ]);
916
+ renderThread();
917
+ try {
918
+ await sendSteer(raw, botId);
919
+ } catch (err) {
920
+ draft.value = raw;
921
+ fitDraft();
922
+ await loadMessages().catch(() => {});
923
+ toast(err.message || String(err));
924
+ }
925
+ syncComposer();
926
+ schedule();
927
+ }
684
928
  async function sendDraft() {
685
929
  const draft = document.getElementById("draft");
686
930
  const raw = draft.value.trim();
@@ -736,11 +980,77 @@
736
980
  document.getElementById("stop").addEventListener("click", () => {
737
981
  stopTurn();
738
982
  });
983
+ document.getElementById("steer").addEventListener("click", () => {
984
+ steerDraft().catch((err) => toast(err.message || String(err)));
985
+ });
986
+ document.getElementById("html-zoom-close").addEventListener("click", () => {
987
+ closeHtmlZoom();
988
+ });
989
+ document.getElementById("html-zoom").addEventListener("click", (event) => {
990
+ if (event.target.id === "html-zoom") closeHtmlZoom();
991
+ });
739
992
  document.getElementById("thread").addEventListener("click", (event) => {
993
+ const htmlExpand = event.target.closest("[data-html-expand]");
994
+ if (htmlExpand) {
995
+ event.preventDefault();
996
+ const box = htmlExpand.closest(".md-html-preview");
997
+ const src = box && box.querySelector(".md-html-src");
998
+ if (src) openHtmlZoom(src.value);
999
+ return;
1000
+ }
1001
+ const htmlView = event.target.closest("[data-html-view]");
1002
+ if (htmlView) {
1003
+ event.preventDefault();
1004
+ const box = htmlView.closest(".md-html-preview");
1005
+ if (!box) return;
1006
+ const view = htmlView.getAttribute("data-html-view");
1007
+ box.setAttribute("data-view", view);
1008
+ const frame = box.querySelector(".md-html-frame");
1009
+ const pre = box.querySelector(".md-pre");
1010
+ if (frame) frame.hidden = view !== "preview";
1011
+ if (pre) pre.hidden = view !== "code";
1012
+ box.querySelectorAll("[data-html-view]").forEach((tab) => {
1013
+ tab.classList.toggle("on", tab.getAttribute("data-html-view") === view);
1014
+ });
1015
+ return;
1016
+ }
1017
+ const fenceCopy = event.target.closest("[data-fence-copy]");
1018
+ if (fenceCopy) {
1019
+ event.preventDefault();
1020
+ const box = fenceCopy.closest(".md-fence");
1021
+ const src = box && (box.querySelector(".md-html-src") || box.querySelector("code"));
1022
+ const text = src && "value" in src ? src.value : src ? src.textContent : "";
1023
+ if (text && navigator.clipboard) {
1024
+ navigator.clipboard.writeText(text).catch(() => {});
1025
+ }
1026
+ return;
1027
+ }
740
1028
  const stop = event.target.closest("[data-live-stop]");
741
- if (!stop) return;
1029
+ if (stop) {
1030
+ event.preventDefault();
1031
+ stopTurn(stop.getAttribute("data-live-stop"));
1032
+ return;
1033
+ }
1034
+ const pause = event.target.closest("[data-live-pause]");
1035
+ if (pause) {
1036
+ event.preventDefault();
1037
+ pauseTurn(pause.getAttribute("data-live-pause"));
1038
+ return;
1039
+ }
1040
+ const cont = event.target.closest("[data-live-continue]");
1041
+ if (cont) {
1042
+ event.preventDefault();
1043
+ continueTurn(cont.getAttribute("data-live-continue")).catch((err) =>
1044
+ toast(err.message || String(err)),
1045
+ );
1046
+ return;
1047
+ }
1048
+ const steer = event.target.closest("[data-live-steer]");
1049
+ if (!steer) return;
742
1050
  event.preventDefault();
743
- stopTurn(stop.getAttribute("data-live-stop"));
1051
+ steerDraft(steer.getAttribute("data-live-steer")).catch((err) =>
1052
+ toast(err.message || String(err)),
1053
+ );
744
1054
  });
745
1055
  document.getElementById("draft").addEventListener("input", () => {
746
1056
  fitDraft();
@@ -748,6 +1058,18 @@
748
1058
  });
749
1059
  document.getElementById("draft").addEventListener("keydown", (event) => {
750
1060
  const pop = document.getElementById("mention-pop");
1061
+ if (
1062
+ pop.hidden &&
1063
+ event.key === "Enter" &&
1064
+ !event.shiftKey &&
1065
+ (event.metaKey || event.ctrlKey)
1066
+ ) {
1067
+ if (isBusy()) {
1068
+ event.preventDefault();
1069
+ steerDraft().catch((err) => toast(err.message || String(err)));
1070
+ }
1071
+ return;
1072
+ }
751
1073
  if (pop.hidden) return;
752
1074
  if (event.key === "ArrowDown") {
753
1075
  event.preventDefault();
@@ -64,13 +64,10 @@
64
64
  </div>
65
65
  <p class="main-now" id="main-now"></p>
66
66
  <div class="toggles">
67
- <span>Reasoning</span>
68
- <select id="reasoning">
69
- <option value="minimal">Minimal</option>
70
- <option value="low">Low</option>
71
- <option value="medium">Medium</option>
72
- <option value="high">High</option>
73
- </select>
67
+ <span id="reasoning-wrap">
68
+ <span data-i18n="reasoning">推理強度</span>
69
+ <select id="reasoning"></select>
70
+ </span>
74
71
  <label><input type="checkbox" id="fast" /> Fast</label>
75
72
  </div>
76
73
  <div class="panel-head" style="margin-top:1rem">
@@ -278,6 +275,51 @@
278
275
  )
279
276
  .join("");
280
277
  if (preferred && preferred.provider === psel.value) msel.value = preferred.model;
278
+ if (psel.id === "provider") fillReasoning();
279
+ }
280
+ function reasonLabel(value) {
281
+ if (value === "none") return t("reason.none");
282
+ if (value === "minimal") return t("reason.min");
283
+ if (value === "low") return t("reason.low");
284
+ if (value === "medium") return t("reason.mid");
285
+ if (value === "high") return t("reason.high");
286
+ if (value === "xhigh") return t("reason.xhigh");
287
+ if (value === "max") return t("reason.max");
288
+ return value || "";
289
+ }
290
+ function fillReasoning() {
291
+ const sel = document.getElementById("reasoning");
292
+ const wrap = document.getElementById("reasoning-wrap");
293
+ const ref = currentRef();
294
+ const group = pickerGroups().find((p) => p.id === (ref && ref.provider));
295
+ const model = ((group && group.models) || []).find(
296
+ (m) => m.id === (ref && ref.model),
297
+ );
298
+ const spec = (model && model.reasoning) || null;
299
+ const raw = spec && spec.supportedEfforts;
300
+ const choices = raw && raw.length
301
+ ? spec.mandatory
302
+ ? raw.filter((key) => key !== "none")
303
+ : raw.slice()
304
+ : [];
305
+ sel.innerHTML = choices
306
+ .map(
307
+ (value) =>
308
+ '<option value="' +
309
+ escapeHtml(value) +
310
+ '">' +
311
+ escapeHtml(reasonLabel(value)) +
312
+ "</option>",
313
+ )
314
+ .join("");
315
+ const want = data.reasoning;
316
+ if (want && choices.indexOf(want) >= 0) sel.value = want;
317
+ else if (spec && spec.defaultEffort && choices.indexOf(spec.defaultEffort) >= 0) {
318
+ sel.value = spec.defaultEffort;
319
+ } else if (choices.length) {
320
+ sel.value = choices[0];
321
+ }
322
+ if (wrap) wrap.hidden = !choices.length;
281
323
  }
282
324
  function currentRef() {
283
325
  const provider = document.getElementById("provider").value;
@@ -359,7 +401,7 @@
359
401
  }
360
402
  function renderPicker() {
361
403
  fillSelect(document.getElementById("provider"), pickerGroups(), data.default);
362
- document.getElementById("reasoning").value = data.reasoning || "medium";
404
+ fillReasoning();
363
405
  document.getElementById("fast").checked = Boolean(data.fast);
364
406
  const mainNow = document.getElementById("main-now");
365
407
  if (mainNow) {
@@ -696,6 +738,7 @@
696
738
  data.default,
697
739
  );
698
740
  });
741
+ document.getElementById("model").addEventListener("change", fillReasoning);
699
742
  document.getElementById("apply").addEventListener("click", () => applyPicker());
700
743
  document.getElementById("reset-aux").addEventListener("click", async () => {
701
744
  await applyPicker({ aux: {} });