ds-bot 0.1.1 → 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 +5 -5
- package/client.js +1311 -466
- package/index.js +3 -1
- package/package.json +6 -7
- package/src/host/approvals.js +63 -0
- package/src/host/channel.js +14 -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/dsh-update.js +8 -6
- 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 +31 -4
- 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": "已创建群聊",
|
|
@@ -683,6 +710,25 @@ var ZH = {
|
|
|
683
710
|
"Compared with the day before": "与前一天相比",
|
|
684
711
|
"Compared with the {count} days before": "与再往前 {count} 天相比",
|
|
685
712
|
"{share}% of all usage from {span}": "占 {span} 全部用量的 {share}%",
|
|
713
|
+
// About
|
|
714
|
+
"About DS Bot": "关于 DS Bot",
|
|
715
|
+
"Version {version}": "版本 {version}",
|
|
716
|
+
"Star DS Bot on GitHub": "在 GitHub 上给 DS Bot 点 Star",
|
|
717
|
+
"{count} stars": "{count} 个 Star",
|
|
718
|
+
"If DS Bot helps you, a star on GitHub helps more people find it.": "如果 DS Bot 对你有用,欢迎在 GitHub 上点个 Star,让更多人看到它。",
|
|
719
|
+
Updates: "更新",
|
|
720
|
+
"Check for updates": "检查更新",
|
|
721
|
+
"Checking…": "正在检查…",
|
|
722
|
+
"Up to date": "已是最新版本",
|
|
723
|
+
"Checked {time}": "检查于 {time}",
|
|
724
|
+
"Could not check for updates.": "没能检查更新。",
|
|
725
|
+
"Update checks are off.": "已关闭更新检查。",
|
|
726
|
+
"Installed from a local folder; update it there.": "从本地文件夹安装,请在那里更新。",
|
|
727
|
+
"Updates are not offered for this install.": "这种安装方式不提供更新。",
|
|
728
|
+
Links: "链接",
|
|
729
|
+
"Source code": "源代码",
|
|
730
|
+
"Release notes": "发布说明",
|
|
731
|
+
Open: "打开",
|
|
686
732
|
// Feedback and errors
|
|
687
733
|
Feedback: "反馈",
|
|
688
734
|
"Report a problem": "报告问题",
|
|
@@ -727,6 +773,8 @@ var ZH = {
|
|
|
727
773
|
"Its group conversation could not open": "它的群聊会话打不开",
|
|
728
774
|
// The composer's +
|
|
729
775
|
"Add files": "添加文件",
|
|
776
|
+
"Reference a chat": "引用对话",
|
|
777
|
+
"Chat with {name}": "与 {name} 的对话",
|
|
730
778
|
"Scheduled task": "定时任务",
|
|
731
779
|
"Send to": "发给",
|
|
732
780
|
"Scheduled task: {text}": "定时任务:{text}",
|
|
@@ -794,6 +842,7 @@ var HOST_ERRORS = [
|
|
|
794
842
|
[/^Model (.+) is not available\. Available: (.*)$/, "模型 $1 不可用。可用的:$2"],
|
|
795
843
|
[/^A Bot named (.+) already exists$/, "已经有叫 $1 的 Bot 了"],
|
|
796
844
|
[/^Name (.*) is not available$/, "名字 $1 不可用"],
|
|
845
|
+
[/^"(.+)" calls everyone in a group, so it cannot be a Bot name$/, "「$1」在群里表示所有人,不能用作 Bot 名字"],
|
|
797
846
|
[/^Another group chat is already called "(.+)"$/, "已经有叫「$1」的群聊了"],
|
|
798
847
|
[/^A group chat holds at most (\d+) Bots$/, "一个群聊最多 $1 个 Bot"],
|
|
799
848
|
[/^Reply mode must be one of (.+)$/, "回复模式只能是 $1"],
|
|
@@ -822,6 +871,7 @@ var HOST_ZH = {
|
|
|
822
871
|
"Avatar images must be PNG, JPEG, WebP, or GIF": "头像图片必须是 PNG、JPEG、WebP 或 GIF",
|
|
823
872
|
"Avatar image is too large": "头像图片太大了",
|
|
824
873
|
"A Bot needs a name": "Bot 需要一个名字",
|
|
874
|
+
"A Bot name cannot contain @, ` or a line break": "Bot 名字里不能有 @、` 或换行",
|
|
825
875
|
"Unknown Bot": "找不到这个 Bot",
|
|
826
876
|
"Unknown conversation": "找不到这个对话",
|
|
827
877
|
"A group chat needs at least two Bots": "群聊至少要两个 Bot",
|
|
@@ -940,12 +990,17 @@ function failureLabel(code, message) {
|
|
|
940
990
|
var URL_LIMIT = 7e3;
|
|
941
991
|
var WHAT_MAX = 2e3;
|
|
942
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.";
|
|
943
994
|
function issueUrl(base, { what = "", diagnostics }) {
|
|
944
995
|
const text = what.trim().slice(0, WHAT_MAX);
|
|
945
996
|
const title = text.split("\n")[0].slice(0, 80);
|
|
946
997
|
const build = (body) => `${base}?${new URLSearchParams({ template: "bug.yml", ...title ? { title } : {}, ...text ? { what: text } : {}, diagnostics: body })}`;
|
|
947
998
|
const full2 = build(diagnostics);
|
|
948
|
-
|
|
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 };
|
|
949
1004
|
}
|
|
950
1005
|
|
|
951
1006
|
// src/client/api.js
|
|
@@ -1094,8 +1149,8 @@ function bodyOf(def, paint, flat = false) {
|
|
|
1094
1149
|
return "";
|
|
1095
1150
|
}
|
|
1096
1151
|
}
|
|
1097
|
-
var eyeMarkup = ([cx, cy, w,
|
|
1098
|
-
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>`;
|
|
1099
1154
|
var maskBox = (size) => `maskUnits="userSpaceOnUse" x="${-size}" y="${-size}" width="${size * 3}" height="${size * 3}"`;
|
|
1100
1155
|
var holes = (def, eyes) => `<rect x="${-def.size}" y="${-def.size}" width="${def.size * 3}" height="${def.size * 3}" fill="#fff"/>${eyes}`;
|
|
1101
1156
|
var MASK_ID = "%MASK%";
|
|
@@ -1281,20 +1336,65 @@ function registerTheme(id, theme) {
|
|
|
1281
1336
|
}
|
|
1282
1337
|
var allThemes = () => ({ ...THEMES, ...customThemes });
|
|
1283
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
|
+
|
|
1284
1386
|
// src/client/text.js
|
|
1285
1387
|
var MENTION_ORIGIN = "https://bots.local/";
|
|
1286
|
-
var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1287
1388
|
function linkMentions(text, bots, selfId) {
|
|
1288
|
-
if (!text ||
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
});
|
|
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);
|
|
1298
1398
|
}
|
|
1299
1399
|
function mentionTarget(event) {
|
|
1300
1400
|
const anchor = event.target instanceof Element ? event.target.closest(`a[href^="${MENTION_ORIGIN}"]`) : null;
|
|
@@ -1386,6 +1486,30 @@ function toolArgs(root) {
|
|
|
1386
1486
|
value: (key) => object()?.[key]
|
|
1387
1487
|
};
|
|
1388
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
|
+
}
|
|
1389
1513
|
function commRunOf(order, peers, turn, hiddenTurns = []) {
|
|
1390
1514
|
const turns = [];
|
|
1391
1515
|
for (const entry of order) {
|
|
@@ -1470,12 +1594,12 @@ function liveSprings(apply2) {
|
|
|
1470
1594
|
const dt = Math.min(0.5, Math.max(1e-3, (now - last) / 1e3));
|
|
1471
1595
|
last = now;
|
|
1472
1596
|
const steps = Math.ceil(dt * 240);
|
|
1473
|
-
const
|
|
1597
|
+
const h32 = dt / steps;
|
|
1474
1598
|
let moving = false;
|
|
1475
1599
|
for (const value of values.values()) {
|
|
1476
1600
|
for (let index = 0; index < steps; index += 1) {
|
|
1477
|
-
value.v += (-value.k * (value.x - value.to) - value.c * value.v) *
|
|
1478
|
-
value.x += value.v *
|
|
1601
|
+
value.v += (-value.k * (value.x - value.to) - value.c * value.v) * h32;
|
|
1602
|
+
value.x += value.v * h32;
|
|
1479
1603
|
}
|
|
1480
1604
|
if (Math.abs(value.x - value.to) < 0.05 && Math.abs(value.v) < 1) {
|
|
1481
1605
|
value.x = value.to;
|
|
@@ -1821,39 +1945,52 @@ button.bt-lead{border:0;background:none;padding:0;cursor:pointer}
|
|
|
1821
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")}}
|
|
1822
1946
|
.bt-x:hover{background:var(--bt-hover);color:var(--bt-ink)}
|
|
1823
1947
|
|
|
1824
|
-
.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)}
|
|
1825
1950
|
@keyframes bt-q-in{from{transform:translateY(10px) scale(.97)}}
|
|
1826
1951
|
.bt-q-settled{animation:none}
|
|
1827
1952
|
.bt-q-head{display:flex;align-items:flex-start;gap:8px;min-width:0}
|
|
1828
1953
|
.bt-q-text{flex:1 1 0;min-width:0;display:flex;flex-direction:column}
|
|
1829
|
-
.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}
|
|
1830
1955
|
.bt-q-detail{font-size:13px;line-height:18px;color:var(--bt-ink-2)}
|
|
1831
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")}}
|
|
1832
1957
|
.bt-q-x svg{width:14px;height:14px}
|
|
1833
1958
|
.bt-q-x:hover{background:var(--bt-hover);color:var(--bt-ink)}
|
|
1834
1959
|
.bt-q-x:focus-visible,.bt-q-opt:focus-visible{outline:2px solid var(--bt-accent);outline-offset:-2px}
|
|
1835
|
-
.bt-q-list{display:flex;flex-direction:column;
|
|
1836
|
-
.bt-q-opt{display:flex;align-items:
|
|
1837
|
-
.bt-q-
|
|
1838
|
-
.bt-q
|
|
1839
|
-
.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}
|
|
1840
1967
|
button.bt-q-opt:hover{background:var(--bt-hover)}
|
|
1841
1968
|
button.bt-q-opt:active{background:var(--bt-active)}
|
|
1842
1969
|
.bt-q-opt[aria-pressed=true]{background:var(--bt-active)}
|
|
1843
|
-
.bt-
|
|
1844
|
-
.bt-
|
|
1845
|
-
.bt-q-
|
|
1846
|
-
.
|
|
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}
|
|
1847
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}
|
|
1848
1980
|
.bt-q-settled .bt-q-check{animation:none}
|
|
1849
1981
|
.bt-q-check svg{width:16px;height:16px}
|
|
1850
1982
|
.bt-q-settled .bt-key{opacity:.4}
|
|
1851
1983
|
.bt-q-settled .bt-q-label{color:color-mix(in srgb,var(--bt-ink) 45%,transparent)}
|
|
1852
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}
|
|
1853
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}
|
|
1854
1991
|
.bt-q-custom{display:flex;align-items:flex-start;gap:8px;width:100%;min-width:0}
|
|
1855
|
-
.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-
|
|
1856
|
-
.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)}
|
|
1857
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}
|
|
1858
1995
|
.bt-q-input:not(:placeholder-shown){field-sizing:content}
|
|
1859
1996
|
.bt-q-input::placeholder{color:var(--bt-ink-3)}
|
|
@@ -1865,16 +2002,14 @@ button.bt-q-opt:active{background:var(--bt-active)}
|
|
|
1865
2002
|
.bt-q-btn{background:var(--bt-hover);color:var(--bt-ink)}
|
|
1866
2003
|
.bt-q-btn:hover{background:var(--bt-active)}
|
|
1867
2004
|
.bt-q-foot{display:flex;justify-content:flex-end;gap:8px}
|
|
1868
|
-
.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)}
|
|
1869
2006
|
.bt-q-raised .bt-q-head{padding:14px 16px 4px 20px}
|
|
1870
2007
|
.bt-q-raised .bt-q-head:last-child{padding-bottom:16px}
|
|
1871
|
-
.bt-q-raised .bt-q-
|
|
1872
|
-
.bt-q-raised .bt-q-
|
|
1873
|
-
.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)}
|
|
1874
2010
|
.bt-q-raised button.bt-q-opt:hover{color:var(--bt-ink)}
|
|
1875
2011
|
.bt-q-raised .bt-q-opt[aria-pressed=true]{background:transparent;color:var(--bt-ink)}
|
|
1876
2012
|
.bt-q-raised .bt-q-opt[aria-pressed=true]:hover{background:var(--bt-hover)}
|
|
1877
|
-
.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}
|
|
1878
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)}
|
|
1879
2014
|
.bt-q-raised .bt-q-body{padding:2px 6px 0}
|
|
1880
2015
|
.bt-q-raised .bt-q-desc{color:color-mix(in srgb,var(--bt-ink) 61%,transparent)}
|
|
@@ -1938,6 +2073,7 @@ button.bt-q-opt:active{background:var(--bt-active)}
|
|
|
1938
2073
|
@keyframes bt-sheet-out{to{opacity:0;transform:translateY(16px);filter:blur(4px)}}
|
|
1939
2074
|
.bt-exchange .bt-dialog{position:relative}
|
|
1940
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}
|
|
1941
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}
|
|
1942
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}
|
|
1943
2079
|
.bt-exchange[data-leaving] .bt-dialog-foot{animation:bt-foot-out .22s cubic-bezier(.4,0,1,1) forwards}
|
|
@@ -2403,6 +2539,11 @@ var CONVERSATION_CSS = `
|
|
|
2403
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}
|
|
2404
2540
|
.bt-event button:hover{background:var(--bt-hover)}
|
|
2405
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}
|
|
2406
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}
|
|
2407
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}
|
|
2408
2549
|
.bt-activity{display:flex;align-items:center;gap:10px;margin:8px 0 4px 2px;font-size:14px;color:var(--bt-ink-2)}
|
|
@@ -2645,7 +2786,7 @@ function installCompiledStyles(text, tag) {
|
|
|
2645
2786
|
var import_react2 = require("react");
|
|
2646
2787
|
var STATES = ["idle", "thinking", "searching", "working", "sending", "orbit", "alert", "done"];
|
|
2647
2788
|
var botState = (running, activity, alert) => running ? STATES.includes(activity) ? activity : "working" : alert ? "alert" : "idle";
|
|
2648
|
-
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";
|
|
2649
2790
|
function useGaze(ref, enabled) {
|
|
2650
2791
|
(0, import_react2.useEffect)(() => {
|
|
2651
2792
|
if (!enabled) return void 0;
|
|
@@ -2892,6 +3033,7 @@ function PinMarkIcon() {
|
|
|
2892
3033
|
}
|
|
2893
3034
|
var UnreadIcon = lineIcon(["M10 3.5a4.5 4.5 0 0 0-4.5 4.5v3L4 13.5h12L14.5 11V8", "M8.3 16a1.8 1.8 0 0 0 3.4 0", "M15.5 3.2a1.6 1.6 0 1 1 0 3.2 1.6 1.6 0 0 1 0-3.2z"]);
|
|
2894
3035
|
var PencilIcon = lineIcon(["M9 4H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-3", "M14.3 3.7a1.6 1.6 0 0 1 2.3 2.3L10.5 12l-3 .8.8-3z"]);
|
|
3036
|
+
var InfoIcon = lineIcon(["M10 3.5a6.5 6.5 0 1 1 0 13 6.5 6.5 0 0 1 0-13z", "M10 9.25v4", "M10 6.6h.01"]);
|
|
2895
3037
|
var StarLineIcon = lineIcon(["M10 3l2.1 4.3 4.7.7-3.4 3.3.8 4.7L10 13.8 5.8 16l.8-4.7L3.2 8l4.7-.7z"]);
|
|
2896
3038
|
var CopyIcon = lineIcon(["M8.5 7h6A1.5 1.5 0 0 1 16 8.5v6a1.5 1.5 0 0 1-1.5 1.5h-6A1.5 1.5 0 0 1 7 14.5v-6A1.5 1.5 0 0 1 8.5 7z", "M4 12.5V5.5A1.5 1.5 0 0 1 5.5 4H13"]);
|
|
2897
3039
|
var HideIcon = lineIcon(["M3 3l14 14", "M8.6 5.2A7.6 7.6 0 0 1 10 5c4.5 0 7 5 7 5a12.6 12.6 0 0 1-2 2.6M12.2 14.5A6.8 6.8 0 0 1 10 15c-4.5 0-7-5-7-5a12.4 12.4 0 0 1 3-3.4", "M8.5 8.5a2 2 0 0 0 3 3"]);
|
|
@@ -3677,25 +3819,54 @@ function ThinkingRow({ text }) {
|
|
|
3677
3819
|
(0, import_react6.createElement)("div", { className: "bt-think-text" }, text)
|
|
3678
3820
|
);
|
|
3679
3821
|
}
|
|
3680
|
-
function AssistantCell({ node, groupPart, sessionId, useRoster, useUi, useTurnSteps, actions }) {
|
|
3822
|
+
function AssistantCell({ node, groupPart, sessionId, useRoster, useUi, useTurnSteps, useTurnContext, actions }) {
|
|
3681
3823
|
const roster = useRoster((value) => value);
|
|
3682
3824
|
const data = node.data;
|
|
3683
3825
|
const steps = useTurnSteps(String(data.turn));
|
|
3826
|
+
const contexts = useTurnContext(String(data.turn));
|
|
3684
3827
|
const hidden = Boolean(roster.roomsById[sessionId]) || isHiddenTurn(roster, sessionId, data.turn);
|
|
3685
3828
|
const texts2 = groupPart === "reasoning" ? [] : data.blocks.filter((block) => block.kind === "text" && block.text.trim() !== "").map((block) => block.text);
|
|
3686
3829
|
const running = data.status === "running";
|
|
3687
3830
|
useLiveNote(actions, sessionId, `text:${node.key ?? data.seq}`, !hidden && running && texts2.length > 0 ? { text: true } : null);
|
|
3688
3831
|
if (hidden) return null;
|
|
3689
3832
|
const reasoning = groupPart === "response" ? "" : turnReasoning(steps, data);
|
|
3690
|
-
|
|
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;
|
|
3691
3836
|
return (0, import_react6.createElement)(
|
|
3692
3837
|
import_react6.Fragment,
|
|
3693
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
|
+
}),
|
|
3694
3852
|
reasoning ? (0, import_react6.createElement)(ThinkingRow, { text: reasoning }) : null,
|
|
3695
|
-
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
|
|
3696
3854
|
);
|
|
3697
3855
|
}
|
|
3698
|
-
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
|
+
}
|
|
3699
3870
|
const parts = [];
|
|
3700
3871
|
parts.push((0, import_react6.createElement)("span", { key: "p" }, prefix));
|
|
3701
3872
|
bots.forEach((bot, index) => {
|
|
@@ -3834,9 +4005,10 @@ function EarlierParts({ sessionId, roster, actions }) {
|
|
|
3834
4005
|
case "event": {
|
|
3835
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 });
|
|
3836
4007
|
const other = roster.byId[row.botId] ?? { id: row.botId, name: row.name ?? t("a Bot"), color: "gray" };
|
|
3837
|
-
|
|
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: () => {
|
|
3838
4010
|
if (selfId && other.id) actions.openExchange(selfId, other.id);
|
|
3839
|
-
} });
|
|
4011
|
+
}, whole: true });
|
|
3840
4012
|
}
|
|
3841
4013
|
default:
|
|
3842
4014
|
return null;
|
|
@@ -3850,7 +4022,8 @@ function TriggerCell({ node, sessionId, useRoster, useSessions, useTurnClock, us
|
|
|
3850
4022
|
const data = node.data;
|
|
3851
4023
|
const source = data.source !== null && typeof data.source === "object" ? data.source : {};
|
|
3852
4024
|
const selfId = roster.byId[sessionId]?.id ?? sessionId;
|
|
3853
|
-
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";
|
|
3854
4027
|
const turn = node.location?.turn?.turn ?? data.turn;
|
|
3855
4028
|
(0, import_react6.useLayoutEffect)(() => {
|
|
3856
4029
|
if (relayed) actions.noteTurnPeer(sessionId, turn, source.senderSessionId);
|
|
@@ -3876,8 +4049,9 @@ function TriggerCell({ node, sessionId, useRoster, useSessions, useTurnClock, us
|
|
|
3876
4049
|
if (source.kind === "bot") {
|
|
3877
4050
|
if (source.handoff === true) return roster.roomsById[sessionId] ? null : (0, import_react6.createElement)(EarlierParts, { sessionId, roster, actions });
|
|
3878
4051
|
if (source.role === "kickoff" || source.role === "room") return null;
|
|
3879
|
-
if (source.role === "secret") return null;
|
|
4052
|
+
if (source.role === "secret" || deferred) return null;
|
|
3880
4053
|
if (source.role === "post") {
|
|
4054
|
+
if (source.failed !== void 0) return null;
|
|
3881
4055
|
const room2 = roster.roomsById[sessionId];
|
|
3882
4056
|
const poster = roster.byId[source.senderSessionId] ?? { id: source.senderSessionId, name: source.senderName ?? t("a Bot"), color: "gray" };
|
|
3883
4057
|
return (0, import_react6.createElement)(GroupMessage, { bot: poster, text: stripHeader(contentText(data.content)), room: room2, roster, actions, msgKey: `${sessionId}:${data.turn}:post`, useUi });
|
|
@@ -3899,9 +4073,10 @@ function TriggerCell({ node, sessionId, useRoster, useSessions, useTurnClock, us
|
|
|
3899
4073
|
if (!bots.some((bot) => bot.id === peer.id)) bots.push(peer);
|
|
3900
4074
|
}
|
|
3901
4075
|
return (0, import_react6.createElement)(EventLine, {
|
|
3902
|
-
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"),
|
|
3903
4077
|
bots,
|
|
3904
|
-
onOpen: (bot) => actions.openExchange(selfId, bot.id)
|
|
4078
|
+
onOpen: (bot) => actions.openExchange(selfId, bot.id),
|
|
4079
|
+
whole: true
|
|
3905
4080
|
});
|
|
3906
4081
|
}
|
|
3907
4082
|
if (source.kind === "schedule") {
|
|
@@ -3941,11 +4116,11 @@ function clockLabel(time, now = Date.now()) {
|
|
|
3941
4116
|
const date = new Date(time);
|
|
3942
4117
|
const midnight = (value) => new Date(value.getFullYear(), value.getMonth(), value.getDate()).getTime();
|
|
3943
4118
|
const days = Math.round((midnight(new Date(now)) - midnight(date)) / 864e5);
|
|
3944
|
-
const
|
|
3945
|
-
if (days === 0) return t("Today {time}", { time:
|
|
3946
|
-
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 });
|
|
3947
4122
|
const sameYear = date.getFullYear() === new Date(now).getFullYear();
|
|
3948
|
-
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}`;
|
|
3949
4124
|
}
|
|
3950
4125
|
function nextTurnStart(turns, turn) {
|
|
3951
4126
|
let next;
|
|
@@ -4005,7 +4180,7 @@ var TOOL_PHRASES = [
|
|
|
4005
4180
|
[/image|photo|draw/i, "Working on an image"]
|
|
4006
4181
|
];
|
|
4007
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" };
|
|
4008
|
-
function toolActivity(name, arg, findBot) {
|
|
4183
|
+
function toolActivity(name, arg, findBot, self2) {
|
|
4009
4184
|
switch (name) {
|
|
4010
4185
|
case "ask_user":
|
|
4011
4186
|
return null;
|
|
@@ -4021,10 +4196,14 @@ function toolActivity(name, arg, findBot) {
|
|
|
4021
4196
|
const target = findBot(arg("to"));
|
|
4022
4197
|
return { label: target ? t("Asking {name}", { name: target.name }) : t("Asking another Bot"), state: "sending" };
|
|
4023
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
|
+
}
|
|
4024
4203
|
case "create_bot":
|
|
4025
4204
|
return { label: t("Creating {name}", { name: arg("name") ?? t("a Bot") }), state: "sending" };
|
|
4026
4205
|
case "update_bot": {
|
|
4027
|
-
const target = findBot(arg("name")) ?? findBot(arg("bot"));
|
|
4206
|
+
const target = findBot(arg("name")) ?? findBot(arg("bot")) ?? (arg("bot") ? void 0 : self2);
|
|
4028
4207
|
return { label: t("Updating {name}", { name: target?.name ?? t("a Bot") }), state: "sending" };
|
|
4029
4208
|
}
|
|
4030
4209
|
case "list_bots":
|
|
@@ -4045,6 +4224,10 @@ function toolActivity(name, arg, findBot) {
|
|
|
4045
4224
|
return { label: phrase ? t(phrase) : null, state: toolState(name) };
|
|
4046
4225
|
}
|
|
4047
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
|
+
}
|
|
4048
4231
|
function useLiveNote(actions, sessionId, id, entry) {
|
|
4049
4232
|
const key = entry ? JSON.stringify(entry) : "";
|
|
4050
4233
|
(0, import_react6.useEffect)(() => {
|
|
@@ -4053,7 +4236,7 @@ function useLiveNote(actions, sessionId, id, entry) {
|
|
|
4053
4236
|
return () => actions.noteLive(sessionId, id, null);
|
|
4054
4237
|
}, [sessionId, id, key]);
|
|
4055
4238
|
}
|
|
4056
|
-
function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi }) {
|
|
4239
|
+
function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi, useWork }) {
|
|
4057
4240
|
const [live, setLive] = (0, import_react6.useState)({ replies: [], speaking: null });
|
|
4058
4241
|
(0, import_react6.useEffect)(() => {
|
|
4059
4242
|
if (done) return void 0;
|
|
@@ -4086,6 +4269,7 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4086
4269
|
}
|
|
4087
4270
|
}
|
|
4088
4271
|
const pending = done || !live.speaking ? [] : [live.speaking];
|
|
4272
|
+
const speakerTool = useWork?.((work) => work.bots?.[live.speaking]?.tool) ?? void 0;
|
|
4089
4273
|
if (replies.length === 0 && pending.length === 0) return null;
|
|
4090
4274
|
const room2 = roster.roomsById[sessionId];
|
|
4091
4275
|
return (0, import_react6.createElement)(
|
|
@@ -4093,6 +4277,13 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4093
4277
|
{ className: "bt-group-replies" },
|
|
4094
4278
|
replies.map((reply, index) => {
|
|
4095
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
|
+
}
|
|
4096
4287
|
if (reply.kind === "failed") {
|
|
4097
4288
|
return (0, import_react6.createElement)(
|
|
4098
4289
|
"div",
|
|
@@ -4107,6 +4298,21 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4107
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 })
|
|
4108
4299
|
);
|
|
4109
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
|
+
}
|
|
4110
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 });
|
|
4111
4317
|
}),
|
|
4112
4318
|
pending.map((id) => {
|
|
@@ -4115,13 +4321,13 @@ function GroupReplies({ sessionId, callId, done, content, roster, actions, useUi
|
|
|
4115
4321
|
return (0, import_react6.createElement)(
|
|
4116
4322
|
"div",
|
|
4117
4323
|
{ key: `pending-${id}`, className: "bt-activity" },
|
|
4118
|
-
(0, import_react6.createElement)(BotMark, { bot, size: 22, state: "thinking" }),
|
|
4119
|
-
(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 }))
|
|
4120
4326
|
);
|
|
4121
4327
|
})
|
|
4122
4328
|
);
|
|
4123
4329
|
}
|
|
4124
|
-
function ToolCell({ node, sessionId, useRoster, useUi, actions }) {
|
|
4330
|
+
function ToolCell({ node, sessionId, useRoster, useUi, useWork, actions }) {
|
|
4125
4331
|
const roster = useRoster((value) => value);
|
|
4126
4332
|
const root = node.data.root;
|
|
4127
4333
|
const done = root.kind === "tool-result";
|
|
@@ -4130,13 +4336,18 @@ function ToolCell({ node, sessionId, useRoster, useUi, actions }) {
|
|
|
4130
4336
|
const tool = toolName(root);
|
|
4131
4337
|
const { text: arg, value: argValue } = toolArgs(root);
|
|
4132
4338
|
const findBot = (nameOrId) => roster.byId[nameOrId] ?? roster.bots.find((bot) => bot.name.toLowerCase() === String(nameOrId ?? "").toLowerCase());
|
|
4133
|
-
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]));
|
|
4134
4340
|
if (hidden) return null;
|
|
4135
4341
|
switch (tool) {
|
|
4136
4342
|
case "message_bot": {
|
|
4137
4343
|
const target = findBot(arg("to")) ?? { name: arg("to") ?? "…", color: "gray" };
|
|
4138
4344
|
if (done && root.isError) return (0, import_react6.createElement)("div", { className: "bt-event" }, t("Couldn't message {name}", { name: target.name }));
|
|
4139
|
-
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 });
|
|
4140
4351
|
}
|
|
4141
4352
|
case "create_bot": {
|
|
4142
4353
|
const created = findBot(arg("name"));
|
|
@@ -4144,11 +4355,11 @@ function ToolCell({ node, sessionId, useRoster, useUi, actions }) {
|
|
|
4144
4355
|
return (0, import_react6.createElement)(EventLine, { prefix: t("Created"), bots: [created], onOpen: (bot) => actions.openSession(bot.id) });
|
|
4145
4356
|
}
|
|
4146
4357
|
case "update_bot": {
|
|
4147
|
-
const target = findBot(arg("name")) ?? findBot(arg("bot"));
|
|
4358
|
+
const target = findBot(arg("name")) ?? findBot(arg("bot")) ?? (arg("bot") ? void 0 : roster.byId[sessionId]);
|
|
4148
4359
|
return done && target ? (0, import_react6.createElement)(EventLine, { prefix: t("Updated"), bots: [target], onOpen: (bot) => actions.openSession(bot.id) }) : null;
|
|
4149
4360
|
}
|
|
4150
4361
|
case "group_relay":
|
|
4151
|
-
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 });
|
|
4152
4363
|
case "create_group":
|
|
4153
4364
|
case "update_group":
|
|
4154
4365
|
case "delete_group":
|
|
@@ -4283,10 +4494,11 @@ function useRowEntrances(replacing) {
|
|
|
4283
4494
|
};
|
|
4284
4495
|
}, []);
|
|
4285
4496
|
}
|
|
4286
|
-
function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, actions }) {
|
|
4497
|
+
function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, useSession, actions }) {
|
|
4287
4498
|
const roster = useRoster((value) => value);
|
|
4288
4499
|
const running = useSessionStatus((map) => map.get(sessionId)?.running === true);
|
|
4289
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)));
|
|
4290
4502
|
const bot = roster.byId[sessionId];
|
|
4291
4503
|
const room2 = roster.roomsById[sessionId];
|
|
4292
4504
|
const entries2 = (0, import_react7.useMemo)(() => Object.values(JSON.parse(liveKey)), [liveKey]);
|
|
@@ -4295,7 +4507,10 @@ function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, ac
|
|
|
4295
4507
|
const relaying = tools.some((entry) => entry.relay);
|
|
4296
4508
|
const visible = running && !streaming && !relaying && (bot !== void 0 || room2 !== void 0);
|
|
4297
4509
|
const tool = tools.at(-1);
|
|
4298
|
-
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" };
|
|
4299
4514
|
const [shown, setShown] = (0, import_react7.useState)(null);
|
|
4300
4515
|
const wantKey = want?.key ?? null;
|
|
4301
4516
|
const markState = (0, import_react7.useRef)("thinking");
|
|
@@ -4369,6 +4584,16 @@ function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, ac
|
|
|
4369
4584
|
), host);
|
|
4370
4585
|
}
|
|
4371
4586
|
|
|
4587
|
+
// src/client/dsh-offer.js
|
|
4588
|
+
var APP_OFFER = /* @__PURE__ */ new Set(["available", "downloading", "verifying", "installing", "ready"]);
|
|
4589
|
+
var bare = (value) => String(value ?? "").trim().replace(/^v/i, "").split("+")[0];
|
|
4590
|
+
var sameVersion = (a, b) => bare(a) !== "" && bare(a) === bare(b);
|
|
4591
|
+
function showsDshRow(dsh, app) {
|
|
4592
|
+
if (!dsh?.verified || !dsh.current || sameVersion(dsh.verified, dsh.current)) return false;
|
|
4593
|
+
if (dsh.kind === "npm") return true;
|
|
4594
|
+
return dsh.kind === "app" && APP_OFFER.has(app?.phase) && sameVersion(app.version, dsh.verified);
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4372
4597
|
// src/client/sidebar.js
|
|
4373
4598
|
var import_react8 = require("react");
|
|
4374
4599
|
var import_react_dom3 = require("react-dom");
|
|
@@ -4722,7 +4947,7 @@ function UpdateButton({ useUpdate, actions }) {
|
|
|
4722
4947
|
const phase = update?.phase ?? "idle";
|
|
4723
4948
|
const dsh = update?.dsh;
|
|
4724
4949
|
const dshPhase = dsh?.phase ?? "idle";
|
|
4725
|
-
const dshRow = dsh
|
|
4950
|
+
const dshRow = showsDshRow(dsh, update?.app);
|
|
4726
4951
|
if (!update?.available && !dshRow && phase === "idle" && dshPhase === "idle") return null;
|
|
4727
4952
|
const cloudPhase = phase === "installing" || dshPhase === "installing" ? "installing" : phase !== "idle" ? phase : dshPhase;
|
|
4728
4953
|
const toggle = () => {
|
|
@@ -4741,7 +4966,7 @@ function UpdateButton({ useUpdate, actions }) {
|
|
|
4741
4966
|
const label = phase === "installing" ? t("Updating DS Bot…") : dshPhase === "installing" ? t("Updating DSH…") : phase === "installed" ? t("DS Bot {version} is installed. Restart DSH to use it.", { version: update.installed ?? update.latest }) : dshPhase === "installed" ? t("Restart DSH to start using DSH {version}.", { version: dsh.installed ?? dsh.verified }) : update?.available ? t("DS Bot {version} is available", { version: update.latest }) : t("DSH {version} is available", { version: dsh?.verified });
|
|
4742
4967
|
const message = error || (phase === "failed" ? update.error : "");
|
|
4743
4968
|
const dshMessage = dshError || (dshPhase === "failed" ? dsh?.error : "");
|
|
4744
|
-
const dshText = dshPhase === "installed" ? t("Restart DSH to start using DSH {version}.", { version: dsh.installed ?? dsh.verified }) : dsh?.available === false && dsh?.blockedBy
|
|
4969
|
+
const dshText = dshPhase === "installed" ? t("Restart DSH to start using DSH {version}.", { version: dsh.installed ?? dsh.verified }) : dsh?.available === false && dsh?.blockedBy ? t("Update DS Bot {version} first.", { version: dsh.blockedBy }) : t("You have {version}.", { version: dsh?.current });
|
|
4745
4970
|
return (0, import_react8.createElement)(
|
|
4746
4971
|
import_react8.Fragment,
|
|
4747
4972
|
null,
|
|
@@ -4786,7 +5011,8 @@ function UpdateButton({ useUpdate, actions }) {
|
|
|
4786
5011
|
(0, import_react8.createElement)(
|
|
4787
5012
|
"div",
|
|
4788
5013
|
{ className: "bt-update-actions" },
|
|
4789
|
-
dsh.kind === "app" ? (0, import_react8.createElement)("button", { type: "button", className: "bt-update-go", onClick: () => actions.openAppUpdates()
|
|
5014
|
+
dsh.kind === "app" ? dsh.available ? (0, import_react8.createElement)("button", { type: "button", className: "bt-update-go", onClick: () => void actions.openAppUpdates(dsh.verified).catch(() => {
|
|
5015
|
+
}) }, t("Open updates")) : null : !dsh.available && dshPhase === "idle" ? null : dshPhase === "installed" ? null : (0, import_react8.createElement)("button", {
|
|
4790
5016
|
type: "button",
|
|
4791
5017
|
className: "bt-update-go",
|
|
4792
5018
|
disabled: dshPhase === "installing",
|
|
@@ -5089,7 +5315,7 @@ var SIDEBAR_CSS = `
|
|
|
5089
5315
|
.bt-update-go:hover:not(:disabled){opacity:.9}
|
|
5090
5316
|
.bt-update-go:active:not(:disabled){transform:scale(.96)}
|
|
5091
5317
|
.bt-update-go:disabled{opacity:.6;cursor:default}
|
|
5092
|
-
.bt-update-dsh{margin-top:12px;padding-top:12px;border-top:.5px solid var(--bt-line-2)}
|
|
5318
|
+
.bt-update-dsh:not(:first-child){margin-top:12px;padding-top:12px;border-top:.5px solid var(--bt-line-2)}
|
|
5093
5319
|
@media (prefers-reduced-motion:reduce){.bt-round.bt-update,.bt-update-pop,.bt-update .bt-cloud-arrow{animation:none}}
|
|
5094
5320
|
|
|
5095
5321
|
/* The DSH Agent row and its expanding Session list. The black whale is the
|
|
@@ -5145,7 +5371,7 @@ function MemoryNotice({ shown }) {
|
|
|
5145
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)
|
|
5146
5372
|
);
|
|
5147
5373
|
}
|
|
5148
|
-
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 }) {
|
|
5149
5375
|
const roster = useRoster((value) => value);
|
|
5150
5376
|
const title = useSessions((list) => list.byId[sessionId]?.displayTitle ?? list.byId[sessionId]?.title ?? "");
|
|
5151
5377
|
const firstTurns = useSessions((list) => {
|
|
@@ -5209,7 +5435,7 @@ function HeaderPill({ sessionId, useRoster, useSessions, useSessionStatus, useAc
|
|
|
5209
5435
|
),
|
|
5210
5436
|
(0, import_react9.createElement)("span", { className: "bt-sr", role: "status" }, memoryNotice ? t("Memory updated") : ""),
|
|
5211
5437
|
(0, import_react9.createElement)(NewMessagePills, { key: sessionId, sessionId, useSessions, useUi, useTurnClock }),
|
|
5212
|
-
(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 })
|
|
5213
5439
|
);
|
|
5214
5440
|
}
|
|
5215
5441
|
function useTranscriptView() {
|
|
@@ -5441,7 +5667,16 @@ function ComposerVoice({ sessionId, inputActions, actions, useUi }) {
|
|
|
5441
5667
|
dictation.note ? (0, import_react10.createElement)("span", { className: "bt-mic-note", role: "status" }, dictation.note) : null
|
|
5442
5668
|
);
|
|
5443
5669
|
}
|
|
5444
|
-
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 }) {
|
|
5445
5680
|
const roster = useRoster((value) => value);
|
|
5446
5681
|
const [open, setOpen] = (0, import_react10.useState)(null);
|
|
5447
5682
|
const [shown, leaving] = useLinger(open, 120);
|
|
@@ -5483,6 +5718,11 @@ function ComposerPlus({ sessionId, useRoster, actions }) {
|
|
|
5483
5718
|
setOpen(null);
|
|
5484
5719
|
actions.openSettings("schedules", target);
|
|
5485
5720
|
};
|
|
5721
|
+
const reference = () => {
|
|
5722
|
+
setOpen(null);
|
|
5723
|
+
actions.referenceChat(sessionId, inputActions?.captureInsertion?.());
|
|
5724
|
+
};
|
|
5725
|
+
const canReference = typeof inputActions?.captureInsertion === "function" && actions.canReferenceChat();
|
|
5486
5726
|
return (0, import_react10.createElement)(
|
|
5487
5727
|
import_react10.Fragment,
|
|
5488
5728
|
null,
|
|
@@ -5508,11 +5748,100 @@ function ComposerPlus({ sessionId, useRoster, actions }) {
|
|
|
5508
5748
|
"data-leaving": leaving || void 0
|
|
5509
5749
|
},
|
|
5510
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,
|
|
5511
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")))
|
|
5512
5753
|
), document.body) : null
|
|
5513
5754
|
);
|
|
5514
5755
|
}
|
|
5515
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
|
+
|
|
5516
5845
|
// src/client/speech.js
|
|
5517
5846
|
var VOICE_BUNDLE = "@deepseek-ai/dsh-experimental-voice-input-bundle";
|
|
5518
5847
|
var USABLE = /* @__PURE__ */ new Set(["ready", "standby", "waking"]);
|
|
@@ -7330,8 +7659,9 @@ function CreateBot({ roster, actions, start, brief = "", onBack, canGoBack, onCr
|
|
|
7330
7659
|
const fallbackName = freshBotName(roster, t("New Bot"));
|
|
7331
7660
|
const finalName = name.trim() || fallbackName;
|
|
7332
7661
|
const clash = roster.bots.some((bot) => bot.name.toLowerCase() === finalName.toLowerCase());
|
|
7662
|
+
const problem = nameProblem(finalName);
|
|
7333
7663
|
const create = async () => {
|
|
7334
|
-
if (busy || clash) return;
|
|
7664
|
+
if (busy || clash || problem) return;
|
|
7335
7665
|
setError("");
|
|
7336
7666
|
setBusy(true);
|
|
7337
7667
|
try {
|
|
@@ -7413,7 +7743,7 @@ function CreateBot({ roster, actions, start, brief = "", onBack, canGoBack, onCr
|
|
|
7413
7743
|
}
|
|
7414
7744
|
}
|
|
7415
7745
|
}),
|
|
7416
|
-
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
|
|
7417
7747
|
),
|
|
7418
7748
|
(0, import_react17.createElement)(
|
|
7419
7749
|
"div",
|
|
@@ -7430,7 +7760,7 @@ function CreateBot({ roster, actions, start, brief = "", onBack, canGoBack, onCr
|
|
|
7430
7760
|
"div",
|
|
7431
7761
|
{ className: "bt-create-foot" },
|
|
7432
7762
|
(0, import_react17.createElement)("button", { type: "button", className: "bt-soft", onClick: onBack }, canGoBack ? t("Back") : t("Cancel")),
|
|
7433
|
-
(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"))
|
|
7434
7764
|
)
|
|
7435
7765
|
);
|
|
7436
7766
|
if (inline) return (0, import_react17.createElement)("div", { ref: frame, className: "bt-create-inline", role: "group", "aria-label": t("Create Bot") }, card);
|
|
@@ -8813,6 +9143,7 @@ function SpanPicker({ span, today, oldest, active, near, onPick, onClose }) {
|
|
|
8813
9143
|
);
|
|
8814
9144
|
}
|
|
8815
9145
|
function Heat({ view, keep, paint, index }) {
|
|
9146
|
+
const [hover, setHover] = (0, import_react22.useState)(null);
|
|
8816
9147
|
const today = hourMs(`${view.now.slice(0, 10)} 00`);
|
|
8817
9148
|
const first = mondayOf(today) - (HEAT_WEEKS - 1) * 7 * DAY_MS;
|
|
8818
9149
|
const from = dayKey(first);
|
|
@@ -8838,12 +9169,17 @@ function Heat({ view, keep, paint, index }) {
|
|
|
8838
9169
|
key: ms,
|
|
8839
9170
|
className: "bt-us-cell",
|
|
8840
9171
|
style: { "--w": week },
|
|
9172
|
+
"data-on": hover?.ms === ms ? "" : void 0,
|
|
8841
9173
|
"data-future": ms > today ? "" : void 0,
|
|
8842
9174
|
"data-today": ms === today ? "" : void 0,
|
|
8843
|
-
|
|
9175
|
+
onMouseEnter: ms > today ? void 0 : () => setHover({ ms, week, day })
|
|
8844
9176
|
}, level > 0 ? (0, import_react22.createElement)("i", { style: { background: soften(paint, levels[level - 1]) } }) : null));
|
|
8845
9177
|
}
|
|
8846
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;
|
|
8847
9183
|
const active = [...daily.values()].filter((value) => value > 0).length;
|
|
8848
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 });
|
|
8849
9185
|
return (0, import_react22.createElement)(
|
|
@@ -8851,7 +9187,17 @@ function Heat({ view, keep, paint, index }) {
|
|
|
8851
9187
|
{ className: "bt-us-card bt-us-heat", style: { "--i": index } },
|
|
8852
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)),
|
|
8853
9189
|
(0, import_react22.createElement)("div", { className: "bt-us-heat-months", "aria-hidden": true }, months),
|
|
8854
|
-
(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
|
+
),
|
|
8855
9201
|
(0, import_react22.createElement)(
|
|
8856
9202
|
"div",
|
|
8857
9203
|
{ className: "bt-us-heat-scale", "aria-hidden": true },
|
|
@@ -8866,9 +9212,18 @@ function Skeleton2() {
|
|
|
8866
9212
|
"div",
|
|
8867
9213
|
{ className: "bt-us", "aria-busy": true },
|
|
8868
9214
|
(0, import_react22.createElement)("div", { className: "bt-us-skel", style: { height: 32, width: 320, borderRadius: 999 } }),
|
|
8869
|
-
(0, import_react22.createElement)(
|
|
8870
|
-
|
|
8871
|
-
|
|
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
|
+
)
|
|
8872
9227
|
);
|
|
8873
9228
|
}
|
|
8874
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;
|
|
@@ -9038,19 +9393,23 @@ function UsagePage({ roster, actions, focusId }) {
|
|
|
9038
9393
|
const owners = ranked(part.owners);
|
|
9039
9394
|
body = [
|
|
9040
9395
|
(0, import_react22.createElement)(Profile, { key: "profile", face, range, share: all.total.total > 0 ? whole / all.total.total : 0 }),
|
|
9041
|
-
(0, import_react22.createElement)(Stats, { key: "stats", range, total: part.total, previous: part.previous }),
|
|
9042
|
-
(0, import_react22.createElement)(Chart, { key: "chart", range, columns, series, tipSeries, onPick: pick2 }),
|
|
9043
9396
|
(0, import_react22.createElement)(
|
|
9044
9397
|
"div",
|
|
9045
|
-
{ key: "
|
|
9046
|
-
|
|
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 }),
|
|
9047
9401
|
(0, import_react22.createElement)(
|
|
9048
|
-
|
|
9049
|
-
{
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
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
|
+
)
|
|
9054
9413
|
];
|
|
9055
9414
|
} else {
|
|
9056
9415
|
const owners = ranked(all.owners);
|
|
@@ -9058,22 +9417,26 @@ function UsagePage({ roster, actions, focusId }) {
|
|
|
9058
9417
|
const rest = owners.filter(([at]) => !isBot(at));
|
|
9059
9418
|
const usedModels = ranked(all.models);
|
|
9060
9419
|
body = [
|
|
9061
|
-
(0, import_react22.createElement)(Stats, { key: "stats", range, total: all.total, previous: all.previous }),
|
|
9062
|
-
(0, import_react22.createElement)(Chart, { key: "chart", range, columns, series, tipSeries, by, onBy: setBy, onPick: pick2 }),
|
|
9063
9420
|
(0, import_react22.createElement)(
|
|
9064
9421
|
"div",
|
|
9065
|
-
{ 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 }),
|
|
9066
9425
|
(0, import_react22.createElement)(
|
|
9067
|
-
|
|
9068
|
-
{
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
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)
|
|
9073
9437
|
),
|
|
9074
|
-
(0, import_react22.createElement)(
|
|
9075
|
-
)
|
|
9076
|
-
(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
|
+
)
|
|
9077
9440
|
];
|
|
9078
9441
|
}
|
|
9079
9442
|
return (0, import_react22.createElement)(
|
|
@@ -9134,6 +9497,10 @@ var USAGE_CSS = `
|
|
|
9134
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)}
|
|
9135
9498
|
.bt-us-note{display:inline-flex;align-items:center;gap:8px;font-size:12px;line-height:16px;color:var(--bt-ink-3)}
|
|
9136
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}
|
|
9137
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}
|
|
9138
9505
|
.bt-us-stat{display:flex;flex-direction:column;justify-content:flex-end;gap:6px;min-width:0;padding:16px 18px}
|
|
9139
9506
|
.bt-us-stat+.bt-us-stat{border-left:1px solid var(--bt-line)}
|
|
@@ -9173,7 +9540,7 @@ var USAGE_CSS = `
|
|
|
9173
9540
|
button.bt-us-key{cursor:pointer;transition:background-color .12s ease,color .12s ease}
|
|
9174
9541
|
button.bt-us-key:hover{background:var(--bt-active);color:var(--bt-ink)}
|
|
9175
9542
|
.bt-us-dot{flex:none;width:8px;height:8px;border-radius:50%}
|
|
9176
|
-
.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}
|
|
9177
9544
|
.bt-us-rows{display:flex;flex-direction:column;margin:0 -8px}
|
|
9178
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)}
|
|
9179
9546
|
button.bt-us-row{cursor:pointer;transition:background-color .12s ease}
|
|
@@ -9207,7 +9574,10 @@ button.bt-us-row:hover{background:var(--bt-hover)}
|
|
|
9207
9574
|
.bt-us-heat-months,.bt-us-heat-grid{display:grid;grid-template-columns:repeat(${HEAT_WEEKS},1fr);gap:3px}
|
|
9208
9575
|
.bt-us-heat-months{height:14px;font-size:10px;line-height:14px;color:var(--bt-ink-3)}
|
|
9209
9576
|
.bt-us-heat-months span{white-space:nowrap}
|
|
9577
|
+
.bt-us-heat-plot{position:relative}
|
|
9210
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}
|
|
9211
9581
|
.bt-us-cell{position:relative;aspect-ratio:1;border-radius:3px;background:var(--bt-hover);overflow:hidden}
|
|
9212
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)}
|
|
9213
9583
|
.bt-us-cell>i{position:absolute;inset:0}
|
|
@@ -9216,6 +9586,10 @@ button.bt-us-row:hover{background:var(--bt-hover)}
|
|
|
9216
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)}
|
|
9217
9587
|
.bt-us-heat-scale .bt-us-cell{width:10px}
|
|
9218
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}
|
|
9219
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)}
|
|
9220
9594
|
.bt-us-empty b{margin-top:6px;font-size:15px;font-weight:600;color:var(--bt-ink)}
|
|
9221
9595
|
@keyframes bt-us-in{from{opacity:0;transform:translateY(6px)}}
|
|
@@ -9338,8 +9712,8 @@ var copy = (text) => navigator.clipboard === void 0 ? Promise.resolve(false) : n
|
|
|
9338
9712
|
function whenText(time) {
|
|
9339
9713
|
const date = new Date(time);
|
|
9340
9714
|
const today = (/* @__PURE__ */ new Date()).toDateString() === date.toDateString();
|
|
9341
|
-
const
|
|
9342
|
-
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}`;
|
|
9343
9717
|
}
|
|
9344
9718
|
function entrySummary(entry) {
|
|
9345
9719
|
if (entry.source === "turn") return failureLabel(entry.code, entry.text);
|
|
@@ -9502,55 +9876,196 @@ var FEEDBACK_CSS = `
|
|
|
9502
9876
|
@media (prefers-reduced-motion:reduce){.bt-toast{animation:none}.bt-toast[data-leaving]{transition:none}}
|
|
9503
9877
|
`;
|
|
9504
9878
|
|
|
9505
|
-
// src/client/
|
|
9879
|
+
// src/client/about.js
|
|
9506
9880
|
var import_react25 = require("react");
|
|
9881
|
+
var CHEER_MS = 1800;
|
|
9882
|
+
var starCount = (count) => new Intl.NumberFormat(dateLocale(), { notation: "compact", maximumFractionDigits: 1 }).format(count);
|
|
9883
|
+
function botHint(update) {
|
|
9884
|
+
if (update.phase === "installing") return t("Updating DS Bot…");
|
|
9885
|
+
if (update.phase === "installed") return t("Restart DSH to start using DS Bot {version}.", { version: update.installed ?? update.latest });
|
|
9886
|
+
if (update.phase === "failed") return t("The update did not finish: {reason}", { reason: hostText(update.error ?? "") });
|
|
9887
|
+
if (update.available) return t("DS Bot {version} is available", { version: update.latest });
|
|
9888
|
+
if (update.enabled === false) return t("Update checks are off.");
|
|
9889
|
+
if (update.source === "path") return t("Installed from a local folder; update it there.");
|
|
9890
|
+
if (!update.checkedAt) return null;
|
|
9891
|
+
if (!update.latest && update.source !== "registry") return t("Updates are not offered for this install.");
|
|
9892
|
+
const checked = t("Checked {time}", { time: whenText(update.checkedAt) });
|
|
9893
|
+
return `${update.latest ? t("Up to date") : t("Could not check for updates.")} · ${checked}`;
|
|
9894
|
+
}
|
|
9895
|
+
function dshHint(dsh, shown) {
|
|
9896
|
+
if (dsh.phase === "installing") return t("Updating DSH…");
|
|
9897
|
+
if (dsh.phase === "installed") return t("Restart DSH to start using DSH {version}.", { version: dsh.installed ?? dsh.verified });
|
|
9898
|
+
if (dsh.phase === "failed") return t("The update did not finish: {reason}", { reason: hostText(dsh.error ?? "") });
|
|
9899
|
+
if (dsh.available === false && dsh.blockedBy) return t("Update DS Bot {version} first.", { version: dsh.blockedBy });
|
|
9900
|
+
if (shown) return t("DSH {version} is available", { version: dsh.verified });
|
|
9901
|
+
return t("Version {version}", { version: dsh.current });
|
|
9902
|
+
}
|
|
9903
|
+
var linkProps = (href) => ({ className: "bt-soft bt-soft-sm bt-about-link", href, target: "_blank", rel: "noopener noreferrer" });
|
|
9904
|
+
function AboutPage({ useUpdate, actions, run, Section, Row: Row3 }) {
|
|
9905
|
+
const update = useUpdate((value) => value) ?? {};
|
|
9906
|
+
const [checking, setChecking] = (0, import_react25.useState)(false);
|
|
9907
|
+
const [stars, setStars] = (0, import_react25.useState)(null);
|
|
9908
|
+
(0, import_react25.useEffect)(() => {
|
|
9909
|
+
let live = true;
|
|
9910
|
+
actions.repoStars().then((count) => {
|
|
9911
|
+
if (live && Number.isInteger(count)) setStars(count);
|
|
9912
|
+
}, () => {
|
|
9913
|
+
});
|
|
9914
|
+
return () => {
|
|
9915
|
+
live = false;
|
|
9916
|
+
};
|
|
9917
|
+
}, []);
|
|
9918
|
+
const [cheer, setCheer] = (0, import_react25.useState)(true);
|
|
9919
|
+
(0, import_react25.useEffect)(() => {
|
|
9920
|
+
if (!cheer) return void 0;
|
|
9921
|
+
const timer = setTimeout(() => setCheer(false), CHEER_MS);
|
|
9922
|
+
return () => clearTimeout(timer);
|
|
9923
|
+
}, [cheer]);
|
|
9924
|
+
const check = () => {
|
|
9925
|
+
setChecking(true);
|
|
9926
|
+
void run(actions.checkUpdate()).then((done) => {
|
|
9927
|
+
setChecking(false);
|
|
9928
|
+
if (done) setCheer(true);
|
|
9929
|
+
});
|
|
9930
|
+
};
|
|
9931
|
+
const phase = update.phase ?? "idle";
|
|
9932
|
+
const dsh = update.dsh;
|
|
9933
|
+
const dshPhase = dsh?.phase ?? "idle";
|
|
9934
|
+
(0, import_react25.useEffect)(() => {
|
|
9935
|
+
if (phase === "installed" || dshPhase === "installed") setCheer(true);
|
|
9936
|
+
}, [phase, dshPhase]);
|
|
9937
|
+
const markState = checking ? "searching" : phase === "installing" || dshPhase === "installing" ? "working" : cheer ? "done" : "idle";
|
|
9938
|
+
const dshNew = showsDshRow(dsh, update.app);
|
|
9939
|
+
const dshShown = dshNew && dsh.available;
|
|
9940
|
+
const botControl = phase === "installed" ? null : update.available || phase === "installing" ? (0, import_react25.createElement)(
|
|
9941
|
+
"button",
|
|
9942
|
+
{ type: "button", className: "bt-fb-primary", disabled: phase === "installing", onClick: () => run(actions.installUpdate()) },
|
|
9943
|
+
phase === "installing" ? t("Updating…") : phase === "failed" ? t("Try again") : t("Update")
|
|
9944
|
+
) : (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", disabled: checking || update.enabled === false, onClick: check }, checking ? t("Checking…") : t("Check for updates"));
|
|
9945
|
+
const dshControl = !dshNew || dshPhase === "installed" ? null : dsh.kind === "app" ? dsh.available ? (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => run(actions.openAppUpdates(dsh.verified)) }, t("Open updates")) : null : dsh.available || dshPhase !== "idle" ? (0, import_react25.createElement)(
|
|
9946
|
+
"button",
|
|
9947
|
+
{ type: "button", className: "bt-fb-primary", disabled: dshPhase === "installing", onClick: () => run(actions.installDshUpdate()) },
|
|
9948
|
+
dshPhase === "installing" ? t("Updating…") : dshPhase === "failed" ? t("Try again") : t("Update")
|
|
9949
|
+
) : null;
|
|
9950
|
+
const repo = update.repoUrl;
|
|
9951
|
+
return [
|
|
9952
|
+
(0, import_react25.createElement)(
|
|
9953
|
+
"div",
|
|
9954
|
+
{ key: "hero", className: "bt-about-hero" },
|
|
9955
|
+
(0, import_react25.createElement)("span", { className: "bt-about-mark" }, (0, import_react25.createElement)(BotMark, { look: MAIN_LOOK, bot: { id: "about" }, size: 52, state: markState, live: true, gaze: true, pokeable: true })),
|
|
9956
|
+
(0, import_react25.createElement)(
|
|
9957
|
+
"div",
|
|
9958
|
+
{ className: "bt-about-copy" },
|
|
9959
|
+
(0, import_react25.createElement)("span", { className: "bt-about-name" }, "DS Bot"),
|
|
9960
|
+
update.current ? (0, import_react25.createElement)("span", { className: "bt-about-version" }, t("Version {version}", { version: update.current })) : null
|
|
9961
|
+
),
|
|
9962
|
+
repo ? (0, import_react25.createElement)(
|
|
9963
|
+
"a",
|
|
9964
|
+
{
|
|
9965
|
+
className: "bt-star",
|
|
9966
|
+
href: repo,
|
|
9967
|
+
target: "_blank",
|
|
9968
|
+
rel: "noopener noreferrer",
|
|
9969
|
+
title: t("Star DS Bot on GitHub"),
|
|
9970
|
+
"aria-label": stars === null ? t("Star DS Bot on GitHub") : `${t("Star DS Bot on GitHub")} · ${t("{count} stars", { count: stars })}`
|
|
9971
|
+
},
|
|
9972
|
+
(0, import_react25.createElement)("span", { className: "bt-star-main" }, (0, import_react25.createElement)("span", { className: "bt-star-icon" }, (0, import_react25.createElement)(StarLineIcon)), "Star"),
|
|
9973
|
+
stars === null ? null : (0, import_react25.createElement)("span", { className: "bt-star-count" }, starCount(stars))
|
|
9974
|
+
) : null
|
|
9975
|
+
),
|
|
9976
|
+
repo ? (0, import_react25.createElement)("p", { key: "ask", className: "bt-about-ask" }, t("If DS Bot helps you, a star on GitHub helps more people find it.")) : null,
|
|
9977
|
+
(0, import_react25.createElement)(
|
|
9978
|
+
Section,
|
|
9979
|
+
{ key: "updates", title: t("Updates") },
|
|
9980
|
+
(0, import_react25.createElement)(Row3, { label: "DS Bot", hint: botHint(update) }, botControl, update.available && update.notesUrl ? (0, import_react25.createElement)("a", linkProps(update.notesUrl), t("What's new")) : null),
|
|
9981
|
+
dsh?.current ? (0, import_react25.createElement)(Row3, { label: "DeepSeek Harness", hint: dshHint(dsh, dshShown) }, dshControl) : null
|
|
9982
|
+
),
|
|
9983
|
+
(0, import_react25.createElement)(
|
|
9984
|
+
Section,
|
|
9985
|
+
{ key: "links", title: t("Links") },
|
|
9986
|
+
repo ? (0, import_react25.createElement)(Row3, { label: t("Source code"), hint: repo.replace(/^https:\/\//, "") }, (0, import_react25.createElement)("a", linkProps(repo), (0, import_react25.createElement)(GitHubMark), t("Open"))) : null,
|
|
9987
|
+
update.notesUrl ? (0, import_react25.createElement)(Row3, { label: t("Release notes") }, (0, import_react25.createElement)("a", linkProps(update.notesUrl), t("Open"))) : null,
|
|
9988
|
+
(0, import_react25.createElement)(Row3, { label: t("Report a problem") }, (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => actions.openSettings("feedback") }, t("Open")))
|
|
9989
|
+
)
|
|
9990
|
+
];
|
|
9991
|
+
}
|
|
9992
|
+
var ABOUT_CSS = `
|
|
9993
|
+
.bt-about-hero{display:flex;align-items:center;gap:14px;padding:16px;border:1px solid var(--bt-line);border-radius:12px;background:var(--bt-card)}
|
|
9994
|
+
.bt-about-mark{flex:none;display:inline-flex;position:relative}
|
|
9995
|
+
.bt-about-mark::before{content:'';position:absolute;inset:-20%;border-radius:50%}
|
|
9996
|
+
body:not([data-bt-motion=quiet]) .bt-about-mark:hover .bt-mark[data-live][data-state=idle]:not(.bt-mark-poke) .bt-b{rotate:8deg;translate:5% -1%;animation:bt-nuzzle .78s ease-in-out .32s infinite}
|
|
9997
|
+
body:not([data-bt-motion=quiet]) .bt-about-mark:hover .bt-mark[data-live][data-state=idle] .bt-eye{scale:1 0;animation:none}
|
|
9998
|
+
body:not([data-bt-motion=quiet]) .bt-about-mark:hover .bt-mark[data-live][data-state=idle] .bt-eye-happy{opacity:1;transition-delay:.1s}
|
|
9999
|
+
.bt-about-copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
10000
|
+
.bt-about-name{font-size:16px;line-height:22px;font-weight:600}
|
|
10001
|
+
.bt-about-version{font-size:12px;line-height:16px;color:var(--bt-ink-2);font-variant-numeric:tabular-nums}
|
|
10002
|
+
.bt-about-ask{margin:-10px 4px 0;font-size:12px;line-height:16px;color:var(--bt-ink-3)}
|
|
10003
|
+
.bt-star{flex:none;display:inline-flex;align-items:stretch;height:30px;border-radius:999px;border:1px solid var(--bt-line-2,var(--bt-line));overflow:hidden;color:var(--bt-ink);text-decoration:none;font-size:12px;line-height:28px;transition:transform .12s ease,border-color .15s ease}
|
|
10004
|
+
.bt-star:hover{border-color:color-mix(in srgb,#e3a008 55%,var(--bt-line))}
|
|
10005
|
+
.bt-star:active{transform:scale(.96)}
|
|
10006
|
+
.bt-star-main{display:inline-flex;align-items:center;gap:6px;padding:0 12px 0 10px;background:var(--bt-hover);font-weight:500}
|
|
10007
|
+
.bt-star-icon{display:inline-flex;color:var(--bt-ink-2);transition:color .15s ease}
|
|
10008
|
+
.bt-star-icon path{transition:fill .15s ease}
|
|
10009
|
+
.bt-star:hover .bt-star-icon{color:#e3a008;animation:bt-star-pop ${SPRING_POP.duration}ms ${SPRING_POP.easing}}
|
|
10010
|
+
.bt-star:hover .bt-star-icon path{fill:currentColor}
|
|
10011
|
+
.bt-star-count{display:inline-flex;align-items:center;padding:0 11px;border-left:1px solid var(--bt-line-2,var(--bt-line));color:var(--bt-ink-2);font-variant-numeric:tabular-nums;animation:bt-star-count-in ${SPRING_POP.duration}ms ${SPRING_POP.easing},bt-veil-in .2s ease-out}
|
|
10012
|
+
@keyframes bt-star-pop{from{scale:.6;rotate:-30deg}}
|
|
10013
|
+
@keyframes bt-star-count-in{from{translate:-6px 0}}
|
|
10014
|
+
.bt-about-link{display:inline-flex;align-items:center;gap:6px;text-decoration:none}
|
|
10015
|
+
.bt-about-link svg{width:14px;height:14px}
|
|
10016
|
+
.bt-fb-primary:disabled{opacity:.6;cursor:default;filter:none}
|
|
10017
|
+
@media (prefers-reduced-motion:reduce){.bt-star:hover .bt-star-icon,.bt-star-count{animation:none}}
|
|
10018
|
+
`;
|
|
10019
|
+
|
|
10020
|
+
// src/client/settings.js
|
|
10021
|
+
var import_react26 = require("react");
|
|
9507
10022
|
var capitalize2 = (word) => word[0].toUpperCase() + word.slice(1);
|
|
9508
|
-
var SETTINGS_PAGES = [["general", "General"], ["bots", "Bots"], ["groups", "Group chats"], ["connectors", "Connectors"], ["schedules", "Scheduled tasks"], ["usage", "Usage"], ["secrets", "Secrets"], ["feedback", "Feedback"]];
|
|
9509
|
-
var PAGE_ICONS = { ...SETTINGS_ICONS, connectors: ConnectorIcon, schedules: ClockIcon, secrets: KeyIcon, feedback: FeedbackIcon };
|
|
10023
|
+
var SETTINGS_PAGES = [["general", "General"], ["bots", "Bots"], ["groups", "Group chats"], ["connectors", "Connectors"], ["schedules", "Scheduled tasks"], ["usage", "Usage"], ["secrets", "Secrets"], ["feedback", "Feedback"], ["about", "About DS Bot"]];
|
|
10024
|
+
var PAGE_ICONS = { ...SETTINGS_ICONS, connectors: ConnectorIcon, schedules: ClockIcon, secrets: KeyIcon, feedback: FeedbackIcon, about: InfoIcon };
|
|
9510
10025
|
function SetSection({ title, children }) {
|
|
9511
|
-
return (0,
|
|
10026
|
+
return (0, import_react26.createElement)("section", { className: "bt-set-section" }, (0, import_react26.createElement)("h3", null, title), (0, import_react26.createElement)("div", { className: "bt-set-card" }, children));
|
|
9512
10027
|
}
|
|
9513
10028
|
function SetRow({ label, hint, children }) {
|
|
9514
|
-
return (0,
|
|
10029
|
+
return (0, import_react26.createElement)(
|
|
9515
10030
|
"div",
|
|
9516
10031
|
{ className: "bt-set-row" },
|
|
9517
|
-
(0,
|
|
9518
|
-
children ? (0,
|
|
10032
|
+
(0, import_react26.createElement)("div", { className: "bt-set-copy" }, (0, import_react26.createElement)("span", null, label), hint ? (0, import_react26.createElement)("span", { className: "bt-set-hint" }, hint) : null),
|
|
10033
|
+
children ? (0, import_react26.createElement)("div", { className: "bt-set-control" }, children) : null
|
|
9519
10034
|
);
|
|
9520
10035
|
}
|
|
9521
10036
|
function GeneralPage({ roster, actions, run }) {
|
|
9522
10037
|
const prefs = roster.prefs ?? {};
|
|
9523
10038
|
const themes = allThemes();
|
|
9524
10039
|
const current = themes[prefs.theme] ? prefs.theme : "deepseek";
|
|
9525
|
-
const [custom, setCustom] = (0,
|
|
9526
|
-
const timer = (0,
|
|
9527
|
-
(0,
|
|
10040
|
+
const [custom, setCustom] = (0, import_react26.useState)(prefs.accent ?? "#4d6bfe");
|
|
10041
|
+
const timer = (0, import_react26.useRef)(0);
|
|
10042
|
+
(0, import_react26.useEffect)(() => () => clearTimeout(timer.current), []);
|
|
9528
10043
|
const pickCustom = (value) => {
|
|
9529
10044
|
setCustom(value);
|
|
9530
10045
|
clearTimeout(timer.current);
|
|
9531
10046
|
timer.current = setTimeout(() => run(actions.setPrefs({ accent: value })), 350);
|
|
9532
10047
|
};
|
|
9533
10048
|
return [
|
|
9534
|
-
(0,
|
|
10049
|
+
(0, import_react26.createElement)(
|
|
9535
10050
|
SetSection,
|
|
9536
10051
|
{ key: "look", title: t("Appearance") },
|
|
9537
|
-
(0,
|
|
10052
|
+
(0, import_react26.createElement)(
|
|
9538
10053
|
SetRow,
|
|
9539
10054
|
{ label: t("Theme"), hint: t("Colors for the whole team, on every device.") },
|
|
9540
|
-
(0,
|
|
10055
|
+
(0, import_react26.createElement)(
|
|
9541
10056
|
"select",
|
|
9542
10057
|
{ className: "bt-select", "aria-label": t("Theme"), value: current, onChange: (event) => run(actions.setPrefs({ theme: event.target.value })) },
|
|
9543
|
-
Object.entries(themes).map(([id, theme]) => (0,
|
|
10058
|
+
Object.entries(themes).map(([id, theme]) => (0, import_react26.createElement)("option", { key: id, value: id }, t(theme.label)))
|
|
9544
10059
|
)
|
|
9545
10060
|
),
|
|
9546
|
-
(0,
|
|
10061
|
+
(0, import_react26.createElement)(
|
|
9547
10062
|
SetRow,
|
|
9548
10063
|
{ label: t("Accent"), hint: t("Unread marks, badges, and the brand mark.") },
|
|
9549
|
-
(0,
|
|
10064
|
+
(0, import_react26.createElement)(
|
|
9550
10065
|
"div",
|
|
9551
10066
|
{ className: "bt-swatches" },
|
|
9552
|
-
(0,
|
|
9553
|
-
ACCENT_INKS.map((ink) => (0,
|
|
10067
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-swatch bt-swatch-default", title: t("Theme accent"), "aria-label": t("Theme accent"), "aria-pressed": !prefs.accent, onClick: () => run(actions.setPrefs({ accent: null })) }),
|
|
10068
|
+
ACCENT_INKS.map((ink) => (0, import_react26.createElement)("button", {
|
|
9554
10069
|
key: ink,
|
|
9555
10070
|
type: "button",
|
|
9556
10071
|
className: "bt-swatch",
|
|
@@ -9560,21 +10075,21 @@ function GeneralPage({ roster, actions, run }) {
|
|
|
9560
10075
|
style: { background: INKS[ink][0] },
|
|
9561
10076
|
onClick: () => run(actions.setPrefs({ accent: INKS[ink][0] }))
|
|
9562
10077
|
})),
|
|
9563
|
-
(0,
|
|
10078
|
+
(0, import_react26.createElement)(
|
|
9564
10079
|
"label",
|
|
9565
10080
|
{ className: "bt-swatch bt-swatch-custom", title: t("Custom accent"), "data-on": Boolean(prefs.accent) && !ACCENT_INKS.some((ink) => INKS[ink][0] === prefs.accent) },
|
|
9566
|
-
(0,
|
|
10081
|
+
(0, import_react26.createElement)("input", { type: "color", value: custom, "aria-label": t("Custom accent"), onChange: (event) => pickCustom(event.target.value) })
|
|
9567
10082
|
)
|
|
9568
10083
|
)
|
|
9569
10084
|
)
|
|
9570
10085
|
),
|
|
9571
|
-
(0,
|
|
10086
|
+
(0, import_react26.createElement)(
|
|
9572
10087
|
SetSection,
|
|
9573
10088
|
{ key: "mode", title: t("Mode") },
|
|
9574
|
-
(0,
|
|
10089
|
+
(0, import_react26.createElement)(
|
|
9575
10090
|
SetRow,
|
|
9576
10091
|
{ label: t("Classic Agent mode") },
|
|
9577
|
-
(0,
|
|
10092
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => actions.setSurface("agent") }, t("Switch"))
|
|
9578
10093
|
)
|
|
9579
10094
|
)
|
|
9580
10095
|
];
|
|
@@ -9583,52 +10098,52 @@ function MainBotsSection({ roster, actions, run }) {
|
|
|
9583
10098
|
const mains = (roster.mainBotIds ?? []).map((id) => roster.byId[id]).filter(Boolean);
|
|
9584
10099
|
const [first, ...others] = mains;
|
|
9585
10100
|
const candidates = roster.bots.filter((bot) => !isMainOf(roster, bot.id));
|
|
9586
|
-
return (0,
|
|
10101
|
+
return (0, import_react26.createElement)(
|
|
9587
10102
|
SetSection,
|
|
9588
10103
|
{ title: t("Main Bot") },
|
|
9589
|
-
(0,
|
|
10104
|
+
(0, import_react26.createElement)(
|
|
9590
10105
|
SetRow,
|
|
9591
10106
|
{
|
|
9592
10107
|
label: t("Main Bot"),
|
|
9593
10108
|
hint: first ? t("First in the sidebar. Pick another Bot to hand it the role; {name} then stops being a Main Bot.", { name: first.name }) : void 0
|
|
9594
10109
|
},
|
|
9595
|
-
(0,
|
|
10110
|
+
(0, import_react26.createElement)("select", {
|
|
9596
10111
|
className: "bt-select",
|
|
9597
10112
|
"aria-label": t("Main Bot"),
|
|
9598
10113
|
value: first?.id ?? "",
|
|
9599
10114
|
onChange: (event) => run(actions.setMain(event.target.value, true, first ? { replace: first.id } : {}))
|
|
9600
|
-
}, first ? null : (0,
|
|
10115
|
+
}, first ? null : (0, import_react26.createElement)("option", { value: "" }, t("Choose a Bot…")), roster.bots.map((bot) => (0, import_react26.createElement)("option", { key: bot.id, value: bot.id }, bot.name)))
|
|
9601
10116
|
),
|
|
9602
|
-
others.map((bot) => (0,
|
|
10117
|
+
others.map((bot) => (0, import_react26.createElement)(
|
|
9603
10118
|
SetRow,
|
|
9604
10119
|
{ key: bot.id, label: bot.name, hint: t("Also a Main Bot.") },
|
|
9605
|
-
(0,
|
|
9606
|
-
(0,
|
|
10120
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => run(actions.setMain(bot.id, true, { primary: true })) }, t("Move to first")),
|
|
10121
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => run(actions.setMain(bot.id, false)) }, t("Remove Main Bot role"))
|
|
9607
10122
|
)),
|
|
9608
|
-
candidates.length > 0 ? (0,
|
|
10123
|
+
candidates.length > 0 ? (0, import_react26.createElement)(
|
|
9609
10124
|
SetRow,
|
|
9610
10125
|
{ label: t("Add a Main Bot"), hint: t("Main Bots can create and change Bots, and run every group chat.") },
|
|
9611
|
-
(0,
|
|
10126
|
+
(0, import_react26.createElement)("select", {
|
|
9612
10127
|
className: "bt-select",
|
|
9613
10128
|
"aria-label": t("Add a Main Bot"),
|
|
9614
10129
|
value: "",
|
|
9615
10130
|
onChange: (event) => {
|
|
9616
10131
|
if (event.target.value) void run(actions.setMain(event.target.value, true));
|
|
9617
10132
|
}
|
|
9618
|
-
}, (0,
|
|
10133
|
+
}, (0, import_react26.createElement)("option", { value: "" }, t("Choose a Bot…")), candidates.map((bot) => (0, import_react26.createElement)("option", { key: bot.id, value: bot.id }, bot.name)))
|
|
9619
10134
|
) : null
|
|
9620
10135
|
);
|
|
9621
10136
|
}
|
|
9622
10137
|
var MOTIONS = [["quiet", "Quiet"], ["normal", "Normal"], ["lively", "Lively"]];
|
|
9623
10138
|
function MotionSection({ roster, actions, run }) {
|
|
9624
10139
|
const motion = roster.prefs?.motion === "quiet" || roster.prefs?.motion === "lively" ? roster.prefs.motion : "normal";
|
|
9625
|
-
return (0,
|
|
10140
|
+
return (0, import_react26.createElement)(
|
|
9626
10141
|
SetSection,
|
|
9627
10142
|
{ title: t("Animation") },
|
|
9628
|
-
(0,
|
|
10143
|
+
(0, import_react26.createElement)(
|
|
9629
10144
|
SetRow,
|
|
9630
10145
|
{ label: t("Bot animation") },
|
|
9631
|
-
(0,
|
|
10146
|
+
(0, import_react26.createElement)(Segmented, { label: t("Bot animation"), value: motion, className: "bt-motion-seg" }, MOTIONS.map(([id, label]) => (0, import_react26.createElement)("button", {
|
|
9632
10147
|
key: id,
|
|
9633
10148
|
type: "button",
|
|
9634
10149
|
"aria-pressed": motion === id,
|
|
@@ -9640,69 +10155,69 @@ function MotionSection({ roster, actions, run }) {
|
|
|
9640
10155
|
);
|
|
9641
10156
|
}
|
|
9642
10157
|
function BotsPage({ roster, actions, run }) {
|
|
9643
|
-
const [creating, setCreating] = (0,
|
|
10158
|
+
const [creating, setCreating] = (0, import_react26.useState)(false);
|
|
9644
10159
|
return [
|
|
9645
|
-
(0,
|
|
9646
|
-
(0,
|
|
9647
|
-
creating ? (0,
|
|
10160
|
+
(0, import_react26.createElement)(MainBotsSection, { key: "main", roster, actions, run }),
|
|
10161
|
+
(0, import_react26.createElement)(MotionSection, { key: "motion", roster, actions, run }),
|
|
10162
|
+
creating ? (0, import_react26.createElement)(CreateBot, { key: "create", roster, actions, inline: true, onBack: () => setCreating(false), onCreated: (bot) => actions.openSettings("bots", bot.id) }) : (0, import_react26.createElement)(
|
|
9648
10163
|
"div",
|
|
9649
10164
|
{ key: "new", className: "bt-actions" },
|
|
9650
|
-
(0,
|
|
10165
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm bt-with-icon", onClick: () => setCreating(true) }, (0, import_react26.createElement)(PlusIcon), t("New Bot"))
|
|
9651
10166
|
),
|
|
9652
|
-
(0,
|
|
10167
|
+
(0, import_react26.createElement)(
|
|
9653
10168
|
SetSection,
|
|
9654
10169
|
{ key: "bots", title: t("Bots · {count}", { count: roster.bots.length }) },
|
|
9655
|
-
roster.bots.map((bot) => (0,
|
|
10170
|
+
roster.bots.map((bot) => (0, import_react26.createElement)(
|
|
9656
10171
|
"button",
|
|
9657
10172
|
{ key: bot.id, type: "button", className: "bt-set-row", onClick: () => actions.openSettings("bots", bot.id) },
|
|
9658
|
-
(0,
|
|
10173
|
+
(0, import_react26.createElement)(
|
|
9659
10174
|
"span",
|
|
9660
10175
|
{ className: "bt-set-bot" },
|
|
9661
|
-
(0,
|
|
9662
|
-
(0,
|
|
9663
|
-
bot.role ? (0,
|
|
9664
|
-
isMainOf(roster, bot.id) ? (0,
|
|
9665
|
-
bot.hidden && !isMainOf(roster, bot.id) ? (0,
|
|
10176
|
+
(0, import_react26.createElement)(BotAvatar, { bot, size: 28, badge: false, live: false }),
|
|
10177
|
+
(0, import_react26.createElement)("span", { className: "bt-row-title" }, bot.name),
|
|
10178
|
+
bot.role ? (0, import_react26.createElement)("span", { className: "bt-tag" }, bot.role) : null,
|
|
10179
|
+
isMainOf(roster, bot.id) ? (0, import_react26.createElement)("span", { className: "bt-tag" }, t("Main Bot")) : null,
|
|
10180
|
+
bot.hidden && !isMainOf(roster, bot.id) ? (0, import_react26.createElement)("span", { className: "bt-tag" }, t("Hidden")) : null
|
|
9666
10181
|
),
|
|
9667
|
-
(0,
|
|
10182
|
+
(0, import_react26.createElement)("span", { className: "bt-chevron" }, (0, import_react26.createElement)(ChevronRightIcon))
|
|
9668
10183
|
))
|
|
9669
10184
|
)
|
|
9670
10185
|
];
|
|
9671
10186
|
}
|
|
9672
10187
|
function GroupsPage({ roster, actions, run }) {
|
|
9673
|
-
const [creating, setCreating] = (0,
|
|
10188
|
+
const [creating, setCreating] = (0, import_react26.useState)(false);
|
|
9674
10189
|
return [
|
|
9675
|
-
creating ? (0,
|
|
10190
|
+
creating ? (0, import_react26.createElement)(NewGroupForm, { key: "create", roster, actions, run, onCancel: () => setCreating(false), onCreated: (room2) => actions.openSettings("groups", room2.id) }) : (0, import_react26.createElement)(
|
|
9676
10191
|
"div",
|
|
9677
10192
|
{ key: "new", className: "bt-actions" },
|
|
9678
|
-
(0,
|
|
10193
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm bt-with-icon", disabled: roster.bots.length < 2, title: roster.bots.length < 2 ? t("A group chat needs at least two Bots") : void 0, onClick: () => setCreating(true) }, (0, import_react26.createElement)(PlusIcon), t("Create group chat"))
|
|
9679
10194
|
),
|
|
9680
|
-
(0,
|
|
10195
|
+
(0, import_react26.createElement)(
|
|
9681
10196
|
SetSection,
|
|
9682
10197
|
{ key: "groups", title: t("Group chats · {count}", { count: roster.rooms.length }) },
|
|
9683
|
-
roster.rooms.length === 0 ? (0,
|
|
10198
|
+
roster.rooms.length === 0 ? (0, import_react26.createElement)(SetRow, { label: t("No group chats yet."), hint: t("A group chat lets several Bots work on one thing with you.") }) : roster.rooms.map((room2) => {
|
|
9684
10199
|
const admin = roster.byId[room2.admin];
|
|
9685
|
-
return (0,
|
|
10200
|
+
return (0, import_react26.createElement)(
|
|
9686
10201
|
"button",
|
|
9687
10202
|
{ key: room2.id, type: "button", className: "bt-set-row", onClick: () => actions.openSettings("groups", room2.id) },
|
|
9688
|
-
(0,
|
|
10203
|
+
(0, import_react26.createElement)(
|
|
9689
10204
|
"span",
|
|
9690
10205
|
{ className: "bt-set-bot" },
|
|
9691
|
-
(0,
|
|
9692
|
-
(0,
|
|
9693
|
-
(0,
|
|
9694
|
-
admin ? (0,
|
|
9695
|
-
room2.hidden ? (0,
|
|
10206
|
+
(0, import_react26.createElement)(RoomAvatar, { room: room2, roster, size: 28 }),
|
|
10207
|
+
(0, import_react26.createElement)("span", { className: "bt-row-title" }, room2.name),
|
|
10208
|
+
(0, import_react26.createElement)("span", { className: "bt-tag" }, t("{count} members", { count: room2.members.length })),
|
|
10209
|
+
admin ? (0, import_react26.createElement)("span", { className: "bt-tag bt-tag-admin" }, (0, import_react26.createElement)(AdminStar), admin.name) : null,
|
|
10210
|
+
room2.hidden ? (0, import_react26.createElement)("span", { className: "bt-tag" }, t("Hidden")) : null
|
|
9696
10211
|
),
|
|
9697
|
-
(0,
|
|
10212
|
+
(0, import_react26.createElement)("span", { className: "bt-chevron" }, (0, import_react26.createElement)(ChevronRightIcon))
|
|
9698
10213
|
);
|
|
9699
10214
|
})
|
|
9700
10215
|
)
|
|
9701
10216
|
];
|
|
9702
10217
|
}
|
|
9703
|
-
function SettingsDialog({ page, id, roster, actions, leaving = false }) {
|
|
9704
|
-
const [error, setError] = (0,
|
|
9705
|
-
const close = (0,
|
|
10218
|
+
function SettingsDialog({ page, id, roster, actions, useUpdate, leaving = false }) {
|
|
10219
|
+
const [error, setError] = (0, import_react26.useState)("");
|
|
10220
|
+
const close = (0, import_react26.useCallback)(() => actions.closeSettings(), []);
|
|
9706
10221
|
useEscape(close);
|
|
9707
10222
|
const run = (promise) => Promise.resolve(promise).then(() => {
|
|
9708
10223
|
setError("");
|
|
@@ -9720,32 +10235,32 @@ function SettingsDialog({ page, id, roster, actions, leaving = false }) {
|
|
|
9720
10235
|
const nav = useGlide(shown, { axis: "y" });
|
|
9721
10236
|
const pageKey = `${shown}:${bot?.id ?? room2?.id ?? ""}`;
|
|
9722
10237
|
const depth = bot || room2 ? 1 : 0;
|
|
9723
|
-
const last = (0,
|
|
10238
|
+
const last = (0, import_react26.useRef)({ key: pageKey, depth, move: "up" });
|
|
9724
10239
|
if (last.current.key !== pageKey) {
|
|
9725
10240
|
last.current = { key: pageKey, depth, move: depth > last.current.depth ? "in" : depth < last.current.depth ? "out" : "up" };
|
|
9726
10241
|
}
|
|
9727
|
-
const [usageOpened, setUsageOpened] = (0,
|
|
10242
|
+
const [usageOpened, setUsageOpened] = (0, import_react26.useState)(shown === "usage");
|
|
9728
10243
|
if (shown === "usage" && !usageOpened) setUsageOpened(true);
|
|
9729
|
-
const errorLine = error ? (0,
|
|
9730
|
-
return (0,
|
|
10244
|
+
const errorLine = error ? (0, import_react26.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
|
|
10245
|
+
return (0, import_react26.createElement)(
|
|
9731
10246
|
"div",
|
|
9732
10247
|
{ className: "bt-settings-layer", "data-leaving": leaving || void 0 },
|
|
9733
|
-
(0,
|
|
9734
|
-
(0,
|
|
10248
|
+
(0, import_react26.createElement)("div", { className: "bt-scrim", onMouseDown: close }),
|
|
10249
|
+
(0, import_react26.createElement)(
|
|
9735
10250
|
"div",
|
|
9736
10251
|
{ className: "bt-settings", role: "dialog", "aria-modal": true, "aria-label": t("DS Bot settings"), "data-wide": shown === "usage" ? "" : void 0 },
|
|
9737
|
-
(0,
|
|
10252
|
+
(0, import_react26.createElement)(
|
|
9738
10253
|
"nav",
|
|
9739
10254
|
{ ref: nav.box, className: "bt-settings-nav", "aria-label": t("Settings pages") },
|
|
9740
|
-
(0,
|
|
9741
|
-
SETTINGS_PAGES.map(([key, text]) => (0,
|
|
10255
|
+
(0, import_react26.createElement)("span", { ref: nav.thumb, className: "bt-thumb", "aria-hidden": true }),
|
|
10256
|
+
SETTINGS_PAGES.map(([key, text]) => (0, import_react26.createElement)("button", {
|
|
9742
10257
|
key,
|
|
9743
10258
|
type: "button",
|
|
9744
10259
|
className: "bt-settings-item",
|
|
9745
10260
|
"aria-current": shown === key ? "page" : void 0,
|
|
9746
10261
|
onClick: () => actions.openSettings(key)
|
|
9747
|
-
}, (0,
|
|
9748
|
-
(0,
|
|
10262
|
+
}, (0, import_react26.createElement)(PAGE_ICONS[key]), t(text))),
|
|
10263
|
+
(0, import_react26.createElement)("button", {
|
|
9749
10264
|
type: "button",
|
|
9750
10265
|
className: "bt-settings-item bt-settings-shell",
|
|
9751
10266
|
title: t("Models, providers, and the rest of DeepSeek Harness"),
|
|
@@ -9753,62 +10268,63 @@ function SettingsDialog({ page, id, roster, actions, leaving = false }) {
|
|
|
9753
10268
|
close();
|
|
9754
10269
|
openHarnessSettings();
|
|
9755
10270
|
}
|
|
9756
|
-
}, (0,
|
|
10271
|
+
}, (0, import_react26.createElement)(SlidersIcon), t("Harness settings"))
|
|
9757
10272
|
),
|
|
9758
|
-
(0,
|
|
10273
|
+
(0, import_react26.createElement)(
|
|
9759
10274
|
"div",
|
|
9760
10275
|
{ className: "bt-settings-main" },
|
|
9761
|
-
(0,
|
|
10276
|
+
(0, import_react26.createElement)(
|
|
9762
10277
|
"div",
|
|
9763
10278
|
{ className: "bt-settings-head" },
|
|
9764
|
-
back ? (0,
|
|
9765
|
-
(0,
|
|
10279
|
+
back ? (0, import_react26.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": back, title: back, onClick: () => actions.openSettings(shown) }, (0, import_react26.createElement)(ChevronLeftIcon)) : null,
|
|
10280
|
+
(0, import_react26.createElement)("h2", { key: bot?.id ?? room2?.id ?? shown }, bot?.name ?? room2?.name ?? label)
|
|
9766
10281
|
),
|
|
9767
|
-
shown === "usage" ? null : (0,
|
|
10282
|
+
shown === "usage" ? null : (0, import_react26.createElement)(
|
|
9768
10283
|
"div",
|
|
9769
10284
|
{ key: pageKey, className: "bt-settings-scroll bt-settings-page", "data-move": last.current.move },
|
|
9770
10285
|
errorLine,
|
|
9771
|
-
shown === "general" ? (0,
|
|
9772
|
-
bot ? (0,
|
|
9773
|
-
shown === "bots" && !bot ? (0,
|
|
9774
|
-
room2 ? (0,
|
|
9775
|
-
shown === "groups" && !room2 ? (0,
|
|
9776
|
-
shown === "connectors" ? (0,
|
|
9777
|
-
shown === "schedules" ? (0,
|
|
9778
|
-
shown === "secrets" ? (0,
|
|
9779
|
-
shown === "feedback" ? (0,
|
|
10286
|
+
shown === "general" ? (0, import_react26.createElement)(GeneralPage, { roster, actions, run }) : null,
|
|
10287
|
+
bot ? (0, import_react26.createElement)("div", { className: "bt-settings-form" }, (0, import_react26.createElement)(BotSettingsForm, { key: bot.id, bot, roster, actions, run })) : null,
|
|
10288
|
+
shown === "bots" && !bot ? (0, import_react26.createElement)(BotsPage, { roster, actions, run }) : null,
|
|
10289
|
+
room2 ? (0, import_react26.createElement)("div", { className: "bt-settings-form" }, (0, import_react26.createElement)(GroupSettingsForm, { key: room2.id, room: room2, roster, actions, run })) : null,
|
|
10290
|
+
shown === "groups" && !room2 ? (0, import_react26.createElement)(GroupsPage, { roster, actions, run }) : null,
|
|
10291
|
+
shown === "connectors" ? (0, import_react26.createElement)(ConnectorsPage, { roster, actions }) : null,
|
|
10292
|
+
shown === "schedules" ? (0, import_react26.createElement)(SchedulesPage, { roster, actions, start: id ?? void 0 }) : null,
|
|
10293
|
+
shown === "secrets" ? (0, import_react26.createElement)(SecretsPage, { roster, actions, run, Section: SetSection }) : null,
|
|
10294
|
+
shown === "feedback" ? (0, import_react26.createElement)(FeedbackPage, { actions, run, Section: SetSection, Row: SetRow }) : null,
|
|
10295
|
+
shown === "about" ? (0, import_react26.createElement)(AboutPage, { useUpdate, actions, run, Section: SetSection, Row: SetRow }) : null
|
|
9780
10296
|
),
|
|
9781
|
-
usageOpened ? (0,
|
|
10297
|
+
usageOpened ? (0, import_react26.createElement)(
|
|
9782
10298
|
"div",
|
|
9783
10299
|
{ key: "usage", className: "bt-settings-scroll bt-settings-usage", "data-off": shown === "usage" ? void 0 : "", "aria-hidden": shown === "usage" ? void 0 : true },
|
|
9784
10300
|
shown === "usage" ? errorLine : null,
|
|
9785
|
-
(0,
|
|
10301
|
+
(0, import_react26.createElement)(UsagePage, { roster, actions, focusId: usageFocus })
|
|
9786
10302
|
) : null
|
|
9787
10303
|
),
|
|
9788
|
-
(0,
|
|
10304
|
+
(0, import_react26.createElement)("button", { type: "button", className: "bt-settings-close", "aria-label": t("Close settings"), onClick: close }, (0, import_react26.createElement)(CloseIcon))
|
|
9789
10305
|
)
|
|
9790
10306
|
);
|
|
9791
10307
|
}
|
|
9792
10308
|
|
|
9793
10309
|
// src/client/context-menu.js
|
|
9794
|
-
var
|
|
10310
|
+
var import_react27 = require("react");
|
|
9795
10311
|
function ContextMenu({ menu, roster, actions, leaving = false }) {
|
|
9796
10312
|
useEscape(() => actions.closeMenu());
|
|
9797
|
-
(0,
|
|
10313
|
+
(0, import_react27.useEffect)(() => {
|
|
9798
10314
|
const onDown = (event) => {
|
|
9799
10315
|
if (!(event.target instanceof Element) || !event.target.closest(".bt-menu")) actions.closeMenu();
|
|
9800
10316
|
};
|
|
9801
10317
|
window.addEventListener("mousedown", onDown);
|
|
9802
10318
|
return () => window.removeEventListener("mousedown", onDown);
|
|
9803
10319
|
}, []);
|
|
9804
|
-
const ref = (0,
|
|
9805
|
-
const [place, setPlace] = (0,
|
|
9806
|
-
(0,
|
|
10320
|
+
const ref = (0, import_react27.useRef)(null);
|
|
10321
|
+
const [place, setPlace] = (0, import_react27.useState)({ left: menu.x, top: menu.y });
|
|
10322
|
+
(0, import_react27.useLayoutEffect)(() => {
|
|
9807
10323
|
const box = ref.current?.getBoundingClientRect();
|
|
9808
10324
|
if (!box) return;
|
|
9809
10325
|
setPlace({ left: Math.max(8, Math.min(menu.x, window.innerWidth - box.width - 8)), top: Math.max(8, Math.min(menu.y, window.innerHeight - box.height - 8)) });
|
|
9810
10326
|
}, [menu.x, menu.y]);
|
|
9811
|
-
if (menu.kind === "agent" || menu.kind === "agent-session") return (0,
|
|
10327
|
+
if (menu.kind === "agent" || menu.kind === "agent-session") return (0, import_react27.createElement)(AgentMenu, { menu, roster, actions, leaving });
|
|
9812
10328
|
const bot = roster.byId[menu.id];
|
|
9813
10329
|
const room2 = roster.roomsById[menu.id];
|
|
9814
10330
|
const entry = bot ?? room2;
|
|
@@ -9839,14 +10355,14 @@ function ContextMenu({ menu, roster, actions, leaving = false }) {
|
|
|
9839
10355
|
isMainBot ? null : [room2 ? t("Delete group") : t("Delete"), TrashIcon, () => bot ? actions.deleteBot(bot.id) : actions.deleteRoom(menu.id), true]
|
|
9840
10356
|
]
|
|
9841
10357
|
].map((group) => group.filter(Boolean)).filter((group) => group.length > 0);
|
|
9842
|
-
return (0,
|
|
10358
|
+
return (0, import_react27.createElement)(
|
|
9843
10359
|
"div",
|
|
9844
10360
|
{ ref, className: "bt-menu", role: "menu", "aria-label": t("{name} actions", { name: bot?.name ?? room2.name }), style: place, "data-leaving": leaving || void 0 },
|
|
9845
|
-
groups.map((group, index) => (0,
|
|
9846
|
-
|
|
10361
|
+
groups.map((group, index) => (0, import_react27.createElement)(
|
|
10362
|
+
import_react27.Fragment,
|
|
9847
10363
|
{ key: index },
|
|
9848
|
-
index > 0 ? (0,
|
|
9849
|
-
group.map(([label, Icon, run, danger]) => (0,
|
|
10364
|
+
index > 0 ? (0, import_react27.createElement)("div", { className: "bt-menu-sep", role: "separator" }) : null,
|
|
10365
|
+
group.map(([label, Icon, run, danger]) => (0, import_react27.createElement)("button", {
|
|
9850
10366
|
key: label,
|
|
9851
10367
|
type: "button",
|
|
9852
10368
|
role: "menuitem",
|
|
@@ -9855,14 +10371,14 @@ function ContextMenu({ menu, roster, actions, leaving = false }) {
|
|
|
9855
10371
|
actions.closeMenu();
|
|
9856
10372
|
void Promise.resolve(run()).catch((error) => actions.reportError(error, "menu"));
|
|
9857
10373
|
}
|
|
9858
|
-
}, (0,
|
|
10374
|
+
}, (0, import_react27.createElement)(Icon), label))
|
|
9859
10375
|
))
|
|
9860
10376
|
);
|
|
9861
10377
|
}
|
|
9862
10378
|
function AgentMenu({ menu, roster, actions, leaving }) {
|
|
9863
|
-
const ref = (0,
|
|
9864
|
-
const [place, setPlace] = (0,
|
|
9865
|
-
(0,
|
|
10379
|
+
const ref = (0, import_react27.useRef)(null);
|
|
10380
|
+
const [place, setPlace] = (0, import_react27.useState)({ left: menu.x, top: menu.y });
|
|
10381
|
+
(0, import_react27.useLayoutEffect)(() => {
|
|
9866
10382
|
const box = ref.current?.getBoundingClientRect();
|
|
9867
10383
|
if (!box) return;
|
|
9868
10384
|
setPlace({ left: Math.max(8, Math.min(menu.x, window.innerWidth - box.width - 8)), top: Math.max(8, Math.min(menu.y, window.innerHeight - box.height - 8)) });
|
|
@@ -9884,14 +10400,14 @@ function AgentMenu({ menu, roster, actions, leaving }) {
|
|
|
9884
10400
|
[t("Archive"), TrashIcon, () => actions.archiveAgentSession(agent.id, menu.id), true]
|
|
9885
10401
|
]
|
|
9886
10402
|
];
|
|
9887
|
-
return (0,
|
|
10403
|
+
return (0, import_react27.createElement)(
|
|
9888
10404
|
"div",
|
|
9889
10405
|
{ ref, className: "bt-menu", role: "menu", "aria-label": menu.kind === "agent" ? "DSH Agent" : t("Session actions"), style: place, "data-leaving": leaving || void 0 },
|
|
9890
|
-
groups.map((group, index) => (0,
|
|
9891
|
-
|
|
10406
|
+
groups.map((group, index) => (0, import_react27.createElement)(
|
|
10407
|
+
import_react27.Fragment,
|
|
9892
10408
|
{ key: index },
|
|
9893
|
-
index > 0 ? (0,
|
|
9894
|
-
group.map(([label, Icon, run, danger]) => (0,
|
|
10409
|
+
index > 0 ? (0, import_react27.createElement)("div", { className: "bt-menu-sep", role: "separator" }) : null,
|
|
10410
|
+
group.map(([label, Icon, run, danger]) => (0, import_react27.createElement)("button", {
|
|
9895
10411
|
key: label,
|
|
9896
10412
|
type: "button",
|
|
9897
10413
|
role: "menuitem",
|
|
@@ -9900,7 +10416,7 @@ function AgentMenu({ menu, roster, actions, leaving }) {
|
|
|
9900
10416
|
actions.closeMenu();
|
|
9901
10417
|
void Promise.resolve(run()).catch((error) => console.warn("[ds-bot]", error));
|
|
9902
10418
|
}
|
|
9903
|
-
}, (0,
|
|
10419
|
+
}, (0, import_react27.createElement)(Icon), label))
|
|
9904
10420
|
))
|
|
9905
10421
|
);
|
|
9906
10422
|
}
|
|
@@ -9970,8 +10486,142 @@ function migrateStorage(storage = globalThis.localStorage) {
|
|
|
9970
10486
|
}
|
|
9971
10487
|
}
|
|
9972
10488
|
|
|
10489
|
+
// src/client/member-status.js
|
|
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
|
+
|
|
9973
10623
|
// src/client/details-drawer.js
|
|
9974
|
-
var
|
|
10624
|
+
var import_react29 = require("react");
|
|
9975
10625
|
function transcriptOf(list, id, name, hiddenTurns = []) {
|
|
9976
10626
|
const outline = list.projectionsBySession?.[id]?.values?.turnOutline ?? list.byId[id]?.projectionValues?.turnOutline;
|
|
9977
10627
|
if (!Array.isArray(outline)) return "";
|
|
@@ -9996,10 +10646,10 @@ var EMPTY_TABS = { ids: [], active: null };
|
|
|
9996
10646
|
var PERMISSION_LABELS = { "read-only": "Read only", "workspace-write": "Workspace", "danger-full-access": "Full access" };
|
|
9997
10647
|
var DANGER = /* @__PURE__ */ new Set(["danger-full-access"]);
|
|
9998
10648
|
function PermissionRow({ bot, roster, actions, run }) {
|
|
9999
|
-
const [pending, setPending] = (0,
|
|
10649
|
+
const [pending, setPending] = (0, import_react29.useState)(null);
|
|
10000
10650
|
const catalog = roster.permissions;
|
|
10001
10651
|
const saved = bot.permission ?? catalog?.defaultPreset;
|
|
10002
|
-
(0,
|
|
10652
|
+
(0, import_react29.useEffect)(() => {
|
|
10003
10653
|
if (pending === saved) setPending(null);
|
|
10004
10654
|
}, [pending, saved]);
|
|
10005
10655
|
if (!catalog?.options?.length) return null;
|
|
@@ -10009,14 +10659,14 @@ function PermissionRow({ bot, roster, actions, run }) {
|
|
|
10009
10659
|
setPending(value);
|
|
10010
10660
|
void run(actions.updateBot(bot.id, { permission: value })).catch(() => setPending(null));
|
|
10011
10661
|
};
|
|
10012
|
-
return (0,
|
|
10662
|
+
return (0, import_react29.createElement)(
|
|
10013
10663
|
"div",
|
|
10014
10664
|
{ className: "bt-drawer-permission" },
|
|
10015
|
-
(0,
|
|
10016
|
-
(0,
|
|
10665
|
+
(0, import_react29.createElement)("div", { className: "bt-section-title" }, t("Permissions")),
|
|
10666
|
+
(0, import_react29.createElement)(
|
|
10017
10667
|
Segmented,
|
|
10018
10668
|
{ label: t("Permissions"), value: current },
|
|
10019
|
-
catalog.options.map((option) => (0,
|
|
10669
|
+
catalog.options.map((option) => (0, import_react29.createElement)("button", {
|
|
10020
10670
|
key: option.value,
|
|
10021
10671
|
type: "button",
|
|
10022
10672
|
"aria-pressed": option.value === current,
|
|
@@ -10030,42 +10680,42 @@ function PermissionRow({ bot, roster, actions, run }) {
|
|
|
10030
10680
|
function BrowserCards({ bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) {
|
|
10031
10681
|
const state = useBrowserTabs((map) => map[bot.id] ?? EMPTY_TABS);
|
|
10032
10682
|
const pages = useBrowserPages((map) => map);
|
|
10033
|
-
(0,
|
|
10683
|
+
(0, import_react29.useEffect)(() => {
|
|
10034
10684
|
actions.browserEnsure?.(bot.id);
|
|
10035
10685
|
}, [bot.id]);
|
|
10036
10686
|
if (roster.browser !== true || !actions.browserShow) {
|
|
10037
|
-
return (0,
|
|
10687
|
+
return (0, import_react29.createElement)("div", { className: "bt-muted" }, t("The browser is in the DSH Desktop app."));
|
|
10038
10688
|
}
|
|
10039
10689
|
if (state.ids.length === 0) {
|
|
10040
|
-
return (0,
|
|
10690
|
+
return (0, import_react29.createElement)(
|
|
10041
10691
|
"div",
|
|
10042
10692
|
{ className: "bt-browser-empty-mark" },
|
|
10043
|
-
(0,
|
|
10044
|
-
(0,
|
|
10045
|
-
(0,
|
|
10046
|
-
(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"))
|
|
10047
10697
|
);
|
|
10048
10698
|
}
|
|
10049
|
-
return (0,
|
|
10699
|
+
return (0, import_react29.createElement)(
|
|
10050
10700
|
"div",
|
|
10051
10701
|
{ className: "bt-bcards" },
|
|
10052
10702
|
state.ids.map((tabId, index) => {
|
|
10053
10703
|
const page = pages[tabId] ?? {};
|
|
10054
10704
|
const label = page.title || shortUrl(page.url) || t("New tab");
|
|
10055
|
-
return (0,
|
|
10705
|
+
return (0, import_react29.createElement)(
|
|
10056
10706
|
"div",
|
|
10057
10707
|
{ key: tabId, className: "bt-bcard", "data-active": state.active === tabId || void 0, style: { animationDelay: `${index * 30}ms` } },
|
|
10058
|
-
(0,
|
|
10708
|
+
(0, import_react29.createElement)(
|
|
10059
10709
|
"button",
|
|
10060
10710
|
{ type: "button", className: "bt-bcard-shot", "aria-label": t("Open {title}", { title: label }), title: page.url, onClick: () => actions.browserPick(sessionId, tabId) },
|
|
10061
|
-
page.excerpt ? (0,
|
|
10711
|
+
page.excerpt ? (0, import_react29.createElement)(
|
|
10062
10712
|
"span",
|
|
10063
10713
|
{ className: "bt-bcard-preview" },
|
|
10064
|
-
(0,
|
|
10065
|
-
(0,
|
|
10066
|
-
) : (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))
|
|
10067
10717
|
),
|
|
10068
|
-
(0,
|
|
10718
|
+
(0, import_react29.createElement)("button", {
|
|
10069
10719
|
type: "button",
|
|
10070
10720
|
className: "bt-bcard-x",
|
|
10071
10721
|
"aria-label": t("Close tab"),
|
|
@@ -10074,78 +10724,87 @@ function BrowserCards({ bot, sessionId, roster, actions, useBrowserTabs, useBrow
|
|
|
10074
10724
|
void actions.browserCloseTab(bot.id, tabId);
|
|
10075
10725
|
}
|
|
10076
10726
|
}, "×"),
|
|
10077
|
-
(0,
|
|
10727
|
+
(0, import_react29.createElement)(
|
|
10078
10728
|
"div",
|
|
10079
10729
|
{ className: "bt-bcard-row" },
|
|
10080
|
-
(0,
|
|
10081
|
-
(0,
|
|
10082
|
-
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
|
|
10083
10733
|
)
|
|
10084
10734
|
);
|
|
10085
10735
|
}),
|
|
10086
|
-
(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"))
|
|
10087
10737
|
);
|
|
10088
10738
|
}
|
|
10089
10739
|
function About({ text, title = "About" }) {
|
|
10090
|
-
const [open, setOpen] = (0,
|
|
10740
|
+
const [open, setOpen] = (0, import_react29.useState)(false);
|
|
10091
10741
|
const long = text.split("\n").length > 5 || text.length > 240;
|
|
10092
|
-
const body = (0,
|
|
10742
|
+
const body = (0, import_react29.useRef)(null);
|
|
10093
10743
|
useMorph(body, open);
|
|
10094
|
-
return (0,
|
|
10744
|
+
return (0, import_react29.createElement)(
|
|
10095
10745
|
"div",
|
|
10096
10746
|
null,
|
|
10097
|
-
(0,
|
|
10098
|
-
(0,
|
|
10099
|
-
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
|
|
10100
10750
|
);
|
|
10101
10751
|
}
|
|
10102
|
-
function GroupOverview({ room: room2, roster, actions, usage, openSettings }) {
|
|
10752
|
+
function GroupOverview({ room: room2, roster, actions, usage, useWork, openSettings }) {
|
|
10103
10753
|
const members = room2.members.map((id) => roster.byId[id]).filter(Boolean);
|
|
10754
|
+
const { entries: entries2, now } = useMemberWork(room2, useWork);
|
|
10104
10755
|
return [
|
|
10105
|
-
room2.notice ? (0,
|
|
10106
|
-
(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)(
|
|
10107
10758
|
"div",
|
|
10108
10759
|
{ key: "members" },
|
|
10109
|
-
(0,
|
|
10110
|
-
(0,
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
"
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
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
|
+
}))
|
|
10121
10780
|
),
|
|
10122
|
-
(0,
|
|
10781
|
+
(0, import_react29.createElement)(
|
|
10123
10782
|
"div",
|
|
10124
10783
|
{ key: "mode" },
|
|
10125
|
-
(0,
|
|
10126
|
-
(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")))
|
|
10127
10786
|
),
|
|
10128
|
-
usage ? (0,
|
|
10787
|
+
usage ? (0, import_react29.createElement)(
|
|
10129
10788
|
"div",
|
|
10130
10789
|
{ key: "usage" },
|
|
10131
|
-
(0,
|
|
10132
|
-
(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))
|
|
10133
10792
|
) : null
|
|
10134
10793
|
];
|
|
10135
10794
|
}
|
|
10136
|
-
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 }) {
|
|
10137
10796
|
const owner = roster.byId[sessionId];
|
|
10138
10797
|
const part = owner ? currentPart(owner) : sessionId;
|
|
10139
10798
|
const isMainBot = isMainOf(roster, owner?.id);
|
|
10140
|
-
const [tab, setTab] = (0,
|
|
10799
|
+
const [tab, setTab] = (0, import_react29.useState)("details");
|
|
10141
10800
|
const room2 = roster.roomsById[sessionId];
|
|
10142
|
-
const [view, setView] = (0,
|
|
10143
|
-
const [editing, setEditing] = (0,
|
|
10144
|
-
const [draftName, setDraftName] = (0,
|
|
10145
|
-
const [copied, setCopied] = (0,
|
|
10146
|
-
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)("");
|
|
10147
10806
|
const glide = useGlide(tab);
|
|
10148
|
-
const last = (0,
|
|
10807
|
+
const last = (0, import_react29.useRef)({ view, tab, move: void 0 });
|
|
10149
10808
|
if (last.current.view !== view) last.current = { view, tab, move: view === "settings" ? "in" : "out" };
|
|
10150
10809
|
else if (last.current.tab !== tab) last.current = { view, tab, move: "tab" };
|
|
10151
10810
|
const move = last.current.move;
|
|
@@ -10170,36 +10829,36 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
|
|
|
10170
10829
|
setTimeout(() => setCopied(false), 1600);
|
|
10171
10830
|
});
|
|
10172
10831
|
};
|
|
10173
|
-
const errorLine = error ? (0,
|
|
10832
|
+
const errorLine = error ? (0, import_react29.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
|
|
10174
10833
|
if ((bot || room2) && view === "settings") {
|
|
10175
10834
|
return [
|
|
10176
|
-
(0,
|
|
10835
|
+
(0, import_react29.createElement)(
|
|
10177
10836
|
"div",
|
|
10178
10837
|
{ key: "head", className: "bt-subhead", "data-move": move },
|
|
10179
|
-
(0,
|
|
10180
|
-
(0,
|
|
10181
|
-
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))
|
|
10182
10841
|
),
|
|
10183
|
-
(0,
|
|
10842
|
+
(0, import_react29.createElement)(
|
|
10184
10843
|
"div",
|
|
10185
10844
|
{ key: "set-body", className: "bt-drawer-body bt-set-body", "data-move": move },
|
|
10186
10845
|
errorLine,
|
|
10187
|
-
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 })
|
|
10188
10847
|
)
|
|
10189
10848
|
];
|
|
10190
10849
|
}
|
|
10191
|
-
const top = (0,
|
|
10850
|
+
const top = (0, import_react29.createElement)(
|
|
10192
10851
|
"div",
|
|
10193
10852
|
{ className: "bt-drawer-top", "data-move": side },
|
|
10194
|
-
bot || room2 ? (0,
|
|
10195
|
-
bot && roster.browser === true && actions.openBrowser ? (0,
|
|
10196
|
-
bot || room2 ? (0,
|
|
10197
|
-
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))
|
|
10198
10857
|
);
|
|
10199
10858
|
if (!bot && !room2) {
|
|
10200
10859
|
return [
|
|
10201
|
-
(0,
|
|
10202
|
-
(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.")))
|
|
10203
10862
|
];
|
|
10204
10863
|
}
|
|
10205
10864
|
const current = bot?.name ?? room2.name;
|
|
@@ -10213,12 +10872,12 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
|
|
|
10213
10872
|
const subtitle = bot ? bot.role : roomSubtitle(room2, roster);
|
|
10214
10873
|
const tabs = bot ? ["details", "browser"] : ["details"];
|
|
10215
10874
|
return [
|
|
10216
|
-
(0,
|
|
10217
|
-
(0,
|
|
10875
|
+
(0, import_react29.createElement)(import_react29.Fragment, { key: "top" }, top),
|
|
10876
|
+
(0, import_react29.createElement)(
|
|
10218
10877
|
"div",
|
|
10219
10878
|
{ key: "id", className: "bt-drawer-id", "data-move": side },
|
|
10220
|
-
bot ? (0,
|
|
10221
|
-
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", {
|
|
10222
10881
|
className: "bt-drawer-name-input",
|
|
10223
10882
|
autoFocus: true,
|
|
10224
10883
|
value: draftName,
|
|
@@ -10233,49 +10892,49 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
|
|
|
10233
10892
|
setEditing(false);
|
|
10234
10893
|
}
|
|
10235
10894
|
}
|
|
10236
|
-
}) : (0,
|
|
10895
|
+
}) : (0, import_react29.createElement)("button", { type: "button", className: "bt-drawer-name", "aria-label": t("Rename {name}", { name: current }), title: t("Rename"), onClick: () => {
|
|
10237
10896
|
setDraftName(room2 && !room2.named ? "" : current);
|
|
10238
10897
|
setEditing(true);
|
|
10239
10898
|
} }, current),
|
|
10240
|
-
subtitle ? (0,
|
|
10899
|
+
subtitle ? (0, import_react29.createElement)("span", { className: "bt-drawer-role" }, subtitle) : null
|
|
10241
10900
|
),
|
|
10242
|
-
(0,
|
|
10901
|
+
(0, import_react29.createElement)(
|
|
10243
10902
|
"div",
|
|
10244
10903
|
{ key: "tabs", ref: glide.box, className: "bt-tabs", role: "tablist", "data-move": side },
|
|
10245
|
-
(0,
|
|
10246
|
-
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])))
|
|
10247
10906
|
),
|
|
10248
|
-
(0,
|
|
10907
|
+
(0, import_react29.createElement)(
|
|
10249
10908
|
"div",
|
|
10250
10909
|
{ key: `body:${tab}`, className: "bt-drawer-body", "data-move": move },
|
|
10251
10910
|
errorLine,
|
|
10252
|
-
room2 && tab === "details" ? (0,
|
|
10911
|
+
room2 && tab === "details" ? (0, import_react29.createElement)(GroupOverview, { room: room2, roster, actions, usage, useWork, openSettings: () => setView("settings") }) : null,
|
|
10253
10912
|
bot && tab === "details" ? [
|
|
10254
|
-
(0,
|
|
10913
|
+
(0, import_react29.createElement)(
|
|
10255
10914
|
"div",
|
|
10256
10915
|
{ key: "model", className: "bt-drawer-model" },
|
|
10257
|
-
(0,
|
|
10258
|
-
(0,
|
|
10259
|
-
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 }))
|
|
10260
10919
|
),
|
|
10261
|
-
(0,
|
|
10262
|
-
bot.instructions ? (0,
|
|
10263
|
-
roster.memory === false ? null : (0,
|
|
10264
|
-
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)(
|
|
10265
10924
|
"div",
|
|
10266
10925
|
{ key: "usage" },
|
|
10267
|
-
(0,
|
|
10268
|
-
(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))
|
|
10269
10928
|
) : null,
|
|
10270
|
-
(0,
|
|
10929
|
+
(0, import_react29.createElement)(ScheduleSummary, { key: "routines", bot, actions })
|
|
10271
10930
|
] : null,
|
|
10272
|
-
bot && tab === "browser" ? (0,
|
|
10931
|
+
bot && tab === "browser" ? (0, import_react29.createElement)(BrowserCards, { bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) : null
|
|
10273
10932
|
)
|
|
10274
10933
|
];
|
|
10275
10934
|
}
|
|
10276
10935
|
|
|
10277
10936
|
// src/client/browser-pane.js
|
|
10278
|
-
var
|
|
10937
|
+
var import_react30 = require("react");
|
|
10279
10938
|
var import_react_dom7 = require("react-dom");
|
|
10280
10939
|
var PANEL_LINGER_MS = 300;
|
|
10281
10940
|
var WIDTH_KEY = STORAGE.browserWidth;
|
|
@@ -10290,10 +10949,10 @@ var EMPTY_TABS2 = { ids: [], active: null };
|
|
|
10290
10949
|
var SPRING_LEAD2 = springEasing(576, 39.4);
|
|
10291
10950
|
var SPRING_TRAIL2 = springEasing(144, 19.7);
|
|
10292
10951
|
var icon = (paths) => function BrowserIcon() {
|
|
10293
|
-
return (0,
|
|
10952
|
+
return (0, import_react30.createElement)(
|
|
10294
10953
|
"svg",
|
|
10295
10954
|
{ width: 16, height: 16, viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true },
|
|
10296
|
-
paths.map((d, index) => (0,
|
|
10955
|
+
paths.map((d, index) => (0, import_react30.createElement)("path", { key: index, d }))
|
|
10297
10956
|
);
|
|
10298
10957
|
};
|
|
10299
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"]);
|
|
@@ -10305,9 +10964,9 @@ var CloseIcon2 = icon(["M5.5 5.5l9 9M14.5 5.5l-9 9"]);
|
|
|
10305
10964
|
var PlusTabIcon = icon(["M10 5v10M5 10h10"]);
|
|
10306
10965
|
var LockIcon2 = icon(["M6.5 9V7a3.5 3.5 0 0 1 7 0v2", "M5.5 9h9v7h-9z"]);
|
|
10307
10966
|
function Favicon({ src, className }) {
|
|
10308
|
-
const [failed, setFailed] = (0,
|
|
10309
|
-
if (!src || failed === src) return (0,
|
|
10310
|
-
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) });
|
|
10311
10970
|
}
|
|
10312
10971
|
var savedWidth = () => {
|
|
10313
10972
|
try {
|
|
@@ -10366,11 +11025,11 @@ var reducedMotion2 = () => typeof matchMedia === "function" && matchMedia("(pref
|
|
|
10366
11025
|
var tabLabel = (page) => page.title || shortAddress(page.url) || t("New tab");
|
|
10367
11026
|
function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions, useBrowserPages }) {
|
|
10368
11027
|
const pages = useBrowserPages((map) => map);
|
|
10369
|
-
const wrap = (0,
|
|
10370
|
-
const ind = (0,
|
|
10371
|
-
const spot = (0,
|
|
10372
|
-
const under = (0,
|
|
10373
|
-
(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)(() => {
|
|
10374
11033
|
const box = wrap.current;
|
|
10375
11034
|
const capsule = ind.current;
|
|
10376
11035
|
if (!box || !capsule) return void 0;
|
|
@@ -10399,7 +11058,7 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10399
11058
|
trail.cancel();
|
|
10400
11059
|
};
|
|
10401
11060
|
});
|
|
10402
|
-
(0,
|
|
11061
|
+
(0, import_react30.useEffect)(() => {
|
|
10403
11062
|
const box = wrap.current;
|
|
10404
11063
|
if (!box) return void 0;
|
|
10405
11064
|
const observer = new ResizeObserver(() => {
|
|
@@ -10413,13 +11072,13 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10413
11072
|
observer.observe(box);
|
|
10414
11073
|
return () => observer.disconnect();
|
|
10415
11074
|
}, []);
|
|
10416
|
-
return (0,
|
|
11075
|
+
return (0, import_react30.createElement)(
|
|
10417
11076
|
"div",
|
|
10418
11077
|
{ className: "bt-ptabs", role: "tablist", "aria-label": t("Panel tabs") },
|
|
10419
|
-
(0,
|
|
11078
|
+
(0, import_react30.createElement)(
|
|
10420
11079
|
"div",
|
|
10421
11080
|
{ className: "bt-ptabs-in", ref: wrap },
|
|
10422
|
-
(0,
|
|
11081
|
+
(0, import_react30.createElement)(
|
|
10423
11082
|
"button",
|
|
10424
11083
|
{
|
|
10425
11084
|
type: "button",
|
|
@@ -10429,13 +11088,13 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10429
11088
|
title: bot.name,
|
|
10430
11089
|
onClick: () => onView("details")
|
|
10431
11090
|
},
|
|
10432
|
-
(0,
|
|
10433
|
-
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
|
|
10434
11093
|
),
|
|
10435
11094
|
ids.map((tabId) => {
|
|
10436
11095
|
const page = pages[tabId] ?? EMPTY_PAGE;
|
|
10437
11096
|
const on = view === "browser" && tabId === active;
|
|
10438
|
-
return (0,
|
|
11097
|
+
return (0, import_react30.createElement)(
|
|
10439
11098
|
"button",
|
|
10440
11099
|
{
|
|
10441
11100
|
key: tabId,
|
|
@@ -10456,9 +11115,9 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10456
11115
|
}
|
|
10457
11116
|
}
|
|
10458
11117
|
},
|
|
10459
|
-
(0,
|
|
10460
|
-
(0,
|
|
10461
|
-
(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", {
|
|
10462
11121
|
className: "bt-ptab-x",
|
|
10463
11122
|
role: "button",
|
|
10464
11123
|
"aria-label": t("Close tab"),
|
|
@@ -10470,20 +11129,20 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
|
|
|
10470
11129
|
}, "×")
|
|
10471
11130
|
);
|
|
10472
11131
|
}),
|
|
10473
|
-
(0,
|
|
11132
|
+
(0, import_react30.createElement)("span", { ref: ind, className: "bt-ptab-ind", "aria-hidden": true })
|
|
10474
11133
|
),
|
|
10475
11134
|
// New tab and Close stay outside the scrolling tabs, so a narrow panel keeps both.
|
|
10476
|
-
(0,
|
|
10477
|
-
(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))
|
|
10478
11137
|
);
|
|
10479
11138
|
}
|
|
10480
11139
|
function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages }) {
|
|
10481
11140
|
const page = useBrowserPages((pages) => pages[tabId ?? ""]) ?? EMPTY_PAGE;
|
|
10482
|
-
const frame = (0,
|
|
10483
|
-
const input = (0,
|
|
10484
|
-
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);
|
|
10485
11144
|
const showGuest = landed && !leaving && Boolean(page.url) && !page.error;
|
|
10486
|
-
(0,
|
|
11145
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10487
11146
|
const element = frame.current;
|
|
10488
11147
|
if (!element || leaving || !tabId) {
|
|
10489
11148
|
void actions.browserShow(null);
|
|
@@ -10509,7 +11168,7 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
|
|
|
10509
11168
|
window.removeEventListener("resize", schedule);
|
|
10510
11169
|
};
|
|
10511
11170
|
}, [tabId, showGuest, leaving]);
|
|
10512
|
-
(0,
|
|
11171
|
+
(0, import_react30.useEffect)(() => {
|
|
10513
11172
|
setDraft(null);
|
|
10514
11173
|
if (landed && !page.url) input.current?.focus();
|
|
10515
11174
|
}, [tabId, landed]);
|
|
@@ -10525,27 +11184,27 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
|
|
|
10525
11184
|
};
|
|
10526
11185
|
const editing = draft !== null;
|
|
10527
11186
|
const secure = /^https:/i.test(page.url);
|
|
10528
|
-
return (0,
|
|
11187
|
+
return (0, import_react30.createElement)(
|
|
10529
11188
|
"div",
|
|
10530
11189
|
{ className: "bt-panel-view bt-panel-browser", "aria-label": t("{name}'s browser", { name: bot.name }) },
|
|
10531
|
-
(0,
|
|
11190
|
+
(0, import_react30.createElement)(
|
|
10532
11191
|
"div",
|
|
10533
11192
|
{ className: "bt-browser-bar" },
|
|
10534
|
-
(0,
|
|
10535
|
-
(0,
|
|
10536
|
-
(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", {
|
|
10537
11196
|
type: "button",
|
|
10538
11197
|
className: "bt-icon-btn",
|
|
10539
11198
|
disabled: !page.url,
|
|
10540
11199
|
"aria-label": page.loading ? t("Stop") : t("Reload"),
|
|
10541
11200
|
title: page.loading ? t("Stop") : t("Reload"),
|
|
10542
11201
|
onClick: () => actions.browserCommand(tabId, page.loading ? "stop" : "reload")
|
|
10543
|
-
}, (0,
|
|
10544
|
-
(0,
|
|
11202
|
+
}, (0, import_react30.createElement)(page.loading ? StopIcon : ReloadIcon)),
|
|
11203
|
+
(0, import_react30.createElement)(
|
|
10545
11204
|
"form",
|
|
10546
11205
|
{ className: "bt-browser-address", onSubmit: go, "data-editing": editing || void 0 },
|
|
10547
|
-
(0,
|
|
10548
|
-
(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", {
|
|
10549
11208
|
ref: input,
|
|
10550
11209
|
type: "text",
|
|
10551
11210
|
spellCheck: false,
|
|
@@ -10569,71 +11228,71 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
|
|
|
10569
11228
|
}
|
|
10570
11229
|
})
|
|
10571
11230
|
),
|
|
10572
|
-
page.loading ? (0,
|
|
11231
|
+
page.loading ? (0, import_react30.createElement)("span", { className: "bt-browser-progress", "aria-hidden": true }) : null
|
|
10573
11232
|
),
|
|
10574
|
-
(0,
|
|
11233
|
+
(0, import_react30.createElement)(
|
|
10575
11234
|
"div",
|
|
10576
11235
|
{ ref: frame, className: "bt-browser-frame", "data-guest": showGuest || void 0 },
|
|
10577
|
-
page.error ? (0,
|
|
11236
|
+
page.error ? (0, import_react30.createElement)(
|
|
10578
11237
|
"div",
|
|
10579
11238
|
{ className: "bt-browser-empty" },
|
|
10580
|
-
(0,
|
|
10581
|
-
(0,
|
|
10582
|
-
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: () => {
|
|
10583
11242
|
void actions.browserNavigate(tabId, page.url);
|
|
10584
11243
|
} }, t("Try again")) : null
|
|
10585
|
-
) : !page.url ? (0,
|
|
11244
|
+
) : !page.url ? (0, import_react30.createElement)(
|
|
10586
11245
|
"div",
|
|
10587
11246
|
{ className: "bt-browser-empty" },
|
|
10588
|
-
(0,
|
|
10589
|
-
(0,
|
|
10590
|
-
(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 }))
|
|
10591
11250
|
) : null
|
|
10592
11251
|
)
|
|
10593
11252
|
);
|
|
10594
11253
|
}
|
|
10595
|
-
function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatus, useActivity, useBrowserTabs, useBrowserPages }) {
|
|
11254
|
+
function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatus, useActivity, useWork, useBrowserTabs, useBrowserPages }) {
|
|
10596
11255
|
const bot = roster.byId[pane.id];
|
|
10597
11256
|
const canBrowse = Boolean(bot && roster.browser === true && actions.browserShow);
|
|
10598
11257
|
const view = pane.view === "browser" && canBrowse ? "browser" : "details";
|
|
10599
11258
|
const tabState = useBrowserTabs((map) => bot ? map[bot.id] ?? EMPTY_TABS2 : EMPTY_TABS2);
|
|
10600
11259
|
const active = tabState.active;
|
|
10601
11260
|
const page = useBrowserPages((pages) => (active ? pages[active] : null) ?? EMPTY_PAGE);
|
|
10602
|
-
const root = (0,
|
|
10603
|
-
const fly = (0,
|
|
10604
|
-
const [landed, setLanded] = (0,
|
|
10605
|
-
const [settling, setSettling] = (0,
|
|
10606
|
-
const [width, setWidth] = (0,
|
|
10607
|
-
const [viewport, setViewport] = (0,
|
|
10608
|
-
const [dragging, setDragging] = (0,
|
|
10609
|
-
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);
|
|
10610
11269
|
const instant = dragging || resizing;
|
|
10611
|
-
const [reading, setReading] = (0,
|
|
11270
|
+
const [reading, setReading] = (0, import_react30.useState)(false);
|
|
10612
11271
|
const panelWidth = fitWidth(width ?? (view === "browser" ? DEFAULT_WIDTH : DETAILS_WIDTH), widthRange(view, viewport));
|
|
10613
|
-
(0,
|
|
11272
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10614
11273
|
if (view === "browser" && width === null) setWidth(DEFAULT_WIDTH);
|
|
10615
11274
|
}, [view, width]);
|
|
10616
|
-
(0,
|
|
11275
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10617
11276
|
chatRoom.hold(instant);
|
|
10618
11277
|
}, [instant]);
|
|
10619
|
-
const [last, setLast] = (0,
|
|
11278
|
+
const [last, setLast] = (0, import_react30.useState)({ view, width: panelWidth });
|
|
10620
11279
|
if (last.view !== view || last.width !== panelWidth) {
|
|
10621
11280
|
setLast({ view, width: panelWidth });
|
|
10622
11281
|
if (last.view !== view) setSettling(last.width !== panelWidth && !instant);
|
|
10623
11282
|
}
|
|
10624
|
-
const shownView = (0,
|
|
10625
|
-
(0,
|
|
11283
|
+
const shownView = (0, import_react30.useRef)(view);
|
|
11284
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10626
11285
|
const switched = settling && shownView.current !== view;
|
|
10627
11286
|
shownView.current = view;
|
|
10628
11287
|
if (switched) chatRoom.hold(true);
|
|
10629
11288
|
chatRoom.set(leaving ? null : panelWidth);
|
|
10630
11289
|
if (switched) chatRoom.hold(false);
|
|
10631
11290
|
}, [view, panelWidth, leaving]);
|
|
10632
|
-
(0,
|
|
11291
|
+
(0, import_react30.useEffect)(() => () => {
|
|
10633
11292
|
chatRoom.set(null);
|
|
10634
11293
|
chatRoom.hold(false);
|
|
10635
11294
|
}, []);
|
|
10636
|
-
(0,
|
|
11295
|
+
(0, import_react30.useEffect)(() => {
|
|
10637
11296
|
let timer;
|
|
10638
11297
|
const onResize = () => {
|
|
10639
11298
|
setResizing(true);
|
|
@@ -10647,28 +11306,28 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10647
11306
|
window.removeEventListener("resize", onResize);
|
|
10648
11307
|
};
|
|
10649
11308
|
}, []);
|
|
10650
|
-
(0,
|
|
11309
|
+
(0, import_react30.useEffect)(() => {
|
|
10651
11310
|
if (!settling) return void 0;
|
|
10652
11311
|
const timer = setTimeout(() => setSettling(false), SPRING_SHAPE.duration + 40);
|
|
10653
11312
|
return () => clearTimeout(timer);
|
|
10654
11313
|
}, [settling, view]);
|
|
10655
|
-
(0,
|
|
11314
|
+
(0, import_react30.useEffect)(() => {
|
|
10656
11315
|
const timer = setTimeout(() => setLanded(true), 360);
|
|
10657
11316
|
return () => clearTimeout(timer);
|
|
10658
11317
|
}, []);
|
|
10659
|
-
(0,
|
|
11318
|
+
(0, import_react30.useEffect)(() => {
|
|
10660
11319
|
if (canBrowse) actions.browserEnsure(bot.id);
|
|
10661
11320
|
}, [bot?.id, canBrowse]);
|
|
10662
|
-
(0,
|
|
11321
|
+
(0, import_react30.useEffect)(() => {
|
|
10663
11322
|
if (view === "browser" && (active === null || !tabState.ids.includes(active))) actions.setPanelView("details");
|
|
10664
11323
|
}, [view, active, tabState.ids.length]);
|
|
10665
|
-
(0,
|
|
11324
|
+
(0, import_react30.useEffect)(() => {
|
|
10666
11325
|
if (view !== "browser" || leaving) void actions.browserShow?.(null);
|
|
10667
11326
|
}, [view, leaving]);
|
|
10668
|
-
(0,
|
|
11327
|
+
(0, import_react30.useEffect)(() => () => {
|
|
10669
11328
|
void actions.browserShow?.(null);
|
|
10670
11329
|
}, []);
|
|
10671
|
-
(0,
|
|
11330
|
+
(0, import_react30.useEffect)(() => {
|
|
10672
11331
|
if (!page.readAt || Date.now() - page.readAt > READ_FLASH_MS) return void 0;
|
|
10673
11332
|
setReading(true);
|
|
10674
11333
|
const timer = setTimeout(() => setReading(false), READ_FLASH_MS);
|
|
@@ -10705,7 +11364,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10705
11364
|
return actions[name](...args);
|
|
10706
11365
|
};
|
|
10707
11366
|
}
|
|
10708
|
-
(0,
|
|
11367
|
+
(0, import_react30.useLayoutEffect)(() => {
|
|
10709
11368
|
const move = fly.current;
|
|
10710
11369
|
if (!move) return;
|
|
10711
11370
|
fly.current = null;
|
|
@@ -10788,7 +11447,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10788
11447
|
for (const name of ends) grip.addEventListener(name, onEnd);
|
|
10789
11448
|
};
|
|
10790
11449
|
const hasStrip = canBrowse && tabState.ids.length > 0;
|
|
10791
|
-
return (0,
|
|
11450
|
+
return (0, import_react30.createElement)(
|
|
10792
11451
|
"aside",
|
|
10793
11452
|
{
|
|
10794
11453
|
ref: root,
|
|
@@ -10805,15 +11464,15 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10805
11464
|
if (event.target === event.currentTarget) setLanded(true);
|
|
10806
11465
|
}
|
|
10807
11466
|
},
|
|
10808
|
-
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 }),
|
|
10809
11468
|
// Over the whole window while dragging: the cursor stays a resize cursor and the
|
|
10810
11469
|
// pages underneath (the chat, the guest) see no pointer.
|
|
10811
|
-
dragging ? (0, import_react_dom7.createPortal)((0,
|
|
10812
|
-
hasStrip ? (0,
|
|
10813
|
-
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)(
|
|
10814
11473
|
"div",
|
|
10815
11474
|
{ key: `details:${pane.id}:${pane.renaming ? "rename" : ""}:${pane.tab ?? ""}`, className: "bt-panel-view" },
|
|
10816
|
-
(0,
|
|
11475
|
+
(0, import_react30.createElement)(DetailsDrawer, {
|
|
10817
11476
|
sessionId: pane.id,
|
|
10818
11477
|
startRenaming: pane.renaming,
|
|
10819
11478
|
startTab: pane.tab,
|
|
@@ -10823,6 +11482,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
|
|
|
10823
11482
|
useSessions,
|
|
10824
11483
|
useSessionStatus,
|
|
10825
11484
|
useActivity,
|
|
11485
|
+
useWork,
|
|
10826
11486
|
useBrowserTabs,
|
|
10827
11487
|
useBrowserPages
|
|
10828
11488
|
})
|
|
@@ -10909,46 +11569,46 @@ var BROWSER_CSS = `
|
|
|
10909
11569
|
`;
|
|
10910
11570
|
|
|
10911
11571
|
// src/client/overlays.js
|
|
10912
|
-
var
|
|
11572
|
+
var import_react31 = require("react");
|
|
10913
11573
|
var import_react_dom8 = require("react-dom");
|
|
10914
11574
|
var DIALOG_EXIT_MS = 160;
|
|
10915
11575
|
var MENU_EXIT_MS3 = 120;
|
|
10916
|
-
function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity, useRegistry, useBrowserPages, useBrowserTabs, actions }) {
|
|
11576
|
+
function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity, useWork, useRegistry, useBrowserPages, useBrowserTabs, useUpdate, actions }) {
|
|
10917
11577
|
const roster = useRoster((value) => value);
|
|
10918
|
-
const
|
|
11578
|
+
const ui2 = useUi((value) => value);
|
|
10919
11579
|
useRegistry((value) => value);
|
|
10920
11580
|
const current = useSessions((list) => Object.values(list.byId).find((session) => (session?.retainedBy?.mainView ?? 0) > 0)?.id ?? null);
|
|
10921
|
-
const previous = (0,
|
|
10922
|
-
(0,
|
|
11581
|
+
const previous = (0, import_react31.useRef)(current);
|
|
11582
|
+
(0, import_react31.useEffect)(() => {
|
|
10923
11583
|
if (previous.current === current) return;
|
|
10924
11584
|
previous.current = current;
|
|
10925
11585
|
if (current) actions.followPanel?.(current);
|
|
10926
11586
|
}, [current]);
|
|
10927
|
-
const open =
|
|
10928
|
-
const [kept, setKept] = (0,
|
|
10929
|
-
(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)(() => {
|
|
10930
11590
|
if (open) {
|
|
10931
11591
|
setKept(open);
|
|
10932
11592
|
return void 0;
|
|
10933
11593
|
}
|
|
10934
11594
|
const timer = setTimeout(() => setKept(null), PANEL_LINGER_MS);
|
|
10935
11595
|
return () => clearTimeout(timer);
|
|
10936
|
-
}, [
|
|
11596
|
+
}, [ui2.details, ui2.view, ui2.renaming, ui2.tab]);
|
|
10937
11597
|
const pane = open ?? kept;
|
|
10938
|
-
const [settings, settingsLeaving] = useLinger(
|
|
10939
|
-
const [memory, memoryLeaving] = useLinger(
|
|
10940
|
-
const [menu, menuLeaving] = useLinger(
|
|
10941
|
-
return (0,
|
|
10942
|
-
|
|
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,
|
|
10943
11603
|
null,
|
|
10944
|
-
pane ? (0, import_react_dom8.createPortal)((0,
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
menu ? (0, import_react_dom8.createPortal)((0,
|
|
10950
|
-
settings ? (0, import_react_dom8.createPortal)((0,
|
|
10951
|
-
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
|
|
10952
11612
|
);
|
|
10953
11613
|
}
|
|
10954
11614
|
|
|
@@ -11778,6 +12438,117 @@ function writeMode(mode, storage = globalThis.localStorage) {
|
|
|
11778
12438
|
}
|
|
11779
12439
|
}
|
|
11780
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
|
+
|
|
11781
12552
|
// src/client/index.js
|
|
11782
12553
|
var inject = ["sessions", "uiWorkspace", "uiConversation", "slots", "theme", "layout", "configForms"];
|
|
11783
12554
|
function apply(ctx) {
|
|
@@ -11805,7 +12576,7 @@ function apply(ctx) {
|
|
|
11805
12576
|
} catch {
|
|
11806
12577
|
seenAt = {};
|
|
11807
12578
|
}
|
|
11808
|
-
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: {} });
|
|
11809
12580
|
const surface = createSource({ mode: readMode(), plain: false });
|
|
11810
12581
|
let agentSessionCache = /* @__PURE__ */ new Set();
|
|
11811
12582
|
try {
|
|
@@ -11823,7 +12594,7 @@ function apply(ctx) {
|
|
|
11823
12594
|
};
|
|
11824
12595
|
let expected = null;
|
|
11825
12596
|
const setPlain = (plain) => surface.set((state) => state.plain === plain ? state : { ...state, plain });
|
|
11826
|
-
const dropPanels = () =>
|
|
12597
|
+
const dropPanels = () => ui2.set((state) => state.details || state.renaming ? { ...state, details: null, view: "details", renaming: false, tab: null } : state);
|
|
11827
12598
|
const seenKey = (id) => roster.getSnapshot().byId?.[id]?.id ?? id;
|
|
11828
12599
|
const leaveVisits = (ids) => {
|
|
11829
12600
|
const now = Date.now();
|
|
@@ -11834,11 +12605,13 @@ function apply(ctx) {
|
|
|
11834
12605
|
}
|
|
11835
12606
|
};
|
|
11836
12607
|
ctx.effect(() => {
|
|
11837
|
-
const onPageHide = () => leaveVisits(Object.keys(
|
|
12608
|
+
const onPageHide = () => leaveVisits(Object.keys(ui2.getSnapshot().visits ?? {}));
|
|
11838
12609
|
window.addEventListener("pagehide", onPageHide);
|
|
11839
12610
|
return () => window.removeEventListener("pagehide", onPageHide);
|
|
11840
12611
|
}, "ds-bot: remember visits on page hide");
|
|
11841
12612
|
const activity = createSource({});
|
|
12613
|
+
const work = createSource({ bots: {}, approvals: [] });
|
|
12614
|
+
let workKey = "";
|
|
11842
12615
|
const live = createSource({});
|
|
11843
12616
|
const turnClock = createSource({});
|
|
11844
12617
|
let activityKey = "{}";
|
|
@@ -11861,7 +12634,7 @@ function apply(ctx) {
|
|
|
11861
12634
|
} catch {
|
|
11862
12635
|
savedPrefs = {};
|
|
11863
12636
|
}
|
|
11864
|
-
const setUnread = (id, value) =>
|
|
12637
|
+
const setUnread = (id, value) => ui2.set((state) => {
|
|
11865
12638
|
if (Boolean(state.unread[id]) === value) return state;
|
|
11866
12639
|
const unread = { ...state.unread };
|
|
11867
12640
|
if (value) unread[id] = true;
|
|
@@ -11886,6 +12659,11 @@ function apply(ctx) {
|
|
|
11886
12659
|
activityKey = nextActivity;
|
|
11887
12660
|
activity.set(value.activity ?? {});
|
|
11888
12661
|
}
|
|
12662
|
+
const nextWork = JSON.stringify(value.work ?? null);
|
|
12663
|
+
if (nextWork !== workKey) {
|
|
12664
|
+
workKey = nextWork;
|
|
12665
|
+
work.set(value.work ?? { bots: {}, approvals: [] });
|
|
12666
|
+
}
|
|
11889
12667
|
noteMemoryNews(value.memoryNews ?? {});
|
|
11890
12668
|
const current = roster.getSnapshot();
|
|
11891
12669
|
if (value.connectors !== void 0 && current.ready && JSON.stringify(value.connectors) !== JSON.stringify(current.connectors ?? [])) {
|
|
@@ -11949,7 +12727,7 @@ function apply(ctx) {
|
|
|
11949
12727
|
if (stopped) return;
|
|
11950
12728
|
const value = await call("update-check", { fresh: false }).catch(() => null);
|
|
11951
12729
|
if (stopped) return;
|
|
11952
|
-
if (value) update.set(value);
|
|
12730
|
+
if (value) update.set((previous) => ({ ...value, app: previous.app }));
|
|
11953
12731
|
timer = setTimeout(loop, value?.phase === "installing" || value?.dsh?.phase === "installing" ? 2e3 : 60 * 6e4);
|
|
11954
12732
|
};
|
|
11955
12733
|
wakeUpdate = loop;
|
|
@@ -11961,6 +12739,22 @@ function apply(ctx) {
|
|
|
11961
12739
|
};
|
|
11962
12740
|
};
|
|
11963
12741
|
}, "ds-bot: update check");
|
|
12742
|
+
const appUpdates = window.dshDesktop?.protocolVersion === 1 ? window.dshDesktop.updates ?? null : null;
|
|
12743
|
+
if (appUpdates?.status && appUpdates.subscribe) {
|
|
12744
|
+
ctx.effect(() => {
|
|
12745
|
+
let stopped = false;
|
|
12746
|
+
const take = (app) => {
|
|
12747
|
+
if (!stopped && app) update.set((previous) => ({ ...previous, app }));
|
|
12748
|
+
};
|
|
12749
|
+
void appUpdates.status().then(take, () => {
|
|
12750
|
+
});
|
|
12751
|
+
const unsubscribe = appUpdates.subscribe(take);
|
|
12752
|
+
return () => {
|
|
12753
|
+
stopped = true;
|
|
12754
|
+
unsubscribe?.();
|
|
12755
|
+
};
|
|
12756
|
+
}, "ds-bot: app update status");
|
|
12757
|
+
}
|
|
11964
12758
|
const bridge = window.dshDesktop?.protocolVersion === 1 ? window.dshDesktop.browser ?? null : null;
|
|
11965
12759
|
const browsers = bridge ? createBotBrowsers({ bridge, createSource }) : null;
|
|
11966
12760
|
if (browsers) {
|
|
@@ -11976,7 +12770,7 @@ function apply(ctx) {
|
|
|
11976
12770
|
stopReads = null;
|
|
11977
12771
|
}
|
|
11978
12772
|
void browsers.retain(snapshot.bots.map((bot) => bot.id));
|
|
11979
|
-
|
|
12773
|
+
ui2.set((state) => state.details && state.view === "browser" && (!on || !snapshot.byId[state.details]) ? { ...state, view: "details" } : state);
|
|
11980
12774
|
};
|
|
11981
12775
|
sync();
|
|
11982
12776
|
const offRoster = roster.subscribe(sync);
|
|
@@ -11996,9 +12790,12 @@ function apply(ctx) {
|
|
|
11996
12790
|
const render = () => {
|
|
11997
12791
|
const next = roster.getSnapshot().bots.map((bot) => {
|
|
11998
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]`;
|
|
11999
12794
|
const look = lookOf(bot);
|
|
12000
|
-
const
|
|
12001
|
-
|
|
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}`;
|
|
12002
12799
|
}).join("\n");
|
|
12003
12800
|
if (next !== last) style.textContent = last = next;
|
|
12004
12801
|
};
|
|
@@ -12113,7 +12910,7 @@ function apply(ctx) {
|
|
|
12113
12910
|
isAgentSession: (id) => isAgentSessionId(id),
|
|
12114
12911
|
openSession: (id) => {
|
|
12115
12912
|
if (isAgentSessionId(id)) return actions.openAgentSession(id);
|
|
12116
|
-
|
|
12913
|
+
ui2.set((state) => state.newChat || state.exchange || state.menu || state.palette ? { ...state, newChat: null, exchange: null, menu: null, palette: null } : state);
|
|
12117
12914
|
setPlain(false);
|
|
12118
12915
|
const target = partFor(id);
|
|
12119
12916
|
expected = target;
|
|
@@ -12122,7 +12919,7 @@ function apply(ctx) {
|
|
|
12122
12919
|
// The conversation replacements withdraw for a plain Session; set the surface
|
|
12123
12920
|
// before the navigation so the Bot skin never flashes.
|
|
12124
12921
|
openAgentSession: (sessionId) => {
|
|
12125
|
-
|
|
12922
|
+
ui2.set((state) => ({ ...state, newChat: null, exchange: null, menu: null, palette: null, details: null, view: "details", renaming: false, tab: null }));
|
|
12126
12923
|
setPlain(true);
|
|
12127
12924
|
expected = sessionId;
|
|
12128
12925
|
ctx.uiWorkspace.openSession(sessionId);
|
|
@@ -12163,8 +12960,8 @@ function apply(ctx) {
|
|
|
12163
12960
|
else if (roster.getSnapshot().mainBotId) actions.openSession(roster.getSnapshot().mainBotId);
|
|
12164
12961
|
},
|
|
12165
12962
|
renameAgentSession: (agentId, sessionId, title) => after(call("agent-rename", { agentId, sessionId, title })),
|
|
12166
|
-
beginAgentRename: (sessionId) =>
|
|
12167
|
-
endAgentRename: () =>
|
|
12963
|
+
beginAgentRename: (sessionId) => ui2.set((state) => ({ ...state, renameAgent: sessionId })),
|
|
12964
|
+
endAgentRename: () => ui2.set((state) => state.renameAgent ? { ...state, renameAgent: null } : state),
|
|
12168
12965
|
// Removing the Agent archives every Session it owns; one of them may fill the
|
|
12169
12966
|
// main view, and it must not stay there wearing the Bot surface afterwards.
|
|
12170
12967
|
removeAgent: async (agentId) => {
|
|
@@ -12190,26 +12987,26 @@ function apply(ctx) {
|
|
|
12190
12987
|
ctx.layout.selectPanel(id);
|
|
12191
12988
|
},
|
|
12192
12989
|
openNewChat: (mode) => {
|
|
12193
|
-
|
|
12990
|
+
ui2.set((state) => ({ ...state, newChat: mode, menu: null, palette: null }));
|
|
12194
12991
|
},
|
|
12195
12992
|
openPalette: (query = "") => {
|
|
12196
|
-
|
|
12993
|
+
ui2.set((state) => ({ ...state, palette: { query, token: Date.now() }, newChat: null, menu: null }));
|
|
12197
12994
|
},
|
|
12198
12995
|
// The token keeps a late exit timer from closing a palette opened again since.
|
|
12199
12996
|
closePalette: (token) => {
|
|
12200
|
-
|
|
12997
|
+
ui2.set((state) => state.palette && (token === void 0 || state.palette.token === token) ? { ...state, palette: null } : state);
|
|
12201
12998
|
},
|
|
12202
12999
|
openExchange: (a, b) => {
|
|
12203
|
-
if (a && b)
|
|
13000
|
+
if (a && b) ui2.set((state) => ({ ...state, exchange: [a, b] }));
|
|
12204
13001
|
},
|
|
12205
13002
|
openVoice: (id) => {
|
|
12206
|
-
if (id)
|
|
13003
|
+
if (id) ui2.set((state) => ({ ...state, voice: id, menu: null }));
|
|
12207
13004
|
},
|
|
12208
13005
|
closeVoice: () => {
|
|
12209
|
-
|
|
13006
|
+
ui2.set((state) => state.voice ? { ...state, voice: null } : state);
|
|
12210
13007
|
},
|
|
12211
13008
|
toggleDetails: (id, force) => {
|
|
12212
|
-
|
|
13009
|
+
ui2.set((state) => {
|
|
12213
13010
|
const details = force || state.details !== id || state.view !== "details" ? id : null;
|
|
12214
13011
|
return { ...state, renaming: false, tab: null, details, view: "details" };
|
|
12215
13012
|
});
|
|
@@ -12217,34 +13014,34 @@ function apply(ctx) {
|
|
|
12217
13014
|
// The open panel follows the main view; a subject with no browser (a group
|
|
12218
13015
|
// chat, an outside Session) settles on the details view.
|
|
12219
13016
|
followPanel: (id) => {
|
|
12220
|
-
|
|
13017
|
+
ui2.set((state) => {
|
|
12221
13018
|
if (!state.details || state.details === id) return state;
|
|
12222
13019
|
const view = state.view === "browser" && roster.getSnapshot().byId[id] === void 0 ? "details" : state.view;
|
|
12223
13020
|
return { ...state, details: id, view, renaming: false, tab: null };
|
|
12224
13021
|
});
|
|
12225
13022
|
},
|
|
12226
13023
|
showDetails: (id, tab = null) => {
|
|
12227
|
-
|
|
13024
|
+
ui2.set((state) => ({ ...state, details: id, view: "details", renaming: false, tab }));
|
|
12228
13025
|
},
|
|
12229
13026
|
// Opening the conversation would move focus to its composer and blur the name field.
|
|
12230
13027
|
renameBot: (id) => {
|
|
12231
|
-
|
|
13028
|
+
ui2.set((state) => ({ ...state, details: id, view: "details", renaming: true, tab: null }));
|
|
12232
13029
|
},
|
|
12233
13030
|
// `id` opens one Bot (page 'bots') or one group chat (page 'groups').
|
|
12234
13031
|
openSettings: (page = "general", id = null) => {
|
|
12235
|
-
|
|
13032
|
+
ui2.set((state) => ({ ...state, settings: { page, id }, menu: null, palette: null, newChat: null }));
|
|
12236
13033
|
},
|
|
12237
13034
|
closeSettings: () => {
|
|
12238
|
-
|
|
13035
|
+
ui2.set((state) => state.settings ? { ...state, settings: null } : state);
|
|
12239
13036
|
},
|
|
12240
13037
|
openMemory: (botId) => {
|
|
12241
|
-
|
|
13038
|
+
ui2.set((state) => ({ ...state, memory: botId, menu: null, palette: null }));
|
|
12242
13039
|
},
|
|
12243
13040
|
closeMemory: () => {
|
|
12244
|
-
|
|
13041
|
+
ui2.set((state) => state.memory ? { ...state, memory: null } : state);
|
|
12245
13042
|
},
|
|
12246
13043
|
openConnectors: () => {
|
|
12247
|
-
|
|
13044
|
+
ui2.set((state) => ({ ...state, settings: { page: "connectors", id: null }, menu: null, palette: null, newChat: null }));
|
|
12248
13045
|
},
|
|
12249
13046
|
// Agent mode hands the surface back to the shell; everything open on the Bot
|
|
12250
13047
|
// surface closes first so nothing stale reopens on the way back.
|
|
@@ -12252,7 +13049,7 @@ function apply(ctx) {
|
|
|
12252
13049
|
if (mode !== "bot" && mode !== "agent") return;
|
|
12253
13050
|
if (surface.getSnapshot().mode === mode) return;
|
|
12254
13051
|
if (mode === "agent") {
|
|
12255
|
-
|
|
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 }));
|
|
12256
13053
|
}
|
|
12257
13054
|
writeMode(mode);
|
|
12258
13055
|
surface.set((state) => ({ ...state, mode }));
|
|
@@ -12279,18 +13076,18 @@ function apply(ctx) {
|
|
|
12279
13076
|
beginVisit: (id) => {
|
|
12280
13077
|
const made = roster.getSnapshot().byId[id] ?? roster.getSnapshot().roomsById[id];
|
|
12281
13078
|
const seen = seenAt[seenKey(id)] ?? (made?.createdBy && Number.isFinite(made.createdAt) ? made.createdAt : null);
|
|
12282
|
-
|
|
13079
|
+
ui2.set((state) => ({ ...state, visits: { ...state.visits, [id]: { seen, entered: Date.now() } } }));
|
|
12283
13080
|
},
|
|
12284
13081
|
endVisit: (id) => {
|
|
12285
13082
|
leaveVisits([id]);
|
|
12286
|
-
|
|
13083
|
+
ui2.set((state) => {
|
|
12287
13084
|
if (!(id in (state.visits ?? {}))) return state;
|
|
12288
13085
|
const visits = { ...state.visits };
|
|
12289
13086
|
delete visits[id];
|
|
12290
13087
|
return { ...state, visits };
|
|
12291
13088
|
});
|
|
12292
13089
|
},
|
|
12293
|
-
toggleReaction: (key, index, emoji) =>
|
|
13090
|
+
toggleReaction: (key, index, emoji) => ui2.set((state) => {
|
|
12294
13091
|
const entry = { ...state.reactions?.[key] ?? {} };
|
|
12295
13092
|
const list = entry[index] ?? [];
|
|
12296
13093
|
entry[index] = list.includes(emoji) ? list.filter((item) => item !== emoji) : [...list, emoji];
|
|
@@ -12304,19 +13101,19 @@ function apply(ctx) {
|
|
|
12304
13101
|
}
|
|
12305
13102
|
return { ...state, reactions };
|
|
12306
13103
|
}),
|
|
12307
|
-
quote: (sessionId, text) =>
|
|
12308
|
-
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),
|
|
12309
13106
|
clearUnread: (id) => setUnread(id, false),
|
|
12310
13107
|
setFlags: (id, flags) => after(call("set-flags", { id, ...flags })),
|
|
12311
13108
|
deleteRoom: (id) => after(call("delete-room", { id })),
|
|
12312
13109
|
openMenu: (menu) => {
|
|
12313
|
-
|
|
13110
|
+
ui2.set((state) => ({ ...state, menu }));
|
|
12314
13111
|
},
|
|
12315
13112
|
closeMenu: () => {
|
|
12316
|
-
|
|
13113
|
+
ui2.set((state) => state.menu ? { ...state, menu: null } : state);
|
|
12317
13114
|
},
|
|
12318
13115
|
closeOverlay: () => {
|
|
12319
|
-
|
|
13116
|
+
ui2.set((state) => ({ ...state, newChat: null, exchange: null, details: state.newChat || state.exchange ? state.details : null }));
|
|
12320
13117
|
},
|
|
12321
13118
|
createBot: (payload) => after(call("create-bot", payload)),
|
|
12322
13119
|
createRoom: (members, options = {}) => after(call("create-room", { members, ...options })),
|
|
@@ -12332,6 +13129,7 @@ function apply(ctx) {
|
|
|
12332
13129
|
send: (sessionId, text) => after(call("send", { sessionId: partFor(sessionId), text })),
|
|
12333
13130
|
answerQuestion: (sessionId, questionId, answer) => after(call("answer-question", { sessionId: partFor(sessionId), questionId, answer })),
|
|
12334
13131
|
dismissQuestion: (sessionId) => after(call("dismiss-question", { sessionId })),
|
|
13132
|
+
answerApproval: (id, choice) => after(call("approval-answer", { id, choice })),
|
|
12335
13133
|
// Values go out in these requests and never come back in a response.
|
|
12336
13134
|
secretSet: (payload) => after(call("secret-set", payload)),
|
|
12337
13135
|
secretAllow: (requestId) => after(call("secret-allow", { requestId })),
|
|
@@ -12359,9 +13157,9 @@ function apply(ctx) {
|
|
|
12359
13157
|
// A failed action away from any form (a menu, the palette) shows as a toast.
|
|
12360
13158
|
reportError: (error, where = "action") => {
|
|
12361
13159
|
noteClientError(where, error);
|
|
12362
|
-
|
|
13160
|
+
ui2.set((state) => ({ ...state, toast: { text: error?.message ?? String(error), token: Date.now() } }));
|
|
12363
13161
|
},
|
|
12364
|
-
dismissToast: (token) =>
|
|
13162
|
+
dismissToast: (token) => ui2.set((state) => state.toast && (token === void 0 || state.toast.token === token) ? { ...state, toast: null } : state)
|
|
12365
13163
|
};
|
|
12366
13164
|
if (browsers) {
|
|
12367
13165
|
Object.assign(actions, {
|
|
@@ -12371,7 +13169,7 @@ function apply(ctx) {
|
|
|
12371
13169
|
if (!bot) return;
|
|
12372
13170
|
browsers.ensureTabs(bot.id);
|
|
12373
13171
|
if (browsers.tabs.getSnapshot()[bot.id]?.active == null) browsers.openTab(bot.id);
|
|
12374
|
-
|
|
13172
|
+
ui2.set((state) => ({ ...state, details: id, view: "browser", renaming: false, tab: null }));
|
|
12375
13173
|
},
|
|
12376
13174
|
// '+' on the strip or the drawer's new-tab card: a blank tab in the browser view.
|
|
12377
13175
|
browserNewTab: (id) => {
|
|
@@ -12379,7 +13177,7 @@ function apply(ctx) {
|
|
|
12379
13177
|
if (!bot) return null;
|
|
12380
13178
|
browsers.ensureTabs(bot.id);
|
|
12381
13179
|
const tabId = browsers.openTab(bot.id);
|
|
12382
|
-
|
|
13180
|
+
ui2.set((state) => ({ ...state, details: id, view: "browser", renaming: false, tab: null }));
|
|
12383
13181
|
return tabId;
|
|
12384
13182
|
},
|
|
12385
13183
|
// A drawer card: its tab active, the panel on the browser view.
|
|
@@ -12387,7 +13185,7 @@ function apply(ctx) {
|
|
|
12387
13185
|
const bot = roster.getSnapshot().byId[id];
|
|
12388
13186
|
if (!bot) return;
|
|
12389
13187
|
browsers.activate(bot.id, tabId);
|
|
12390
|
-
|
|
13188
|
+
ui2.set((state) => ({ ...state, details: id, view: "browser" }));
|
|
12391
13189
|
},
|
|
12392
13190
|
browserEnsure: (botId) => browsers.ensureTabs(botId),
|
|
12393
13191
|
browserCloseTab: (botId, tabId) => browsers.closeTab(botId, tabId),
|
|
@@ -12400,22 +13198,22 @@ function apply(ctx) {
|
|
|
12400
13198
|
}
|
|
12401
13199
|
actions.setPanelView = (view) => {
|
|
12402
13200
|
if (view !== "details" && view !== "browser") return;
|
|
12403
|
-
|
|
13201
|
+
ui2.set((state) => state.details && state.view !== view ? { ...state, view } : state);
|
|
12404
13202
|
};
|
|
12405
13203
|
actions.closePanel = () => {
|
|
12406
|
-
|
|
13204
|
+
ui2.set((state) => state.details ? { ...state, details: null, view: "details", renaming: false, tab: null } : state);
|
|
12407
13205
|
};
|
|
12408
13206
|
actions.signInEntry = () => ctx.slots.entries?.("settings.models.sign-in")?.[0] ?? null;
|
|
12409
13207
|
actions.canSignIn = () => Boolean(actions.signInEntry());
|
|
12410
|
-
actions.signIn = () =>
|
|
12411
|
-
actions.closeSignIn = () =>
|
|
13208
|
+
actions.signIn = () => ui2.set((state) => ({ ...state, signIn: (state.signIn ?? 0) + 1 }));
|
|
13209
|
+
actions.closeSignIn = () => ui2.set((state) => ({ ...state, signIn: null }));
|
|
12412
13210
|
actions.accountT = () => ctx.get("locale").bind("settings.account");
|
|
12413
13211
|
actions.speech = () => ctx.get("remote.speech") ?? void 0;
|
|
12414
13212
|
actions.voiceSetup = () => {
|
|
12415
13213
|
const navigation = ctx.get("pluginNavigation");
|
|
12416
13214
|
return navigation ? () => navigation.openBundle(VOICE_BUNDLE) : null;
|
|
12417
13215
|
};
|
|
12418
|
-
actions.seatAccount = (on) =>
|
|
13216
|
+
actions.seatAccount = (on) => ui2.set((state) => state.accountSeated === on ? state : { ...state, accountSeated: on });
|
|
12419
13217
|
actions.installUpdate = async () => {
|
|
12420
13218
|
const value = await call("update-install");
|
|
12421
13219
|
update.set((previous) => ({ ...previous, ...value }));
|
|
@@ -12426,7 +13224,17 @@ function apply(ctx) {
|
|
|
12426
13224
|
update.set((previous) => ({ ...previous, dsh }));
|
|
12427
13225
|
wakeUpdate();
|
|
12428
13226
|
};
|
|
12429
|
-
actions.openAppUpdates = () =>
|
|
13227
|
+
actions.openAppUpdates = async (verified) => {
|
|
13228
|
+
const app = await appUpdates?.status?.();
|
|
13229
|
+
if (app) update.set((previous) => ({ ...previous, app }));
|
|
13230
|
+
if (sameVersion(app?.version, verified)) await appUpdates.open();
|
|
13231
|
+
};
|
|
13232
|
+
actions.checkUpdate = async () => {
|
|
13233
|
+
const value = await call("update-check", { fresh: true });
|
|
13234
|
+
update.set((previous) => ({ ...value, app: previous.app }));
|
|
13235
|
+
wakeUpdate();
|
|
13236
|
+
};
|
|
13237
|
+
actions.repoStars = () => call("repo-stars");
|
|
12430
13238
|
actions.noteTurnTime = (sessionId, turn, key, time) => {
|
|
12431
13239
|
if (sessionId === void 0 || !Number.isFinite(turn) || !Number.isFinite(time)) return;
|
|
12432
13240
|
turnClock.set((state) => {
|
|
@@ -12466,7 +13274,7 @@ function apply(ctx) {
|
|
|
12466
13274
|
});
|
|
12467
13275
|
const browserPages = browsers?.pages ?? createSource({});
|
|
12468
13276
|
const browserTabs = browsers?.tabs ?? createSource({});
|
|
12469
|
-
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 } });
|
|
12470
13278
|
const assistantFace = (sessionId) => {
|
|
12471
13279
|
const binding = ctx.sessions.binding(sessionId);
|
|
12472
13280
|
if (binding === void 0) throw new Error(`ds-bot: unknown session "${sessionId}"`);
|
|
@@ -12476,10 +13284,41 @@ function apply(ctx) {
|
|
|
12476
13284
|
const snapshot = chat.getSnapshot();
|
|
12477
13285
|
if (snapshot === void 0) throw new Error("ds-bot: Chat target is unavailable");
|
|
12478
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");
|
|
12479
13292
|
}
|
|
12480
13293
|
} };
|
|
12481
13294
|
};
|
|
12482
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
|
+
};
|
|
12483
13322
|
const installShell = () => [
|
|
12484
13323
|
installCompiledStyles(SHELL_CSS, "shell"),
|
|
12485
13324
|
installCompiledStyles(SIDEBAR_CSS, "sidebar"),
|
|
@@ -12488,6 +13327,9 @@ function apply(ctx) {
|
|
|
12488
13327
|
installCompiledStyles(USAGE_CSS, "usage"),
|
|
12489
13328
|
installCompiledStyles(SCHEDULE_CSS, "schedules"),
|
|
12490
13329
|
installCompiledStyles(FEEDBACK_CSS, "feedback"),
|
|
13330
|
+
installCompiledStyles(ABOUT_CSS, "about"),
|
|
13331
|
+
installCompiledStyles(MEMBER_CSS, "members"),
|
|
13332
|
+
installCompiledStyles(USER_MESSAGE_CSS, "user-messages"),
|
|
12491
13333
|
browsers ? installCompiledStyles(BROWSER_CSS, "browser") : null,
|
|
12492
13334
|
installTheme(),
|
|
12493
13335
|
shadow("sidebar.workspaces", { priority: -10, inject: face }, TeamSidebar),
|
|
@@ -12509,6 +13351,7 @@ function apply(ctx) {
|
|
|
12509
13351
|
installCompiledStyles(CONVERSATION_CSS, "conversation"),
|
|
12510
13352
|
installBubbleTokens(),
|
|
12511
13353
|
installMentions(),
|
|
13354
|
+
installAtMenu(),
|
|
12512
13355
|
// Bot conversations are always flat, so the work-details mode is not a choice:
|
|
12513
13356
|
// the mode is held at `verbose` while this group is mounted.
|
|
12514
13357
|
holdFlatTranscript(ctx.configForms.get("ui-chat")),
|
|
@@ -12522,6 +13365,7 @@ function apply(ctx) {
|
|
|
12522
13365
|
// every message.
|
|
12523
13366
|
shadow("conversation.chat.node", { key: "question-reply", priority: -10, locale: "chat" }, AnsweredCell),
|
|
12524
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)) : [],
|
|
12525
13369
|
shadow("conversation.input.permission", { priority: -10 }, Nothing),
|
|
12526
13370
|
shadow("conversation.input.plan", { priority: -10 }, Nothing),
|
|
12527
13371
|
// The composer carries no model picker or run statistics; a Bot's model lives
|
|
@@ -12535,6 +13379,7 @@ function apply(ctx) {
|
|
|
12535
13379
|
priority: -10,
|
|
12536
13380
|
select: (owner) => owner.pendingInteraction?.kind === "question" ? owner.pendingInteraction : null
|
|
12537
13381
|
}, QuestionCard),
|
|
13382
|
+
shadow("conversation.input.dock", { id: "bot-members", order: -120, inject: face }, MemberDock),
|
|
12538
13383
|
shadow("conversation.input.dock", { id: "bot-model", order: -110, inject: face }, ModelChoiceDock),
|
|
12539
13384
|
shadow("conversation.input.dock", { id: "bot-question", order: -100, inject: face }, QuestionDock),
|
|
12540
13385
|
shadow("conversation.input.dock", { id: "bot-secret", order: -90, inject: face }, SecretDock)
|