ds-bot 0.1.2 → 0.1.3
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/README.md +2 -2
- package/client.js +989 -344
- package/index.js +3 -1
- package/package.json +6 -4
- package/src/host/approvals.js +63 -0
- package/src/host/channel.js +12 -3
- package/src/host/constants.js +4 -1
- package/src/host/delivery.js +19 -7
- package/src/host/diagnostics.js +7 -5
- package/src/host/groups.js +195 -47
- package/src/host/home-lock.js +16 -10
- package/src/host/images.js +37 -0
- package/src/host/memory.js +27 -16
- package/src/host/parts.js +92 -21
- package/src/host/prompt.js +6 -2
- package/src/host/roster.js +109 -56
- package/src/host/store.js +113 -28
- package/src/host/time-context.js +46 -0
- package/src/host/tools.js +79 -28
- package/src/host/turns.js +176 -38
- package/src/host/update.js +5 -1
- package/src/shared/mentions.js +65 -0
- package/web-search.js +23 -7
package/client.js
CHANGED
|
@@ -3,9 +3,11 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
id: 'ds-bot',
|
|
4
4
|
factory: (require) => {
|
|
5
5
|
const module = { exports: {} }
|
|
6
|
+
var __create = Object.create;
|
|
6
7
|
var __defProp = Object.defineProperty;
|
|
7
8
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
9
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
11
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
12
|
var __export = (target, all) => {
|
|
11
13
|
for (var name in all)
|
|
@@ -19,6 +21,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
21
|
}
|
|
20
22
|
return to;
|
|
21
23
|
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
22
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
33
|
|
|
24
34
|
// src/client/index.js
|
|
@@ -57,6 +67,9 @@ var ZH = {
|
|
|
57
67
|
Close: "关闭",
|
|
58
68
|
Copy: "复制",
|
|
59
69
|
Copied: "已复制",
|
|
70
|
+
Content: "内容",
|
|
71
|
+
"Referenced {labels}": "引用了 {labels}",
|
|
72
|
+
", ": "、",
|
|
60
73
|
You: "你",
|
|
61
74
|
you: "你",
|
|
62
75
|
Bot: "Bot",
|
|
@@ -417,6 +430,7 @@ var ZH = {
|
|
|
417
430
|
"Replies from": "群聊回复:",
|
|
418
431
|
"Reply from": "收到回复:",
|
|
419
432
|
"Message from": "收到消息:",
|
|
433
|
+
"Stopped by": "叫停者:",
|
|
420
434
|
"{count} messages with": "{count} 条消息,来自",
|
|
421
435
|
"Routine started": "例行任务开始",
|
|
422
436
|
Update: "更新",
|
|
@@ -443,6 +457,8 @@ var ZH = {
|
|
|
443
457
|
"Checking the keys": "查看密钥",
|
|
444
458
|
"Asking {name}": "询问 {name}",
|
|
445
459
|
"Asking another Bot": "询问其他 Bot",
|
|
460
|
+
"Stopping {name}": "叫停 {name}",
|
|
461
|
+
"Stopping another Bot": "叫停其他 Bot",
|
|
446
462
|
"Creating {name}": "创建 {name}",
|
|
447
463
|
"Updating {name}": "更新 {name}",
|
|
448
464
|
"Checking the team": "查看团队",
|
|
@@ -451,8 +467,19 @@ var ZH = {
|
|
|
451
467
|
"{name} looked up its own chat": "{name} 查看了自己的对话",
|
|
452
468
|
"{name} looked up its own chat for “{text}”": "{name} 在自己的对话里查找「{text}」",
|
|
453
469
|
"{name} is typing…": "{name} 正在输入…",
|
|
470
|
+
"{name} didn't answer in time": "{name} 没有及时回应",
|
|
471
|
+
"{name} paused for your approval": "{name} 停下来等你批准",
|
|
472
|
+
"{name} is waiting for your approval": "{name} 等你批准",
|
|
473
|
+
"{first} and {second} are waiting for your approval": "{first}、{second} 等你批准",
|
|
474
|
+
"{first}, {second} and {count} more are waiting for your approval": "{first}、{second} 等 {total} 个等你批准",
|
|
475
|
+
"Waiting for your approval": "等你批准",
|
|
476
|
+
"in its own chat": "在自己的对话里",
|
|
477
|
+
Reject: "拒绝",
|
|
478
|
+
"Allow once": "允许一次",
|
|
454
479
|
"Couldn't message {name}": "没能给 {name} 发消息",
|
|
455
480
|
Messaged: "已发消息给",
|
|
481
|
+
"Couldn't stop {name}": "没能叫停 {name}",
|
|
482
|
+
Stopped: "已叫停",
|
|
456
483
|
Created: "已创建",
|
|
457
484
|
Updated: "已更新",
|
|
458
485
|
"Created group": "已创建群聊",
|
|
@@ -746,6 +773,8 @@ var ZH = {
|
|
|
746
773
|
"Its group conversation could not open": "它的群聊会话打不开",
|
|
747
774
|
// The composer's +
|
|
748
775
|
"Add files": "添加文件",
|
|
776
|
+
"Reference a chat": "引用对话",
|
|
777
|
+
"Chat with {name}": "与 {name} 的对话",
|
|
749
778
|
"Scheduled task": "定时任务",
|
|
750
779
|
"Send to": "发给",
|
|
751
780
|
"Scheduled task: {text}": "定时任务:{text}",
|
|
@@ -813,6 +842,7 @@ var HOST_ERRORS = [
|
|
|
813
842
|
[/^Model (.+) is not available\. Available: (.*)$/, "模型 $1 不可用。可用的:$2"],
|
|
814
843
|
[/^A Bot named (.+) already exists$/, "已经有叫 $1 的 Bot 了"],
|
|
815
844
|
[/^Name (.*) is not available$/, "名字 $1 不可用"],
|
|
845
|
+
[/^"(.+)" calls everyone in a group, so it cannot be a Bot name$/, "「$1」在群里表示所有人,不能用作 Bot 名字"],
|
|
816
846
|
[/^Another group chat is already called "(.+)"$/, "已经有叫「$1」的群聊了"],
|
|
817
847
|
[/^A group chat holds at most (\d+) Bots$/, "一个群聊最多 $1 个 Bot"],
|
|
818
848
|
[/^Reply mode must be one of (.+)$/, "回复模式只能是 $1"],
|
|
@@ -841,6 +871,7 @@ var HOST_ZH = {
|
|
|
841
871
|
"Avatar images must be PNG, JPEG, WebP, or GIF": "头像图片必须是 PNG、JPEG、WebP 或 GIF",
|
|
842
872
|
"Avatar image is too large": "头像图片太大了",
|
|
843
873
|
"A Bot needs a name": "Bot 需要一个名字",
|
|
874
|
+
"A Bot name cannot contain @, ` or a line break": "Bot 名字里不能有 @、` 或换行",
|
|
844
875
|
"Unknown Bot": "找不到这个 Bot",
|
|
845
876
|
"Unknown conversation": "找不到这个对话",
|
|
846
877
|
"A group chat needs at least two Bots": "群聊至少要两个 Bot",
|
|
@@ -959,12 +990,17 @@ function failureLabel(code, message) {
|
|
|
959
990
|
var URL_LIMIT = 7e3;
|
|
960
991
|
var WHAT_MAX = 2e3;
|
|
961
992
|
var PASTE_NOTE = "DS Bot copied the diagnostics to the clipboard. Paste them here.";
|
|
993
|
+
var REST_NOTE = "The recent errors do not fit in the link. DS Bot copied the diagnostics to the clipboard: paste them below this line.";
|
|
962
994
|
function issueUrl(base, { what = "", diagnostics }) {
|
|
963
995
|
const text = what.trim().slice(0, WHAT_MAX);
|
|
964
996
|
const title = text.split("\n")[0].slice(0, 80);
|
|
965
997
|
const build = (body) => `${base}?${new URLSearchParams({ template: "bug.yml", ...title ? { title } : {}, ...text ? { what: text } : {}, diagnostics: body })}`;
|
|
966
998
|
const full2 = build(diagnostics);
|
|
967
|
-
|
|
999
|
+
if (full2.length <= URL_LIMIT) return { url: full2, inline: true };
|
|
1000
|
+
const facts = build(`${diagnostics.split("\n\n")[0]}
|
|
1001
|
+
|
|
1002
|
+
${REST_NOTE}`);
|
|
1003
|
+
return { url: facts.length <= URL_LIMIT ? facts : build(PASTE_NOTE), inline: false };
|
|
968
1004
|
}
|
|
969
1005
|
|
|
970
1006
|
// src/client/api.js
|
|
@@ -1113,8 +1149,8 @@ function bodyOf(def, paint, flat = false) {
|
|
|
1113
1149
|
return "";
|
|
1114
1150
|
}
|
|
1115
1151
|
}
|
|
1116
|
-
var eyeMarkup = ([cx, cy, w,
|
|
1117
|
-
var happyEyeMarkup = ([cx, cy, w,
|
|
1152
|
+
var eyeMarkup = ([cx, cy, w, h32, tilt]) => `<g transform="translate(${cx} ${cy}) rotate(${tilt})"><rect class="bt-eye" x="${-w / 2}" y="${-h32 / 2}" width="${w}" height="${h32}" rx="${w / 2}"/></g>`;
|
|
1153
|
+
var happyEyeMarkup = ([cx, cy, w, h32, tilt]) => `<g transform="translate(${cx} ${cy}) rotate(${tilt})"><path class="bt-eye-happy" opacity="0" d="M${-w * 0.62} ${h32 * 0.14}Q0 ${-h32 * 0.42} ${w * 0.62} ${h32 * 0.14}" fill="none" stroke="#000" stroke-width="${w * 0.5}" stroke-linecap="round"/></g>`;
|
|
1118
1154
|
var maskBox = (size) => `maskUnits="userSpaceOnUse" x="${-size}" y="${-size}" width="${size * 3}" height="${size * 3}"`;
|
|
1119
1155
|
var holes = (def, eyes) => `<rect x="${-def.size}" y="${-def.size}" width="${def.size * 3}" height="${def.size * 3}" fill="#fff"/>${eyes}`;
|
|
1120
1156
|
var MASK_ID = "%MASK%";
|
|
@@ -1300,20 +1336,65 @@ function registerTheme(id, theme) {
|
|
|
1300
1336
|
}
|
|
1301
1337
|
var allThemes = () => ({ ...THEMES, ...customThemes });
|
|
1302
1338
|
|
|
1339
|
+
// src/shared/mentions.js
|
|
1340
|
+
var ALL_WORDS = ["all", "everyone", "所有人", "全体"];
|
|
1341
|
+
var NAME_GOES_ON = /^(?:[a-z0-9_\-/\\]|\.[a-z0-9_-])/i;
|
|
1342
|
+
var ADDRESS_BEFORE = /[a-z0-9_./]/i;
|
|
1343
|
+
var LITERAL = /```[\s\S]*?(?:```|$)|`[^`\n]*`|\[[^\]\n]*\]\([^)\s]*(?:\s+(?:"[^"\n]*"|'[^'\n]*'))?\)/g;
|
|
1344
|
+
var fitsAt = (text, at, word) => text.slice(at, at + word.length).toLowerCase() === word.toLowerCase() && !NAME_GOES_ON.test(text.slice(at + word.length));
|
|
1345
|
+
var overlaps = (spans, start, end) => spans.some((span) => start < span.end && span.start < end);
|
|
1346
|
+
function nameProblem(name) {
|
|
1347
|
+
const text = String(name ?? "").trim();
|
|
1348
|
+
if (text === "") return "A Bot needs a name";
|
|
1349
|
+
if (/[@`\r\n]/.test(text)) return "A Bot name cannot contain @, ` or a line break";
|
|
1350
|
+
if (ALL_WORDS.includes(text.toLowerCase())) return `"${text}" calls everyone in a group, so it cannot be a Bot name`;
|
|
1351
|
+
return void 0;
|
|
1352
|
+
}
|
|
1353
|
+
function referenceSpans(text, labels = []) {
|
|
1354
|
+
const spans = [];
|
|
1355
|
+
for (const label of [...labels].sort((a, b) => b.length - a.length)) {
|
|
1356
|
+
const mention = `@${label}`;
|
|
1357
|
+
for (let at = text.indexOf(mention); at !== -1; at = text.indexOf(mention, at + 1)) {
|
|
1358
|
+
const end = at + mention.length;
|
|
1359
|
+
if (at > 0 && ADDRESS_BEFORE.test(text[at - 1]) || NAME_GOES_ON.test(text.slice(end)) || overlaps(spans, at, end)) continue;
|
|
1360
|
+
spans.push({ start: at, end, label });
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
return spans.sort((a, b) => a.start - b.start);
|
|
1365
|
+
}
|
|
1366
|
+
function mentionSpans(text, bots, { references = [] } = {}) {
|
|
1367
|
+
const value = String(text ?? "");
|
|
1368
|
+
const skip = [
|
|
1369
|
+
...[...value.matchAll(LITERAL)].map((match) => ({ start: match.index, end: match.index + match[0].length })),
|
|
1370
|
+
...referenceSpans(value, references)
|
|
1371
|
+
];
|
|
1372
|
+
const byLength = bots.filter((bot) => bot?.name).sort((a, b) => b.name.length - a.name.length);
|
|
1373
|
+
const spans = [];
|
|
1374
|
+
for (let at = value.indexOf("@"); at !== -1; at = value.indexOf("@", at + 1)) {
|
|
1375
|
+
if (at > 0 && ADDRESS_BEFORE.test(value[at - 1]) || overlaps(skip, at, at + 1)) continue;
|
|
1376
|
+
const bot = byLength.find((candidate) => fitsAt(value, at + 1, candidate.name));
|
|
1377
|
+
const all = bot ? void 0 : ALL_WORDS.find((word) => fitsAt(value, at + 1, word));
|
|
1378
|
+
if (!bot && !all) continue;
|
|
1379
|
+
const end = at + 1 + (bot ? bot.name.length : all.length);
|
|
1380
|
+
spans.push(bot ? { start: at, end, bot } : { start: at, end, all: true });
|
|
1381
|
+
at = end - 1;
|
|
1382
|
+
}
|
|
1383
|
+
return spans;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1303
1386
|
// src/client/text.js
|
|
1304
1387
|
var MENTION_ORIGIN = "https://bots.local/";
|
|
1305
|
-
var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1306
1388
|
function linkMentions(text, bots, selfId) {
|
|
1307
|
-
if (!text ||
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
});
|
|
1389
|
+
if (!text || !text.includes("@")) return text;
|
|
1390
|
+
let out = "";
|
|
1391
|
+
let cursor = 0;
|
|
1392
|
+
for (const span of mentionSpans(text, bots)) {
|
|
1393
|
+
if (!span.bot || span.bot.id === selfId) continue;
|
|
1394
|
+
out += `${text.slice(cursor, span.start)}[${text.slice(span.start + 1, span.end).replace(/[[\]\\]/g, "\\$&")}](${MENTION_ORIGIN}${span.bot.id})`;
|
|
1395
|
+
cursor = span.end;
|
|
1396
|
+
}
|
|
1397
|
+
return cursor === 0 ? text : out + text.slice(cursor);
|
|
1317
1398
|
}
|
|
1318
1399
|
function mentionTarget(event) {
|
|
1319
1400
|
const anchor = event.target instanceof Element ? event.target.closest(`a[href^="${MENTION_ORIGIN}"]`) : null;
|
|
@@ -1405,6 +1486,30 @@ function toolArgs(root) {
|
|
|
1405
1486
|
value: (key) => object()?.[key]
|
|
1406
1487
|
};
|
|
1407
1488
|
}
|
|
1489
|
+
var RELAY_ROLES = /* @__PURE__ */ new Set(["request", "reply", "stopped"]);
|
|
1490
|
+
var isRelayContext = (data) => {
|
|
1491
|
+
const source = data?.source;
|
|
1492
|
+
if (source === null || typeof source !== "object" || source.kind !== "bot" || source.handoff === true || source.deferred === true) return false;
|
|
1493
|
+
return RELAY_ROLES.has(source.role) && typeof source.senderSessionId === "string";
|
|
1494
|
+
};
|
|
1495
|
+
var stepShows = (step) => (step?.blocks ?? []).some((block) => {
|
|
1496
|
+
if (block === void 0 || block.kind === "tool-call") return false;
|
|
1497
|
+
return block.kind === "text" || block.kind === "reasoning" ? String(block.text ?? "").trim() !== "" : true;
|
|
1498
|
+
});
|
|
1499
|
+
var stepHasReasoning = (step) => (step?.blocks ?? []).some((block) => block?.kind === "reasoning" && String(block.text ?? "").trim() !== "");
|
|
1500
|
+
var relayPart = (data, groupPart) => groupPart === void 0 || (stepHasReasoning(data) ? groupPart === "reasoning" : groupPart === "response");
|
|
1501
|
+
function relaysBefore(contexts, steps, step) {
|
|
1502
|
+
const index = steps.findIndex((entry) => entry?.step === step?.step);
|
|
1503
|
+
if (index === -1) return [];
|
|
1504
|
+
let prev = -Infinity;
|
|
1505
|
+
for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
|
|
1506
|
+
if (stepShows(steps[cursor])) {
|
|
1507
|
+
prev = steps[cursor].time;
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return (contexts ?? []).filter((data) => isRelayContext(data) && typeof data.time === "number" && data.time >= prev && data.time < step.time);
|
|
1512
|
+
}
|
|
1408
1513
|
function commRunOf(order, peers, turn, hiddenTurns = []) {
|
|
1409
1514
|
const turns = [];
|
|
1410
1515
|
for (const entry of order) {
|
|
@@ -1489,12 +1594,12 @@ function liveSprings(apply2) {
|
|
|
1489
1594
|
const dt = Math.min(0.5, Math.max(1e-3, (now - last) / 1e3));
|
|
1490
1595
|
last = now;
|
|
1491
1596
|
const steps = Math.ceil(dt * 240);
|
|
1492
|
-
const
|
|
1597
|
+
const h32 = dt / steps;
|
|
1493
1598
|
let moving = false;
|
|
1494
1599
|
for (const value of values.values()) {
|
|
1495
1600
|
for (let index = 0; index < steps; index += 1) {
|
|
1496
|
-
value.v += (-value.k * (value.x - value.to) - value.c * value.v) *
|
|
1497
|
-
value.x += value.v *
|
|
1601
|
+
value.v += (-value.k * (value.x - value.to) - value.c * value.v) * h32;
|
|
1602
|
+
value.x += value.v * h32;
|
|
1498
1603
|
}
|
|
1499
1604
|
if (Math.abs(value.x - value.to) < 0.05 && Math.abs(value.v) < 1) {
|
|
1500
1605
|
value.x = value.to;
|
|
@@ -1840,39 +1945,52 @@ button.bt-lead{border:0;background:none;padding:0;cursor:pointer}
|
|
|
1840
1945
|
.bt-x{border:0;background:none;color:var(--bt-ink-2);cursor:pointer;width:24px;height:24px;border-radius:6px;font-size:16px;line-height:24px;padding:0;transition:background-color .12s ease,color .12s ease,${spring(SPRING_TAP, "scale")}}
|
|
1841
1946
|
.bt-x:hover{background:var(--bt-hover);color:var(--bt-ink)}
|
|
1842
1947
|
|
|
1843
|
-
.bt-q{margin:0 auto 8px;width:100%;max-width:var(--dsh-chat-content-width,748px);display:flex;flex-direction:column;gap:
|
|
1948
|
+
.bt-q{margin:0 auto 8px;width:100%;max-width:var(--dsh-chat-content-width,748px);display:flex;flex-direction:column;gap:12px;--bt-q-edge:color-mix(in srgb,var(--bt-ink) 10%,transparent);--bt-q-rule:color-mix(in srgb,var(--bt-ink) 8%,transparent);--bt-q-rule-2:color-mix(in srgb,var(--bt-ink) 5%,transparent);background:var(--bt-card);box-shadow:0 1px 3px rgba(20,20,20,.04),inset 0 0 0 1px var(--bt-q-edge);border-radius:16px;padding:14px 16px;box-sizing:border-box;color:var(--bt-ink);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;animation:bt-q-in ${SPRING_SHAPE.duration}ms ${SPRING_SHAPE.easing} backwards,bt-veil-in .2s ease-out backwards}
|
|
1949
|
+
body[data-ds-dark-theme] .bt-q{--bt-q-edge:rgba(255,255,255,.16);--bt-q-rule:rgba(255,255,255,.12);--bt-q-rule-2:rgba(255,255,255,.08)}
|
|
1844
1950
|
@keyframes bt-q-in{from{transform:translateY(10px) scale(.97)}}
|
|
1845
1951
|
.bt-q-settled{animation:none}
|
|
1846
1952
|
.bt-q-head{display:flex;align-items:flex-start;gap:8px;min-width:0}
|
|
1847
1953
|
.bt-q-text{flex:1 1 0;min-width:0;display:flex;flex-direction:column}
|
|
1848
|
-
.bt-q-title{margin:0;font-size:
|
|
1954
|
+
.bt-q-title{margin:0;font-size:15px;line-height:22px;font-weight:600;overflow-wrap:anywhere}
|
|
1849
1955
|
.bt-q-detail{font-size:13px;line-height:18px;color:var(--bt-ink-2)}
|
|
1850
1956
|
.bt-q-x{flex:none;width:20px;height:20px;border:0;padding:0;border-radius:6px;background:none;color:var(--bt-ink-2);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background-color .12s ease,color .12s ease,${spring(SPRING_TAP, "scale")}}
|
|
1851
1957
|
.bt-q-x svg{width:14px;height:14px}
|
|
1852
1958
|
.bt-q-x:hover{background:var(--bt-hover);color:var(--bt-ink)}
|
|
1853
1959
|
.bt-q-x:focus-visible,.bt-q-opt:focus-visible{outline:2px solid var(--bt-accent);outline-offset:-2px}
|
|
1854
|
-
.bt-q-list{display:flex;flex-direction:column;
|
|
1855
|
-
.bt-q-opt{display:flex;align-items:
|
|
1856
|
-
.bt-q-
|
|
1857
|
-
.bt-q
|
|
1858
|
-
.bt-q-opt
|
|
1960
|
+
.bt-q-list{display:flex;flex-direction:column;gap:1px;min-width:0;box-sizing:border-box;margin:0 -8px}
|
|
1961
|
+
.bt-q-opt{position:relative;display:flex;align-items:flex-start;gap:16px;width:100%;min-width:0;box-sizing:border-box;margin:0;border:0;border-radius:12px;background:transparent;padding:10px 8px;font:inherit;color:var(--bt-ink);text-align:start;cursor:pointer;transition:background-color .12s ease}
|
|
1962
|
+
.bt-q:not(.bt-q-raised)>.bt-q-head:has(+.bt-q-list){padding-bottom:12px;border-bottom:1px solid var(--bt-q-rule)}
|
|
1963
|
+
.bt-q:not(.bt-q-raised)>.bt-q-head+.bt-q-list{margin-top:-6px}
|
|
1964
|
+
.bt-q-opt+.bt-q-opt::before{content:"";position:absolute;left:calc(var(--bt-key,24px) + 24px);right:8px;top:-1px;height:1px;background:var(--bt-q-rule-2);transition:opacity .12s ease;pointer-events:none}
|
|
1965
|
+
button.bt-q-opt:hover::before,button.bt-q-opt:hover+.bt-q-opt::before,.bt-q-opt[aria-pressed=true]::before,.bt-q-opt[aria-pressed=true]+.bt-q-opt::before{opacity:0}
|
|
1966
|
+
.bt-q-raised .bt-q-opt::before{display:none}
|
|
1859
1967
|
button.bt-q-opt:hover{background:var(--bt-hover)}
|
|
1860
1968
|
button.bt-q-opt:active{background:var(--bt-active)}
|
|
1861
1969
|
.bt-q-opt[aria-pressed=true]{background:var(--bt-active)}
|
|
1862
|
-
.bt-
|
|
1863
|
-
.bt-
|
|
1864
|
-
.bt-q-
|
|
1865
|
-
.
|
|
1970
|
+
.bt-q:not(.bt-q-raised) :is(button.bt-q-opt:hover,.bt-q-opt[aria-pressed=true]) .bt-key{background:var(--bt-card);color:var(--bt-ink)}
|
|
1971
|
+
.bt-key{flex:none;box-sizing:border-box;width:var(--bt-key,24px);height:var(--bt-key,24px);border-radius:8px;background:var(--bt-hover);color:var(--bt-ink-2);font-size:12px;line-height:var(--bt-key,24px);font-weight:500;text-align:center;font-variant-numeric:tabular-nums;transition:background-color .12s ease,color .12s ease}
|
|
1972
|
+
.bt-q-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px;padding-top:2px}
|
|
1973
|
+
/* With a description the key spans the two text lines less 2px above and below. */
|
|
1974
|
+
.bt-q-opt:has(.bt-q-desc){--bt-key:34px}
|
|
1975
|
+
.bt-q-opt:has(.bt-q-desc) .bt-key{margin-top:2px;border-radius:10px;font-size:13px}
|
|
1976
|
+
.bt-q-opt:has(.bt-q-desc) .bt-q-body{padding-top:0}
|
|
1977
|
+
.bt-q-label{font-size:14px;line-height:20px;font-weight:500;white-space:normal;overflow-wrap:anywhere}
|
|
1978
|
+
.bt-q-desc{font-size:12px;line-height:17px;color:var(--bt-ink-2);overflow-wrap:anywhere}
|
|
1866
1979
|
.bt-q-check{flex:none;display:inline-flex;align-items:center;color:var(--bt-ink-2);animation:bt-check-pop ${SPRING_POP.duration}ms ${SPRING_POP.easing} backwards}
|
|
1867
1980
|
.bt-q-settled .bt-q-check{animation:none}
|
|
1868
1981
|
.bt-q-check svg{width:16px;height:16px}
|
|
1869
1982
|
.bt-q-settled .bt-key{opacity:.4}
|
|
1870
1983
|
.bt-q-settled .bt-q-label{color:color-mix(in srgb,var(--bt-ink) 45%,transparent)}
|
|
1871
1984
|
.bt-q-dismissed .bt-q-title{color:var(--bt-ink-2)}
|
|
1985
|
+
.bt-q.bt-q-settled{gap:6px;padding:12px 16px}
|
|
1986
|
+
.bt-q-settled .bt-q-title{font-size:14px;line-height:20px;font-weight:500}
|
|
1987
|
+
.bt-q.bt-q-settled>.bt-q-head:has(+.bt-q-list){padding-bottom:0;border-bottom:0}
|
|
1988
|
+
.bt-q.bt-q-settled>.bt-q-head+.bt-q-list{margin-top:0}
|
|
1989
|
+
.bt-q-settled .bt-q-opt{padding:4px 8px}
|
|
1872
1990
|
.bt-q-badge{flex:none;height:20px;padding:0 7px;border-radius:999px;border:1px dotted var(--bt-line-2);color:var(--bt-ink-2);font-size:12px;line-height:18px;box-sizing:border-box}
|
|
1873
1991
|
.bt-q-custom{display:flex;align-items:flex-start;gap:8px;width:100%;min-width:0}
|
|
1874
|
-
.bt-q-field{flex:1 1 0;min-width:0;display:flex;box-sizing:border-box;min-height:32px;padding:5px 11px;border-radius:8px;border:.5px solid var(--bt-line-
|
|
1875
|
-
.bt-q-field:focus-within{border-color:var(--bt-
|
|
1992
|
+
.bt-q-field{flex:1 1 0;min-width:0;display:flex;box-sizing:border-box;min-height:32px;padding:5px 11px;border-radius:8px;border:.5px solid var(--bt-line-2);background:none;cursor:text;transition:border-color .12s ease}
|
|
1993
|
+
.bt-q-field:focus-within{border-color:color-mix(in srgb,var(--bt-ink) 30%,transparent)}
|
|
1876
1994
|
.bt-q-input{flex:1;width:100%;min-width:0;max-height:120px;margin:0;padding:0;border:0;outline:none;background:transparent;color:var(--bt-ink);font:inherit;font-size:14px;line-height:20px;resize:none;overflow-y:hidden;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
1877
1995
|
.bt-q-input:not(:placeholder-shown){field-sizing:content}
|
|
1878
1996
|
.bt-q-input::placeholder{color:var(--bt-ink-3)}
|
|
@@ -1884,16 +2002,14 @@ button.bt-q-opt:active{background:var(--bt-active)}
|
|
|
1884
2002
|
.bt-q-btn{background:var(--bt-hover);color:var(--bt-ink)}
|
|
1885
2003
|
.bt-q-btn:hover{background:var(--bt-active)}
|
|
1886
2004
|
.bt-q-foot{display:flex;justify-content:flex-end;gap:8px}
|
|
1887
|
-
.bt-q-raised{width:calc(var(--dsh-chat-content-width,748px) - 32px);max-width:calc(100% - 32px);gap:0;padding:0;
|
|
2005
|
+
.bt-q-raised{width:calc(var(--dsh-chat-content-width,748px) - 32px);max-width:calc(100% - 32px);gap:0;padding:0;box-shadow:0 2px 8px rgba(20,20,20,.035),inset 0 0 0 .5px color-mix(in srgb,var(--bt-ink) 5%,transparent)}
|
|
1888
2006
|
.bt-q-raised .bt-q-head{padding:14px 16px 4px 20px}
|
|
1889
2007
|
.bt-q-raised .bt-q-head:last-child{padding-bottom:16px}
|
|
1890
|
-
.bt-q-raised .bt-q-
|
|
1891
|
-
.bt-q-raised .bt-q-
|
|
1892
|
-
.bt-q-raised .bt-q-opt{align-items:flex-start;gap:4px;padding:7px;border:0;border-radius:16px;color:var(--bt-ink-2)}
|
|
2008
|
+
.bt-q-raised .bt-q-list{gap:1px;margin:0;padding:8px}
|
|
2009
|
+
.bt-q-raised .bt-q-opt{gap:4px;padding:7px;border-radius:16px;color:var(--bt-ink-2)}
|
|
1893
2010
|
.bt-q-raised button.bt-q-opt:hover{color:var(--bt-ink)}
|
|
1894
2011
|
.bt-q-raised .bt-q-opt[aria-pressed=true]{background:transparent;color:var(--bt-ink)}
|
|
1895
2012
|
.bt-q-raised .bt-q-opt[aria-pressed=true]:hover{background:var(--bt-hover)}
|
|
1896
|
-
.bt-q-raised .bt-key{width:24px;min-width:24px;height:24px;padding:0;border:0;border-radius:8px;background:var(--bt-hover);color:var(--bt-ink-2);font-size:12px;line-height:24px;font-weight:500;transition:background-color .12s ease,color .12s ease}
|
|
1897
2013
|
.bt-q-raised .bt-q-opt[aria-pressed=true] .bt-key,.bt-q-ownrow:focus-within .bt-key,.bt-q-ownrow[data-filled] .bt-key{background:var(--bt-user);color:var(--bt-user-ink)}
|
|
1898
2014
|
.bt-q-raised .bt-q-body{padding:2px 6px 0}
|
|
1899
2015
|
.bt-q-raised .bt-q-desc{color:color-mix(in srgb,var(--bt-ink) 61%,transparent)}
|
|
@@ -1957,6 +2073,7 @@ button.bt-q-opt:active{background:var(--bt-active)}
|
|
|
1957
2073
|
@keyframes bt-sheet-out{to{opacity:0;transform:translateY(16px);filter:blur(4px)}}
|
|
1958
2074
|
.bt-exchange .bt-dialog{position:relative}
|
|
1959
2075
|
.bt-exchange .bt-dialog-log{max-width:none;margin:0;padding:39px max(16px,calc((100% - 960px)/2)) 76px;scrollbar-width:thin}
|
|
2076
|
+
.bt-exchange .bt-tools{display:none}
|
|
1960
2077
|
.bt-exchange .bt-dialog-foot{position:absolute;left:0;right:0;bottom:0;height:76px;box-sizing:border-box;align-items:flex-end;padding:0 0 28px;background:var(--bt-main);z-index:3;animation:bt-foot-in .3s cubic-bezier(.22,1,.36,1) .3s both}
|
|
1961
2078
|
.bt-exchange .bt-dialog-foot::before{content:"";position:absolute;left:0;right:0;bottom:100%;height:40px;background:linear-gradient(to top,var(--bt-main),transparent);pointer-events:none}
|
|
1962
2079
|
.bt-exchange[data-leaving] .bt-dialog-foot{animation:bt-foot-out .22s cubic-bezier(.4,0,1,1) forwards}
|
|
@@ -2422,6 +2539,11 @@ var CONVERSATION_CSS = `
|
|
|
2422
2539
|
.bt-event button{border:0;background:none;color:inherit;font:inherit;display:inline-flex;align-items:center;gap:4px;cursor:pointer;height:24px;padding:4px 6px 4px 4px;border-radius:999px;box-sizing:border-box;transition:background-color .12s ease}
|
|
2423
2540
|
.bt-event button:hover{background:var(--bt-hover)}
|
|
2424
2541
|
.bt-event button:active{background:var(--bt-active)}
|
|
2542
|
+
.bt-event button.bt-event-whole{min-width:0;max-width:100%;padding:4px 6px 4px 8px}
|
|
2543
|
+
.bt-event-whole>*{flex:none}
|
|
2544
|
+
.bt-event-whole>span:first-child{margin-right:2px}
|
|
2545
|
+
/* One button cannot wrap like separate parts, so a long name is cut to fit the chat. */
|
|
2546
|
+
.bt-event-whole>.bt-event-name{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2425
2547
|
.bt-time-sep{display:flex;justify-content:center;align-items:center;height:28px;margin:14px 0 8px;font-size:12px;line-height:16px;color:var(--bt-ink-2);white-space:nowrap}
|
|
2426
2548
|
[class*="_scroll"]>[class*="_column"]::before{content:var(--bt-first-sep,none);display:block;flex:none;height:28px;margin:17px 0 12px;font-size:12px;line-height:28px;text-align:center;color:var(--bt-ink-2);white-space:nowrap}
|
|
2427
2549
|
.bt-activity{display:flex;align-items:center;gap:10px;margin:8px 0 4px 2px;font-size:14px;color:var(--bt-ink-2)}
|
|
@@ -2664,7 +2786,7 @@ function installCompiledStyles(text, tag) {
|
|
|
2664
2786
|
var import_react2 = require("react");
|
|
2665
2787
|
var STATES = ["idle", "thinking", "searching", "working", "sending", "orbit", "alert", "done"];
|
|
2666
2788
|
var botState = (running, activity, alert) => running ? STATES.includes(activity) ? activity : "working" : alert ? "alert" : "idle";
|
|
2667
|
-
var toolState = (name) => /^(message_bot|create_bot|update_bot|create_group|update_group|delete_group|post_to_group)$/.test(name) ? "sending" : /search|fetch|browse|read|grep|glob|list|recall/i.test(name) ? "searching" : "working";
|
|
2789
|
+
var toolState = (name) => /^(message_bot|stop_bot|create_bot|update_bot|create_group|update_group|delete_group|post_to_group)$/.test(name) ? "sending" : /search|fetch|browse|read|grep|glob|list|recall/i.test(name) ? "searching" : "working";
|
|
2668
2790
|
function useGaze(ref, enabled) {
|
|
2669
2791
|
(0, import_react2.useEffect)(() => {
|
|
2670
2792
|
if (!enabled) return void 0;
|
|
@@ -3697,25 +3819,54 @@ function ThinkingRow({ text }) {
|
|
|
3697
3819
|
(0, import_react6.createElement)("div", { className: "bt-think-text" }, text)
|
|
3698
3820
|
);
|
|
3699
3821
|
}
|
|
3700
|
-
function AssistantCell({ node, groupPart, sessionId, useRoster, useUi, useTurnSteps, actions }) {
|
|
3822
|
+
function AssistantCell({ node, groupPart, sessionId, useRoster, useUi, useTurnSteps, useTurnContext, actions }) {
|
|
3701
3823
|
const roster = useRoster((value) => value);
|
|
3702
3824
|
const data = node.data;
|
|
3703
3825
|
const steps = useTurnSteps(String(data.turn));
|
|
3826
|
+
const contexts = useTurnContext(String(data.turn));
|
|
3704
3827
|
const hidden = Boolean(roster.roomsById[sessionId]) || isHiddenTurn(roster, sessionId, data.turn);
|
|
3705
3828
|
const texts2 = groupPart === "reasoning" ? [] : data.blocks.filter((block) => block.kind === "text" && block.text.trim() !== "").map((block) => block.text);
|
|
3706
3829
|
const running = data.status === "running";
|
|
3707
3830
|
useLiveNote(actions, sessionId, `text:${node.key ?? data.seq}`, !hidden && running && texts2.length > 0 ? { text: true } : null);
|
|
3708
3831
|
if (hidden) return null;
|
|
3709
3832
|
const reasoning = groupPart === "response" ? "" : turnReasoning(steps, data);
|
|
3710
|
-
|
|
3833
|
+
const relays = relayPart(data, groupPart) ? relaysBefore(contexts, steps, data) : [];
|
|
3834
|
+
if (texts2.length === 0 && !reasoning && relays.length === 0) return null;
|
|
3835
|
+
const selfId = roster.byId[sessionId]?.id ?? sessionId;
|
|
3711
3836
|
return (0, import_react6.createElement)(
|
|
3712
3837
|
import_react6.Fragment,
|
|
3713
3838
|
null,
|
|
3839
|
+
relays.map((relay) => {
|
|
3840
|
+
const source = relay.source;
|
|
3841
|
+
const sender = roster.byId[source.senderSessionId] ?? { id: source.senderSessionId, name: source.senderName ?? t("a Bot"), color: "gray" };
|
|
3842
|
+
return (0, import_react6.createElement)(EventLine, {
|
|
3843
|
+
key: relay.seq,
|
|
3844
|
+
prefix: source.role === "stopped" ? t("Stopped by") : source.role === "reply" ? t("Reply from") : t("Message from"),
|
|
3845
|
+
bots: [sender],
|
|
3846
|
+
onOpen: () => {
|
|
3847
|
+
if (sender.id) actions.openExchange(selfId, sender.id);
|
|
3848
|
+
},
|
|
3849
|
+
whole: true
|
|
3850
|
+
});
|
|
3851
|
+
}),
|
|
3714
3852
|
reasoning ? (0, import_react6.createElement)(ThinkingRow, { text: reasoning }) : null,
|
|
3715
|
-
texts2.length ? (0, import_react6.createElement)(Bubbles, { texts: texts2, roster, selfId
|
|
3853
|
+
texts2.length ? (0, import_react6.createElement)(Bubbles, { texts: texts2, roster, selfId, actions, streaming: running, sessionId, msgKey: `${sessionId}:${data.turn}:${node.key ?? data.seq ?? ""}`, useUi }) : null
|
|
3716
3854
|
);
|
|
3717
3855
|
}
|
|
3718
|
-
function EventLine({ prefix, bots, joiner = t("and"), onOpen }) {
|
|
3856
|
+
function EventLine({ prefix, bots, joiner = t("and"), onOpen, whole = false }) {
|
|
3857
|
+
if (whole && bots.length === 1) {
|
|
3858
|
+
return (0, import_react6.createElement)(
|
|
3859
|
+
"div",
|
|
3860
|
+
{ className: "bt-event" },
|
|
3861
|
+
(0, import_react6.createElement)(
|
|
3862
|
+
"button",
|
|
3863
|
+
{ type: "button", className: "bt-event-whole", title: bots[0].name, onClick: () => onOpen?.(bots[0]) },
|
|
3864
|
+
(0, import_react6.createElement)("span", null, prefix),
|
|
3865
|
+
(0, import_react6.createElement)(BotMark, { bot: bots[0], size: 16 }),
|
|
3866
|
+
(0, import_react6.createElement)("span", { className: "bt-event-name" }, bots[0].name)
|
|
3867
|
+
)
|
|
3868
|
+
);
|
|
3869
|
+
}
|
|
3719
3870
|
const parts = [];
|
|
3720
3871
|
parts.push((0, import_react6.createElement)("span", { key: "p" }, prefix));
|
|
3721
3872
|
bots.forEach((bot, index) => {
|
|
@@ -3854,9 +4005,10 @@ function EarlierParts({ sessionId, roster, actions }) {
|
|
|
3854
4005
|
case "event": {
|
|
3855
4006
|
if (row.role === "report") return (0, import_react6.createElement)(GroupEvent, { key, prefix: t("Replies from"), room: roster.roomsById[row.roomId], name: t("a group chat"), roster, actions });
|
|
3856
4007
|
const other = roster.byId[row.botId] ?? { id: row.botId, name: row.name ?? t("a Bot"), color: "gray" };
|
|
3857
|
-
|
|
4008
|
+
const prefix = { stopped: t("Stopped by"), reply: t("Reply from") }[row.role] ?? t("Message from");
|
|
4009
|
+
return (0, import_react6.createElement)(EventLine, { key, prefix, bots: [other], onOpen: () => {
|
|
3858
4010
|
if (selfId && other.id) actions.openExchange(selfId, other.id);
|
|
3859
|
-
} });
|
|
4011
|
+
}, whole: true });
|
|
3860
4012
|
}
|
|
3861
4013
|
default:
|
|
3862
4014
|
return null;
|
|
@@ -3870,7 +4022,8 @@ function TriggerCell({ node, sessionId, useRoster, useSessions, useTurnClock, us
|
|
|
3870
4022
|
const data = node.data;
|
|
3871
4023
|
const source = data.source !== null && typeof data.source === "object" ? data.source : {};
|
|
3872
4024
|
const selfId = roster.byId[sessionId]?.id ?? sessionId;
|
|
3873
|
-
const
|
|
4025
|
+
const deferred = source.kind === "bot" && source.deferred === true;
|
|
4026
|
+
const relayed = source.kind === "bot" && source.handoff !== true && !deferred && (source.role === "request" || source.role === "reply" || source.role === "stopped") && typeof source.senderSessionId === "string";
|
|
3874
4027
|
const turn = node.location?.turn?.turn ?? data.turn;
|
|
3875
4028
|
(0, import_react6.useLayoutEffect)(() => {
|
|
3876
4029
|
if (relayed) actions.noteTurnPeer(sessionId, turn, source.senderSessionId);
|
|
@@ -3896,8 +4049,9 @@ function TriggerCell({ node, sessionId, useRoster, useSessions, useTurnClock, us
|
|
|
3896
4049
|
if (source.kind === "bot") {
|
|
3897
4050
|
if (source.handoff === true) return roster.roomsById[sessionId] ? null : (0, import_react6.createElement)(EarlierParts, { sessionId, roster, actions });
|
|
3898
4051
|
if (source.role === "kickoff" || source.role === "room") return null;
|
|
3899
|
-
if (source.role === "secret") return null;
|
|
4052
|
+
if (source.role === "secret" || deferred) return null;
|
|
3900
4053
|
if (source.role === "post") {
|
|
4054
|
+
if (source.failed !== void 0) return null;
|
|
3901
4055
|
const room2 = roster.roomsById[sessionId];
|
|
3902
4056
|
const poster = roster.byId[source.senderSessionId] ?? { id: source.senderSessionId, name: source.senderName ?? t("a Bot"), color: "gray" };
|
|
3903
4057
|
return (0, import_react6.createElement)(GroupMessage, { bot: poster, text: stripHeader(contentText(data.content)), room: room2, roster, actions, msgKey: `${sessionId}:${data.turn}:post`, useUi });
|
|
@@ -3919,9 +4073,10 @@ function TriggerCell({ node, sessionId, useRoster, useSessions, useTurnClock, us
|
|
|
3919
4073
|
if (!bots.some((bot) => bot.id === peer.id)) bots.push(peer);
|
|
3920
4074
|
}
|
|
3921
4075
|
return (0, import_react6.createElement)(EventLine, {
|
|
3922
|
-
prefix: count > 1 ? t("{count} messages with", { count }) : t("Message from"),
|
|
4076
|
+
prefix: source.role === "stopped" ? t("Stopped by") : count > 1 ? t("{count} messages with", { count }) : t("Message from"),
|
|
3923
4077
|
bots,
|
|
3924
|
-
onOpen: (bot) => actions.openExchange(selfId, bot.id)
|
|
4078
|
+
onOpen: (bot) => actions.openExchange(selfId, bot.id),
|
|
4079
|
+
whole: true
|
|
3925
4080
|
});
|
|
3926
4081
|
}
|
|
3927
4082
|
if (source.kind === "schedule") {
|
|
@@ -3961,11 +4116,11 @@ function clockLabel(time, now = Date.now()) {
|
|
|
3961
4116
|
const date = new Date(time);
|
|
3962
4117
|
const midnight = (value) => new Date(value.getFullYear(), value.getMonth(), value.getDate()).getTime();
|
|
3963
4118
|
const days = Math.round((midnight(new Date(now)) - midnight(date)) / 864e5);
|
|
3964
|
-
const
|
|
3965
|
-
if (days === 0) return t("Today {time}", { time:
|
|
3966
|
-
if (days === 1) return t("Yesterday {time}", { time:
|
|
4119
|
+
const clock4 = date.toLocaleTimeString(dateLocale(), { hour: "numeric", minute: "2-digit" });
|
|
4120
|
+
if (days === 0) return t("Today {time}", { time: clock4 });
|
|
4121
|
+
if (days === 1) return t("Yesterday {time}", { time: clock4 });
|
|
3967
4122
|
const sameYear = date.getFullYear() === new Date(now).getFullYear();
|
|
3968
|
-
return `${date.toLocaleDateString(dateLocale(), { weekday: "short", month: "short", day: "numeric", ...sameYear ? {} : { year: "numeric" } })} ${
|
|
4123
|
+
return `${date.toLocaleDateString(dateLocale(), { weekday: "short", month: "short", day: "numeric", ...sameYear ? {} : { year: "numeric" } })} ${clock4}`;
|
|
3969
4124
|
}
|
|
3970
4125
|
function nextTurnStart(turns, turn) {
|
|
3971
4126
|
let next;
|
|
@@ -4025,7 +4180,7 @@ var TOOL_PHRASES = [
|
|
|
4025
4180
|
[/image|photo|draw/i, "Working on an image"]
|
|
4026
4181
|
];
|
|
4027
4182
|
var GROUP_PHRASES = { create_group: "Creating a group", update_group: "Updating the group", delete_group: "Deleting the group", post_to_group: "Posting in the group" };
|
|
4028
|
-
function toolActivity(name, arg, findBot) {
|
|
4183
|
+
function toolActivity(name, arg, findBot, self2) {
|
|
4029
4184
|
switch (name) {
|
|
4030
4185
|
case "ask_user":
|
|
4031
4186
|
return null;
|
|
@@ -4041,10 +4196,14 @@ function toolActivity(name, arg, findBot) {
|
|
|
4041
4196
|
const target = findBot(arg("to"));
|
|
4042
4197
|
return { label: target ? t("Asking {name}", { name: target.name }) : t("Asking another Bot"), state: "sending" };
|
|
4043
4198
|
}
|
|
4199
|
+
case "stop_bot": {
|
|
4200
|
+
const target = findBot(arg("bot"));
|
|
4201
|
+
return { label: target ? t("Stopping {name}", { name: target.name }) : t("Stopping another Bot"), state: "sending" };
|
|
4202
|
+
}
|
|
4044
4203
|
case "create_bot":
|
|
4045
4204
|
return { label: t("Creating {name}", { name: arg("name") ?? t("a Bot") }), state: "sending" };
|
|
4046
4205
|
case "update_bot": {
|
|
4047
|
-
const target = findBot(arg("name")) ?? findBot(arg("bot"));
|
|
4206
|
+
const target = findBot(arg("name")) ?? findBot(arg("bot")) ?? (arg("bot") ? void 0 : self2);
|
|
4048
4207
|
return { label: t("Updating {name}", { name: target?.name ?? t("a Bot") }), state: "sending" };
|
|
4049
4208
|
}
|
|
4050
4209
|
case "list_bots":
|
|
@@ -4065,6 +4224,10 @@ function toolActivity(name, arg, findBot) {
|
|
|
4065
4224
|
return { label: phrase ? t(phrase) : null, state: toolState(name) };
|
|
4066
4225
|
}
|
|
4067
4226
|
}
|
|
4227
|
+
function workPhrase(tool) {
|
|
4228
|
+
if (!tool) return t("Thinking about it");
|
|
4229
|
+
return toolActivity(tool, () => void 0, () => void 0)?.label ?? t("On it");
|
|
4230
|
+
}
|
|
4068
4231
|
function useLiveNote(actions, sessionId, id, entry) {
|
|
4069
4232
|
const key = entry ? JSON.stringify(entry) : "";
|
|
4070
4233
|
(0, import_react6.useEffect)(() => {
|
|
@@ -4073,7 +4236,7 @@ function useLiveNote(actions, sessionId, id, entry) {
|
|
|
4073
4236
|
return () => actions.noteLive(sessionId, id, null);
|
|
4074
4237
|
}, [sessionId, id, key]);
|
|
4075
4238
|
}
|
|
4076
|
-
function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi }) {
|
|
4239
|
+
function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi, useWork }) {
|
|
4077
4240
|
const [live, setLive] = (0, import_react6.useState)({ replies: [], speaking: null });
|
|
4078
4241
|
(0, import_react6.useEffect)(() => {
|
|
4079
4242
|
if (done) return void 0;
|
|
@@ -4106,6 +4269,7 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4106
4269
|
}
|
|
4107
4270
|
}
|
|
4108
4271
|
const pending = done || !live.speaking ? [] : [live.speaking];
|
|
4272
|
+
const speakerTool = useWork?.((work) => work.bots?.[live.speaking]?.tool) ?? void 0;
|
|
4109
4273
|
if (replies.length === 0 && pending.length === 0) return null;
|
|
4110
4274
|
const room2 = roster.roomsById[sessionId];
|
|
4111
4275
|
return (0, import_react6.createElement)(
|
|
@@ -4113,6 +4277,13 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4113
4277
|
{ className: "bt-group-replies" },
|
|
4114
4278
|
replies.map((reply, index) => {
|
|
4115
4279
|
const bot = roster.byId[reply.botId] ?? { id: reply.botId, name: reply.name, color: reply.color ?? "gray" };
|
|
4280
|
+
if (reply.kind === "user") {
|
|
4281
|
+
return (0, import_react6.createElement)("div", { key: `user-${index}`, className: "bt-urow" }, (0, import_react6.createElement)(
|
|
4282
|
+
"div",
|
|
4283
|
+
{ className: "bt-ububble" },
|
|
4284
|
+
(0, import_react6.createElement)(import_dsh_client_ui_primitives.MarkdownText, { text: reply.text, labels: mdLabels(), variant: "compact" })
|
|
4285
|
+
));
|
|
4286
|
+
}
|
|
4116
4287
|
if (reply.kind === "failed") {
|
|
4117
4288
|
return (0, import_react6.createElement)(
|
|
4118
4289
|
"div",
|
|
@@ -4127,6 +4298,21 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4127
4298
|
reply.text ? t("{name} looked up its own chat for “{text}”", { name: bot.name, text: reply.text }) : t("{name} looked up its own chat", { name: bot.name })
|
|
4128
4299
|
);
|
|
4129
4300
|
}
|
|
4301
|
+
if (reply.kind === "missed") {
|
|
4302
|
+
return (0, import_react6.createElement)("div", { key: `${reply.botId}-${index}`, className: "bt-event" }, t("{name} didn't answer in time", { name: bot.name }));
|
|
4303
|
+
}
|
|
4304
|
+
if (reply.kind === "approval") {
|
|
4305
|
+
return (0, import_react6.createElement)(
|
|
4306
|
+
"div",
|
|
4307
|
+
{ key: `${reply.botId}-${index}`, className: "bt-event" },
|
|
4308
|
+
(0, import_react6.createElement)(
|
|
4309
|
+
"button",
|
|
4310
|
+
{ type: "button", className: "bt-event-whole", onClick: () => actions.showDetails(sessionId) },
|
|
4311
|
+
(0, import_react6.createElement)(BotMark, { bot, size: 16 }),
|
|
4312
|
+
(0, import_react6.createElement)("span", { className: "bt-event-name" }, t("{name} paused for your approval", { name: bot.name }))
|
|
4313
|
+
)
|
|
4314
|
+
);
|
|
4315
|
+
}
|
|
4130
4316
|
return (0, import_react6.createElement)(GroupMessage, { key: `${reply.botId}-${index}`, bot, text: reply.text, room: room2, roster, actions, msgKey: callId ? `${sessionId}:${callId}:${index}` : void 0, useUi });
|
|
4131
4317
|
}),
|
|
4132
4318
|
pending.map((id) => {
|
|
@@ -4135,13 +4321,13 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4135
4321
|
return (0, import_react6.createElement)(
|
|
4136
4322
|
"div",
|
|
4137
4323
|
{ key: `pending-${id}`, className: "bt-activity" },
|
|
4138
|
-
(0, import_react6.createElement)(BotMark, { bot, size: 22, state: "thinking" }),
|
|
4139
|
-
(0, import_react6.createElement)(Shimmer, null, t("{name} is typing…", { name: bot.name }))
|
|
4324
|
+
(0, import_react6.createElement)(BotMark, { bot, size: 22, state: speakerTool ? toolState(speakerTool) : "thinking" }),
|
|
4325
|
+
(0, import_react6.createElement)(Shimmer, null, speakerTool ? `${bot.name} · ${workPhrase(speakerTool)}…` : t("{name} is typing…", { name: bot.name }))
|
|
4140
4326
|
);
|
|
4141
4327
|
})
|
|
4142
4328
|
);
|
|
4143
4329
|
}
|
|
4144
|
-
function ToolCell({ node, sessionId, useRoster, useUi, actions }) {
|
|
4330
|
+
function ToolCell({ node, sessionId, useRoster, useUi, useWork, actions }) {
|
|
4145
4331
|
const roster = useRoster((value) => value);
|
|
4146
4332
|
const root = node.data.root;
|
|
4147
4333
|
const done = root.kind === "tool-result";
|
|
@@ -4150,13 +4336,18 @@ function ToolCell({ node, sessionId, useRoster, useUi, actions }) {
|
|
|
4150
4336
|
const tool = toolName(root);
|
|
4151
4337
|
const { text: arg, value: argValue } = toolArgs(root);
|
|
4152
4338
|
const findBot = (nameOrId) => roster.byId[nameOrId] ?? roster.bots.find((bot) => bot.name.toLowerCase() === String(nameOrId ?? "").toLowerCase());
|
|
4153
|
-
useLiveNote(actions, sessionId, `tool:${root.callId ?? node.key}`, done || hidden ? null : toolActivity(tool, arg, findBot));
|
|
4339
|
+
useLiveNote(actions, sessionId, `tool:${root.callId ?? node.key}`, done || hidden ? null : toolActivity(tool, arg, findBot, roster.byId[sessionId]));
|
|
4154
4340
|
if (hidden) return null;
|
|
4155
4341
|
switch (tool) {
|
|
4156
4342
|
case "message_bot": {
|
|
4157
4343
|
const target = findBot(arg("to")) ?? { name: arg("to") ?? "…", color: "gray" };
|
|
4158
4344
|
if (done && root.isError) return (0, import_react6.createElement)("div", { className: "bt-event" }, t("Couldn't message {name}", { name: target.name }));
|
|
4159
|
-
return (0, import_react6.createElement)(EventLine, { prefix: t("Messaged"), bots: [target], onOpen: (bot) => bot.id && actions.openExchange(self?.id ?? sessionId, bot.id) });
|
|
4345
|
+
return (0, import_react6.createElement)(EventLine, { prefix: t("Messaged"), bots: [target], onOpen: (bot) => bot.id && actions.openExchange(self?.id ?? sessionId, bot.id), whole: true });
|
|
4346
|
+
}
|
|
4347
|
+
case "stop_bot": {
|
|
4348
|
+
const target = findBot(arg("bot")) ?? { name: arg("bot") ?? "…", color: "gray" };
|
|
4349
|
+
if (done && root.isError) return (0, import_react6.createElement)("div", { className: "bt-event" }, t("Couldn't stop {name}", { name: target.name }));
|
|
4350
|
+
return (0, import_react6.createElement)(EventLine, { prefix: t("Stopped"), bots: [target], onOpen: (bot) => bot.id && actions.openExchange(roster.byId[sessionId]?.id ?? sessionId, bot.id), whole: true });
|
|
4160
4351
|
}
|
|
4161
4352
|
case "create_bot": {
|
|
4162
4353
|
const created = findBot(arg("name"));
|
|
@@ -4164,11 +4355,11 @@ function ToolCell({ node, sessionId, useRoster, useUi, actions }) {
|
|
|
4164
4355
|
return (0, import_react6.createElement)(EventLine, { prefix: t("Created"), bots: [created], onOpen: (bot) => actions.openSession(bot.id) });
|
|
4165
4356
|
}
|
|
4166
4357
|
case "update_bot": {
|
|
4167
|
-
const target = findBot(arg("name")) ?? findBot(arg("bot"));
|
|
4358
|
+
const target = findBot(arg("name")) ?? findBot(arg("bot")) ?? (arg("bot") ? void 0 : roster.byId[sessionId]);
|
|
4168
4359
|
return done && target ? (0, import_react6.createElement)(EventLine, { prefix: t("Updated"), bots: [target], onOpen: (bot) => actions.openSession(bot.id) }) : null;
|
|
4169
4360
|
}
|
|
4170
4361
|
case "group_relay":
|
|
4171
|
-
return (0, import_react6.createElement)(GroupReplies, { sessionId, callId: root.callId, done, content: root.content, roster, actions, useUi });
|
|
4362
|
+
return (0, import_react6.createElement)(GroupReplies, { sessionId, callId: root.callId, done, content: root.content, roster, actions, useUi, useWork });
|
|
4172
4363
|
case "create_group":
|
|
4173
4364
|
case "update_group":
|
|
4174
4365
|
case "delete_group":
|
|
@@ -4303,10 +4494,11 @@ function useRowEntrances(replacing) {
|
|
|
4303
4494
|
};
|
|
4304
4495
|
}, []);
|
|
4305
4496
|
}
|
|
4306
|
-
function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, actions }) {
|
|
4497
|
+
function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, useSession, actions }) {
|
|
4307
4498
|
const roster = useRoster((value) => value);
|
|
4308
4499
|
const running = useSessionStatus((map) => map.get(sessionId)?.running === true);
|
|
4309
4500
|
const liveKey = useLive((state) => JSON.stringify(state[sessionId] ?? {}));
|
|
4501
|
+
const pendingInput = useSession((snapshot) => Math.max(0, ...(snapshot?.pendingSubmissions ?? []).filter((sub) => sub.placement !== "queued").map((sub) => sub.time)));
|
|
4310
4502
|
const bot = roster.byId[sessionId];
|
|
4311
4503
|
const room2 = roster.roomsById[sessionId];
|
|
4312
4504
|
const entries2 = (0, import_react7.useMemo)(() => Object.values(JSON.parse(liveKey)), [liveKey]);
|
|
@@ -4315,7 +4507,10 @@ function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, ac
|
|
|
4315
4507
|
const relaying = tools.some((entry) => entry.relay);
|
|
4316
4508
|
const visible = running && !streaming && !relaying && (bot !== void 0 || room2 !== void 0);
|
|
4317
4509
|
const tool = tools.at(-1);
|
|
4318
|
-
const
|
|
4510
|
+
const inputRef = (0, import_react7.useRef)(0);
|
|
4511
|
+
const inputAt = Math.max(inputRef.current, pendingInput);
|
|
4512
|
+
inputRef.current = inputAt;
|
|
4513
|
+
const want = !visible ? null : inputAt > (tool?.at ?? 0) ? { key: `input:${inputAt}`, pool: PHRASES.thinking, state: "thinking" } : tool ? { key: tool.label ? `tool:${tool.label}` : "working", label: tool.label, pool: PHRASES.working, state: tool.state ?? "working" } : { key: "thinking", pool: PHRASES.thinking, state: "thinking" };
|
|
4319
4514
|
const [shown, setShown] = (0, import_react7.useState)(null);
|
|
4320
4515
|
const wantKey = want?.key ?? null;
|
|
4321
4516
|
const markState = (0, import_react7.useRef)("thinking");
|
|
@@ -5176,7 +5371,7 @@ function MemoryNotice({ shown }) {
|
|
|
5176
5371
|
(0, import_react9.createElement)("span", { className: "bt-pill-mem-in", ref: inner }, (0, import_react9.createElement)("span", { className: "bt-pill-mem-ico" }, (0, import_react9.createElement)(MemoryIcon)), label)
|
|
5177
5372
|
);
|
|
5178
5373
|
}
|
|
5179
|
-
function HeaderPill({ sessionId, useRoster, useSessions, useSessionStatus, useActivity, useMemoryNews, useTurnClock, useUi, useLive, actions }) {
|
|
5374
|
+
function HeaderPill({ sessionId, useRoster, useSessions, useSessionStatus, useSession, useActivity, useMemoryNews, useTurnClock, useUi, useLive, actions }) {
|
|
5180
5375
|
const roster = useRoster((value) => value);
|
|
5181
5376
|
const title = useSessions((list) => list.byId[sessionId]?.displayTitle ?? list.byId[sessionId]?.title ?? "");
|
|
5182
5377
|
const firstTurns = useSessions((list) => {
|
|
@@ -5240,7 +5435,7 @@ function HeaderPill({ sessionId, useRoster, useSessions, useSessionStatus, useAc
|
|
|
5240
5435
|
),
|
|
5241
5436
|
(0, import_react9.createElement)("span", { className: "bt-sr", role: "status" }, memoryNotice ? t("Memory updated") : ""),
|
|
5242
5437
|
(0, import_react9.createElement)(NewMessagePills, { key: sessionId, sessionId, useSessions, useUi, useTurnClock }),
|
|
5243
|
-
(0, import_react9.createElement)(ActivityIndicator, { key: `activity:${sessionId}`, sessionId, useRoster, useSessionStatus, useLive, actions })
|
|
5438
|
+
(0, import_react9.createElement)(ActivityIndicator, { key: `activity:${sessionId}`, sessionId, useRoster, useSessionStatus, useSession, useLive, actions })
|
|
5244
5439
|
);
|
|
5245
5440
|
}
|
|
5246
5441
|
function useTranscriptView() {
|
|
@@ -5472,7 +5667,16 @@ function ComposerVoice({ sessionId, inputActions, actions, useUi }) {
|
|
|
5472
5667
|
dictation.note ? (0, import_react10.createElement)("span", { className: "bt-mic-note", role: "status" }, dictation.note) : null
|
|
5473
5668
|
);
|
|
5474
5669
|
}
|
|
5475
|
-
function
|
|
5670
|
+
function menuIcons(roster) {
|
|
5671
|
+
const marks = /* @__PURE__ */ new Map();
|
|
5672
|
+
return (row) => {
|
|
5673
|
+
if (!row.bot) return PeopleIcon;
|
|
5674
|
+
const id = row.bot.id;
|
|
5675
|
+
if (!marks.has(id)) marks.set(id, ({ size = 14 }) => (0, import_react10.createElement)(BotMark, { bot: roster.getSnapshot().byId[id] ?? row.bot, size }));
|
|
5676
|
+
return marks.get(id);
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5679
|
+
function ComposerPlus({ sessionId, inputActions, useRoster, actions }) {
|
|
5476
5680
|
const roster = useRoster((value) => value);
|
|
5477
5681
|
const [open, setOpen] = (0, import_react10.useState)(null);
|
|
5478
5682
|
const [shown, leaving] = useLinger(open, 120);
|
|
@@ -5514,6 +5718,11 @@ function ComposerPlus({ sessionId, useRoster, actions }) {
|
|
|
5514
5718
|
setOpen(null);
|
|
5515
5719
|
actions.openSettings("schedules", target);
|
|
5516
5720
|
};
|
|
5721
|
+
const reference = () => {
|
|
5722
|
+
setOpen(null);
|
|
5723
|
+
actions.referenceChat(sessionId, inputActions?.captureInsertion?.());
|
|
5724
|
+
};
|
|
5725
|
+
const canReference = typeof inputActions?.captureInsertion === "function" && actions.canReferenceChat();
|
|
5517
5726
|
return (0, import_react10.createElement)(
|
|
5518
5727
|
import_react10.Fragment,
|
|
5519
5728
|
null,
|
|
@@ -5539,11 +5748,100 @@ function ComposerPlus({ sessionId, useRoster, actions }) {
|
|
|
5539
5748
|
"data-leaving": leaving || void 0
|
|
5540
5749
|
},
|
|
5541
5750
|
(0, import_react10.createElement)("button", { type: "button", role: "menuitem", onClick: attach }, (0, import_react10.createElement)(PaperclipIcon), (0, import_react10.createElement)("span", { className: "bt-menu-label" }, t("Add files"))),
|
|
5751
|
+
canReference ? (0, import_react10.createElement)("button", { type: "button", role: "menuitem", onMouseDown: keepComposerFocus, onClick: reference }, (0, import_react10.createElement)(ChatsIcon), (0, import_react10.createElement)("span", { className: "bt-menu-label" }, t("Reference a chat"))) : null,
|
|
5542
5752
|
(0, import_react10.createElement)("button", { type: "button", role: "menuitem", onClick: schedule }, (0, import_react10.createElement)(ClockIcon), (0, import_react10.createElement)("span", { className: "bt-menu-label" }, t("Scheduled task")))
|
|
5543
5753
|
), document.body) : null
|
|
5544
5754
|
);
|
|
5545
5755
|
}
|
|
5546
5756
|
|
|
5757
|
+
// src/client/at-menu.js
|
|
5758
|
+
var BOTS_SOURCE = "ds-bot";
|
|
5759
|
+
var CHATS_SOURCE = "ds-bot-chats";
|
|
5760
|
+
var ROOM_ALL = ["all", "everyone", "所有人", "全体"];
|
|
5761
|
+
var OTHER_CHATS_MAX = 8;
|
|
5762
|
+
var conversationOf = (roster, sessionId) => {
|
|
5763
|
+
if (!roster.ready) return null;
|
|
5764
|
+
const room2 = roster.roomsById[sessionId];
|
|
5765
|
+
if (room2) return { room: room2 };
|
|
5766
|
+
const self2 = roster.byId[sessionId];
|
|
5767
|
+
return self2 ? { self: self2 } : null;
|
|
5768
|
+
};
|
|
5769
|
+
function mentionRows(roster, sessionId, query = "") {
|
|
5770
|
+
const here = conversationOf(roster, sessionId);
|
|
5771
|
+
if (here === null) return null;
|
|
5772
|
+
const bots = here.room ? here.room.members.map((id) => roster.byId[id]).filter(Boolean) : roster.bots.filter((bot) => bot.id !== here.self.id);
|
|
5773
|
+
const needle = query.trim().toLowerCase();
|
|
5774
|
+
const rows = bots.filter((bot) => bot.name.toLowerCase().includes(needle)).map((bot) => ({ bot, text: `@${bot.name} ` }));
|
|
5775
|
+
const everyone = t("Everyone");
|
|
5776
|
+
if (here.room && bots.length > 1 && [everyone, ...ROOM_ALL].some((word) => word.toLowerCase().startsWith(needle))) {
|
|
5777
|
+
rows.push({ room: here.room, text: `@${everyone} ` });
|
|
5778
|
+
}
|
|
5779
|
+
return rows;
|
|
5780
|
+
}
|
|
5781
|
+
function chatRows(roster, list, sessionId) {
|
|
5782
|
+
const here = conversationOf(roster, sessionId);
|
|
5783
|
+
if (here === null) return [];
|
|
5784
|
+
const byId = list?.byId ?? {};
|
|
5785
|
+
const bots = roster.bots.filter((bot) => bot.id !== here.self?.id).map((bot) => {
|
|
5786
|
+
const id = currentPart(bot);
|
|
5787
|
+
return { sessionId: id, label: t("Chat with {name}", { name: bot.name }), bot, updatedAt: byId[id]?.updatedAt };
|
|
5788
|
+
});
|
|
5789
|
+
const team = /* @__PURE__ */ new Set([
|
|
5790
|
+
...roster.bots.flatMap((bot) => [bot.id, currentPart(bot), ...bot.parts ?? []]),
|
|
5791
|
+
...roster.rooms.flatMap((room2) => [room2.id, ...room2.parts ?? []])
|
|
5792
|
+
]);
|
|
5793
|
+
const others = (list?.ids ?? []).map((id) => byId[id]).filter((row) => row !== void 0 && row.id !== sessionId && !team.has(row.id) && row.origin !== "subagent" && row.blank !== true).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)).slice(0, OTHER_CHATS_MAX).map((row) => ({ sessionId: row.id, label: row.displayTitle || row.title || row.id, updatedAt: row.updatedAt }));
|
|
5794
|
+
return [...bots, ...others];
|
|
5795
|
+
}
|
|
5796
|
+
function sessionMention(sessionId, label) {
|
|
5797
|
+
let binary = "";
|
|
5798
|
+
for (const byte of new TextEncoder().encode(JSON.stringify(sessionId))) binary += String.fromCharCode(byte);
|
|
5799
|
+
const payload = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
5800
|
+
return `@[${label.replace(/[\\\]]/g, (mark) => `\\${mark}`)}](dsh-session:${payload})`;
|
|
5801
|
+
}
|
|
5802
|
+
function atSources({ roster, sessions, launched, iconFor }) {
|
|
5803
|
+
const bots = {
|
|
5804
|
+
trigger: "@",
|
|
5805
|
+
name: BOTS_SOURCE,
|
|
5806
|
+
order: -20,
|
|
5807
|
+
showGroupTitle: false,
|
|
5808
|
+
async candidates(session, { query }) {
|
|
5809
|
+
return (mentionRows(roster.getSnapshot(), session.sessionId, query) ?? []).map((row) => ({
|
|
5810
|
+
name: row.bot ? row.bot.name : t("Everyone"),
|
|
5811
|
+
...row.bot?.role ? { description: row.bot.role } : {},
|
|
5812
|
+
...row.room ? { description: t("{count} members", { count: row.room.members.length }) } : {},
|
|
5813
|
+
icon: iconFor(row),
|
|
5814
|
+
section: t("Bots"),
|
|
5815
|
+
value: row.text
|
|
5816
|
+
}));
|
|
5817
|
+
},
|
|
5818
|
+
onPick: ({ candidate }) => candidate.value ? { text: candidate.value } : void 0
|
|
5819
|
+
};
|
|
5820
|
+
const chats = {
|
|
5821
|
+
trigger: "@",
|
|
5822
|
+
name: CHATS_SOURCE,
|
|
5823
|
+
order: -10,
|
|
5824
|
+
showGroupTitle: false,
|
|
5825
|
+
async candidates(session) {
|
|
5826
|
+
if (!launched(session.sessionId)) return [];
|
|
5827
|
+
return chatRows(roster.getSnapshot(), sessions.list.getSnapshot(), session.sessionId).map((row) => ({
|
|
5828
|
+
name: row.bot ? row.bot.name : row.label,
|
|
5829
|
+
...row.updatedAt ? { description: relativeShort(row.updatedAt) } : {},
|
|
5830
|
+
icon: row.bot ? iconFor(row) : "session",
|
|
5831
|
+
section: row.bot ? t("Bots") : t("Conversations"),
|
|
5832
|
+
value: JSON.stringify({ sessionId: row.sessionId, label: row.label })
|
|
5833
|
+
}));
|
|
5834
|
+
},
|
|
5835
|
+
onPick({ candidate }) {
|
|
5836
|
+
const { sessionId, label } = JSON.parse(candidate.value);
|
|
5837
|
+
const mention = sessionMention(sessionId, label);
|
|
5838
|
+
return { insert: { source: CHATS_SOURCE, ref: mention, label, appearance: "session", clipboardText: mention } };
|
|
5839
|
+
},
|
|
5840
|
+
codec: { clipboardText: (ref) => ref, serialize: (ref) => Promise.resolve(ref) }
|
|
5841
|
+
};
|
|
5842
|
+
return [bots, chats];
|
|
5843
|
+
}
|
|
5844
|
+
|
|
5547
5845
|
// src/client/speech.js
|
|
5548
5846
|
var VOICE_BUNDLE = "@deepseek-ai/dsh-experimental-voice-input-bundle";
|
|
5549
5847
|
var USABLE = /* @__PURE__ */ new Set(["ready", "standby", "waking"]);
|
|
@@ -7361,8 +7659,9 @@ function CreateBot({ roster, actions, start, brief = "", onBack, canGoBack, onCr
|
|
|
7361
7659
|
const fallbackName = freshBotName(roster, t("New Bot"));
|
|
7362
7660
|
const finalName = name.trim() || fallbackName;
|
|
7363
7661
|
const clash = roster.bots.some((bot) => bot.name.toLowerCase() === finalName.toLowerCase());
|
|
7662
|
+
const problem = nameProblem(finalName);
|
|
7364
7663
|
const create = async () => {
|
|
7365
|
-
if (busy || clash) return;
|
|
7664
|
+
if (busy || clash || problem) return;
|
|
7366
7665
|
setError("");
|
|
7367
7666
|
setBusy(true);
|
|
7368
7667
|
try {
|
|
@@ -7444,7 +7743,7 @@ function CreateBot({ roster, actions, start, brief = "", onBack, canGoBack, onCr
|
|
|
7444
7743
|
}
|
|
7445
7744
|
}
|
|
7446
7745
|
}),
|
|
7447
|
-
clash ? (0, import_react17.createElement)("span", { className: "bt-note bt-danger" }, t("A Bot named {name} already exists", { name: finalName })) : null
|
|
7746
|
+
problem ? (0, import_react17.createElement)("span", { className: "bt-note bt-danger" }, hostText(problem)) : clash ? (0, import_react17.createElement)("span", { className: "bt-note bt-danger" }, t("A Bot named {name} already exists", { name: finalName })) : null
|
|
7448
7747
|
),
|
|
7449
7748
|
(0, import_react17.createElement)(
|
|
7450
7749
|
"div",
|
|
@@ -7461,7 +7760,7 @@ function CreateBot({ roster, actions, start, brief = "", onBack, canGoBack, onCr
|
|
|
7461
7760
|
"div",
|
|
7462
7761
|
{ className: "bt-create-foot" },
|
|
7463
7762
|
(0, import_react17.createElement)("button", { type: "button", className: "bt-soft", onClick: onBack }, canGoBack ? t("Back") : t("Cancel")),
|
|
7464
|
-
(0, import_react17.createElement)("button", { type: "button", className: "bt-send bt-create-go", disabled: busy || clash, onClick: () => void create() }, busy ? t("Creating…") : t("Create"))
|
|
7763
|
+
(0, import_react17.createElement)("button", { type: "button", className: "bt-send bt-create-go", disabled: busy || clash || Boolean(problem), onClick: () => void create() }, busy ? t("Creating…") : t("Create"))
|
|
7465
7764
|
)
|
|
7466
7765
|
);
|
|
7467
7766
|
if (inline) return (0, import_react17.createElement)("div", { ref: frame, className: "bt-create-inline", role: "group", "aria-label": t("Create Bot") }, card);
|
|
@@ -8844,6 +9143,7 @@ function SpanPicker({ span, today, oldest, active, near, onPick, onClose }) {
|
|
|
8844
9143
|
);
|
|
8845
9144
|
}
|
|
8846
9145
|
function Heat({ view, keep, paint, index }) {
|
|
9146
|
+
const [hover, setHover] = (0, import_react22.useState)(null);
|
|
8847
9147
|
const today = hourMs(`${view.now.slice(0, 10)} 00`);
|
|
8848
9148
|
const first = mondayOf(today) - (HEAT_WEEKS - 1) * 7 * DAY_MS;
|
|
8849
9149
|
const from = dayKey(first);
|
|
@@ -8869,12 +9169,17 @@ function Heat({ view, keep, paint, index }) {
|
|
|
8869
9169
|
key: ms,
|
|
8870
9170
|
className: "bt-us-cell",
|
|
8871
9171
|
style: { "--w": week },
|
|
9172
|
+
"data-on": hover?.ms === ms ? "" : void 0,
|
|
8872
9173
|
"data-future": ms > today ? "" : void 0,
|
|
8873
9174
|
"data-today": ms === today ? "" : void 0,
|
|
8874
|
-
|
|
9175
|
+
onMouseEnter: ms > today ? void 0 : () => setHover({ ms, week, day })
|
|
8875
9176
|
}, level > 0 ? (0, import_react22.createElement)("i", { style: { background: soften(paint, levels[level - 1]) } }) : null));
|
|
8876
9177
|
}
|
|
8877
9178
|
}
|
|
9179
|
+
const tipSide = hover ? {
|
|
9180
|
+
...hover.week >= HEAT_WEEKS / 2 ? { right: `calc(${(HEAT_WEEKS - hover.week) / HEAT_WEEKS * 100}% + 8px)` } : { left: `calc(${(hover.week + 1) / HEAT_WEEKS * 100}% + 8px)` },
|
|
9181
|
+
...hover.day < 4 ? { top: `calc(${hover.day / 7 * 100}% - 2px)` } : { top: "auto", bottom: `calc(${(6 - hover.day) / 7 * 100}% - 2px)` }
|
|
9182
|
+
} : null;
|
|
8878
9183
|
const active = [...daily.values()].filter((value) => value > 0).length;
|
|
8879
9184
|
const summary = active === 1 ? t("Active on 1 day in the last year") : t("Active on {count} days in the last year", { count: active });
|
|
8880
9185
|
return (0, import_react22.createElement)(
|
|
@@ -8882,7 +9187,17 @@ function Heat({ view, keep, paint, index }) {
|
|
|
8882
9187
|
{ className: "bt-us-card bt-us-heat", style: { "--i": index } },
|
|
8883
9188
|
(0, import_react22.createElement)("div", { className: "bt-us-card-head" }, (0, import_react22.createElement)("h3", null, t("Activity")), (0, import_react22.createElement)("span", { className: "bt-us-head-note" }, summary)),
|
|
8884
9189
|
(0, import_react22.createElement)("div", { className: "bt-us-heat-months", "aria-hidden": true }, months),
|
|
8885
|
-
(0, import_react22.createElement)(
|
|
9190
|
+
(0, import_react22.createElement)(
|
|
9191
|
+
"div",
|
|
9192
|
+
{ className: "bt-us-heat-plot", onMouseLeave: () => setHover(null) },
|
|
9193
|
+
(0, import_react22.createElement)("div", { className: "bt-us-heat-grid", role: "img", "aria-label": summary }, cells),
|
|
9194
|
+
hover ? (0, import_react22.createElement)(
|
|
9195
|
+
"div",
|
|
9196
|
+
{ className: "bt-us-tip bt-us-heat-tip", style: tipSide },
|
|
9197
|
+
(0, import_react22.createElement)("div", { className: "bt-us-tip-title" }, dates({ month: "long", day: "numeric", weekday: "short" }).format(hover.ms)),
|
|
9198
|
+
(0, import_react22.createElement)("div", { className: "bt-us-tip-total" }, t("{count} tokens", { count: full(daily.get(dayKey(hover.ms)) ?? 0) }))
|
|
9199
|
+
) : null
|
|
9200
|
+
),
|
|
8886
9201
|
(0, import_react22.createElement)(
|
|
8887
9202
|
"div",
|
|
8888
9203
|
{ className: "bt-us-heat-scale", "aria-hidden": true },
|
|
@@ -8897,9 +9212,18 @@ function Skeleton2() {
|
|
|
8897
9212
|
"div",
|
|
8898
9213
|
{ className: "bt-us", "aria-busy": true },
|
|
8899
9214
|
(0, import_react22.createElement)("div", { className: "bt-us-skel", style: { height: 32, width: 320, borderRadius: 999 } }),
|
|
8900
|
-
(0, import_react22.createElement)(
|
|
8901
|
-
|
|
8902
|
-
|
|
9215
|
+
(0, import_react22.createElement)(
|
|
9216
|
+
"div",
|
|
9217
|
+
{ className: "bt-us-sheet bt-us-skel-sheet" },
|
|
9218
|
+
(0, import_react22.createElement)("div", { className: "bt-us-skel", style: { height: 94 } }),
|
|
9219
|
+
(0, import_react22.createElement)("div", { className: "bt-us-skel", style: { height: 318 } }),
|
|
9220
|
+
(0, import_react22.createElement)(
|
|
9221
|
+
"div",
|
|
9222
|
+
{ className: "bt-us-pair" },
|
|
9223
|
+
(0, import_react22.createElement)("div", { className: "bt-us-skel", style: { height: 200 } }),
|
|
9224
|
+
(0, import_react22.createElement)("div", { className: "bt-us-skel", style: { height: 200 } })
|
|
9225
|
+
)
|
|
9226
|
+
)
|
|
8903
9227
|
);
|
|
8904
9228
|
}
|
|
8905
9229
|
var focusOf = (id) => typeof id !== "string" ? null : id.startsWith("model:") ? { kind: "model", key: id.slice("model:".length) } : id.startsWith("owner:") ? { kind: "owner", key: id.slice("owner:".length) } : null;
|
|
@@ -9069,19 +9393,23 @@ function UsagePage({ roster, actions, focusId }) {
|
|
|
9069
9393
|
const owners = ranked(part.owners);
|
|
9070
9394
|
body = [
|
|
9071
9395
|
(0, import_react22.createElement)(Profile, { key: "profile", face, range, share: all.total.total > 0 ? whole / all.total.total : 0 }),
|
|
9072
|
-
(0, import_react22.createElement)(Stats, { key: "stats", range, total: part.total, previous: part.previous }),
|
|
9073
|
-
(0, import_react22.createElement)(Chart, { key: "chart", range, columns, series, tipSeries, onPick: pick2 }),
|
|
9074
9396
|
(0, import_react22.createElement)(
|
|
9075
9397
|
"div",
|
|
9076
|
-
{ key: "
|
|
9077
|
-
|
|
9398
|
+
{ key: "sheet", className: "bt-us-sheet" },
|
|
9399
|
+
(0, import_react22.createElement)(Stats, { key: "stats", range, total: part.total, previous: part.previous }),
|
|
9400
|
+
(0, import_react22.createElement)(Chart, { key: "chart", range, columns, series, tipSeries, onPick: pick2 }),
|
|
9078
9401
|
(0, import_react22.createElement)(
|
|
9079
|
-
|
|
9080
|
-
{
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9402
|
+
"div",
|
|
9403
|
+
{ key: "pair", className: "bt-us-pair" },
|
|
9404
|
+
ownerAt >= 0 ? (0, import_react22.createElement)(ListCard, { title: t("Models"), count: usedModels.length, index: 2 }, usedModels.length > 0 ? usedModels.map(modelRow) : nothing) : (0, import_react22.createElement)(ListCard, { title: t("Who used it"), count: owners.length, index: 2 }, owners.length > 0 ? owners.map((entry, index) => ownerRow(entry, index)) : nothing),
|
|
9405
|
+
(0, import_react22.createElement)(
|
|
9406
|
+
ListCard,
|
|
9407
|
+
{ title: t("What for"), index: 3 },
|
|
9408
|
+
kinds.length > 0 ? kinds.map(([at, sum], index) => (0, import_react22.createElement)(Row2, { key: at, name: kindLabel(view.kinds[at], ownerAt >= 0 && isBot(ownerAt)), sum, whole, index, fill: soften(face.paint, 0.8) })) : nothing
|
|
9409
|
+
)
|
|
9410
|
+
),
|
|
9411
|
+
(0, import_react22.createElement)(Heat, { key: "heat", view, keep, paint: face.paint, index: 4 })
|
|
9412
|
+
)
|
|
9085
9413
|
];
|
|
9086
9414
|
} else {
|
|
9087
9415
|
const owners = ranked(all.owners);
|
|
@@ -9089,22 +9417,26 @@ function UsagePage({ roster, actions, focusId }) {
|
|
|
9089
9417
|
const rest = owners.filter(([at]) => !isBot(at));
|
|
9090
9418
|
const usedModels = ranked(all.models);
|
|
9091
9419
|
body = [
|
|
9092
|
-
(0, import_react22.createElement)(Stats, { key: "stats", range, total: all.total, previous: all.previous }),
|
|
9093
|
-
(0, import_react22.createElement)(Chart, { key: "chart", range, columns, series, tipSeries, by, onBy: setBy, onPick: pick2 }),
|
|
9094
9420
|
(0, import_react22.createElement)(
|
|
9095
9421
|
"div",
|
|
9096
|
-
{ key: "
|
|
9422
|
+
{ key: "sheet", className: "bt-us-sheet" },
|
|
9423
|
+
(0, import_react22.createElement)(Stats, { key: "stats", range, total: all.total, previous: all.previous }),
|
|
9424
|
+
(0, import_react22.createElement)(Chart, { key: "chart", range, columns, series, tipSeries, by, onBy: setBy, onPick: pick2 }),
|
|
9097
9425
|
(0, import_react22.createElement)(
|
|
9098
|
-
|
|
9099
|
-
{
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9426
|
+
"div",
|
|
9427
|
+
{ key: "pair", className: "bt-us-pair" },
|
|
9428
|
+
(0, import_react22.createElement)(
|
|
9429
|
+
ListCard,
|
|
9430
|
+
{ title: t("Bots"), count: bots.length, index: 2 },
|
|
9431
|
+
owners.length > 0 ? null : nothing,
|
|
9432
|
+
bots.map((entry, index) => ownerRow(entry, index)),
|
|
9433
|
+
bots.length > 0 && rest.length > 0 ? (0, import_react22.createElement)("div", { className: "bt-us-split", role: "separator" }) : null,
|
|
9434
|
+
rest.map((entry, index) => ownerRow(entry, bots.length + index))
|
|
9435
|
+
),
|
|
9436
|
+
(0, import_react22.createElement)(ListCard, { title: t("Models"), count: usedModels.length, index: 3 }, usedModels.length > 0 ? usedModels.map(modelRow) : nothing)
|
|
9104
9437
|
),
|
|
9105
|
-
(0, import_react22.createElement)(
|
|
9106
|
-
)
|
|
9107
|
-
(0, import_react22.createElement)(Heat, { key: "heat", view, keep, paint: "var(--bt-accent)", index: 4 })
|
|
9438
|
+
(0, import_react22.createElement)(Heat, { key: "heat", view, keep, paint: "var(--bt-accent)", index: 4 })
|
|
9439
|
+
)
|
|
9108
9440
|
];
|
|
9109
9441
|
}
|
|
9110
9442
|
return (0, import_react22.createElement)(
|
|
@@ -9165,6 +9497,10 @@ var USAGE_CSS = `
|
|
|
9165
9497
|
.bt-us-tabs>.bt-thumb{top:3px;bottom:3px;border-radius:999px;background:var(--bt-card);box-shadow:0 1px 3px rgba(0,0,0,.08),0 0 0 .5px var(--bt-line-2)}
|
|
9166
9498
|
.bt-us-note{display:inline-flex;align-items:center;gap:8px;font-size:12px;line-height:16px;color:var(--bt-ink-3)}
|
|
9167
9499
|
.bt-us-note::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--bt-accent);animation:bt-us-pulse 1.4s ease-in-out infinite}
|
|
9500
|
+
.bt-us-sheet{display:flex;flex-direction:column;gap:4px}
|
|
9501
|
+
.bt-us-sheet>.bt-us-stats,.bt-us-sheet>.bt-us-card,.bt-us-sheet>.bt-us-pair>.bt-us-card{border-radius:0}
|
|
9502
|
+
.bt-us-sheet>:first-child{border-radius:16px 16px 0 0}
|
|
9503
|
+
.bt-us-sheet>:last-child{border-radius:0 0 16px 16px}
|
|
9168
9504
|
.bt-us-stats{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;border-radius:16px;border:1px solid var(--bt-line);background:var(--bt-card);animation:bt-us-in .45s var(--bt-us-ease) backwards}
|
|
9169
9505
|
.bt-us-stat{display:flex;flex-direction:column;justify-content:flex-end;gap:6px;min-width:0;padding:16px 18px}
|
|
9170
9506
|
.bt-us-stat+.bt-us-stat{border-left:1px solid var(--bt-line)}
|
|
@@ -9204,7 +9540,7 @@ var USAGE_CSS = `
|
|
|
9204
9540
|
button.bt-us-key{cursor:pointer;transition:background-color .12s ease,color .12s ease}
|
|
9205
9541
|
button.bt-us-key:hover{background:var(--bt-active);color:var(--bt-ink)}
|
|
9206
9542
|
.bt-us-dot{flex:none;width:8px;height:8px;border-radius:50%}
|
|
9207
|
-
.bt-us-pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:
|
|
9543
|
+
.bt-us-pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:4px}
|
|
9208
9544
|
.bt-us-rows{display:flex;flex-direction:column;margin:0 -8px}
|
|
9209
9545
|
.bt-us-row{display:flex;align-items:center;gap:12px;width:100%;box-sizing:border-box;padding:8px;border:0;border-radius:12px;background:none;color:var(--bt-ink);font:inherit;text-align:left;animation:bt-us-in .4s var(--bt-us-ease) backwards;animation-delay:calc(var(--i,0) * 35ms + 90ms)}
|
|
9210
9546
|
button.bt-us-row{cursor:pointer;transition:background-color .12s ease}
|
|
@@ -9238,7 +9574,10 @@ button.bt-us-row:hover{background:var(--bt-hover)}
|
|
|
9238
9574
|
.bt-us-heat-months,.bt-us-heat-grid{display:grid;grid-template-columns:repeat(${HEAT_WEEKS},1fr);gap:3px}
|
|
9239
9575
|
.bt-us-heat-months{height:14px;font-size:10px;line-height:14px;color:var(--bt-ink-3)}
|
|
9240
9576
|
.bt-us-heat-months span{white-space:nowrap}
|
|
9577
|
+
.bt-us-heat-plot{position:relative}
|
|
9241
9578
|
.bt-us-heat-grid{grid-template-rows:repeat(7,auto);grid-auto-flow:column}
|
|
9579
|
+
.bt-us-heat-grid .bt-us-cell[data-on]{outline:1.5px solid var(--bt-ink-2);outline-offset:1px}
|
|
9580
|
+
.bt-us-heat-tip{min-width:0;white-space:nowrap}
|
|
9242
9581
|
.bt-us-cell{position:relative;aspect-ratio:1;border-radius:3px;background:var(--bt-hover);overflow:hidden}
|
|
9243
9582
|
.bt-us-heat-grid .bt-us-cell{animation:bt-us-cell .5s var(--bt-us-ease) backwards;animation-delay:calc(var(--w,0) * 7ms + 120ms)}
|
|
9244
9583
|
.bt-us-cell>i{position:absolute;inset:0}
|
|
@@ -9247,6 +9586,10 @@ button.bt-us-row:hover{background:var(--bt-hover)}
|
|
|
9247
9586
|
.bt-us-heat-scale{display:flex;align-items:center;justify-content:flex-end;gap:4px;font-size:11px;line-height:14px;color:var(--bt-ink-3)}
|
|
9248
9587
|
.bt-us-heat-scale .bt-us-cell{width:10px}
|
|
9249
9588
|
.bt-us-skel{border-radius:16px;background:linear-gradient(90deg,var(--bt-hover) 0%,var(--bt-active) 50%,var(--bt-hover) 100%);background-size:200% 100%;animation:bt-us-shine 1.4s ease-in-out infinite}
|
|
9589
|
+
.bt-us-skel-sheet>.bt-us-skel:first-child{border-radius:16px 16px 0 0}
|
|
9590
|
+
.bt-us-skel-sheet>.bt-us-skel:not(:first-child){border-radius:0}
|
|
9591
|
+
.bt-us-skel-sheet>.bt-us-pair>.bt-us-skel:first-child{border-radius:0 0 0 16px}
|
|
9592
|
+
.bt-us-skel-sheet>.bt-us-pair>.bt-us-skel:last-child{border-radius:0 0 16px 0}
|
|
9250
9593
|
.bt-us-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:72px 16px;text-align:center;font-size:13px;line-height:18px;color:var(--bt-ink-3)}
|
|
9251
9594
|
.bt-us-empty b{margin-top:6px;font-size:15px;font-weight:600;color:var(--bt-ink)}
|
|
9252
9595
|
@keyframes bt-us-in{from{opacity:0;transform:translateY(6px)}}
|
|
@@ -9369,8 +9712,8 @@ var copy = (text) => navigator.clipboard === void 0 ? Promise.resolve(false) : n
|
|
|
9369
9712
|
function whenText(time) {
|
|
9370
9713
|
const date = new Date(time);
|
|
9371
9714
|
const today = (/* @__PURE__ */ new Date()).toDateString() === date.toDateString();
|
|
9372
|
-
const
|
|
9373
|
-
return today ?
|
|
9715
|
+
const clock4 = date.toLocaleTimeString(dateLocale(), { hour: "2-digit", minute: "2-digit" });
|
|
9716
|
+
return today ? clock4 : `${date.toLocaleDateString(dateLocale(), { month: "short", day: "numeric" })} ${clock4}`;
|
|
9374
9717
|
}
|
|
9375
9718
|
function entrySummary(entry) {
|
|
9376
9719
|
if (entry.source === "turn") return failureLabel(entry.code, entry.text);
|
|
@@ -10143,8 +10486,142 @@ function migrateStorage(storage = globalThis.localStorage) {
|
|
|
10143
10486
|
}
|
|
10144
10487
|
}
|
|
10145
10488
|
|
|
10146
|
-
// src/client/
|
|
10489
|
+
// src/client/member-status.js
|
|
10147
10490
|
var import_react28 = require("react");
|
|
10491
|
+
var NO_WORK = { bots: {}, approvals: [] };
|
|
10492
|
+
function memberWork(room2, work = NO_WORK) {
|
|
10493
|
+
return room2.members.map((id) => {
|
|
10494
|
+
const approvals = (work.approvals ?? []).filter((entry) => entry.botId === id);
|
|
10495
|
+
const doing = work.bots?.[id];
|
|
10496
|
+
return { id, approvals, doing, state: approvals.length > 0 ? "ask" : doing ? "work" : "idle" };
|
|
10497
|
+
});
|
|
10498
|
+
}
|
|
10499
|
+
function busyMembers(room2, work, roster) {
|
|
10500
|
+
const busy = memberWork(room2, work).filter((entry) => entry.state !== "idle" && roster.byId[entry.id]);
|
|
10501
|
+
return [...busy.filter((entry) => entry.state === "ask"), ...busy.filter((entry) => entry.state === "work")].map((entry) => ({ ...entry, bot: roster.byId[entry.id] }));
|
|
10502
|
+
}
|
|
10503
|
+
function summaryText(busy) {
|
|
10504
|
+
const asks = busy.filter((entry) => entry.state === "ask");
|
|
10505
|
+
const workers = busy.filter((entry) => entry.state === "work");
|
|
10506
|
+
const names = asks.slice(0, 2).map((entry) => entry.bot.name);
|
|
10507
|
+
const waiting = asks.length === 0 ? "" : asks.length === 1 ? t("{name} is waiting for your approval", { name: names[0] }) : asks.length === 2 ? t("{first} and {second} are waiting for your approval", { first: names[0], second: names[1] }) : t("{first}, {second} and {count} more are waiting for your approval", { first: names[0], second: names[1], count: asks.length - 2, total: asks.length });
|
|
10508
|
+
const first = workers[0];
|
|
10509
|
+
const doing = first ? `${first.bot.name} · ${workPhrase(first.doing.tool)}…${workers.length > 1 ? ` +${workers.length - 1}` : ""}` : "";
|
|
10510
|
+
return { waiting, doing };
|
|
10511
|
+
}
|
|
10512
|
+
function Face({ entry, size }) {
|
|
10513
|
+
return (0, import_react28.createElement)(
|
|
10514
|
+
"span",
|
|
10515
|
+
{ className: "bt-mw-face", "data-ask": entry.state === "ask" || void 0 },
|
|
10516
|
+
(0, import_react28.createElement)(BotMark, { bot: entry.bot, size, state: entry.state === "ask" ? "alert" : toolState(entry.doing?.tool ?? "") })
|
|
10517
|
+
);
|
|
10518
|
+
}
|
|
10519
|
+
function MemberDock({ sessionId, useRoster, useWork, actions }) {
|
|
10520
|
+
const roster = useRoster((value) => value);
|
|
10521
|
+
const work = useWork((value) => value);
|
|
10522
|
+
const room2 = roster.roomsById[sessionId];
|
|
10523
|
+
if (!room2) return null;
|
|
10524
|
+
const busy = busyMembers(room2, work, roster);
|
|
10525
|
+
if (busy.length === 0) return null;
|
|
10526
|
+
const { waiting, doing } = summaryText(busy);
|
|
10527
|
+
return (0, import_react28.createElement)(
|
|
10528
|
+
"div",
|
|
10529
|
+
{ className: "bt-event bt-mw-dock" },
|
|
10530
|
+
(0, import_react28.createElement)(
|
|
10531
|
+
"button",
|
|
10532
|
+
{ type: "button", className: "bt-event-whole bt-mw-line", "aria-label": [waiting, doing].filter(Boolean).join(" · "), onClick: () => actions.showDetails(sessionId) },
|
|
10533
|
+
(0, import_react28.createElement)("span", { className: "bt-mw-stack", "aria-hidden": true }, busy.slice(0, 4).map((entry) => (0, import_react28.createElement)(Face, { key: entry.id, entry, size: 16 }))),
|
|
10534
|
+
waiting ? (0, import_react28.createElement)("span", { className: "bt-mw-ask" }, waiting) : null,
|
|
10535
|
+
waiting && doing ? (0, import_react28.createElement)("span", { "aria-hidden": true }, "·") : null,
|
|
10536
|
+
doing ? (0, import_react28.createElement)("span", { className: "bt-event-name" }, (0, import_react28.createElement)(Shimmer, null, doing)) : null
|
|
10537
|
+
)
|
|
10538
|
+
);
|
|
10539
|
+
}
|
|
10540
|
+
function useNow2(active) {
|
|
10541
|
+
const [now, setNow] = (0, import_react28.useState)(Date.now);
|
|
10542
|
+
(0, import_react28.useEffect)(() => {
|
|
10543
|
+
if (!active) return void 0;
|
|
10544
|
+
const timer = setInterval(() => setNow(Date.now()), 3e4);
|
|
10545
|
+
return () => clearInterval(timer);
|
|
10546
|
+
}, [active]);
|
|
10547
|
+
return now;
|
|
10548
|
+
}
|
|
10549
|
+
var since = (now, at) => typeof at === "number" && now - at >= 6e4 ? ` · ${elapsedLabel(now - at)}` : "";
|
|
10550
|
+
function ApprovalCard({ approval, bot, actions }) {
|
|
10551
|
+
const [busy, setBusy] = (0, import_react28.useState)(false);
|
|
10552
|
+
const answer = (choice) => {
|
|
10553
|
+
setBusy(true);
|
|
10554
|
+
void Promise.resolve(actions.answerApproval(approval.id, choice)).catch((error) => actions.reportError(error, "approval")).finally(() => setBusy(false));
|
|
10555
|
+
};
|
|
10556
|
+
return (0, import_react28.createElement)(
|
|
10557
|
+
"div",
|
|
10558
|
+
{ className: "bt-mw-card", role: "group", "aria-label": t("{name} is waiting for your approval", { name: bot.name }) },
|
|
10559
|
+
(0, import_react28.createElement)("div", { className: "bt-mw-tool" }, approval.tool),
|
|
10560
|
+
approval.detail?.text ? (0, import_react28.createElement)("div", { className: "bt-mw-text" }, approval.detail.text) : null,
|
|
10561
|
+
approval.reason ? (0, import_react28.createElement)("div", { className: "bt-mw-reason" }, approval.reason) : null,
|
|
10562
|
+
approval.detail?.code ? (0, import_react28.createElement)("code", { className: "bt-mw-code", title: approval.detail.code }, approval.detail.code) : null,
|
|
10563
|
+
approval.answerable ? (0, import_react28.createElement)(
|
|
10564
|
+
"div",
|
|
10565
|
+
{ className: "bt-mw-actions" },
|
|
10566
|
+
(0, import_react28.createElement)("button", { type: "button", className: "bt-q-btn", disabled: busy, onClick: () => answer("reject") }, t("Reject")),
|
|
10567
|
+
(0, import_react28.createElement)("button", { type: "button", className: "bt-q-submit", disabled: busy, onClick: () => answer("allow") }, t("Allow once"))
|
|
10568
|
+
) : (0, import_react28.createElement)(
|
|
10569
|
+
"div",
|
|
10570
|
+
{ className: "bt-mw-actions" },
|
|
10571
|
+
(0, import_react28.createElement)("button", { type: "button", className: "bt-q-btn", onClick: () => actions.openSession(bot.id) }, t("Open {name}'s chat", { name: bot.name }))
|
|
10572
|
+
)
|
|
10573
|
+
);
|
|
10574
|
+
}
|
|
10575
|
+
function MemberWork({ entry, bot, room: room2, roster, actions, now }) {
|
|
10576
|
+
if (entry.state === "ask") {
|
|
10577
|
+
return (0, import_react28.createElement)(
|
|
10578
|
+
"div",
|
|
10579
|
+
{ className: "bt-mw-member" },
|
|
10580
|
+
(0, import_react28.createElement)("div", { className: "bt-mw-state bt-mw-ask" }, `${t("Waiting for your approval")}${since(now, entry.approvals[0].at)}`),
|
|
10581
|
+
entry.approvals.map((approval) => (0, import_react28.createElement)(ApprovalCard, { key: approval.id, approval, bot, actions }))
|
|
10582
|
+
);
|
|
10583
|
+
}
|
|
10584
|
+
if (entry.state !== "work") return null;
|
|
10585
|
+
const where = entry.doing.roomId === room2.id ? "" : entry.doing.roomId ? t("in {name}", { name: roster.roomsById[entry.doing.roomId]?.name ?? t("a group chat") }) : t("in its own chat");
|
|
10586
|
+
return (0, import_react28.createElement)(
|
|
10587
|
+
"div",
|
|
10588
|
+
{ className: "bt-mw-member" },
|
|
10589
|
+
(0, import_react28.createElement)("div", { className: "bt-mw-state" }, (0, import_react28.createElement)(Shimmer, null, `${workPhrase(entry.doing.tool)}…`), where ? ` ${where}` : "", since(now, entry.doing.since))
|
|
10590
|
+
);
|
|
10591
|
+
}
|
|
10592
|
+
function useMemberWork(room2, useWork) {
|
|
10593
|
+
const work = useWork?.((value) => value) ?? NO_WORK;
|
|
10594
|
+
const entries2 = room2 ? memberWork(room2, work) : [];
|
|
10595
|
+
const now = useNow2(entries2.some((entry) => entry.state !== "idle"));
|
|
10596
|
+
return { entries: entries2, now };
|
|
10597
|
+
}
|
|
10598
|
+
var MEMBER_CSS = `
|
|
10599
|
+
.bt-event.bt-mw-dock{margin:0 0 6px;pointer-events:none}
|
|
10600
|
+
.bt-mw-dock>button{pointer-events:auto;animation:bt-mw-in .32s cubic-bezier(.23,1,.32,1) backwards}
|
|
10601
|
+
.bt-mw-line{gap:6px!important}
|
|
10602
|
+
.bt-mw-stack{display:inline-flex;align-items:center;flex:none;line-height:0}
|
|
10603
|
+
.bt-mw-stack>*+*{margin-left:-4px}
|
|
10604
|
+
.bt-mw-face{position:relative;display:inline-flex;flex:none;border-radius:50%}
|
|
10605
|
+
.bt-mw-stack .bt-mw-face{box-shadow:0 0 0 1.5px var(--bt-main,#fff)}
|
|
10606
|
+
.bt-mw-face[data-ask]::after{content:"";position:absolute;right:-2px;bottom:-2px;width:7px;height:7px;border-radius:50%;background:#e3a008;box-shadow:0 0 0 1.5px var(--bt-main,#fff)}
|
|
10607
|
+
.bt-mw-ask{color:#b7791f;flex:none;white-space:nowrap}
|
|
10608
|
+
body[data-ds-dark-theme] .bt-mw-ask{color:#f0b84a}
|
|
10609
|
+
.bt-mw-member{padding:0 6px 8px 42px;display:flex;flex-direction:column;gap:6px}
|
|
10610
|
+
.bt-mw-state{font-size:12px;line-height:16px;color:var(--bt-ink-2);margin-top:-4px}
|
|
10611
|
+
.bt-mw-card{display:flex;flex-direction:column;gap:4px;padding:10px 12px;border-radius:12px;background:var(--bt-hover);font-size:12px;line-height:17px;color:var(--bt-ink-2)}
|
|
10612
|
+
.bt-mw-tool{font-weight:600;color:var(--bt-ink)}
|
|
10613
|
+
.bt-mw-text{color:var(--bt-ink);overflow-wrap:anywhere}
|
|
10614
|
+
.bt-mw-reason{overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
|
|
10615
|
+
.bt-mw-code{display:block;font:11px/16px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--bt-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
10616
|
+
.bt-mw-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:4px}
|
|
10617
|
+
.bt-mw-actions>button{height:28px;padding:0 12px;font-size:12px}
|
|
10618
|
+
.bt-mw-actions>button:disabled{opacity:.5;cursor:default}
|
|
10619
|
+
@keyframes bt-mw-in{from{opacity:0;transform:translateY(4px)}}
|
|
10620
|
+
@media (prefers-reduced-motion:reduce){.bt-mw-dock>button{animation:none}}
|
|
10621
|
+
`;
|
|
10622
|
+
|
|
10623
|
+
// src/client/details-drawer.js
|
|
10624
|
+
var import_react29 = require("react");
|
|
10148
10625
|
function transcriptOf(list, id, name, hiddenTurns = []) {
|
|
10149
10626
|
const outline = list.projectionsBySession?.[id]?.values?.turnOutline ?? list.byId[id]?.projectionValues?.turnOutline;
|
|
10150
10627
|
if (!Array.isArray(outline)) return "";
|
|
@@ -10169,10 +10646,10 @@ var EMPTY_TABS = { ids: [], active: null };
|
|
|
10169
10646
|
var PERMISSION_LABELS = { "read-only": "Read only", "workspace-write": "Workspace", "danger-full-access": "Full access" };
|
|
10170
10647
|
var DANGER = /* @__PURE__ */ new Set(["danger-full-access"]);
|
|
10171
10648
|
function PermissionRow({ bot, roster, actions, run }) {
|
|
10172
|
-
const [pending, setPending] = (0,
|
|
10649
|
+
const [pending, setPending] = (0, import_react29.useState)(null);
|
|
10173
10650
|
const catalog = roster.permissions;
|
|
10174
10651
|
const saved = bot.permission ?? catalog?.defaultPreset;
|
|
10175
|
-
(0,
|
|
10652
|
+
(0, import_react29.useEffect)(() => {
|
|
10176
10653
|
if (pending === saved) setPending(null);
|
|
10177
10654
|
}, [pending, saved]);
|
|
10178
10655
|
if (!catalog?.options?.length) return null;
|
|
@@ -10182,14 +10659,14 @@ function PermissionRow({ bot, roster, actions, run }) {
|
|
|
10182
10659
|
setPending(value);
|
|
10183
10660
|
void run(actions.updateBot(bot.id, { permission: value })).catch(() => setPending(null));
|
|
10184
10661
|
};
|
|
10185
|
-
return (0,
|
|
10662
|
+
return (0, import_react29.createElement)(
|
|
10186
10663
|
"div",
|
|
10187
10664
|
{ className: "bt-drawer-permission" },
|
|
10188
|
-
(0,
|
|
10189
|
-
(0,
|
|
10665
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Permissions")),
|
|
10666
|
+
(0, import_react29.createElement)(
|
|
10190
10667
|
Segmented,
|
|
10191
10668
|
{ label: t("Permissions"), value: current },
|
|
10192
|
-
catalog.options.map((option) => (0,
|
|
10669
|
+
catalog.options.map((option) => (0, import_react29.createElement)("button", {
|
|
10193
10670
|
key: option.value,
|
|
10194
10671
|
type: "button",
|
|
10195
10672
|
"aria-pressed": option.value === current,
|
|
@@ -10203,42 +10680,42 @@ function PermissionRow({ bot, roster, actions, run }) {
|
|
|
10203
10680
|
function BrowserCards({ bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) {
|
|
10204
10681
|
const state = useBrowserTabs((map) => map[bot.id] ?? EMPTY_TABS);
|
|
10205
10682
|
const pages = useBrowserPages((map) => map);
|
|
10206
|
-
(0,
|
|
10683
|
+
(0, import_react29.useEffect)(() => {
|
|
10207
10684
|
actions.browserEnsure?.(bot.id);
|
|
10208
10685
|
}, [bot.id]);
|
|
10209
10686
|
if (roster.browser !== true || !actions.browserShow) {
|
|
10210
|
-
return (0,
|
|
10687
|
+
return (0, import_react29.createElement)("div", { className: "bt-muted" }, t("The browser is in the DSH Desktop app."));
|
|
10211
10688
|
}
|
|
10212
10689
|
if (state.ids.length === 0) {
|
|
10213
|
-
return (0,
|
|
10690
|
+
return (0, import_react29.createElement)(
|
|
10214
10691
|
"div",
|
|
10215
10692
|
{ className: "bt-browser-empty-mark" },
|
|
10216
|
-
(0,
|
|
10217
|
-
(0,
|
|
10218
|
-
(0,
|
|
10219
|
-
(0,
|
|
10693
|
+
(0, import_react29.createElement)(BotMark, { bot, size: 40, state: "idle", live: true }),
|
|
10694
|
+
(0, import_react29.createElement)("div", { className: "bt-browser-empty-title" }, t("{name}'s browser", { name: bot.name })),
|
|
10695
|
+
(0, import_react29.createElement)("div", { className: "bt-muted" }, t("Open a page here and {name} can read it with you. Logins and cookies stay with {name}; other Bots never see them.", { name: bot.name })),
|
|
10696
|
+
(0, import_react29.createElement)("button", { type: "button", className: "bt-browser-retry", onClick: () => actions.openBrowser(sessionId) }, t("Open browser"))
|
|
10220
10697
|
);
|
|
10221
10698
|
}
|
|
10222
|
-
return (0,
|
|
10699
|
+
return (0, import_react29.createElement)(
|
|
10223
10700
|
"div",
|
|
10224
10701
|
{ className: "bt-bcards" },
|
|
10225
10702
|
state.ids.map((tabId, index) => {
|
|
10226
10703
|
const page = pages[tabId] ?? {};
|
|
10227
10704
|
const label = page.title || shortUrl(page.url) || t("New tab");
|
|
10228
|
-
return (0,
|
|
10705
|
+
return (0, import_react29.createElement)(
|
|
10229
10706
|
"div",
|
|
10230
10707
|
{ key: tabId, className: "bt-bcard", "data-active": state.active === tabId || void 0, style: { animationDelay: `${index * 30}ms` } },
|
|
10231
|
-
(0,
|
|
10708
|
+
(0, import_react29.createElement)(
|
|
10232
10709
|
"button",
|
|
10233
10710
|
{ type: "button", className: "bt-bcard-shot", "aria-label": t("Open {title}", { title: label }), title: page.url, onClick: () => actions.browserPick(sessionId, tabId) },
|
|
10234
|
-
page.excerpt ? (0,
|
|
10711
|
+
page.excerpt ? (0, import_react29.createElement)(
|
|
10235
10712
|
"span",
|
|
10236
10713
|
{ className: "bt-bcard-preview" },
|
|
10237
|
-
(0,
|
|
10238
|
-
(0,
|
|
10239
|
-
) : (0,
|
|
10714
|
+
(0, import_react29.createElement)("span", { className: "bt-bcard-ptitle" }, label),
|
|
10715
|
+
(0, import_react29.createElement)("span", { className: "bt-bcard-excerpt" }, page.excerpt)
|
|
10716
|
+
) : (0, import_react29.createElement)("span", { className: "bt-bcard-blank" }, (0, import_react29.createElement)(GlobeIcon), (0, import_react29.createElement)("span", null, label))
|
|
10240
10717
|
),
|
|
10241
|
-
(0,
|
|
10718
|
+
(0, import_react29.createElement)("button", {
|
|
10242
10719
|
type: "button",
|
|
10243
10720
|
className: "bt-bcard-x",
|
|
10244
10721
|
"aria-label": t("Close tab"),
|
|
@@ -10247,78 +10724,87 @@ function BrowserCards({ bot, sessionId, roster, actions, useBrowserTabs, useBrow
|
|
|
10247
10724
|
void actions.browserCloseTab(bot.id, tabId);
|
|
10248
10725
|
}
|
|
10249
10726
|
}, "×"),
|
|
10250
|
-
(0,
|
|
10727
|
+
(0, import_react29.createElement)(
|
|
10251
10728
|
"div",
|
|
10252
10729
|
{ className: "bt-bcard-row" },
|
|
10253
|
-
(0,
|
|
10254
|
-
(0,
|
|
10255
|
-
page.url ? (0,
|
|
10730
|
+
(0, import_react29.createElement)(Favicon, { src: page.favicon, className: "bt-bcard-fav" }),
|
|
10731
|
+
(0, import_react29.createElement)("span", { className: "bt-bcard-title" }, label),
|
|
10732
|
+
page.url ? (0, import_react29.createElement)("span", { className: "bt-bcard-host" }, hostOf(page.url)) : null
|
|
10256
10733
|
)
|
|
10257
10734
|
);
|
|
10258
10735
|
}),
|
|
10259
|
-
(0,
|
|
10736
|
+
(0, import_react29.createElement)("button", { type: "button", className: "bt-bcard-new", style: { animationDelay: `${state.ids.length * 30}ms` }, onClick: () => actions.browserNewTab(sessionId) }, (0, import_react29.createElement)(PlusIcon), t("New tab"))
|
|
10260
10737
|
);
|
|
10261
10738
|
}
|
|
10262
10739
|
function About({ text, title = "About" }) {
|
|
10263
|
-
const [open, setOpen] = (0,
|
|
10740
|
+
const [open, setOpen] = (0, import_react29.useState)(false);
|
|
10264
10741
|
const long = text.split("\n").length > 5 || text.length > 240;
|
|
10265
|
-
const body = (0,
|
|
10742
|
+
const body = (0, import_react29.useRef)(null);
|
|
10266
10743
|
useMorph(body, open);
|
|
10267
|
-
return (0,
|
|
10744
|
+
return (0, import_react29.createElement)(
|
|
10268
10745
|
"div",
|
|
10269
10746
|
null,
|
|
10270
|
-
(0,
|
|
10271
|
-
(0,
|
|
10272
|
-
long ? (0,
|
|
10747
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t(title)),
|
|
10748
|
+
(0, import_react29.createElement)("div", { ref: body, className: "bt-about", "data-open": open || void 0 }, text),
|
|
10749
|
+
long ? (0, import_react29.createElement)("button", { type: "button", className: "bt-more", onClick: () => setOpen((value) => !value) }, open ? t("Show less") : t("Show more")) : null
|
|
10273
10750
|
);
|
|
10274
10751
|
}
|
|
10275
|
-
function GroupOverview({ room: room2, roster, actions, usage, openSettings }) {
|
|
10752
|
+
function GroupOverview({ room: room2, roster, actions, usage, useWork, openSettings }) {
|
|
10276
10753
|
const members = room2.members.map((id) => roster.byId[id]).filter(Boolean);
|
|
10754
|
+
const { entries: entries2, now } = useMemberWork(room2, useWork);
|
|
10277
10755
|
return [
|
|
10278
|
-
room2.notice ? (0,
|
|
10279
|
-
(0,
|
|
10756
|
+
room2.notice ? (0, import_react29.createElement)(About, { key: "notice", title: "Notice", text: room2.notice }) : (0, import_react29.createElement)("button", { key: "notice", type: "button", className: "bt-more", onClick: openSettings }, t("Add a notice in group settings")),
|
|
10757
|
+
(0, import_react29.createElement)(
|
|
10280
10758
|
"div",
|
|
10281
10759
|
{ key: "members" },
|
|
10282
|
-
(0,
|
|
10283
|
-
(0,
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
"
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10760
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Members · {count}", { count: members.length })),
|
|
10761
|
+
(0, import_react29.createElement)("div", { className: "bt-members", role: "list" }, members.map((member) => {
|
|
10762
|
+
const entry = entries2.find((item) => item.id === member.id);
|
|
10763
|
+
return (0, import_react29.createElement)(
|
|
10764
|
+
"div",
|
|
10765
|
+
{ key: member.id, role: "listitem" },
|
|
10766
|
+
(0, import_react29.createElement)(
|
|
10767
|
+
"div",
|
|
10768
|
+
{ className: "bt-member" },
|
|
10769
|
+
(0, import_react29.createElement)(
|
|
10770
|
+
"button",
|
|
10771
|
+
{ type: "button", className: "bt-member-name", title: t("Open {name}'s chat", { name: member.name }), onClick: () => actions.openSession(member.id) },
|
|
10772
|
+
(0, import_react29.createElement)(BotAvatar, { bot: member, size: 26, main: isMainOf(roster, member.id), admin: room2.admin === member.id, live: false }),
|
|
10773
|
+
(0, import_react29.createElement)("span", null, member.name)
|
|
10774
|
+
),
|
|
10775
|
+
room2.admin === member.id ? (0, import_react29.createElement)("span", { className: "bt-chip bt-chip-admin" }, t("Admin")) : null
|
|
10776
|
+
),
|
|
10777
|
+
entry ? (0, import_react29.createElement)(MemberWork, { entry, bot: member, room: room2, roster, actions, now }) : null
|
|
10778
|
+
);
|
|
10779
|
+
}))
|
|
10294
10780
|
),
|
|
10295
|
-
(0,
|
|
10781
|
+
(0, import_react29.createElement)(
|
|
10296
10782
|
"div",
|
|
10297
10783
|
{ key: "mode" },
|
|
10298
|
-
(0,
|
|
10299
|
-
(0,
|
|
10784
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Who answers")),
|
|
10785
|
+
(0, import_react29.createElement)("div", { className: "bt-kv" }, (0, import_react29.createElement)("span", null, t(ROOM_MODES.find(([id]) => id === shownMode(room2, roster))?.[1] ?? "Everyone")))
|
|
10300
10786
|
),
|
|
10301
|
-
usage ? (0,
|
|
10787
|
+
usage ? (0, import_react29.createElement)(
|
|
10302
10788
|
"div",
|
|
10303
10789
|
{ key: "usage" },
|
|
10304
|
-
(0,
|
|
10305
|
-
(0,
|
|
10790
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Usage")),
|
|
10791
|
+
(0, import_react29.createElement)("div", { className: "bt-kv" }, (0, import_react29.createElement)("span", null, room2.name), (0, import_react29.createElement)("span", null, usage))
|
|
10306
10792
|
) : null
|
|
10307
10793
|
];
|
|
10308
10794
|
}
|
|
10309
|
-
function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, actions, useSessions, useSessionStatus, useActivity, useBrowserTabs, useBrowserPages }) {
|
|
10795
|
+
function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, actions, useSessions, useSessionStatus, useActivity, useWork, useBrowserTabs, useBrowserPages }) {
|
|
10310
10796
|
const owner = roster.byId[sessionId];
|
|
10311
10797
|
const part = owner ? currentPart(owner) : sessionId;
|
|
10312
10798
|
const isMainBot = isMainOf(roster, owner?.id);
|
|
10313
|
-
const [tab, setTab] = (0,
|
|
10799
|
+
const [tab, setTab] = (0, import_react29.useState)("details");
|
|
10314
10800
|
const room2 = roster.roomsById[sessionId];
|
|
10315
|
-
const [view, setView] = (0,
|
|
10316
|
-
const [editing, setEditing] = (0,
|
|
10317
|
-
const [draftName, setDraftName] = (0,
|
|
10318
|
-
const [copied, setCopied] = (0,
|
|
10319
|
-
const [error, setError] = (0,
|
|
10801
|
+
const [view, setView] = (0, import_react29.useState)(startTab === "settings" && (owner || room2) ? "settings" : "overview");
|
|
10802
|
+
const [editing, setEditing] = (0, import_react29.useState)(Boolean(startRenaming && (roster.byId[sessionId] || roster.roomsById[sessionId])));
|
|
10803
|
+
const [draftName, setDraftName] = (0, import_react29.useState)(roster.byId[sessionId]?.name ?? (roster.roomsById[sessionId]?.named ? roster.roomsById[sessionId].name : ""));
|
|
10804
|
+
const [copied, setCopied] = (0, import_react29.useState)(false);
|
|
10805
|
+
const [error, setError] = (0, import_react29.useState)("");
|
|
10320
10806
|
const glide = useGlide(tab);
|
|
10321
|
-
const last = (0,
|
|
10807
|
+
const last = (0, import_react29.useRef)({ view, tab, move: void 0 });
|
|
10322
10808
|
if (last.current.view !== view) last.current = { view, tab, move: view === "settings" ? "in" : "out" };
|
|
10323
10809
|
else if (last.current.tab !== tab) last.current = { view, tab, move: "tab" };
|
|
10324
10810
|
const move = last.current.move;
|
|
@@ -10343,36 +10829,36 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
|
|
|
10343
10829
|
setTimeout(() => setCopied(false), 1600);
|
|
10344
10830
|
});
|
|
10345
10831
|
};
|
|
10346
|
-
const errorLine = error ? (0,
|
|
10832
|
+
const errorLine = error ? (0, import_react29.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
|
|
10347
10833
|
if ((bot || room2) && view === "settings") {
|
|
10348
10834
|
return [
|
|
10349
|
-
(0,
|
|
10835
|
+
(0, import_react29.createElement)(
|
|
10350
10836
|
"div",
|
|
10351
10837
|
{ key: "head", className: "bt-subhead", "data-move": move },
|
|
10352
|
-
(0,
|
|
10353
|
-
(0,
|
|
10354
|
-
hasStrip ? null : (0,
|
|
10838
|
+
(0, import_react29.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Back to details"), onClick: () => setView("overview") }, (0, import_react29.createElement)(ChevronLeftIcon)),
|
|
10839
|
+
(0, import_react29.createElement)("span", { className: "bt-subhead-title" }, bot ? t("Settings") : t("Group settings")),
|
|
10840
|
+
hasStrip ? null : (0, import_react29.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Close details"), onClick: close }, (0, import_react29.createElement)(CloseIcon))
|
|
10355
10841
|
),
|
|
10356
|
-
(0,
|
|
10842
|
+
(0, import_react29.createElement)(
|
|
10357
10843
|
"div",
|
|
10358
10844
|
{ key: "set-body", className: "bt-drawer-body bt-set-body", "data-move": move },
|
|
10359
10845
|
errorLine,
|
|
10360
|
-
bot ? (0,
|
|
10846
|
+
bot ? (0, import_react29.createElement)(BotSettingsForm, { bot, roster, actions, run }) : (0, import_react29.createElement)(GroupSettingsForm, { room: room2, roster, actions, run, onDeleted: close })
|
|
10361
10847
|
)
|
|
10362
10848
|
];
|
|
10363
10849
|
}
|
|
10364
|
-
const top = (0,
|
|
10850
|
+
const top = (0, import_react29.createElement)(
|
|
10365
10851
|
"div",
|
|
10366
10852
|
{ className: "bt-drawer-top", "data-move": side },
|
|
10367
|
-
bot || room2 ? (0,
|
|
10368
|
-
bot && roster.browser === true && actions.openBrowser ? (0,
|
|
10369
|
-
bot || room2 ? (0,
|
|
10370
|
-
hasStrip ? null : (0,
|
|
10853
|
+
bot || room2 ? (0, import_react29.createElement)("button", { type: "button", className: "bt-round", "aria-label": bot ? t("Edit Bot") : t("Group settings"), title: bot ? t("Edit Bot") : t("Group settings"), "data-info-row": "settings", onClick: () => setView("settings") }, (0, import_react29.createElement)(GearIcon)) : null,
|
|
10854
|
+
bot && roster.browser === true && actions.openBrowser ? (0, import_react29.createElement)("button", { type: "button", className: "bt-round", "aria-label": t("Open browser"), title: t("Open this Bot's browser"), onClick: () => actions.openBrowser(sessionId) }, (0, import_react29.createElement)(GlobeIcon)) : null,
|
|
10855
|
+
bot || room2 ? (0, import_react29.createElement)("button", { type: "button", className: "bt-round", "aria-label": copied ? t("Copied conversation") : t("Share conversation"), title: copied ? t("Copied") : t("Copy conversation as Markdown"), onClick: share }, copied ? (0, import_react29.createElement)("span", { key: "copied", className: "bt-pop-in" }, "✓") : (0, import_react29.createElement)(ShareIcon)) : null,
|
|
10856
|
+
hasStrip ? null : (0, import_react29.createElement)("button", { type: "button", className: "bt-round", "aria-label": t("Close details"), onClick: close }, (0, import_react29.createElement)(CloseIcon))
|
|
10371
10857
|
);
|
|
10372
10858
|
if (!bot && !room2) {
|
|
10373
10859
|
return [
|
|
10374
|
-
(0,
|
|
10375
|
-
(0,
|
|
10860
|
+
(0, import_react29.createElement)(import_react29.Fragment, { key: "top" }, top),
|
|
10861
|
+
(0, import_react29.createElement)("div", { key: "body", className: "bt-drawer-body" }, (0, import_react29.createElement)("div", { className: "bt-muted" }, t("This conversation is not part of your Bot team.")))
|
|
10376
10862
|
];
|
|
10377
10863
|
}
|
|
10378
10864
|
const current = bot?.name ?? room2.name;
|
|
@@ -10386,12 +10872,12 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
|
|
|
10386
10872
|
const subtitle = bot ? bot.role : roomSubtitle(room2, roster);
|
|
10387
10873
|
const tabs = bot ? ["details", "browser"] : ["details"];
|
|
10388
10874
|
return [
|
|
10389
|
-
(0,
|
|
10390
|
-
(0,
|
|
10875
|
+
(0, import_react29.createElement)(import_react29.Fragment, { key: "top" }, top),
|
|
10876
|
+
(0, import_react29.createElement)(
|
|
10391
10877
|
"div",
|
|
10392
10878
|
{ key: "id", className: "bt-drawer-id", "data-move": side },
|
|
10393
|
-
bot ? (0,
|
|
10394
|
-
editing ? (0,
|
|
10879
|
+
bot ? (0, import_react29.createElement)(BotAvatar, { bot, size: 64, state: botState(running, doing, roster.questions?.[part] !== void 0), main: isMainBot, badge: false, gaze: true, pokeable: true }) : (0, import_react29.createElement)(RoomAvatar, { room: room2, roster, size: 64 }),
|
|
10880
|
+
editing ? (0, import_react29.createElement)("input", {
|
|
10395
10881
|
className: "bt-drawer-name-input",
|
|
10396
10882
|
autoFocus: true,
|
|
10397
10883
|
value: draftName,
|
|
@@ -10406,49 +10892,49 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
|
|
|
10406
10892
|
setEditing(false);
|
|
10407
10893
|
}
|
|
10408
10894
|
}
|
|
10409
|
-
}) : (0,
|
|
10895
|
+
}) : (0, import_react29.createElement)("button", { type: "button", className: "bt-drawer-name", "aria-label": t("Rename {name}", { name: current }), title: t("Rename"), onClick: () => {
|
|
10410
10896
|
setDraftName(room2 && !room2.named ? "" : current);
|
|
10411
10897
|
setEditing(true);
|
|
10412
10898
|
} }, current),
|
|
10413
|
-
subtitle ? (0,
|
|
10899
|
+
subtitle ? (0, import_react29.createElement)("span", { className: "bt-drawer-role" }, subtitle) : null
|
|
10414
10900
|
),
|
|
10415
|
-
(0,
|
|
10901
|
+
(0, import_react29.createElement)(
|
|
10416
10902
|
"div",
|
|
10417
10903
|
{ key: "tabs", ref: glide.box, className: "bt-tabs", role: "tablist", "data-move": side },
|
|
10418
|
-
(0,
|
|
10419
|
-
tabs.map((key) => (0,
|
|
10904
|
+
(0, import_react29.createElement)("span", { ref: glide.thumb, className: "bt-thumb", "aria-hidden": true }),
|
|
10905
|
+
tabs.map((key) => (0, import_react29.createElement)("button", { key, type: "button", role: "tab", className: "bt-tab", "aria-selected": tab === key, onClick: () => setTab(key) }, t(TAB_LABELS[key])))
|
|
10420
10906
|
),
|
|
10421
|
-
(0,
|
|
10907
|
+
(0, import_react29.createElement)(
|
|
10422
10908
|
"div",
|
|
10423
10909
|
{ key: `body:${tab}`, className: "bt-drawer-body", "data-move": move },
|
|
10424
10910
|
errorLine,
|
|
10425
|
-
room2 && tab === "details" ? (0,
|
|
10911
|
+
room2 && tab === "details" ? (0, import_react29.createElement)(GroupOverview, { room: room2, roster, actions, usage, useWork, openSettings: () => setView("settings") }) : null,
|
|
10426
10912
|
bot && tab === "details" ? [
|
|
10427
|
-
(0,
|
|
10913
|
+
(0, import_react29.createElement)(
|
|
10428
10914
|
"div",
|
|
10429
10915
|
{ key: "model", className: "bt-drawer-model" },
|
|
10430
|
-
(0,
|
|
10431
|
-
(0,
|
|
10432
|
-
modelOf(bot, roster) ? null : (0,
|
|
10916
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Model")),
|
|
10917
|
+
(0, import_react29.createElement)(ModelMenu, { bot, roster, actions, run, wide: true }),
|
|
10918
|
+
modelOf(bot, roster) ? null : (0, import_react29.createElement)("div", { className: "bt-note" }, t("Pick a model and {name} can start.", { name: bot.name }))
|
|
10433
10919
|
),
|
|
10434
|
-
(0,
|
|
10435
|
-
bot.instructions ? (0,
|
|
10436
|
-
roster.memory === false ? null : (0,
|
|
10437
|
-
usage ? (0,
|
|
10920
|
+
(0, import_react29.createElement)(PermissionRow, { key: "permission", bot, roster, actions, run }),
|
|
10921
|
+
bot.instructions ? (0, import_react29.createElement)(About, { key: "about", text: bot.instructions }) : (0, import_react29.createElement)("button", { key: "about", type: "button", className: "bt-more", onClick: () => setView("settings") }, t("Add instructions in Bot settings")),
|
|
10922
|
+
roster.memory === false ? null : (0, import_react29.createElement)(MemoryRow, { key: "memory", bot, actions }),
|
|
10923
|
+
usage ? (0, import_react29.createElement)(
|
|
10438
10924
|
"div",
|
|
10439
10925
|
{ key: "usage" },
|
|
10440
|
-
(0,
|
|
10441
|
-
(0,
|
|
10926
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Usage")),
|
|
10927
|
+
(0, import_react29.createElement)("div", { className: "bt-kv" }, (0, import_react29.createElement)("span", null, bot.name), (0, import_react29.createElement)("span", null, usage))
|
|
10442
10928
|
) : null,
|
|
10443
|
-
(0,
|
|
10929
|
+
(0, import_react29.createElement)(ScheduleSummary, { key: "routines", bot, actions })
|
|
10444
10930
|
] : null,
|
|
10445
|
-
bot && tab === "browser" ? (0,
|
|
10931
|
+
bot && tab === "browser" ? (0, import_react29.createElement)(BrowserCards, { bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) : null
|
|
10446
10932
|
)
|
|
10447
10933
|
];
|
|
10448
10934
|
}
|
|
10449
10935
|
|
|
10450
10936
|
// src/client/browser-pane.js
|
|
10451
|
-
var
|
|
10937
|
+
var import_react30 = require("react");
|
|
10452
10938
|
var import_react_dom7 = require("react-dom");
|
|
10453
10939
|
var PANEL_LINGER_MS = 300;
|
|
10454
10940
|
var WIDTH_KEY = STORAGE.browserWidth;
|
|
@@ -10463,10 +10949,10 @@ var EMPTY_TABS2 = { ids: [], active: null };
|
|
|
10463
10949
|
var SPRING_LEAD2 = springEasing(576, 39.4);
|
|
10464
10950
|
var SPRING_TRAIL2 = springEasing(144, 19.7);
|
|
10465
10951
|
var icon = (paths) => function BrowserIcon() {
|
|
10466
|
-
return (0,
|
|
10952
|
+
return (0, import_react30.createElement)(
|
|
10467
10953
|
"svg",
|
|
10468
10954
|
{ width: 16, height: 16, viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true },
|
|
10469
|
-
paths.map((d, index) => (0,
|
|
10955
|
+
paths.map((d, index) => (0, import_react30.createElement)("path", { key: index, d }))
|
|
10470
10956
|
);
|
|
10471
10957
|
};
|
|
10472
10958
|
var GlobeIcon = icon(["M10 3a7 7 0 1 0 0 14 7 7 0 0 0 0-14z", "M3 10h14", "M10 3c2 2.2 2.9 4.5 2.9 7S12 14.8 10 17c-2-2.2-2.9-4.5-2.9-7S8 5.2 10 3z"]);
|
|
@@ -10478,9 +10964,9 @@ var CloseIcon2 = icon(["M5.5 5.5l9 9M14.5 5.5l-9 9"]);
|
|
|
10478
10964
|
var PlusTabIcon = icon(["M10 5v10M5 10h10"]);
|
|
10479
10965
|
var LockIcon2 = icon(["M6.5 9V7a3.5 3.5 0 0 1 7 0v2", "M5.5 9h9v7h-9z"]);
|
|
10480
10966
|
function Favicon({ src, className }) {
|
|
10481
|
-
const [failed, setFailed] = (0,
|
|
10482
|
-
if (!src || failed === src) return (0,
|
|
10483
|
-
return (0,
|
|
10967
|
+
const [failed, setFailed] = (0, import_react30.useState)(null);
|
|
10968
|
+
if (!src || failed === src) return (0, import_react30.createElement)("span", { className: `${className} bt-ptab-globe` }, (0, import_react30.createElement)(GlobeIcon));
|
|
10969
|
+
return (0, import_react30.createElement)("img", { className, src, alt: "", "aria-hidden": true, onError: () => setFailed(src) });
|
|
10484
10970
|
}
|
|
10485
10971
|
var savedWidth = () => {
|
|
10486
10972
|
try {
|
|
@@ -10539,11 +11025,11 @@ var reducedMotion2 = () => typeof matchMedia === "function" && matchMedia("(pref
|
|
|
10539
11025
|
var tabLabel = (page) => page.title || shortAddress(page.url) || t("New tab");
|
|
10540
11026
|
function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions, useBrowserPages }) {
|
|
10541
11027
|
const pages = useBrowserPages((map) => map);
|
|
10542
|
-
const wrap = (0,
|
|
10543
|
-
const ind = (0,
|
|
10544
|
-
const spot = (0,
|
|
10545
|
-
const under = (0,
|
|
10546
|
-
(0,
|
|
11028
|
+
const wrap = (0, import_react30.useRef)(null);
|
|
11029
|
+
const ind = (0, import_react30.useRef)(null);
|
|
11030
|
+
const spot = (0, import_react30.useRef)(null);
|
|
11031
|
+
const under = (0, import_react30.useRef)(null);
|
|
11032
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10547
11033
|
const box = wrap.current;
|
|
10548
11034
|
const capsule = ind.current;
|
|
10549
11035
|
if (!box || !capsule) return void 0;
|
|
@@ -10572,7 +11058,7 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10572
11058
|
trail.cancel();
|
|
10573
11059
|
};
|
|
10574
11060
|
});
|
|
10575
|
-
(0,
|
|
11061
|
+
(0, import_react30.useEffect)(() => {
|
|
10576
11062
|
const box = wrap.current;
|
|
10577
11063
|
if (!box) return void 0;
|
|
10578
11064
|
const observer = new ResizeObserver(() => {
|
|
@@ -10586,13 +11072,13 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10586
11072
|
observer.observe(box);
|
|
10587
11073
|
return () => observer.disconnect();
|
|
10588
11074
|
}, []);
|
|
10589
|
-
return (0,
|
|
11075
|
+
return (0, import_react30.createElement)(
|
|
10590
11076
|
"div",
|
|
10591
11077
|
{ className: "bt-ptabs", role: "tablist", "aria-label": t("Panel tabs") },
|
|
10592
|
-
(0,
|
|
11078
|
+
(0, import_react30.createElement)(
|
|
10593
11079
|
"div",
|
|
10594
11080
|
{ className: "bt-ptabs-in", ref: wrap },
|
|
10595
|
-
(0,
|
|
11081
|
+
(0, import_react30.createElement)(
|
|
10596
11082
|
"button",
|
|
10597
11083
|
{
|
|
10598
11084
|
type: "button",
|
|
@@ -10602,13 +11088,13 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10602
11088
|
title: bot.name,
|
|
10603
11089
|
onClick: () => onView("details")
|
|
10604
11090
|
},
|
|
10605
|
-
(0,
|
|
10606
|
-
view === "details" ? (0,
|
|
11091
|
+
(0, import_react30.createElement)(BotMark, { bot, size: 20, state: reading ? "searching" : "idle" }),
|
|
11092
|
+
view === "details" ? (0, import_react30.createElement)("span", { className: "bt-ptab-name" }, bot.name) : null
|
|
10607
11093
|
),
|
|
10608
11094
|
ids.map((tabId) => {
|
|
10609
11095
|
const page = pages[tabId] ?? EMPTY_PAGE;
|
|
10610
11096
|
const on = view === "browser" && tabId === active;
|
|
10611
|
-
return (0,
|
|
11097
|
+
return (0, import_react30.createElement)(
|
|
10612
11098
|
"button",
|
|
10613
11099
|
{
|
|
10614
11100
|
key: tabId,
|
|
@@ -10629,9 +11115,9 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10629
11115
|
}
|
|
10630
11116
|
}
|
|
10631
11117
|
},
|
|
10632
|
-
(0,
|
|
10633
|
-
(0,
|
|
10634
|
-
(0,
|
|
11118
|
+
(0, import_react30.createElement)(Favicon, { src: page.favicon, className: "bt-ptab-fav" }),
|
|
11119
|
+
(0, import_react30.createElement)("span", { className: "bt-ptab-title" }, tabLabel(page)),
|
|
11120
|
+
(0, import_react30.createElement)("span", {
|
|
10635
11121
|
className: "bt-ptab-x",
|
|
10636
11122
|
role: "button",
|
|
10637
11123
|
"aria-label": t("Close tab"),
|
|
@@ -10643,20 +11129,20 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10643
11129
|
}, "×")
|
|
10644
11130
|
);
|
|
10645
11131
|
}),
|
|
10646
|
-
(0,
|
|
11132
|
+
(0, import_react30.createElement)("span", { ref: ind, className: "bt-ptab-ind", "aria-hidden": true })
|
|
10647
11133
|
),
|
|
10648
11134
|
// New tab and Close stay outside the scrolling tabs, so a narrow panel keeps both.
|
|
10649
|
-
(0,
|
|
10650
|
-
(0,
|
|
11135
|
+
(0, import_react30.createElement)("button", { type: "button", className: "bt-ptab-new", "aria-label": t("New tab"), title: t("New tab"), onClick: () => actions.browserNewTab(subject) }, (0, import_react30.createElement)(PlusTabIcon)),
|
|
11136
|
+
(0, import_react30.createElement)("button", { type: "button", className: "bt-icon-btn bt-ptab-close", "aria-label": t("Close panel"), title: t("Close panel"), onClick: () => actions.closePanel() }, (0, import_react30.createElement)(CloseIcon2))
|
|
10651
11137
|
);
|
|
10652
11138
|
}
|
|
10653
11139
|
function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages }) {
|
|
10654
11140
|
const page = useBrowserPages((pages) => pages[tabId ?? ""]) ?? EMPTY_PAGE;
|
|
10655
|
-
const frame = (0,
|
|
10656
|
-
const input = (0,
|
|
10657
|
-
const [draft, setDraft] = (0,
|
|
11141
|
+
const frame = (0, import_react30.useRef)(null);
|
|
11142
|
+
const input = (0, import_react30.useRef)(null);
|
|
11143
|
+
const [draft, setDraft] = (0, import_react30.useState)(null);
|
|
10658
11144
|
const showGuest = landed && !leaving && Boolean(page.url) && !page.error;
|
|
10659
|
-
(0,
|
|
11145
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10660
11146
|
const element = frame.current;
|
|
10661
11147
|
if (!element || leaving || !tabId) {
|
|
10662
11148
|
void actions.browserShow(null);
|
|
@@ -10682,7 +11168,7 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
|
|
|
10682
11168
|
window.removeEventListener("resize", schedule);
|
|
10683
11169
|
};
|
|
10684
11170
|
}, [tabId, showGuest, leaving]);
|
|
10685
|
-
(0,
|
|
11171
|
+
(0, import_react30.useEffect)(() => {
|
|
10686
11172
|
setDraft(null);
|
|
10687
11173
|
if (landed && !page.url) input.current?.focus();
|
|
10688
11174
|
}, [tabId, landed]);
|
|
@@ -10698,27 +11184,27 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
|
|
|
10698
11184
|
};
|
|
10699
11185
|
const editing = draft !== null;
|
|
10700
11186
|
const secure = /^https:/i.test(page.url);
|
|
10701
|
-
return (0,
|
|
11187
|
+
return (0, import_react30.createElement)(
|
|
10702
11188
|
"div",
|
|
10703
11189
|
{ className: "bt-panel-view bt-panel-browser", "aria-label": t("{name}'s browser", { name: bot.name }) },
|
|
10704
|
-
(0,
|
|
11190
|
+
(0, import_react30.createElement)(
|
|
10705
11191
|
"div",
|
|
10706
11192
|
{ className: "bt-browser-bar" },
|
|
10707
|
-
(0,
|
|
10708
|
-
(0,
|
|
10709
|
-
(0,
|
|
11193
|
+
(0, import_react30.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Back"), title: t("Back"), disabled: !page.canGoBack, onClick: () => actions.browserCommand(tabId, "back") }, (0, import_react30.createElement)(BackIcon)),
|
|
11194
|
+
(0, import_react30.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Forward"), title: t("Forward"), disabled: !page.canGoForward, onClick: () => actions.browserCommand(tabId, "forward") }, (0, import_react30.createElement)(ForwardIcon)),
|
|
11195
|
+
(0, import_react30.createElement)("button", {
|
|
10710
11196
|
type: "button",
|
|
10711
11197
|
className: "bt-icon-btn",
|
|
10712
11198
|
disabled: !page.url,
|
|
10713
11199
|
"aria-label": page.loading ? t("Stop") : t("Reload"),
|
|
10714
11200
|
title: page.loading ? t("Stop") : t("Reload"),
|
|
10715
11201
|
onClick: () => actions.browserCommand(tabId, page.loading ? "stop" : "reload")
|
|
10716
|
-
}, (0,
|
|
10717
|
-
(0,
|
|
11202
|
+
}, (0, import_react30.createElement)(page.loading ? StopIcon : ReloadIcon)),
|
|
11203
|
+
(0, import_react30.createElement)(
|
|
10718
11204
|
"form",
|
|
10719
11205
|
{ className: "bt-browser-address", onSubmit: go, "data-editing": editing || void 0 },
|
|
10720
|
-
(0,
|
|
10721
|
-
(0,
|
|
11206
|
+
(0, import_react30.createElement)("span", { className: "bt-browser-scheme", "aria-hidden": true }, (0, import_react30.createElement)(secure && !editing ? LockIcon2 : GlobeIcon)),
|
|
11207
|
+
(0, import_react30.createElement)("input", {
|
|
10722
11208
|
ref: input,
|
|
10723
11209
|
type: "text",
|
|
10724
11210
|
spellCheck: false,
|
|
@@ -10742,71 +11228,71 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
|
|
|
10742
11228
|
}
|
|
10743
11229
|
})
|
|
10744
11230
|
),
|
|
10745
|
-
page.loading ? (0,
|
|
11231
|
+
page.loading ? (0, import_react30.createElement)("span", { className: "bt-browser-progress", "aria-hidden": true }) : null
|
|
10746
11232
|
),
|
|
10747
|
-
(0,
|
|
11233
|
+
(0, import_react30.createElement)(
|
|
10748
11234
|
"div",
|
|
10749
11235
|
{ ref: frame, className: "bt-browser-frame", "data-guest": showGuest || void 0 },
|
|
10750
|
-
page.error ? (0,
|
|
11236
|
+
page.error ? (0, import_react30.createElement)(
|
|
10751
11237
|
"div",
|
|
10752
11238
|
{ className: "bt-browser-empty" },
|
|
10753
|
-
(0,
|
|
10754
|
-
(0,
|
|
10755
|
-
page.url ? (0,
|
|
11239
|
+
(0, import_react30.createElement)("div", { className: "bt-browser-empty-title" }, t("This page didn’t load")),
|
|
11240
|
+
(0, import_react30.createElement)("div", { className: "bt-browser-empty-text" }, t(page.error)),
|
|
11241
|
+
page.url ? (0, import_react30.createElement)("button", { type: "button", className: "bt-browser-retry", onClick: () => {
|
|
10756
11242
|
void actions.browserNavigate(tabId, page.url);
|
|
10757
11243
|
} }, t("Try again")) : null
|
|
10758
|
-
) : !page.url ? (0,
|
|
11244
|
+
) : !page.url ? (0, import_react30.createElement)(
|
|
10759
11245
|
"div",
|
|
10760
11246
|
{ className: "bt-browser-empty" },
|
|
10761
|
-
(0,
|
|
10762
|
-
(0,
|
|
10763
|
-
(0,
|
|
11247
|
+
(0, import_react30.createElement)(BotMark, { bot, size: 48, state: "idle", live: true }),
|
|
11248
|
+
(0, import_react30.createElement)("div", { className: "bt-browser-empty-title" }, t("{name}'s browser", { name: bot.name })),
|
|
11249
|
+
(0, import_react30.createElement)("div", { className: "bt-browser-empty-text" }, t("Open a page here and {name} can read it with you. Logins and cookies stay with {name}; other Bots never see them.", { name: bot.name }))
|
|
10764
11250
|
) : null
|
|
10765
11251
|
)
|
|
10766
11252
|
);
|
|
10767
11253
|
}
|
|
10768
|
-
function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatus, useActivity, useBrowserTabs, useBrowserPages }) {
|
|
11254
|
+
function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatus, useActivity, useWork, useBrowserTabs, useBrowserPages }) {
|
|
10769
11255
|
const bot = roster.byId[pane.id];
|
|
10770
11256
|
const canBrowse = Boolean(bot && roster.browser === true && actions.browserShow);
|
|
10771
11257
|
const view = pane.view === "browser" && canBrowse ? "browser" : "details";
|
|
10772
11258
|
const tabState = useBrowserTabs((map) => bot ? map[bot.id] ?? EMPTY_TABS2 : EMPTY_TABS2);
|
|
10773
11259
|
const active = tabState.active;
|
|
10774
11260
|
const page = useBrowserPages((pages) => (active ? pages[active] : null) ?? EMPTY_PAGE);
|
|
10775
|
-
const root = (0,
|
|
10776
|
-
const fly = (0,
|
|
10777
|
-
const [landed, setLanded] = (0,
|
|
10778
|
-
const [settling, setSettling] = (0,
|
|
10779
|
-
const [width, setWidth] = (0,
|
|
10780
|
-
const [viewport, setViewport] = (0,
|
|
10781
|
-
const [dragging, setDragging] = (0,
|
|
10782
|
-
const [resizing, setResizing] = (0,
|
|
11261
|
+
const root = (0, import_react30.useRef)(null);
|
|
11262
|
+
const fly = (0, import_react30.useRef)(null);
|
|
11263
|
+
const [landed, setLanded] = (0, import_react30.useState)(false);
|
|
11264
|
+
const [settling, setSettling] = (0, import_react30.useState)(false);
|
|
11265
|
+
const [width, setWidth] = (0, import_react30.useState)(savedWidth);
|
|
11266
|
+
const [viewport, setViewport] = (0, import_react30.useState)(() => window.innerWidth);
|
|
11267
|
+
const [dragging, setDragging] = (0, import_react30.useState)(false);
|
|
11268
|
+
const [resizing, setResizing] = (0, import_react30.useState)(false);
|
|
10783
11269
|
const instant = dragging || resizing;
|
|
10784
|
-
const [reading, setReading] = (0,
|
|
11270
|
+
const [reading, setReading] = (0, import_react30.useState)(false);
|
|
10785
11271
|
const panelWidth = fitWidth(width ?? (view === "browser" ? DEFAULT_WIDTH : DETAILS_WIDTH), widthRange(view, viewport));
|
|
10786
|
-
(0,
|
|
11272
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10787
11273
|
if (view === "browser" && width === null) setWidth(DEFAULT_WIDTH);
|
|
10788
11274
|
}, [view, width]);
|
|
10789
|
-
(0,
|
|
11275
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10790
11276
|
chatRoom.hold(instant);
|
|
10791
11277
|
}, [instant]);
|
|
10792
|
-
const [last, setLast] = (0,
|
|
11278
|
+
const [last, setLast] = (0, import_react30.useState)({ view, width: panelWidth });
|
|
10793
11279
|
if (last.view !== view || last.width !== panelWidth) {
|
|
10794
11280
|
setLast({ view, width: panelWidth });
|
|
10795
11281
|
if (last.view !== view) setSettling(last.width !== panelWidth && !instant);
|
|
10796
11282
|
}
|
|
10797
|
-
const shownView = (0,
|
|
10798
|
-
(0,
|
|
11283
|
+
const shownView = (0, import_react30.useRef)(view);
|
|
11284
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10799
11285
|
const switched = settling && shownView.current !== view;
|
|
10800
11286
|
shownView.current = view;
|
|
10801
11287
|
if (switched) chatRoom.hold(true);
|
|
10802
11288
|
chatRoom.set(leaving ? null : panelWidth);
|
|
10803
11289
|
if (switched) chatRoom.hold(false);
|
|
10804
11290
|
}, [view, panelWidth, leaving]);
|
|
10805
|
-
(0,
|
|
11291
|
+
(0, import_react30.useEffect)(() => () => {
|
|
10806
11292
|
chatRoom.set(null);
|
|
10807
11293
|
chatRoom.hold(false);
|
|
10808
11294
|
}, []);
|
|
10809
|
-
(0,
|
|
11295
|
+
(0, import_react30.useEffect)(() => {
|
|
10810
11296
|
let timer;
|
|
10811
11297
|
const onResize = () => {
|
|
10812
11298
|
setResizing(true);
|
|
@@ -10820,28 +11306,28 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10820
11306
|
window.removeEventListener("resize", onResize);
|
|
10821
11307
|
};
|
|
10822
11308
|
}, []);
|
|
10823
|
-
(0,
|
|
11309
|
+
(0, import_react30.useEffect)(() => {
|
|
10824
11310
|
if (!settling) return void 0;
|
|
10825
11311
|
const timer = setTimeout(() => setSettling(false), SPRING_SHAPE.duration + 40);
|
|
10826
11312
|
return () => clearTimeout(timer);
|
|
10827
11313
|
}, [settling, view]);
|
|
10828
|
-
(0,
|
|
11314
|
+
(0, import_react30.useEffect)(() => {
|
|
10829
11315
|
const timer = setTimeout(() => setLanded(true), 360);
|
|
10830
11316
|
return () => clearTimeout(timer);
|
|
10831
11317
|
}, []);
|
|
10832
|
-
(0,
|
|
11318
|
+
(0, import_react30.useEffect)(() => {
|
|
10833
11319
|
if (canBrowse) actions.browserEnsure(bot.id);
|
|
10834
11320
|
}, [bot?.id, canBrowse]);
|
|
10835
|
-
(0,
|
|
11321
|
+
(0, import_react30.useEffect)(() => {
|
|
10836
11322
|
if (view === "browser" && (active === null || !tabState.ids.includes(active))) actions.setPanelView("details");
|
|
10837
11323
|
}, [view, active, tabState.ids.length]);
|
|
10838
|
-
(0,
|
|
11324
|
+
(0, import_react30.useEffect)(() => {
|
|
10839
11325
|
if (view !== "browser" || leaving) void actions.browserShow?.(null);
|
|
10840
11326
|
}, [view, leaving]);
|
|
10841
|
-
(0,
|
|
11327
|
+
(0, import_react30.useEffect)(() => () => {
|
|
10842
11328
|
void actions.browserShow?.(null);
|
|
10843
11329
|
}, []);
|
|
10844
|
-
(0,
|
|
11330
|
+
(0, import_react30.useEffect)(() => {
|
|
10845
11331
|
if (!page.readAt || Date.now() - page.readAt > READ_FLASH_MS) return void 0;
|
|
10846
11332
|
setReading(true);
|
|
10847
11333
|
const timer = setTimeout(() => setReading(false), READ_FLASH_MS);
|
|
@@ -10878,7 +11364,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10878
11364
|
return actions[name](...args);
|
|
10879
11365
|
};
|
|
10880
11366
|
}
|
|
10881
|
-
(0,
|
|
11367
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10882
11368
|
const move = fly.current;
|
|
10883
11369
|
if (!move) return;
|
|
10884
11370
|
fly.current = null;
|
|
@@ -10961,7 +11447,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10961
11447
|
for (const name of ends) grip.addEventListener(name, onEnd);
|
|
10962
11448
|
};
|
|
10963
11449
|
const hasStrip = canBrowse && tabState.ids.length > 0;
|
|
10964
|
-
return (0,
|
|
11450
|
+
return (0, import_react30.createElement)(
|
|
10965
11451
|
"aside",
|
|
10966
11452
|
{
|
|
10967
11453
|
ref: root,
|
|
@@ -10978,15 +11464,15 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10978
11464
|
if (event.target === event.currentTarget) setLanded(true);
|
|
10979
11465
|
}
|
|
10980
11466
|
},
|
|
10981
|
-
leaving ? null : (0,
|
|
11467
|
+
leaving ? null : (0, import_react30.createElement)("div", { className: "bt-panel-grip", role: "separator", "aria-orientation": "vertical", "aria-label": t("Resize panel"), onPointerDown: startResize }),
|
|
10982
11468
|
// Over the whole window while dragging: the cursor stays a resize cursor and the
|
|
10983
11469
|
// pages underneath (the chat, the guest) see no pointer.
|
|
10984
|
-
dragging ? (0, import_react_dom7.createPortal)((0,
|
|
10985
|
-
hasStrip ? (0,
|
|
10986
|
-
view === "browser" && bot ? (0,
|
|
11470
|
+
dragging ? (0, import_react_dom7.createPortal)((0, import_react30.createElement)("div", { className: "bt-drag-shield", "aria-hidden": true }), document.body) : null,
|
|
11471
|
+
hasStrip ? (0, import_react30.createElement)(PanelStrip, { bot, subject: pane.id, view, ids: tabState.ids, active, reading, onView: switchView, actions: panelActions, useBrowserPages }) : null,
|
|
11472
|
+
view === "browser" && bot ? (0, import_react30.createElement)(BrowserView, { key: `browser:${active}`, bot, tabId: active, leaving, landed: landed && !settling, actions, useBrowserPages }) : (0, import_react30.createElement)(
|
|
10987
11473
|
"div",
|
|
10988
11474
|
{ key: `details:${pane.id}:${pane.renaming ? "rename" : ""}:${pane.tab ?? ""}`, className: "bt-panel-view" },
|
|
10989
|
-
(0,
|
|
11475
|
+
(0, import_react30.createElement)(DetailsDrawer, {
|
|
10990
11476
|
sessionId: pane.id,
|
|
10991
11477
|
startRenaming: pane.renaming,
|
|
10992
11478
|
startTab: pane.tab,
|
|
@@ -10996,6 +11482,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10996
11482
|
useSessions,
|
|
10997
11483
|
useSessionStatus,
|
|
10998
11484
|
useActivity,
|
|
11485
|
+
useWork,
|
|
10999
11486
|
useBrowserTabs,
|
|
11000
11487
|
useBrowserPages
|
|
11001
11488
|
})
|
|
@@ -11082,46 +11569,46 @@ var BROWSER_CSS = `
|
|
|
11082
11569
|
`;
|
|
11083
11570
|
|
|
11084
11571
|
// src/client/overlays.js
|
|
11085
|
-
var
|
|
11572
|
+
var import_react31 = require("react");
|
|
11086
11573
|
var import_react_dom8 = require("react-dom");
|
|
11087
11574
|
var DIALOG_EXIT_MS = 160;
|
|
11088
11575
|
var MENU_EXIT_MS3 = 120;
|
|
11089
|
-
function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity, useRegistry, useBrowserPages, useBrowserTabs, useUpdate, actions }) {
|
|
11576
|
+
function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity, useWork, useRegistry, useBrowserPages, useBrowserTabs, useUpdate, actions }) {
|
|
11090
11577
|
const roster = useRoster((value) => value);
|
|
11091
|
-
const
|
|
11578
|
+
const ui2 = useUi((value) => value);
|
|
11092
11579
|
useRegistry((value) => value);
|
|
11093
11580
|
const current = useSessions((list) => Object.values(list.byId).find((session) => (session?.retainedBy?.mainView ?? 0) > 0)?.id ?? null);
|
|
11094
|
-
const previous = (0,
|
|
11095
|
-
(0,
|
|
11581
|
+
const previous = (0, import_react31.useRef)(current);
|
|
11582
|
+
(0, import_react31.useEffect)(() => {
|
|
11096
11583
|
if (previous.current === current) return;
|
|
11097
11584
|
previous.current = current;
|
|
11098
11585
|
if (current) actions.followPanel?.(current);
|
|
11099
11586
|
}, [current]);
|
|
11100
|
-
const open =
|
|
11101
|
-
const [kept, setKept] = (0,
|
|
11102
|
-
(0,
|
|
11587
|
+
const open = ui2.details ? { id: ui2.details, view: ui2.view ?? "details", renaming: ui2.renaming, tab: ui2.tab } : null;
|
|
11588
|
+
const [kept, setKept] = (0, import_react31.useState)(open);
|
|
11589
|
+
(0, import_react31.useEffect)(() => {
|
|
11103
11590
|
if (open) {
|
|
11104
11591
|
setKept(open);
|
|
11105
11592
|
return void 0;
|
|
11106
11593
|
}
|
|
11107
11594
|
const timer = setTimeout(() => setKept(null), PANEL_LINGER_MS);
|
|
11108
11595
|
return () => clearTimeout(timer);
|
|
11109
|
-
}, [
|
|
11596
|
+
}, [ui2.details, ui2.view, ui2.renaming, ui2.tab]);
|
|
11110
11597
|
const pane = open ?? kept;
|
|
11111
|
-
const [settings, settingsLeaving] = useLinger(
|
|
11112
|
-
const [memory, memoryLeaving] = useLinger(
|
|
11113
|
-
const [menu, menuLeaving] = useLinger(
|
|
11114
|
-
return (0,
|
|
11115
|
-
|
|
11598
|
+
const [settings, settingsLeaving] = useLinger(ui2.settings, DIALOG_EXIT_MS);
|
|
11599
|
+
const [memory, memoryLeaving] = useLinger(ui2.memory && roster.byId[ui2.memory] ? ui2.memory : null, DIALOG_EXIT_MS);
|
|
11600
|
+
const [menu, menuLeaving] = useLinger(ui2.menu, MENU_EXIT_MS3);
|
|
11601
|
+
return (0, import_react31.createElement)(
|
|
11602
|
+
import_react31.Fragment,
|
|
11116
11603
|
null,
|
|
11117
|
-
pane ? (0, import_react_dom8.createPortal)((0,
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
menu ? (0, import_react_dom8.createPortal)((0,
|
|
11123
|
-
settings ? (0, import_react_dom8.createPortal)((0,
|
|
11124
|
-
memory && roster.byId[memory] ? (0, import_react_dom8.createPortal)((0,
|
|
11604
|
+
pane ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(BotPanel, { pane, leaving: open === null, roster, actions, useSessions, useSessionStatus, useActivity, useWork, useBrowserTabs, useBrowserPages }), document.body) : null,
|
|
11605
|
+
ui2.newChat ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(NewChat, { key: ui2.newChat, mode: ui2.newChat, roster, actions }), document.body) : null,
|
|
11606
|
+
ui2.palette ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(CommandPalette, { key: ui2.palette.token, start: ui2.palette.query, token: ui2.palette.token, roster, actions, useSessions }), document.body) : null,
|
|
11607
|
+
ui2.exchange ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(ExchangeDialog, { pair: ui2.exchange, roster, actions }), document.body) : null,
|
|
11608
|
+
ui2.voice ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(VoiceMode, { key: ui2.voice, sessionId: ui2.voice, roster, actions, useSessions, useSessionStatus }), document.body) : null,
|
|
11609
|
+
menu ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(ContextMenu, { key: `${menu.kind ?? ""}:${menu.id}:${menu.x}:${menu.y}`, menu, roster, actions, leaving: menuLeaving }), document.body) : null,
|
|
11610
|
+
settings ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(SettingsDialog, { page: settings.page, id: settings.id, roster, actions, useUpdate, leaving: settingsLeaving }), document.body) : null,
|
|
11611
|
+
memory && roster.byId[memory] ? (0, import_react_dom8.createPortal)((0, import_react31.createElement)(MemoryDialog, { key: memory, bot: roster.byId[memory], actions, leaving: memoryLeaving }), document.body) : null
|
|
11125
11612
|
);
|
|
11126
11613
|
}
|
|
11127
11614
|
|
|
@@ -11951,6 +12438,117 @@ function writeMode(mode, storage = globalThis.localStorage) {
|
|
|
11951
12438
|
}
|
|
11952
12439
|
}
|
|
11953
12440
|
|
|
12441
|
+
// src/client/user-message.js
|
|
12442
|
+
var import_react32 = require("react");
|
|
12443
|
+
var ui = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
|
|
12444
|
+
var canDrawUserMessages = () => typeof ui.projectUserText === "function";
|
|
12445
|
+
function contentParts(content) {
|
|
12446
|
+
const texts2 = [];
|
|
12447
|
+
const attachments = [];
|
|
12448
|
+
const rest = [];
|
|
12449
|
+
for (const block of Array.isArray(content) ? content : []) {
|
|
12450
|
+
if (block?.type === "text" && typeof block.text === "string") texts2.push(block.text);
|
|
12451
|
+
else if ((block?.type === "image" || block?.type === "file") && block.attachment !== void 0) attachments.push(block);
|
|
12452
|
+
else rest.push(block);
|
|
12453
|
+
}
|
|
12454
|
+
return { text: texts2.join("\n"), attachments, rest };
|
|
12455
|
+
}
|
|
12456
|
+
function mentionableBots(roster, sessionId) {
|
|
12457
|
+
const room2 = roster.roomsById[sessionId];
|
|
12458
|
+
if (room2) return room2.members.map((id) => roster.byId[id]).filter(Boolean);
|
|
12459
|
+
return roster.byId[sessionId] ? roster.bots : [];
|
|
12460
|
+
}
|
|
12461
|
+
function userTextParts(text, bots, referenceLabels = []) {
|
|
12462
|
+
const parts = [];
|
|
12463
|
+
let cursor = 0;
|
|
12464
|
+
for (const span of mentionSpans(text, bots, { references: referenceLabels })) {
|
|
12465
|
+
if (!span.bot) continue;
|
|
12466
|
+
if (span.start > cursor) parts.push({ text: text.slice(cursor, span.start) });
|
|
12467
|
+
parts.push({ bot: span.bot, label: text.slice(span.start + 1, span.end) });
|
|
12468
|
+
cursor = span.end;
|
|
12469
|
+
}
|
|
12470
|
+
if (cursor < text.length) parts.push({ text: text.slice(cursor) });
|
|
12471
|
+
return parts;
|
|
12472
|
+
}
|
|
12473
|
+
function UserText({ text, bots, referenceLabels, skillNames, references, actions }) {
|
|
12474
|
+
return userTextParts(text, bots, referenceLabels).map((part, index) => part.bot ? (0, import_react32.createElement)(
|
|
12475
|
+
"button",
|
|
12476
|
+
{ key: index, type: "button", className: "bt-umention", title: `@${part.bot.name}`, onClick: () => actions.openSession(part.bot.id) },
|
|
12477
|
+
(0, import_react32.createElement)(BotMark, { bot: part.bot, size: 16 }),
|
|
12478
|
+
part.label
|
|
12479
|
+
) : (0, import_react32.createElement)(import_react32.Fragment, { key: index }, ui.projectUserText(part.text, referenceLabels, skillNames, "skill", references)));
|
|
12480
|
+
}
|
|
12481
|
+
function Attachments({ attachments, renderMessageImages }) {
|
|
12482
|
+
const compact2 = attachments.length > 1;
|
|
12483
|
+
return (0, import_react32.createElement)("div", { className: "bt-uattach" }, attachments.map((block, index) => {
|
|
12484
|
+
if (block.type === "image") {
|
|
12485
|
+
return renderMessageImages ? (0, import_react32.createElement)(import_react32.Fragment, { key: index }, renderMessageImages({ images: [{ attachment: block.attachment }], align: "end", compact: compact2 })) : null;
|
|
12486
|
+
}
|
|
12487
|
+
const file = block.attachment;
|
|
12488
|
+
const meta = [ui.fileExtension?.(file.name)?.toUpperCase().slice(0, 8), ui.fileSizeText?.(file.bytes)].filter(Boolean).join(" · ");
|
|
12489
|
+
return (0, import_react32.createElement)(
|
|
12490
|
+
"span",
|
|
12491
|
+
{ key: index, className: "bt-ufile", title: file.name },
|
|
12492
|
+
ui.FileTypeIcon ? (0, import_react32.createElement)(ui.FileTypeIcon, { path: file.name, className: "bt-ufile-icon" }) : null,
|
|
12493
|
+
(0, import_react32.createElement)("span", { className: "bt-ufile-text" }, (0, import_react32.createElement)("span", { className: "bt-ufile-name" }, file.name), meta ? (0, import_react32.createElement)("span", { className: "bt-ufile-meta" }, meta) : null)
|
|
12494
|
+
);
|
|
12495
|
+
}));
|
|
12496
|
+
}
|
|
12497
|
+
function CopyButton({ text }) {
|
|
12498
|
+
const [copied, setCopied] = (0, import_react32.useState)(false);
|
|
12499
|
+
const copy2 = () => {
|
|
12500
|
+
void navigator.clipboard?.writeText(text).then(() => {
|
|
12501
|
+
setCopied(true);
|
|
12502
|
+
setTimeout(() => setCopied(false), 1e3);
|
|
12503
|
+
});
|
|
12504
|
+
};
|
|
12505
|
+
return (0, import_react32.createElement)("button", { type: "button", className: "bt-tool", "aria-label": copied ? t("Copied") : t("Copy"), title: copied ? t("Copied") : t("Copy"), onClick: copy2 }, copied ? (0, import_react32.createElement)(CheckIcon) : (0, import_react32.createElement)(CopyIcon));
|
|
12506
|
+
}
|
|
12507
|
+
var clock3 = (time) => typeof time === "number" ? new Date(time).toLocaleTimeString(dateLocale(), { hour: "numeric", minute: "2-digit" }) : void 0;
|
|
12508
|
+
function UserMessageCell({ node, sessionId, useRoster, actions, renderMessageImages, openFile, openSkill }) {
|
|
12509
|
+
const roster = useRoster((value) => value);
|
|
12510
|
+
const data = node.data ?? {};
|
|
12511
|
+
const { text, attachments, rest } = contentParts(data.content);
|
|
12512
|
+
const referenceLabels = data.referenceLabels ?? [];
|
|
12513
|
+
const references = openFile && openSkill ? { openFile, openSkill } : void 0;
|
|
12514
|
+
const steering = node.kind === "steering";
|
|
12515
|
+
return (0, import_react32.createElement)(
|
|
12516
|
+
"div",
|
|
12517
|
+
{ className: "bt-umsg", "data-steering": steering || void 0 },
|
|
12518
|
+
(0, import_react32.createElement)(
|
|
12519
|
+
"div",
|
|
12520
|
+
{ className: "bt-ustack" },
|
|
12521
|
+
attachments.length > 0 ? (0, import_react32.createElement)(Attachments, { attachments, renderMessageImages }) : null,
|
|
12522
|
+
text !== "" || rest.length > 0 ? (0, import_react32.createElement)(
|
|
12523
|
+
"div",
|
|
12524
|
+
{ className: "bt-ubody", title: clock3(data.time) },
|
|
12525
|
+
(0, import_react32.createElement)(UserText, { text, bots: mentionableBots(roster, sessionId), referenceLabels, skillNames: data.skillNames ?? [], references, actions }),
|
|
12526
|
+
rest.map((block, index) => ui.JsonBlock ? (0, import_react32.createElement)(ui.JsonBlock, { key: index, label: t("Content"), payload: block }) : null)
|
|
12527
|
+
) : null,
|
|
12528
|
+
referenceLabels.length > 0 ? (0, import_react32.createElement)("div", { className: "bt-urefs" }, t("Referenced {labels}", { labels: referenceLabels.join(t(", ")) })) : null
|
|
12529
|
+
),
|
|
12530
|
+
text === "" ? null : (0, import_react32.createElement)("div", { className: "bt-utools" }, (0, import_react32.createElement)(CopyButton, { text }))
|
|
12531
|
+
);
|
|
12532
|
+
}
|
|
12533
|
+
var USER_MESSAGE_CSS = `
|
|
12534
|
+
.bt-umsg{display:flex;flex-direction:row-reverse;align-items:center;justify-content:flex-start;gap:4px}
|
|
12535
|
+
.bt-ustack{min-width:0;max-width:min(calc(var(--dsh-chat-content-width,748px) * .702),82%);display:flex;flex-direction:column;align-items:flex-end;gap:8px}
|
|
12536
|
+
.bt-ubody{max-width:100%;box-sizing:border-box;background:var(--dsw-specific-bubble,var(--bt-user));color:var(--bt-user-ink);border-radius:18px;padding:7px 12px;font-size:var(--dsh-content-font-size,14px);line-height:calc(var(--dsh-content-font-size,14px) + 8px);white-space:pre-wrap;word-break:break-word}
|
|
12537
|
+
.bt-ubody a{color:inherit}
|
|
12538
|
+
.bt-umention{display:inline-flex;align-items:center;gap:3px;vertical-align:-3px;max-width:100%;padding:0 1px;border:0;background:none;color:inherit;font:inherit;font-weight:600;line-height:inherit;cursor:pointer;border-radius:6px;white-space:nowrap}
|
|
12539
|
+
.bt-umention:hover{text-decoration:underline;text-underline-offset:2px}
|
|
12540
|
+
.bt-umention>:first-child{flex:none}
|
|
12541
|
+
.bt-uattach{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;max-width:100%}
|
|
12542
|
+
.bt-ufile{display:inline-flex;align-items:center;gap:10px;box-sizing:border-box;width:240px;min-height:64px;padding:8px 12px;border-radius:18px;border:.5px solid var(--bt-line-2);background:var(--bt-card)}
|
|
12543
|
+
.bt-ufile-icon{flex:none;width:28px;height:28px}
|
|
12544
|
+
.bt-ufile-text{display:flex;flex-direction:column;min-width:0}
|
|
12545
|
+
.bt-ufile-name{font-size:14px;line-height:22px;font-weight:500;color:var(--bt-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
12546
|
+
.bt-ufile-meta{font-size:12px;line-height:15px;color:var(--bt-ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
12547
|
+
.bt-urefs{font-size:13px;line-height:18px;color:var(--bt-ink-3)}
|
|
12548
|
+
.bt-utools{display:flex;flex:none;gap:2px;opacity:0;visibility:hidden;transition:opacity .12s ease,visibility .12s}
|
|
12549
|
+
.bt-umsg:hover .bt-utools,.bt-umsg:focus-within .bt-utools{opacity:1;visibility:visible}
|
|
12550
|
+
`;
|
|
12551
|
+
|
|
11954
12552
|
// src/client/index.js
|
|
11955
12553
|
var inject = ["sessions", "uiWorkspace", "uiConversation", "slots", "theme", "layout", "configForms"];
|
|
11956
12554
|
function apply(ctx) {
|
|
@@ -11978,7 +12576,7 @@ function apply(ctx) {
|
|
|
11978
12576
|
} catch {
|
|
11979
12577
|
seenAt = {};
|
|
11980
12578
|
}
|
|
11981
|
-
const
|
|
12579
|
+
const ui2 = createSource({ details: null, view: "details", renaming: false, tab: null, newChat: null, exchange: null, menu: null, voice: null, palette: null, quote: null, settings: null, memory: null, toast: null, signIn: null, renameAgent: null, unread: savedUnread, reactions: savedReactions, visits: {} });
|
|
11982
12580
|
const surface = createSource({ mode: readMode(), plain: false });
|
|
11983
12581
|
let agentSessionCache = /* @__PURE__ */ new Set();
|
|
11984
12582
|
try {
|
|
@@ -11996,7 +12594,7 @@ function apply(ctx) {
|
|
|
11996
12594
|
};
|
|
11997
12595
|
let expected = null;
|
|
11998
12596
|
const setPlain = (plain) => surface.set((state) => state.plain === plain ? state : { ...state, plain });
|
|
11999
|
-
const dropPanels = () =>
|
|
12597
|
+
const dropPanels = () => ui2.set((state) => state.details || state.renaming ? { ...state, details: null, view: "details", renaming: false, tab: null } : state);
|
|
12000
12598
|
const seenKey = (id) => roster.getSnapshot().byId?.[id]?.id ?? id;
|
|
12001
12599
|
const leaveVisits = (ids) => {
|
|
12002
12600
|
const now = Date.now();
|
|
@@ -12007,11 +12605,13 @@ function apply(ctx) {
|
|
|
12007
12605
|
}
|
|
12008
12606
|
};
|
|
12009
12607
|
ctx.effect(() => {
|
|
12010
|
-
const onPageHide = () => leaveVisits(Object.keys(
|
|
12608
|
+
const onPageHide = () => leaveVisits(Object.keys(ui2.getSnapshot().visits ?? {}));
|
|
12011
12609
|
window.addEventListener("pagehide", onPageHide);
|
|
12012
12610
|
return () => window.removeEventListener("pagehide", onPageHide);
|
|
12013
12611
|
}, "ds-bot: remember visits on page hide");
|
|
12014
12612
|
const activity = createSource({});
|
|
12613
|
+
const work = createSource({ bots: {}, approvals: [] });
|
|
12614
|
+
let workKey = "";
|
|
12015
12615
|
const live = createSource({});
|
|
12016
12616
|
const turnClock = createSource({});
|
|
12017
12617
|
let activityKey = "{}";
|
|
@@ -12034,7 +12634,7 @@ function apply(ctx) {
|
|
|
12034
12634
|
} catch {
|
|
12035
12635
|
savedPrefs = {};
|
|
12036
12636
|
}
|
|
12037
|
-
const setUnread = (id, value) =>
|
|
12637
|
+
const setUnread = (id, value) => ui2.set((state) => {
|
|
12038
12638
|
if (Boolean(state.unread[id]) === value) return state;
|
|
12039
12639
|
const unread = { ...state.unread };
|
|
12040
12640
|
if (value) unread[id] = true;
|
|
@@ -12059,6 +12659,11 @@ function apply(ctx) {
|
|
|
12059
12659
|
activityKey = nextActivity;
|
|
12060
12660
|
activity.set(value.activity ?? {});
|
|
12061
12661
|
}
|
|
12662
|
+
const nextWork = JSON.stringify(value.work ?? null);
|
|
12663
|
+
if (nextWork !== workKey) {
|
|
12664
|
+
workKey = nextWork;
|
|
12665
|
+
work.set(value.work ?? { bots: {}, approvals: [] });
|
|
12666
|
+
}
|
|
12062
12667
|
noteMemoryNews(value.memoryNews ?? {});
|
|
12063
12668
|
const current = roster.getSnapshot();
|
|
12064
12669
|
if (value.connectors !== void 0 && current.ready && JSON.stringify(value.connectors) !== JSON.stringify(current.connectors ?? [])) {
|
|
@@ -12165,7 +12770,7 @@ function apply(ctx) {
|
|
|
12165
12770
|
stopReads = null;
|
|
12166
12771
|
}
|
|
12167
12772
|
void browsers.retain(snapshot.bots.map((bot) => bot.id));
|
|
12168
|
-
|
|
12773
|
+
ui2.set((state) => state.details && state.view === "browser" && (!on || !snapshot.byId[state.details]) ? { ...state, view: "details" } : state);
|
|
12169
12774
|
};
|
|
12170
12775
|
sync();
|
|
12171
12776
|
const offRoster = roster.subscribe(sync);
|
|
@@ -12185,9 +12790,12 @@ function apply(ctx) {
|
|
|
12185
12790
|
const render = () => {
|
|
12186
12791
|
const next = roster.getSnapshot().bots.map((bot) => {
|
|
12187
12792
|
const anchor = `.bt-bubble a[href="${MENTION_ORIGIN}${bot.id}"]`;
|
|
12793
|
+
const chip = /\s/.test(bot.name) ? "" : `[class*="_userRow"] [data-ref-chip="file"][title="@${bot.name.replace(/["\\]/g, "\\$&")}" i]`;
|
|
12188
12794
|
const look = lookOf(bot);
|
|
12189
|
-
const
|
|
12190
|
-
|
|
12795
|
+
const marks = chip ? `${anchor}::before,${chip}::before` : `${anchor}::before`;
|
|
12796
|
+
const mark = look.image ? `${marks}{background:center/cover url("${look.image}");-webkit-mask:none;mask:none;border-radius:50%}` : `${marks}{background:${paintCss(look.color)};-webkit-mask-image:${maskOf(look.shape)};mask-image:${maskOf(look.shape)}}`;
|
|
12797
|
+
const chipBase = chip ? `${chip}{color:inherit;font-weight:600}${chip}>svg{display:none}${chip}::before{content:"";display:inline-block;flex:none;width:16px;height:16px;margin-right:3px;vertical-align:-3px;-webkit-mask:${DEFAULT_MASK} center/contain no-repeat;mask:${DEFAULT_MASK} center/contain no-repeat}` : "";
|
|
12798
|
+
return `${anchor}{color:${inkText(look.color)}}${chipBase}${mark}`;
|
|
12191
12799
|
}).join("\n");
|
|
12192
12800
|
if (next !== last) style.textContent = last = next;
|
|
12193
12801
|
};
|
|
@@ -12302,7 +12910,7 @@ function apply(ctx) {
|
|
|
12302
12910
|
isAgentSession: (id) => isAgentSessionId(id),
|
|
12303
12911
|
openSession: (id) => {
|
|
12304
12912
|
if (isAgentSessionId(id)) return actions.openAgentSession(id);
|
|
12305
|
-
|
|
12913
|
+
ui2.set((state) => state.newChat || state.exchange || state.menu || state.palette ? { ...state, newChat: null, exchange: null, menu: null, palette: null } : state);
|
|
12306
12914
|
setPlain(false);
|
|
12307
12915
|
const target = partFor(id);
|
|
12308
12916
|
expected = target;
|
|
@@ -12311,7 +12919,7 @@ function apply(ctx) {
|
|
|
12311
12919
|
// The conversation replacements withdraw for a plain Session; set the surface
|
|
12312
12920
|
// before the navigation so the Bot skin never flashes.
|
|
12313
12921
|
openAgentSession: (sessionId) => {
|
|
12314
|
-
|
|
12922
|
+
ui2.set((state) => ({ ...state, newChat: null, exchange: null, menu: null, palette: null, details: null, view: "details", renaming: false, tab: null }));
|
|
12315
12923
|
setPlain(true);
|
|
12316
12924
|
expected = sessionId;
|
|
12317
12925
|
ctx.uiWorkspace.openSession(sessionId);
|
|
@@ -12352,8 +12960,8 @@ function apply(ctx) {
|
|
|
12352
12960
|
else if (roster.getSnapshot().mainBotId) actions.openSession(roster.getSnapshot().mainBotId);
|
|
12353
12961
|
},
|
|
12354
12962
|
renameAgentSession: (agentId, sessionId, title) => after(call("agent-rename", { agentId, sessionId, title })),
|
|
12355
|
-
beginAgentRename: (sessionId) =>
|
|
12356
|
-
endAgentRename: () =>
|
|
12963
|
+
beginAgentRename: (sessionId) => ui2.set((state) => ({ ...state, renameAgent: sessionId })),
|
|
12964
|
+
endAgentRename: () => ui2.set((state) => state.renameAgent ? { ...state, renameAgent: null } : state),
|
|
12357
12965
|
// Removing the Agent archives every Session it owns; one of them may fill the
|
|
12358
12966
|
// main view, and it must not stay there wearing the Bot surface afterwards.
|
|
12359
12967
|
removeAgent: async (agentId) => {
|
|
@@ -12379,26 +12987,26 @@ function apply(ctx) {
|
|
|
12379
12987
|
ctx.layout.selectPanel(id);
|
|
12380
12988
|
},
|
|
12381
12989
|
openNewChat: (mode) => {
|
|
12382
|
-
|
|
12990
|
+
ui2.set((state) => ({ ...state, newChat: mode, menu: null, palette: null }));
|
|
12383
12991
|
},
|
|
12384
12992
|
openPalette: (query = "") => {
|
|
12385
|
-
|
|
12993
|
+
ui2.set((state) => ({ ...state, palette: { query, token: Date.now() }, newChat: null, menu: null }));
|
|
12386
12994
|
},
|
|
12387
12995
|
// The token keeps a late exit timer from closing a palette opened again since.
|
|
12388
12996
|
closePalette: (token) => {
|
|
12389
|
-
|
|
12997
|
+
ui2.set((state) => state.palette && (token === void 0 || state.palette.token === token) ? { ...state, palette: null } : state);
|
|
12390
12998
|
},
|
|
12391
12999
|
openExchange: (a, b) => {
|
|
12392
|
-
if (a && b)
|
|
13000
|
+
if (a && b) ui2.set((state) => ({ ...state, exchange: [a, b] }));
|
|
12393
13001
|
},
|
|
12394
13002
|
openVoice: (id) => {
|
|
12395
|
-
if (id)
|
|
13003
|
+
if (id) ui2.set((state) => ({ ...state, voice: id, menu: null }));
|
|
12396
13004
|
},
|
|
12397
13005
|
closeVoice: () => {
|
|
12398
|
-
|
|
13006
|
+
ui2.set((state) => state.voice ? { ...state, voice: null } : state);
|
|
12399
13007
|
},
|
|
12400
13008
|
toggleDetails: (id, force) => {
|
|
12401
|
-
|
|
13009
|
+
ui2.set((state) => {
|
|
12402
13010
|
const details = force || state.details !== id || state.view !== "details" ? id : null;
|
|
12403
13011
|
return { ...state, renaming: false, tab: null, details, view: "details" };
|
|
12404
13012
|
});
|
|
@@ -12406,34 +13014,34 @@ function apply(ctx) {
|
|
|
12406
13014
|
// The open panel follows the main view; a subject with no browser (a group
|
|
12407
13015
|
// chat, an outside Session) settles on the details view.
|
|
12408
13016
|
followPanel: (id) => {
|
|
12409
|
-
|
|
13017
|
+
ui2.set((state) => {
|
|
12410
13018
|
if (!state.details || state.details === id) return state;
|
|
12411
13019
|
const view = state.view === "browser" && roster.getSnapshot().byId[id] === void 0 ? "details" : state.view;
|
|
12412
13020
|
return { ...state, details: id, view, renaming: false, tab: null };
|
|
12413
13021
|
});
|
|
12414
13022
|
},
|
|
12415
13023
|
showDetails: (id, tab = null) => {
|
|
12416
|
-
|
|
13024
|
+
ui2.set((state) => ({ ...state, details: id, view: "details", renaming: false, tab }));
|
|
12417
13025
|
},
|
|
12418
13026
|
// Opening the conversation would move focus to its composer and blur the name field.
|
|
12419
13027
|
renameBot: (id) => {
|
|
12420
|
-
|
|
13028
|
+
ui2.set((state) => ({ ...state, details: id, view: "details", renaming: true, tab: null }));
|
|
12421
13029
|
},
|
|
12422
13030
|
// `id` opens one Bot (page 'bots') or one group chat (page 'groups').
|
|
12423
13031
|
openSettings: (page = "general", id = null) => {
|
|
12424
|
-
|
|
13032
|
+
ui2.set((state) => ({ ...state, settings: { page, id }, menu: null, palette: null, newChat: null }));
|
|
12425
13033
|
},
|
|
12426
13034
|
closeSettings: () => {
|
|
12427
|
-
|
|
13035
|
+
ui2.set((state) => state.settings ? { ...state, settings: null } : state);
|
|
12428
13036
|
},
|
|
12429
13037
|
openMemory: (botId) => {
|
|
12430
|
-
|
|
13038
|
+
ui2.set((state) => ({ ...state, memory: botId, menu: null, palette: null }));
|
|
12431
13039
|
},
|
|
12432
13040
|
closeMemory: () => {
|
|
12433
|
-
|
|
13041
|
+
ui2.set((state) => state.memory ? { ...state, memory: null } : state);
|
|
12434
13042
|
},
|
|
12435
13043
|
openConnectors: () => {
|
|
12436
|
-
|
|
13044
|
+
ui2.set((state) => ({ ...state, settings: { page: "connectors", id: null }, menu: null, palette: null, newChat: null }));
|
|
12437
13045
|
},
|
|
12438
13046
|
// Agent mode hands the surface back to the shell; everything open on the Bot
|
|
12439
13047
|
// surface closes first so nothing stale reopens on the way back.
|
|
@@ -12441,7 +13049,7 @@ function apply(ctx) {
|
|
|
12441
13049
|
if (mode !== "bot" && mode !== "agent") return;
|
|
12442
13050
|
if (surface.getSnapshot().mode === mode) return;
|
|
12443
13051
|
if (mode === "agent") {
|
|
12444
|
-
|
|
13052
|
+
ui2.set((state) => ({ ...state, details: null, view: "details", settings: null, palette: null, newChat: null, menu: null, voice: null, exchange: null, memory: null, toast: null }));
|
|
12445
13053
|
}
|
|
12446
13054
|
writeMode(mode);
|
|
12447
13055
|
surface.set((state) => ({ ...state, mode }));
|
|
@@ -12468,18 +13076,18 @@ function apply(ctx) {
|
|
|
12468
13076
|
beginVisit: (id) => {
|
|
12469
13077
|
const made = roster.getSnapshot().byId[id] ?? roster.getSnapshot().roomsById[id];
|
|
12470
13078
|
const seen = seenAt[seenKey(id)] ?? (made?.createdBy && Number.isFinite(made.createdAt) ? made.createdAt : null);
|
|
12471
|
-
|
|
13079
|
+
ui2.set((state) => ({ ...state, visits: { ...state.visits, [id]: { seen, entered: Date.now() } } }));
|
|
12472
13080
|
},
|
|
12473
13081
|
endVisit: (id) => {
|
|
12474
13082
|
leaveVisits([id]);
|
|
12475
|
-
|
|
13083
|
+
ui2.set((state) => {
|
|
12476
13084
|
if (!(id in (state.visits ?? {}))) return state;
|
|
12477
13085
|
const visits = { ...state.visits };
|
|
12478
13086
|
delete visits[id];
|
|
12479
13087
|
return { ...state, visits };
|
|
12480
13088
|
});
|
|
12481
13089
|
},
|
|
12482
|
-
toggleReaction: (key, index, emoji) =>
|
|
13090
|
+
toggleReaction: (key, index, emoji) => ui2.set((state) => {
|
|
12483
13091
|
const entry = { ...state.reactions?.[key] ?? {} };
|
|
12484
13092
|
const list = entry[index] ?? [];
|
|
12485
13093
|
entry[index] = list.includes(emoji) ? list.filter((item) => item !== emoji) : [...list, emoji];
|
|
@@ -12493,19 +13101,19 @@ function apply(ctx) {
|
|
|
12493
13101
|
}
|
|
12494
13102
|
return { ...state, reactions };
|
|
12495
13103
|
}),
|
|
12496
|
-
quote: (sessionId, text) =>
|
|
12497
|
-
clearQuote: (token) =>
|
|
13104
|
+
quote: (sessionId, text) => ui2.set((state) => ({ ...state, quote: { sessionId, text, token: Date.now() } })),
|
|
13105
|
+
clearQuote: (token) => ui2.set((state) => state.quote?.token === token ? { ...state, quote: null } : state),
|
|
12498
13106
|
clearUnread: (id) => setUnread(id, false),
|
|
12499
13107
|
setFlags: (id, flags) => after(call("set-flags", { id, ...flags })),
|
|
12500
13108
|
deleteRoom: (id) => after(call("delete-room", { id })),
|
|
12501
13109
|
openMenu: (menu) => {
|
|
12502
|
-
|
|
13110
|
+
ui2.set((state) => ({ ...state, menu }));
|
|
12503
13111
|
},
|
|
12504
13112
|
closeMenu: () => {
|
|
12505
|
-
|
|
13113
|
+
ui2.set((state) => state.menu ? { ...state, menu: null } : state);
|
|
12506
13114
|
},
|
|
12507
13115
|
closeOverlay: () => {
|
|
12508
|
-
|
|
13116
|
+
ui2.set((state) => ({ ...state, newChat: null, exchange: null, details: state.newChat || state.exchange ? state.details : null }));
|
|
12509
13117
|
},
|
|
12510
13118
|
createBot: (payload) => after(call("create-bot", payload)),
|
|
12511
13119
|
createRoom: (members, options = {}) => after(call("create-room", { members, ...options })),
|
|
@@ -12521,6 +13129,7 @@ function apply(ctx) {
|
|
|
12521
13129
|
send: (sessionId, text) => after(call("send", { sessionId: partFor(sessionId), text })),
|
|
12522
13130
|
answerQuestion: (sessionId, questionId, answer) => after(call("answer-question", { sessionId: partFor(sessionId), questionId, answer })),
|
|
12523
13131
|
dismissQuestion: (sessionId) => after(call("dismiss-question", { sessionId })),
|
|
13132
|
+
answerApproval: (id, choice) => after(call("approval-answer", { id, choice })),
|
|
12524
13133
|
// Values go out in these requests and never come back in a response.
|
|
12525
13134
|
secretSet: (payload) => after(call("secret-set", payload)),
|
|
12526
13135
|
secretAllow: (requestId) => after(call("secret-allow", { requestId })),
|
|
@@ -12548,9 +13157,9 @@ function apply(ctx) {
|
|
|
12548
13157
|
// A failed action away from any form (a menu, the palette) shows as a toast.
|
|
12549
13158
|
reportError: (error, where = "action") => {
|
|
12550
13159
|
noteClientError(where, error);
|
|
12551
|
-
|
|
13160
|
+
ui2.set((state) => ({ ...state, toast: { text: error?.message ?? String(error), token: Date.now() } }));
|
|
12552
13161
|
},
|
|
12553
|
-
dismissToast: (token) =>
|
|
13162
|
+
dismissToast: (token) => ui2.set((state) => state.toast && (token === void 0 || state.toast.token === token) ? { ...state, toast: null } : state)
|
|
12554
13163
|
};
|
|
12555
13164
|
if (browsers) {
|
|
12556
13165
|
Object.assign(actions, {
|
|
@@ -12560,7 +13169,7 @@ function apply(ctx) {
|
|
|
12560
13169
|
if (!bot) return;
|
|
12561
13170
|
browsers.ensureTabs(bot.id);
|
|
12562
13171
|
if (browsers.tabs.getSnapshot()[bot.id]?.active == null) browsers.openTab(bot.id);
|
|
12563
|
-
|
|
13172
|
+
ui2.set((state) => ({ ...state, details: id, view: "browser", renaming: false, tab: null }));
|
|
12564
13173
|
},
|
|
12565
13174
|
// '+' on the strip or the drawer's new-tab card: a blank tab in the browser view.
|
|
12566
13175
|
browserNewTab: (id) => {
|
|
@@ -12568,7 +13177,7 @@ function apply(ctx) {
|
|
|
12568
13177
|
if (!bot) return null;
|
|
12569
13178
|
browsers.ensureTabs(bot.id);
|
|
12570
13179
|
const tabId = browsers.openTab(bot.id);
|
|
12571
|
-
|
|
13180
|
+
ui2.set((state) => ({ ...state, details: id, view: "browser", renaming: false, tab: null }));
|
|
12572
13181
|
return tabId;
|
|
12573
13182
|
},
|
|
12574
13183
|
// A drawer card: its tab active, the panel on the browser view.
|
|
@@ -12576,7 +13185,7 @@ function apply(ctx) {
|
|
|
12576
13185
|
const bot = roster.getSnapshot().byId[id];
|
|
12577
13186
|
if (!bot) return;
|
|
12578
13187
|
browsers.activate(bot.id, tabId);
|
|
12579
|
-
|
|
13188
|
+
ui2.set((state) => ({ ...state, details: id, view: "browser" }));
|
|
12580
13189
|
},
|
|
12581
13190
|
browserEnsure: (botId) => browsers.ensureTabs(botId),
|
|
12582
13191
|
browserCloseTab: (botId, tabId) => browsers.closeTab(botId, tabId),
|
|
@@ -12589,22 +13198,22 @@ function apply(ctx) {
|
|
|
12589
13198
|
}
|
|
12590
13199
|
actions.setPanelView = (view) => {
|
|
12591
13200
|
if (view !== "details" && view !== "browser") return;
|
|
12592
|
-
|
|
13201
|
+
ui2.set((state) => state.details && state.view !== view ? { ...state, view } : state);
|
|
12593
13202
|
};
|
|
12594
13203
|
actions.closePanel = () => {
|
|
12595
|
-
|
|
13204
|
+
ui2.set((state) => state.details ? { ...state, details: null, view: "details", renaming: false, tab: null } : state);
|
|
12596
13205
|
};
|
|
12597
13206
|
actions.signInEntry = () => ctx.slots.entries?.("settings.models.sign-in")?.[0] ?? null;
|
|
12598
13207
|
actions.canSignIn = () => Boolean(actions.signInEntry());
|
|
12599
|
-
actions.signIn = () =>
|
|
12600
|
-
actions.closeSignIn = () =>
|
|
13208
|
+
actions.signIn = () => ui2.set((state) => ({ ...state, signIn: (state.signIn ?? 0) + 1 }));
|
|
13209
|
+
actions.closeSignIn = () => ui2.set((state) => ({ ...state, signIn: null }));
|
|
12601
13210
|
actions.accountT = () => ctx.get("locale").bind("settings.account");
|
|
12602
13211
|
actions.speech = () => ctx.get("remote.speech") ?? void 0;
|
|
12603
13212
|
actions.voiceSetup = () => {
|
|
12604
13213
|
const navigation = ctx.get("pluginNavigation");
|
|
12605
13214
|
return navigation ? () => navigation.openBundle(VOICE_BUNDLE) : null;
|
|
12606
13215
|
};
|
|
12607
|
-
actions.seatAccount = (on) =>
|
|
13216
|
+
actions.seatAccount = (on) => ui2.set((state) => state.accountSeated === on ? state : { ...state, accountSeated: on });
|
|
12608
13217
|
actions.installUpdate = async () => {
|
|
12609
13218
|
const value = await call("update-install");
|
|
12610
13219
|
update.set((previous) => ({ ...previous, ...value }));
|
|
@@ -12665,7 +13274,7 @@ function apply(ctx) {
|
|
|
12665
13274
|
});
|
|
12666
13275
|
const browserPages = browsers?.pages ?? createSource({});
|
|
12667
13276
|
const browserTabs = browsers?.tabs ?? createSource({});
|
|
12668
|
-
const face = () => ({ actions, hooks: { roster, ui, activity, memoryNews, registry, turnClock, live, browserPages, browserTabs, surface, update } });
|
|
13277
|
+
const face = () => ({ actions, hooks: { roster, ui: ui2, activity, work, memoryNews, registry, turnClock, live, browserPages, browserTabs, surface, update } });
|
|
12669
13278
|
const assistantFace = (sessionId) => {
|
|
12670
13279
|
const binding = ctx.sessions.binding(sessionId);
|
|
12671
13280
|
if (binding === void 0) throw new Error(`ds-bot: unknown session "${sessionId}"`);
|
|
@@ -12675,10 +13284,41 @@ function apply(ctx) {
|
|
|
12675
13284
|
const snapshot = chat.getSnapshot();
|
|
12676
13285
|
if (snapshot === void 0) throw new Error("ds-bot: Chat target is unavailable");
|
|
12677
13286
|
return snapshot.nodes.turnDataSource(Number(turn), "assistant-step");
|
|
13287
|
+
},
|
|
13288
|
+
turnContext: (turn) => {
|
|
13289
|
+
const snapshot = chat.getSnapshot();
|
|
13290
|
+
if (snapshot === void 0) throw new Error("ds-bot: Chat target is unavailable");
|
|
13291
|
+
return snapshot.nodes.turnDataSource(Number(turn), "context");
|
|
12678
13292
|
}
|
|
12679
13293
|
} };
|
|
12680
13294
|
};
|
|
12681
13295
|
const shadow = (name, options, component) => ctx.slots.inject(name, () => ctx.slots.register({ name, ...options }, component));
|
|
13296
|
+
let atMenu = null;
|
|
13297
|
+
const installAtMenu = () => {
|
|
13298
|
+
const fiber = ctx.inject(["inputTriggers"], (scope) => {
|
|
13299
|
+
scope.effect(() => {
|
|
13300
|
+
const triggers = scope.inputTriggers;
|
|
13301
|
+
const controllerOf = (sessionId) => {
|
|
13302
|
+
const binding = ctx.sessions.binding(sessionId);
|
|
13303
|
+
return binding === void 0 ? void 0 : triggers.sessionOf(binding.ctx);
|
|
13304
|
+
};
|
|
13305
|
+
const launched = (sessionId) => controllerOf(sessionId)?.launcher.getSnapshot() === CHATS_SOURCE;
|
|
13306
|
+
const offs = atSources({ roster, sessions: ctx.sessions, launched, iconFor: menuIcons(roster) }).map((source) => triggers.registerSource(source));
|
|
13307
|
+
atMenu = { controllerOf };
|
|
13308
|
+
return () => {
|
|
13309
|
+
atMenu = null;
|
|
13310
|
+
for (const off of offs) off();
|
|
13311
|
+
};
|
|
13312
|
+
}, "ds-bot: @ menu");
|
|
13313
|
+
});
|
|
13314
|
+
return () => fiber.dispose();
|
|
13315
|
+
};
|
|
13316
|
+
actions.canReferenceChat = () => atMenu !== null;
|
|
13317
|
+
actions.referenceChat = (sessionId, span) => {
|
|
13318
|
+
const controller = atMenu?.controllerOf(sessionId);
|
|
13319
|
+
if (controller === void 0 || span === void 0) return;
|
|
13320
|
+
controller.toggleSource(CHATS_SOURCE, { trigger: "@", query: "", quoted: false, position: "inline", span });
|
|
13321
|
+
};
|
|
12682
13322
|
const installShell = () => [
|
|
12683
13323
|
installCompiledStyles(SHELL_CSS, "shell"),
|
|
12684
13324
|
installCompiledStyles(SIDEBAR_CSS, "sidebar"),
|
|
@@ -12688,6 +13328,8 @@ function apply(ctx) {
|
|
|
12688
13328
|
installCompiledStyles(SCHEDULE_CSS, "schedules"),
|
|
12689
13329
|
installCompiledStyles(FEEDBACK_CSS, "feedback"),
|
|
12690
13330
|
installCompiledStyles(ABOUT_CSS, "about"),
|
|
13331
|
+
installCompiledStyles(MEMBER_CSS, "members"),
|
|
13332
|
+
installCompiledStyles(USER_MESSAGE_CSS, "user-messages"),
|
|
12691
13333
|
browsers ? installCompiledStyles(BROWSER_CSS, "browser") : null,
|
|
12692
13334
|
installTheme(),
|
|
12693
13335
|
shadow("sidebar.workspaces", { priority: -10, inject: face }, TeamSidebar),
|
|
@@ -12709,6 +13351,7 @@ function apply(ctx) {
|
|
|
12709
13351
|
installCompiledStyles(CONVERSATION_CSS, "conversation"),
|
|
12710
13352
|
installBubbleTokens(),
|
|
12711
13353
|
installMentions(),
|
|
13354
|
+
installAtMenu(),
|
|
12712
13355
|
// Bot conversations are always flat, so the work-details mode is not a choice:
|
|
12713
13356
|
// the mode is held at `verbose` while this group is mounted.
|
|
12714
13357
|
holdFlatTranscript(ctx.configForms.get("ui-chat")),
|
|
@@ -12722,6 +13365,7 @@ function apply(ctx) {
|
|
|
12722
13365
|
// every message.
|
|
12723
13366
|
shadow("conversation.chat.node", { key: "question-reply", priority: -10, locale: "chat" }, AnsweredCell),
|
|
12724
13367
|
shadow("conversation.chat.node", { key: "compaction", priority: -10, locale: "chat" }, Nothing),
|
|
13368
|
+
...canDrawUserMessages() ? ["user", "steering"].map((key) => shadow("conversation.chat.node", { key, priority: -10, locale: "chat", inject: face }, UserMessageCell)) : [],
|
|
12725
13369
|
shadow("conversation.input.permission", { priority: -10 }, Nothing),
|
|
12726
13370
|
shadow("conversation.input.plan", { priority: -10 }, Nothing),
|
|
12727
13371
|
// The composer carries no model picker or run statistics; a Bot's model lives
|
|
@@ -12735,6 +13379,7 @@ function apply(ctx) {
|
|
|
12735
13379
|
priority: -10,
|
|
12736
13380
|
select: (owner) => owner.pendingInteraction?.kind === "question" ? owner.pendingInteraction : null
|
|
12737
13381
|
}, QuestionCard),
|
|
13382
|
+
shadow("conversation.input.dock", { id: "bot-members", order: -120, inject: face }, MemberDock),
|
|
12738
13383
|
shadow("conversation.input.dock", { id: "bot-model", order: -110, inject: face }, ModelChoiceDock),
|
|
12739
13384
|
shadow("conversation.input.dock", { id: "bot-question", order: -100, inject: face }, QuestionDock),
|
|
12740
13385
|
shadow("conversation.input.dock", { id: "bot-secret", order: -90, inject: face }, SecretDock)
|