opencode-subagent-magazine 1.1.2 → 1.2.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/dist/tui.js ADDED
@@ -0,0 +1,2038 @@
1
+ // src/index.tsx
2
+ import { createTextNode as _$createTextNode } from "@opentui/solid";
3
+ import { insertNode as _$insertNode } from "@opentui/solid";
4
+ import { createComponent as _$createComponent } from "@opentui/solid";
5
+ import { effect as _$effect } from "@opentui/solid";
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
+ import { setProp as _$setProp } from "@opentui/solid";
10
+ import { createElement as _$createElement } from "@opentui/solid";
11
+ import { createMemo, createSignal, createEffect, onMount, onCleanup, untrack, Show, For } from "solid-js";
12
+
13
+ // src/_version.ts
14
+ var PLUGIN_VERSION = "1.2.0";
15
+
16
+ // src/index.tsx
17
+ var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "delegate", "call_omo_agent"]);
18
+ var I18N = {
19
+ zh: {
20
+ "panel.title": "\u5B50\u4EE3\u7406",
21
+ "status.none": "\u6682\u65E0\u5B50\u4EE3\u7406",
22
+ "agent.label": "\u4EE3\u7406",
23
+ "status.label": "\u72B6\u6001",
24
+ "time.label": "\u8017\u65F6",
25
+ "tokens.label": "\u4E0A\u4E0B\u6587",
26
+ "error.label": "\u9519\u8BEF",
27
+ "model.label": "\u6A21\u578B",
28
+ "todo.label": "\u8FDB\u5EA6",
29
+ "open.label": "\u8FDB\u5165\u4F1A\u8BDD",
30
+ "cost.label": "\u8D39\u7528",
31
+ "scroll.more": "\u66F4\u591A",
32
+ "scroll.top": "\u56DE\u9876",
33
+ "scroll.bottom": "\u56DE\u5E95",
34
+ "dismiss.label": "\u6807\u8BB0\u5B8C\u6210",
35
+ "status.running": "\u8FD0\u884C\u4E2D",
36
+ "status.done": "\u5DF2\u5B8C\u6210",
37
+ "status.error": "\u9519\u8BEF",
38
+ "order.desc": "\u964D\u5E8F\uFF08\u6700\u65B0\u5728\u524D\uFF09",
39
+ "order.asc": "\u5347\u5E8F\uFF08\u6700\u65E9\u5728\u524D\uFF09",
40
+ "scroll.wheel": "\u6EDA\u8F6E\u7FFB\u9875",
41
+ "scroll.click": "\u70B9\u51FB\u7FFB\u9875"
42
+ },
43
+ en: {
44
+ "panel.title": "SubAgent",
45
+ "status.none": "No sub-agents yet",
46
+ "agent.label": "agent",
47
+ "status.label": "status",
48
+ "time.label": "time",
49
+ "tokens.label": "tokens",
50
+ "error.label": "error",
51
+ "model.label": "model",
52
+ "todo.label": "todo",
53
+ "open.label": "Open session",
54
+ "cost.label": "cost",
55
+ "scroll.more": "more",
56
+ "scroll.top": "Top",
57
+ "scroll.bottom": "Bottom",
58
+ "dismiss.label": "dismiss",
59
+ "status.running": "running",
60
+ "status.done": "done",
61
+ "status.error": "error",
62
+ "order.desc": "Desc (newest first)",
63
+ "order.asc": "Asc (oldest first)",
64
+ "scroll.wheel": "Wheel Scroll",
65
+ "scroll.click": "Click Scroll"
66
+ }
67
+ };
68
+ function detectLang() {
69
+ const env = process?.env?.OPENCODE_LANG ?? process?.env?.LANG ?? "";
70
+ if (env.startsWith("zh")) return "zh";
71
+ return "en";
72
+ }
73
+ function charColumns(c) {
74
+ const code = c.codePointAt(0) ?? 0;
75
+ if (code < 32) return 0;
76
+ if (code < 127) return 1;
77
+ if (code < 160) return 0;
78
+ if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 128591 || code >= 131072 && code <= 262141) return 2;
79
+ return 1;
80
+ }
81
+ function visualWidth(s) {
82
+ let w = 0;
83
+ for (const c of s) w += charColumns(c);
84
+ return w;
85
+ }
86
+ function truncate(text, maxCols) {
87
+ if (visualWidth(text) <= maxCols) return text;
88
+ let cols = 0;
89
+ let i = 0;
90
+ for (const c of text) {
91
+ const w = charColumns(c);
92
+ if (cols + w > maxCols - 1) break;
93
+ cols += w;
94
+ i += c.length;
95
+ }
96
+ return text.slice(0, i) + "\u2026";
97
+ }
98
+ function fmtDurationShort(ms, running) {
99
+ if (running && ms < 2e3) return "";
100
+ if (ms < 1e3) return (ms / 1e3).toFixed(2) + "s";
101
+ if (ms < 6e4) return (ms / 1e3).toFixed(2) + "s";
102
+ const m = Math.floor(ms / 6e4);
103
+ const s = Math.round(ms % 6e4 / 1e3);
104
+ return `${m}m${s}s`;
105
+ }
106
+ function fmtTokens(n) {
107
+ if (n < 1e3) return `${n}`;
108
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
109
+ return `${(n / 1e6).toFixed(1)}M`;
110
+ }
111
+ function rgb(raw) {
112
+ if (typeof raw === "string" && raw.startsWith("#")) {
113
+ const h = raw.slice(1);
114
+ return {
115
+ r: parseInt(h.slice(0, 2), 16),
116
+ g: parseInt(h.slice(2, 4), 16),
117
+ b: parseInt(h.slice(4, 6), 16)
118
+ };
119
+ }
120
+ if (raw && typeof raw === "object") {
121
+ const o = raw;
122
+ if (typeof o.r === "number" && typeof o.g === "number" && typeof o.b === "number") {
123
+ const scale = o.r > 1 || o.g > 1 || o.b > 1 ? 1 : 255;
124
+ return {
125
+ r: Math.round(o.r * scale),
126
+ g: Math.round(o.g * scale),
127
+ b: Math.round(o.b * scale)
128
+ };
129
+ }
130
+ }
131
+ return null;
132
+ }
133
+ function saturation(r, g, b) {
134
+ const max = Math.max(r, g, b) / 255;
135
+ const min = Math.min(r, g, b) / 255;
136
+ const delta = max - min;
137
+ if (delta === 0) return 0;
138
+ const L = (max + min) / 2;
139
+ return L <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
140
+ }
141
+ function desaturateTo(raw, maxSat, fallback) {
142
+ const c = rgb(raw);
143
+ if (!c) return fallback;
144
+ const sat = saturation(c.r, c.g, c.b);
145
+ if (sat <= maxSat) {
146
+ return "#" + [c.r, c.g, c.b].map((v) => v.toString(16).padStart(2, "0")).join("");
147
+ }
148
+ const luma = c.r * 0.299 + c.g * 0.587 + c.b * 0.114;
149
+ let lo = 0, hi = 1;
150
+ for (let i = 0; i < 12; i++) {
151
+ const mid = (lo + hi) / 2;
152
+ const nr2 = Math.round(c.r + (luma - c.r) * mid);
153
+ const ng2 = Math.round(c.g + (luma - c.g) * mid);
154
+ const nb2 = Math.round(c.b + (luma - c.b) * mid);
155
+ if (saturation(nr2, ng2, nb2) > maxSat) lo = mid;
156
+ else hi = mid;
157
+ }
158
+ const nr = Math.round(c.r + (luma - c.r) * hi);
159
+ const ng = Math.round(c.g + (luma - c.g) * hi);
160
+ const nb = Math.round(c.b + (luma - c.b) * hi);
161
+ return "#" + [nr, ng, nb].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
162
+ }
163
+ function dimColor(hex, factor = 0.5) {
164
+ const c = rgb(hex);
165
+ if (!c) return hex;
166
+ const r = Math.round(c.r * factor);
167
+ const g = Math.round(c.g * factor);
168
+ const b = Math.round(c.b * factor);
169
+ return "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
170
+ }
171
+ var FALLBACK = {
172
+ primary: "#8B9DAF",
173
+ text: "#C5C5BB",
174
+ muted: "#7A7A72",
175
+ success: "#9CAF8B",
176
+ warning: "#C5B88D",
177
+ error: "#B08A8A",
178
+ border: "#6B6B63"
179
+ };
180
+ var MAX_SAT = 0.28;
181
+ var LEFT_PAD = 4;
182
+ var INDENT = 2;
183
+ function safeErrorMsg(err) {
184
+ if (!err) return "";
185
+ if (typeof err === "string") return err;
186
+ if (typeof err === "object") return String(err.message || err.code || "");
187
+ return "";
188
+ }
189
+ var globalEntryCache = /* @__PURE__ */ new Map();
190
+ function SubAgentPanel(props) {
191
+ const t = (key) => I18N[props.lang()][key] ?? key;
192
+ const SESSION_DATA_KEY = `${KV_PREFIX}.session_data`;
193
+ const TTL_MS = 3 * 24 * 60 * 60 * 1e3;
194
+ const loadSessionData = () => {
195
+ try {
196
+ const raw = props.api.kv.get(SESSION_DATA_KEY, "{}");
197
+ return JSON.parse(String(raw));
198
+ } catch {
199
+ return {};
200
+ }
201
+ };
202
+ const saveSessionData = (data) => {
203
+ try {
204
+ props.api.kv.set(SESSION_DATA_KEY, JSON.stringify(data));
205
+ } catch {
206
+ }
207
+ };
208
+ const loadEntries = (sid) => {
209
+ const m = /* @__PURE__ */ new Map();
210
+ try {
211
+ const rec = loadSessionData()[sid];
212
+ if (rec?.entries) {
213
+ for (const e of rec.entries) m.set(e.id, e);
214
+ }
215
+ } catch {
216
+ }
217
+ return m;
218
+ };
219
+ let persistTimer;
220
+ const persistEntries = (sid, entries2) => {
221
+ clearTimeout(persistTimer);
222
+ persistTimer = setTimeout(() => {
223
+ try {
224
+ const data = loadSessionData();
225
+ data[sid] = {
226
+ ...data[sid],
227
+ ts: Date.now(),
228
+ entries: [...entries2.values()]
229
+ };
230
+ saveSessionData(data);
231
+ } catch {
232
+ }
233
+ }, 200);
234
+ };
235
+ const persistScroll = (sid, scroll) => {
236
+ try {
237
+ const data = loadSessionData();
238
+ data[sid] = {
239
+ ...data[sid],
240
+ ts: Date.now(),
241
+ scroll
242
+ };
243
+ saveSessionData(data);
244
+ } catch {
245
+ }
246
+ };
247
+ const persistExpanded = (sid, expanded2) => {
248
+ try {
249
+ const data = loadSessionData();
250
+ data[sid] = {
251
+ ...data[sid],
252
+ ts: Date.now(),
253
+ expanded: expanded2
254
+ };
255
+ saveSessionData(data);
256
+ } catch {
257
+ }
258
+ };
259
+ const cleanupOldSessions = () => {
260
+ try {
261
+ const data = loadSessionData();
262
+ const cutoff = Date.now() - TTL_MS;
263
+ let changed = false;
264
+ for (const sid of Object.keys(data)) {
265
+ if (data[sid].ts < cutoff) {
266
+ delete data[sid];
267
+ changed = true;
268
+ }
269
+ }
270
+ if (changed) saveSessionData(data);
271
+ } catch {
272
+ }
273
+ };
274
+ cleanupOldSessions();
275
+ const [entryMap, setEntryMapRaw] = createSignal(loadEntries(props.sessionId));
276
+ const setEntryMap = (arg) => {
277
+ setEntryMapRaw((prev) => {
278
+ const next = typeof arg === "function" ? arg(prev) : arg;
279
+ let needsImmediateFlush = false;
280
+ for (const [id, entry] of next) {
281
+ const prevEntry = prev.get(id);
282
+ if (prevEntry?.status === "running" && (entry.status === "done" || entry.status === "error")) {
283
+ needsImmediateFlush = true;
284
+ break;
285
+ }
286
+ }
287
+ if (needsImmediateFlush) {
288
+ clearTimeout(persistTimer);
289
+ try {
290
+ const data = loadSessionData();
291
+ data[props.sessionId] = {
292
+ ...data[props.sessionId],
293
+ ts: Date.now(),
294
+ entries: [...next.values()]
295
+ };
296
+ saveSessionData(data);
297
+ } catch {
298
+ }
299
+ } else {
300
+ persistEntries(props.sessionId, next);
301
+ }
302
+ globalEntryCache.set(props.sessionId, new Map(next));
303
+ return next;
304
+ });
305
+ };
306
+ const [panelWidth, setPanelWidth] = createSignal(28);
307
+ const [open, setOpen] = createSignal((() => {
308
+ try {
309
+ return props.api.kv.get(`${KV_PREFIX}.open`, true);
310
+ } catch {
311
+ return true;
312
+ }
313
+ })());
314
+ const [expanded, setExpanded] = createSignal((() => {
315
+ try {
316
+ return loadSessionData()[props.sessionId]?.expanded || void 0;
317
+ } catch {
318
+ return void 0;
319
+ }
320
+ })());
321
+ const [hoveredOpen, setHoveredOpen] = createSignal(void 0);
322
+ const [hoveredDismiss, setHoveredDismiss] = createSignal(void 0);
323
+ const [hoveredTop, setHoveredTop] = createSignal(false);
324
+ const [hoveredMoreAbove, setHoveredMoreAbove] = createSignal(false);
325
+ const [hoveredMoreBelow, setHoveredMoreBelow] = createSignal(false);
326
+ const [scrollOffset, setScrollOffset] = createSignal((() => {
327
+ try {
328
+ return loadSessionData()[props.sessionId]?.scroll ?? 0;
329
+ } catch {
330
+ return 0;
331
+ }
332
+ })());
333
+ const [now, setNow] = createSignal(Date.now());
334
+ const [renderTick, setRenderTick] = createSignal(0);
335
+ let boxEl;
336
+ let disposed = false;
337
+ const readSessionTokens = (sid) => {
338
+ if (!sid) return void 0;
339
+ try {
340
+ const msgs = props.api.state.session.messages(sid);
341
+ if (msgs) {
342
+ for (let i = msgs.length - 1; i >= 0; i--) {
343
+ const m = msgs[i];
344
+ if (m.role !== "assistant") continue;
345
+ const t2 = m.tokens;
346
+ if (!t2) continue;
347
+ const cache = t2.cache;
348
+ const ctx = (Number(t2.input) || 0) + (cache?.read ?? 0);
349
+ if (ctx > 0) return ctx;
350
+ }
351
+ }
352
+ return void 0;
353
+ } catch {
354
+ return void 0;
355
+ }
356
+ };
357
+ const readSessionCost = (sid) => {
358
+ if (!sid) return void 0;
359
+ try {
360
+ const session = props.api.state.session.get(sid);
361
+ if (session?.cost != null && session.cost > 0) return session.cost;
362
+ const msgs = props.api.state.session.messages(sid);
363
+ if (!msgs) return void 0;
364
+ let total = 0;
365
+ for (const m of msgs) {
366
+ if (m.role === "assistant" && typeof m.cost === "number") total += m.cost;
367
+ }
368
+ return total > 0 ? total : void 0;
369
+ } catch {
370
+ return void 0;
371
+ }
372
+ };
373
+ const readSessionModel = (sid) => {
374
+ if (!sid) return void 0;
375
+ try {
376
+ const msgs = props.api.state.session.messages(sid);
377
+ if (msgs) {
378
+ for (let i = msgs.length - 1; i >= 0; i--) {
379
+ const m = msgs[i];
380
+ if (m.role === "assistant" && m.modelID) return String(m.modelID);
381
+ }
382
+ }
383
+ return void 0;
384
+ } catch {
385
+ return void 0;
386
+ }
387
+ };
388
+ const readSessionTodo = (sid) => {
389
+ if (!sid) return void 0;
390
+ try {
391
+ const todos = props.api.state.session.todo(sid);
392
+ if (!todos || todos.length === 0) return void 0;
393
+ let done = 0;
394
+ for (const t2 of todos) {
395
+ if (t2.status === "completed" || t2.status === "cancelled") done++;
396
+ }
397
+ return {
398
+ total: todos.length,
399
+ done
400
+ };
401
+ } catch {
402
+ return void 0;
403
+ }
404
+ };
405
+ const upsertEntry = (partial) => {
406
+ setEntryMap((prev) => {
407
+ const existing = prev.get(partial.id);
408
+ const next = new Map(prev);
409
+ const nowTs = Date.now();
410
+ const e = partial.status;
411
+ const ended = e === "done" || e === "error";
412
+ next.set(partial.id, {
413
+ ...existing ?? {
414
+ startedAt: nowTs
415
+ },
416
+ ...partial,
417
+ startedAt: existing?.startedAt || partial.startedAt || nowTs,
418
+ endedAt: ended ? existing?.endedAt || nowTs : void 0
419
+ });
420
+ return next;
421
+ });
422
+ };
423
+ const handlePartUpdated = (event) => {
424
+ const e = event;
425
+ const props_ = e.properties;
426
+ const part = props_?.part;
427
+ if (!part) return;
428
+ if (part.type === "subtask") {
429
+ const agent = String(part.agent ?? "?");
430
+ const prompt = String(part.prompt ?? "");
431
+ const desc = String(part.description ?? "");
432
+ const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
433
+ const id = `sub:${String(part.id ?? crypto.randomUUID())}`;
434
+ const subSid = part.sessionID !== void 0 ? String(part.sessionID) : void 0;
435
+ const partModel = part.model;
436
+ const modelId = partModel?.modelID ? String(partModel.modelID) : void 0;
437
+ upsertEntry({
438
+ id,
439
+ title,
440
+ agent,
441
+ prompt,
442
+ sessionId: subSid,
443
+ status: "running",
444
+ model: modelId
445
+ });
446
+ }
447
+ if (part.type === "tool") {
448
+ const tool = String(part.tool ?? "");
449
+ if (!SUBAGENT_TOOLS.has(tool)) return;
450
+ const st = part.state;
451
+ const rawStatus = String(st?.status ?? "");
452
+ if (rawStatus === "pending" || rawStatus === "") return;
453
+ if (rawStatus === "error") {
454
+ const id2 = `tool:${String(part.id ?? "")}`;
455
+ if (!part.id) return;
456
+ const existing = entryMap().get(id2);
457
+ if (existing) {
458
+ upsertEntry({
459
+ id: id2,
460
+ title: existing.title,
461
+ agent: existing.agent,
462
+ prompt: existing.prompt,
463
+ status: "error"
464
+ });
465
+ }
466
+ return;
467
+ }
468
+ const input = st?.input;
469
+ let status = "running";
470
+ if (rawStatus === "completed") status = "done";
471
+ if (input?.run_in_background === true && status === "done") {
472
+ const stMetaCheck = st?.metadata;
473
+ const hasChild = stMetaCheck?.session_id !== void 0 || stMetaCheck?.sessionId !== void 0;
474
+ if (hasChild) status = "running";
475
+ }
476
+ const agent = String(part.subagent_type ?? input?.subagent_type ?? input?.category ?? tool);
477
+ const prompt = String(input?.prompt ?? part.description ?? "");
478
+ const desc = input?.description !== void 0 ? String(input.description) : "";
479
+ const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
480
+ const id = `tool:${String(part.id ?? crypto.randomUUID())}`;
481
+ const stMeta = st?.metadata;
482
+ const subSid = stMeta?.session_id !== void 0 ? String(stMeta.session_id) : stMeta?.sessionId !== void 0 ? String(stMeta.sessionId) : void 0;
483
+ upsertEntry({
484
+ id,
485
+ title,
486
+ agent,
487
+ prompt,
488
+ sessionId: subSid,
489
+ status
490
+ });
491
+ }
492
+ };
493
+ const handleSessionEnd = (event, status) => {
494
+ const e = event;
495
+ const props_ = e.properties;
496
+ const sid = String(props_?.sessionID ?? "");
497
+ if (!sid) return;
498
+ const sessionTokens = readSessionTokens(sid);
499
+ const sessionCost = readSessionCost(sid);
500
+ const sessionModel = readSessionModel(sid);
501
+ const sessionTodo = readSessionTodo(sid);
502
+ let sessionAgent;
503
+ let errorMsg;
504
+ try {
505
+ const s = props.api.state.session.get(sid);
506
+ sessionAgent = s?.agent;
507
+ if (status === "error") {
508
+ const evtErr = props_?.error;
509
+ errorMsg = safeErrorMsg(evtErr) || safeErrorMsg(props_?.message);
510
+ if (!errorMsg) {
511
+ const msgs = props.api.state.session.messages(sid);
512
+ if (msgs) {
513
+ for (let i = msgs.length - 1; i >= 0; i--) {
514
+ const m = msgs[i];
515
+ if (m.role === "assistant" && m.error) {
516
+ errorMsg = safeErrorMsg(m.error);
517
+ break;
518
+ }
519
+ }
520
+ }
521
+ }
522
+ }
523
+ } catch {
524
+ }
525
+ const tryMatchAndUpdate = (entriesMap, targetSid, targetStatus, nowTs) => {
526
+ for (const [, entry] of entriesMap) {
527
+ if (entry.sessionId === targetSid && entry.status === "running") {
528
+ entry.status = targetStatus;
529
+ entry.endedAt = nowTs;
530
+ entry.tokens = entry.tokens ?? sessionTokens;
531
+ entry.cost = entry.cost ?? sessionCost;
532
+ entry.model = entry.model ?? sessionModel;
533
+ entry.todoTotal = entry.todoTotal ?? sessionTodo?.total;
534
+ entry.todoDone = entry.todoDone ?? sessionTodo?.done;
535
+ entry.error = errorMsg || entry.error;
536
+ return true;
537
+ }
538
+ }
539
+ if (sessionAgent) {
540
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
541
+ const saNorm = normalize(sessionAgent);
542
+ let best = null;
543
+ for (const [, entry] of entriesMap) {
544
+ if (entry.status !== "running") continue;
545
+ const eaNorm = normalize(entry.agent);
546
+ if (!eaNorm || !saNorm) continue;
547
+ if (!eaNorm.includes(saNorm) && !saNorm.includes(eaNorm)) continue;
548
+ const gap = nowTs - (entry.startedAt || 0);
549
+ if (!best || gap > best.gap) best = {
550
+ entry,
551
+ gap
552
+ };
553
+ }
554
+ if (!best) {
555
+ for (const [, entry] of entriesMap) {
556
+ if (entry.status !== "running") continue;
557
+ if (entry.sessionId) continue;
558
+ const gap = nowTs - (entry.startedAt || 0);
559
+ if (!best || gap > best.gap) best = {
560
+ entry,
561
+ gap
562
+ };
563
+ }
564
+ }
565
+ if (best) {
566
+ best.entry.status = targetStatus;
567
+ best.entry.endedAt = nowTs;
568
+ best.entry.tokens = best.entry.tokens ?? sessionTokens;
569
+ best.entry.cost = best.entry.cost ?? sessionCost;
570
+ best.entry.model = best.entry.model ?? sessionModel;
571
+ best.entry.todoTotal = best.entry.todoTotal ?? sessionTodo?.total;
572
+ best.entry.todoDone = best.entry.todoDone ?? sessionTodo?.done;
573
+ best.entry.sessionId = targetSid;
574
+ best.entry.error = errorMsg || best.entry.error;
575
+ return true;
576
+ }
577
+ }
578
+ return false;
579
+ };
580
+ setEntryMap((prev) => {
581
+ let changed = false;
582
+ const next = new Map(prev);
583
+ for (const [id, entry] of next) {
584
+ if (entry.sessionId !== sid) continue;
585
+ if (entry.status !== "running" && entry.status !== "done") continue;
586
+ if (sid === props.sessionId) continue;
587
+ const alreadySettled = entry.status !== "running";
588
+ next.set(id, {
589
+ ...entry,
590
+ ...alreadySettled ? {} : {
591
+ status,
592
+ endedAt: Date.now()
593
+ },
594
+ tokens: entry.tokens ?? sessionTokens,
595
+ cost: entry.cost ?? sessionCost,
596
+ model: entry.model ?? sessionModel,
597
+ todoTotal: entry.todoTotal ?? sessionTodo?.total,
598
+ todoDone: entry.todoDone ?? sessionTodo?.done,
599
+ error: errorMsg || entry.error
600
+ });
601
+ changed = true;
602
+ }
603
+ if (!changed && sessionAgent) {
604
+ const nowTs = Date.now();
605
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
606
+ const saNorm = normalize(sessionAgent);
607
+ let best = null;
608
+ for (const [id, entry] of next) {
609
+ if (entry.status !== "running") continue;
610
+ const eaNorm = normalize(entry.agent);
611
+ if (!eaNorm || !saNorm) continue;
612
+ if (!eaNorm.includes(saNorm) && !saNorm.includes(eaNorm)) continue;
613
+ const gap = nowTs - (entry.startedAt || 0);
614
+ if (!best || gap > best.gap) best = {
615
+ id,
616
+ gap
617
+ };
618
+ }
619
+ if (!best) {
620
+ for (const [id, entry] of next) {
621
+ if (entry.status !== "running") continue;
622
+ if (entry.sessionId) continue;
623
+ const gap = nowTs - (entry.startedAt || 0);
624
+ if (!best || gap > best.gap) best = {
625
+ id,
626
+ gap
627
+ };
628
+ }
629
+ }
630
+ if (best) {
631
+ const entry = next.get(best.id);
632
+ next.set(best.id, {
633
+ ...entry,
634
+ status,
635
+ endedAt: nowTs,
636
+ tokens: sessionTokens || entry.tokens,
637
+ cost: sessionCost || entry.cost,
638
+ sessionId: sid,
639
+ error: errorMsg || entry.error
640
+ });
641
+ changed = true;
642
+ }
643
+ }
644
+ return changed ? next : prev;
645
+ });
646
+ try {
647
+ const sessionObj = props.api.state.session.get(sid);
648
+ const parentSid = sessionObj?.parentID;
649
+ if (parentSid && parentSid !== props.sessionId) {
650
+ const parentCache = globalEntryCache.get(parentSid);
651
+ const nowTs = Date.now();
652
+ let found = false;
653
+ if (parentCache) {
654
+ found = tryMatchAndUpdate(parentCache, sid, status, nowTs);
655
+ }
656
+ if (!found) {
657
+ const data = loadSessionData();
658
+ const rec = data[parentSid];
659
+ if (rec?.entries) {
660
+ const fallbackMap = new Map(rec.entries.map((e2) => [e2.id, e2]));
661
+ found = tryMatchAndUpdate(fallbackMap, sid, status, nowTs);
662
+ if (found) {
663
+ data[parentSid] = {
664
+ ...rec,
665
+ ts: nowTs,
666
+ entries: [...fallbackMap.values()]
667
+ };
668
+ saveSessionData(data);
669
+ globalEntryCache.set(parentSid, fallbackMap);
670
+ }
671
+ }
672
+ }
673
+ if (found && parentCache) {
674
+ const data = loadSessionData();
675
+ data[parentSid] = {
676
+ ...data[parentSid],
677
+ ts: nowTs,
678
+ entries: [...parentCache.values()]
679
+ };
680
+ saveSessionData(data);
681
+ }
682
+ }
683
+ } catch {
684
+ }
685
+ setTimeout(() => {
686
+ if (disposed) return;
687
+ const finalTokens = readSessionTokens(sid);
688
+ const finalCost = readSessionCost(sid);
689
+ const finalModel = readSessionModel(sid);
690
+ const finalTodo = readSessionTodo(sid);
691
+ setEntryMap((prev) => {
692
+ let changed = false;
693
+ const next = new Map(prev);
694
+ for (const [id, entry] of next) {
695
+ if (entry.sessionId !== sid) continue;
696
+ const t2 = finalTokens ?? entry.tokens;
697
+ const c = finalCost ?? entry.cost;
698
+ const m = finalModel ?? entry.model;
699
+ const tt = finalTodo?.total ?? entry.todoTotal;
700
+ const td = finalTodo?.done ?? entry.todoDone;
701
+ if (t2 !== entry.tokens || c !== entry.cost || m !== entry.model || tt !== entry.todoTotal || td !== entry.todoDone) {
702
+ next.set(id, {
703
+ ...entry,
704
+ tokens: t2,
705
+ cost: c,
706
+ model: m,
707
+ todoTotal: tt,
708
+ todoDone: td
709
+ });
710
+ changed = true;
711
+ }
712
+ }
713
+ return changed ? next : prev;
714
+ });
715
+ bump();
716
+ }, 150);
717
+ };
718
+ const bump = () => setRenderTick((v) => v + 1);
719
+ onMount(() => {
720
+ const clock = setInterval(() => {
721
+ setNow(Date.now());
722
+ bump();
723
+ }, 100);
724
+ const tokenTimer = setInterval(() => {
725
+ untrack(() => {
726
+ setEntryMapRaw((prev) => {
727
+ let changed = false;
728
+ const next = new Map(prev);
729
+ for (const [id, entry] of next) {
730
+ if (entry.status === "running" && entry.sessionId) {
731
+ let isChild = false;
732
+ try {
733
+ const s = props.api.state.session.get(entry.sessionId);
734
+ isChild = s?.parentID === props.sessionId;
735
+ } catch {
736
+ }
737
+ if (!isChild) continue;
738
+ const total = readSessionTokens(entry.sessionId);
739
+ const todo = readSessionTodo(entry.sessionId);
740
+ const model = entry.model ?? readSessionModel(entry.sessionId);
741
+ const nextEntry = {
742
+ ...entry
743
+ };
744
+ if (total !== void 0 && total !== entry.tokens) {
745
+ nextEntry.tokens = total;
746
+ changed = true;
747
+ }
748
+ if (todo !== void 0) {
749
+ if (todo.total !== entry.todoTotal || todo.done !== entry.todoDone) {
750
+ nextEntry.todoTotal = todo.total;
751
+ nextEntry.todoDone = todo.done;
752
+ changed = true;
753
+ }
754
+ }
755
+ if (model && !entry.model) {
756
+ nextEntry.model = model;
757
+ changed = true;
758
+ }
759
+ if (changed) next.set(id, nextEntry);
760
+ }
761
+ }
762
+ return changed ? next : prev;
763
+ });
764
+ });
765
+ bump();
766
+ }, 500);
767
+ bump();
768
+ const unsubPart = props.api.event.on("message.part.updated", (e) => {
769
+ handlePartUpdated(e);
770
+ bump();
771
+ });
772
+ const unsubMsg = props.api.event.on("message.updated", () => bump());
773
+ const unsubIdle = props.api.event.on("session.idle", (e) => {
774
+ handleSessionEnd(e, "done");
775
+ bump();
776
+ });
777
+ const unsubError = props.api.event.on("session.error", (e) => {
778
+ handleSessionEnd(e, "error");
779
+ bump();
780
+ });
781
+ onCleanup(() => {
782
+ disposed = true;
783
+ clearInterval(clock);
784
+ clearInterval(tokenTimer);
785
+ unsubPart();
786
+ unsubMsg();
787
+ unsubIdle();
788
+ unsubError();
789
+ });
790
+ });
791
+ let lastSid = props.sessionId;
792
+ createEffect(() => {
793
+ const sid = props.sessionId;
794
+ const switched = sid !== lastSid;
795
+ lastSid = sid;
796
+ const t2 = setTimeout(() => {
797
+ untrack(() => {
798
+ if (switched) {
799
+ const saved = loadSessionData()[sid]?.scroll ?? 0;
800
+ setScrollOffset(saved);
801
+ }
802
+ setEntryMapRaw((prev) => {
803
+ const next = switched ? new Map(globalEntryCache.get(sid) ?? loadEntries(sid)) : new Map(prev);
804
+ try {
805
+ const msgs = props.api.state.session.messages(sid);
806
+ if (msgs && msgs.length) {
807
+ for (const msg of msgs) {
808
+ const parts = props.api.state.part(msg.id) ?? [];
809
+ for (const partRaw of parts) {
810
+ const part = partRaw;
811
+ if (part.type === "tool") {
812
+ const tool = String(part.tool ?? "");
813
+ if (!SUBAGENT_TOOLS.has(tool)) continue;
814
+ const id = `tool:${String(part.id ?? "")}`;
815
+ if (!part.id) continue;
816
+ const st = part.state;
817
+ const rawStatus = String(st?.status ?? "");
818
+ const exists = next.get(id);
819
+ if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
820
+ if (rawStatus === "error") {
821
+ if (exists && exists.status === "running") {
822
+ next.set(id, {
823
+ ...exists,
824
+ status: "error",
825
+ endedAt: Date.now()
826
+ });
827
+ }
828
+ continue;
829
+ }
830
+ let status = "running";
831
+ if (rawStatus === "completed") status = "done";
832
+ if (st?.input?.run_in_background === true && status === "done") {
833
+ const scanStMeta = st?.metadata;
834
+ const scanHasChild = scanStMeta?.session_id !== void 0 || scanStMeta?.sessionId !== void 0;
835
+ if (scanHasChild) status = "running";
836
+ }
837
+ if (exists && exists.status !== "running") continue;
838
+ if (exists && status === "running") {
839
+ if (!rawStatus) {
840
+ const msgTokens = msg?.tokens;
841
+ if (msgTokens && (Number(msgTokens.input) > 0 || Number(msgTokens.output) > 0)) {
842
+ status = "done";
843
+ } else if (Date.now() - exists.startedAt > 30 * 60 * 1e3) {
844
+ status = "done";
845
+ } else {
846
+ continue;
847
+ }
848
+ } else {
849
+ continue;
850
+ }
851
+ }
852
+ const input = st?.input;
853
+ const agent = String(part.subagent_type ?? input?.subagent_type ?? tool);
854
+ const prompt = String(input?.prompt ?? part.description ?? "");
855
+ const desc = input?.description !== void 0 ? String(input.description) : "";
856
+ const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
857
+ let tokens;
858
+ const scanStMeta2 = st?.metadata;
859
+ const scanSubSid = scanStMeta2?.session_id !== void 0 ? String(scanStMeta2.session_id) : scanStMeta2?.sessionId !== void 0 ? String(scanStMeta2.sessionId) : void 0;
860
+ if (scanSubSid) tokens = readSessionTokens(scanSubSid);
861
+ const ended = status === "done";
862
+ next.set(id, {
863
+ id,
864
+ title,
865
+ agent,
866
+ prompt,
867
+ // Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
868
+ tokens: exists?.tokens ?? tokens,
869
+ sessionId: exists?.sessionId ?? scanSubSid,
870
+ status,
871
+ startedAt: exists?.startedAt || Date.now(),
872
+ endedAt: ended ? exists?.endedAt || Date.now() : void 0
873
+ });
874
+ }
875
+ }
876
+ }
877
+ }
878
+ } catch {
879
+ }
880
+ return next;
881
+ });
882
+ setEntryMapRaw((prev) => {
883
+ let changed = false;
884
+ const next = new Map(prev);
885
+ for (const [id, entry] of next) {
886
+ if (entry.status !== "running" || !entry.sessionId) continue;
887
+ try {
888
+ const st = props.api.state.session.status(entry.sessionId);
889
+ if (!st || st.type !== "idle") continue;
890
+ const tokens = readSessionTokens(entry.sessionId);
891
+ const cost = readSessionCost(entry.sessionId);
892
+ next.set(id, {
893
+ ...entry,
894
+ status: "done",
895
+ endedAt: Date.now(),
896
+ tokens: tokens ?? entry.tokens,
897
+ cost: cost ?? entry.cost
898
+ });
899
+ changed = true;
900
+ } catch {
901
+ }
902
+ }
903
+ return changed ? next : prev;
904
+ });
905
+ bump();
906
+ });
907
+ }, 150);
908
+ onCleanup(() => clearTimeout(t2));
909
+ });
910
+ const pal = createMemo(() => {
911
+ const th = props.theme;
912
+ const sat = (k, fb) => desaturateTo(th[k], MAX_SAT, fb);
913
+ return {
914
+ primary: sat("primary", FALLBACK.primary),
915
+ text: sat("text", FALLBACK.text),
916
+ muted: sat("textMuted", FALLBACK.muted),
917
+ success: sat("success", FALLBACK.success),
918
+ warning: sat("warning", FALLBACK.warning),
919
+ error: sat("error", FALLBACK.error),
920
+ border: sat("border", FALLBACK.border)
921
+ };
922
+ });
923
+ const entryList = createMemo(() => {
924
+ const entries2 = [...entryMap().values()];
925
+ if (props.sortOrder() === "desc") {
926
+ return entries2.sort((a, b) => b.startedAt - a.startedAt);
927
+ }
928
+ return entries2.sort((a, b) => a.startedAt - b.startedAt);
929
+ });
930
+ const max = props.maxEntries;
931
+ const clampedOffset = createMemo(() => {
932
+ const total = entryList().length;
933
+ const m = max();
934
+ if (total <= m) return 0;
935
+ return Math.min(scrollOffset(), total - m);
936
+ });
937
+ const visibleList = createMemo(() => entryList().slice(clampedOffset(), clampedOffset() + max()));
938
+ const hiddenAbove = createMemo(() => clampedOffset());
939
+ const hiddenBelow = createMemo(() => Math.max(0, entryList().length - clampedOffset() - max()));
940
+ createEffect(() => {
941
+ if (hiddenAbove() === 0) setHoveredMoreAbove(false);
942
+ });
943
+ let sortInitialized = false;
944
+ createEffect(() => {
945
+ props.sortOrder();
946
+ if (!sortInitialized) {
947
+ sortInitialized = true;
948
+ return;
949
+ }
950
+ const total = untrack(() => entryList().length);
951
+ const m = untrack(() => max());
952
+ const target = props.sortOrder() === "desc" ? 0 : Math.max(0, total - m);
953
+ setScrollOffset(target);
954
+ setTimeout(() => {
955
+ try {
956
+ persistScroll(props.sessionId, target);
957
+ } catch {
958
+ }
959
+ }, 0);
960
+ });
961
+ let prevEntryCount = 0;
962
+ createEffect(() => {
963
+ const total = entryList().length;
964
+ if (prevEntryCount === 0) {
965
+ prevEntryCount = total;
966
+ return;
967
+ }
968
+ if (total === prevEntryCount) return;
969
+ const m = max();
970
+ const wasAtNewest = props.sortOrder() === "desc" ? untrack(() => scrollOffset() === 0) : untrack(() => scrollOffset() >= prevEntryCount - m);
971
+ prevEntryCount = total;
972
+ if (wasAtNewest) {
973
+ const target = props.sortOrder() === "desc" ? 0 : Math.max(0, total - m);
974
+ setScrollOffset(target);
975
+ setTimeout(() => {
976
+ try {
977
+ persistScroll(props.sessionId, target);
978
+ } catch {
979
+ }
980
+ }, 0);
981
+ }
982
+ });
983
+ const entries = createMemo(() => {
984
+ const nowVal = now();
985
+ return entryList().map((e) => ({
986
+ ...e,
987
+ elapsed: (e.endedAt ?? nowVal) - e.startedAt
988
+ }));
989
+ });
990
+ const doneCount = createMemo(() => entryList().filter((e) => e.status === "done").length);
991
+ const runningCount = createMemo(() => entryList().filter((e) => e.status === "running").length);
992
+ const errCount = createMemo(() => entryList().filter((e) => e.status === "error").length);
993
+ const anyEntry = () => entryList().length > 0;
994
+ const totalTokens = createMemo(() => {
995
+ let sum = 0;
996
+ for (const e of entryList()) {
997
+ if (e.tokens) sum += e.tokens;
998
+ }
999
+ return sum;
1000
+ });
1001
+ const totalCost = createMemo(() => {
1002
+ let sum = 0;
1003
+ for (const e of entryList()) {
1004
+ if (e.cost) sum += e.cost;
1005
+ }
1006
+ return sum;
1007
+ });
1008
+ const toggleExpand = (id) => {
1009
+ setExpanded((prev) => {
1010
+ const next = prev === id ? void 0 : id;
1011
+ try {
1012
+ persistExpanded(props.sessionId, next ?? "");
1013
+ } catch {
1014
+ }
1015
+ return next;
1016
+ });
1017
+ };
1018
+ const sep = () => "\u2500".repeat(Math.max(1, panelWidth()));
1019
+ const expandedMaxLabelW = createMemo(() => {
1020
+ 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")];
1021
+ return Math.max(...labels.map((l) => visualWidth(l + ": ")));
1022
+ });
1023
+ const expandedPad = (label) => Math.max(0, expandedMaxLabelW() - visualWidth(label + ": "));
1024
+ const expandedValAvail = () => Math.max(6, panelWidth() - INDENT - expandedMaxLabelW());
1025
+ const summaryParts = createMemo(() => {
1026
+ if (!anyEntry()) return null;
1027
+ const dot = "\u25CF";
1028
+ const cost = totalCost();
1029
+ return {
1030
+ done: `${dot}${doneCount()}`,
1031
+ running: runningCount() > 0 ? `${dot}${runningCount()}` : null,
1032
+ err: errCount() > 0 ? `${dot}${errCount()}` : null,
1033
+ duration: totalTokens() > 0 ? fmtTokens(totalTokens()) : "",
1034
+ cost: cost > 0 ? `$${cost.toFixed(2)}` : ""
1035
+ };
1036
+ });
1037
+ const summaryCols = createMemo(() => {
1038
+ const p = summaryParts();
1039
+ if (!p) return 0;
1040
+ let w = visualWidth(p.done);
1041
+ if (p.running) w += 1 + visualWidth(p.running);
1042
+ if (p.err) w += 1 + visualWidth(p.err);
1043
+ w += p.duration ? 1 + visualWidth(p.duration) : 0;
1044
+ w += p.cost ? 1 + visualWidth(p.cost) : 0;
1045
+ return w;
1046
+ });
1047
+ const versionText = ` v${PLUGIN_VERSION}`;
1048
+ const versionW = visualWidth(versionText);
1049
+ const showVersion = createMemo(() => {
1050
+ if (!open()) return false;
1051
+ const icon = "\u25BC";
1052
+ const need = visualWidth(icon) + 1 + visualWidth(t("panel.title")) + versionW + summaryCols();
1053
+ return need <= panelWidth();
1054
+ });
1055
+ const leftCols = createMemo(() => {
1056
+ const icon = open() ? "\u25BC" : "\u25B6";
1057
+ let w = visualWidth(icon) + 1 + visualWidth(t("panel.title"));
1058
+ if (showVersion()) w += versionW;
1059
+ return w;
1060
+ });
1061
+ const spacerCols = createMemo(() => {
1062
+ if (!anyEntry()) return 0;
1063
+ return Math.max(0, panelWidth() - leftCols() - summaryCols());
1064
+ });
1065
+ const valueCols = (label) => Math.max(4, panelWidth() - INDENT - visualWidth(label + ": "));
1066
+ return (() => {
1067
+ var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createElement("span"), _el$4 = _$createElement("span");
1068
+ _$insertNode(_el$, _el$2);
1069
+ var _ref$ = boxEl;
1070
+ typeof _ref$ === "function" ? _$use(_ref$, _el$) : boxEl = _el$;
1071
+ _$setProp(_el$, "border", false);
1072
+ _$setProp(_el$, "paddingTop", 0);
1073
+ _$setProp(_el$, "paddingBottom", 0);
1074
+ _$setProp(_el$, "paddingLeft", 0);
1075
+ _$setProp(_el$, "paddingRight", 0);
1076
+ _$setProp(_el$, "flexDirection", "column");
1077
+ _$setProp(_el$, "gap", 0);
1078
+ _$setProp(_el$, "onSizeChange", () => {
1079
+ const w = boxEl ? Math.max(20, boxEl.width ?? 0) : 28;
1080
+ setPanelWidth((prev) => prev === w ? prev : w);
1081
+ });
1082
+ _$insertNode(_el$2, _el$3);
1083
+ _$insertNode(_el$2, _el$4);
1084
+ _$setProp(_el$2, "onMouseUp", () => {
1085
+ setOpen((o) => {
1086
+ const n = !o;
1087
+ try {
1088
+ props.api.kv.set(`${KV_PREFIX}.open`, n);
1089
+ } catch {
1090
+ }
1091
+ return n;
1092
+ });
1093
+ bump();
1094
+ });
1095
+ _$insert(_el$3, () => renderTick() >= 0 && open() ? "\u25BC " : "\u25B6 ");
1096
+ _$insert(_el$4, () => t("panel.title"));
1097
+ _$insert(_el$2, _$createComponent(Show, {
1098
+ get when() {
1099
+ return showVersion();
1100
+ },
1101
+ get children() {
1102
+ var _el$5 = _$createElement("span");
1103
+ _$insert(_el$5, versionText);
1104
+ _$effect((_$p) => _$setProp(_el$5, "style", {
1105
+ fg: dimColor(pal().muted, 0.75)
1106
+ }, _$p));
1107
+ return _el$5;
1108
+ }
1109
+ }), null);
1110
+ _$insert(_el$2, (() => {
1111
+ var _c$ = _$memo(() => !!anyEntry());
1112
+ return () => _c$() ? [(() => {
1113
+ var _el$11 = _$createElement("span");
1114
+ _$insert(_el$11, () => " ".repeat(spacerCols()));
1115
+ _$effect((_$p) => _$setProp(_el$11, "style", {
1116
+ fg: pal().muted
1117
+ }, _$p));
1118
+ return _el$11;
1119
+ })(), (() => {
1120
+ var _el$12 = _$createElement("span");
1121
+ _$insert(_el$12, () => summaryParts().done);
1122
+ _$effect((_$p) => _$setProp(_el$12, "style", {
1123
+ fg: pal().success
1124
+ }, _$p));
1125
+ return _el$12;
1126
+ })(), _$memo(() => _$memo(() => runningCount() > 0)() && (() => {
1127
+ var _el$13 = _$createElement("span"), _el$14 = _$createTextNode(` `);
1128
+ _$insertNode(_el$13, _el$14);
1129
+ _$insert(_el$13, () => summaryParts().running, null);
1130
+ _$effect((_$p) => _$setProp(_el$13, "style", {
1131
+ fg: pal().warning
1132
+ }, _$p));
1133
+ return _el$13;
1134
+ })()), _$memo(() => _$memo(() => errCount() > 0)() && (() => {
1135
+ var _el$15 = _$createElement("span"), _el$16 = _$createTextNode(` `);
1136
+ _$insertNode(_el$15, _el$16);
1137
+ _$insert(_el$15, () => summaryParts().err, null);
1138
+ _$effect((_$p) => _$setProp(_el$15, "style", {
1139
+ fg: pal().error
1140
+ }, _$p));
1141
+ return _el$15;
1142
+ })()), _$memo(() => _$memo(() => !!summaryParts().duration)() ? (() => {
1143
+ var _el$17 = _$createElement("span"), _el$18 = _$createTextNode(` `);
1144
+ _$insertNode(_el$17, _el$18);
1145
+ _$insert(_el$17, () => summaryParts().duration, null);
1146
+ _$effect((_$p) => _$setProp(_el$17, "style", {
1147
+ fg: pal().muted
1148
+ }, _$p));
1149
+ return _el$17;
1150
+ })() : null), _$memo(() => _$memo(() => !!summaryParts().cost)() ? (() => {
1151
+ var _el$19 = _$createElement("span"), _el$20 = _$createTextNode(` `);
1152
+ _$insertNode(_el$19, _el$20);
1153
+ _$insert(_el$19, () => summaryParts().cost, null);
1154
+ _$effect((_$p) => _$setProp(_el$19, "style", {
1155
+ fg: pal().warning
1156
+ }, _$p));
1157
+ return _el$19;
1158
+ })() : null)] : null;
1159
+ })(), null);
1160
+ _$insert(_el$, _$createComponent(Show, {
1161
+ get when() {
1162
+ return open();
1163
+ },
1164
+ get children() {
1165
+ return [(() => {
1166
+ var _el$6 = _$createElement("text");
1167
+ _$insert(_el$6, sep);
1168
+ _$effect((_$p) => _$setProp(_el$6, "fg", pal().muted, _$p));
1169
+ return _el$6;
1170
+ })(), _$createComponent(Show, {
1171
+ get when() {
1172
+ return anyEntry();
1173
+ },
1174
+ get fallback() {
1175
+ return (() => {
1176
+ var _el$21 = _$createElement("text"), _el$22 = _$createTextNode(` &gt; `), _el$24 = _$createTextNode(` `);
1177
+ _$insertNode(_el$21, _el$22);
1178
+ _$insertNode(_el$21, _el$24);
1179
+ _$insert(_el$21, () => t("status.none"), _el$24);
1180
+ _$effect((_$p) => _$setProp(_el$21, "style", {
1181
+ fg: pal().muted
1182
+ }, _$p));
1183
+ return _el$21;
1184
+ })();
1185
+ },
1186
+ get children() {
1187
+ var _el$7 = _$createElement("box");
1188
+ _$setProp(_el$7, "onMouseScroll", (e) => {
1189
+ if (props.scrollMode() === "click") return;
1190
+ const total = entryList().length;
1191
+ const m = max();
1192
+ if (total <= m) return;
1193
+ const dir = e.button === 0 ? 1 : -1;
1194
+ setScrollOffset((prev) => {
1195
+ const next = Math.max(0, Math.min(prev + dir, total - m));
1196
+ try {
1197
+ persistScroll(props.sessionId, next);
1198
+ } catch {
1199
+ }
1200
+ return next;
1201
+ });
1202
+ });
1203
+ _$insert(_el$7, _$createComponent(Show, {
1204
+ get when() {
1205
+ return hiddenAbove() > 0;
1206
+ },
1207
+ get children() {
1208
+ var _el$8 = _$createElement("text"), _el$9 = _$createElement("span"), _el$0 = _$createTextNode(` &uarr; `), _el$10 = _$createTextNode(` `);
1209
+ _$insertNode(_el$8, _el$9);
1210
+ _$setProp(_el$8, "onMouseOver", () => setHoveredMoreAbove(true));
1211
+ _$setProp(_el$8, "onMouseOut", () => setHoveredMoreAbove(false));
1212
+ _$setProp(_el$8, "onMouseUp", () => {
1213
+ const total = entryList().length;
1214
+ const m = max();
1215
+ if (total <= m) return;
1216
+ const next = Math.max(0, scrollOffset() - m);
1217
+ if (next === 0) {
1218
+ setTimeout(() => {
1219
+ setScrollOffset(next);
1220
+ try {
1221
+ persistScroll(props.sessionId, next);
1222
+ } catch {
1223
+ }
1224
+ }, 0);
1225
+ } else {
1226
+ setScrollOffset(next);
1227
+ try {
1228
+ persistScroll(props.sessionId, next);
1229
+ } catch {
1230
+ }
1231
+ }
1232
+ });
1233
+ _$insertNode(_el$9, _el$0);
1234
+ _$insertNode(_el$9, _el$10);
1235
+ _$insert(_el$9, hiddenAbove, _el$10);
1236
+ _$insert(_el$9, () => t("scroll.more"), null);
1237
+ _$effect((_$p) => _$setProp(_el$9, "style", {
1238
+ fg: hoveredMoreAbove() ? pal().warning : pal().muted
1239
+ }, _$p));
1240
+ return _el$8;
1241
+ }
1242
+ }), null);
1243
+ _$insert(_el$7, _$createComponent(For, {
1244
+ get each() {
1245
+ return visibleList();
1246
+ },
1247
+ children: (entry) => {
1248
+ const isExpanded = () => expanded() === entry.id;
1249
+ const isRunning = entry.status === "running";
1250
+ const isError = entry.status === "error";
1251
+ const elapsed = () => (entry.endedAt ?? now()) - entry.startedAt;
1252
+ const statusDot = () => "\u25CF";
1253
+ const statusColor = () => {
1254
+ if (!isRunning) return isError ? pal().error : pal().success;
1255
+ const t2 = (Math.sin(now() % 2e3 / 2e3 * Math.PI * 2 - Math.PI / 2) + 1) / 2;
1256
+ const a = rgb(pal().muted), b = rgb(pal().warning);
1257
+ if (!a || !b) return pal().warning;
1258
+ const r = Math.round(a.r + (b.r - a.r) * t2);
1259
+ const g = Math.round(a.g + (b.g - a.g) * t2);
1260
+ const bl = Math.round(a.b + (b.b - a.b) * t2);
1261
+ return "#" + [r, g, bl].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
1262
+ };
1263
+ const timeColor = () => isRunning ? pal().warning : isError ? pal().error : pal().muted;
1264
+ const tokenText = () => !isExpanded() && entry.tokens !== void 0 && entry.tokens > 0 ? ` ${fmtTokens(entry.tokens)}` : "";
1265
+ const timeText = () => !isExpanded() && (elapsed() >= 2e3 || entry.endedAt !== void 0) ? fmtDurationShort(elapsed(), isRunning) : "";
1266
+ const suffixW = () => {
1267
+ let w = 0;
1268
+ const t2 = timeText();
1269
+ if (t2) w += 1 + visualWidth(t2);
1270
+ const tk = tokenText();
1271
+ if (tk) w += visualWidth(tk);
1272
+ return w;
1273
+ };
1274
+ const labelAvail = () => Math.max(6, panelWidth() - LEFT_PAD - suffixW());
1275
+ const labelText = () => {
1276
+ const max2 = labelAvail();
1277
+ const text = entry.title || entry.agent;
1278
+ const truncated = truncate(text, max2);
1279
+ const pad = Math.max(0, max2 - visualWidth(truncated));
1280
+ return truncated + " ".repeat(pad);
1281
+ };
1282
+ return [(() => {
1283
+ var _el$25 = _$createElement("text"), _el$26 = _$createElement("span"), _el$27 = _$createTextNode(` `), _el$28 = _$createElement("span"), _el$29 = _$createTextNode(` `), _el$30 = _$createElement("span");
1284
+ _$insertNode(_el$25, _el$26);
1285
+ _$insertNode(_el$25, _el$27);
1286
+ _$insertNode(_el$25, _el$28);
1287
+ _$insertNode(_el$25, _el$29);
1288
+ _$insertNode(_el$25, _el$30);
1289
+ _$setProp(_el$25, "onMouseUp", () => toggleExpand(entry.id));
1290
+ _$insert(_el$26, () => isExpanded() ? "\u25BC" : "\u25B6");
1291
+ _$insert(_el$28, statusDot);
1292
+ _$insert(_el$30, labelText);
1293
+ _$insert(_el$25, (() => {
1294
+ var _c$2 = _$memo(() => !!timeText());
1295
+ return () => _c$2() ? [" ", (() => {
1296
+ var _el$81 = _$createElement("span");
1297
+ _$insert(_el$81, timeText);
1298
+ _$effect((_$p) => _$setProp(_el$81, "style", {
1299
+ fg: timeColor()
1300
+ }, _$p));
1301
+ return _el$81;
1302
+ })()] : null;
1303
+ })(), null);
1304
+ _$insert(_el$25, (() => {
1305
+ var _c$3 = _$memo(() => !!tokenText());
1306
+ return () => _c$3() ? (() => {
1307
+ var _el$82 = _$createElement("span");
1308
+ _$insert(_el$82, tokenText);
1309
+ _$effect((_$p) => _$setProp(_el$82, "style", {
1310
+ fg: pal().muted
1311
+ }, _$p));
1312
+ return _el$82;
1313
+ })() : null;
1314
+ })(), null);
1315
+ _$effect((_p$) => {
1316
+ var _v$3 = {
1317
+ fg: pal().muted
1318
+ }, _v$4 = {
1319
+ fg: statusColor()
1320
+ }, _v$5 = {
1321
+ fg: pal().text
1322
+ };
1323
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$26, "style", _v$3, _p$.e));
1324
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$28, "style", _v$4, _p$.t));
1325
+ _v$5 !== _p$.a && (_p$.a = _$setProp(_el$30, "style", _v$5, _p$.a));
1326
+ return _p$;
1327
+ }, {
1328
+ e: void 0,
1329
+ t: void 0,
1330
+ a: void 0
1331
+ });
1332
+ return _el$25;
1333
+ })(), _$createComponent(Show, {
1334
+ get when() {
1335
+ return isExpanded();
1336
+ },
1337
+ get children() {
1338
+ return [(() => {
1339
+ var _el$31 = _$createElement("text"), _el$32 = _$createTextNode(` `), _el$33 = _$createElement("span"), _el$34 = _$createTextNode(`: `), _el$35 = _$createElement("span"), _el$36 = _$createElement("span");
1340
+ _$insertNode(_el$31, _el$32);
1341
+ _$insertNode(_el$31, _el$33);
1342
+ _$insertNode(_el$31, _el$35);
1343
+ _$insertNode(_el$31, _el$36);
1344
+ _$insertNode(_el$33, _el$34);
1345
+ _$insert(_el$33, () => t("agent.label"), _el$34);
1346
+ _$insert(_el$35, () => " ".repeat(expandedPad(t("agent.label"))));
1347
+ _$insert(_el$36, () => entry.agent);
1348
+ _$effect((_p$) => {
1349
+ var _v$6 = {
1350
+ fg: pal().primary
1351
+ }, _v$7 = {
1352
+ fg: pal().muted
1353
+ }, _v$8 = {
1354
+ fg: pal().muted
1355
+ };
1356
+ _v$6 !== _p$.e && (_p$.e = _$setProp(_el$33, "style", _v$6, _p$.e));
1357
+ _v$7 !== _p$.t && (_p$.t = _$setProp(_el$35, "style", _v$7, _p$.t));
1358
+ _v$8 !== _p$.a && (_p$.a = _$setProp(_el$36, "style", _v$8, _p$.a));
1359
+ return _p$;
1360
+ }, {
1361
+ e: void 0,
1362
+ t: void 0,
1363
+ a: void 0
1364
+ });
1365
+ return _el$31;
1366
+ })(), (() => {
1367
+ var _el$37 = _$createElement("text"), _el$38 = _$createTextNode(` `), _el$39 = _$createElement("span"), _el$40 = _$createTextNode(`: `), _el$41 = _$createElement("span"), _el$42 = _$createElement("span");
1368
+ _$insertNode(_el$37, _el$38);
1369
+ _$insertNode(_el$37, _el$39);
1370
+ _$insertNode(_el$37, _el$41);
1371
+ _$insertNode(_el$37, _el$42);
1372
+ _$insertNode(_el$39, _el$40);
1373
+ _$insert(_el$39, () => t("status.label"), _el$40);
1374
+ _$insert(_el$41, () => " ".repeat(expandedPad(t("status.label"))));
1375
+ _$insert(_el$42, () => isRunning ? t("status.running") : isError ? t("status.error") : t("status.done"));
1376
+ _$effect((_p$) => {
1377
+ var _v$9 = {
1378
+ fg: pal().primary
1379
+ }, _v$0 = {
1380
+ fg: pal().muted
1381
+ }, _v$1 = {
1382
+ fg: isRunning ? pal().warning : isError ? pal().error : pal().success
1383
+ };
1384
+ _v$9 !== _p$.e && (_p$.e = _$setProp(_el$39, "style", _v$9, _p$.e));
1385
+ _v$0 !== _p$.t && (_p$.t = _$setProp(_el$41, "style", _v$0, _p$.t));
1386
+ _v$1 !== _p$.a && (_p$.a = _$setProp(_el$42, "style", _v$1, _p$.a));
1387
+ return _p$;
1388
+ }, {
1389
+ e: void 0,
1390
+ t: void 0,
1391
+ a: void 0
1392
+ });
1393
+ return _el$37;
1394
+ })(), _$createComponent(Show, {
1395
+ get when() {
1396
+ return elapsed() >= 2e3 || entry.endedAt !== void 0;
1397
+ },
1398
+ get children() {
1399
+ var _el$43 = _$createElement("text"), _el$44 = _$createTextNode(` `), _el$45 = _$createElement("span"), _el$46 = _$createTextNode(`: `), _el$47 = _$createElement("span"), _el$48 = _$createElement("span");
1400
+ _$insertNode(_el$43, _el$44);
1401
+ _$insertNode(_el$43, _el$45);
1402
+ _$insertNode(_el$43, _el$47);
1403
+ _$insertNode(_el$43, _el$48);
1404
+ _$insertNode(_el$45, _el$46);
1405
+ _$insert(_el$45, () => t("time.label"), _el$46);
1406
+ _$insert(_el$47, () => " ".repeat(expandedPad(t("time.label"))));
1407
+ _$insert(_el$48, () => fmtDurationShort(elapsed(), isRunning));
1408
+ _$effect((_p$) => {
1409
+ var _v$10 = {
1410
+ fg: pal().primary
1411
+ }, _v$11 = {
1412
+ fg: pal().muted
1413
+ }, _v$12 = {
1414
+ fg: pal().muted
1415
+ };
1416
+ _v$10 !== _p$.e && (_p$.e = _$setProp(_el$45, "style", _v$10, _p$.e));
1417
+ _v$11 !== _p$.t && (_p$.t = _$setProp(_el$47, "style", _v$11, _p$.t));
1418
+ _v$12 !== _p$.a && (_p$.a = _$setProp(_el$48, "style", _v$12, _p$.a));
1419
+ return _p$;
1420
+ }, {
1421
+ e: void 0,
1422
+ t: void 0,
1423
+ a: void 0
1424
+ });
1425
+ return _el$43;
1426
+ }
1427
+ }), _$createComponent(Show, {
1428
+ get when() {
1429
+ return entry.tokens !== void 0;
1430
+ },
1431
+ get children() {
1432
+ var _el$49 = _$createElement("text"), _el$50 = _$createTextNode(` `), _el$51 = _$createElement("span"), _el$52 = _$createTextNode(`: `), _el$53 = _$createElement("span"), _el$54 = _$createElement("span");
1433
+ _$insertNode(_el$49, _el$50);
1434
+ _$insertNode(_el$49, _el$51);
1435
+ _$insertNode(_el$49, _el$53);
1436
+ _$insertNode(_el$49, _el$54);
1437
+ _$insertNode(_el$51, _el$52);
1438
+ _$insert(_el$51, () => t("tokens.label"), _el$52);
1439
+ _$insert(_el$53, () => " ".repeat(expandedPad(t("tokens.label"))));
1440
+ _$insert(_el$54, () => fmtTokens(entry.tokens));
1441
+ _$effect((_p$) => {
1442
+ var _v$13 = {
1443
+ fg: pal().primary
1444
+ }, _v$14 = {
1445
+ fg: pal().muted
1446
+ }, _v$15 = {
1447
+ fg: pal().muted
1448
+ };
1449
+ _v$13 !== _p$.e && (_p$.e = _$setProp(_el$51, "style", _v$13, _p$.e));
1450
+ _v$14 !== _p$.t && (_p$.t = _$setProp(_el$53, "style", _v$14, _p$.t));
1451
+ _v$15 !== _p$.a && (_p$.a = _$setProp(_el$54, "style", _v$15, _p$.a));
1452
+ return _p$;
1453
+ }, {
1454
+ e: void 0,
1455
+ t: void 0,
1456
+ a: void 0
1457
+ });
1458
+ return _el$49;
1459
+ }
1460
+ }), _$createComponent(Show, {
1461
+ get when() {
1462
+ return entry.error;
1463
+ },
1464
+ get children() {
1465
+ var _el$55 = _$createElement("text"), _el$56 = _$createTextNode(` `), _el$57 = _$createElement("span"), _el$58 = _$createTextNode(`: `), _el$59 = _$createElement("span"), _el$60 = _$createElement("span");
1466
+ _$insertNode(_el$55, _el$56);
1467
+ _$insertNode(_el$55, _el$57);
1468
+ _$insertNode(_el$55, _el$59);
1469
+ _$insertNode(_el$55, _el$60);
1470
+ _$insertNode(_el$57, _el$58);
1471
+ _$insert(_el$57, () => t("error.label"), _el$58);
1472
+ _$insert(_el$59, () => " ".repeat(expandedPad(t("error.label"))));
1473
+ _$insert(_el$60, () => truncate(String(entry.error), expandedValAvail()));
1474
+ _$effect((_p$) => {
1475
+ var _v$16 = {
1476
+ fg: pal().error
1477
+ }, _v$17 = {
1478
+ fg: pal().muted
1479
+ }, _v$18 = {
1480
+ fg: pal().error
1481
+ };
1482
+ _v$16 !== _p$.e && (_p$.e = _$setProp(_el$57, "style", _v$16, _p$.e));
1483
+ _v$17 !== _p$.t && (_p$.t = _$setProp(_el$59, "style", _v$17, _p$.t));
1484
+ _v$18 !== _p$.a && (_p$.a = _$setProp(_el$60, "style", _v$18, _p$.a));
1485
+ return _p$;
1486
+ }, {
1487
+ e: void 0,
1488
+ t: void 0,
1489
+ a: void 0
1490
+ });
1491
+ return _el$55;
1492
+ }
1493
+ }), _$createComponent(Show, {
1494
+ get when() {
1495
+ return entry.cost !== void 0;
1496
+ },
1497
+ get children() {
1498
+ var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(` `), _el$63 = _$createElement("span"), _el$64 = _$createTextNode(`: `), _el$65 = _$createElement("span"), _el$66 = _$createElement("span"), _el$67 = _$createTextNode(`$`);
1499
+ _$insertNode(_el$61, _el$62);
1500
+ _$insertNode(_el$61, _el$63);
1501
+ _$insertNode(_el$61, _el$65);
1502
+ _$insertNode(_el$61, _el$66);
1503
+ _$insertNode(_el$63, _el$64);
1504
+ _$insert(_el$63, () => t("cost.label"), _el$64);
1505
+ _$insert(_el$65, () => " ".repeat(expandedPad(t("cost.label"))));
1506
+ _$insertNode(_el$66, _el$67);
1507
+ _$insert(_el$66, () => entry.cost.toFixed(4), null);
1508
+ _$effect((_p$) => {
1509
+ var _v$19 = {
1510
+ fg: pal().primary
1511
+ }, _v$20 = {
1512
+ fg: pal().muted
1513
+ }, _v$21 = {
1514
+ fg: pal().muted
1515
+ };
1516
+ _v$19 !== _p$.e && (_p$.e = _$setProp(_el$63, "style", _v$19, _p$.e));
1517
+ _v$20 !== _p$.t && (_p$.t = _$setProp(_el$65, "style", _v$20, _p$.t));
1518
+ _v$21 !== _p$.a && (_p$.a = _$setProp(_el$66, "style", _v$21, _p$.a));
1519
+ return _p$;
1520
+ }, {
1521
+ e: void 0,
1522
+ t: void 0,
1523
+ a: void 0
1524
+ });
1525
+ return _el$61;
1526
+ }
1527
+ }), _$createComponent(Show, {
1528
+ get when() {
1529
+ return entry.model;
1530
+ },
1531
+ get children() {
1532
+ var _el$68 = _$createElement("text"), _el$69 = _$createTextNode(` `), _el$70 = _$createElement("span"), _el$71 = _$createTextNode(`: `), _el$72 = _$createElement("span"), _el$73 = _$createElement("span");
1533
+ _$insertNode(_el$68, _el$69);
1534
+ _$insertNode(_el$68, _el$70);
1535
+ _$insertNode(_el$68, _el$72);
1536
+ _$insertNode(_el$68, _el$73);
1537
+ _$insertNode(_el$70, _el$71);
1538
+ _$insert(_el$70, () => t("model.label"), _el$71);
1539
+ _$insert(_el$72, () => " ".repeat(expandedPad(t("model.label"))));
1540
+ _$insert(_el$73, () => truncate(entry.model, expandedValAvail()));
1541
+ _$effect((_p$) => {
1542
+ var _v$22 = {
1543
+ fg: pal().primary
1544
+ }, _v$23 = {
1545
+ fg: pal().muted
1546
+ }, _v$24 = {
1547
+ fg: pal().muted
1548
+ };
1549
+ _v$22 !== _p$.e && (_p$.e = _$setProp(_el$70, "style", _v$22, _p$.e));
1550
+ _v$23 !== _p$.t && (_p$.t = _$setProp(_el$72, "style", _v$23, _p$.t));
1551
+ _v$24 !== _p$.a && (_p$.a = _$setProp(_el$73, "style", _v$24, _p$.a));
1552
+ return _p$;
1553
+ }, {
1554
+ e: void 0,
1555
+ t: void 0,
1556
+ a: void 0
1557
+ });
1558
+ return _el$68;
1559
+ }
1560
+ }), _$createComponent(Show, {
1561
+ get when() {
1562
+ return entry.todoTotal !== void 0;
1563
+ },
1564
+ get children() {
1565
+ var _el$74 = _$createElement("text"), _el$75 = _$createTextNode(` `), _el$76 = _$createElement("span"), _el$77 = _$createTextNode(`: `), _el$78 = _$createElement("span"), _el$79 = _$createElement("span"), _el$80 = _$createTextNode(`/`);
1566
+ _$insertNode(_el$74, _el$75);
1567
+ _$insertNode(_el$74, _el$76);
1568
+ _$insertNode(_el$74, _el$78);
1569
+ _$insertNode(_el$74, _el$79);
1570
+ _$insertNode(_el$76, _el$77);
1571
+ _$insert(_el$76, () => t("todo.label"), _el$77);
1572
+ _$insert(_el$78, () => " ".repeat(expandedPad(t("todo.label"))));
1573
+ _$insertNode(_el$79, _el$80);
1574
+ _$insert(_el$79, () => entry.todoDone, _el$80);
1575
+ _$insert(_el$79, () => entry.todoTotal, null);
1576
+ _$effect((_p$) => {
1577
+ var _v$25 = {
1578
+ fg: pal().primary
1579
+ }, _v$26 = {
1580
+ fg: pal().muted
1581
+ }, _v$27 = {
1582
+ fg: pal().muted
1583
+ };
1584
+ _v$25 !== _p$.e && (_p$.e = _$setProp(_el$76, "style", _v$25, _p$.e));
1585
+ _v$26 !== _p$.t && (_p$.t = _$setProp(_el$78, "style", _v$26, _p$.t));
1586
+ _v$27 !== _p$.a && (_p$.a = _$setProp(_el$79, "style", _v$27, _p$.a));
1587
+ return _p$;
1588
+ }, {
1589
+ e: void 0,
1590
+ t: void 0,
1591
+ a: void 0
1592
+ });
1593
+ return _el$74;
1594
+ }
1595
+ }), _$createComponent(Show, {
1596
+ get when() {
1597
+ return entry.sessionId || isRunning;
1598
+ },
1599
+ get children() {
1600
+ return (() => {
1601
+ const dismissLabel = () => `- ${t("dismiss.label")}`;
1602
+ const openPrefix = () => " \u2192 ";
1603
+ const openFull = () => entry.sessionId ? openPrefix() + t("open.label") : "";
1604
+ const openW = () => entry.sessionId ? visualWidth(openFull()) : 0;
1605
+ const spacerW = () => Math.max(
1606
+ 1,
1607
+ panelWidth() - openW() - visualWidth(dismissLabel()) - 2
1608
+ /* indent */
1609
+ );
1610
+ return (() => {
1611
+ var _el$83 = _$createElement("box");
1612
+ _$setProp(_el$83, "flexDirection", "row");
1613
+ _$insert(_el$83, _$createComponent(Show, {
1614
+ get when() {
1615
+ return entry.sessionId;
1616
+ },
1617
+ get fallback() {
1618
+ return (() => {
1619
+ var _el$90 = _$createElement("text");
1620
+ _$insertNode(_el$90, _$createTextNode(` `));
1621
+ return _el$90;
1622
+ })();
1623
+ },
1624
+ get children() {
1625
+ var _el$84 = _$createElement("text"), _el$85 = _$createElement("span"), _el$86 = _$createElement("span");
1626
+ _$insertNode(_el$84, _el$85);
1627
+ _$insertNode(_el$84, _el$86);
1628
+ _$setProp(_el$84, "onMouseOver", () => setHoveredOpen(entry.id));
1629
+ _$setProp(_el$84, "onMouseOut", () => setHoveredOpen(void 0));
1630
+ _$setProp(_el$84, "onMouseUp", () => {
1631
+ if (entry.sessionId) {
1632
+ props.api.route.navigate("session", {
1633
+ sessionID: entry.sessionId
1634
+ });
1635
+ }
1636
+ });
1637
+ _$insert(_el$85, openPrefix);
1638
+ _$insert(_el$86, () => t("open.label"));
1639
+ _$effect((_p$) => {
1640
+ var _v$28 = {
1641
+ fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
1642
+ }, _v$29 = {
1643
+ fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
1644
+ };
1645
+ _v$28 !== _p$.e && (_p$.e = _$setProp(_el$85, "style", _v$28, _p$.e));
1646
+ _v$29 !== _p$.t && (_p$.t = _$setProp(_el$86, "style", _v$29, _p$.t));
1647
+ return _p$;
1648
+ }, {
1649
+ e: void 0,
1650
+ t: void 0
1651
+ });
1652
+ return _el$84;
1653
+ }
1654
+ }), null);
1655
+ _$insert(_el$83, _$createComponent(Show, {
1656
+ when: isRunning,
1657
+ get children() {
1658
+ return [(() => {
1659
+ var _el$87 = _$createElement("text");
1660
+ _$insert(_el$87, () => " ".repeat(spacerW()));
1661
+ _$effect((_$p) => _$setProp(_el$87, "style", {
1662
+ fg: pal().muted
1663
+ }, _$p));
1664
+ return _el$87;
1665
+ })(), (() => {
1666
+ var _el$88 = _$createElement("text"), _el$89 = _$createElement("span");
1667
+ _$insertNode(_el$88, _el$89);
1668
+ _$setProp(_el$88, "onMouseOver", () => setHoveredDismiss(entry.id));
1669
+ _$setProp(_el$88, "onMouseOut", () => setHoveredDismiss(void 0));
1670
+ _$setProp(_el$88, "onMouseUp", () => {
1671
+ upsertEntry({
1672
+ id: entry.id,
1673
+ title: entry.title,
1674
+ agent: entry.agent,
1675
+ prompt: entry.prompt,
1676
+ status: "done"
1677
+ });
1678
+ });
1679
+ _$insert(_el$89, dismissLabel);
1680
+ _$effect((_$p) => _$setProp(_el$89, "style", {
1681
+ fg: hoveredDismiss() === entry.id ? pal().warning : pal().muted
1682
+ }, _$p));
1683
+ return _el$88;
1684
+ })()];
1685
+ }
1686
+ }), null);
1687
+ return _el$83;
1688
+ })();
1689
+ })();
1690
+ }
1691
+ })];
1692
+ }
1693
+ })];
1694
+ }
1695
+ }), null);
1696
+ _$insert(_el$7, _$createComponent(Show, {
1697
+ get when() {
1698
+ return hiddenBelow() > 0 || (props.sortOrder() === "desc" ? scrollOffset() > 0 : entryList().length > max() && clampedOffset() < entryList().length - max());
1699
+ },
1700
+ get children() {
1701
+ return (() => {
1702
+ const showMore = hiddenBelow() > 0;
1703
+ const showTop = props.sortOrder() === "desc" ? scrollOffset() > 0 : entryList().length > max() && clampedOffset() < entryList().length - max();
1704
+ const left = showMore ? ` \u2193 ${hiddenBelow()} ${t("scroll.more")}` : " ";
1705
+ const right = props.sortOrder() === "desc" ? `\u2191 ${t("scroll.top")}` : `\u2193 ${t("scroll.bottom")}`;
1706
+ const pad = showTop ? Math.max(1, panelWidth() - visualWidth(left) - visualWidth(right)) : 0;
1707
+ return (() => {
1708
+ var _el$92 = _$createElement("box"), _el$93 = _$createElement("text"), _el$94 = _$createElement("span");
1709
+ _$insertNode(_el$92, _el$93);
1710
+ _$setProp(_el$92, "flexDirection", "row");
1711
+ _$insertNode(_el$93, _el$94);
1712
+ _$setProp(_el$93, "onMouseOver", () => showMore && setHoveredMoreBelow(true));
1713
+ _$setProp(_el$93, "onMouseOut", () => setHoveredMoreBelow(false));
1714
+ _$setProp(_el$93, "onMouseUp", () => {
1715
+ if (!showMore) return;
1716
+ const total = entryList().length;
1717
+ const m = max();
1718
+ if (total <= m) return;
1719
+ setScrollOffset((prev) => Math.min(total - m, prev + m));
1720
+ try {
1721
+ persistScroll(props.sessionId, scrollOffset());
1722
+ } catch {
1723
+ }
1724
+ setHoveredMoreBelow(false);
1725
+ });
1726
+ _$insert(_el$94, left);
1727
+ _$insert(_el$92, showTop ? [(() => {
1728
+ var _el$95 = _$createElement("text");
1729
+ _$insert(_el$95, () => " ".repeat(pad));
1730
+ _$effect((_$p) => _$setProp(_el$95, "style", {
1731
+ fg: pal().muted
1732
+ }, _$p));
1733
+ return _el$95;
1734
+ })(), (() => {
1735
+ var _el$96 = _$createElement("text"), _el$97 = _$createElement("span");
1736
+ _$insertNode(_el$96, _el$97);
1737
+ _$setProp(_el$96, "onMouseOver", () => setHoveredTop(true));
1738
+ _$setProp(_el$96, "onMouseOut", () => setHoveredTop(false));
1739
+ _$setProp(_el$96, "onMouseUp", () => {
1740
+ const total = entryList().length;
1741
+ const m = max();
1742
+ if (props.sortOrder() === "desc") {
1743
+ setScrollOffset(0);
1744
+ } else {
1745
+ setScrollOffset(Math.max(0, total - m));
1746
+ }
1747
+ setHoveredTop(false);
1748
+ });
1749
+ _$insert(_el$97, right);
1750
+ _$effect((_$p) => _$setProp(_el$97, "style", {
1751
+ fg: hoveredTop() ? pal().warning : pal().muted
1752
+ }, _$p));
1753
+ return _el$96;
1754
+ })()] : null, null);
1755
+ _$effect((_$p) => _$setProp(_el$94, "style", {
1756
+ fg: showMore && hoveredMoreBelow() ? pal().warning : pal().muted
1757
+ }, _$p));
1758
+ return _el$92;
1759
+ })();
1760
+ })();
1761
+ }
1762
+ }), null);
1763
+ return _el$7;
1764
+ }
1765
+ })];
1766
+ }
1767
+ }), null);
1768
+ _$effect((_p$) => {
1769
+ var _v$ = {
1770
+ fg: pal().muted
1771
+ }, _v$2 = {
1772
+ fg: pal().primary
1773
+ };
1774
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$, _p$.e));
1775
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$2, _p$.t));
1776
+ return _p$;
1777
+ }, {
1778
+ e: void 0,
1779
+ t: void 0
1780
+ });
1781
+ return _el$;
1782
+ })();
1783
+ }
1784
+ function createSidebarSlot(api, sig) {
1785
+ return {
1786
+ order: 60,
1787
+ slots: {
1788
+ sidebar_content(ctx, input) {
1789
+ sig.sessionId = input.session_id;
1790
+ return _$createComponent(SubAgentPanel, {
1791
+ get theme() {
1792
+ return ctx.theme.current;
1793
+ },
1794
+ api,
1795
+ get lang() {
1796
+ return sig.lang;
1797
+ },
1798
+ get maxEntries() {
1799
+ return sig.maxEntries;
1800
+ },
1801
+ get sortOrder() {
1802
+ return sig.sortOrder;
1803
+ },
1804
+ get scrollMode() {
1805
+ return sig.scrollMode;
1806
+ },
1807
+ get sessionId() {
1808
+ return input.session_id;
1809
+ }
1810
+ });
1811
+ }
1812
+ }
1813
+ };
1814
+ }
1815
+ var KV_PREFIX = "subagent_magazine";
1816
+ var tui = async (api) => {
1817
+ const stored = String(api.kv.get(`${KV_PREFIX}.lang`, ""));
1818
+ const initialLang = stored === "zh" || stored === "en" ? stored : detectLang();
1819
+ const [lang, setLang] = createSignal(initialLang);
1820
+ const [maxEntries, setMaxEntries] = createSignal(parseInt(String(api.kv.get(`${KV_PREFIX}.max_entries`, "10")), 10) || 10);
1821
+ const [sortOrder, setSortOrder] = createSignal(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
1822
+ const [scrollMode, setScrollMode] = createSignal(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
1823
+ const signals = {
1824
+ lang,
1825
+ setLang,
1826
+ maxEntries,
1827
+ setMaxEntries,
1828
+ sortOrder,
1829
+ setSortOrder,
1830
+ scrollMode,
1831
+ setScrollMode,
1832
+ sessionId: ""
1833
+ };
1834
+ api.slots.register(createSidebarSlot(api, signals));
1835
+ api.command?.register(() => [{
1836
+ title: "SubAgent Magazine: Language",
1837
+ value: "subagent-lang",
1838
+ description: "Switch display language (\u4E2D\u6587 / English)",
1839
+ slash: {
1840
+ name: "subagent-lang"
1841
+ },
1842
+ onSelect: (dialog) => {
1843
+ dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
1844
+ title: "Language / \u8BED\u8A00",
1845
+ options: [{
1846
+ title: "\u4E2D\u6587",
1847
+ value: "zh"
1848
+ }, {
1849
+ title: "English",
1850
+ value: "en"
1851
+ }],
1852
+ onSelect: (opt) => {
1853
+ const l = opt.value;
1854
+ setLang(l);
1855
+ api.kv.set(`${KV_PREFIX}.lang`, l);
1856
+ api.ui.toast({
1857
+ message: l === "zh" ? "\u8BED\u8A00: \u4E2D\u6587" : "Language: English"
1858
+ });
1859
+ dialog?.clear();
1860
+ }
1861
+ }));
1862
+ }
1863
+ }, {
1864
+ title: "SubAgent Magazine: Sort Order",
1865
+ value: "subagent-order",
1866
+ description: "Set sub-agent entry sort order (desc / asc)",
1867
+ slash: {
1868
+ name: "subagent-order"
1869
+ },
1870
+ onSelect: (dialog) => {
1871
+ const t = (k) => I18N[lang()][k] ?? k;
1872
+ dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
1873
+ title: "Sort Order / \u6392\u5E8F\u65B9\u5F0F",
1874
+ get options() {
1875
+ return [{
1876
+ title: t("order.desc"),
1877
+ value: "desc"
1878
+ }, {
1879
+ title: t("order.asc"),
1880
+ value: "asc"
1881
+ }];
1882
+ },
1883
+ onSelect: (opt) => {
1884
+ const o = opt.value;
1885
+ setSortOrder(o);
1886
+ api.kv.set(`${KV_PREFIX}.order`, o);
1887
+ api.ui.toast({
1888
+ message: o === "desc" ? t("order.desc") : t("order.asc")
1889
+ });
1890
+ dialog?.clear();
1891
+ }
1892
+ }));
1893
+ }
1894
+ }, {
1895
+ title: "SubAgent Magazine: Scroll Mode",
1896
+ value: "subagent-scroll",
1897
+ description: "Set scroll mode (wheel / click)",
1898
+ slash: {
1899
+ name: "subagent-scroll"
1900
+ },
1901
+ onSelect: (dialog) => {
1902
+ const t = (k) => I18N[lang()][k] ?? k;
1903
+ dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
1904
+ title: "Scroll Mode / \u6EDA\u52A8\u6A21\u5F0F",
1905
+ get options() {
1906
+ return [{
1907
+ title: t("scroll.wheel"),
1908
+ value: "wheel"
1909
+ }, {
1910
+ title: t("scroll.click"),
1911
+ value: "click"
1912
+ }];
1913
+ },
1914
+ onSelect: (opt) => {
1915
+ const m = opt.value;
1916
+ setScrollMode(m);
1917
+ api.kv.set(`${KV_PREFIX}.scroll_mode`, m);
1918
+ api.ui.toast({
1919
+ message: m === "wheel" ? t("scroll.wheel") : t("scroll.click")
1920
+ });
1921
+ dialog?.clear();
1922
+ }
1923
+ }));
1924
+ }
1925
+ }, {
1926
+ title: "SubAgent Magazine: Max Entries",
1927
+ value: "subagent-max",
1928
+ description: "Set max visible sub-agent entries in sidebar",
1929
+ slash: {
1930
+ name: "subagent-max"
1931
+ },
1932
+ onSelect: (dialog) => {
1933
+ dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
1934
+ title: "Max Visible Entries",
1935
+ description: () => (() => {
1936
+ var _el$98 = _$createElement("text");
1937
+ _$insertNode(_el$98, _$createTextNode(`Number of entries to show in the sidebar (1\u201350)`));
1938
+ return _el$98;
1939
+ })(),
1940
+ get value() {
1941
+ return String(maxEntries());
1942
+ },
1943
+ onConfirm: (val) => {
1944
+ const n = Math.max(1, Math.min(50, parseInt(val, 10) || 10));
1945
+ setMaxEntries(n);
1946
+ api.kv.set(`${KV_PREFIX}.max_entries`, n);
1947
+ api.ui.toast({
1948
+ message: `Max entries: ${n}`
1949
+ });
1950
+ dialog?.clear();
1951
+ }
1952
+ }));
1953
+ }
1954
+ }, {
1955
+ title: "SubAgent Magazine: Version",
1956
+ value: "subagent-version",
1957
+ description: "Show plugin version",
1958
+ slash: {
1959
+ name: "subagent-version"
1960
+ },
1961
+ onSelect: (dialog) => {
1962
+ api.ui.toast({
1963
+ message: `opencode-subagent-magazine v${PLUGIN_VERSION}`
1964
+ });
1965
+ dialog?.clear();
1966
+ }
1967
+ }, {
1968
+ title: "SubAgent Magazine: Session",
1969
+ value: "subagent-session",
1970
+ description: "Show current session ID",
1971
+ slash: {
1972
+ name: "subagent-session"
1973
+ },
1974
+ onSelect: (dialog) => {
1975
+ api.ui.toast({
1976
+ message: `Session: ${signals.sessionId}`
1977
+ });
1978
+ dialog?.clear();
1979
+ }
1980
+ }, {
1981
+ title: "SubAgent Magazine: Clear Running",
1982
+ value: "subagent-clear-running",
1983
+ description: "Mark all running sub-agent entries as done (for stuck/zombie entries)",
1984
+ slash: {
1985
+ name: "subagent-clear-running"
1986
+ },
1987
+ onSelect: (dialog) => {
1988
+ const entries = globalEntryCache.get(signals.sessionId);
1989
+ if (!entries || entries.size === 0) {
1990
+ const msg = signals.lang() === "zh" ? "\u6682\u65E0\u5B50\u4EE3\u7406\u6761\u76EE" : "No sub-agent entries found";
1991
+ api.ui.toast({
1992
+ message: msg
1993
+ });
1994
+ dialog?.clear();
1995
+ return;
1996
+ }
1997
+ let count = 0;
1998
+ for (const [, entry] of entries) {
1999
+ if (entry.status === "running") {
2000
+ entry.status = "done";
2001
+ entry.endedAt = Date.now();
2002
+ count++;
2003
+ }
2004
+ }
2005
+ if (count > 0) {
2006
+ try {
2007
+ const data = JSON.parse(String(api.kv.get(`${KV_PREFIX}.session_data`, "{}")));
2008
+ data[signals.sessionId] = {
2009
+ ts: Date.now(),
2010
+ entries: [...entries.values()],
2011
+ scroll: data[signals.sessionId]?.scroll ?? 0,
2012
+ expanded: data[signals.sessionId]?.expanded ?? ""
2013
+ };
2014
+ api.kv.set(`${KV_PREFIX}.session_data`, JSON.stringify(data));
2015
+ } catch {
2016
+ }
2017
+ const msg = signals.lang() === "zh" ? `\u5DF2\u6807\u8BB0 ${count} \u4E2A\u8FD0\u884C\u4E2D\u7684\u6761\u76EE\u4E3A\u5B8C\u6210` : `Marked ${count} running entries as done`;
2018
+ api.ui.toast({
2019
+ message: msg
2020
+ });
2021
+ } else {
2022
+ const msg = signals.lang() === "zh" ? "\u6CA1\u6709\u9700\u8981\u6E05\u7406\u7684\u8FD0\u884C\u4E2D\u6761\u76EE" : "No running entries to clear";
2023
+ api.ui.toast({
2024
+ message: msg
2025
+ });
2026
+ }
2027
+ dialog?.clear();
2028
+ }
2029
+ }]);
2030
+ };
2031
+ var mod = {
2032
+ id: "opencode-subagent-magazine",
2033
+ tui
2034
+ };
2035
+ var index_default = mod;
2036
+ export {
2037
+ index_default as default
2038
+ };