@kevin5251984/guild 0.2.20 → 0.2.22
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/cordis.yml +2 -0
- package/package.json +1 -1
- package/src/cron-schedule.ts +388 -0
- package/src/cron.ts +403 -0
- package/src/db.ts +135 -0
- package/src/generate.ts +9 -0
- package/src/handlers.ts +102 -5
- package/src/harness.ts +25 -5
- package/src/llm.ts +1 -1
- package/src/plugins/cron.ts +26 -0
- package/src/public/chat.css +118 -9
- package/src/public/chat.html +475 -43
- package/src/public/i18n.js +23 -0
- package/src/public/md.js +109 -15
- package/src/public/mobile.css +116 -2
- package/src/public/mobile.html +185 -15
- package/src/router.ts +161 -2
- package/src/store.ts +116 -33
- package/src/subagent.ts +3 -0
- package/src/tools.ts +57 -1
- package/vendor/protocol/src/index.ts +7 -2
package/src/public/i18n.js
CHANGED
|
@@ -31,6 +31,8 @@ var I18N_ROWS = [
|
|
|
31
31
|
["search.traj", "搜尋 system / tool / spawn / 子代理 / 結果", "Search system / tool / spawn / subagent / result"],
|
|
32
32
|
["channels", "委託", "Channels"],
|
|
33
33
|
["dms", "密談", "Whispers"],
|
|
34
|
+
["cron.navEmpty", "還沒有排程", "No schedules"],
|
|
35
|
+
["cron.navWhere", "@{handle} · {schedule}", "@{handle} · {schedule}"],
|
|
34
36
|
["newChannel", "新委託", "New channel"],
|
|
35
37
|
["newChannelName", "委託名稱", "Channel name"],
|
|
36
38
|
["create", "建立", "Create"],
|
|
@@ -90,6 +92,9 @@ var I18N_ROWS = [
|
|
|
90
92
|
["stop", "停止", "Stop"],
|
|
91
93
|
["steer.hint", "Enter 排隊 · {mod}↩ 插入這輪", "Enter to queue · {mod}↩ to steer this turn"],
|
|
92
94
|
["live.stop", "停止", "Stop"],
|
|
95
|
+
["live.pause", "暫停", "Pause"],
|
|
96
|
+
["live.continue", "繼續", "Continue"],
|
|
97
|
+
["live.paused", "已暫停", "Paused"],
|
|
93
98
|
["live.steer", "插入這輪", "Insert into turn"],
|
|
94
99
|
["steer.queue", "排隊", "Queue"],
|
|
95
100
|
["steer.insert", "插入引導", "Steer"],
|
|
@@ -153,6 +158,7 @@ var I18N_ROWS = [
|
|
|
153
158
|
["attach.rules", "規則", "Rules"],
|
|
154
159
|
["attach.term", "終端", "Terminal"],
|
|
155
160
|
["attach.tree", "工作區", "Workspace"],
|
|
161
|
+
["attach.cron", "排程", "Schedule"],
|
|
156
162
|
["attach.slash", "指令", "Commands"],
|
|
157
163
|
["insert", "插入 {name}", "Insert {name}"],
|
|
158
164
|
["remove", "移除", "Remove"],
|
|
@@ -221,6 +227,23 @@ var I18N_ROWS = [
|
|
|
221
227
|
["slash.here", "叫所有人", "Ping everyone"],
|
|
222
228
|
["slash.hereHint", "通知這個據點上的所有冒險者。通常是錯的:出活仍點名一人。", "Notify every adventurer in this hall. Usually wrong: call one @handle."],
|
|
223
229
|
["slash.library", "開工坊", "Open workshop"],
|
|
230
|
+
["slash.cron", "排程", "Schedule"],
|
|
231
|
+
["slash.cronHint", "請這席用 cronjob 排", "Ask this seat to cronjob"],
|
|
232
|
+
["cron.title", "排程", "Schedule"],
|
|
233
|
+
["cron.help", "新增會請這席呼叫 cronjob。時間用自然語言寫即可,例如「每10分鐘跟我說一次 Hi」。guildd 每 60 秒看一次;到點後這席開一輪新的 @handle,prompt 必須自包含。", "Add asks this seat to call cronjob. Write the time in natural language, e.g. \"every 10 minutes say Hi\". guildd ticks every 60s. Due jobs start a fresh @handle turn with a self-contained prompt."],
|
|
234
|
+
["cron.seat", "席", "Seat"],
|
|
235
|
+
["cron.ask", "要排什麼", "What to schedule"],
|
|
236
|
+
["cron.askPh", "每10分鐘跟我說一次 Hi", "every 10 minutes say Hi"],
|
|
237
|
+
["cron.askBody", "請用 cronjob 排程:{ask}", "Create a cronjob for this: {ask}"],
|
|
238
|
+
["cron.needAsk", "先寫要排什麼", "Write what to schedule first"],
|
|
239
|
+
["cron.needSeat", "這個據點沒有席", "No seat in this hall"],
|
|
240
|
+
["cron.add", "請他排", "Ask them"],
|
|
241
|
+
["cron.pause", "暫停", "Pause"],
|
|
242
|
+
["cron.resume", "繼續", "Resume"],
|
|
243
|
+
["cron.run", "立刻跑", "Run now"],
|
|
244
|
+
["cron.remove", "刪除", "Remove"],
|
|
245
|
+
["cron.empty", "這個據點還沒有排程。", "No schedules in this hall."],
|
|
246
|
+
["cron.next", "下次 {when}", "Next {when}"],
|
|
224
247
|
["slash.sec.skill", "技能", "Skills"],
|
|
225
248
|
["slash.sec.agent", "子代理", "Subagents"],
|
|
226
249
|
["slash.sec.cmd", "指令", "Commands"],
|
package/src/public/md.js
CHANGED
|
@@ -19,22 +19,39 @@ function isFileCode(text) {
|
|
|
19
19
|
return /[A-Za-z0-9]\/[A-Za-z0-9._@+-]/.test(s);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function localImgSrc(href) {
|
|
23
|
+
const raw = String(href || "")
|
|
24
|
+
.trim()
|
|
25
|
+
.replace(/"/g, "");
|
|
26
|
+
if (!raw) return "";
|
|
27
|
+
if (/^\/generated\/[A-Za-z0-9._-]+$/.test(raw)) return raw;
|
|
28
|
+
if (/^https?:\/\//i.test(raw)) return raw;
|
|
29
|
+
let path = raw;
|
|
30
|
+
if (/^file:\/\//i.test(path)) path = path.replace(/^file:\/\//i, "");
|
|
31
|
+
if (!path.startsWith("/") || path.startsWith("//") || path.includes("..")) {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
if (!/\.(?:png|jpe?g|gif|webp)$/i.test(path)) return "";
|
|
35
|
+
return "/local?p=" + encodeURIComponent(path);
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
function inlineMd(text) {
|
|
23
39
|
let out = escapeMd(text);
|
|
24
|
-
out = out.replace(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
out = out.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g, function (m, alt, href) {
|
|
41
|
+
const src = localImgSrc(href);
|
|
42
|
+
if (!src) return m;
|
|
43
|
+
const label = alt || "image";
|
|
44
|
+
return (
|
|
45
|
+
'<a class="md-img-link" href="' +
|
|
46
|
+
src +
|
|
47
|
+
'" target="_blank" rel="noopener noreferrer">' +
|
|
48
|
+
'<img class="md-img" src="' +
|
|
49
|
+
src +
|
|
50
|
+
'" alt="' +
|
|
51
|
+
label +
|
|
52
|
+
'" loading="lazy"></a>'
|
|
53
|
+
);
|
|
54
|
+
});
|
|
38
55
|
out = out.replace(/\[([^\]]+)\]\((https?:[^)\s]+)\)/g, function (_m, label, href) {
|
|
39
56
|
return (
|
|
40
57
|
'<a href="' +
|
|
@@ -327,6 +344,83 @@ function renderMarkdown(raw) {
|
|
|
327
344
|
return html.join("");
|
|
328
345
|
}
|
|
329
346
|
|
|
347
|
+
function htmlPreviewSrcdoc(raw, lang) {
|
|
348
|
+
const text = String(raw || "");
|
|
349
|
+
if (/svg/i.test(String(lang || ""))) {
|
|
350
|
+
return (
|
|
351
|
+
'<!doctype html><html><body style="margin:0;background:#fff">' +
|
|
352
|
+
text +
|
|
353
|
+
"</body></html>"
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
if (/<!doctype html|<html[\s>]/i.test(text)) return text;
|
|
357
|
+
return (
|
|
358
|
+
'<!doctype html><html><body style="margin:0;background:transparent">' +
|
|
359
|
+
text +
|
|
360
|
+
"</body></html>"
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function hydrateHtmlPreviews(root) {
|
|
365
|
+
if (!root || typeof root.querySelectorAll !== "function") return;
|
|
366
|
+
root.querySelectorAll(".md-html-preview").forEach((box) => {
|
|
367
|
+
const src = box.querySelector(".md-html-src");
|
|
368
|
+
const frame = box.querySelector(".md-html-frame");
|
|
369
|
+
if (!src || !frame) return;
|
|
370
|
+
const raw = src.value;
|
|
371
|
+
const lang = (box.querySelector(".md-fence-lang") || {}).textContent || "";
|
|
372
|
+
const doc = htmlPreviewSrcdoc(raw, lang);
|
|
373
|
+
if (frame.dataset.ready && frame.srcdoc === doc) return;
|
|
374
|
+
frame.dataset.ready = "1";
|
|
375
|
+
frame.srcdoc = doc;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function holdHtmlFrames(root) {
|
|
380
|
+
const frames = [];
|
|
381
|
+
if (!root || typeof root.querySelectorAll !== "function") return frames;
|
|
382
|
+
root.querySelectorAll("article.msg .md-html-preview").forEach((box) => {
|
|
383
|
+
const article = box.closest("article.msg[data-id]");
|
|
384
|
+
const frame = box.querySelector(".md-html-frame");
|
|
385
|
+
const src = box.querySelector(".md-html-src");
|
|
386
|
+
if (!article || !frame || !src || !frame.dataset.ready) return;
|
|
387
|
+
frames.push({
|
|
388
|
+
id: article.getAttribute("data-id"),
|
|
389
|
+
src: src.value,
|
|
390
|
+
view: box.getAttribute("data-view") || "preview",
|
|
391
|
+
frame: frame,
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
return frames;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function putHtmlFrames(root, frames) {
|
|
398
|
+
if (!root || typeof root.querySelector !== "function") return;
|
|
399
|
+
(frames || []).forEach((item) => {
|
|
400
|
+
const article = root.querySelector(
|
|
401
|
+
'article.msg[data-id="' + CSS.escape(item.id) + '"]',
|
|
402
|
+
);
|
|
403
|
+
const box = article && article.querySelector(".md-html-preview");
|
|
404
|
+
const src = box && box.querySelector(".md-html-src");
|
|
405
|
+
const fresh = box && box.querySelector(".md-html-frame");
|
|
406
|
+
if (!box || !src || !fresh || src.value !== item.src) return;
|
|
407
|
+
item.frame.dataset.ready = "1";
|
|
408
|
+
fresh.replaceWith(item.frame);
|
|
409
|
+
box.setAttribute("data-view", item.view);
|
|
410
|
+
box.querySelectorAll("[data-html-view]").forEach((tab) => {
|
|
411
|
+
tab.classList.toggle("on", tab.getAttribute("data-html-view") === item.view);
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
330
416
|
if (typeof module !== "undefined" && module.exports) {
|
|
331
|
-
module.exports = {
|
|
417
|
+
module.exports = {
|
|
418
|
+
renderMarkdown,
|
|
419
|
+
inlineMd,
|
|
420
|
+
hydrateHtmlPreviews,
|
|
421
|
+
htmlPreviewSrcdoc,
|
|
422
|
+
holdHtmlFrames,
|
|
423
|
+
putHtmlFrames,
|
|
424
|
+
localImgSrc,
|
|
425
|
+
};
|
|
332
426
|
}
|
package/src/public/mobile.css
CHANGED
|
@@ -57,6 +57,8 @@ textarea:focus-visible {
|
|
|
57
57
|
.navlist a,
|
|
58
58
|
.back,
|
|
59
59
|
.turn-stop,
|
|
60
|
+
.turn-pause,
|
|
61
|
+
.turn-continue,
|
|
60
62
|
.turn-steer,
|
|
61
63
|
.steer,
|
|
62
64
|
.send,
|
|
@@ -72,6 +74,8 @@ textarea:focus-visible {
|
|
|
72
74
|
.navlist a:active,
|
|
73
75
|
.back:active,
|
|
74
76
|
.turn-stop:active:not(:disabled),
|
|
77
|
+
.turn-pause:active:not(:disabled),
|
|
78
|
+
.turn-continue:active:not(:disabled),
|
|
75
79
|
.turn-steer:active:not(:disabled),
|
|
76
80
|
.steer:active:not(:disabled),
|
|
77
81
|
.send:active:not(:disabled),
|
|
@@ -83,6 +87,8 @@ textarea:focus-visible {
|
|
|
83
87
|
.navlist a,
|
|
84
88
|
.back,
|
|
85
89
|
.turn-stop,
|
|
90
|
+
.turn-pause,
|
|
91
|
+
.turn-continue,
|
|
86
92
|
.turn-steer,
|
|
87
93
|
.steer,
|
|
88
94
|
.send,
|
|
@@ -97,6 +103,8 @@ textarea:focus-visible {
|
|
|
97
103
|
.navlist a:active,
|
|
98
104
|
.back:active,
|
|
99
105
|
.turn-stop:active,
|
|
106
|
+
.turn-pause:active,
|
|
107
|
+
.turn-continue:active,
|
|
100
108
|
.turn-steer:active,
|
|
101
109
|
.steer:active,
|
|
102
110
|
.send:active,
|
|
@@ -326,6 +334,7 @@ textarea:focus-visible {
|
|
|
326
334
|
background: var(--fill);
|
|
327
335
|
overflow: auto;
|
|
328
336
|
}
|
|
337
|
+
.assistant-text .md-html-preview { overflow: hidden; }
|
|
329
338
|
.assistant-text .md-fence-bar {
|
|
330
339
|
display: flex;
|
|
331
340
|
align-items: center;
|
|
@@ -336,14 +345,114 @@ textarea:focus-visible {
|
|
|
336
345
|
color: var(--muted);
|
|
337
346
|
font-size: 0.75rem;
|
|
338
347
|
}
|
|
348
|
+
.assistant-text .md-fence-tabs {
|
|
349
|
+
display: inline-flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
gap: 2px;
|
|
352
|
+
margin-left: auto;
|
|
353
|
+
margin-right: 4px;
|
|
354
|
+
}
|
|
355
|
+
.assistant-text .md-fence-tab {
|
|
356
|
+
margin: 0;
|
|
357
|
+
min-height: var(--tap);
|
|
358
|
+
padding: 0 10px;
|
|
359
|
+
border: 0;
|
|
360
|
+
border-radius: 8px;
|
|
361
|
+
background: transparent;
|
|
362
|
+
color: var(--muted);
|
|
363
|
+
font: inherit;
|
|
364
|
+
font-size: 0.78rem;
|
|
365
|
+
cursor: pointer;
|
|
366
|
+
}
|
|
367
|
+
.assistant-text .md-fence-tab.on,
|
|
368
|
+
.assistant-text .md-fence-tab:hover { background: var(--lift); color: var(--text); }
|
|
369
|
+
.assistant-text .md-fence-acts {
|
|
370
|
+
display: flex;
|
|
371
|
+
align-items: center;
|
|
372
|
+
gap: 2px;
|
|
373
|
+
}
|
|
374
|
+
.assistant-text .md-fence-btn {
|
|
375
|
+
margin: 0;
|
|
376
|
+
width: var(--tap);
|
|
377
|
+
height: var(--tap);
|
|
378
|
+
padding: 0;
|
|
379
|
+
border: 0;
|
|
380
|
+
border-radius: 8px;
|
|
381
|
+
background: transparent;
|
|
382
|
+
color: var(--muted);
|
|
383
|
+
cursor: pointer;
|
|
384
|
+
display: inline-flex;
|
|
385
|
+
align-items: center;
|
|
386
|
+
justify-content: center;
|
|
387
|
+
}
|
|
388
|
+
.assistant-text .md-html-frame {
|
|
389
|
+
display: block;
|
|
390
|
+
width: 100%;
|
|
391
|
+
min-height: 220px;
|
|
392
|
+
height: 52vh;
|
|
393
|
+
border: 0;
|
|
394
|
+
background: transparent;
|
|
395
|
+
color-scheme: dark;
|
|
396
|
+
border-radius: 0 0 12px 12px;
|
|
397
|
+
}
|
|
398
|
+
.assistant-text .md-html-preview[data-view="code"] .md-html-frame { display: none; }
|
|
399
|
+
.assistant-text .md-html-preview[data-view="preview"] .md-pre { display: none; }
|
|
339
400
|
.assistant-text .md-pre {
|
|
340
401
|
margin: 0;
|
|
341
402
|
padding: 0.6rem 0.8rem 0.8rem;
|
|
342
403
|
overflow: auto;
|
|
343
404
|
font-size: 0.82rem;
|
|
405
|
+
white-space: pre;
|
|
406
|
+
overflow-wrap: normal;
|
|
407
|
+
word-break: normal;
|
|
344
408
|
}
|
|
345
|
-
.
|
|
409
|
+
.html-zoom {
|
|
410
|
+
position: fixed;
|
|
411
|
+
inset: 0;
|
|
412
|
+
z-index: 80;
|
|
413
|
+
display: grid;
|
|
414
|
+
grid-template-rows: auto 1fr;
|
|
415
|
+
padding: max(12px, env(safe-area-inset-top)) 12px max(12px, env(safe-area-inset-bottom));
|
|
416
|
+
background: rgba(0, 0, 0, 0.72);
|
|
417
|
+
}
|
|
418
|
+
.html-zoom[hidden] { display: none !important; }
|
|
419
|
+
.html-zoom-bar {
|
|
420
|
+
display: flex;
|
|
421
|
+
align-items: center;
|
|
422
|
+
justify-content: space-between;
|
|
423
|
+
gap: 12px;
|
|
424
|
+
margin: 0 0 12px;
|
|
425
|
+
color: var(--text);
|
|
426
|
+
}
|
|
427
|
+
.html-zoom-close {
|
|
428
|
+
margin: 0;
|
|
429
|
+
width: var(--tap);
|
|
430
|
+
height: var(--tap);
|
|
431
|
+
border: 0;
|
|
432
|
+
border-radius: 50%;
|
|
433
|
+
background: var(--fill);
|
|
434
|
+
color: var(--text);
|
|
435
|
+
font-size: 1.35rem;
|
|
436
|
+
cursor: pointer;
|
|
437
|
+
}
|
|
438
|
+
.html-zoom-frame {
|
|
439
|
+
width: 100%;
|
|
440
|
+
height: 100%;
|
|
441
|
+
border: 0;
|
|
442
|
+
border-radius: 16px;
|
|
443
|
+
background: #fff;
|
|
444
|
+
}
|
|
445
|
+
.assistant-text .md-img-link {
|
|
446
|
+
display: inline-block;
|
|
346
447
|
max-width: 100%;
|
|
448
|
+
line-height: 0;
|
|
449
|
+
}
|
|
450
|
+
.assistant-text img.md-img {
|
|
451
|
+
max-width: min(100%, 22rem);
|
|
452
|
+
max-height: min(16rem, 40vh);
|
|
453
|
+
width: auto;
|
|
454
|
+
height: auto;
|
|
455
|
+
object-fit: contain;
|
|
347
456
|
border-radius: 10px;
|
|
348
457
|
}
|
|
349
458
|
.turn-live {
|
|
@@ -416,6 +525,8 @@ textarea:focus-visible {
|
|
|
416
525
|
margin-top: 4px;
|
|
417
526
|
}
|
|
418
527
|
.turn-stop,
|
|
528
|
+
.turn-pause,
|
|
529
|
+
.turn-continue,
|
|
419
530
|
.turn-steer,
|
|
420
531
|
.steer,
|
|
421
532
|
.send,
|
|
@@ -447,13 +558,16 @@ textarea:focus-visible {
|
|
|
447
558
|
background: var(--lift);
|
|
448
559
|
border-color: color-mix(in srgb, var(--danger) 45%, var(--line));
|
|
449
560
|
}
|
|
450
|
-
|
|
561
|
+
.turn-pause,
|
|
562
|
+
.turn-continue,
|
|
451
563
|
.turn-steer,
|
|
452
564
|
.steer {
|
|
453
565
|
color: var(--steel);
|
|
454
566
|
background: var(--fill);
|
|
455
567
|
border-color: color-mix(in srgb, var(--steel) 38%, var(--line));
|
|
456
568
|
}
|
|
569
|
+
.turn-pause:hover,
|
|
570
|
+
.turn-continue:hover,
|
|
457
571
|
.turn-steer:hover,
|
|
458
572
|
.steer:hover {
|
|
459
573
|
background: var(--lift);
|
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-frame" />
|
|
13
13
|
<script src="/i18n.js"></script>
|
|
14
14
|
<script src="/md.js"></script>
|
|
15
15
|
</head>
|
|
@@ -49,6 +49,13 @@
|
|
|
49
49
|
</form>
|
|
50
50
|
</section>
|
|
51
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>
|
|
52
59
|
<script>
|
|
53
60
|
(function () {
|
|
54
61
|
const COLORS = ["#7c6af7", "#5b8def", "#2ea887", "#e07a3d", "#d4537e"];
|
|
@@ -141,6 +148,20 @@
|
|
|
141
148
|
? "/dms/" + encodeURIComponent(r.id) + "/abort"
|
|
142
149
|
: "/channels/" + encodeURIComponent(r.id) + "/abort";
|
|
143
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
|
+
}
|
|
144
165
|
function steerUrl() {
|
|
145
166
|
const r = currentRoomRef();
|
|
146
167
|
if (!r) return "";
|
|
@@ -225,6 +246,7 @@
|
|
|
225
246
|
thinking: row.thinking || "",
|
|
226
247
|
steps: Array.isArray(row.steps) ? row.steps.slice(-5) : [],
|
|
227
248
|
startedAt: row.startedAt || "",
|
|
249
|
+
paused: Boolean(row.paused),
|
|
228
250
|
}));
|
|
229
251
|
}
|
|
230
252
|
function isBusy() {
|
|
@@ -342,10 +364,11 @@
|
|
|
342
364
|
}
|
|
343
365
|
function formatBody(text) {
|
|
344
366
|
const cleaned = visibleAssistantText(text);
|
|
345
|
-
|
|
367
|
+
const clipped =
|
|
368
|
+
cleaned.length > 48000 ? cleaned.slice(0, 48000) + "\n…" : cleaned;
|
|
346
369
|
return typeof renderMarkdown === "function"
|
|
347
|
-
? renderMarkdown(
|
|
348
|
-
: escapeHtml(
|
|
370
|
+
? renderMarkdown(clipped)
|
|
371
|
+
: escapeHtml(clipped);
|
|
349
372
|
}
|
|
350
373
|
function liveStepLabel(name) {
|
|
351
374
|
if (name === "think") return t("think");
|
|
@@ -409,19 +432,35 @@
|
|
|
409
432
|
'</span><span class="handle">@' +
|
|
410
433
|
escapeHtml(bot.handle) +
|
|
411
434
|
'</span></div><div class="turn-live"><div class="turn-status">' +
|
|
412
|
-
t("deepDiving") +
|
|
435
|
+
(live.paused ? t("live.paused") : t("deepDiving")) +
|
|
413
436
|
clock +
|
|
414
437
|
"</div>" +
|
|
415
438
|
liveStepsHtml(live.steps) +
|
|
416
|
-
'<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="' +
|
|
417
452
|
escapeHtml(bot.id) +
|
|
418
453
|
'">' +
|
|
419
454
|
t("live.stop") +
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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>"
|
|
425
464
|
);
|
|
426
465
|
})
|
|
427
466
|
.join("");
|
|
@@ -445,6 +484,8 @@
|
|
|
445
484
|
return (
|
|
446
485
|
'<article class="msg' +
|
|
447
486
|
(you ? " you" : " bot") +
|
|
487
|
+
'" data-id="' +
|
|
488
|
+
escapeHtml(msg.id || "") +
|
|
448
489
|
'"><span class="avatar" style="' +
|
|
449
490
|
avStyle +
|
|
450
491
|
'">' +
|
|
@@ -481,9 +522,28 @@
|
|
|
481
522
|
escapeHtml(t("mobile.recent", { n: cap })) +
|
|
482
523
|
"</div>"
|
|
483
524
|
: "";
|
|
525
|
+
const keptHtml =
|
|
526
|
+
typeof holdHtmlFrames === "function" ? holdHtmlFrames(root) : [];
|
|
484
527
|
root.innerHTML = notice + rows.map(msgHtml).join("") + turnStatusHtml();
|
|
528
|
+
if (typeof putHtmlFrames === "function") putHtmlFrames(root, keptHtml);
|
|
529
|
+
if (typeof hydrateHtmlPreviews === "function") {
|
|
530
|
+
hydrateHtmlPreviews(root);
|
|
531
|
+
}
|
|
485
532
|
if (pin) root.scrollTop = root.scrollHeight;
|
|
486
533
|
}
|
|
534
|
+
function openHtmlZoom(raw) {
|
|
535
|
+
const wrap = document.getElementById("html-zoom");
|
|
536
|
+
const frame = document.getElementById("html-zoom-frame");
|
|
537
|
+
if (!wrap || !frame) return;
|
|
538
|
+
frame.srcdoc = raw || "";
|
|
539
|
+
wrap.hidden = false;
|
|
540
|
+
}
|
|
541
|
+
function closeHtmlZoom() {
|
|
542
|
+
const wrap = document.getElementById("html-zoom");
|
|
543
|
+
const frame = document.getElementById("html-zoom-frame");
|
|
544
|
+
if (frame) frame.srcdoc = "";
|
|
545
|
+
if (wrap) wrap.hidden = true;
|
|
546
|
+
}
|
|
487
547
|
function steerModLabel() {
|
|
488
548
|
return /Mac|iPhone|iPad/.test(navigator.platform || "") ? "\u2318" : "Ctrl+";
|
|
489
549
|
}
|
|
@@ -607,12 +667,25 @@
|
|
|
607
667
|
renderList();
|
|
608
668
|
if (state.kind) renderHead();
|
|
609
669
|
}
|
|
670
|
+
function sameMessages(next) {
|
|
671
|
+
const prev = state.messages || [];
|
|
672
|
+
if (prev.length !== next.length) return false;
|
|
673
|
+
return next.every(
|
|
674
|
+
(msg, i) =>
|
|
675
|
+
prev[i].id === msg.id &&
|
|
676
|
+
prev[i].body === msg.body &&
|
|
677
|
+
Boolean(prev[i].finishedAt) === Boolean(msg.finishedAt),
|
|
678
|
+
);
|
|
679
|
+
}
|
|
610
680
|
async function loadMessages(opts) {
|
|
611
681
|
if (!state.kind) return;
|
|
612
682
|
const res = await fetch(roomMessagesUrl());
|
|
613
683
|
const body = await res.json().catch(() => []);
|
|
614
684
|
if (!res.ok) throw new Error(body.error || "load failed");
|
|
615
|
-
|
|
685
|
+
const incoming = Array.isArray(body) ? body : [];
|
|
686
|
+
const unchanged = sameMessages(incoming);
|
|
687
|
+
state.messages = incoming;
|
|
688
|
+
if (opts && opts.merge && unchanged) return;
|
|
616
689
|
renderThread({ pinBottom: !(opts && opts.merge) });
|
|
617
690
|
}
|
|
618
691
|
async function pollLive() {
|
|
@@ -629,10 +702,10 @@
|
|
|
629
702
|
state.lives = lives;
|
|
630
703
|
if (!lives.length && !state.posting) {
|
|
631
704
|
if (was) await loadMessages({ merge: true }).catch(() => {});
|
|
632
|
-
|
|
705
|
+
} else if (now !== was) {
|
|
706
|
+
await loadMessages({ merge: true }).catch(() => {});
|
|
633
707
|
} else {
|
|
634
|
-
|
|
635
|
-
else renderThread();
|
|
708
|
+
renderThread();
|
|
636
709
|
}
|
|
637
710
|
syncComposer();
|
|
638
711
|
} catch {
|
|
@@ -771,6 +844,48 @@
|
|
|
771
844
|
renderThread();
|
|
772
845
|
await loadMessages().catch(() => {});
|
|
773
846
|
}
|
|
847
|
+
async function pauseTurn(botId) {
|
|
848
|
+
try {
|
|
849
|
+
await fetch(pauseUrl(), {
|
|
850
|
+
method: "POST",
|
|
851
|
+
headers: { "content-type": "application/json" },
|
|
852
|
+
body: JSON.stringify(botId ? { botId: botId } : {}),
|
|
853
|
+
});
|
|
854
|
+
} catch {
|
|
855
|
+
/* ignore */
|
|
856
|
+
}
|
|
857
|
+
(state.lives || []).forEach((row) => {
|
|
858
|
+
if (!botId || row.botId === botId) row.paused = true;
|
|
859
|
+
});
|
|
860
|
+
renderThread();
|
|
861
|
+
await pollLive().catch(() => {});
|
|
862
|
+
}
|
|
863
|
+
async function continueTurn(botId) {
|
|
864
|
+
(state.lives || []).forEach((row) => {
|
|
865
|
+
if (row.botId === botId) row.paused = false;
|
|
866
|
+
});
|
|
867
|
+
state.posting = true;
|
|
868
|
+
syncComposer();
|
|
869
|
+
renderThread();
|
|
870
|
+
try {
|
|
871
|
+
const res = await fetch(continueUrl(), {
|
|
872
|
+
method: "POST",
|
|
873
|
+
headers: { "content-type": "application/json" },
|
|
874
|
+
body: JSON.stringify({ botId: botId }),
|
|
875
|
+
});
|
|
876
|
+
const body = await res.json().catch(() => ({}));
|
|
877
|
+
if (!res.ok) throw new Error(body.error || "continue failed");
|
|
878
|
+
await loadMessages();
|
|
879
|
+
await pollLive();
|
|
880
|
+
} catch (err) {
|
|
881
|
+
toast(err.message || String(err));
|
|
882
|
+
await pollLive().catch(() => {});
|
|
883
|
+
} finally {
|
|
884
|
+
state.posting = false;
|
|
885
|
+
syncComposer();
|
|
886
|
+
schedule();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
774
889
|
async function sendSteer(raw, botId) {
|
|
775
890
|
const text = String(raw || "").trim();
|
|
776
891
|
if (!text || !state.kind) return;
|
|
@@ -886,13 +1001,68 @@
|
|
|
886
1001
|
document.getElementById("steer").addEventListener("click", () => {
|
|
887
1002
|
steerDraft().catch((err) => toast(err.message || String(err)));
|
|
888
1003
|
});
|
|
1004
|
+
document.getElementById("html-zoom-close").addEventListener("click", () => {
|
|
1005
|
+
closeHtmlZoom();
|
|
1006
|
+
});
|
|
1007
|
+
document.getElementById("html-zoom").addEventListener("click", (event) => {
|
|
1008
|
+
if (event.target.id === "html-zoom") closeHtmlZoom();
|
|
1009
|
+
});
|
|
889
1010
|
document.getElementById("thread").addEventListener("click", (event) => {
|
|
1011
|
+
const htmlExpand = event.target.closest("[data-html-expand]");
|
|
1012
|
+
if (htmlExpand) {
|
|
1013
|
+
event.preventDefault();
|
|
1014
|
+
const box = htmlExpand.closest(".md-html-preview");
|
|
1015
|
+
const src = box && box.querySelector(".md-html-src");
|
|
1016
|
+
if (src) openHtmlZoom(src.value);
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
const htmlView = event.target.closest("[data-html-view]");
|
|
1020
|
+
if (htmlView) {
|
|
1021
|
+
event.preventDefault();
|
|
1022
|
+
const box = htmlView.closest(".md-html-preview");
|
|
1023
|
+
if (!box) return;
|
|
1024
|
+
const view = htmlView.getAttribute("data-html-view");
|
|
1025
|
+
box.setAttribute("data-view", view);
|
|
1026
|
+
const frame = box.querySelector(".md-html-frame");
|
|
1027
|
+
const pre = box.querySelector(".md-pre");
|
|
1028
|
+
if (frame) frame.hidden = view !== "preview";
|
|
1029
|
+
if (pre) pre.hidden = view !== "code";
|
|
1030
|
+
box.querySelectorAll("[data-html-view]").forEach((tab) => {
|
|
1031
|
+
tab.classList.toggle("on", tab.getAttribute("data-html-view") === view);
|
|
1032
|
+
});
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const fenceCopy = event.target.closest("[data-fence-copy]");
|
|
1036
|
+
if (fenceCopy) {
|
|
1037
|
+
event.preventDefault();
|
|
1038
|
+
const box = fenceCopy.closest(".md-fence");
|
|
1039
|
+
const src = box && (box.querySelector(".md-html-src") || box.querySelector("code"));
|
|
1040
|
+
const text = src && "value" in src ? src.value : src ? src.textContent : "";
|
|
1041
|
+
if (text && navigator.clipboard) {
|
|
1042
|
+
navigator.clipboard.writeText(text).catch(() => {});
|
|
1043
|
+
}
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
890
1046
|
const stop = event.target.closest("[data-live-stop]");
|
|
891
1047
|
if (stop) {
|
|
892
1048
|
event.preventDefault();
|
|
893
1049
|
stopTurn(stop.getAttribute("data-live-stop"));
|
|
894
1050
|
return;
|
|
895
1051
|
}
|
|
1052
|
+
const pause = event.target.closest("[data-live-pause]");
|
|
1053
|
+
if (pause) {
|
|
1054
|
+
event.preventDefault();
|
|
1055
|
+
pauseTurn(pause.getAttribute("data-live-pause"));
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
const cont = event.target.closest("[data-live-continue]");
|
|
1059
|
+
if (cont) {
|
|
1060
|
+
event.preventDefault();
|
|
1061
|
+
continueTurn(cont.getAttribute("data-live-continue")).catch((err) =>
|
|
1062
|
+
toast(err.message || String(err)),
|
|
1063
|
+
);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
896
1066
|
const steer = event.target.closest("[data-live-steer]");
|
|
897
1067
|
if (!steer) return;
|
|
898
1068
|
event.preventDefault();
|