@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.
- package/package.json +2 -2
- package/src/cli.ts +2 -5
- package/src/db.ts +39 -6
- package/src/generate.ts +3 -3
- package/src/handlers.ts +126 -50
- package/src/harness.ts +20 -4
- package/src/host-browse.ts +149 -4
- package/src/llm.ts +37 -15
- package/src/mcp.ts +32 -5
- package/src/mention.ts +80 -12
- package/src/oauth.ts +2 -2
- package/src/opencode-free.ts +3 -2
- package/src/public/chat.css +6 -0
- package/src/public/chat.html +260 -47
- package/src/public/i18n.js +6 -0
- package/src/public/md.js +18 -1
- package/src/public/mobile.css +224 -14
- package/src/public/mobile.html +344 -22
- package/src/public/settings.html +51 -8
- package/src/reasoning-catalog.ts +346 -0
- package/src/router.ts +172 -9
- package/src/store.ts +109 -38
- package/src/subagent.ts +10 -5
- package/src/tools.ts +8 -3
- package/src/version.ts +25 -0
- package/vendor/protocol/src/index.ts +19 -2
package/src/public/mobile.html
CHANGED
|
@@ -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=
|
|
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
|
-
|
|
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
|
-
|
|
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 || [])
|
|
213
|
-
|
|
214
|
-
bot
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
367
|
+
const clipped =
|
|
368
|
+
cleaned.length > 48000 ? cleaned.slice(0, 48000) + "\n…" : cleaned;
|
|
278
369
|
return typeof renderMarkdown === "function"
|
|
279
|
-
? renderMarkdown(
|
|
280
|
-
: escapeHtml(
|
|
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"
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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();
|
package/src/public/settings.html
CHANGED
|
@@ -64,13 +64,10 @@
|
|
|
64
64
|
</div>
|
|
65
65
|
<p class="main-now" id="main-now"></p>
|
|
66
66
|
<div class="toggles">
|
|
67
|
-
<span>
|
|
68
|
-
|
|
69
|
-
<
|
|
70
|
-
|
|
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
|
-
|
|
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: {} });
|