opencode-subagent-magazine 1.1.3 → 1.2.1-beta.0
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/LICENSE +21 -21
- package/README.md +187 -179
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/index.js +169 -8
- package/dist/server.d.ts +3 -0
- package/dist/server.js +6 -0
- package/dist/tui.js +412 -233
- package/install.mjs +103 -103
- package/package.json +62 -63
- package/src/_version.ts +1 -1
- package/src/index.tsx +1782 -1604
- package/src/server.ts +10 -0
package/dist/index.js
CHANGED
|
@@ -21,10 +21,15 @@ const I18N = {
|
|
|
21
21
|
"cost.label": "费用",
|
|
22
22
|
"scroll.more": "更多",
|
|
23
23
|
"scroll.top": "回顶",
|
|
24
|
+
"scroll.bottom": "回底",
|
|
24
25
|
"dismiss.label": "标记完成",
|
|
25
26
|
"status.running": "运行中",
|
|
26
27
|
"status.done": "已完成",
|
|
27
28
|
"status.error": "错误",
|
|
29
|
+
"order.desc": "降序(最新在前)",
|
|
30
|
+
"order.asc": "升序(最早在前)",
|
|
31
|
+
"scroll.wheel": "滚轮翻页",
|
|
32
|
+
"scroll.click": "点击翻页",
|
|
28
33
|
},
|
|
29
34
|
en: {
|
|
30
35
|
"panel.title": "SubAgent",
|
|
@@ -40,10 +45,15 @@ const I18N = {
|
|
|
40
45
|
"cost.label": "cost",
|
|
41
46
|
"scroll.more": "more",
|
|
42
47
|
"scroll.top": "Top",
|
|
48
|
+
"scroll.bottom": "Bottom",
|
|
43
49
|
"dismiss.label": "dismiss",
|
|
44
50
|
"status.running": "running",
|
|
45
51
|
"status.done": "done",
|
|
46
52
|
"status.error": "error",
|
|
53
|
+
"order.desc": "Desc (newest first)",
|
|
54
|
+
"order.asc": "Asc (oldest first)",
|
|
55
|
+
"scroll.wheel": "Wheel Scroll",
|
|
56
|
+
"scroll.click": "Click Scroll",
|
|
47
57
|
},
|
|
48
58
|
};
|
|
49
59
|
function detectLang() {
|
|
@@ -325,6 +335,8 @@ function SubAgentPanel(props) {
|
|
|
325
335
|
const [hoveredOpen, setHoveredOpen] = createSignal(undefined);
|
|
326
336
|
const [hoveredDismiss, setHoveredDismiss] = createSignal(undefined);
|
|
327
337
|
const [hoveredTop, setHoveredTop] = createSignal(false);
|
|
338
|
+
const [hoveredMoreAbove, setHoveredMoreAbove] = createSignal(false);
|
|
339
|
+
const [hoveredMoreBelow, setHoveredMoreBelow] = createSignal(false);
|
|
328
340
|
const [scrollOffset, setScrollOffset] = createSignal((() => { try {
|
|
329
341
|
return loadSessionData()[props.sessionId]?.scroll ?? 0;
|
|
330
342
|
}
|
|
@@ -982,7 +994,11 @@ function SubAgentPanel(props) {
|
|
|
982
994
|
// ── derived signals ──
|
|
983
995
|
// Stable list — only changes when entryMap changes
|
|
984
996
|
const entryList = createMemo(() => {
|
|
985
|
-
|
|
997
|
+
const entries = [...entryMap().values()];
|
|
998
|
+
if (props.sortOrder() === "desc") {
|
|
999
|
+
return entries.sort((a, b) => b.startedAt - a.startedAt);
|
|
1000
|
+
}
|
|
1001
|
+
return entries.sort((a, b) => a.startedAt - b.startedAt);
|
|
986
1002
|
});
|
|
987
1003
|
const max = props.maxEntries;
|
|
988
1004
|
const clampedOffset = createMemo(() => {
|
|
@@ -995,6 +1011,56 @@ function SubAgentPanel(props) {
|
|
|
995
1011
|
const visibleList = createMemo(() => entryList().slice(clampedOffset(), clampedOffset() + max()));
|
|
996
1012
|
const hiddenAbove = createMemo(() => clampedOffset());
|
|
997
1013
|
const hiddenBelow = createMemo(() => Math.max(0, entryList().length - clampedOffset() - max()));
|
|
1014
|
+
// Drop hover state when ↑ more disappears (hiddenAbove hits zero)
|
|
1015
|
+
createEffect(() => {
|
|
1016
|
+
if (hiddenAbove() === 0)
|
|
1017
|
+
setHoveredMoreAbove(false);
|
|
1018
|
+
});
|
|
1019
|
+
// Reset scroll on sort order change: jump to newest in view
|
|
1020
|
+
let sortInitialized = false;
|
|
1021
|
+
createEffect(() => {
|
|
1022
|
+
props.sortOrder();
|
|
1023
|
+
if (!sortInitialized) {
|
|
1024
|
+
sortInitialized = true;
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
const total = untrack(() => entryList().length);
|
|
1028
|
+
const m = untrack(() => max());
|
|
1029
|
+
const target = props.sortOrder() === "desc" ? 0 : Math.max(0, total - m);
|
|
1030
|
+
setScrollOffset(target);
|
|
1031
|
+
setTimeout(() => {
|
|
1032
|
+
try {
|
|
1033
|
+
persistScroll(props.sessionId, target);
|
|
1034
|
+
}
|
|
1035
|
+
catch { }
|
|
1036
|
+
}, 0);
|
|
1037
|
+
});
|
|
1038
|
+
// When new entries arrive while viewing the newest end, keep the view at newest
|
|
1039
|
+
let prevEntryCount = 0;
|
|
1040
|
+
createEffect(() => {
|
|
1041
|
+
const total = entryList().length;
|
|
1042
|
+
if (prevEntryCount === 0) {
|
|
1043
|
+
prevEntryCount = total;
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (total === prevEntryCount)
|
|
1047
|
+
return;
|
|
1048
|
+
const m = max();
|
|
1049
|
+
const wasAtNewest = props.sortOrder() === "desc"
|
|
1050
|
+
? untrack(() => scrollOffset() === 0)
|
|
1051
|
+
: untrack(() => scrollOffset() >= prevEntryCount - m);
|
|
1052
|
+
prevEntryCount = total;
|
|
1053
|
+
if (wasAtNewest) {
|
|
1054
|
+
const target = props.sortOrder() === "desc" ? 0 : Math.max(0, total - m);
|
|
1055
|
+
setScrollOffset(target);
|
|
1056
|
+
setTimeout(() => {
|
|
1057
|
+
try {
|
|
1058
|
+
persistScroll(props.sessionId, target);
|
|
1059
|
+
}
|
|
1060
|
+
catch { }
|
|
1061
|
+
}, 0);
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
998
1064
|
const entries = createMemo(() => {
|
|
999
1065
|
const nowVal = now();
|
|
1000
1066
|
return entryList().map((e) => ({
|
|
@@ -1107,6 +1173,8 @@ function SubAgentPanel(props) {
|
|
|
1107
1173
|
});
|
|
1108
1174
|
bump();
|
|
1109
1175
|
}, children: [_jsx("span", { style: { fg: pal().muted }, children: renderTick() >= 0 && open() ? "\u25bc " : "\u25b6 " }), _jsx("span", { style: { fg: pal().primary }, children: t("panel.title") }), _jsx(Show, { when: showVersion(), children: _jsx("span", { style: { fg: dimColor(pal().muted, 0.75) }, children: versionText }) }), anyEntry() ? (_jsxs(_Fragment, { children: [_jsx("span", { style: { fg: pal().muted }, children: " ".repeat(spacerCols()) }), _jsx("span", { style: { fg: pal().success }, children: summaryParts().done }), runningCount() > 0 && (_jsxs("span", { style: { fg: pal().warning }, children: [" ", summaryParts().running] })), errCount() > 0 && (_jsxs("span", { style: { fg: pal().error }, children: [" ", summaryParts().err] })), summaryParts().duration ? (_jsxs("span", { style: { fg: pal().muted }, children: [" ", summaryParts().duration] })) : null, summaryParts().cost ? (_jsxs("span", { style: { fg: pal().warning }, children: [" ", summaryParts().cost] })) : null] })) : null] }), _jsxs(Show, { when: open(), children: [_jsx("text", { fg: pal().muted, children: sep() }), _jsx(Show, { when: anyEntry(), fallback: _jsxs("text", { style: { fg: pal().muted }, children: [" ", "> ", t("status.none"), " "] }), children: _jsxs("box", { onMouseScroll: (e) => {
|
|
1176
|
+
if (props.scrollMode() === "click")
|
|
1177
|
+
return;
|
|
1110
1178
|
const total = entryList().length;
|
|
1111
1179
|
const m = max();
|
|
1112
1180
|
if (total <= m)
|
|
@@ -1120,7 +1188,29 @@ function SubAgentPanel(props) {
|
|
|
1120
1188
|
catch { }
|
|
1121
1189
|
return next;
|
|
1122
1190
|
});
|
|
1123
|
-
}, children: [_jsx(Show, { when: hiddenAbove() > 0, children:
|
|
1191
|
+
}, children: [_jsx(Show, { when: hiddenAbove() > 0, children: _jsx("text", { onMouseOver: () => setHoveredMoreAbove(true), onMouseOut: () => setHoveredMoreAbove(false), onMouseUp: () => {
|
|
1192
|
+
const total = entryList().length;
|
|
1193
|
+
const m = max();
|
|
1194
|
+
if (total <= m)
|
|
1195
|
+
return;
|
|
1196
|
+
const next = Math.max(0, scrollOffset() - m);
|
|
1197
|
+
if (next === 0) {
|
|
1198
|
+
setTimeout(() => {
|
|
1199
|
+
setScrollOffset(next);
|
|
1200
|
+
try {
|
|
1201
|
+
persistScroll(props.sessionId, next);
|
|
1202
|
+
}
|
|
1203
|
+
catch { }
|
|
1204
|
+
}, 0);
|
|
1205
|
+
}
|
|
1206
|
+
else {
|
|
1207
|
+
setScrollOffset(next);
|
|
1208
|
+
try {
|
|
1209
|
+
persistScroll(props.sessionId, next);
|
|
1210
|
+
}
|
|
1211
|
+
catch { }
|
|
1212
|
+
}
|
|
1213
|
+
}, children: _jsxs("span", { style: { fg: hoveredMoreAbove() ? pal().warning : pal().muted }, children: [" ", "\u2191 ", hiddenAbove(), " ", t("scroll.more")] }) }) }), _jsx(For, { each: visibleList(), children: (entry) => {
|
|
1124
1214
|
const isExpanded = () => expanded() === entry.id;
|
|
1125
1215
|
const isRunning = entry.status === "running";
|
|
1126
1216
|
const isError = entry.status === "error";
|
|
@@ -1178,13 +1268,40 @@ function SubAgentPanel(props) {
|
|
|
1178
1268
|
upsertEntry({ id: entry.id, title: entry.title, agent: entry.agent, prompt: entry.prompt, status: "done" });
|
|
1179
1269
|
}, children: _jsx("span", { style: { fg: hoveredDismiss() === entry.id ? pal().warning : pal().muted }, children: dismissLabel() }) })] }) })] }));
|
|
1180
1270
|
})() })] })] }));
|
|
1181
|
-
} }), _jsx(Show, { when: hiddenBelow() > 0 || scrollOffset() > 0, children: (() => {
|
|
1271
|
+
} }), _jsx(Show, { when: hiddenBelow() > 0 || (props.sortOrder() === "desc" ? scrollOffset() > 0 : entryList().length > max() && clampedOffset() < entryList().length - max()), children: (() => {
|
|
1182
1272
|
const showMore = hiddenBelow() > 0;
|
|
1183
|
-
const showTop =
|
|
1273
|
+
const showTop = props.sortOrder() === "desc"
|
|
1274
|
+
? scrollOffset() > 0
|
|
1275
|
+
: entryList().length > max() && clampedOffset() < entryList().length - max();
|
|
1184
1276
|
const left = showMore ? ` \u2193 ${hiddenBelow()} ${t("scroll.more")}` : " ";
|
|
1185
|
-
const right =
|
|
1277
|
+
const right = props.sortOrder() === "desc"
|
|
1278
|
+
? `\u2191 ${t("scroll.top")}`
|
|
1279
|
+
: `\u2193 ${t("scroll.bottom")}`;
|
|
1186
1280
|
const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0;
|
|
1187
|
-
return (_jsxs("box", { flexDirection: "row", children: [_jsx("text", {
|
|
1281
|
+
return (_jsxs("box", { flexDirection: "row", children: [_jsx("text", { onMouseOver: () => showMore && setHoveredMoreBelow(true), onMouseOut: () => setHoveredMoreBelow(false), onMouseUp: () => {
|
|
1282
|
+
if (!showMore)
|
|
1283
|
+
return;
|
|
1284
|
+
const total = entryList().length;
|
|
1285
|
+
const m = max();
|
|
1286
|
+
if (total <= m)
|
|
1287
|
+
return;
|
|
1288
|
+
setScrollOffset((prev) => Math.min(total - m, prev + m));
|
|
1289
|
+
try {
|
|
1290
|
+
persistScroll(props.sessionId, scrollOffset());
|
|
1291
|
+
}
|
|
1292
|
+
catch { }
|
|
1293
|
+
setHoveredMoreBelow(false);
|
|
1294
|
+
}, children: _jsx("span", { style: { fg: showMore && hoveredMoreBelow() ? pal().warning : pal().muted }, children: left }) }), showTop ? (_jsxs(_Fragment, { children: [_jsx("text", { style: { fg: pal().muted }, children: " ".repeat(pad) }), _jsx("text", { onMouseOver: () => setHoveredTop(true), onMouseOut: () => setHoveredTop(false), onMouseUp: () => {
|
|
1295
|
+
const total = entryList().length;
|
|
1296
|
+
const m = max();
|
|
1297
|
+
if (props.sortOrder() === "desc") {
|
|
1298
|
+
setScrollOffset(0);
|
|
1299
|
+
}
|
|
1300
|
+
else {
|
|
1301
|
+
setScrollOffset(Math.max(0, total - m));
|
|
1302
|
+
}
|
|
1303
|
+
setHoveredTop(false);
|
|
1304
|
+
}, children: _jsx("span", { style: { fg: hoveredTop() ? pal().warning : pal().muted }, children: right }) })] })) : null] }));
|
|
1188
1305
|
})() })] }) })] })] }));
|
|
1189
1306
|
}
|
|
1190
1307
|
function createSidebarSlot(api, sig) {
|
|
@@ -1193,7 +1310,7 @@ function createSidebarSlot(api, sig) {
|
|
|
1193
1310
|
slots: {
|
|
1194
1311
|
sidebar_content(ctx, input) {
|
|
1195
1312
|
sig.sessionId = input.session_id;
|
|
1196
|
-
return (_jsx(SubAgentPanel, { theme: ctx.theme.current, api: api, lang: sig.lang, maxEntries: sig.maxEntries, sessionId: input.session_id }));
|
|
1313
|
+
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 }));
|
|
1197
1314
|
},
|
|
1198
1315
|
},
|
|
1199
1316
|
};
|
|
@@ -1205,7 +1322,9 @@ const tui = async (api) => {
|
|
|
1205
1322
|
const initialLang = stored === "zh" || stored === "en" ? stored : detectLang();
|
|
1206
1323
|
const [lang, setLang] = createSignal(initialLang);
|
|
1207
1324
|
const [maxEntries, setMaxEntries] = createSignal(parseInt(String(api.kv.get(`${KV_PREFIX}.max_entries`, "10")), 10) || 10);
|
|
1208
|
-
const
|
|
1325
|
+
const [sortOrder, setSortOrder] = createSignal(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
|
|
1326
|
+
const [scrollMode, setScrollMode] = createSignal(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
|
|
1327
|
+
const signals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, sessionId: "" };
|
|
1209
1328
|
api.slots.register(createSidebarSlot(api, signals));
|
|
1210
1329
|
// ── slash command: /subagent-lang ──
|
|
1211
1330
|
api.command?.register(() => [
|
|
@@ -1229,6 +1348,48 @@ const tui = async (api) => {
|
|
|
1229
1348
|
} })));
|
|
1230
1349
|
},
|
|
1231
1350
|
},
|
|
1351
|
+
{
|
|
1352
|
+
title: "SubAgent Magazine: Sort Order",
|
|
1353
|
+
value: "subagent-order",
|
|
1354
|
+
description: "Set sub-agent entry sort order (desc / asc)",
|
|
1355
|
+
slash: { name: "subagent-order" },
|
|
1356
|
+
onSelect: (dialog) => {
|
|
1357
|
+
const t = (k) => I18N[lang()][k] ?? k;
|
|
1358
|
+
dialog?.replace(() => (_jsx(api.ui.DialogSelect, { title: "Sort Order / \u6392\u5E8F\u65B9\u5F0F", options: [
|
|
1359
|
+
{ title: t("order.desc"), value: "desc" },
|
|
1360
|
+
{ title: t("order.asc"), value: "asc" },
|
|
1361
|
+
], onSelect: (opt) => {
|
|
1362
|
+
const o = opt.value;
|
|
1363
|
+
setSortOrder(o);
|
|
1364
|
+
api.kv.set(`${KV_PREFIX}.order`, o);
|
|
1365
|
+
api.ui.toast({
|
|
1366
|
+
message: o === "desc" ? t("order.desc") : t("order.asc"),
|
|
1367
|
+
});
|
|
1368
|
+
dialog?.clear();
|
|
1369
|
+
} })));
|
|
1370
|
+
},
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
title: "SubAgent Magazine: Scroll Mode",
|
|
1374
|
+
value: "subagent-scroll",
|
|
1375
|
+
description: "Set scroll mode (wheel / click)",
|
|
1376
|
+
slash: { name: "subagent-scroll" },
|
|
1377
|
+
onSelect: (dialog) => {
|
|
1378
|
+
const t = (k) => I18N[lang()][k] ?? k;
|
|
1379
|
+
dialog?.replace(() => (_jsx(api.ui.DialogSelect, { title: "Scroll Mode / \u6EDA\u52A8\u6A21\u5F0F", options: [
|
|
1380
|
+
{ title: t("scroll.wheel"), value: "wheel" },
|
|
1381
|
+
{ title: t("scroll.click"), value: "click" },
|
|
1382
|
+
], onSelect: (opt) => {
|
|
1383
|
+
const m = opt.value;
|
|
1384
|
+
setScrollMode(m);
|
|
1385
|
+
api.kv.set(`${KV_PREFIX}.scroll_mode`, m);
|
|
1386
|
+
api.ui.toast({
|
|
1387
|
+
message: m === "wheel" ? t("scroll.wheel") : t("scroll.click"),
|
|
1388
|
+
});
|
|
1389
|
+
dialog?.clear();
|
|
1390
|
+
} })));
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1232
1393
|
{
|
|
1233
1394
|
title: "SubAgent Magazine: Max Entries",
|
|
1234
1395
|
value: "subagent-max",
|
package/dist/server.d.ts
ADDED