@michengai/dsh-btw 0.1.16 → 0.1.18

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [0.1.18] - 2026-10-10
6
+
7
+ - Side questions start again when dsh-tui is installed. The plugin no longer fails to activate, so `/btw` stays available on hosts that use the command registry. Side questions still only answer and do not run tools. A side question that is still cleaning up after the plugin is removed cannot suddenly run tools.
8
+
9
+ ## [0.1.17] - 2026-10-07
10
+
11
+ - Side-question answers use the host Markdown renderer, so `$...$`, `$$...$$`, and TeX delimiters render as formulas, matching the main conversation. Hosts without that renderer still show the original text. Copy keeps the raw Markdown, and answers do not load remote images.
12
+ - A code block’s own copy button says “Copy code” instead of reusing the whole-answer label.
13
+
5
14
  ## [0.1.16] - 2026-10-06
6
15
 
7
16
  - The installed-plugin list shows “BTW” and a localized description instead of the package name. Titles and descriptions come from `meta.title` and `meta.description` in `locale/zh.json` and `locale/en.json`.
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## 未发布
4
4
 
5
+ ## [0.1.18] - 2026-10-10
6
+
7
+ - 安装了 dsh-tui 时,旁问插件可以正常启动,不再因为激活失败而缺少 `/btw`。旁问仍然只回答、不执行工具。插件卸载后,尚未清理完的旁问也不会突然获得工具。
8
+
9
+ ## [0.1.17] - 2026-10-07
10
+
11
+ - 旁问答案使用宿主 Markdown 渲染,`$...$`、`$$...$$` 和 TeX 分隔符与主会话一样显示为公式。宿主没有该渲染器时仍显示原文本。复制仍保留原始 Markdown,答案不加载远程图片。
12
+ - 代码块自己的复制按钮改为「复制代码」,不再和整段回答的复制按钮共用文案。
13
+
5
14
  ## [0.1.16] - 2026-10-06
6
15
 
7
16
  - 已安装插件列表显示「BTW」和中文简介,不再只用包名。显示名来自 `locale/zh.json` 和 `locale/en.json` 的 `meta.title`、`meta.description`。
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  - **Context-aware questions**: explain concepts, recap conclusions, or ask a quick question about the current conversation.
24
24
  - **Answers only**: no reading new files, browsing the web, running commands, or editing code.
25
- - **Independent answer bubbles**: render Markdown, copy answers, collapse, expand, and close individual bubbles. Multiple answers can stay visible.
25
+ - **Independent answer bubbles**: render Markdown and formulas, copy answers, collapse, expand, and close individual bubbles. Multiple answers can stay visible.
26
26
  - **Keep the main task going**: side questions are answered separately and do not add answers to the main conversation.
27
27
  - **Cancel anytime**: close a bubble while it is answering to cancel that question.
28
28
  - **Themes and languages**: follow DSH light and dark themes and switch between Chinese and English UI.
package/README.zh-CN.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  - **上下文旁问**:根据当前会话内容解释概念、回顾结论或回答临时问题。
24
24
  - **只回答,不执行**:不会读取新文件、联网、运行命令或修改代码。
25
- - **独立答案气泡**:支持 Markdown、复制、折叠、展开和关闭,可同时查看多条旁问。
25
+ - **独立答案气泡**:支持 Markdown、公式、复制、折叠、展开和关闭,可同时查看多条旁问。
26
26
  - **不打断主任务**:旁问单独作答,答案不会写入主会话。
27
27
  - **随时取消**:关闭正在回答的气泡即可取消该旁问。
28
28
  - **主题与国际化**:跟随 DSH 浅色、深色主题,界面支持中文和英文切换。
package/lib/client.js CHANGED
@@ -367,7 +367,7 @@ __export(index_exports, {
367
367
  inject: () => inject
368
368
  });
369
369
  module.exports = __toCommonJS(index_exports);
370
- var import_react6 = __toESM(require("react"), 1);
370
+ var import_react7 = __toESM(require("react"), 1);
371
371
 
372
372
  // node_modules/lucide-react/dist/esm/createLucideIcon.js
373
373
  var import_react2 = require("react");
@@ -494,8 +494,11 @@ var zh = {
494
494
  "error.reference": `\u8BF7\u9009\u62E9 1\u2013${MAX_REFERENCE_LENGTH} \u4E2A\u5B57\u7B26\u7684\u6B63\u6587\uFF08\u542B\u7A7A\u767D\uFF0C\u90E8\u5206\u7B26\u53F7\u8BA1\u4E3A\u591A\u4E2A\u5B57\u7B26\uFF09\u3002`,
495
495
  "bubble.label": "\u65C1\u95EE\u56DE\u7B54",
496
496
  "bubble.image": "\u56FE\u7247",
497
+ "bubble.footnotes": "\u811A\u6CE8",
497
498
  "action.copy": "\u590D\u5236\u56DE\u7B54",
498
499
  "action.copied": "\u5DF2\u590D\u5236",
500
+ "action.copyCode": "\u590D\u5236\u4EE3\u7801",
501
+ "action.copiedCode": "\u5DF2\u590D\u5236\u4EE3\u7801",
499
502
  "action.expand": "\u5C55\u5F00\u56DE\u7B54",
500
503
  "action.collapse": "\u6298\u53E0\u56DE\u7B54",
501
504
  "action.close": "\u5173\u95ED\u65C1\u95EE",
@@ -543,8 +546,11 @@ var en = {
543
546
  "error.reference": `Select 1\u2013${MAX_REFERENCE_LENGTH.toLocaleString("en-US")} characters of message text, including whitespace (some symbols count as multiple characters).`,
544
547
  "bubble.label": "Side question answer",
545
548
  "bubble.image": "Image",
549
+ "bubble.footnotes": "Footnotes",
546
550
  "action.copy": "Copy answer",
547
551
  "action.copied": "Copied",
552
+ "action.copyCode": "Copy code",
553
+ "action.copiedCode": "Copied code",
548
554
  "action.expand": "Expand answer",
549
555
  "action.collapse": "Collapse answer",
550
556
  "action.close": "Close side question",
@@ -680,6 +686,9 @@ var BubbleStore = class {
680
686
  };
681
687
 
682
688
  // src/client/BubbleView.tsx
689
+ var import_react5 = __toESM(require("react"), 1);
690
+
691
+ // src/client/answer-markdown.tsx
683
692
  var import_react4 = __toESM(require("react"), 1);
684
693
 
685
694
  // node_modules/devlop/lib/default.js
@@ -13367,12 +13376,91 @@ function remarkGfm(options) {
13367
13376
  toMarkdownExtensions.push(gfmToMarkdown(settings));
13368
13377
  }
13369
13378
 
13379
+ // src/client/host-markdown.ts
13380
+ var cached;
13381
+ function isHostMarkdown(value) {
13382
+ if (typeof value === "function") return true;
13383
+ return typeof value === "object" && value !== null && "$$typeof" in value;
13384
+ }
13385
+ function loadHostMarkdownText() {
13386
+ if (cached !== void 0) return cached;
13387
+ try {
13388
+ const loaded = require("@deepseek-ai/dsh-client-ui-primitives");
13389
+ cached = isHostMarkdown(loaded?.MarkdownText) ? loaded.MarkdownText : null;
13390
+ } catch {
13391
+ cached = null;
13392
+ }
13393
+ return cached;
13394
+ }
13395
+
13396
+ // src/client/answer-markdown.tsx
13397
+ var IMAGE = /^!\[([^\]\n]*)\](?:\([^)\n]*\)|\[[^\]\n]*\])/;
13398
+ function neutralizeMarkdownImages(source, emptyAlt = "") {
13399
+ let out = "";
13400
+ let index2 = 0;
13401
+ while (index2 < source.length) {
13402
+ const fence = source.startsWith("```", index2) ? "```" : source.startsWith("~~~", index2) ? "~~~" : "";
13403
+ if (fence) {
13404
+ const end = source.indexOf(fence, index2 + fence.length);
13405
+ if (end === -1) return out + source.slice(index2);
13406
+ out += source.slice(index2, end + fence.length);
13407
+ index2 = end + fence.length;
13408
+ continue;
13409
+ }
13410
+ if (source[index2] === "`") {
13411
+ let width = 0;
13412
+ while (source[index2 + width] === "`") width += 1;
13413
+ const fence2 = "`".repeat(width);
13414
+ const end = source.indexOf(fence2, index2 + width);
13415
+ if (end === -1) return out + source.slice(index2);
13416
+ out += source.slice(index2, end + width);
13417
+ index2 = end + width;
13418
+ continue;
13419
+ }
13420
+ const image3 = IMAGE.exec(source.slice(index2));
13421
+ if (image3) {
13422
+ out += image3[1] || emptyAlt;
13423
+ index2 += image3[0].length;
13424
+ continue;
13425
+ }
13426
+ out += source[index2];
13427
+ index2 += 1;
13428
+ }
13429
+ return out;
13430
+ }
13431
+ function FallbackMarkdown({ text: text7 }) {
13432
+ return /* @__PURE__ */ import_react4.default.createElement(Markdown, { remarkPlugins: [remarkGfm], skipHtml: true, components: {
13433
+ a: (props) => /* @__PURE__ */ import_react4.default.createElement("a", { href: props.href, target: "_blank", rel: "noreferrer noopener" }, props.children),
13434
+ img: () => null
13435
+ } }, text7);
13436
+ }
13437
+ var AnswerBoundary = class extends import_react4.default.Component {
13438
+ state = { failed: false };
13439
+ static getDerivedStateFromError() {
13440
+ return { failed: true };
13441
+ }
13442
+ render() {
13443
+ return this.state.failed ? this.props.fallback : this.props.children;
13444
+ }
13445
+ };
13446
+ function AnswerMarkdown({ text: text7, labels, imageLabel }) {
13447
+ const safe = neutralizeMarkdownImages(text7, imageLabel);
13448
+ const fallback = /* @__PURE__ */ import_react4.default.createElement(FallbackMarkdown, { text: safe });
13449
+ const Host = loadHostMarkdownText();
13450
+ if (!Host) return fallback;
13451
+ return /* @__PURE__ */ import_react4.default.createElement(AnswerBoundary, { key: safe, fallback }, /* @__PURE__ */ import_react4.default.createElement(Host, { text: safe, streaming: false, labels, variant: "compact" }));
13452
+ }
13453
+
13370
13454
  // src/client/BubbleView.tsx
13371
13455
  function BubbleView({ item, close, t }) {
13372
- const [collapsed, setCollapsed] = (0, import_react4.useState)(false);
13373
- const [copied, setCopied] = (0, import_react4.useState)(false);
13374
- const [copyError, setCopyError] = (0, import_react4.useState)(false);
13456
+ const [collapsed, setCollapsed] = (0, import_react5.useState)(false);
13457
+ const [copied, setCopied] = (0, import_react5.useState)(false);
13458
+ const [copyError, setCopyError] = (0, import_react5.useState)(false);
13375
13459
  const busy = item.phase === "answering" || item.phase === "closing";
13460
+ const labels = (0, import_react5.useMemo)(() => ({
13461
+ code: { copyLabel: t("action.copyCode"), copiedLabel: t("action.copiedCode") },
13462
+ footnotes: t("bubble.footnotes")
13463
+ }), [t]);
13376
13464
  const copy = async () => {
13377
13465
  try {
13378
13466
  await navigator.clipboard.writeText(item.answer);
@@ -13382,18 +13470,15 @@ function BubbleView({ item, close, t }) {
13382
13470
  setCopyError(true);
13383
13471
  }
13384
13472
  };
13385
- return /* @__PURE__ */ import_react4.default.createElement("article", { className: "btw-bubble", "data-phase": item.phase, "aria-label": t("bubble.label") }, /* @__PURE__ */ import_react4.default.createElement("header", { className: "btw-header" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "btw-symbol" }, /* @__PURE__ */ import_react4.default.createElement(MessageCircle, { size: 16 })), /* @__PURE__ */ import_react4.default.createElement("span", { className: "btw-question" }, item.question), /* @__PURE__ */ import_react4.default.createElement("div", { className: "btw-actions" }, item.answer && /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", title: t(copied ? "action.copied" : "action.copy"), "aria-label": t("action.copy"), onClick: () => {
13473
+ return /* @__PURE__ */ import_react5.default.createElement("article", { className: "btw-bubble", "data-phase": item.phase, "aria-label": t("bubble.label") }, /* @__PURE__ */ import_react5.default.createElement("header", { className: "btw-header" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "btw-symbol" }, /* @__PURE__ */ import_react5.default.createElement(MessageCircle, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("span", { className: "btw-question" }, item.question), /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-actions" }, item.answer && /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(copied ? "action.copied" : "action.copy"), "aria-label": t("action.copy"), onClick: () => {
13386
13474
  void copy();
13387
- } }, copied ? /* @__PURE__ */ import_react4.default.createElement(Check, { size: 16 }) : /* @__PURE__ */ import_react4.default.createElement(Copy, { size: 16 })), /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", title: t(collapsed ? "action.expand" : "action.collapse"), "aria-label": t(collapsed ? "action.expand" : "action.collapse"), "aria-expanded": !collapsed, onClick: () => setCollapsed(!collapsed) }, collapsed ? /* @__PURE__ */ import_react4.default.createElement(ChevronUp, { size: 16 }) : /* @__PURE__ */ import_react4.default.createElement(ChevronDown, { size: 16 })), /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", title: t(item.closeFailed ? "action.retryClose" : "action.close"), "aria-label": t("action.close"), disabled: item.phase === "closing", onClick: close }, /* @__PURE__ */ import_react4.default.createElement(X, { size: 16 })))), !collapsed && item.reference && /* @__PURE__ */ import_react4.default.createElement("details", { className: "btw-reference" }, /* @__PURE__ */ import_react4.default.createElement("summary", null, t("selection.reference")), /* @__PURE__ */ import_react4.default.createElement("blockquote", null, item.reference)), busy && /* @__PURE__ */ import_react4.default.createElement("div", { className: "btw-status", role: "status" }, /* @__PURE__ */ import_react4.default.createElement(LoaderCircle, { size: 14, className: "btw-spinner" }), t(item.phase === "closing" ? "status.closing" : "status.answering")), !collapsed && item.answer && /* @__PURE__ */ import_react4.default.createElement("div", { className: "btw-answer" }, /* @__PURE__ */ import_react4.default.createElement(Markdown, { remarkPlugins: [remarkGfm], skipHtml: true, components: {
13388
- a: (props) => /* @__PURE__ */ import_react4.default.createElement("a", { href: props.href, target: "_blank", rel: "noreferrer noopener" }, props.children),
13389
- img: (props) => /* @__PURE__ */ import_react4.default.createElement("span", null, props.alt || t("bubble.image"))
13390
- } }, item.answer)), item.error && /* @__PURE__ */ import_react4.default.createElement("p", { className: "btw-error", role: "alert" }, item.closeFailed ? t("error.close", { detail: item.error }) : item.error), copyError && /* @__PURE__ */ import_react4.default.createElement("p", { className: "btw-error", role: "alert" }, t("error.copy")));
13475
+ } }, copied ? /* @__PURE__ */ import_react5.default.createElement(Check, { size: 16 }) : /* @__PURE__ */ import_react5.default.createElement(Copy, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(collapsed ? "action.expand" : "action.collapse"), "aria-label": t(collapsed ? "action.expand" : "action.collapse"), "aria-expanded": !collapsed, onClick: () => setCollapsed(!collapsed) }, collapsed ? /* @__PURE__ */ import_react5.default.createElement(ChevronUp, { size: 16 }) : /* @__PURE__ */ import_react5.default.createElement(ChevronDown, { size: 16 })), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", title: t(item.closeFailed ? "action.retryClose" : "action.close"), "aria-label": t("action.close"), disabled: item.phase === "closing", onClick: close }, /* @__PURE__ */ import_react5.default.createElement(X, { size: 16 })))), !collapsed && item.reference && /* @__PURE__ */ import_react5.default.createElement("details", { className: "btw-reference" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, t("selection.reference")), /* @__PURE__ */ import_react5.default.createElement("blockquote", null, item.reference)), busy && /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-status", role: "status" }, /* @__PURE__ */ import_react5.default.createElement(LoaderCircle, { size: 14, className: "btw-spinner" }), t(item.phase === "closing" ? "status.closing" : "status.answering")), !collapsed && item.answer && /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-answer" }, /* @__PURE__ */ import_react5.default.createElement(AnswerMarkdown, { text: item.answer, labels, imageLabel: t("bubble.image") })), item.error && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, item.closeFailed ? t("error.close", { detail: item.error }) : item.error), copyError && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, t("error.copy")));
13391
13476
  }
13392
13477
  function BubbleDock({ store, sessionId, t }) {
13393
- const snapshot = (0, import_react4.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
13478
+ const snapshot = (0, import_react5.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
13394
13479
  const items = snapshot.filter((item) => item.sessionId === sessionId);
13395
13480
  if (!items.length) return null;
13396
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "btw-dock" }, items.map((item) => /* @__PURE__ */ import_react4.default.createElement(BubbleView, { key: item.id, item, t, close: () => {
13481
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-dock" }, items.map((item) => /* @__PURE__ */ import_react5.default.createElement(BubbleView, { key: item.id, item, t, close: () => {
13397
13482
  void store.close(item.id);
13398
13483
  } })));
13399
13484
  }
@@ -13414,7 +13499,7 @@ function addQuoteToComposer(input, reference, t) {
13414
13499
  }
13415
13500
 
13416
13501
  // src/client/SelectionAsk.tsx
13417
- var import_react5 = __toESM(require("react"), 1);
13502
+ var import_react6 = __toESM(require("react"), 1);
13418
13503
 
13419
13504
  // src/client/selection.ts
13420
13505
  function captureSelection(scope, target) {
@@ -13434,21 +13519,21 @@ function captureSelection(scope, target) {
13434
13519
 
13435
13520
  // src/client/SelectionAsk.tsx
13436
13521
  function SelectionAsk({ store, sessionId, t, addToConversation }) {
13437
- const anchor = (0, import_react5.useRef)(null);
13438
- const dialog = (0, import_react5.useRef)(null);
13439
- const previousFocus = (0, import_react5.useRef)(null);
13440
- const mounted = (0, import_react5.useRef)(false);
13441
- const submitting = (0, import_react5.useRef)(false);
13442
- const [draft, setDraft] = (0, import_react5.useState)();
13443
- const [question, setQuestion] = (0, import_react5.useState)("");
13444
- const [error, setError] = (0, import_react5.useState)("");
13445
- const [busy, setBusy] = (0, import_react5.useState)(false);
13522
+ const anchor = (0, import_react6.useRef)(null);
13523
+ const dialog = (0, import_react6.useRef)(null);
13524
+ const previousFocus = (0, import_react6.useRef)(null);
13525
+ const mounted = (0, import_react6.useRef)(false);
13526
+ const submitting = (0, import_react6.useRef)(false);
13527
+ const [draft, setDraft] = (0, import_react6.useState)();
13528
+ const [question, setQuestion] = (0, import_react6.useState)("");
13529
+ const [error, setError] = (0, import_react6.useState)("");
13530
+ const [busy, setBusy] = (0, import_react6.useState)(false);
13446
13531
  const dismiss = () => {
13447
13532
  if (submitting.current) return;
13448
13533
  setDraft(void 0);
13449
13534
  previousFocus.current?.focus({ preventScroll: true });
13450
13535
  };
13451
- (0, import_react5.useEffect)(() => {
13536
+ (0, import_react6.useEffect)(() => {
13452
13537
  mounted.current = true;
13453
13538
  const scope = anchor.current?.closest("[data-conversation-scroll]");
13454
13539
  if (!scope) return () => {
@@ -13474,7 +13559,7 @@ function SelectionAsk({ store, sessionId, t, addToConversation }) {
13474
13559
  scope.removeEventListener("keyup", open);
13475
13560
  };
13476
13561
  }, [sessionId]);
13477
- (0, import_react5.useLayoutEffect)(() => {
13562
+ (0, import_react6.useLayoutEffect)(() => {
13478
13563
  const element3 = dialog.current;
13479
13564
  if (!draft || !element3) return;
13480
13565
  if (!draft.form) {
@@ -13497,7 +13582,7 @@ function SelectionAsk({ store, sessionId, t, addToConversation }) {
13497
13582
  if (draft.form) element3.querySelector("textarea")?.focus();
13498
13583
  else if (draft.keyboard) element3.querySelector("button")?.focus({ preventScroll: true });
13499
13584
  }, [draft]);
13500
- (0, import_react5.useLayoutEffect)(() => {
13585
+ (0, import_react6.useLayoutEffect)(() => {
13501
13586
  const element3 = dialog.current;
13502
13587
  return () => {
13503
13588
  if (!element3) return;
@@ -13505,7 +13590,7 @@ function SelectionAsk({ store, sessionId, t, addToConversation }) {
13505
13590
  if (element3 instanceof HTMLDialogElement && element3.open) element3.close();
13506
13591
  };
13507
13592
  }, [draft?.form]);
13508
- (0, import_react5.useEffect)(() => {
13593
+ (0, import_react6.useEffect)(() => {
13509
13594
  if (!draft) return;
13510
13595
  const scope = anchor.current?.closest("[data-conversation-scroll]");
13511
13596
  const hide = () => setDraft(void 0);
@@ -13578,7 +13663,7 @@ function SelectionAsk({ store, sessionId, t, addToConversation }) {
13578
13663
  }
13579
13664
  };
13580
13665
  const Surface = draft?.form ? "dialog" : "div";
13581
- return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("span", { ref: anchor, hidden: true }), draft && /* @__PURE__ */ import_react5.default.createElement(
13666
+ return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement("span", { ref: anchor, hidden: true }), draft && /* @__PURE__ */ import_react6.default.createElement(
13582
13667
  Surface,
13583
13668
  {
13584
13669
  ref: (element3) => {
@@ -13595,10 +13680,10 @@ function SelectionAsk({ store, sessionId, t, addToConversation }) {
13595
13680
  if (event.target === event.currentTarget && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom)) dismiss();
13596
13681
  }
13597
13682
  },
13598
- draft.form ? /* @__PURE__ */ import_react5.default.createElement("form", { onSubmit: (event) => {
13683
+ draft.form ? /* @__PURE__ */ import_react6.default.createElement("form", { onSubmit: (event) => {
13599
13684
  event.preventDefault();
13600
13685
  void send(question);
13601
- } }, /* @__PURE__ */ import_react5.default.createElement("h3", null, t("selection.ask")), /* @__PURE__ */ import_react5.default.createElement("details", { className: "btw-reference" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, t("selection.reference"), " \xB7 ", draft.reference.length, /* @__PURE__ */ import_react5.default.createElement("span", { className: "btw-reference-preview" }, draft.reference.slice(0, 120), draft.reference.length > 120 ? "\u2026" : "")), /* @__PURE__ */ import_react5.default.createElement("blockquote", null, draft.reference)), /* @__PURE__ */ import_react5.default.createElement("label", null, /* @__PURE__ */ import_react5.default.createElement("span", null, t("selection.question")), /* @__PURE__ */ import_react5.default.createElement(
13686
+ } }, /* @__PURE__ */ import_react6.default.createElement("h3", null, t("selection.ask")), /* @__PURE__ */ import_react6.default.createElement("details", { className: "btw-reference" }, /* @__PURE__ */ import_react6.default.createElement("summary", null, t("selection.reference"), " \xB7 ", draft.reference.length, /* @__PURE__ */ import_react6.default.createElement("span", { className: "btw-reference-preview" }, draft.reference.slice(0, 120), draft.reference.length > 120 ? "\u2026" : "")), /* @__PURE__ */ import_react6.default.createElement("blockquote", null, draft.reference)), /* @__PURE__ */ import_react6.default.createElement("label", null, /* @__PURE__ */ import_react6.default.createElement("span", null, t("selection.question")), /* @__PURE__ */ import_react6.default.createElement(
13602
13687
  "textarea",
13603
13688
  {
13604
13689
  value: question,
@@ -13612,11 +13697,11 @@ function SelectionAsk({ store, sessionId, t, addToConversation }) {
13612
13697
  }
13613
13698
  }
13614
13699
  }
13615
- )), /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-selection-hint" }, t("selection.context")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "btw-selection-actions" }, /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", disabled: busy, onClick: dismiss }, t("selection.cancel")), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", disabled: busy || draft.reference.length > MAX_REFERENCE_LENGTH, onClick: () => {
13700
+ )), /* @__PURE__ */ import_react6.default.createElement("p", { className: "btw-selection-hint" }, t("selection.context")), /* @__PURE__ */ import_react6.default.createElement("div", { className: "btw-selection-actions" }, /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", disabled: busy, onClick: dismiss }, t("selection.cancel")), /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", disabled: busy || draft.reference.length > MAX_REFERENCE_LENGTH, onClick: () => {
13616
13701
  void send(t("selection.explain"));
13617
- } }, t("selection.explain")), /* @__PURE__ */ import_react5.default.createElement("button", { type: "submit", disabled: busy || !question.trim() || draft.reference.length > MAX_REFERENCE_LENGTH }, t("selection.send")))) : /* @__PURE__ */ import_react5.default.createElement("div", { role: "toolbar", "aria-label": t("selection.ask"), onMouseDown: (event) => event.preventDefault() }, /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", onClick: add }, t("selection.add")), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", onClick: () => setDraft({ ...draft, form: true }) }, t("selection.btw"))),
13618
- draft.form && draft.reference.length > MAX_REFERENCE_LENGTH && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, t("error.reference")),
13619
- error && /* @__PURE__ */ import_react5.default.createElement("p", { className: "btw-error", role: "alert" }, error)
13702
+ } }, t("selection.explain")), /* @__PURE__ */ import_react6.default.createElement("button", { type: "submit", disabled: busy || !question.trim() || draft.reference.length > MAX_REFERENCE_LENGTH }, t("selection.send")))) : /* @__PURE__ */ import_react6.default.createElement("div", { role: "toolbar", "aria-label": t("selection.ask"), onMouseDown: (event) => event.preventDefault() }, /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", onClick: add }, t("selection.add")), /* @__PURE__ */ import_react6.default.createElement("button", { type: "button", onClick: () => setDraft({ ...draft, form: true }) }, t("selection.btw"))),
13703
+ draft.form && draft.reference.length > MAX_REFERENCE_LENGTH && /* @__PURE__ */ import_react6.default.createElement("p", { className: "btw-error", role: "alert" }, t("error.reference")),
13704
+ error && /* @__PURE__ */ import_react6.default.createElement("p", { className: "btw-error", role: "alert" }, error)
13620
13705
  ));
13621
13706
  }
13622
13707
 
@@ -13650,7 +13735,7 @@ var CSS = `
13650
13735
  .btw-answer{padding:0 14px 14px;max-height:300px;overflow:auto;overflow-wrap:anywhere;font-size:13px;line-height:1.7}
13651
13736
  .btw-answer>*:first-child{margin-top:0}.btw-answer>*:last-child{margin-bottom:0}
13652
13737
  .btw-answer p{margin:8px 0}.btw-answer h1,.btw-answer h2,.btw-answer h3{font-size:15px;line-height:1.5;margin:12px 0 6px;font-weight:600}
13653
- .btw-answer pre{max-width:100%;overflow:auto;background:var(--dsw-alias-markdown-code-block);padding:10px;border-radius:8px;white-space:pre;font-size:12px}
13738
+ .btw-answer pre{max-width:100%;overflow:auto;background:var(--dsw-alias-markdown-code-block);padding:10px;border-radius:8px;white-space:pre;font-size:12px}.btw-answer .katex-display{max-width:100%;overflow:auto}
13654
13739
  .btw-answer code{font-family:Consolas,monospace;font-size:12px}.btw-answer :not(pre)>code{background:var(--dsw-alias-markdown-code-block);border-radius:4px;padding:1px 3px}.btw-answer a{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}
13655
13740
  .btw-answer table{display:block;max-width:100%;overflow:auto;border-collapse:collapse}.btw-answer th,.btw-answer td{border:1px solid var(--dsw-alias-border-l2);padding:5px 9px}
13656
13741
  .btw-answer ul,.btw-answer ol{padding-left:22px}.btw-answer blockquote{margin:8px 0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary)}
@@ -13662,7 +13747,7 @@ var CSS = `
13662
13747
  // src/client/index.tsx
13663
13748
  var inject = ["slots", "remote", "remote.commands", "locale", "conversation", "sessions"];
13664
13749
  function BtwCommandIcon({ size = 16, className }) {
13665
- return /* @__PURE__ */ import_react6.default.createElement(MessageCircle, { size, className });
13750
+ return /* @__PURE__ */ import_react7.default.createElement(MessageCircle, { size, className });
13666
13751
  }
13667
13752
  function sessionIdOf(session) {
13668
13753
  if (typeof session === "string") return session;
@@ -13781,11 +13866,11 @@ function apply(ctx) {
13781
13866
  });
13782
13867
  ctx.effect(() => () => store.dispose());
13783
13868
  function Dock(props) {
13784
- return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement(SelectionAsk, { key: props.session.sessionId, store, sessionId: props.session.sessionId, t: props.t, addToConversation: (reference) => {
13869
+ return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(SelectionAsk, { key: props.session.sessionId, store, sessionId: props.session.sessionId, t: props.t, addToConversation: (reference) => {
13785
13870
  const session = ctx.get("sessions").scope(props.session.sessionId);
13786
13871
  if (!session) throw new Error(props.t("error.backend"));
13787
13872
  addQuoteToComposer(ctx.conversation.input.for(session), reference, props.t);
13788
- } }), /* @__PURE__ */ import_react6.default.createElement(BubbleDock, { store, sessionId: props.session.sessionId, t: props.t }));
13873
+ } }), /* @__PURE__ */ import_react7.default.createElement(BubbleDock, { store, sessionId: props.session.sessionId, t: props.t }));
13789
13874
  }
13790
13875
  slots.inject("conversation.input.dock", () => slots.register({ name: "conversation.input.dock", id: "michengai-btw", order: -50, locale: NS }, Dock));
13791
13876
  slots.inject("conversation.chat.commandview", () => {
package/lib/index.js CHANGED
@@ -18,8 +18,11 @@ var zh = {
18
18
  "error.reference": `\u8BF7\u9009\u62E9 1\u2013${MAX_REFERENCE_LENGTH} \u4E2A\u5B57\u7B26\u7684\u6B63\u6587\uFF08\u542B\u7A7A\u767D\uFF0C\u90E8\u5206\u7B26\u53F7\u8BA1\u4E3A\u591A\u4E2A\u5B57\u7B26\uFF09\u3002`,
19
19
  "bubble.label": "\u65C1\u95EE\u56DE\u7B54",
20
20
  "bubble.image": "\u56FE\u7247",
21
+ "bubble.footnotes": "\u811A\u6CE8",
21
22
  "action.copy": "\u590D\u5236\u56DE\u7B54",
22
23
  "action.copied": "\u5DF2\u590D\u5236",
24
+ "action.copyCode": "\u590D\u5236\u4EE3\u7801",
25
+ "action.copiedCode": "\u5DF2\u590D\u5236\u4EE3\u7801",
23
26
  "action.expand": "\u5C55\u5F00\u56DE\u7B54",
24
27
  "action.collapse": "\u6298\u53E0\u56DE\u7B54",
25
28
  "action.close": "\u5173\u95ED\u65C1\u95EE",
@@ -67,8 +70,11 @@ var en = {
67
70
  "error.reference": `Select 1\u2013${MAX_REFERENCE_LENGTH.toLocaleString("en-US")} characters of message text, including whitespace (some symbols count as multiple characters).`,
68
71
  "bubble.label": "Side question answer",
69
72
  "bubble.image": "Image",
73
+ "bubble.footnotes": "Footnotes",
70
74
  "action.copy": "Copy answer",
71
75
  "action.copied": "Copied",
76
+ "action.copyCode": "Copy code",
77
+ "action.copiedCode": "Copied code",
72
78
  "action.expand": "Expand answer",
73
79
  "action.collapse": "Collapse answer",
74
80
  "action.close": "Close side question",
@@ -417,10 +423,21 @@ var PERSONA = `\u4F60\u662F\u5F53\u524D\u4E3B\u4EFB\u52A1\u4E4B\u5916\u7684\u4E0
417
423
  function apply(ctx) {
418
424
  ctx.effect(() => hideInternalCommands(ctx.commands));
419
425
  const labels = /* @__PURE__ */ new Set();
420
- const host = ctx.extend({ fiber: ctx.root.fiber });
421
- const protection = createAnswerOnlyGuard(labels, identityOfFromContext(host));
422
- const releaseGuard = host.tools.guard(protection);
423
- const stopWatch = host.on("session/event", (session, event) => protection.recognize(session, event));
426
+ const protection = createAnswerOnlyGuard(labels, identityOfFromContext(ctx));
427
+ const releasePluginGuard = ctx.tools.guard(protection);
428
+ const childGuards = /* @__PURE__ */ new WeakMap();
429
+ const arm = (agent) => {
430
+ protection.own(agent);
431
+ if (!agent.ctx || childGuards.has(agent)) return;
432
+ childGuards.set(agent, agent.ctx.tools.guard(protection));
433
+ };
434
+ const releaseChild = async (agent) => {
435
+ if (!agent) return;
436
+ const release = childGuards.get(agent);
437
+ childGuards.delete(agent);
438
+ await release?.();
439
+ };
440
+ const stopWatch = ctx.on("session/event", (session, event) => protection.recognize(session, event));
424
441
  const jobs = new SideJobs(async (request) => {
425
442
  const provider = ctx.subagents.getProvider("fork");
426
443
  if (!provider?.inheritsParentContext || !provider.capabilities.toolFilter || !provider.capabilities.persona) {
@@ -439,12 +456,16 @@ function apply(ctx) {
439
456
 
440
457
  ${request.question}` }]
441
458
  });
442
- if (run.localAgent) protection.own(run.localAgent);
459
+ if (run.localAgent) arm(run.localAgent);
443
460
  return {
444
461
  result: run.result,
445
462
  dispose: async () => {
446
- await run.dispose();
447
- labels.delete(label);
463
+ try {
464
+ await run.dispose();
465
+ } finally {
466
+ await releaseChild(run.localAgent);
467
+ labels.delete(label);
468
+ }
448
469
  }
449
470
  };
450
471
  } catch (error) {
@@ -455,7 +476,7 @@ ${request.question}` }]
455
476
  onError: (error) => ctx.logger.warn(error),
456
477
  onIdle: async () => {
457
478
  stopWatch();
458
- await releaseGuard();
479
+ await releasePluginGuard();
459
480
  }
460
481
  });
461
482
  ctx.effect(() => () => jobs.dispose());
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { type HostMarkdownLabels } from './host-markdown';
3
+ /** 旁问不加载远程图片。代码块和行内代码里的写法保持原样。 */
4
+ export declare function neutralizeMarkdownImages(source: string, emptyAlt?: string): string;
5
+ export declare function AnswerMarkdown({ text, labels, imageLabel }: {
6
+ text: string;
7
+ labels: HostMarkdownLabels;
8
+ imageLabel: string;
9
+ }): React.JSX.Element;
@@ -0,0 +1,16 @@
1
+ import type { ComponentType } from 'react';
2
+ export interface HostMarkdownLabels {
3
+ code: {
4
+ copyLabel: string;
5
+ copiedLabel: string;
6
+ };
7
+ footnotes: string;
8
+ }
9
+ export type HostMarkdownText = ComponentType<{
10
+ text: string;
11
+ streaming?: boolean;
12
+ labels: HostMarkdownLabels;
13
+ variant?: 'body' | 'compact';
14
+ }>;
15
+ /** 宿主基座模块。旧宿主没有它时返回 null,调用方改用自带 Markdown。 */
16
+ export declare function loadHostMarkdownText(): HostMarkdownText | null;
@@ -1 +1 @@
1
- export declare const CSS = "\n.btw-reference-preview{display:block;margin-top:6px;max-height:3.2em;overflow:hidden;overflow-wrap:anywhere;line-height:1.6;color:var(--dsw-alias-label-primary)}\n.btw-selection-dialog,.btw-selection-menu{position:fixed;box-sizing:border-box;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;font:inherit;font-size:13px;max-height:calc(100dvh - 32px);overflow:auto;box-shadow:0 12px 36px #0003}\n.btw-selection-dialog{width:min(520px,calc(100vw - 32px));margin:auto}.btw-selection-menu{margin:0;width:max-content;max-width:calc(100vw - 16px);padding:0;inset:auto;border-radius:10px;overflow:hidden;border-color:var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);font-family:inherit;font-size:12px;font-weight:500;line-height:16px;box-shadow:0 2px 6px #0001}\n.btw-selection-menu::backdrop{background:transparent}.btw-selection-dialog::backdrop{background:#0004}\n.btw-selection-dialog h3{margin:0 0 12px;font-size:16px}.btw-selection-dialog label{display:flex;flex-direction:column;gap:8px;margin-top:12px}\n.btw-selection-dialog textarea{box-sizing:border-box;width:100%;min-height:96px;resize:vertical;font:inherit;padding:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-base);color:inherit}\n.btw-selection-dialog button,.btw-selection-menu button{font:inherit;color:inherit;background:var(--dsw-alias-interactive-bg-hover);border:0;border-radius:6px;padding:8px 12px;cursor:pointer;min-height:36px}\n.btw-selection-menu [role=\"toolbar\"]{display:flex;align-items:center}.btw-selection-menu button+button{border-left:1px solid var(--dsw-alias-border-l1)}\n.btw-selection-menu button{display:inline-flex;align-items:center;height:28px;min-height:28px;padding:0 8px;border-radius:0;text-align:left;white-space:nowrap;background:transparent}.btw-selection-menu button:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.btw-selection-dialog button:disabled{opacity:.5;cursor:default}.btw-selection-dialog :focus-visible,.btw-selection-menu :focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:2px}\n.btw-selection-menu :focus-visible{outline-width:1px;outline-offset:-2px}\n.btw-selection-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px}.btw-selection-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6;margin:10px 4px}\n.btw-reference{padding:0 14px 10px;font-size:12px}.btw-reference summary{cursor:pointer;color:var(--dsw-alias-label-secondary)}.btw-reference blockquote{margin:8px 0 0;padding:8px;border-left:2px solid var(--dsw-alias-border-l2);white-space:pre-wrap;overflow-wrap:anywhere;max-height:150px;overflow:auto}.btw-selection-dialog .btw-reference{padding:0}\n.btw-dock{display:flex;flex:none;flex-direction:column;gap:8px;width:calc(100% - var(--dsh-composer-side-clearance,0px) - var(--dsh-composer-side-clearance,0px));max-width:var(--dsh-composer-card-max-width,100%);margin:0 auto;max-height:440px;overflow:auto;padding:4px 0 10px;box-sizing:border-box;letter-spacing:0}\n.btw-bubble{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;min-width:0;flex-shrink:0;font-size:13px;line-height:1.6;font-family:var(--dsw-font-family,inherit);letter-spacing:0}\n.btw-header{display:flex;align-items:center;gap:8px;padding:8px 10px 8px 14px;min-width:0;min-height:44px}\n.btw-symbol{display:flex;align-items:center;color:var(--dsw-alias-label-secondary);height:28px;flex-shrink:0}\n.btw-question{flex:1;min-width:0;overflow-wrap:anywhere;font-size:13px;font-weight:500;line-height:22px;max-height:66px;overflow:auto}\n.btw-actions{display:flex;gap:4px;flex-shrink:0}\n.btw-actions button{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;transition:background-color 120ms ease}\n.btw-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.btw-actions button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:-2px}\n.btw-actions button:disabled{cursor:wait;opacity:.5}\n.btw-status{display:flex;align-items:center;gap:8px;padding:0 14px 12px;color:var(--dsw-alias-label-secondary);font-size:13px}\n.btw-spinner{animation:btw-spin 1.2s linear infinite}@keyframes btw-spin{to{transform:rotate(360deg)}}\n.btw-answer{padding:0 14px 14px;max-height:300px;overflow:auto;overflow-wrap:anywhere;font-size:13px;line-height:1.7}\n.btw-answer>*:first-child{margin-top:0}.btw-answer>*:last-child{margin-bottom:0}\n.btw-answer p{margin:8px 0}.btw-answer h1,.btw-answer h2,.btw-answer h3{font-size:15px;line-height:1.5;margin:12px 0 6px;font-weight:600}\n.btw-answer pre{max-width:100%;overflow:auto;background:var(--dsw-alias-markdown-code-block);padding:10px;border-radius:8px;white-space:pre;font-size:12px}\n.btw-answer code{font-family:Consolas,monospace;font-size:12px}.btw-answer :not(pre)>code{background:var(--dsw-alias-markdown-code-block);border-radius:4px;padding:1px 3px}.btw-answer a{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}\n.btw-answer table{display:block;max-width:100%;overflow:auto;border-collapse:collapse}.btw-answer th,.btw-answer td{border:1px solid var(--dsw-alias-border-l2);padding:5px 9px}\n.btw-answer ul,.btw-answer ol{padding-left:22px}.btw-answer blockquote{margin:8px 0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary)}\n.btw-error{margin:0;padding:0 14px 12px;font-size:13px;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}\n@media(max-width:480px){.btw-dock{max-height:330px}.btw-header{padding:6px 8px 6px 12px;gap:6px}.btw-answer{padding:0 12px 12px;max-height:230px}.btw-status,.btw-error{padding-left:12px}.btw-actions{gap:0}.btw-actions button{width:36px;height:36px}}\n@media(prefers-reduced-motion:reduce){.btw-spinner{animation:none}.btw-actions button{transition:none}}\n";
1
+ export declare const CSS = "\n.btw-reference-preview{display:block;margin-top:6px;max-height:3.2em;overflow:hidden;overflow-wrap:anywhere;line-height:1.6;color:var(--dsw-alias-label-primary)}\n.btw-selection-dialog,.btw-selection-menu{position:fixed;box-sizing:border-box;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;font:inherit;font-size:13px;max-height:calc(100dvh - 32px);overflow:auto;box-shadow:0 12px 36px #0003}\n.btw-selection-dialog{width:min(520px,calc(100vw - 32px));margin:auto}.btw-selection-menu{margin:0;width:max-content;max-width:calc(100vw - 16px);padding:0;inset:auto;border-radius:10px;overflow:hidden;border-color:var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);font-family:inherit;font-size:12px;font-weight:500;line-height:16px;box-shadow:0 2px 6px #0001}\n.btw-selection-menu::backdrop{background:transparent}.btw-selection-dialog::backdrop{background:#0004}\n.btw-selection-dialog h3{margin:0 0 12px;font-size:16px}.btw-selection-dialog label{display:flex;flex-direction:column;gap:8px;margin-top:12px}\n.btw-selection-dialog textarea{box-sizing:border-box;width:100%;min-height:96px;resize:vertical;font:inherit;padding:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-base);color:inherit}\n.btw-selection-dialog button,.btw-selection-menu button{font:inherit;color:inherit;background:var(--dsw-alias-interactive-bg-hover);border:0;border-radius:6px;padding:8px 12px;cursor:pointer;min-height:36px}\n.btw-selection-menu [role=\"toolbar\"]{display:flex;align-items:center}.btw-selection-menu button+button{border-left:1px solid var(--dsw-alias-border-l1)}\n.btw-selection-menu button{display:inline-flex;align-items:center;height:28px;min-height:28px;padding:0 8px;border-radius:0;text-align:left;white-space:nowrap;background:transparent}.btw-selection-menu button:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.btw-selection-dialog button:disabled{opacity:.5;cursor:default}.btw-selection-dialog :focus-visible,.btw-selection-menu :focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:2px}\n.btw-selection-menu :focus-visible{outline-width:1px;outline-offset:-2px}\n.btw-selection-actions{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:8px}.btw-selection-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6;margin:10px 4px}\n.btw-reference{padding:0 14px 10px;font-size:12px}.btw-reference summary{cursor:pointer;color:var(--dsw-alias-label-secondary)}.btw-reference blockquote{margin:8px 0 0;padding:8px;border-left:2px solid var(--dsw-alias-border-l2);white-space:pre-wrap;overflow-wrap:anywhere;max-height:150px;overflow:auto}.btw-selection-dialog .btw-reference{padding:0}\n.btw-dock{display:flex;flex:none;flex-direction:column;gap:8px;width:calc(100% - var(--dsh-composer-side-clearance,0px) - var(--dsh-composer-side-clearance,0px));max-width:var(--dsh-composer-card-max-width,100%);margin:0 auto;max-height:440px;overflow:auto;padding:4px 0 10px;box-sizing:border-box;letter-spacing:0}\n.btw-bubble{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;min-width:0;flex-shrink:0;font-size:13px;line-height:1.6;font-family:var(--dsw-font-family,inherit);letter-spacing:0}\n.btw-header{display:flex;align-items:center;gap:8px;padding:8px 10px 8px 14px;min-width:0;min-height:44px}\n.btw-symbol{display:flex;align-items:center;color:var(--dsw-alias-label-secondary);height:28px;flex-shrink:0}\n.btw-question{flex:1;min-width:0;overflow-wrap:anywhere;font-size:13px;font-weight:500;line-height:22px;max-height:66px;overflow:auto}\n.btw-actions{display:flex;gap:4px;flex-shrink:0}\n.btw-actions button{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;transition:background-color 120ms ease}\n.btw-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.btw-actions button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,var(--dsw-alias-label-primary));outline-offset:-2px}\n.btw-actions button:disabled{cursor:wait;opacity:.5}\n.btw-status{display:flex;align-items:center;gap:8px;padding:0 14px 12px;color:var(--dsw-alias-label-secondary);font-size:13px}\n.btw-spinner{animation:btw-spin 1.2s linear infinite}@keyframes btw-spin{to{transform:rotate(360deg)}}\n.btw-answer{padding:0 14px 14px;max-height:300px;overflow:auto;overflow-wrap:anywhere;font-size:13px;line-height:1.7}\n.btw-answer>*:first-child{margin-top:0}.btw-answer>*:last-child{margin-bottom:0}\n.btw-answer p{margin:8px 0}.btw-answer h1,.btw-answer h2,.btw-answer h3{font-size:15px;line-height:1.5;margin:12px 0 6px;font-weight:600}\n.btw-answer pre{max-width:100%;overflow:auto;background:var(--dsw-alias-markdown-code-block);padding:10px;border-radius:8px;white-space:pre;font-size:12px}.btw-answer .katex-display{max-width:100%;overflow:auto}\n.btw-answer code{font-family:Consolas,monospace;font-size:12px}.btw-answer :not(pre)>code{background:var(--dsw-alias-markdown-code-block);border-radius:4px;padding:1px 3px}.btw-answer a{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}\n.btw-answer table{display:block;max-width:100%;overflow:auto;border-collapse:collapse}.btw-answer th,.btw-answer td{border:1px solid var(--dsw-alias-border-l2);padding:5px 9px}\n.btw-answer ul,.btw-answer ol{padding-left:22px}.btw-answer blockquote{margin:8px 0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary)}\n.btw-error{margin:0;padding:0 14px 12px;font-size:13px;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere}\n@media(max-width:480px){.btw-dock{max-height:330px}.btw-header{padding:6px 8px 6px 12px;gap:6px}.btw-answer{padding:0 12px 12px;max-height:230px}.btw-status,.btw-error{padding-left:12px}.btw-actions{gap:0}.btw-actions button{width:36px;height:36px}}\n@media(prefers-reduced-motion:reduce){.btw-spinner{animation:none}.btw-actions button{transition:none}}\n";
@@ -15,8 +15,11 @@ export declare const zh: {
15
15
  'error.reference': string;
16
16
  'bubble.label': string;
17
17
  'bubble.image': string;
18
+ 'bubble.footnotes': string;
18
19
  'action.copy': string;
19
20
  'action.copied': string;
21
+ 'action.copyCode': string;
22
+ 'action.copiedCode': string;
20
23
  'action.expand': string;
21
24
  'action.collapse': string;
22
25
  'action.close': string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-btw",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "DSH 一次性只读旁问插件:基于当前上下文回答,独立气泡,不执行工具 · One-shot read-only side-ask plugin: answers from current context in an independent bubble, no tool execution",
5
5
  "repository": {
6
6
  "type": "git",