ds-bot 0.1.1 → 0.1.2

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/client.js CHANGED
@@ -683,6 +683,25 @@ var ZH = {
683
683
  "Compared with the day before": "与前一天相比",
684
684
  "Compared with the {count} days before": "与再往前 {count} 天相比",
685
685
  "{share}% of all usage from {span}": "占 {span} 全部用量的 {share}%",
686
+ // About
687
+ "About DS Bot": "关于 DS Bot",
688
+ "Version {version}": "版本 {version}",
689
+ "Star DS Bot on GitHub": "在 GitHub 上给 DS Bot 点 Star",
690
+ "{count} stars": "{count} 个 Star",
691
+ "If DS Bot helps you, a star on GitHub helps more people find it.": "如果 DS Bot 对你有用,欢迎在 GitHub 上点个 Star,让更多人看到它。",
692
+ Updates: "更新",
693
+ "Check for updates": "检查更新",
694
+ "Checking…": "正在检查…",
695
+ "Up to date": "已是最新版本",
696
+ "Checked {time}": "检查于 {time}",
697
+ "Could not check for updates.": "没能检查更新。",
698
+ "Update checks are off.": "已关闭更新检查。",
699
+ "Installed from a local folder; update it there.": "从本地文件夹安装,请在那里更新。",
700
+ "Updates are not offered for this install.": "这种安装方式不提供更新。",
701
+ Links: "链接",
702
+ "Source code": "源代码",
703
+ "Release notes": "发布说明",
704
+ Open: "打开",
686
705
  // Feedback and errors
687
706
  Feedback: "反馈",
688
707
  "Report a problem": "报告问题",
@@ -1094,8 +1113,8 @@ function bodyOf(def, paint, flat = false) {
1094
1113
  return "";
1095
1114
  }
1096
1115
  }
1097
- var eyeMarkup = ([cx, cy, w, h29, tilt]) => `<g transform="translate(${cx} ${cy}) rotate(${tilt})"><rect class="bt-eye" x="${-w / 2}" y="${-h29 / 2}" width="${w}" height="${h29}" rx="${w / 2}"/></g>`;
1098
- var happyEyeMarkup = ([cx, cy, w, h29, tilt]) => `<g transform="translate(${cx} ${cy}) rotate(${tilt})"><path class="bt-eye-happy" opacity="0" d="M${-w * 0.62} ${h29 * 0.14}Q0 ${-h29 * 0.42} ${w * 0.62} ${h29 * 0.14}" fill="none" stroke="#000" stroke-width="${w * 0.5}" stroke-linecap="round"/></g>`;
1116
+ var eyeMarkup = ([cx, cy, w, h30, tilt]) => `<g transform="translate(${cx} ${cy}) rotate(${tilt})"><rect class="bt-eye" x="${-w / 2}" y="${-h30 / 2}" width="${w}" height="${h30}" rx="${w / 2}"/></g>`;
1117
+ var happyEyeMarkup = ([cx, cy, w, h30, tilt]) => `<g transform="translate(${cx} ${cy}) rotate(${tilt})"><path class="bt-eye-happy" opacity="0" d="M${-w * 0.62} ${h30 * 0.14}Q0 ${-h30 * 0.42} ${w * 0.62} ${h30 * 0.14}" fill="none" stroke="#000" stroke-width="${w * 0.5}" stroke-linecap="round"/></g>`;
1099
1118
  var maskBox = (size) => `maskUnits="userSpaceOnUse" x="${-size}" y="${-size}" width="${size * 3}" height="${size * 3}"`;
1100
1119
  var holes = (def, eyes) => `<rect x="${-def.size}" y="${-def.size}" width="${def.size * 3}" height="${def.size * 3}" fill="#fff"/>${eyes}`;
1101
1120
  var MASK_ID = "%MASK%";
@@ -1470,12 +1489,12 @@ function liveSprings(apply2) {
1470
1489
  const dt = Math.min(0.5, Math.max(1e-3, (now - last) / 1e3));
1471
1490
  last = now;
1472
1491
  const steps = Math.ceil(dt * 240);
1473
- const h29 = dt / steps;
1492
+ const h30 = dt / steps;
1474
1493
  let moving = false;
1475
1494
  for (const value of values.values()) {
1476
1495
  for (let index = 0; index < steps; index += 1) {
1477
- value.v += (-value.k * (value.x - value.to) - value.c * value.v) * h29;
1478
- value.x += value.v * h29;
1496
+ value.v += (-value.k * (value.x - value.to) - value.c * value.v) * h30;
1497
+ value.x += value.v * h30;
1479
1498
  }
1480
1499
  if (Math.abs(value.x - value.to) < 0.05 && Math.abs(value.v) < 1) {
1481
1500
  value.x = value.to;
@@ -2892,6 +2911,7 @@ function PinMarkIcon() {
2892
2911
  }
2893
2912
  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
2913
  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"]);
2914
+ 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
2915
  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
2916
  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
2917
  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"]);
@@ -4369,6 +4389,16 @@ function ActivityIndicator({ sessionId, useRoster, useSessionStatus, useLive, ac
4369
4389
  ), host);
4370
4390
  }
4371
4391
 
4392
+ // src/client/dsh-offer.js
4393
+ var APP_OFFER = /* @__PURE__ */ new Set(["available", "downloading", "verifying", "installing", "ready"]);
4394
+ var bare = (value) => String(value ?? "").trim().replace(/^v/i, "").split("+")[0];
4395
+ var sameVersion = (a, b) => bare(a) !== "" && bare(a) === bare(b);
4396
+ function showsDshRow(dsh, app) {
4397
+ if (!dsh?.verified || !dsh.current || sameVersion(dsh.verified, dsh.current)) return false;
4398
+ if (dsh.kind === "npm") return true;
4399
+ return dsh.kind === "app" && APP_OFFER.has(app?.phase) && sameVersion(app.version, dsh.verified);
4400
+ }
4401
+
4372
4402
  // src/client/sidebar.js
4373
4403
  var import_react8 = require("react");
4374
4404
  var import_react_dom3 = require("react-dom");
@@ -4722,7 +4752,7 @@ function UpdateButton({ useUpdate, actions }) {
4722
4752
  const phase = update?.phase ?? "idle";
4723
4753
  const dsh = update?.dsh;
4724
4754
  const dshPhase = dsh?.phase ?? "idle";
4725
- const dshRow = dsh && dsh.verified && dsh.current && dsh.verified !== dsh.current;
4755
+ const dshRow = showsDshRow(dsh, update?.app);
4726
4756
  if (!update?.available && !dshRow && phase === "idle" && dshPhase === "idle") return null;
4727
4757
  const cloudPhase = phase === "installing" || dshPhase === "installing" ? "installing" : phase !== "idle" ? phase : dshPhase;
4728
4758
  const toggle = () => {
@@ -4741,7 +4771,7 @@ function UpdateButton({ useUpdate, actions }) {
4741
4771
  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
4772
  const message = error || (phase === "failed" ? update.error : "");
4743
4773
  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 && dsh.kind === "npm" ? t("Update DS Bot {version} first.", { version: dsh.blockedBy }) : t("You have {version}.", { version: dsh?.current });
4774
+ 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
4775
  return (0, import_react8.createElement)(
4746
4776
  import_react8.Fragment,
4747
4777
  null,
@@ -4786,7 +4816,8 @@ function UpdateButton({ useUpdate, actions }) {
4786
4816
  (0, import_react8.createElement)(
4787
4817
  "div",
4788
4818
  { className: "bt-update-actions" },
4789
- dsh.kind === "app" ? (0, import_react8.createElement)("button", { type: "button", className: "bt-update-go", onClick: () => actions.openAppUpdates() }, t("Open updates")) : !dsh.available && dshPhase === "idle" ? null : dshPhase === "installed" ? null : (0, import_react8.createElement)("button", {
4819
+ dsh.kind === "app" ? dsh.available ? (0, import_react8.createElement)("button", { type: "button", className: "bt-update-go", onClick: () => void actions.openAppUpdates(dsh.verified).catch(() => {
4820
+ }) }, t("Open updates")) : null : !dsh.available && dshPhase === "idle" ? null : dshPhase === "installed" ? null : (0, import_react8.createElement)("button", {
4790
4821
  type: "button",
4791
4822
  className: "bt-update-go",
4792
4823
  disabled: dshPhase === "installing",
@@ -5089,7 +5120,7 @@ var SIDEBAR_CSS = `
5089
5120
  .bt-update-go:hover:not(:disabled){opacity:.9}
5090
5121
  .bt-update-go:active:not(:disabled){transform:scale(.96)}
5091
5122
  .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)}
5123
+ .bt-update-dsh:not(:first-child){margin-top:12px;padding-top:12px;border-top:.5px solid var(--bt-line-2)}
5093
5124
  @media (prefers-reduced-motion:reduce){.bt-round.bt-update,.bt-update-pop,.bt-update .bt-cloud-arrow{animation:none}}
5094
5125
 
5095
5126
  /* The DSH Agent row and its expanding Session list. The black whale is the
@@ -9502,55 +9533,196 @@ var FEEDBACK_CSS = `
9502
9533
  @media (prefers-reduced-motion:reduce){.bt-toast{animation:none}.bt-toast[data-leaving]{transition:none}}
9503
9534
  `;
9504
9535
 
9505
- // src/client/settings.js
9536
+ // src/client/about.js
9506
9537
  var import_react25 = require("react");
9538
+ var CHEER_MS = 1800;
9539
+ var starCount = (count) => new Intl.NumberFormat(dateLocale(), { notation: "compact", maximumFractionDigits: 1 }).format(count);
9540
+ function botHint(update) {
9541
+ if (update.phase === "installing") return t("Updating DS Bot…");
9542
+ if (update.phase === "installed") return t("Restart DSH to start using DS Bot {version}.", { version: update.installed ?? update.latest });
9543
+ if (update.phase === "failed") return t("The update did not finish: {reason}", { reason: hostText(update.error ?? "") });
9544
+ if (update.available) return t("DS Bot {version} is available", { version: update.latest });
9545
+ if (update.enabled === false) return t("Update checks are off.");
9546
+ if (update.source === "path") return t("Installed from a local folder; update it there.");
9547
+ if (!update.checkedAt) return null;
9548
+ if (!update.latest && update.source !== "registry") return t("Updates are not offered for this install.");
9549
+ const checked = t("Checked {time}", { time: whenText(update.checkedAt) });
9550
+ return `${update.latest ? t("Up to date") : t("Could not check for updates.")} · ${checked}`;
9551
+ }
9552
+ function dshHint(dsh, shown) {
9553
+ if (dsh.phase === "installing") return t("Updating DSH…");
9554
+ if (dsh.phase === "installed") return t("Restart DSH to start using DSH {version}.", { version: dsh.installed ?? dsh.verified });
9555
+ if (dsh.phase === "failed") return t("The update did not finish: {reason}", { reason: hostText(dsh.error ?? "") });
9556
+ if (dsh.available === false && dsh.blockedBy) return t("Update DS Bot {version} first.", { version: dsh.blockedBy });
9557
+ if (shown) return t("DSH {version} is available", { version: dsh.verified });
9558
+ return t("Version {version}", { version: dsh.current });
9559
+ }
9560
+ var linkProps = (href) => ({ className: "bt-soft bt-soft-sm bt-about-link", href, target: "_blank", rel: "noopener noreferrer" });
9561
+ function AboutPage({ useUpdate, actions, run, Section, Row: Row3 }) {
9562
+ const update = useUpdate((value) => value) ?? {};
9563
+ const [checking, setChecking] = (0, import_react25.useState)(false);
9564
+ const [stars, setStars] = (0, import_react25.useState)(null);
9565
+ (0, import_react25.useEffect)(() => {
9566
+ let live = true;
9567
+ actions.repoStars().then((count) => {
9568
+ if (live && Number.isInteger(count)) setStars(count);
9569
+ }, () => {
9570
+ });
9571
+ return () => {
9572
+ live = false;
9573
+ };
9574
+ }, []);
9575
+ const [cheer, setCheer] = (0, import_react25.useState)(true);
9576
+ (0, import_react25.useEffect)(() => {
9577
+ if (!cheer) return void 0;
9578
+ const timer = setTimeout(() => setCheer(false), CHEER_MS);
9579
+ return () => clearTimeout(timer);
9580
+ }, [cheer]);
9581
+ const check = () => {
9582
+ setChecking(true);
9583
+ void run(actions.checkUpdate()).then((done) => {
9584
+ setChecking(false);
9585
+ if (done) setCheer(true);
9586
+ });
9587
+ };
9588
+ const phase = update.phase ?? "idle";
9589
+ const dsh = update.dsh;
9590
+ const dshPhase = dsh?.phase ?? "idle";
9591
+ (0, import_react25.useEffect)(() => {
9592
+ if (phase === "installed" || dshPhase === "installed") setCheer(true);
9593
+ }, [phase, dshPhase]);
9594
+ const markState = checking ? "searching" : phase === "installing" || dshPhase === "installing" ? "working" : cheer ? "done" : "idle";
9595
+ const dshNew = showsDshRow(dsh, update.app);
9596
+ const dshShown = dshNew && dsh.available;
9597
+ const botControl = phase === "installed" ? null : update.available || phase === "installing" ? (0, import_react25.createElement)(
9598
+ "button",
9599
+ { type: "button", className: "bt-fb-primary", disabled: phase === "installing", onClick: () => run(actions.installUpdate()) },
9600
+ phase === "installing" ? t("Updating…") : phase === "failed" ? t("Try again") : t("Update")
9601
+ ) : (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"));
9602
+ 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)(
9603
+ "button",
9604
+ { type: "button", className: "bt-fb-primary", disabled: dshPhase === "installing", onClick: () => run(actions.installDshUpdate()) },
9605
+ dshPhase === "installing" ? t("Updating…") : dshPhase === "failed" ? t("Try again") : t("Update")
9606
+ ) : null;
9607
+ const repo = update.repoUrl;
9608
+ return [
9609
+ (0, import_react25.createElement)(
9610
+ "div",
9611
+ { key: "hero", className: "bt-about-hero" },
9612
+ (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 })),
9613
+ (0, import_react25.createElement)(
9614
+ "div",
9615
+ { className: "bt-about-copy" },
9616
+ (0, import_react25.createElement)("span", { className: "bt-about-name" }, "DS Bot"),
9617
+ update.current ? (0, import_react25.createElement)("span", { className: "bt-about-version" }, t("Version {version}", { version: update.current })) : null
9618
+ ),
9619
+ repo ? (0, import_react25.createElement)(
9620
+ "a",
9621
+ {
9622
+ className: "bt-star",
9623
+ href: repo,
9624
+ target: "_blank",
9625
+ rel: "noopener noreferrer",
9626
+ title: t("Star DS Bot on GitHub"),
9627
+ "aria-label": stars === null ? t("Star DS Bot on GitHub") : `${t("Star DS Bot on GitHub")} · ${t("{count} stars", { count: stars })}`
9628
+ },
9629
+ (0, import_react25.createElement)("span", { className: "bt-star-main" }, (0, import_react25.createElement)("span", { className: "bt-star-icon" }, (0, import_react25.createElement)(StarLineIcon)), "Star"),
9630
+ stars === null ? null : (0, import_react25.createElement)("span", { className: "bt-star-count" }, starCount(stars))
9631
+ ) : null
9632
+ ),
9633
+ 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,
9634
+ (0, import_react25.createElement)(
9635
+ Section,
9636
+ { key: "updates", title: t("Updates") },
9637
+ (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),
9638
+ dsh?.current ? (0, import_react25.createElement)(Row3, { label: "DeepSeek Harness", hint: dshHint(dsh, dshShown) }, dshControl) : null
9639
+ ),
9640
+ (0, import_react25.createElement)(
9641
+ Section,
9642
+ { key: "links", title: t("Links") },
9643
+ 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,
9644
+ update.notesUrl ? (0, import_react25.createElement)(Row3, { label: t("Release notes") }, (0, import_react25.createElement)("a", linkProps(update.notesUrl), t("Open"))) : null,
9645
+ (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")))
9646
+ )
9647
+ ];
9648
+ }
9649
+ var ABOUT_CSS = `
9650
+ .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)}
9651
+ .bt-about-mark{flex:none;display:inline-flex;position:relative}
9652
+ .bt-about-mark::before{content:'';position:absolute;inset:-20%;border-radius:50%}
9653
+ 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}
9654
+ body:not([data-bt-motion=quiet]) .bt-about-mark:hover .bt-mark[data-live][data-state=idle] .bt-eye{scale:1 0;animation:none}
9655
+ 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}
9656
+ .bt-about-copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
9657
+ .bt-about-name{font-size:16px;line-height:22px;font-weight:600}
9658
+ .bt-about-version{font-size:12px;line-height:16px;color:var(--bt-ink-2);font-variant-numeric:tabular-nums}
9659
+ .bt-about-ask{margin:-10px 4px 0;font-size:12px;line-height:16px;color:var(--bt-ink-3)}
9660
+ .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}
9661
+ .bt-star:hover{border-color:color-mix(in srgb,#e3a008 55%,var(--bt-line))}
9662
+ .bt-star:active{transform:scale(.96)}
9663
+ .bt-star-main{display:inline-flex;align-items:center;gap:6px;padding:0 12px 0 10px;background:var(--bt-hover);font-weight:500}
9664
+ .bt-star-icon{display:inline-flex;color:var(--bt-ink-2);transition:color .15s ease}
9665
+ .bt-star-icon path{transition:fill .15s ease}
9666
+ .bt-star:hover .bt-star-icon{color:#e3a008;animation:bt-star-pop ${SPRING_POP.duration}ms ${SPRING_POP.easing}}
9667
+ .bt-star:hover .bt-star-icon path{fill:currentColor}
9668
+ .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}
9669
+ @keyframes bt-star-pop{from{scale:.6;rotate:-30deg}}
9670
+ @keyframes bt-star-count-in{from{translate:-6px 0}}
9671
+ .bt-about-link{display:inline-flex;align-items:center;gap:6px;text-decoration:none}
9672
+ .bt-about-link svg{width:14px;height:14px}
9673
+ .bt-fb-primary:disabled{opacity:.6;cursor:default;filter:none}
9674
+ @media (prefers-reduced-motion:reduce){.bt-star:hover .bt-star-icon,.bt-star-count{animation:none}}
9675
+ `;
9676
+
9677
+ // src/client/settings.js
9678
+ var import_react26 = require("react");
9507
9679
  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 };
9680
+ 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"]];
9681
+ var PAGE_ICONS = { ...SETTINGS_ICONS, connectors: ConnectorIcon, schedules: ClockIcon, secrets: KeyIcon, feedback: FeedbackIcon, about: InfoIcon };
9510
9682
  function SetSection({ title, children }) {
9511
- return (0, import_react25.createElement)("section", { className: "bt-set-section" }, (0, import_react25.createElement)("h3", null, title), (0, import_react25.createElement)("div", { className: "bt-set-card" }, children));
9683
+ 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
9684
  }
9513
9685
  function SetRow({ label, hint, children }) {
9514
- return (0, import_react25.createElement)(
9686
+ return (0, import_react26.createElement)(
9515
9687
  "div",
9516
9688
  { className: "bt-set-row" },
9517
- (0, import_react25.createElement)("div", { className: "bt-set-copy" }, (0, import_react25.createElement)("span", null, label), hint ? (0, import_react25.createElement)("span", { className: "bt-set-hint" }, hint) : null),
9518
- children ? (0, import_react25.createElement)("div", { className: "bt-set-control" }, children) : null
9689
+ (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),
9690
+ children ? (0, import_react26.createElement)("div", { className: "bt-set-control" }, children) : null
9519
9691
  );
9520
9692
  }
9521
9693
  function GeneralPage({ roster, actions, run }) {
9522
9694
  const prefs = roster.prefs ?? {};
9523
9695
  const themes = allThemes();
9524
9696
  const current = themes[prefs.theme] ? prefs.theme : "deepseek";
9525
- const [custom, setCustom] = (0, import_react25.useState)(prefs.accent ?? "#4d6bfe");
9526
- const timer = (0, import_react25.useRef)(0);
9527
- (0, import_react25.useEffect)(() => () => clearTimeout(timer.current), []);
9697
+ const [custom, setCustom] = (0, import_react26.useState)(prefs.accent ?? "#4d6bfe");
9698
+ const timer = (0, import_react26.useRef)(0);
9699
+ (0, import_react26.useEffect)(() => () => clearTimeout(timer.current), []);
9528
9700
  const pickCustom = (value) => {
9529
9701
  setCustom(value);
9530
9702
  clearTimeout(timer.current);
9531
9703
  timer.current = setTimeout(() => run(actions.setPrefs({ accent: value })), 350);
9532
9704
  };
9533
9705
  return [
9534
- (0, import_react25.createElement)(
9706
+ (0, import_react26.createElement)(
9535
9707
  SetSection,
9536
9708
  { key: "look", title: t("Appearance") },
9537
- (0, import_react25.createElement)(
9709
+ (0, import_react26.createElement)(
9538
9710
  SetRow,
9539
9711
  { label: t("Theme"), hint: t("Colors for the whole team, on every device.") },
9540
- (0, import_react25.createElement)(
9712
+ (0, import_react26.createElement)(
9541
9713
  "select",
9542
9714
  { 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, import_react25.createElement)("option", { key: id, value: id }, t(theme.label)))
9715
+ Object.entries(themes).map(([id, theme]) => (0, import_react26.createElement)("option", { key: id, value: id }, t(theme.label)))
9544
9716
  )
9545
9717
  ),
9546
- (0, import_react25.createElement)(
9718
+ (0, import_react26.createElement)(
9547
9719
  SetRow,
9548
9720
  { label: t("Accent"), hint: t("Unread marks, badges, and the brand mark.") },
9549
- (0, import_react25.createElement)(
9721
+ (0, import_react26.createElement)(
9550
9722
  "div",
9551
9723
  { className: "bt-swatches" },
9552
- (0, import_react25.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 })) }),
9553
- ACCENT_INKS.map((ink) => (0, import_react25.createElement)("button", {
9724
+ (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 })) }),
9725
+ ACCENT_INKS.map((ink) => (0, import_react26.createElement)("button", {
9554
9726
  key: ink,
9555
9727
  type: "button",
9556
9728
  className: "bt-swatch",
@@ -9560,21 +9732,21 @@ function GeneralPage({ roster, actions, run }) {
9560
9732
  style: { background: INKS[ink][0] },
9561
9733
  onClick: () => run(actions.setPrefs({ accent: INKS[ink][0] }))
9562
9734
  })),
9563
- (0, import_react25.createElement)(
9735
+ (0, import_react26.createElement)(
9564
9736
  "label",
9565
9737
  { 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, import_react25.createElement)("input", { type: "color", value: custom, "aria-label": t("Custom accent"), onChange: (event) => pickCustom(event.target.value) })
9738
+ (0, import_react26.createElement)("input", { type: "color", value: custom, "aria-label": t("Custom accent"), onChange: (event) => pickCustom(event.target.value) })
9567
9739
  )
9568
9740
  )
9569
9741
  )
9570
9742
  ),
9571
- (0, import_react25.createElement)(
9743
+ (0, import_react26.createElement)(
9572
9744
  SetSection,
9573
9745
  { key: "mode", title: t("Mode") },
9574
- (0, import_react25.createElement)(
9746
+ (0, import_react26.createElement)(
9575
9747
  SetRow,
9576
9748
  { label: t("Classic Agent mode") },
9577
- (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => actions.setSurface("agent") }, t("Switch"))
9749
+ (0, import_react26.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => actions.setSurface("agent") }, t("Switch"))
9578
9750
  )
9579
9751
  )
9580
9752
  ];
@@ -9583,52 +9755,52 @@ function MainBotsSection({ roster, actions, run }) {
9583
9755
  const mains = (roster.mainBotIds ?? []).map((id) => roster.byId[id]).filter(Boolean);
9584
9756
  const [first, ...others] = mains;
9585
9757
  const candidates = roster.bots.filter((bot) => !isMainOf(roster, bot.id));
9586
- return (0, import_react25.createElement)(
9758
+ return (0, import_react26.createElement)(
9587
9759
  SetSection,
9588
9760
  { title: t("Main Bot") },
9589
- (0, import_react25.createElement)(
9761
+ (0, import_react26.createElement)(
9590
9762
  SetRow,
9591
9763
  {
9592
9764
  label: t("Main Bot"),
9593
9765
  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
9766
  },
9595
- (0, import_react25.createElement)("select", {
9767
+ (0, import_react26.createElement)("select", {
9596
9768
  className: "bt-select",
9597
9769
  "aria-label": t("Main Bot"),
9598
9770
  value: first?.id ?? "",
9599
9771
  onChange: (event) => run(actions.setMain(event.target.value, true, first ? { replace: first.id } : {}))
9600
- }, first ? null : (0, import_react25.createElement)("option", { value: "" }, t("Choose a Bot…")), roster.bots.map((bot) => (0, import_react25.createElement)("option", { key: bot.id, value: bot.id }, bot.name)))
9772
+ }, 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
9773
  ),
9602
- others.map((bot) => (0, import_react25.createElement)(
9774
+ others.map((bot) => (0, import_react26.createElement)(
9603
9775
  SetRow,
9604
9776
  { key: bot.id, label: bot.name, hint: t("Also a Main Bot.") },
9605
- (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => run(actions.setMain(bot.id, true, { primary: true })) }, t("Move to first")),
9606
- (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm", onClick: () => run(actions.setMain(bot.id, false)) }, t("Remove Main Bot role"))
9777
+ (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")),
9778
+ (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
9779
  )),
9608
- candidates.length > 0 ? (0, import_react25.createElement)(
9780
+ candidates.length > 0 ? (0, import_react26.createElement)(
9609
9781
  SetRow,
9610
9782
  { label: t("Add a Main Bot"), hint: t("Main Bots can create and change Bots, and run every group chat.") },
9611
- (0, import_react25.createElement)("select", {
9783
+ (0, import_react26.createElement)("select", {
9612
9784
  className: "bt-select",
9613
9785
  "aria-label": t("Add a Main Bot"),
9614
9786
  value: "",
9615
9787
  onChange: (event) => {
9616
9788
  if (event.target.value) void run(actions.setMain(event.target.value, true));
9617
9789
  }
9618
- }, (0, import_react25.createElement)("option", { value: "" }, t("Choose a Bot…")), candidates.map((bot) => (0, import_react25.createElement)("option", { key: bot.id, value: bot.id }, bot.name)))
9790
+ }, (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
9791
  ) : null
9620
9792
  );
9621
9793
  }
9622
9794
  var MOTIONS = [["quiet", "Quiet"], ["normal", "Normal"], ["lively", "Lively"]];
9623
9795
  function MotionSection({ roster, actions, run }) {
9624
9796
  const motion = roster.prefs?.motion === "quiet" || roster.prefs?.motion === "lively" ? roster.prefs.motion : "normal";
9625
- return (0, import_react25.createElement)(
9797
+ return (0, import_react26.createElement)(
9626
9798
  SetSection,
9627
9799
  { title: t("Animation") },
9628
- (0, import_react25.createElement)(
9800
+ (0, import_react26.createElement)(
9629
9801
  SetRow,
9630
9802
  { label: t("Bot animation") },
9631
- (0, import_react25.createElement)(Segmented, { label: t("Bot animation"), value: motion, className: "bt-motion-seg" }, MOTIONS.map(([id, label]) => (0, import_react25.createElement)("button", {
9803
+ (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
9804
  key: id,
9633
9805
  type: "button",
9634
9806
  "aria-pressed": motion === id,
@@ -9640,69 +9812,69 @@ function MotionSection({ roster, actions, run }) {
9640
9812
  );
9641
9813
  }
9642
9814
  function BotsPage({ roster, actions, run }) {
9643
- const [creating, setCreating] = (0, import_react25.useState)(false);
9815
+ const [creating, setCreating] = (0, import_react26.useState)(false);
9644
9816
  return [
9645
- (0, import_react25.createElement)(MainBotsSection, { key: "main", roster, actions, run }),
9646
- (0, import_react25.createElement)(MotionSection, { key: "motion", roster, actions, run }),
9647
- creating ? (0, import_react25.createElement)(CreateBot, { key: "create", roster, actions, inline: true, onBack: () => setCreating(false), onCreated: (bot) => actions.openSettings("bots", bot.id) }) : (0, import_react25.createElement)(
9817
+ (0, import_react26.createElement)(MainBotsSection, { key: "main", roster, actions, run }),
9818
+ (0, import_react26.createElement)(MotionSection, { key: "motion", roster, actions, run }),
9819
+ 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
9820
  "div",
9649
9821
  { key: "new", className: "bt-actions" },
9650
- (0, import_react25.createElement)("button", { type: "button", className: "bt-soft bt-soft-sm bt-with-icon", onClick: () => setCreating(true) }, (0, import_react25.createElement)(PlusIcon), t("New Bot"))
9822
+ (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
9823
  ),
9652
- (0, import_react25.createElement)(
9824
+ (0, import_react26.createElement)(
9653
9825
  SetSection,
9654
9826
  { key: "bots", title: t("Bots · {count}", { count: roster.bots.length }) },
9655
- roster.bots.map((bot) => (0, import_react25.createElement)(
9827
+ roster.bots.map((bot) => (0, import_react26.createElement)(
9656
9828
  "button",
9657
9829
  { key: bot.id, type: "button", className: "bt-set-row", onClick: () => actions.openSettings("bots", bot.id) },
9658
- (0, import_react25.createElement)(
9830
+ (0, import_react26.createElement)(
9659
9831
  "span",
9660
9832
  { className: "bt-set-bot" },
9661
- (0, import_react25.createElement)(BotAvatar, { bot, size: 28, badge: false, live: false }),
9662
- (0, import_react25.createElement)("span", { className: "bt-row-title" }, bot.name),
9663
- bot.role ? (0, import_react25.createElement)("span", { className: "bt-tag" }, bot.role) : null,
9664
- isMainOf(roster, bot.id) ? (0, import_react25.createElement)("span", { className: "bt-tag" }, t("Main Bot")) : null,
9665
- bot.hidden && !isMainOf(roster, bot.id) ? (0, import_react25.createElement)("span", { className: "bt-tag" }, t("Hidden")) : null
9833
+ (0, import_react26.createElement)(BotAvatar, { bot, size: 28, badge: false, live: false }),
9834
+ (0, import_react26.createElement)("span", { className: "bt-row-title" }, bot.name),
9835
+ bot.role ? (0, import_react26.createElement)("span", { className: "bt-tag" }, bot.role) : null,
9836
+ isMainOf(roster, bot.id) ? (0, import_react26.createElement)("span", { className: "bt-tag" }, t("Main Bot")) : null,
9837
+ bot.hidden && !isMainOf(roster, bot.id) ? (0, import_react26.createElement)("span", { className: "bt-tag" }, t("Hidden")) : null
9666
9838
  ),
9667
- (0, import_react25.createElement)("span", { className: "bt-chevron" }, (0, import_react25.createElement)(ChevronRightIcon))
9839
+ (0, import_react26.createElement)("span", { className: "bt-chevron" }, (0, import_react26.createElement)(ChevronRightIcon))
9668
9840
  ))
9669
9841
  )
9670
9842
  ];
9671
9843
  }
9672
9844
  function GroupsPage({ roster, actions, run }) {
9673
- const [creating, setCreating] = (0, import_react25.useState)(false);
9845
+ const [creating, setCreating] = (0, import_react26.useState)(false);
9674
9846
  return [
9675
- creating ? (0, import_react25.createElement)(NewGroupForm, { key: "create", roster, actions, run, onCancel: () => setCreating(false), onCreated: (room2) => actions.openSettings("groups", room2.id) }) : (0, import_react25.createElement)(
9847
+ 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
9848
  "div",
9677
9849
  { key: "new", className: "bt-actions" },
9678
- (0, import_react25.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_react25.createElement)(PlusIcon), t("Create group chat"))
9850
+ (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
9851
  ),
9680
- (0, import_react25.createElement)(
9852
+ (0, import_react26.createElement)(
9681
9853
  SetSection,
9682
9854
  { key: "groups", title: t("Group chats · {count}", { count: roster.rooms.length }) },
9683
- roster.rooms.length === 0 ? (0, import_react25.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) => {
9855
+ 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
9856
  const admin = roster.byId[room2.admin];
9685
- return (0, import_react25.createElement)(
9857
+ return (0, import_react26.createElement)(
9686
9858
  "button",
9687
9859
  { key: room2.id, type: "button", className: "bt-set-row", onClick: () => actions.openSettings("groups", room2.id) },
9688
- (0, import_react25.createElement)(
9860
+ (0, import_react26.createElement)(
9689
9861
  "span",
9690
9862
  { className: "bt-set-bot" },
9691
- (0, import_react25.createElement)(RoomAvatar, { room: room2, roster, size: 28 }),
9692
- (0, import_react25.createElement)("span", { className: "bt-row-title" }, room2.name),
9693
- (0, import_react25.createElement)("span", { className: "bt-tag" }, t("{count} members", { count: room2.members.length })),
9694
- admin ? (0, import_react25.createElement)("span", { className: "bt-tag bt-tag-admin" }, (0, import_react25.createElement)(AdminStar), admin.name) : null,
9695
- room2.hidden ? (0, import_react25.createElement)("span", { className: "bt-tag" }, t("Hidden")) : null
9863
+ (0, import_react26.createElement)(RoomAvatar, { room: room2, roster, size: 28 }),
9864
+ (0, import_react26.createElement)("span", { className: "bt-row-title" }, room2.name),
9865
+ (0, import_react26.createElement)("span", { className: "bt-tag" }, t("{count} members", { count: room2.members.length })),
9866
+ admin ? (0, import_react26.createElement)("span", { className: "bt-tag bt-tag-admin" }, (0, import_react26.createElement)(AdminStar), admin.name) : null,
9867
+ room2.hidden ? (0, import_react26.createElement)("span", { className: "bt-tag" }, t("Hidden")) : null
9696
9868
  ),
9697
- (0, import_react25.createElement)("span", { className: "bt-chevron" }, (0, import_react25.createElement)(ChevronRightIcon))
9869
+ (0, import_react26.createElement)("span", { className: "bt-chevron" }, (0, import_react26.createElement)(ChevronRightIcon))
9698
9870
  );
9699
9871
  })
9700
9872
  )
9701
9873
  ];
9702
9874
  }
9703
- function SettingsDialog({ page, id, roster, actions, leaving = false }) {
9704
- const [error, setError] = (0, import_react25.useState)("");
9705
- const close = (0, import_react25.useCallback)(() => actions.closeSettings(), []);
9875
+ function SettingsDialog({ page, id, roster, actions, useUpdate, leaving = false }) {
9876
+ const [error, setError] = (0, import_react26.useState)("");
9877
+ const close = (0, import_react26.useCallback)(() => actions.closeSettings(), []);
9706
9878
  useEscape(close);
9707
9879
  const run = (promise) => Promise.resolve(promise).then(() => {
9708
9880
  setError("");
@@ -9720,32 +9892,32 @@ function SettingsDialog({ page, id, roster, actions, leaving = false }) {
9720
9892
  const nav = useGlide(shown, { axis: "y" });
9721
9893
  const pageKey = `${shown}:${bot?.id ?? room2?.id ?? ""}`;
9722
9894
  const depth = bot || room2 ? 1 : 0;
9723
- const last = (0, import_react25.useRef)({ key: pageKey, depth, move: "up" });
9895
+ const last = (0, import_react26.useRef)({ key: pageKey, depth, move: "up" });
9724
9896
  if (last.current.key !== pageKey) {
9725
9897
  last.current = { key: pageKey, depth, move: depth > last.current.depth ? "in" : depth < last.current.depth ? "out" : "up" };
9726
9898
  }
9727
- const [usageOpened, setUsageOpened] = (0, import_react25.useState)(shown === "usage");
9899
+ const [usageOpened, setUsageOpened] = (0, import_react26.useState)(shown === "usage");
9728
9900
  if (shown === "usage" && !usageOpened) setUsageOpened(true);
9729
- const errorLine = error ? (0, import_react25.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
9730
- return (0, import_react25.createElement)(
9901
+ const errorLine = error ? (0, import_react26.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
9902
+ return (0, import_react26.createElement)(
9731
9903
  "div",
9732
9904
  { className: "bt-settings-layer", "data-leaving": leaving || void 0 },
9733
- (0, import_react25.createElement)("div", { className: "bt-scrim", onMouseDown: close }),
9734
- (0, import_react25.createElement)(
9905
+ (0, import_react26.createElement)("div", { className: "bt-scrim", onMouseDown: close }),
9906
+ (0, import_react26.createElement)(
9735
9907
  "div",
9736
9908
  { className: "bt-settings", role: "dialog", "aria-modal": true, "aria-label": t("DS Bot settings"), "data-wide": shown === "usage" ? "" : void 0 },
9737
- (0, import_react25.createElement)(
9909
+ (0, import_react26.createElement)(
9738
9910
  "nav",
9739
9911
  { ref: nav.box, className: "bt-settings-nav", "aria-label": t("Settings pages") },
9740
- (0, import_react25.createElement)("span", { ref: nav.thumb, className: "bt-thumb", "aria-hidden": true }),
9741
- SETTINGS_PAGES.map(([key, text]) => (0, import_react25.createElement)("button", {
9912
+ (0, import_react26.createElement)("span", { ref: nav.thumb, className: "bt-thumb", "aria-hidden": true }),
9913
+ SETTINGS_PAGES.map(([key, text]) => (0, import_react26.createElement)("button", {
9742
9914
  key,
9743
9915
  type: "button",
9744
9916
  className: "bt-settings-item",
9745
9917
  "aria-current": shown === key ? "page" : void 0,
9746
9918
  onClick: () => actions.openSettings(key)
9747
- }, (0, import_react25.createElement)(PAGE_ICONS[key]), t(text))),
9748
- (0, import_react25.createElement)("button", {
9919
+ }, (0, import_react26.createElement)(PAGE_ICONS[key]), t(text))),
9920
+ (0, import_react26.createElement)("button", {
9749
9921
  type: "button",
9750
9922
  className: "bt-settings-item bt-settings-shell",
9751
9923
  title: t("Models, providers, and the rest of DeepSeek Harness"),
@@ -9753,62 +9925,63 @@ function SettingsDialog({ page, id, roster, actions, leaving = false }) {
9753
9925
  close();
9754
9926
  openHarnessSettings();
9755
9927
  }
9756
- }, (0, import_react25.createElement)(SlidersIcon), t("Harness settings"))
9928
+ }, (0, import_react26.createElement)(SlidersIcon), t("Harness settings"))
9757
9929
  ),
9758
- (0, import_react25.createElement)(
9930
+ (0, import_react26.createElement)(
9759
9931
  "div",
9760
9932
  { className: "bt-settings-main" },
9761
- (0, import_react25.createElement)(
9933
+ (0, import_react26.createElement)(
9762
9934
  "div",
9763
9935
  { className: "bt-settings-head" },
9764
- back ? (0, import_react25.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": back, title: back, onClick: () => actions.openSettings(shown) }, (0, import_react25.createElement)(ChevronLeftIcon)) : null,
9765
- (0, import_react25.createElement)("h2", { key: bot?.id ?? room2?.id ?? shown }, bot?.name ?? room2?.name ?? label)
9936
+ 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,
9937
+ (0, import_react26.createElement)("h2", { key: bot?.id ?? room2?.id ?? shown }, bot?.name ?? room2?.name ?? label)
9766
9938
  ),
9767
- shown === "usage" ? null : (0, import_react25.createElement)(
9939
+ shown === "usage" ? null : (0, import_react26.createElement)(
9768
9940
  "div",
9769
9941
  { key: pageKey, className: "bt-settings-scroll bt-settings-page", "data-move": last.current.move },
9770
9942
  errorLine,
9771
- shown === "general" ? (0, import_react25.createElement)(GeneralPage, { roster, actions, run }) : null,
9772
- bot ? (0, import_react25.createElement)("div", { className: "bt-settings-form" }, (0, import_react25.createElement)(BotSettingsForm, { key: bot.id, bot, roster, actions, run })) : null,
9773
- shown === "bots" && !bot ? (0, import_react25.createElement)(BotsPage, { roster, actions, run }) : null,
9774
- room2 ? (0, import_react25.createElement)("div", { className: "bt-settings-form" }, (0, import_react25.createElement)(GroupSettingsForm, { key: room2.id, room: room2, roster, actions, run })) : null,
9775
- shown === "groups" && !room2 ? (0, import_react25.createElement)(GroupsPage, { roster, actions, run }) : null,
9776
- shown === "connectors" ? (0, import_react25.createElement)(ConnectorsPage, { roster, actions }) : null,
9777
- shown === "schedules" ? (0, import_react25.createElement)(SchedulesPage, { roster, actions, start: id ?? void 0 }) : null,
9778
- shown === "secrets" ? (0, import_react25.createElement)(SecretsPage, { roster, actions, run, Section: SetSection }) : null,
9779
- shown === "feedback" ? (0, import_react25.createElement)(FeedbackPage, { actions, run, Section: SetSection, Row: SetRow }) : null
9943
+ shown === "general" ? (0, import_react26.createElement)(GeneralPage, { roster, actions, run }) : null,
9944
+ bot ? (0, import_react26.createElement)("div", { className: "bt-settings-form" }, (0, import_react26.createElement)(BotSettingsForm, { key: bot.id, bot, roster, actions, run })) : null,
9945
+ shown === "bots" && !bot ? (0, import_react26.createElement)(BotsPage, { roster, actions, run }) : null,
9946
+ room2 ? (0, import_react26.createElement)("div", { className: "bt-settings-form" }, (0, import_react26.createElement)(GroupSettingsForm, { key: room2.id, room: room2, roster, actions, run })) : null,
9947
+ shown === "groups" && !room2 ? (0, import_react26.createElement)(GroupsPage, { roster, actions, run }) : null,
9948
+ shown === "connectors" ? (0, import_react26.createElement)(ConnectorsPage, { roster, actions }) : null,
9949
+ shown === "schedules" ? (0, import_react26.createElement)(SchedulesPage, { roster, actions, start: id ?? void 0 }) : null,
9950
+ shown === "secrets" ? (0, import_react26.createElement)(SecretsPage, { roster, actions, run, Section: SetSection }) : null,
9951
+ shown === "feedback" ? (0, import_react26.createElement)(FeedbackPage, { actions, run, Section: SetSection, Row: SetRow }) : null,
9952
+ shown === "about" ? (0, import_react26.createElement)(AboutPage, { useUpdate, actions, run, Section: SetSection, Row: SetRow }) : null
9780
9953
  ),
9781
- usageOpened ? (0, import_react25.createElement)(
9954
+ usageOpened ? (0, import_react26.createElement)(
9782
9955
  "div",
9783
9956
  { key: "usage", className: "bt-settings-scroll bt-settings-usage", "data-off": shown === "usage" ? void 0 : "", "aria-hidden": shown === "usage" ? void 0 : true },
9784
9957
  shown === "usage" ? errorLine : null,
9785
- (0, import_react25.createElement)(UsagePage, { roster, actions, focusId: usageFocus })
9958
+ (0, import_react26.createElement)(UsagePage, { roster, actions, focusId: usageFocus })
9786
9959
  ) : null
9787
9960
  ),
9788
- (0, import_react25.createElement)("button", { type: "button", className: "bt-settings-close", "aria-label": t("Close settings"), onClick: close }, (0, import_react25.createElement)(CloseIcon))
9961
+ (0, import_react26.createElement)("button", { type: "button", className: "bt-settings-close", "aria-label": t("Close settings"), onClick: close }, (0, import_react26.createElement)(CloseIcon))
9789
9962
  )
9790
9963
  );
9791
9964
  }
9792
9965
 
9793
9966
  // src/client/context-menu.js
9794
- var import_react26 = require("react");
9967
+ var import_react27 = require("react");
9795
9968
  function ContextMenu({ menu, roster, actions, leaving = false }) {
9796
9969
  useEscape(() => actions.closeMenu());
9797
- (0, import_react26.useEffect)(() => {
9970
+ (0, import_react27.useEffect)(() => {
9798
9971
  const onDown = (event) => {
9799
9972
  if (!(event.target instanceof Element) || !event.target.closest(".bt-menu")) actions.closeMenu();
9800
9973
  };
9801
9974
  window.addEventListener("mousedown", onDown);
9802
9975
  return () => window.removeEventListener("mousedown", onDown);
9803
9976
  }, []);
9804
- const ref = (0, import_react26.useRef)(null);
9805
- const [place, setPlace] = (0, import_react26.useState)({ left: menu.x, top: menu.y });
9806
- (0, import_react26.useLayoutEffect)(() => {
9977
+ const ref = (0, import_react27.useRef)(null);
9978
+ const [place, setPlace] = (0, import_react27.useState)({ left: menu.x, top: menu.y });
9979
+ (0, import_react27.useLayoutEffect)(() => {
9807
9980
  const box = ref.current?.getBoundingClientRect();
9808
9981
  if (!box) return;
9809
9982
  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
9983
  }, [menu.x, menu.y]);
9811
- if (menu.kind === "agent" || menu.kind === "agent-session") return (0, import_react26.createElement)(AgentMenu, { menu, roster, actions, leaving });
9984
+ if (menu.kind === "agent" || menu.kind === "agent-session") return (0, import_react27.createElement)(AgentMenu, { menu, roster, actions, leaving });
9812
9985
  const bot = roster.byId[menu.id];
9813
9986
  const room2 = roster.roomsById[menu.id];
9814
9987
  const entry = bot ?? room2;
@@ -9839,14 +10012,14 @@ function ContextMenu({ menu, roster, actions, leaving = false }) {
9839
10012
  isMainBot ? null : [room2 ? t("Delete group") : t("Delete"), TrashIcon, () => bot ? actions.deleteBot(bot.id) : actions.deleteRoom(menu.id), true]
9840
10013
  ]
9841
10014
  ].map((group) => group.filter(Boolean)).filter((group) => group.length > 0);
9842
- return (0, import_react26.createElement)(
10015
+ return (0, import_react27.createElement)(
9843
10016
  "div",
9844
10017
  { 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, import_react26.createElement)(
9846
- import_react26.Fragment,
10018
+ groups.map((group, index) => (0, import_react27.createElement)(
10019
+ import_react27.Fragment,
9847
10020
  { key: index },
9848
- index > 0 ? (0, import_react26.createElement)("div", { className: "bt-menu-sep", role: "separator" }) : null,
9849
- group.map(([label, Icon, run, danger]) => (0, import_react26.createElement)("button", {
10021
+ index > 0 ? (0, import_react27.createElement)("div", { className: "bt-menu-sep", role: "separator" }) : null,
10022
+ group.map(([label, Icon, run, danger]) => (0, import_react27.createElement)("button", {
9850
10023
  key: label,
9851
10024
  type: "button",
9852
10025
  role: "menuitem",
@@ -9855,14 +10028,14 @@ function ContextMenu({ menu, roster, actions, leaving = false }) {
9855
10028
  actions.closeMenu();
9856
10029
  void Promise.resolve(run()).catch((error) => actions.reportError(error, "menu"));
9857
10030
  }
9858
- }, (0, import_react26.createElement)(Icon), label))
10031
+ }, (0, import_react27.createElement)(Icon), label))
9859
10032
  ))
9860
10033
  );
9861
10034
  }
9862
10035
  function AgentMenu({ menu, roster, actions, leaving }) {
9863
- const ref = (0, import_react26.useRef)(null);
9864
- const [place, setPlace] = (0, import_react26.useState)({ left: menu.x, top: menu.y });
9865
- (0, import_react26.useLayoutEffect)(() => {
10036
+ const ref = (0, import_react27.useRef)(null);
10037
+ const [place, setPlace] = (0, import_react27.useState)({ left: menu.x, top: menu.y });
10038
+ (0, import_react27.useLayoutEffect)(() => {
9866
10039
  const box = ref.current?.getBoundingClientRect();
9867
10040
  if (!box) return;
9868
10041
  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 +10057,14 @@ function AgentMenu({ menu, roster, actions, leaving }) {
9884
10057
  [t("Archive"), TrashIcon, () => actions.archiveAgentSession(agent.id, menu.id), true]
9885
10058
  ]
9886
10059
  ];
9887
- return (0, import_react26.createElement)(
10060
+ return (0, import_react27.createElement)(
9888
10061
  "div",
9889
10062
  { 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, import_react26.createElement)(
9891
- import_react26.Fragment,
10063
+ groups.map((group, index) => (0, import_react27.createElement)(
10064
+ import_react27.Fragment,
9892
10065
  { key: index },
9893
- index > 0 ? (0, import_react26.createElement)("div", { className: "bt-menu-sep", role: "separator" }) : null,
9894
- group.map(([label, Icon, run, danger]) => (0, import_react26.createElement)("button", {
10066
+ index > 0 ? (0, import_react27.createElement)("div", { className: "bt-menu-sep", role: "separator" }) : null,
10067
+ group.map(([label, Icon, run, danger]) => (0, import_react27.createElement)("button", {
9895
10068
  key: label,
9896
10069
  type: "button",
9897
10070
  role: "menuitem",
@@ -9900,7 +10073,7 @@ function AgentMenu({ menu, roster, actions, leaving }) {
9900
10073
  actions.closeMenu();
9901
10074
  void Promise.resolve(run()).catch((error) => console.warn("[ds-bot]", error));
9902
10075
  }
9903
- }, (0, import_react26.createElement)(Icon), label))
10076
+ }, (0, import_react27.createElement)(Icon), label))
9904
10077
  ))
9905
10078
  );
9906
10079
  }
@@ -9971,7 +10144,7 @@ function migrateStorage(storage = globalThis.localStorage) {
9971
10144
  }
9972
10145
 
9973
10146
  // src/client/details-drawer.js
9974
- var import_react27 = require("react");
10147
+ var import_react28 = require("react");
9975
10148
  function transcriptOf(list, id, name, hiddenTurns = []) {
9976
10149
  const outline = list.projectionsBySession?.[id]?.values?.turnOutline ?? list.byId[id]?.projectionValues?.turnOutline;
9977
10150
  if (!Array.isArray(outline)) return "";
@@ -9996,10 +10169,10 @@ var EMPTY_TABS = { ids: [], active: null };
9996
10169
  var PERMISSION_LABELS = { "read-only": "Read only", "workspace-write": "Workspace", "danger-full-access": "Full access" };
9997
10170
  var DANGER = /* @__PURE__ */ new Set(["danger-full-access"]);
9998
10171
  function PermissionRow({ bot, roster, actions, run }) {
9999
- const [pending, setPending] = (0, import_react27.useState)(null);
10172
+ const [pending, setPending] = (0, import_react28.useState)(null);
10000
10173
  const catalog = roster.permissions;
10001
10174
  const saved = bot.permission ?? catalog?.defaultPreset;
10002
- (0, import_react27.useEffect)(() => {
10175
+ (0, import_react28.useEffect)(() => {
10003
10176
  if (pending === saved) setPending(null);
10004
10177
  }, [pending, saved]);
10005
10178
  if (!catalog?.options?.length) return null;
@@ -10009,14 +10182,14 @@ function PermissionRow({ bot, roster, actions, run }) {
10009
10182
  setPending(value);
10010
10183
  void run(actions.updateBot(bot.id, { permission: value })).catch(() => setPending(null));
10011
10184
  };
10012
- return (0, import_react27.createElement)(
10185
+ return (0, import_react28.createElement)(
10013
10186
  "div",
10014
10187
  { className: "bt-drawer-permission" },
10015
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t("Permissions")),
10016
- (0, import_react27.createElement)(
10188
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t("Permissions")),
10189
+ (0, import_react28.createElement)(
10017
10190
  Segmented,
10018
10191
  { label: t("Permissions"), value: current },
10019
- catalog.options.map((option) => (0, import_react27.createElement)("button", {
10192
+ catalog.options.map((option) => (0, import_react28.createElement)("button", {
10020
10193
  key: option.value,
10021
10194
  type: "button",
10022
10195
  "aria-pressed": option.value === current,
@@ -10030,42 +10203,42 @@ function PermissionRow({ bot, roster, actions, run }) {
10030
10203
  function BrowserCards({ bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) {
10031
10204
  const state = useBrowserTabs((map) => map[bot.id] ?? EMPTY_TABS);
10032
10205
  const pages = useBrowserPages((map) => map);
10033
- (0, import_react27.useEffect)(() => {
10206
+ (0, import_react28.useEffect)(() => {
10034
10207
  actions.browserEnsure?.(bot.id);
10035
10208
  }, [bot.id]);
10036
10209
  if (roster.browser !== true || !actions.browserShow) {
10037
- return (0, import_react27.createElement)("div", { className: "bt-muted" }, t("The browser is in the DSH Desktop app."));
10210
+ return (0, import_react28.createElement)("div", { className: "bt-muted" }, t("The browser is in the DSH Desktop app."));
10038
10211
  }
10039
10212
  if (state.ids.length === 0) {
10040
- return (0, import_react27.createElement)(
10213
+ return (0, import_react28.createElement)(
10041
10214
  "div",
10042
10215
  { className: "bt-browser-empty-mark" },
10043
- (0, import_react27.createElement)(BotMark, { bot, size: 40, state: "idle", live: true }),
10044
- (0, import_react27.createElement)("div", { className: "bt-browser-empty-title" }, t("{name}'s browser", { name: bot.name })),
10045
- (0, import_react27.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 })),
10046
- (0, import_react27.createElement)("button", { type: "button", className: "bt-browser-retry", onClick: () => actions.openBrowser(sessionId) }, t("Open browser"))
10216
+ (0, import_react28.createElement)(BotMark, { bot, size: 40, state: "idle", live: true }),
10217
+ (0, import_react28.createElement)("div", { className: "bt-browser-empty-title" }, t("{name}'s browser", { name: bot.name })),
10218
+ (0, import_react28.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 })),
10219
+ (0, import_react28.createElement)("button", { type: "button", className: "bt-browser-retry", onClick: () => actions.openBrowser(sessionId) }, t("Open browser"))
10047
10220
  );
10048
10221
  }
10049
- return (0, import_react27.createElement)(
10222
+ return (0, import_react28.createElement)(
10050
10223
  "div",
10051
10224
  { className: "bt-bcards" },
10052
10225
  state.ids.map((tabId, index) => {
10053
10226
  const page = pages[tabId] ?? {};
10054
10227
  const label = page.title || shortUrl(page.url) || t("New tab");
10055
- return (0, import_react27.createElement)(
10228
+ return (0, import_react28.createElement)(
10056
10229
  "div",
10057
10230
  { key: tabId, className: "bt-bcard", "data-active": state.active === tabId || void 0, style: { animationDelay: `${index * 30}ms` } },
10058
- (0, import_react27.createElement)(
10231
+ (0, import_react28.createElement)(
10059
10232
  "button",
10060
10233
  { 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, import_react27.createElement)(
10234
+ page.excerpt ? (0, import_react28.createElement)(
10062
10235
  "span",
10063
10236
  { className: "bt-bcard-preview" },
10064
- (0, import_react27.createElement)("span", { className: "bt-bcard-ptitle" }, label),
10065
- (0, import_react27.createElement)("span", { className: "bt-bcard-excerpt" }, page.excerpt)
10066
- ) : (0, import_react27.createElement)("span", { className: "bt-bcard-blank" }, (0, import_react27.createElement)(GlobeIcon), (0, import_react27.createElement)("span", null, label))
10237
+ (0, import_react28.createElement)("span", { className: "bt-bcard-ptitle" }, label),
10238
+ (0, import_react28.createElement)("span", { className: "bt-bcard-excerpt" }, page.excerpt)
10239
+ ) : (0, import_react28.createElement)("span", { className: "bt-bcard-blank" }, (0, import_react28.createElement)(GlobeIcon), (0, import_react28.createElement)("span", null, label))
10067
10240
  ),
10068
- (0, import_react27.createElement)("button", {
10241
+ (0, import_react28.createElement)("button", {
10069
10242
  type: "button",
10070
10243
  className: "bt-bcard-x",
10071
10244
  "aria-label": t("Close tab"),
@@ -10074,62 +10247,62 @@ function BrowserCards({ bot, sessionId, roster, actions, useBrowserTabs, useBrow
10074
10247
  void actions.browserCloseTab(bot.id, tabId);
10075
10248
  }
10076
10249
  }, "×"),
10077
- (0, import_react27.createElement)(
10250
+ (0, import_react28.createElement)(
10078
10251
  "div",
10079
10252
  { className: "bt-bcard-row" },
10080
- (0, import_react27.createElement)(Favicon, { src: page.favicon, className: "bt-bcard-fav" }),
10081
- (0, import_react27.createElement)("span", { className: "bt-bcard-title" }, label),
10082
- page.url ? (0, import_react27.createElement)("span", { className: "bt-bcard-host" }, hostOf(page.url)) : null
10253
+ (0, import_react28.createElement)(Favicon, { src: page.favicon, className: "bt-bcard-fav" }),
10254
+ (0, import_react28.createElement)("span", { className: "bt-bcard-title" }, label),
10255
+ page.url ? (0, import_react28.createElement)("span", { className: "bt-bcard-host" }, hostOf(page.url)) : null
10083
10256
  )
10084
10257
  );
10085
10258
  }),
10086
- (0, import_react27.createElement)("button", { type: "button", className: "bt-bcard-new", style: { animationDelay: `${state.ids.length * 30}ms` }, onClick: () => actions.browserNewTab(sessionId) }, (0, import_react27.createElement)(PlusIcon), t("New tab"))
10259
+ (0, import_react28.createElement)("button", { type: "button", className: "bt-bcard-new", style: { animationDelay: `${state.ids.length * 30}ms` }, onClick: () => actions.browserNewTab(sessionId) }, (0, import_react28.createElement)(PlusIcon), t("New tab"))
10087
10260
  );
10088
10261
  }
10089
10262
  function About({ text, title = "About" }) {
10090
- const [open, setOpen] = (0, import_react27.useState)(false);
10263
+ const [open, setOpen] = (0, import_react28.useState)(false);
10091
10264
  const long = text.split("\n").length > 5 || text.length > 240;
10092
- const body = (0, import_react27.useRef)(null);
10265
+ const body = (0, import_react28.useRef)(null);
10093
10266
  useMorph(body, open);
10094
- return (0, import_react27.createElement)(
10267
+ return (0, import_react28.createElement)(
10095
10268
  "div",
10096
10269
  null,
10097
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t(title)),
10098
- (0, import_react27.createElement)("div", { ref: body, className: "bt-about", "data-open": open || void 0 }, text),
10099
- long ? (0, import_react27.createElement)("button", { type: "button", className: "bt-more", onClick: () => setOpen((value) => !value) }, open ? t("Show less") : t("Show more")) : null
10270
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t(title)),
10271
+ (0, import_react28.createElement)("div", { ref: body, className: "bt-about", "data-open": open || void 0 }, text),
10272
+ long ? (0, import_react28.createElement)("button", { type: "button", className: "bt-more", onClick: () => setOpen((value) => !value) }, open ? t("Show less") : t("Show more")) : null
10100
10273
  );
10101
10274
  }
10102
10275
  function GroupOverview({ room: room2, roster, actions, usage, openSettings }) {
10103
10276
  const members = room2.members.map((id) => roster.byId[id]).filter(Boolean);
10104
10277
  return [
10105
- room2.notice ? (0, import_react27.createElement)(About, { key: "notice", title: "Notice", text: room2.notice }) : (0, import_react27.createElement)("button", { key: "notice", type: "button", className: "bt-more", onClick: openSettings }, t("Add a notice in group settings")),
10106
- (0, import_react27.createElement)(
10278
+ room2.notice ? (0, import_react28.createElement)(About, { key: "notice", title: "Notice", text: room2.notice }) : (0, import_react28.createElement)("button", { key: "notice", type: "button", className: "bt-more", onClick: openSettings }, t("Add a notice in group settings")),
10279
+ (0, import_react28.createElement)(
10107
10280
  "div",
10108
10281
  { key: "members" },
10109
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t("Members · {count}", { count: members.length })),
10110
- (0, import_react27.createElement)("div", { className: "bt-members", role: "list" }, members.map((member) => (0, import_react27.createElement)(
10282
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t("Members · {count}", { count: members.length })),
10283
+ (0, import_react28.createElement)("div", { className: "bt-members", role: "list" }, members.map((member) => (0, import_react28.createElement)(
10111
10284
  "div",
10112
10285
  { key: member.id, className: "bt-member", role: "listitem" },
10113
- (0, import_react27.createElement)(
10286
+ (0, import_react28.createElement)(
10114
10287
  "button",
10115
10288
  { type: "button", className: "bt-member-name", title: t("Open {name}'s chat", { name: member.name }), onClick: () => actions.openSession(member.id) },
10116
- (0, import_react27.createElement)(BotAvatar, { bot: member, size: 26, main: isMainOf(roster, member.id), admin: room2.admin === member.id, live: false }),
10117
- (0, import_react27.createElement)("span", null, member.name)
10289
+ (0, import_react28.createElement)(BotAvatar, { bot: member, size: 26, main: isMainOf(roster, member.id), admin: room2.admin === member.id, live: false }),
10290
+ (0, import_react28.createElement)("span", null, member.name)
10118
10291
  ),
10119
- room2.admin === member.id ? (0, import_react27.createElement)("span", { className: "bt-chip bt-chip-admin" }, t("Admin")) : null
10292
+ room2.admin === member.id ? (0, import_react28.createElement)("span", { className: "bt-chip bt-chip-admin" }, t("Admin")) : null
10120
10293
  )))
10121
10294
  ),
10122
- (0, import_react27.createElement)(
10295
+ (0, import_react28.createElement)(
10123
10296
  "div",
10124
10297
  { key: "mode" },
10125
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t("Who answers")),
10126
- (0, import_react27.createElement)("div", { className: "bt-kv" }, (0, import_react27.createElement)("span", null, t(ROOM_MODES.find(([id]) => id === shownMode(room2, roster))?.[1] ?? "Everyone")))
10298
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t("Who answers")),
10299
+ (0, import_react28.createElement)("div", { className: "bt-kv" }, (0, import_react28.createElement)("span", null, t(ROOM_MODES.find(([id]) => id === shownMode(room2, roster))?.[1] ?? "Everyone")))
10127
10300
  ),
10128
- usage ? (0, import_react27.createElement)(
10301
+ usage ? (0, import_react28.createElement)(
10129
10302
  "div",
10130
10303
  { key: "usage" },
10131
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t("Usage")),
10132
- (0, import_react27.createElement)("div", { className: "bt-kv" }, (0, import_react27.createElement)("span", null, room2.name), (0, import_react27.createElement)("span", null, usage))
10304
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t("Usage")),
10305
+ (0, import_react28.createElement)("div", { className: "bt-kv" }, (0, import_react28.createElement)("span", null, room2.name), (0, import_react28.createElement)("span", null, usage))
10133
10306
  ) : null
10134
10307
  ];
10135
10308
  }
@@ -10137,15 +10310,15 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
10137
10310
  const owner = roster.byId[sessionId];
10138
10311
  const part = owner ? currentPart(owner) : sessionId;
10139
10312
  const isMainBot = isMainOf(roster, owner?.id);
10140
- const [tab, setTab] = (0, import_react27.useState)("details");
10313
+ const [tab, setTab] = (0, import_react28.useState)("details");
10141
10314
  const room2 = roster.roomsById[sessionId];
10142
- const [view, setView] = (0, import_react27.useState)(startTab === "settings" && (owner || room2) ? "settings" : "overview");
10143
- const [editing, setEditing] = (0, import_react27.useState)(Boolean(startRenaming && (roster.byId[sessionId] || roster.roomsById[sessionId])));
10144
- const [draftName, setDraftName] = (0, import_react27.useState)(roster.byId[sessionId]?.name ?? (roster.roomsById[sessionId]?.named ? roster.roomsById[sessionId].name : ""));
10145
- const [copied, setCopied] = (0, import_react27.useState)(false);
10146
- const [error, setError] = (0, import_react27.useState)("");
10315
+ const [view, setView] = (0, import_react28.useState)(startTab === "settings" && (owner || room2) ? "settings" : "overview");
10316
+ const [editing, setEditing] = (0, import_react28.useState)(Boolean(startRenaming && (roster.byId[sessionId] || roster.roomsById[sessionId])));
10317
+ const [draftName, setDraftName] = (0, import_react28.useState)(roster.byId[sessionId]?.name ?? (roster.roomsById[sessionId]?.named ? roster.roomsById[sessionId].name : ""));
10318
+ const [copied, setCopied] = (0, import_react28.useState)(false);
10319
+ const [error, setError] = (0, import_react28.useState)("");
10147
10320
  const glide = useGlide(tab);
10148
- const last = (0, import_react27.useRef)({ view, tab, move: void 0 });
10321
+ const last = (0, import_react28.useRef)({ view, tab, move: void 0 });
10149
10322
  if (last.current.view !== view) last.current = { view, tab, move: view === "settings" ? "in" : "out" };
10150
10323
  else if (last.current.tab !== tab) last.current = { view, tab, move: "tab" };
10151
10324
  const move = last.current.move;
@@ -10170,36 +10343,36 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
10170
10343
  setTimeout(() => setCopied(false), 1600);
10171
10344
  });
10172
10345
  };
10173
- const errorLine = error ? (0, import_react27.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
10346
+ const errorLine = error ? (0, import_react28.createElement)("div", { className: "bt-error", role: "alert", style: { padding: 0 } }, error) : null;
10174
10347
  if ((bot || room2) && view === "settings") {
10175
10348
  return [
10176
- (0, import_react27.createElement)(
10349
+ (0, import_react28.createElement)(
10177
10350
  "div",
10178
10351
  { key: "head", className: "bt-subhead", "data-move": move },
10179
- (0, import_react27.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Back to details"), onClick: () => setView("overview") }, (0, import_react27.createElement)(ChevronLeftIcon)),
10180
- (0, import_react27.createElement)("span", { className: "bt-subhead-title" }, bot ? t("Settings") : t("Group settings")),
10181
- hasStrip ? null : (0, import_react27.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Close details"), onClick: close }, (0, import_react27.createElement)(CloseIcon))
10352
+ (0, import_react28.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Back to details"), onClick: () => setView("overview") }, (0, import_react28.createElement)(ChevronLeftIcon)),
10353
+ (0, import_react28.createElement)("span", { className: "bt-subhead-title" }, bot ? t("Settings") : t("Group settings")),
10354
+ hasStrip ? null : (0, import_react28.createElement)("button", { type: "button", className: "bt-icon-btn", "aria-label": t("Close details"), onClick: close }, (0, import_react28.createElement)(CloseIcon))
10182
10355
  ),
10183
- (0, import_react27.createElement)(
10356
+ (0, import_react28.createElement)(
10184
10357
  "div",
10185
10358
  { key: "set-body", className: "bt-drawer-body bt-set-body", "data-move": move },
10186
10359
  errorLine,
10187
- bot ? (0, import_react27.createElement)(BotSettingsForm, { bot, roster, actions, run }) : (0, import_react27.createElement)(GroupSettingsForm, { room: room2, roster, actions, run, onDeleted: close })
10360
+ bot ? (0, import_react28.createElement)(BotSettingsForm, { bot, roster, actions, run }) : (0, import_react28.createElement)(GroupSettingsForm, { room: room2, roster, actions, run, onDeleted: close })
10188
10361
  )
10189
10362
  ];
10190
10363
  }
10191
- const top = (0, import_react27.createElement)(
10364
+ const top = (0, import_react28.createElement)(
10192
10365
  "div",
10193
10366
  { className: "bt-drawer-top", "data-move": side },
10194
- bot || room2 ? (0, import_react27.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_react27.createElement)(GearIcon)) : null,
10195
- bot && roster.browser === true && actions.openBrowser ? (0, import_react27.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_react27.createElement)(GlobeIcon)) : null,
10196
- bot || room2 ? (0, import_react27.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_react27.createElement)("span", { key: "copied", className: "bt-pop-in" }, "✓") : (0, import_react27.createElement)(ShareIcon)) : null,
10197
- hasStrip ? null : (0, import_react27.createElement)("button", { type: "button", className: "bt-round", "aria-label": t("Close details"), onClick: close }, (0, import_react27.createElement)(CloseIcon))
10367
+ bot || room2 ? (0, import_react28.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_react28.createElement)(GearIcon)) : null,
10368
+ bot && roster.browser === true && actions.openBrowser ? (0, import_react28.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_react28.createElement)(GlobeIcon)) : null,
10369
+ bot || room2 ? (0, import_react28.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_react28.createElement)("span", { key: "copied", className: "bt-pop-in" }, "✓") : (0, import_react28.createElement)(ShareIcon)) : null,
10370
+ hasStrip ? null : (0, import_react28.createElement)("button", { type: "button", className: "bt-round", "aria-label": t("Close details"), onClick: close }, (0, import_react28.createElement)(CloseIcon))
10198
10371
  );
10199
10372
  if (!bot && !room2) {
10200
10373
  return [
10201
- (0, import_react27.createElement)(import_react27.Fragment, { key: "top" }, top),
10202
- (0, import_react27.createElement)("div", { key: "body", className: "bt-drawer-body" }, (0, import_react27.createElement)("div", { className: "bt-muted" }, t("This conversation is not part of your Bot team.")))
10374
+ (0, import_react28.createElement)(import_react28.Fragment, { key: "top" }, top),
10375
+ (0, import_react28.createElement)("div", { key: "body", className: "bt-drawer-body" }, (0, import_react28.createElement)("div", { className: "bt-muted" }, t("This conversation is not part of your Bot team.")))
10203
10376
  ];
10204
10377
  }
10205
10378
  const current = bot?.name ?? room2.name;
@@ -10213,12 +10386,12 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
10213
10386
  const subtitle = bot ? bot.role : roomSubtitle(room2, roster);
10214
10387
  const tabs = bot ? ["details", "browser"] : ["details"];
10215
10388
  return [
10216
- (0, import_react27.createElement)(import_react27.Fragment, { key: "top" }, top),
10217
- (0, import_react27.createElement)(
10389
+ (0, import_react28.createElement)(import_react28.Fragment, { key: "top" }, top),
10390
+ (0, import_react28.createElement)(
10218
10391
  "div",
10219
10392
  { key: "id", className: "bt-drawer-id", "data-move": side },
10220
- bot ? (0, import_react27.createElement)(BotAvatar, { bot, size: 64, state: botState(running, doing, roster.questions?.[part] !== void 0), main: isMainBot, badge: false, gaze: true, pokeable: true }) : (0, import_react27.createElement)(RoomAvatar, { room: room2, roster, size: 64 }),
10221
- editing ? (0, import_react27.createElement)("input", {
10393
+ bot ? (0, import_react28.createElement)(BotAvatar, { bot, size: 64, state: botState(running, doing, roster.questions?.[part] !== void 0), main: isMainBot, badge: false, gaze: true, pokeable: true }) : (0, import_react28.createElement)(RoomAvatar, { room: room2, roster, size: 64 }),
10394
+ editing ? (0, import_react28.createElement)("input", {
10222
10395
  className: "bt-drawer-name-input",
10223
10396
  autoFocus: true,
10224
10397
  value: draftName,
@@ -10233,49 +10406,49 @@ function DetailsDrawer({ sessionId, startRenaming, startTab, hasStrip, roster, a
10233
10406
  setEditing(false);
10234
10407
  }
10235
10408
  }
10236
- }) : (0, import_react27.createElement)("button", { type: "button", className: "bt-drawer-name", "aria-label": t("Rename {name}", { name: current }), title: t("Rename"), onClick: () => {
10409
+ }) : (0, import_react28.createElement)("button", { type: "button", className: "bt-drawer-name", "aria-label": t("Rename {name}", { name: current }), title: t("Rename"), onClick: () => {
10237
10410
  setDraftName(room2 && !room2.named ? "" : current);
10238
10411
  setEditing(true);
10239
10412
  } }, current),
10240
- subtitle ? (0, import_react27.createElement)("span", { className: "bt-drawer-role" }, subtitle) : null
10413
+ subtitle ? (0, import_react28.createElement)("span", { className: "bt-drawer-role" }, subtitle) : null
10241
10414
  ),
10242
- (0, import_react27.createElement)(
10415
+ (0, import_react28.createElement)(
10243
10416
  "div",
10244
10417
  { key: "tabs", ref: glide.box, className: "bt-tabs", role: "tablist", "data-move": side },
10245
- (0, import_react27.createElement)("span", { ref: glide.thumb, className: "bt-thumb", "aria-hidden": true }),
10246
- tabs.map((key) => (0, import_react27.createElement)("button", { key, type: "button", role: "tab", className: "bt-tab", "aria-selected": tab === key, onClick: () => setTab(key) }, t(TAB_LABELS[key])))
10418
+ (0, import_react28.createElement)("span", { ref: glide.thumb, className: "bt-thumb", "aria-hidden": true }),
10419
+ tabs.map((key) => (0, import_react28.createElement)("button", { key, type: "button", role: "tab", className: "bt-tab", "aria-selected": tab === key, onClick: () => setTab(key) }, t(TAB_LABELS[key])))
10247
10420
  ),
10248
- (0, import_react27.createElement)(
10421
+ (0, import_react28.createElement)(
10249
10422
  "div",
10250
10423
  { key: `body:${tab}`, className: "bt-drawer-body", "data-move": move },
10251
10424
  errorLine,
10252
- room2 && tab === "details" ? (0, import_react27.createElement)(GroupOverview, { room: room2, roster, actions, usage, openSettings: () => setView("settings") }) : null,
10425
+ room2 && tab === "details" ? (0, import_react28.createElement)(GroupOverview, { room: room2, roster, actions, usage, openSettings: () => setView("settings") }) : null,
10253
10426
  bot && tab === "details" ? [
10254
- (0, import_react27.createElement)(
10427
+ (0, import_react28.createElement)(
10255
10428
  "div",
10256
10429
  { key: "model", className: "bt-drawer-model" },
10257
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t("Model")),
10258
- (0, import_react27.createElement)(ModelMenu, { bot, roster, actions, run, wide: true }),
10259
- modelOf(bot, roster) ? null : (0, import_react27.createElement)("div", { className: "bt-note" }, t("Pick a model and {name} can start.", { name: bot.name }))
10430
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t("Model")),
10431
+ (0, import_react28.createElement)(ModelMenu, { bot, roster, actions, run, wide: true }),
10432
+ modelOf(bot, roster) ? null : (0, import_react28.createElement)("div", { className: "bt-note" }, t("Pick a model and {name} can start.", { name: bot.name }))
10260
10433
  ),
10261
- (0, import_react27.createElement)(PermissionRow, { key: "permission", bot, roster, actions, run }),
10262
- bot.instructions ? (0, import_react27.createElement)(About, { key: "about", text: bot.instructions }) : (0, import_react27.createElement)("button", { key: "about", type: "button", className: "bt-more", onClick: () => setView("settings") }, t("Add instructions in Bot settings")),
10263
- roster.memory === false ? null : (0, import_react27.createElement)(MemoryRow, { key: "memory", bot, actions }),
10264
- usage ? (0, import_react27.createElement)(
10434
+ (0, import_react28.createElement)(PermissionRow, { key: "permission", bot, roster, actions, run }),
10435
+ bot.instructions ? (0, import_react28.createElement)(About, { key: "about", text: bot.instructions }) : (0, import_react28.createElement)("button", { key: "about", type: "button", className: "bt-more", onClick: () => setView("settings") }, t("Add instructions in Bot settings")),
10436
+ roster.memory === false ? null : (0, import_react28.createElement)(MemoryRow, { key: "memory", bot, actions }),
10437
+ usage ? (0, import_react28.createElement)(
10265
10438
  "div",
10266
10439
  { key: "usage" },
10267
- (0, import_react27.createElement)("div", { className: "bt-section-title" }, t("Usage")),
10268
- (0, import_react27.createElement)("div", { className: "bt-kv" }, (0, import_react27.createElement)("span", null, bot.name), (0, import_react27.createElement)("span", null, usage))
10440
+ (0, import_react28.createElement)("div", { className: "bt-section-title" }, t("Usage")),
10441
+ (0, import_react28.createElement)("div", { className: "bt-kv" }, (0, import_react28.createElement)("span", null, bot.name), (0, import_react28.createElement)("span", null, usage))
10269
10442
  ) : null,
10270
- (0, import_react27.createElement)(ScheduleSummary, { key: "routines", bot, actions })
10443
+ (0, import_react28.createElement)(ScheduleSummary, { key: "routines", bot, actions })
10271
10444
  ] : null,
10272
- bot && tab === "browser" ? (0, import_react27.createElement)(BrowserCards, { bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) : null
10445
+ bot && tab === "browser" ? (0, import_react28.createElement)(BrowserCards, { bot, sessionId, roster, actions, useBrowserTabs, useBrowserPages }) : null
10273
10446
  )
10274
10447
  ];
10275
10448
  }
10276
10449
 
10277
10450
  // src/client/browser-pane.js
10278
- var import_react28 = require("react");
10451
+ var import_react29 = require("react");
10279
10452
  var import_react_dom7 = require("react-dom");
10280
10453
  var PANEL_LINGER_MS = 300;
10281
10454
  var WIDTH_KEY = STORAGE.browserWidth;
@@ -10290,10 +10463,10 @@ var EMPTY_TABS2 = { ids: [], active: null };
10290
10463
  var SPRING_LEAD2 = springEasing(576, 39.4);
10291
10464
  var SPRING_TRAIL2 = springEasing(144, 19.7);
10292
10465
  var icon = (paths) => function BrowserIcon() {
10293
- return (0, import_react28.createElement)(
10466
+ return (0, import_react29.createElement)(
10294
10467
  "svg",
10295
10468
  { 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, import_react28.createElement)("path", { key: index, d }))
10469
+ paths.map((d, index) => (0, import_react29.createElement)("path", { key: index, d }))
10297
10470
  );
10298
10471
  };
10299
10472
  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 +10478,9 @@ var CloseIcon2 = icon(["M5.5 5.5l9 9M14.5 5.5l-9 9"]);
10305
10478
  var PlusTabIcon = icon(["M10 5v10M5 10h10"]);
10306
10479
  var LockIcon2 = icon(["M6.5 9V7a3.5 3.5 0 0 1 7 0v2", "M5.5 9h9v7h-9z"]);
10307
10480
  function Favicon({ src, className }) {
10308
- const [failed, setFailed] = (0, import_react28.useState)(null);
10309
- if (!src || failed === src) return (0, import_react28.createElement)("span", { className: `${className} bt-ptab-globe` }, (0, import_react28.createElement)(GlobeIcon));
10310
- return (0, import_react28.createElement)("img", { className, src, alt: "", "aria-hidden": true, onError: () => setFailed(src) });
10481
+ const [failed, setFailed] = (0, import_react29.useState)(null);
10482
+ if (!src || failed === src) return (0, import_react29.createElement)("span", { className: `${className} bt-ptab-globe` }, (0, import_react29.createElement)(GlobeIcon));
10483
+ return (0, import_react29.createElement)("img", { className, src, alt: "", "aria-hidden": true, onError: () => setFailed(src) });
10311
10484
  }
10312
10485
  var savedWidth = () => {
10313
10486
  try {
@@ -10366,11 +10539,11 @@ var reducedMotion2 = () => typeof matchMedia === "function" && matchMedia("(pref
10366
10539
  var tabLabel = (page) => page.title || shortAddress(page.url) || t("New tab");
10367
10540
  function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions, useBrowserPages }) {
10368
10541
  const pages = useBrowserPages((map) => map);
10369
- const wrap = (0, import_react28.useRef)(null);
10370
- const ind = (0, import_react28.useRef)(null);
10371
- const spot = (0, import_react28.useRef)(null);
10372
- const under = (0, import_react28.useRef)(null);
10373
- (0, import_react28.useLayoutEffect)(() => {
10542
+ const wrap = (0, import_react29.useRef)(null);
10543
+ const ind = (0, import_react29.useRef)(null);
10544
+ const spot = (0, import_react29.useRef)(null);
10545
+ const under = (0, import_react29.useRef)(null);
10546
+ (0, import_react29.useLayoutEffect)(() => {
10374
10547
  const box = wrap.current;
10375
10548
  const capsule = ind.current;
10376
10549
  if (!box || !capsule) return void 0;
@@ -10399,7 +10572,7 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
10399
10572
  trail.cancel();
10400
10573
  };
10401
10574
  });
10402
- (0, import_react28.useEffect)(() => {
10575
+ (0, import_react29.useEffect)(() => {
10403
10576
  const box = wrap.current;
10404
10577
  if (!box) return void 0;
10405
10578
  const observer = new ResizeObserver(() => {
@@ -10413,13 +10586,13 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
10413
10586
  observer.observe(box);
10414
10587
  return () => observer.disconnect();
10415
10588
  }, []);
10416
- return (0, import_react28.createElement)(
10589
+ return (0, import_react29.createElement)(
10417
10590
  "div",
10418
10591
  { className: "bt-ptabs", role: "tablist", "aria-label": t("Panel tabs") },
10419
- (0, import_react28.createElement)(
10592
+ (0, import_react29.createElement)(
10420
10593
  "div",
10421
10594
  { className: "bt-ptabs-in", ref: wrap },
10422
- (0, import_react28.createElement)(
10595
+ (0, import_react29.createElement)(
10423
10596
  "button",
10424
10597
  {
10425
10598
  type: "button",
@@ -10429,13 +10602,13 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
10429
10602
  title: bot.name,
10430
10603
  onClick: () => onView("details")
10431
10604
  },
10432
- (0, import_react28.createElement)(BotMark, { bot, size: 20, state: reading ? "searching" : "idle" }),
10433
- view === "details" ? (0, import_react28.createElement)("span", { className: "bt-ptab-name" }, bot.name) : null
10605
+ (0, import_react29.createElement)(BotMark, { bot, size: 20, state: reading ? "searching" : "idle" }),
10606
+ view === "details" ? (0, import_react29.createElement)("span", { className: "bt-ptab-name" }, bot.name) : null
10434
10607
  ),
10435
10608
  ids.map((tabId) => {
10436
10609
  const page = pages[tabId] ?? EMPTY_PAGE;
10437
10610
  const on = view === "browser" && tabId === active;
10438
- return (0, import_react28.createElement)(
10611
+ return (0, import_react29.createElement)(
10439
10612
  "button",
10440
10613
  {
10441
10614
  key: tabId,
@@ -10456,9 +10629,9 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
10456
10629
  }
10457
10630
  }
10458
10631
  },
10459
- (0, import_react28.createElement)(Favicon, { src: page.favicon, className: "bt-ptab-fav" }),
10460
- (0, import_react28.createElement)("span", { className: "bt-ptab-title" }, tabLabel(page)),
10461
- (0, import_react28.createElement)("span", {
10632
+ (0, import_react29.createElement)(Favicon, { src: page.favicon, className: "bt-ptab-fav" }),
10633
+ (0, import_react29.createElement)("span", { className: "bt-ptab-title" }, tabLabel(page)),
10634
+ (0, import_react29.createElement)("span", {
10462
10635
  className: "bt-ptab-x",
10463
10636
  role: "button",
10464
10637
  "aria-label": t("Close tab"),
@@ -10470,20 +10643,20 @@ function PanelStrip({ bot, subject, view, ids, active, reading, onView, actions,
10470
10643
  }, "×")
10471
10644
  );
10472
10645
  }),
10473
- (0, import_react28.createElement)("span", { ref: ind, className: "bt-ptab-ind", "aria-hidden": true })
10646
+ (0, import_react29.createElement)("span", { ref: ind, className: "bt-ptab-ind", "aria-hidden": true })
10474
10647
  ),
10475
10648
  // New tab and Close stay outside the scrolling tabs, so a narrow panel keeps both.
10476
- (0, import_react28.createElement)("button", { type: "button", className: "bt-ptab-new", "aria-label": t("New tab"), title: t("New tab"), onClick: () => actions.browserNewTab(subject) }, (0, import_react28.createElement)(PlusTabIcon)),
10477
- (0, import_react28.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_react28.createElement)(CloseIcon2))
10649
+ (0, import_react29.createElement)("button", { type: "button", className: "bt-ptab-new", "aria-label": t("New tab"), title: t("New tab"), onClick: () => actions.browserNewTab(subject) }, (0, import_react29.createElement)(PlusTabIcon)),
10650
+ (0, import_react29.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_react29.createElement)(CloseIcon2))
10478
10651
  );
10479
10652
  }
10480
10653
  function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages }) {
10481
10654
  const page = useBrowserPages((pages) => pages[tabId ?? ""]) ?? EMPTY_PAGE;
10482
- const frame = (0, import_react28.useRef)(null);
10483
- const input = (0, import_react28.useRef)(null);
10484
- const [draft, setDraft] = (0, import_react28.useState)(null);
10655
+ const frame = (0, import_react29.useRef)(null);
10656
+ const input = (0, import_react29.useRef)(null);
10657
+ const [draft, setDraft] = (0, import_react29.useState)(null);
10485
10658
  const showGuest = landed && !leaving && Boolean(page.url) && !page.error;
10486
- (0, import_react28.useLayoutEffect)(() => {
10659
+ (0, import_react29.useLayoutEffect)(() => {
10487
10660
  const element = frame.current;
10488
10661
  if (!element || leaving || !tabId) {
10489
10662
  void actions.browserShow(null);
@@ -10509,7 +10682,7 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
10509
10682
  window.removeEventListener("resize", schedule);
10510
10683
  };
10511
10684
  }, [tabId, showGuest, leaving]);
10512
- (0, import_react28.useEffect)(() => {
10685
+ (0, import_react29.useEffect)(() => {
10513
10686
  setDraft(null);
10514
10687
  if (landed && !page.url) input.current?.focus();
10515
10688
  }, [tabId, landed]);
@@ -10525,27 +10698,27 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
10525
10698
  };
10526
10699
  const editing = draft !== null;
10527
10700
  const secure = /^https:/i.test(page.url);
10528
- return (0, import_react28.createElement)(
10701
+ return (0, import_react29.createElement)(
10529
10702
  "div",
10530
10703
  { className: "bt-panel-view bt-panel-browser", "aria-label": t("{name}'s browser", { name: bot.name }) },
10531
- (0, import_react28.createElement)(
10704
+ (0, import_react29.createElement)(
10532
10705
  "div",
10533
10706
  { className: "bt-browser-bar" },
10534
- (0, import_react28.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_react28.createElement)(BackIcon)),
10535
- (0, import_react28.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_react28.createElement)(ForwardIcon)),
10536
- (0, import_react28.createElement)("button", {
10707
+ (0, import_react29.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_react29.createElement)(BackIcon)),
10708
+ (0, import_react29.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_react29.createElement)(ForwardIcon)),
10709
+ (0, import_react29.createElement)("button", {
10537
10710
  type: "button",
10538
10711
  className: "bt-icon-btn",
10539
10712
  disabled: !page.url,
10540
10713
  "aria-label": page.loading ? t("Stop") : t("Reload"),
10541
10714
  title: page.loading ? t("Stop") : t("Reload"),
10542
10715
  onClick: () => actions.browserCommand(tabId, page.loading ? "stop" : "reload")
10543
- }, (0, import_react28.createElement)(page.loading ? StopIcon : ReloadIcon)),
10544
- (0, import_react28.createElement)(
10716
+ }, (0, import_react29.createElement)(page.loading ? StopIcon : ReloadIcon)),
10717
+ (0, import_react29.createElement)(
10545
10718
  "form",
10546
10719
  { className: "bt-browser-address", onSubmit: go, "data-editing": editing || void 0 },
10547
- (0, import_react28.createElement)("span", { className: "bt-browser-scheme", "aria-hidden": true }, (0, import_react28.createElement)(secure && !editing ? LockIcon2 : GlobeIcon)),
10548
- (0, import_react28.createElement)("input", {
10720
+ (0, import_react29.createElement)("span", { className: "bt-browser-scheme", "aria-hidden": true }, (0, import_react29.createElement)(secure && !editing ? LockIcon2 : GlobeIcon)),
10721
+ (0, import_react29.createElement)("input", {
10549
10722
  ref: input,
10550
10723
  type: "text",
10551
10724
  spellCheck: false,
@@ -10569,25 +10742,25 @@ function BrowserView({ bot, tabId, leaving, landed, actions, useBrowserPages })
10569
10742
  }
10570
10743
  })
10571
10744
  ),
10572
- page.loading ? (0, import_react28.createElement)("span", { className: "bt-browser-progress", "aria-hidden": true }) : null
10745
+ page.loading ? (0, import_react29.createElement)("span", { className: "bt-browser-progress", "aria-hidden": true }) : null
10573
10746
  ),
10574
- (0, import_react28.createElement)(
10747
+ (0, import_react29.createElement)(
10575
10748
  "div",
10576
10749
  { ref: frame, className: "bt-browser-frame", "data-guest": showGuest || void 0 },
10577
- page.error ? (0, import_react28.createElement)(
10750
+ page.error ? (0, import_react29.createElement)(
10578
10751
  "div",
10579
10752
  { className: "bt-browser-empty" },
10580
- (0, import_react28.createElement)("div", { className: "bt-browser-empty-title" }, t("This page didn’t load")),
10581
- (0, import_react28.createElement)("div", { className: "bt-browser-empty-text" }, t(page.error)),
10582
- page.url ? (0, import_react28.createElement)("button", { type: "button", className: "bt-browser-retry", onClick: () => {
10753
+ (0, import_react29.createElement)("div", { className: "bt-browser-empty-title" }, t("This page didn’t load")),
10754
+ (0, import_react29.createElement)("div", { className: "bt-browser-empty-text" }, t(page.error)),
10755
+ page.url ? (0, import_react29.createElement)("button", { type: "button", className: "bt-browser-retry", onClick: () => {
10583
10756
  void actions.browserNavigate(tabId, page.url);
10584
10757
  } }, t("Try again")) : null
10585
- ) : !page.url ? (0, import_react28.createElement)(
10758
+ ) : !page.url ? (0, import_react29.createElement)(
10586
10759
  "div",
10587
10760
  { className: "bt-browser-empty" },
10588
- (0, import_react28.createElement)(BotMark, { bot, size: 48, state: "idle", live: true }),
10589
- (0, import_react28.createElement)("div", { className: "bt-browser-empty-title" }, t("{name}'s browser", { name: bot.name })),
10590
- (0, import_react28.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 }))
10761
+ (0, import_react29.createElement)(BotMark, { bot, size: 48, state: "idle", live: true }),
10762
+ (0, import_react29.createElement)("div", { className: "bt-browser-empty-title" }, t("{name}'s browser", { name: bot.name })),
10763
+ (0, import_react29.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
10764
  ) : null
10592
10765
  )
10593
10766
  );
@@ -10599,41 +10772,41 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
10599
10772
  const tabState = useBrowserTabs((map) => bot ? map[bot.id] ?? EMPTY_TABS2 : EMPTY_TABS2);
10600
10773
  const active = tabState.active;
10601
10774
  const page = useBrowserPages((pages) => (active ? pages[active] : null) ?? EMPTY_PAGE);
10602
- const root = (0, import_react28.useRef)(null);
10603
- const fly = (0, import_react28.useRef)(null);
10604
- const [landed, setLanded] = (0, import_react28.useState)(false);
10605
- const [settling, setSettling] = (0, import_react28.useState)(false);
10606
- const [width, setWidth] = (0, import_react28.useState)(savedWidth);
10607
- const [viewport, setViewport] = (0, import_react28.useState)(() => window.innerWidth);
10608
- const [dragging, setDragging] = (0, import_react28.useState)(false);
10609
- const [resizing, setResizing] = (0, import_react28.useState)(false);
10775
+ const root = (0, import_react29.useRef)(null);
10776
+ const fly = (0, import_react29.useRef)(null);
10777
+ const [landed, setLanded] = (0, import_react29.useState)(false);
10778
+ const [settling, setSettling] = (0, import_react29.useState)(false);
10779
+ const [width, setWidth] = (0, import_react29.useState)(savedWidth);
10780
+ const [viewport, setViewport] = (0, import_react29.useState)(() => window.innerWidth);
10781
+ const [dragging, setDragging] = (0, import_react29.useState)(false);
10782
+ const [resizing, setResizing] = (0, import_react29.useState)(false);
10610
10783
  const instant = dragging || resizing;
10611
- const [reading, setReading] = (0, import_react28.useState)(false);
10784
+ const [reading, setReading] = (0, import_react29.useState)(false);
10612
10785
  const panelWidth = fitWidth(width ?? (view === "browser" ? DEFAULT_WIDTH : DETAILS_WIDTH), widthRange(view, viewport));
10613
- (0, import_react28.useLayoutEffect)(() => {
10786
+ (0, import_react29.useLayoutEffect)(() => {
10614
10787
  if (view === "browser" && width === null) setWidth(DEFAULT_WIDTH);
10615
10788
  }, [view, width]);
10616
- (0, import_react28.useLayoutEffect)(() => {
10789
+ (0, import_react29.useLayoutEffect)(() => {
10617
10790
  chatRoom.hold(instant);
10618
10791
  }, [instant]);
10619
- const [last, setLast] = (0, import_react28.useState)({ view, width: panelWidth });
10792
+ const [last, setLast] = (0, import_react29.useState)({ view, width: panelWidth });
10620
10793
  if (last.view !== view || last.width !== panelWidth) {
10621
10794
  setLast({ view, width: panelWidth });
10622
10795
  if (last.view !== view) setSettling(last.width !== panelWidth && !instant);
10623
10796
  }
10624
- const shownView = (0, import_react28.useRef)(view);
10625
- (0, import_react28.useLayoutEffect)(() => {
10797
+ const shownView = (0, import_react29.useRef)(view);
10798
+ (0, import_react29.useLayoutEffect)(() => {
10626
10799
  const switched = settling && shownView.current !== view;
10627
10800
  shownView.current = view;
10628
10801
  if (switched) chatRoom.hold(true);
10629
10802
  chatRoom.set(leaving ? null : panelWidth);
10630
10803
  if (switched) chatRoom.hold(false);
10631
10804
  }, [view, panelWidth, leaving]);
10632
- (0, import_react28.useEffect)(() => () => {
10805
+ (0, import_react29.useEffect)(() => () => {
10633
10806
  chatRoom.set(null);
10634
10807
  chatRoom.hold(false);
10635
10808
  }, []);
10636
- (0, import_react28.useEffect)(() => {
10809
+ (0, import_react29.useEffect)(() => {
10637
10810
  let timer;
10638
10811
  const onResize = () => {
10639
10812
  setResizing(true);
@@ -10647,28 +10820,28 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
10647
10820
  window.removeEventListener("resize", onResize);
10648
10821
  };
10649
10822
  }, []);
10650
- (0, import_react28.useEffect)(() => {
10823
+ (0, import_react29.useEffect)(() => {
10651
10824
  if (!settling) return void 0;
10652
10825
  const timer = setTimeout(() => setSettling(false), SPRING_SHAPE.duration + 40);
10653
10826
  return () => clearTimeout(timer);
10654
10827
  }, [settling, view]);
10655
- (0, import_react28.useEffect)(() => {
10828
+ (0, import_react29.useEffect)(() => {
10656
10829
  const timer = setTimeout(() => setLanded(true), 360);
10657
10830
  return () => clearTimeout(timer);
10658
10831
  }, []);
10659
- (0, import_react28.useEffect)(() => {
10832
+ (0, import_react29.useEffect)(() => {
10660
10833
  if (canBrowse) actions.browserEnsure(bot.id);
10661
10834
  }, [bot?.id, canBrowse]);
10662
- (0, import_react28.useEffect)(() => {
10835
+ (0, import_react29.useEffect)(() => {
10663
10836
  if (view === "browser" && (active === null || !tabState.ids.includes(active))) actions.setPanelView("details");
10664
10837
  }, [view, active, tabState.ids.length]);
10665
- (0, import_react28.useEffect)(() => {
10838
+ (0, import_react29.useEffect)(() => {
10666
10839
  if (view !== "browser" || leaving) void actions.browserShow?.(null);
10667
10840
  }, [view, leaving]);
10668
- (0, import_react28.useEffect)(() => () => {
10841
+ (0, import_react29.useEffect)(() => () => {
10669
10842
  void actions.browserShow?.(null);
10670
10843
  }, []);
10671
- (0, import_react28.useEffect)(() => {
10844
+ (0, import_react29.useEffect)(() => {
10672
10845
  if (!page.readAt || Date.now() - page.readAt > READ_FLASH_MS) return void 0;
10673
10846
  setReading(true);
10674
10847
  const timer = setTimeout(() => setReading(false), READ_FLASH_MS);
@@ -10705,7 +10878,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
10705
10878
  return actions[name](...args);
10706
10879
  };
10707
10880
  }
10708
- (0, import_react28.useLayoutEffect)(() => {
10881
+ (0, import_react29.useLayoutEffect)(() => {
10709
10882
  const move = fly.current;
10710
10883
  if (!move) return;
10711
10884
  fly.current = null;
@@ -10788,7 +10961,7 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
10788
10961
  for (const name of ends) grip.addEventListener(name, onEnd);
10789
10962
  };
10790
10963
  const hasStrip = canBrowse && tabState.ids.length > 0;
10791
- return (0, import_react28.createElement)(
10964
+ return (0, import_react29.createElement)(
10792
10965
  "aside",
10793
10966
  {
10794
10967
  ref: root,
@@ -10805,15 +10978,15 @@ function BotPanel({ pane, leaving, roster, actions, useSessions, useSessionStatu
10805
10978
  if (event.target === event.currentTarget) setLanded(true);
10806
10979
  }
10807
10980
  },
10808
- leaving ? null : (0, import_react28.createElement)("div", { className: "bt-panel-grip", role: "separator", "aria-orientation": "vertical", "aria-label": t("Resize panel"), onPointerDown: startResize }),
10981
+ leaving ? null : (0, import_react29.createElement)("div", { className: "bt-panel-grip", role: "separator", "aria-orientation": "vertical", "aria-label": t("Resize panel"), onPointerDown: startResize }),
10809
10982
  // Over the whole window while dragging: the cursor stays a resize cursor and the
10810
10983
  // pages underneath (the chat, the guest) see no pointer.
10811
- dragging ? (0, import_react_dom7.createPortal)((0, import_react28.createElement)("div", { className: "bt-drag-shield", "aria-hidden": true }), document.body) : null,
10812
- hasStrip ? (0, import_react28.createElement)(PanelStrip, { bot, subject: pane.id, view, ids: tabState.ids, active, reading, onView: switchView, actions: panelActions, useBrowserPages }) : null,
10813
- view === "browser" && bot ? (0, import_react28.createElement)(BrowserView, { key: `browser:${active}`, bot, tabId: active, leaving, landed: landed && !settling, actions, useBrowserPages }) : (0, import_react28.createElement)(
10984
+ dragging ? (0, import_react_dom7.createPortal)((0, import_react29.createElement)("div", { className: "bt-drag-shield", "aria-hidden": true }), document.body) : null,
10985
+ hasStrip ? (0, import_react29.createElement)(PanelStrip, { bot, subject: pane.id, view, ids: tabState.ids, active, reading, onView: switchView, actions: panelActions, useBrowserPages }) : null,
10986
+ view === "browser" && bot ? (0, import_react29.createElement)(BrowserView, { key: `browser:${active}`, bot, tabId: active, leaving, landed: landed && !settling, actions, useBrowserPages }) : (0, import_react29.createElement)(
10814
10987
  "div",
10815
10988
  { key: `details:${pane.id}:${pane.renaming ? "rename" : ""}:${pane.tab ?? ""}`, className: "bt-panel-view" },
10816
- (0, import_react28.createElement)(DetailsDrawer, {
10989
+ (0, import_react29.createElement)(DetailsDrawer, {
10817
10990
  sessionId: pane.id,
10818
10991
  startRenaming: pane.renaming,
10819
10992
  startTab: pane.tab,
@@ -10909,24 +11082,24 @@ var BROWSER_CSS = `
10909
11082
  `;
10910
11083
 
10911
11084
  // src/client/overlays.js
10912
- var import_react29 = require("react");
11085
+ var import_react30 = require("react");
10913
11086
  var import_react_dom8 = require("react-dom");
10914
11087
  var DIALOG_EXIT_MS = 160;
10915
11088
  var MENU_EXIT_MS3 = 120;
10916
- function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity, useRegistry, useBrowserPages, useBrowserTabs, actions }) {
11089
+ function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity, useRegistry, useBrowserPages, useBrowserTabs, useUpdate, actions }) {
10917
11090
  const roster = useRoster((value) => value);
10918
11091
  const ui = useUi((value) => value);
10919
11092
  useRegistry((value) => value);
10920
11093
  const current = useSessions((list) => Object.values(list.byId).find((session) => (session?.retainedBy?.mainView ?? 0) > 0)?.id ?? null);
10921
- const previous = (0, import_react29.useRef)(current);
10922
- (0, import_react29.useEffect)(() => {
11094
+ const previous = (0, import_react30.useRef)(current);
11095
+ (0, import_react30.useEffect)(() => {
10923
11096
  if (previous.current === current) return;
10924
11097
  previous.current = current;
10925
11098
  if (current) actions.followPanel?.(current);
10926
11099
  }, [current]);
10927
11100
  const open = ui.details ? { id: ui.details, view: ui.view ?? "details", renaming: ui.renaming, tab: ui.tab } : null;
10928
- const [kept, setKept] = (0, import_react29.useState)(open);
10929
- (0, import_react29.useEffect)(() => {
11101
+ const [kept, setKept] = (0, import_react30.useState)(open);
11102
+ (0, import_react30.useEffect)(() => {
10930
11103
  if (open) {
10931
11104
  setKept(open);
10932
11105
  return void 0;
@@ -10938,17 +11111,17 @@ function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useActivity
10938
11111
  const [settings, settingsLeaving] = useLinger(ui.settings, DIALOG_EXIT_MS);
10939
11112
  const [memory, memoryLeaving] = useLinger(ui.memory && roster.byId[ui.memory] ? ui.memory : null, DIALOG_EXIT_MS);
10940
11113
  const [menu, menuLeaving] = useLinger(ui.menu, MENU_EXIT_MS3);
10941
- return (0, import_react29.createElement)(
10942
- import_react29.Fragment,
11114
+ return (0, import_react30.createElement)(
11115
+ import_react30.Fragment,
10943
11116
  null,
10944
- pane ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(BotPanel, { pane, leaving: open === null, roster, actions, useSessions, useSessionStatus, useActivity, useBrowserTabs, useBrowserPages }), document.body) : null,
10945
- ui.newChat ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(NewChat, { key: ui.newChat, mode: ui.newChat, roster, actions }), document.body) : null,
10946
- ui.palette ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(CommandPalette, { key: ui.palette.token, start: ui.palette.query, token: ui.palette.token, roster, actions, useSessions }), document.body) : null,
10947
- ui.exchange ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(ExchangeDialog, { pair: ui.exchange, roster, actions }), document.body) : null,
10948
- ui.voice ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(VoiceMode, { key: ui.voice, sessionId: ui.voice, roster, actions, useSessions, useSessionStatus }), document.body) : null,
10949
- menu ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(ContextMenu, { key: `${menu.kind ?? ""}:${menu.id}:${menu.x}:${menu.y}`, menu, roster, actions, leaving: menuLeaving }), document.body) : null,
10950
- settings ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(SettingsDialog, { page: settings.page, id: settings.id, roster, actions, leaving: settingsLeaving }), document.body) : null,
10951
- memory && roster.byId[memory] ? (0, import_react_dom8.createPortal)((0, import_react29.createElement)(MemoryDialog, { key: memory, bot: roster.byId[memory], actions, leaving: memoryLeaving }), document.body) : null
11117
+ pane ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(BotPanel, { pane, leaving: open === null, roster, actions, useSessions, useSessionStatus, useActivity, useBrowserTabs, useBrowserPages }), document.body) : null,
11118
+ ui.newChat ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(NewChat, { key: ui.newChat, mode: ui.newChat, roster, actions }), document.body) : null,
11119
+ ui.palette ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(CommandPalette, { key: ui.palette.token, start: ui.palette.query, token: ui.palette.token, roster, actions, useSessions }), document.body) : null,
11120
+ ui.exchange ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(ExchangeDialog, { pair: ui.exchange, roster, actions }), document.body) : null,
11121
+ ui.voice ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(VoiceMode, { key: ui.voice, sessionId: ui.voice, roster, actions, useSessions, useSessionStatus }), document.body) : null,
11122
+ menu ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(ContextMenu, { key: `${menu.kind ?? ""}:${menu.id}:${menu.x}:${menu.y}`, menu, roster, actions, leaving: menuLeaving }), document.body) : null,
11123
+ settings ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(SettingsDialog, { page: settings.page, id: settings.id, roster, actions, useUpdate, leaving: settingsLeaving }), document.body) : null,
11124
+ memory && roster.byId[memory] ? (0, import_react_dom8.createPortal)((0, import_react30.createElement)(MemoryDialog, { key: memory, bot: roster.byId[memory], actions, leaving: memoryLeaving }), document.body) : null
10952
11125
  );
10953
11126
  }
10954
11127
 
@@ -11949,7 +12122,7 @@ function apply(ctx) {
11949
12122
  if (stopped) return;
11950
12123
  const value = await call("update-check", { fresh: false }).catch(() => null);
11951
12124
  if (stopped) return;
11952
- if (value) update.set(value);
12125
+ if (value) update.set((previous) => ({ ...value, app: previous.app }));
11953
12126
  timer = setTimeout(loop, value?.phase === "installing" || value?.dsh?.phase === "installing" ? 2e3 : 60 * 6e4);
11954
12127
  };
11955
12128
  wakeUpdate = loop;
@@ -11961,6 +12134,22 @@ function apply(ctx) {
11961
12134
  };
11962
12135
  };
11963
12136
  }, "ds-bot: update check");
12137
+ const appUpdates = window.dshDesktop?.protocolVersion === 1 ? window.dshDesktop.updates ?? null : null;
12138
+ if (appUpdates?.status && appUpdates.subscribe) {
12139
+ ctx.effect(() => {
12140
+ let stopped = false;
12141
+ const take = (app) => {
12142
+ if (!stopped && app) update.set((previous) => ({ ...previous, app }));
12143
+ };
12144
+ void appUpdates.status().then(take, () => {
12145
+ });
12146
+ const unsubscribe = appUpdates.subscribe(take);
12147
+ return () => {
12148
+ stopped = true;
12149
+ unsubscribe?.();
12150
+ };
12151
+ }, "ds-bot: app update status");
12152
+ }
11964
12153
  const bridge = window.dshDesktop?.protocolVersion === 1 ? window.dshDesktop.browser ?? null : null;
11965
12154
  const browsers = bridge ? createBotBrowsers({ bridge, createSource }) : null;
11966
12155
  if (browsers) {
@@ -12426,7 +12615,17 @@ function apply(ctx) {
12426
12615
  update.set((previous) => ({ ...previous, dsh }));
12427
12616
  wakeUpdate();
12428
12617
  };
12429
- actions.openAppUpdates = () => window.dshDesktop?.updates?.open?.();
12618
+ actions.openAppUpdates = async (verified) => {
12619
+ const app = await appUpdates?.status?.();
12620
+ if (app) update.set((previous) => ({ ...previous, app }));
12621
+ if (sameVersion(app?.version, verified)) await appUpdates.open();
12622
+ };
12623
+ actions.checkUpdate = async () => {
12624
+ const value = await call("update-check", { fresh: true });
12625
+ update.set((previous) => ({ ...value, app: previous.app }));
12626
+ wakeUpdate();
12627
+ };
12628
+ actions.repoStars = () => call("repo-stars");
12430
12629
  actions.noteTurnTime = (sessionId, turn, key, time) => {
12431
12630
  if (sessionId === void 0 || !Number.isFinite(turn) || !Number.isFinite(time)) return;
12432
12631
  turnClock.set((state) => {
@@ -12488,6 +12687,7 @@ function apply(ctx) {
12488
12687
  installCompiledStyles(USAGE_CSS, "usage"),
12489
12688
  installCompiledStyles(SCHEDULE_CSS, "schedules"),
12490
12689
  installCompiledStyles(FEEDBACK_CSS, "feedback"),
12690
+ installCompiledStyles(ABOUT_CSS, "about"),
12491
12691
  browsers ? installCompiledStyles(BROWSER_CSS, "browser") : null,
12492
12692
  installTheme(),
12493
12693
  shadow("sidebar.workspaces", { priority: -10, inject: face }, TeamSidebar),