opencode-subagent-magazine 1.5.2 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -56,7 +56,8 @@
56
56
  - **TTL 自动清理**:可配置数据保留期限(3/7/14/30 天/无期限),每次访问自动续期,过期自动清除
57
57
  - **手动清除条目**:`/subagent-clear-entries` 清除当前会话所有记录,防止历史条目扫描重建
58
58
  - **语言适配**:支持 `/subagent-lang` 运行时切换中/英文,偏好持久化
59
- - **斜杠命令**:`/subagent-lang` `/subagent-max` `/subagent-order` `/subagent-scroll` `/subagent-ttl` `/subagent-clear-entries` `/subagent-session` `/subagent-version` `/subagent-clear-running` 动态配置
59
+ - **可切换边框**:`/subagent-border` 控制面板外边框显隐(默认隐藏),开启后与缓存统计面板风格统一
60
+ - **斜杠命令**:`/subagent-lang` `/subagent-max` `/subagent-order` `/subagent-scroll` `/subagent-ttl` `/subagent-clear-entries` `/subagent-session` `/subagent-version` `/subagent-clear-running` `/subagent-border` 动态配置
60
61
 
61
62
  ---
62
63
 
@@ -114,6 +115,7 @@ npm install -g opencode-subagent-magazine@latest
114
115
  | `/subagent-session` | 查看当前会话 ID | 弹出当前 OpenCode 会话 ID |
115
116
  | `/subagent-version` | 查看插件版本 | 弹出当前插件版本号 |
116
117
  | `/subagent-clear-running` | 批量清理僵尸条目 | 一键将所有运行中的条目标记为完成,清理卡住的旧数据 |
118
+ | `/subagent-border` | 开关面板边框 | 切换面板外边框显隐(默认隐藏),即时生效并持久化 |
117
119
 
118
120
  <div align="center">
119
121
  <img src="https://raw.githubusercontent.com/Hotakus/opencode-subagent-magazine/master/assets/slash_cmds.png" alt="斜杠命令" width="49%"></img>
@@ -1 +1 @@
1
- export declare const PLUGIN_VERSION = "1.5.2";
1
+ export declare const PLUGIN_VERSION = "1.5.3";
package/dist/_version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // auto-generated
2
- export const PLUGIN_VERSION = "1.5.2";
2
+ export const PLUGIN_VERSION = "1.5.3";
package/dist/i18n.d.ts CHANGED
@@ -49,6 +49,8 @@ declare const ZH_T: {
49
49
  readonly "cancel.status_error": "无法查询会话状态";
50
50
  readonly "cancel.sent": "已发送取消指令";
51
51
  readonly "cancel.failed": "取消失败";
52
+ readonly borderShown: "面板边框 已显示";
53
+ readonly borderHidden: "面板边框 已隐藏";
52
54
  };
53
55
  /** 结构约束:值放宽为 string,键集合来自中文表(新增语言缺 key 会编译报错)。 */
54
56
  export type Translation = {
package/dist/i18n.js CHANGED
@@ -53,6 +53,8 @@ const ZH_T = {
53
53
  "cancel.status_error": "无法查询会话状态",
54
54
  "cancel.sent": "已发送取消指令",
55
55
  "cancel.failed": "取消失败",
56
+ "borderShown": "面板边框 已显示",
57
+ "borderHidden": "面板边框 已隐藏",
56
58
  };
57
59
  const EN_T = {
58
60
  "panel.title": "SubAgent",
@@ -104,6 +106,8 @@ const EN_T = {
104
106
  "cancel.status_error": "Cannot query session status",
105
107
  "cancel.sent": "Cancel instruction sent",
106
108
  "cancel.failed": "Cancellation failed",
109
+ "borderShown": "Panel border shown",
110
+ "borderHidden": "Panel border hidden",
107
111
  };
108
112
  const JA_T = {
109
113
  "panel.title": "サブエージェント",
@@ -155,6 +159,8 @@ const JA_T = {
155
159
  "cancel.status_error": "セッション状態を照会できません",
156
160
  "cancel.sent": "キャンセル指示を送信しました",
157
161
  "cancel.failed": "キャンセルに失敗しました",
162
+ "borderShown": "パネル枠線 表示",
163
+ "borderHidden": "パネル枠線 非表示",
158
164
  };
159
165
  const KO_T = {
160
166
  "panel.title": "서브 에이전트",
@@ -206,6 +212,8 @@ const KO_T = {
206
212
  "cancel.status_error": "세션 상태를 조회할 수 없습니다",
207
213
  "cancel.sent": "취소 지시를 보냈습니다",
208
214
  "cancel.failed": "취소에 실패했습니다",
215
+ "borderShown": "패널 테두리 표시",
216
+ "borderHidden": "패널 테두리 숨김",
209
217
  };
210
218
  export const LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
211
219
  /** 语言元数据:/subagent-lang 选项与自动检测共用。 */
package/dist/index.js CHANGED
@@ -1252,7 +1252,19 @@ function SubAgentPanel(props) {
1252
1252
  return next;
1253
1253
  });
1254
1254
  };
1255
- const sep = () => "\u2500".repeat(Math.max(1, panelWidth()));
1255
+ /** Horizontal space eaten by the panel border (1+1) and its padding (2+2). */
1256
+ const gutter = createMemo(() => (props.borderVisible() ? 6 : 0));
1257
+ const sep = () => "\u2500".repeat(Math.max(1, panelWidth() - gutter()));
1258
+ // The border toggling shifts the box dimensions, which may not reliably
1259
+ // re-fire onSizeChange across (re)mount cycles; re-sync panelWidth with the
1260
+ // live box so the content width stays correct.
1261
+ createEffect(() => {
1262
+ props.borderVisible();
1263
+ if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
1264
+ const w = Math.max(20, boxEl.width);
1265
+ setPanelWidth((prev) => (prev === w ? prev : w));
1266
+ }
1267
+ });
1256
1268
  // ── expanded detail right-align ──
1257
1269
  const expandedMaxLabelW = createMemo(() => {
1258
1270
  const labels = [
@@ -1262,7 +1274,7 @@ function SubAgentPanel(props) {
1262
1274
  return Math.max(...labels.map(l => visualWidth(l + ": ")));
1263
1275
  });
1264
1276
  const expandedPad = (label) => Math.max(0, expandedMaxLabelW() - visualWidth(label + ": "));
1265
- const expandedValAvail = () => Math.max(6, panelWidth() - INDENT - expandedMaxLabelW());
1277
+ const expandedValAvail = () => Math.max(6, panelWidth() - gutter() - INDENT - expandedMaxLabelW());
1266
1278
  // ── header parts for colored spans ──
1267
1279
  const summaryParts = createMemo(() => {
1268
1280
  if (!anyEntry())
@@ -1297,7 +1309,7 @@ function SubAgentPanel(props) {
1297
1309
  return false;
1298
1310
  const icon = "\u25bc";
1299
1311
  const need = visualWidth(icon) + 1 + visualWidth(t("panel.title")) + versionW + summaryCols();
1300
- return need <= panelWidth();
1312
+ return need <= panelWidth() - gutter();
1301
1313
  });
1302
1314
  const leftCols = createMemo(() => {
1303
1315
  const icon = open() ? "\u25bc" : "\u25b6";
@@ -1309,11 +1321,11 @@ function SubAgentPanel(props) {
1309
1321
  const spacerCols = createMemo(() => {
1310
1322
  if (!anyEntry())
1311
1323
  return 0;
1312
- return Math.max(0, panelWidth() - leftCols() - summaryCols());
1324
+ return Math.max(0, panelWidth() - gutter() - leftCols() - summaryCols());
1313
1325
  });
1314
- const valueCols = (label) => Math.max(4, panelWidth() - INDENT - visualWidth(label + ": "));
1326
+ const valueCols = (label) => Math.max(4, panelWidth() - gutter() - INDENT - visualWidth(label + ": "));
1315
1327
  // ── render ──
1316
- return (_jsxs("box", { border: false, paddingTop: 0, paddingBottom: 0, paddingLeft: 0, paddingRight: 0, flexDirection: "column", gap: 0, ref: boxEl, onSizeChange: () => {
1328
+ return (_jsxs("box", { border: props.borderVisible(), ...(props.borderVisible() ? { borderColor: pal().border } : {}), paddingTop: 0, paddingBottom: 0, paddingLeft: props.borderVisible() ? 2 : 0, paddingRight: props.borderVisible() ? 2 : 0, flexDirection: "column", gap: 0, ref: boxEl, onSizeChange: () => {
1317
1329
  const w = boxEl ? Math.max(20, boxEl.width ?? 0) : 28;
1318
1330
  setPanelWidth((prev) => (prev === w ? prev : w));
1319
1331
  }, children: [_jsxs("text", { onMouseUp: () => {
@@ -1405,7 +1417,7 @@ function SubAgentPanel(props) {
1405
1417
  w += visualWidth(tk);
1406
1418
  return w;
1407
1419
  };
1408
- const labelAvail = () => Math.max(6, panelWidth() - LEFT_PAD - suffixW());
1420
+ const labelAvail = () => Math.max(6, panelWidth() - gutter() - LEFT_PAD - suffixW());
1409
1421
  const labelText = () => {
1410
1422
  const max = labelAvail();
1411
1423
  const text = entry.title || entry.agent;
@@ -1443,7 +1455,7 @@ function SubAgentPanel(props) {
1443
1455
  const cancelLabel = () => ` ${t("cancel.label")}`;
1444
1456
  const dismissLabel = () => ` ${t("dismiss.label")}`;
1445
1457
  const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0);
1446
- const spacerW = () => Math.max(1, panelWidth() - openW() - rightW - 2);
1458
+ const spacerW = () => Math.max(1, panelWidth() - gutter() - openW() - rightW - 2);
1447
1459
  return (_jsxs("box", { flexDirection: "row", children: [_jsx(Show, { when: entry.sessionId, children: _jsxs("text", { onMouseOver: () => setHoveredOpen(entry.id), onMouseOut: () => setHoveredOpen(undefined), onMouseUp: () => {
1448
1460
  if (entry.sessionId) {
1449
1461
  props.api.route.navigate("session", { sessionID: entry.sessionId });
@@ -1461,7 +1473,7 @@ function SubAgentPanel(props) {
1461
1473
  const right = props.sortOrder() === "desc"
1462
1474
  ? `\u2191 ${t("scroll.top")}`
1463
1475
  : `\u2193 ${t("scroll.bottom")}`;
1464
- const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0;
1476
+ const pad = showTop ? Math.max(1, panelWidth() - gutter() - visualWidth(left) - visualWidth(right)) : 0;
1465
1477
  return (_jsxs("box", { flexDirection: "row", children: [_jsx("text", { onMouseOver: () => showMore && setHoveredMoreBelow(true), onMouseOut: () => setHoveredMoreBelow(false), onMouseUp: () => {
1466
1478
  if (!showMore)
1467
1479
  return;
@@ -1494,7 +1506,7 @@ function createSidebarSlot(api, sig) {
1494
1506
  slots: {
1495
1507
  sidebar_content(ctx, input) {
1496
1508
  sig.sessionId = input.session_id;
1497
- return (_jsx(SubAgentPanel, { theme: ctx.theme.current, api: api, lang: sig.lang, maxEntries: sig.maxEntries, sortOrder: sig.sortOrder, scrollMode: sig.scrollMode, sessionId: input.session_id }));
1509
+ return (_jsx(SubAgentPanel, { theme: ctx.theme.current, api: api, lang: sig.lang, maxEntries: sig.maxEntries, sortOrder: sig.sortOrder, scrollMode: sig.scrollMode, borderVisible: sig.borderVisible, sessionId: input.session_id }));
1498
1510
  },
1499
1511
  },
1500
1512
  };
@@ -1508,7 +1520,8 @@ const tui = async (api) => {
1508
1520
  const [maxEntries, setMaxEntries] = createSignal(parseInt(String(api.kv.get(`${KV_PREFIX}.max_entries`, "10")), 10) || 10);
1509
1521
  const [sortOrder, setSortOrder] = createSignal(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
1510
1522
  const [scrollMode, setScrollMode] = createSignal(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
1511
- const signals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, sessionId: "" };
1523
+ const [borderVisible, setBorderVisible] = createSignal(api.kv.get(`${KV_PREFIX}.border`, false) === true);
1524
+ const signals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, borderVisible, setBorderVisible, sessionId: "" };
1512
1525
  api.slots.register(createSidebarSlot(api, signals));
1513
1526
  // ── slash command: /subagent-lang ──
1514
1527
  api.command?.register(() => [
@@ -1752,6 +1765,20 @@ const tui = async (api) => {
1752
1765
  } })));
1753
1766
  },
1754
1767
  },
1768
+ {
1769
+ title: "SubAgent Magazine: Border",
1770
+ value: "subagent-border",
1771
+ description: "Show or hide the panel border",
1772
+ slash: { name: "subagent-border" },
1773
+ onSelect: (dialog) => {
1774
+ const t = createT(() => signals.lang());
1775
+ const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, false));
1776
+ api.kv.set(`${KV_PREFIX}.border`, !cur);
1777
+ signals.setBorderVisible(!cur);
1778
+ api.ui.toast({ message: !cur ? t("borderShown") : t("borderHidden") });
1779
+ dialog?.clear();
1780
+ },
1781
+ },
1755
1782
  ]);
1756
1783
  };
1757
1784
  const mod = {
package/dist/tui.js CHANGED
@@ -4,14 +4,16 @@ import { insertNode as _$insertNode } from "@opentui/solid";
4
4
  import { createComponent as _$createComponent } from "@opentui/solid";
5
5
  import { effect as _$effect } from "@opentui/solid";
6
6
  import { insert as _$insert } from "@opentui/solid";
7
- import { memo as _$memo } from "@opentui/solid";
8
- import { use as _$use } from "@opentui/solid";
9
7
  import { setProp as _$setProp } from "@opentui/solid";
8
+ import { use as _$use } from "@opentui/solid";
9
+ import { spread as _$spread } from "@opentui/solid";
10
+ import { mergeProps as _$mergeProps } from "@opentui/solid";
11
+ import { memo as _$memo } from "@opentui/solid";
10
12
  import { createElement as _$createElement } from "@opentui/solid";
11
13
  import { createMemo, createSignal, createEffect, onMount, onCleanup, untrack, Show, For } from "solid-js";
12
14
 
13
15
  // src/_version.ts
14
- var PLUGIN_VERSION = "1.5.2";
16
+ var PLUGIN_VERSION = "1.5.3";
15
17
 
16
18
  // src/clipboard.ts
17
19
  import { spawn } from "node:child_process";
@@ -159,7 +161,9 @@ var ZH_T = {
159
161
  "cancel.already_ended": "\u4F1A\u8BDD\u5DF2\u7ED3\u675F\uFF0C\u65E0\u9700\u53D6\u6D88",
160
162
  "cancel.status_error": "\u65E0\u6CD5\u67E5\u8BE2\u4F1A\u8BDD\u72B6\u6001",
161
163
  "cancel.sent": "\u5DF2\u53D1\u9001\u53D6\u6D88\u6307\u4EE4",
162
- "cancel.failed": "\u53D6\u6D88\u5931\u8D25"
164
+ "cancel.failed": "\u53D6\u6D88\u5931\u8D25",
165
+ "borderShown": "\u9762\u677F\u8FB9\u6846 \u5DF2\u663E\u793A",
166
+ "borderHidden": "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF"
163
167
  };
164
168
  var EN_T = {
165
169
  "panel.title": "SubAgent",
@@ -210,7 +214,9 @@ var EN_T = {
210
214
  "cancel.already_ended": "Session already ended, no need to cancel",
211
215
  "cancel.status_error": "Cannot query session status",
212
216
  "cancel.sent": "Cancel instruction sent",
213
- "cancel.failed": "Cancellation failed"
217
+ "cancel.failed": "Cancellation failed",
218
+ "borderShown": "Panel border shown",
219
+ "borderHidden": "Panel border hidden"
214
220
  };
215
221
  var JA_T = {
216
222
  "panel.title": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
@@ -261,7 +267,9 @@ var JA_T = {
261
267
  "cancel.already_ended": "\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u65E2\u306B\u7D42\u4E86\u3057\u3066\u3044\u307E\u3059",
262
268
  "cancel.status_error": "\u30BB\u30C3\u30B7\u30E7\u30F3\u72B6\u614B\u3092\u7167\u4F1A\u3067\u304D\u307E\u305B\u3093",
263
269
  "cancel.sent": "\u30AD\u30E3\u30F3\u30BB\u30EB\u6307\u793A\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F",
264
- "cancel.failed": "\u30AD\u30E3\u30F3\u30BB\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
270
+ "cancel.failed": "\u30AD\u30E3\u30F3\u30BB\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
271
+ "borderShown": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u8868\u793A",
272
+ "borderHidden": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A"
265
273
  };
266
274
  var KO_T = {
267
275
  "panel.title": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8",
@@ -312,7 +320,9 @@ var KO_T = {
312
320
  "cancel.already_ended": "\uC138\uC158\uC774 \uC774\uBBF8 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
313
321
  "cancel.status_error": "\uC138\uC158 \uC0C1\uD0DC\uB97C \uC870\uD68C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
314
322
  "cancel.sent": "\uCDE8\uC18C \uC9C0\uC2DC\uB97C \uBCF4\uB0C8\uC2B5\uB2C8\uB2E4",
315
- "cancel.failed": "\uCDE8\uC18C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4"
323
+ "cancel.failed": "\uCDE8\uC18C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
324
+ "borderShown": "\uD328\uB110 \uD14C\uB450\uB9AC \uD45C\uC2DC",
325
+ "borderHidden": "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40"
316
326
  };
317
327
  var LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
318
328
  var LANG_META = [
@@ -1568,13 +1578,21 @@ function SubAgentPanel(props) {
1568
1578
  return next;
1569
1579
  });
1570
1580
  };
1571
- const sep = () => "\u2500".repeat(Math.max(1, panelWidth()));
1581
+ const gutter = createMemo(() => props.borderVisible() ? 6 : 0);
1582
+ const sep = () => "\u2500".repeat(Math.max(1, panelWidth() - gutter()));
1583
+ createEffect(() => {
1584
+ props.borderVisible();
1585
+ if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
1586
+ const w = Math.max(20, boxEl.width);
1587
+ setPanelWidth((prev) => prev === w ? prev : w);
1588
+ }
1589
+ });
1572
1590
  const expandedMaxLabelW = createMemo(() => {
1573
1591
  const labels = [t("agent.label"), t("status.label"), t("time.label"), t("tokens.label"), t("error.label"), t("cost.label"), t("model.label"), t("todo.label"), t("session.label")];
1574
1592
  return Math.max(...labels.map((l) => visualWidth(l + ": ")));
1575
1593
  });
1576
1594
  const expandedPad = (label) => Math.max(0, expandedMaxLabelW() - visualWidth(label + ": "));
1577
- const expandedValAvail = () => Math.max(6, panelWidth() - INDENT - expandedMaxLabelW());
1595
+ const expandedValAvail = () => Math.max(6, panelWidth() - gutter() - INDENT - expandedMaxLabelW());
1578
1596
  const summaryParts = createMemo(() => {
1579
1597
  if (!anyEntry()) return null;
1580
1598
  const dot = "\u25CF";
@@ -1603,7 +1621,7 @@ function SubAgentPanel(props) {
1603
1621
  if (!open()) return false;
1604
1622
  const icon = "\u25BC";
1605
1623
  const need = visualWidth(icon) + 1 + visualWidth(t("panel.title")) + versionW + summaryCols();
1606
- return need <= panelWidth();
1624
+ return need <= panelWidth() - gutter();
1607
1625
  });
1608
1626
  const leftCols = createMemo(() => {
1609
1627
  const icon = open() ? "\u25BC" : "\u25B6";
@@ -1613,25 +1631,36 @@ function SubAgentPanel(props) {
1613
1631
  });
1614
1632
  const spacerCols = createMemo(() => {
1615
1633
  if (!anyEntry()) return 0;
1616
- return Math.max(0, panelWidth() - leftCols() - summaryCols());
1634
+ return Math.max(0, panelWidth() - gutter() - leftCols() - summaryCols());
1617
1635
  });
1618
- const valueCols = (label) => Math.max(4, panelWidth() - INDENT - visualWidth(label + ": "));
1636
+ const valueCols = (label) => Math.max(4, panelWidth() - gutter() - INDENT - visualWidth(label + ": "));
1619
1637
  return (() => {
1620
1638
  var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createElement("span"), _el$4 = _$createElement("span");
1621
1639
  _$insertNode(_el$, _el$2);
1622
1640
  var _ref$ = boxEl;
1623
1641
  typeof _ref$ === "function" ? _$use(_ref$, _el$) : boxEl = _el$;
1624
- _$setProp(_el$, "border", false);
1625
- _$setProp(_el$, "paddingTop", 0);
1626
- _$setProp(_el$, "paddingBottom", 0);
1627
- _$setProp(_el$, "paddingLeft", 0);
1628
- _$setProp(_el$, "paddingRight", 0);
1629
- _$setProp(_el$, "flexDirection", "column");
1630
- _$setProp(_el$, "gap", 0);
1631
- _$setProp(_el$, "onSizeChange", () => {
1632
- const w = boxEl ? Math.max(20, boxEl.width ?? 0) : 28;
1633
- setPanelWidth((prev) => prev === w ? prev : w);
1634
- });
1642
+ _$spread(_el$, _$mergeProps({
1643
+ get border() {
1644
+ return props.borderVisible();
1645
+ }
1646
+ }, () => props.borderVisible() ? {
1647
+ borderColor: pal().border
1648
+ } : {}, {
1649
+ "paddingTop": 0,
1650
+ "paddingBottom": 0,
1651
+ get paddingLeft() {
1652
+ return props.borderVisible() ? 2 : 0;
1653
+ },
1654
+ get paddingRight() {
1655
+ return props.borderVisible() ? 2 : 0;
1656
+ },
1657
+ "flexDirection": "column",
1658
+ "gap": 0,
1659
+ "onSizeChange": () => {
1660
+ const w = boxEl ? Math.max(20, boxEl.width ?? 0) : 28;
1661
+ setPanelWidth((prev) => prev === w ? prev : w);
1662
+ }
1663
+ }), true);
1635
1664
  _$insertNode(_el$2, _el$3);
1636
1665
  _$insertNode(_el$2, _el$4);
1637
1666
  _$setProp(_el$2, "onMouseUp", () => {
@@ -1828,7 +1857,7 @@ function SubAgentPanel(props) {
1828
1857
  if (tk) w += visualWidth(tk);
1829
1858
  return w;
1830
1859
  };
1831
- const labelAvail = () => Math.max(6, panelWidth() - LEFT_PAD - suffixW());
1860
+ const labelAvail = () => Math.max(6, panelWidth() - gutter() - LEFT_PAD - suffixW());
1832
1861
  const labelText = () => {
1833
1862
  const max2 = labelAvail();
1834
1863
  const text = entry.title || entry.agent;
@@ -2227,7 +2256,7 @@ ${t("session.toast.copy_failed")}`,
2227
2256
  const cancelLabel = () => ` ${t("cancel.label")}`;
2228
2257
  const dismissLabel = () => ` ${t("dismiss.label")}`;
2229
2258
  const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0);
2230
- const spacerW = () => Math.max(1, panelWidth() - openW() - rightW - 2);
2259
+ const spacerW = () => Math.max(1, panelWidth() - gutter() - openW() - rightW - 2);
2231
2260
  return (() => {
2232
2261
  var _el$91 = _$createElement("box"), _el$95 = _$createElement("text");
2233
2262
  _$insertNode(_el$91, _el$95);
@@ -2330,7 +2359,7 @@ ${t("session.toast.copy_failed")}`,
2330
2359
  const showTop = props.sortOrder() === "desc" ? scrollOffset() > 0 : entryList().length > max() && clampedOffset() < entryList().length - max();
2331
2360
  const left = showMore ? ` \u2193 ${hiddenBelow()} ${t("scroll.more")}` : " ";
2332
2361
  const right = props.sortOrder() === "desc" ? `\u2191 ${t("scroll.top")}` : `\u2193 ${t("scroll.bottom")}`;
2333
- const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0;
2362
+ const pad = showTop ? Math.max(1, panelWidth() - gutter() - visualWidth(left) - visualWidth(right)) : 0;
2334
2363
  return (() => {
2335
2364
  var _el$100 = _$createElement("box"), _el$101 = _$createElement("text"), _el$102 = _$createElement("span");
2336
2365
  _$insertNode(_el$100, _el$101);
@@ -2431,6 +2460,9 @@ function createSidebarSlot(api, sig) {
2431
2460
  get scrollMode() {
2432
2461
  return sig.scrollMode;
2433
2462
  },
2463
+ get borderVisible() {
2464
+ return sig.borderVisible;
2465
+ },
2434
2466
  get sessionId() {
2435
2467
  return input.session_id;
2436
2468
  }
@@ -2447,6 +2479,7 @@ var tui = async (api) => {
2447
2479
  const [maxEntries, setMaxEntries] = createSignal(parseInt(String(api.kv.get(`${KV_PREFIX}.max_entries`, "10")), 10) || 10);
2448
2480
  const [sortOrder, setSortOrder] = createSignal(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
2449
2481
  const [scrollMode, setScrollMode] = createSignal(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
2482
+ const [borderVisible, setBorderVisible] = createSignal(api.kv.get(`${KV_PREFIX}.border`, false) === true);
2450
2483
  const signals = {
2451
2484
  lang,
2452
2485
  setLang,
@@ -2456,6 +2489,8 @@ var tui = async (api) => {
2456
2489
  setSortOrder,
2457
2490
  scrollMode,
2458
2491
  setScrollMode,
2492
+ borderVisible,
2493
+ setBorderVisible,
2459
2494
  sessionId: ""
2460
2495
  };
2461
2496
  api.slots.register(createSidebarSlot(api, signals));
@@ -2789,6 +2824,23 @@ var tui = async (api) => {
2789
2824
  }
2790
2825
  }));
2791
2826
  }
2827
+ }, {
2828
+ title: "SubAgent Magazine: Border",
2829
+ value: "subagent-border",
2830
+ description: "Show or hide the panel border",
2831
+ slash: {
2832
+ name: "subagent-border"
2833
+ },
2834
+ onSelect: (dialog) => {
2835
+ const t = createT(() => signals.lang());
2836
+ const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, false));
2837
+ api.kv.set(`${KV_PREFIX}.border`, !cur);
2838
+ signals.setBorderVisible(!cur);
2839
+ api.ui.toast({
2840
+ message: !cur ? t("borderShown") : t("borderHidden")
2841
+ });
2842
+ dialog?.clear();
2843
+ }
2792
2844
  }]);
2793
2845
  };
2794
2846
  var mod = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-subagent-magazine",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "OpenCode TUI plugin monitoring sub-agent invocation status in real time",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
package/src/_version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // auto-generated
2
- export const PLUGIN_VERSION="1.5.2";
2
+ export const PLUGIN_VERSION="1.5.3";
package/src/i18n.ts CHANGED
@@ -56,6 +56,8 @@ const ZH_T = {
56
56
  "cancel.status_error": "无法查询会话状态",
57
57
  "cancel.sent": "已发送取消指令",
58
58
  "cancel.failed": "取消失败",
59
+ "borderShown": "面板边框 已显示",
60
+ "borderHidden": "面板边框 已隐藏",
59
61
  } as const
60
62
 
61
63
  /** 结构约束:值放宽为 string,键集合来自中文表(新增语言缺 key 会编译报错)。 */
@@ -111,6 +113,8 @@ const EN_T: Translation = {
111
113
  "cancel.status_error": "Cannot query session status",
112
114
  "cancel.sent": "Cancel instruction sent",
113
115
  "cancel.failed": "Cancellation failed",
116
+ "borderShown": "Panel border shown",
117
+ "borderHidden": "Panel border hidden",
114
118
  }
115
119
 
116
120
  const JA_T: Translation = {
@@ -163,6 +167,8 @@ const JA_T: Translation = {
163
167
  "cancel.status_error": "セッション状態を照会できません",
164
168
  "cancel.sent": "キャンセル指示を送信しました",
165
169
  "cancel.failed": "キャンセルに失敗しました",
170
+ "borderShown": "パネル枠線 表示",
171
+ "borderHidden": "パネル枠線 非表示",
166
172
  }
167
173
 
168
174
  const KO_T: Translation = {
@@ -215,6 +221,8 @@ const KO_T: Translation = {
215
221
  "cancel.status_error": "세션 상태를 조회할 수 없습니다",
216
222
  "cancel.sent": "취소 지시를 보냈습니다",
217
223
  "cancel.failed": "취소에 실패했습니다",
224
+ "borderShown": "패널 테두리 표시",
225
+ "borderHidden": "패널 테두리 숨김",
218
226
  }
219
227
 
220
228
  export const LANGS: Record<LangCode, Translation> = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T }
package/src/index.tsx CHANGED
@@ -214,6 +214,7 @@ function SubAgentPanel(props: {
214
214
  maxEntries: () => number
215
215
  sortOrder: () => SortOrder
216
216
  scrollMode: () => ScrollMode
217
+ borderVisible: () => boolean
217
218
  sessionId: string
218
219
  }): JSX.Element {
219
220
  const t = createT(() => props.lang())
@@ -1281,7 +1282,21 @@ function SubAgentPanel(props: {
1281
1282
  })
1282
1283
  }
1283
1284
 
1284
- const sep = () => "\u2500".repeat(Math.max(1, panelWidth()))
1285
+ /** Horizontal space eaten by the panel border (1+1) and its padding (2+2). */
1286
+ const gutter = createMemo(() => (props.borderVisible() ? 6 : 0))
1287
+
1288
+ const sep = () => "\u2500".repeat(Math.max(1, panelWidth() - gutter()))
1289
+
1290
+ // The border toggling shifts the box dimensions, which may not reliably
1291
+ // re-fire onSizeChange across (re)mount cycles; re-sync panelWidth with the
1292
+ // live box so the content width stays correct.
1293
+ createEffect(() => {
1294
+ props.borderVisible()
1295
+ if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
1296
+ const w = Math.max(20, boxEl.width)
1297
+ setPanelWidth((prev) => (prev === w ? prev : w))
1298
+ }
1299
+ })
1285
1300
 
1286
1301
  // ── expanded detail right-align ──
1287
1302
  const expandedMaxLabelW = createMemo(() => {
@@ -1294,7 +1309,7 @@ function SubAgentPanel(props: {
1294
1309
 
1295
1310
  const expandedPad = (label: string) => Math.max(0, expandedMaxLabelW() - visualWidth(label + ": "))
1296
1311
 
1297
- const expandedValAvail = () => Math.max(6, panelWidth() - INDENT - expandedMaxLabelW())
1312
+ const expandedValAvail = () => Math.max(6, panelWidth() - gutter() - INDENT - expandedMaxLabelW())
1298
1313
 
1299
1314
  // ── header parts for colored spans ──
1300
1315
  const summaryParts = createMemo(() => {
@@ -1328,7 +1343,7 @@ function SubAgentPanel(props: {
1328
1343
  if (!open()) return false
1329
1344
  const icon = "\u25bc"
1330
1345
  const need = visualWidth(icon) + 1 + visualWidth(t("panel.title")) + versionW + summaryCols()
1331
- return need <= panelWidth()
1346
+ return need <= panelWidth() - gutter()
1332
1347
  })
1333
1348
 
1334
1349
  const leftCols = createMemo(() => {
@@ -1340,17 +1355,19 @@ function SubAgentPanel(props: {
1340
1355
 
1341
1356
  const spacerCols = createMemo(() => {
1342
1357
  if (!anyEntry()) return 0
1343
- return Math.max(0, panelWidth() - leftCols() - summaryCols())
1358
+ return Math.max(0, panelWidth() - gutter() - leftCols() - summaryCols())
1344
1359
  })
1345
1360
 
1346
1361
  const valueCols = (label: string) =>
1347
- Math.max(4, panelWidth() - INDENT - visualWidth(label + ": "))
1362
+ Math.max(4, panelWidth() - gutter() - INDENT - visualWidth(label + ": "))
1348
1363
 
1349
1364
  // ── render ──
1350
1365
  return (
1351
1366
  <box
1352
- border={false}
1353
- paddingTop={0} paddingBottom={0} paddingLeft={0} paddingRight={0}
1367
+ border={props.borderVisible()}
1368
+ {...(props.borderVisible() ? { borderColor: pal().border } : {})}
1369
+ paddingTop={0} paddingBottom={0}
1370
+ paddingLeft={props.borderVisible() ? 2 : 0} paddingRight={props.borderVisible() ? 2 : 0}
1354
1371
  flexDirection="column" gap={0}
1355
1372
  ref={boxEl}
1356
1373
  onSizeChange={() => {
@@ -1487,7 +1504,7 @@ function SubAgentPanel(props: {
1487
1504
  if (tk) w += visualWidth(tk)
1488
1505
  return w
1489
1506
  }
1490
- const labelAvail = () => Math.max(6, panelWidth() - LEFT_PAD - suffixW())
1507
+ const labelAvail = () => Math.max(6, panelWidth() - gutter() - LEFT_PAD - suffixW())
1491
1508
  const labelText = () => {
1492
1509
  const max = labelAvail()
1493
1510
  const text = entry.title || entry.agent
@@ -1631,7 +1648,7 @@ function SubAgentPanel(props: {
1631
1648
  const cancelLabel = () => ` ${t("cancel.label")}`
1632
1649
  const dismissLabel = () => ` ${t("dismiss.label")}`
1633
1650
  const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0)
1634
- const spacerW = () => Math.max(1, panelWidth() - openW() - rightW - 2)
1651
+ const spacerW = () => Math.max(1, panelWidth() - gutter() - openW() - rightW - 2)
1635
1652
  return (
1636
1653
  <box flexDirection="row">
1637
1654
  <Show when={entry.sessionId}>
@@ -1688,7 +1705,7 @@ function SubAgentPanel(props: {
1688
1705
  const right = props.sortOrder() === "desc"
1689
1706
  ? `\u2191 ${t("scroll.top")}`
1690
1707
  : `\u2193 ${t("scroll.bottom")}`
1691
- const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0
1708
+ const pad = showTop ? Math.max(1, panelWidth() - gutter() - visualWidth(left) - visualWidth(right)) : 0
1692
1709
  return (
1693
1710
  <box flexDirection="row">
1694
1711
  <text
@@ -1753,6 +1770,8 @@ interface SharedSignals {
1753
1770
  setSortOrder: (o: SortOrder) => void
1754
1771
  scrollMode: () => ScrollMode
1755
1772
  setScrollMode: (m: ScrollMode) => void
1773
+ borderVisible: () => boolean
1774
+ setBorderVisible: (v: boolean) => void
1756
1775
  sessionId: string
1757
1776
  }
1758
1777
 
@@ -1770,6 +1789,7 @@ function createSidebarSlot(api: TuiPluginApi, sig: SharedSignals): TuiSlotPlugin
1770
1789
  maxEntries={sig.maxEntries}
1771
1790
  sortOrder={sig.sortOrder}
1772
1791
  scrollMode={sig.scrollMode}
1792
+ borderVisible={sig.borderVisible}
1773
1793
  sessionId={input.session_id}
1774
1794
  />
1775
1795
  )
@@ -1795,8 +1815,11 @@ const tui: TuiPlugin = async (api: TuiPluginApi) => {
1795
1815
  const [scrollMode, setScrollMode] = createSignal<ScrollMode>(
1796
1816
  String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel"
1797
1817
  )
1818
+ const [borderVisible, setBorderVisible] = createSignal<boolean>(
1819
+ (api.kv.get(`${KV_PREFIX}.border`, false) as boolean) === true
1820
+ )
1798
1821
 
1799
- const signals: SharedSignals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, sessionId: "" }
1822
+ const signals: SharedSignals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, borderVisible, setBorderVisible, sessionId: "" }
1800
1823
 
1801
1824
  api.slots.register(createSidebarSlot(api, signals))
1802
1825
 
@@ -2070,6 +2093,20 @@ const tui: TuiPlugin = async (api: TuiPluginApi) => {
2070
2093
  ))
2071
2094
  },
2072
2095
  },
2096
+ {
2097
+ title: "SubAgent Magazine: Border",
2098
+ value: "subagent-border",
2099
+ description: "Show or hide the panel border",
2100
+ slash: { name: "subagent-border" },
2101
+ onSelect: (dialog) => {
2102
+ const t = createT(() => signals.lang())
2103
+ const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, false))
2104
+ api.kv.set(`${KV_PREFIX}.border`, !cur)
2105
+ signals.setBorderVisible(!cur)
2106
+ api.ui.toast({ message: !cur ? t("borderShown") : t("borderHidden") })
2107
+ dialog?.clear()
2108
+ },
2109
+ },
2073
2110
  ])
2074
2111
  }
2075
2112