opencode-subagent-magazine 1.6.0-beta.2 → 1.6.0-beta.4
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 +3 -1
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/core/kv.d.ts +1 -0
- package/dist/core/kv.js +1 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.js +8 -0
- package/dist/index.js +17 -2
- package/dist/panel/SubAgentPanel.d.ts +1 -0
- package/dist/panel/SubAgentPanel.js +54 -37
- package/dist/panel/entry-map.d.ts +6 -0
- package/dist/panel/entry-map.js +25 -0
- package/dist/tui.js +128 -56
- package/dist/v2/commands.js +13 -0
- package/dist/v2/index.js +3 -2
- package/dist/v2.js +124 -56
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/core/color.ts +70 -70
- package/src/core/format.ts +61 -61
- package/src/core/index.ts +6 -6
- package/src/core/kv.ts +37 -36
- package/src/core/state-machine.ts +46 -46
- package/src/core/types.ts +2 -0
- package/src/core/usage.ts +16 -16
- package/src/i18n.ts +8 -0
- package/src/index.tsx +472 -454
- package/src/panel/SubAgentPanel.tsx +59 -37
- package/src/panel/entry-map.ts +39 -0
- package/src/panel/store.ts +8 -8
- package/src/v2/commands.ts +264 -251
- package/src/v2/index.tsx +102 -98
- package/src/v2/theme.ts +14 -14
- package/src/v2/types.ts +165 -165
- package/tui/index.js +11 -11
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
|
-
-
|
|
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>
|
package/dist/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PLUGIN_VERSION = "1.6.0-beta.
|
|
1
|
+
export declare const PLUGIN_VERSION = "1.6.0-beta.4";
|
package/dist/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION = "1.6.0-beta.
|
|
2
|
+
export const PLUGIN_VERSION = "1.6.0-beta.4";
|
package/dist/core/kv.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const SETTING_KEYS: {
|
|
|
14
14
|
readonly scrollMode: "subagent_magazine.scroll_mode";
|
|
15
15
|
readonly open: "subagent_magazine.open";
|
|
16
16
|
readonly ttlDays: "subagent_magazine.ttl_days";
|
|
17
|
+
readonly border: "subagent_magazine.border";
|
|
17
18
|
};
|
|
18
19
|
export declare function loadSessionData(kv: KVApi): Record<string, SessionRecord>;
|
|
19
20
|
export declare function saveSessionData(kv: KVApi, data: Record<string, SessionRecord>): void;
|
package/dist/core/kv.js
CHANGED
package/dist/core/types.d.ts
CHANGED
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
|
@@ -13,7 +13,7 @@ function createSidebarSlot(api, panelApi, sig) {
|
|
|
13
13
|
slots: {
|
|
14
14
|
sidebar_content(ctx, input) {
|
|
15
15
|
sig.sessionId = input.session_id;
|
|
16
|
-
return (_jsx(SubAgentPanel, { api: panelApi, theme: ctx.theme.current, lang: sig.lang, maxEntries: sig.maxEntries, sortOrder: sig.sortOrder, scrollMode: sig.scrollMode, sessionId: input.session_id }));
|
|
16
|
+
return (_jsx(SubAgentPanel, { api: panelApi, theme: ctx.theme.current, lang: sig.lang, maxEntries: sig.maxEntries, sortOrder: sig.sortOrder, scrollMode: sig.scrollMode, borderVisible: sig.borderVisible, sessionId: input.session_id }));
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
};
|
|
@@ -26,7 +26,8 @@ const tui = async (api) => {
|
|
|
26
26
|
const [maxEntries, setMaxEntries] = createSignal(parseInt(String(api.kv.get(`${KV_PREFIX}.max_entries`, "10")), 10) || 10);
|
|
27
27
|
const [sortOrder, setSortOrder] = createSignal(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
|
|
28
28
|
const [scrollMode, setScrollMode] = createSignal(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
|
|
29
|
-
const
|
|
29
|
+
const [borderVisible, setBorderVisible] = createSignal(api.kv.get(`${KV_PREFIX}.border`, false) === true);
|
|
30
|
+
const signals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, borderVisible, setBorderVisible, sessionId: "" };
|
|
30
31
|
// ── V1 PanelApi adapter: wraps the V1 host API into the shared panel contract ──
|
|
31
32
|
const v1Api = {
|
|
32
33
|
kv: api.kv,
|
|
@@ -416,6 +417,20 @@ const tui = async (api) => {
|
|
|
416
417
|
} })));
|
|
417
418
|
},
|
|
418
419
|
},
|
|
420
|
+
{
|
|
421
|
+
title: "SubAgent Magazine: Border",
|
|
422
|
+
value: "subagent-border",
|
|
423
|
+
description: "Show or hide the panel border",
|
|
424
|
+
slash: { name: "subagent-border" },
|
|
425
|
+
onSelect: (dialog) => {
|
|
426
|
+
const t = createT(() => signals.lang());
|
|
427
|
+
const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, false));
|
|
428
|
+
api.kv.set(`${KV_PREFIX}.border`, !cur);
|
|
429
|
+
signals.setBorderVisible(!cur);
|
|
430
|
+
api.ui.toast({ message: !cur ? t("borderShown") : t("borderHidden") });
|
|
431
|
+
dialog?.clear();
|
|
432
|
+
},
|
|
433
|
+
},
|
|
419
434
|
]);
|
|
420
435
|
};
|
|
421
436
|
const mod = {
|
|
@@ -9,6 +9,7 @@ import { rgb, desaturateTo, dimColor, FALLBACK, MAX_SAT } from "../core/color";
|
|
|
9
9
|
import { KV_PREFIX } from "../core/kv";
|
|
10
10
|
import { globalEntryCache, clearTick } from "./store";
|
|
11
11
|
import { isDirectChildSession } from "./session-routing";
|
|
12
|
+
import { findSubEntryKey, upsertSubEntry } from "./entry-map";
|
|
12
13
|
/** Entry line left prefix: icon + space + status dot + space */
|
|
13
14
|
const LEFT_PAD = 4;
|
|
14
15
|
/** Detail row indent: two spaces */
|
|
@@ -51,15 +52,16 @@ export function SubAgentPanel(props) {
|
|
|
51
52
|
return { parentSid: sid, isChild: false };
|
|
52
53
|
};
|
|
53
54
|
const loadEntries = (sid) => {
|
|
54
|
-
|
|
55
|
+
let m = new Map();
|
|
55
56
|
try {
|
|
56
57
|
const { parentSid, isChild } = resolveParent(sid);
|
|
57
58
|
const rec = loadSessionData()[parentSid];
|
|
58
59
|
if (rec) {
|
|
59
60
|
const source = isChild ? rec.children?.[sid]?.entries : rec.entries;
|
|
60
61
|
if (source) {
|
|
61
|
-
for (const e of source)
|
|
62
|
-
m
|
|
62
|
+
for (const e of source) {
|
|
63
|
+
m = upsertSubEntry(m, e);
|
|
64
|
+
}
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -232,20 +234,7 @@ export function SubAgentPanel(props) {
|
|
|
232
234
|
let disposed = false;
|
|
233
235
|
// ── upsert ──
|
|
234
236
|
const upsertEntry = (partial) => {
|
|
235
|
-
setEntryMap((prev) =>
|
|
236
|
-
const existing = prev.get(partial.id);
|
|
237
|
-
const next = new Map(prev);
|
|
238
|
-
const nowTs = Date.now();
|
|
239
|
-
const e = partial.status;
|
|
240
|
-
const ended = e === "done" || e === "error" || e === "cancelled";
|
|
241
|
-
next.set(partial.id, {
|
|
242
|
-
...(existing ?? { startedAt: nowTs }),
|
|
243
|
-
...partial,
|
|
244
|
-
startedAt: existing?.startedAt || partial.startedAt || nowTs,
|
|
245
|
-
endedAt: ended ? (existing?.endedAt || nowTs) : undefined,
|
|
246
|
-
});
|
|
247
|
-
return next;
|
|
248
|
-
});
|
|
237
|
+
setEntryMap((prev) => upsertSubEntry(prev, partial));
|
|
249
238
|
};
|
|
250
239
|
// ── cancel helpers ──
|
|
251
240
|
const isDescendantOf = (childId, rootId) => {
|
|
@@ -370,9 +359,21 @@ export function SubAgentPanel(props) {
|
|
|
370
359
|
const id = `tool:${String(part.id ?? "")}`;
|
|
371
360
|
if (!part.id)
|
|
372
361
|
return;
|
|
373
|
-
const
|
|
362
|
+
const stMeta = st?.metadata;
|
|
363
|
+
const errorSid = stMeta?.session_id !== undefined ? String(stMeta.session_id)
|
|
364
|
+
: stMeta?.sessionId !== undefined ? String(stMeta.sessionId)
|
|
365
|
+
: undefined;
|
|
366
|
+
const key = findSubEntryKey(entryMap(), id, errorSid);
|
|
367
|
+
const existing = key ? entryMap().get(key) : undefined;
|
|
374
368
|
if (existing) {
|
|
375
|
-
upsertEntry({
|
|
369
|
+
upsertEntry({
|
|
370
|
+
id: existing.id,
|
|
371
|
+
title: existing.title,
|
|
372
|
+
agent: existing.agent,
|
|
373
|
+
prompt: existing.prompt,
|
|
374
|
+
sessionId: existing.sessionId ?? errorSid,
|
|
375
|
+
status: "error",
|
|
376
|
+
});
|
|
376
377
|
}
|
|
377
378
|
return;
|
|
378
379
|
}
|
|
@@ -778,7 +779,12 @@ export function SubAgentPanel(props) {
|
|
|
778
779
|
continue;
|
|
779
780
|
const st = part.state;
|
|
780
781
|
const rawStatus = String(st?.status ?? "");
|
|
781
|
-
const
|
|
782
|
+
const scanStMeta = st?.metadata;
|
|
783
|
+
const scanSubSid = scanStMeta?.session_id !== undefined ? String(scanStMeta.session_id)
|
|
784
|
+
: scanStMeta?.sessionId !== undefined ? String(scanStMeta.sessionId)
|
|
785
|
+
: undefined;
|
|
786
|
+
const existingKey = findSubEntryKey(next, id, scanSubSid);
|
|
787
|
+
const exists = existingKey ? next.get(existingKey) : undefined;
|
|
782
788
|
// 已手动清除的条目:scan 发现但不在内存 → 跳过重建
|
|
783
789
|
if (!exists && clearedIds.has(id))
|
|
784
790
|
continue;
|
|
@@ -789,7 +795,7 @@ export function SubAgentPanel(props) {
|
|
|
789
795
|
// "error": only update existing, never create a new entry
|
|
790
796
|
if (rawStatus === "error") {
|
|
791
797
|
if (exists && exists.status === "running") {
|
|
792
|
-
next.set(id, { ...exists, status: "error", endedAt: Date.now() });
|
|
798
|
+
next.set(existingKey ?? id, { ...exists, status: "error", endedAt: Date.now() });
|
|
793
799
|
}
|
|
794
800
|
continue;
|
|
795
801
|
}
|
|
@@ -805,7 +811,7 @@ export function SubAgentPanel(props) {
|
|
|
805
811
|
status = "running";
|
|
806
812
|
}
|
|
807
813
|
// Already settled → skip
|
|
808
|
-
if (exists && exists.status !== "running" && exists.status !== "cancel_requested")
|
|
814
|
+
if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested")
|
|
809
815
|
continue;
|
|
810
816
|
// Running entry with no explicit status improvement from part:
|
|
811
817
|
// try message-level heuristics first, then time-based fallback.
|
|
@@ -834,15 +840,12 @@ export function SubAgentPanel(props) {
|
|
|
834
840
|
const desc = input?.description !== undefined ? String(input.description) : "";
|
|
835
841
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
836
842
|
let tokens;
|
|
837
|
-
const scanStMeta2 = st?.metadata;
|
|
838
|
-
const scanSubSid = scanStMeta2?.session_id !== undefined ? String(scanStMeta2.session_id)
|
|
839
|
-
: scanStMeta2?.sessionId !== undefined ? String(scanStMeta2.sessionId)
|
|
840
|
-
: undefined;
|
|
841
843
|
if (scanSubSid)
|
|
842
844
|
tokens = props.api.usage.readSessionTokens(scanSubSid);
|
|
843
845
|
const ended = status === "done"; // "error" handled above, never reaches here
|
|
844
|
-
|
|
845
|
-
|
|
846
|
+
const entryKey = existingKey ?? id;
|
|
847
|
+
next.set(entryKey, {
|
|
848
|
+
id: exists?.id ?? id, title, agent, prompt,
|
|
846
849
|
// Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
|
|
847
850
|
tokens: exists?.tokens ?? tokens,
|
|
848
851
|
sessionId: exists?.sessionId ?? scanSubSid,
|
|
@@ -850,6 +853,8 @@ export function SubAgentPanel(props) {
|
|
|
850
853
|
startedAt: exists?.startedAt || Date.now(),
|
|
851
854
|
endedAt: ended ? (exists?.endedAt || Date.now()) : undefined,
|
|
852
855
|
});
|
|
856
|
+
if (entryKey !== id)
|
|
857
|
+
next.delete(id);
|
|
853
858
|
}
|
|
854
859
|
}
|
|
855
860
|
}
|
|
@@ -1012,7 +1017,19 @@ export function SubAgentPanel(props) {
|
|
|
1012
1017
|
return next;
|
|
1013
1018
|
});
|
|
1014
1019
|
};
|
|
1015
|
-
|
|
1020
|
+
/** Horizontal space eaten by the panel border (1+1) and its padding (2+2). */
|
|
1021
|
+
const gutter = createMemo(() => (props.borderVisible() ? 6 : 0));
|
|
1022
|
+
const sep = () => "\u2500".repeat(Math.max(1, panelWidth() - gutter()));
|
|
1023
|
+
// The border toggling shifts the box dimensions, which may not reliably
|
|
1024
|
+
// re-fire onSizeChange across (re)mount cycles; re-sync panelWidth with the
|
|
1025
|
+
// live box so the content width stays correct.
|
|
1026
|
+
createEffect(() => {
|
|
1027
|
+
props.borderVisible();
|
|
1028
|
+
if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
|
|
1029
|
+
const w = Math.max(20, boxEl.width);
|
|
1030
|
+
setPanelWidth((prev) => (prev === w ? prev : w));
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1016
1033
|
// ── expanded detail right-align ──
|
|
1017
1034
|
const expandedMaxLabelW = createMemo(() => {
|
|
1018
1035
|
const labels = [
|
|
@@ -1022,7 +1039,7 @@ export function SubAgentPanel(props) {
|
|
|
1022
1039
|
return Math.max(...labels.map(l => visualWidth(l + ": ")));
|
|
1023
1040
|
});
|
|
1024
1041
|
const expandedPad = (label) => Math.max(0, expandedMaxLabelW() - visualWidth(label + ": "));
|
|
1025
|
-
const expandedValAvail = () => Math.max(6, panelWidth() - INDENT - expandedMaxLabelW());
|
|
1042
|
+
const expandedValAvail = () => Math.max(6, panelWidth() - gutter() - INDENT - expandedMaxLabelW());
|
|
1026
1043
|
// ── header parts for colored spans ──
|
|
1027
1044
|
const summaryParts = createMemo(() => {
|
|
1028
1045
|
if (!anyEntry())
|
|
@@ -1057,7 +1074,7 @@ export function SubAgentPanel(props) {
|
|
|
1057
1074
|
return false;
|
|
1058
1075
|
const icon = "\u25bc";
|
|
1059
1076
|
const need = visualWidth(icon) + 1 + visualWidth(t("panel.title")) + versionW + summaryCols();
|
|
1060
|
-
return need <= panelWidth();
|
|
1077
|
+
return need <= panelWidth() - gutter();
|
|
1061
1078
|
});
|
|
1062
1079
|
const leftCols = createMemo(() => {
|
|
1063
1080
|
const icon = open() ? "\u25bc" : "\u25b6";
|
|
@@ -1069,11 +1086,11 @@ export function SubAgentPanel(props) {
|
|
|
1069
1086
|
const spacerCols = createMemo(() => {
|
|
1070
1087
|
if (!anyEntry())
|
|
1071
1088
|
return 0;
|
|
1072
|
-
return Math.max(0, panelWidth() - leftCols() - summaryCols());
|
|
1089
|
+
return Math.max(0, panelWidth() - gutter() - leftCols() - summaryCols());
|
|
1073
1090
|
});
|
|
1074
|
-
const valueCols = (label) => Math.max(4, panelWidth() - INDENT - visualWidth(label + ": "));
|
|
1091
|
+
const valueCols = (label) => Math.max(4, panelWidth() - gutter() - INDENT - visualWidth(label + ": "));
|
|
1075
1092
|
// ── render ──
|
|
1076
|
-
return (_jsxs("box", { border:
|
|
1093
|
+
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: () => {
|
|
1077
1094
|
const w = boxEl ? Math.max(20, boxEl.width ?? 0) : 28;
|
|
1078
1095
|
setPanelWidth((prev) => (prev === w ? prev : w));
|
|
1079
1096
|
}, children: [_jsxs("text", { onMouseUp: () => {
|
|
@@ -1165,7 +1182,7 @@ export function SubAgentPanel(props) {
|
|
|
1165
1182
|
w += visualWidth(tk);
|
|
1166
1183
|
return w;
|
|
1167
1184
|
};
|
|
1168
|
-
const labelAvail = () => Math.max(6, panelWidth() - LEFT_PAD - suffixW());
|
|
1185
|
+
const labelAvail = () => Math.max(6, panelWidth() - gutter() - LEFT_PAD - suffixW());
|
|
1169
1186
|
const labelText = () => {
|
|
1170
1187
|
const max = labelAvail();
|
|
1171
1188
|
const text = entry.title || entry.agent;
|
|
@@ -1201,7 +1218,7 @@ export function SubAgentPanel(props) {
|
|
|
1201
1218
|
const cancelLabel = () => ` ${t("cancel.label")}`;
|
|
1202
1219
|
const dismissLabel = () => ` ${t("dismiss.label")}`;
|
|
1203
1220
|
const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0);
|
|
1204
|
-
const spacerW = () => Math.max(1, panelWidth() - openW() - rightW - 2);
|
|
1221
|
+
const spacerW = () => Math.max(1, panelWidth() - gutter() - openW() - rightW - 2);
|
|
1205
1222
|
return (_jsxs("box", { flexDirection: "row", children: [_jsx(Show, { when: entry.sessionId, children: _jsxs("text", { onMouseOver: () => setHoveredOpen(entry.id), onMouseOut: () => setHoveredOpen(undefined), onMouseUp: () => {
|
|
1206
1223
|
if (entry.sessionId) {
|
|
1207
1224
|
props.api.route.navigateSession(entry.sessionId);
|
|
@@ -1219,7 +1236,7 @@ export function SubAgentPanel(props) {
|
|
|
1219
1236
|
const right = props.sortOrder() === "desc"
|
|
1220
1237
|
? `\u2191 ${t("scroll.top")}`
|
|
1221
1238
|
: `\u2193 ${t("scroll.bottom")}`;
|
|
1222
|
-
const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0;
|
|
1239
|
+
const pad = showTop ? Math.max(1, panelWidth() - gutter() - visualWidth(left) - visualWidth(right)) : 0;
|
|
1223
1240
|
return (_jsxs("box", { flexDirection: "row", children: [_jsx("text", { onMouseOver: () => showMore && setHoveredMoreBelow(true), onMouseOut: () => setHoveredMoreBelow(false), onMouseUp: () => {
|
|
1224
1241
|
if (!showMore)
|
|
1225
1242
|
return;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SubEntry } from "../core/types";
|
|
2
|
+
export type SubEntryPatch = Omit<SubEntry, "startedAt" | "endedAt"> & {
|
|
3
|
+
startedAt?: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function findSubEntryKey(entries: ReadonlyMap<string, SubEntry>, id: string, sessionId?: string): string | undefined;
|
|
6
|
+
export declare function upsertSubEntry(entries: ReadonlyMap<string, SubEntry>, partial: SubEntryPatch, now?: number): Map<string, SubEntry>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function findSubEntryKey(entries, id, sessionId) {
|
|
2
|
+
if (sessionId) {
|
|
3
|
+
for (const [key, entry] of entries) {
|
|
4
|
+
if (entry.sessionId === sessionId)
|
|
5
|
+
return key;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return entries.has(id) ? id : undefined;
|
|
9
|
+
}
|
|
10
|
+
export function upsertSubEntry(entries, partial, now = Date.now()) {
|
|
11
|
+
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id;
|
|
12
|
+
const existing = entries.get(key);
|
|
13
|
+
const next = new Map(entries);
|
|
14
|
+
if (key !== partial.id)
|
|
15
|
+
next.delete(partial.id);
|
|
16
|
+
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled";
|
|
17
|
+
next.set(key, {
|
|
18
|
+
...(existing ?? { startedAt: now }),
|
|
19
|
+
...partial,
|
|
20
|
+
id: existing?.id ?? partial.id,
|
|
21
|
+
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
22
|
+
endedAt: ended ? (existing?.endedAt || now) : undefined,
|
|
23
|
+
});
|
|
24
|
+
return next;
|
|
25
|
+
}
|