aoaoe 0.83.0 → 0.84.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/index.js +16 -5
- package/dist/input.d.ts +3 -0
- package/dist/input.js +21 -0
- package/dist/tui.d.ts +20 -4
- package/dist/tui.js +90 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -433,6 +433,17 @@ async function main() {
|
|
|
433
433
|
tui.log("system", `session not found: ${target}`);
|
|
434
434
|
}
|
|
435
435
|
});
|
|
436
|
+
// wire /mute toggle
|
|
437
|
+
input.onMute((target) => {
|
|
438
|
+
const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
|
|
439
|
+
const ok = tui.toggleMute(num ?? target);
|
|
440
|
+
if (ok) {
|
|
441
|
+
tui.log("system", `mute toggled: ${target}`);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
tui.log("system", `session not found: ${target}`);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
436
447
|
// wire mouse move to hover highlight on session cards (disabled in compact)
|
|
437
448
|
input.onMouseMove((row, _col) => {
|
|
438
449
|
if (tui.getViewMode() === "overview" && !tui.isCompact()) {
|
|
@@ -990,17 +1001,17 @@ async function daemonTick(config, poller, reasoner, executor, reasonerConsole, p
|
|
|
990
1001
|
}));
|
|
991
1002
|
const narration = narrateObservation(sessionInfos, changedTitles);
|
|
992
1003
|
tui.log("observation", narration + (userMessage ? " +your message" : ""));
|
|
993
|
-
// event highlights — call attention to important events
|
|
1004
|
+
// event highlights — call attention to important events (with sessionId for mute filtering)
|
|
994
1005
|
for (const snap of observation.sessions) {
|
|
995
1006
|
const s = snap.session;
|
|
996
1007
|
if (s.status === "error" && changedTitles.has(s.title)) {
|
|
997
|
-
tui.log("! action", `${s.title} hit an error! The AI will investigate
|
|
1008
|
+
tui.log("! action", `${s.title} hit an error! The AI will investigate.`, s.id);
|
|
998
1009
|
}
|
|
999
1010
|
if (s.status === "done" && changedTitles.has(s.title)) {
|
|
1000
|
-
tui.log("+ action", `${s.title} finished its task
|
|
1011
|
+
tui.log("+ action", `${s.title} finished its task!`, s.id);
|
|
1001
1012
|
}
|
|
1002
1013
|
if (snap.userActive) {
|
|
1003
|
-
tui.log("status", `You're working in ${s.title} — the AI won't interfere
|
|
1014
|
+
tui.log("status", `You're working in ${s.title} — the AI won't interfere.`, s.id);
|
|
1004
1015
|
}
|
|
1005
1016
|
}
|
|
1006
1017
|
}
|
|
@@ -1080,7 +1091,7 @@ async function daemonTick(config, poller, reasoner, executor, reasonerConsole, p
|
|
|
1080
1091
|
? `${plainEnglish} — ${friendlyError(entry.detail)}`
|
|
1081
1092
|
: plainEnglish;
|
|
1082
1093
|
if (tui) {
|
|
1083
|
-
tui.log(tag, displayText);
|
|
1094
|
+
tui.log(tag, displayText, sessionId);
|
|
1084
1095
|
}
|
|
1085
1096
|
else {
|
|
1086
1097
|
const icon = entry.success ? "+" : "!";
|
package/dist/input.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type FocusHandler = () => void;
|
|
|
11
11
|
export type MarkHandler = () => void;
|
|
12
12
|
export type JumpHandler = (num: number) => void;
|
|
13
13
|
export type MarksHandler = () => void;
|
|
14
|
+
export type MuteHandler = (target: string) => void;
|
|
14
15
|
export interface MouseEvent {
|
|
15
16
|
button: number;
|
|
16
17
|
col: number;
|
|
@@ -44,6 +45,7 @@ export declare class InputReader {
|
|
|
44
45
|
private markHandler;
|
|
45
46
|
private jumpHandler;
|
|
46
47
|
private marksHandler;
|
|
48
|
+
private muteHandler;
|
|
47
49
|
private mouseDataListener;
|
|
48
50
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
49
51
|
onQueueChange(handler: (count: number) => void): void;
|
|
@@ -61,6 +63,7 @@ export declare class InputReader {
|
|
|
61
63
|
onMark(handler: MarkHandler): void;
|
|
62
64
|
onJump(handler: JumpHandler): void;
|
|
63
65
|
onMarks(handler: MarksHandler): void;
|
|
66
|
+
onMute(handler: MuteHandler): void;
|
|
64
67
|
private notifyQueueChange;
|
|
65
68
|
start(): void;
|
|
66
69
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -43,6 +43,7 @@ export class InputReader {
|
|
|
43
43
|
markHandler = null;
|
|
44
44
|
jumpHandler = null;
|
|
45
45
|
marksHandler = null;
|
|
46
|
+
muteHandler = null;
|
|
46
47
|
mouseDataListener = null;
|
|
47
48
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
48
49
|
onScroll(handler) {
|
|
@@ -108,6 +109,10 @@ export class InputReader {
|
|
|
108
109
|
onMarks(handler) {
|
|
109
110
|
this.marksHandler = handler;
|
|
110
111
|
}
|
|
112
|
+
// register a callback for mute/unmute commands (/mute <target>)
|
|
113
|
+
onMute(handler) {
|
|
114
|
+
this.muteHandler = handler;
|
|
115
|
+
}
|
|
111
116
|
notifyQueueChange() {
|
|
112
117
|
this.queueChangeHandler?.(this.queue.length);
|
|
113
118
|
}
|
|
@@ -290,6 +295,7 @@ ${BOLD}navigation:${RESET}
|
|
|
290
295
|
/pin [N|name] pin/unpin a session to the top (toggle)
|
|
291
296
|
/bell toggle terminal bell on errors/completions
|
|
292
297
|
/focus toggle focus mode (show only pinned sessions)
|
|
298
|
+
/mute [N|name] mute/unmute a session's activity entries (toggle)
|
|
293
299
|
/mark bookmark current activity position
|
|
294
300
|
/jump N jump to bookmark N
|
|
295
301
|
/marks list all bookmarks
|
|
@@ -421,6 +427,21 @@ ${BOLD}other:${RESET}
|
|
|
421
427
|
console.error(`${DIM}focus not available (no TUI)${RESET}`);
|
|
422
428
|
}
|
|
423
429
|
break;
|
|
430
|
+
case "/mute": {
|
|
431
|
+
const muteArg = line.slice("/mute".length).trim();
|
|
432
|
+
if (this.muteHandler) {
|
|
433
|
+
if (muteArg) {
|
|
434
|
+
this.muteHandler(muteArg);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
console.error(`${DIM}usage: /mute <N|name> — toggle mute for a session${RESET}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
console.error(`${DIM}mute not available (no TUI)${RESET}`);
|
|
442
|
+
}
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
424
445
|
case "/mark":
|
|
425
446
|
if (this.markHandler) {
|
|
426
447
|
this.markHandler();
|
package/dist/tui.d.ts
CHANGED
|
@@ -28,10 +28,10 @@ declare const COMPACT_NAME_LEN = 10;
|
|
|
28
28
|
declare const PIN_ICON = "\u25B2";
|
|
29
29
|
/**
|
|
30
30
|
* Format sessions as inline compact tokens, wrapped to fit maxWidth.
|
|
31
|
-
* Each token: "{idx}{pin?}{dot}{name}" — e.g. "1▲●Alpha" for pinned, "2
|
|
31
|
+
* Each token: "{idx}{pin?}{mute?}{dot}{name}" — e.g. "1▲●Alpha" for pinned, "2◌●Bravo" for muted.
|
|
32
32
|
* Returns array of formatted row strings (one per display row).
|
|
33
33
|
*/
|
|
34
|
-
declare function formatCompactRows(sessions: DaemonSessionState[], maxWidth: number, pinnedIds?: Set<string>): string[];
|
|
34
|
+
declare function formatCompactRows(sessions: DaemonSessionState[], maxWidth: number, pinnedIds?: Set<string>, mutedIds?: Set<string>): string[];
|
|
35
35
|
/** Compute how many display rows compact mode needs (minimum 1). */
|
|
36
36
|
declare function computeCompactRowCount(sessions: DaemonSessionState[], maxWidth: number): number;
|
|
37
37
|
declare function phaseDisplay(phase: DaemonPhase, paused: boolean, spinnerFrame: number): string;
|
|
@@ -39,7 +39,12 @@ export interface ActivityEntry {
|
|
|
39
39
|
time: string;
|
|
40
40
|
tag: string;
|
|
41
41
|
text: string;
|
|
42
|
+
sessionId?: string;
|
|
42
43
|
}
|
|
44
|
+
/** Mute indicator for muted sessions (shown dim beside session card). */
|
|
45
|
+
declare const MUTE_ICON = "\u25CC";
|
|
46
|
+
/** Determine if an activity entry should be hidden due to muting. */
|
|
47
|
+
export declare function shouldMuteEntry(entry: ActivityEntry, mutedIds: Set<string>): boolean;
|
|
43
48
|
export declare class TUI {
|
|
44
49
|
private active;
|
|
45
50
|
private countdownTimer;
|
|
@@ -69,6 +74,7 @@ export declare class TUI {
|
|
|
69
74
|
private bookmarks;
|
|
70
75
|
private bellEnabled;
|
|
71
76
|
private lastBellAt;
|
|
77
|
+
private mutedIds;
|
|
72
78
|
private viewMode;
|
|
73
79
|
private drilldownSessionId;
|
|
74
80
|
private sessionOutputs;
|
|
@@ -113,6 +119,16 @@ export declare class TUI {
|
|
|
113
119
|
setBell(enabled: boolean): void;
|
|
114
120
|
/** Return whether terminal bell is enabled. */
|
|
115
121
|
isBellEnabled(): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Toggle mute for a session (by 1-indexed number, ID, ID prefix, or title).
|
|
124
|
+
* Muted sessions' activity entries are hidden from the log (still buffered + persisted).
|
|
125
|
+
* Returns true if session found.
|
|
126
|
+
*/
|
|
127
|
+
toggleMute(sessionIdOrIndex: string | number): boolean;
|
|
128
|
+
/** Check if a session ID is muted. */
|
|
129
|
+
isMuted(id: string): boolean;
|
|
130
|
+
/** Return count of muted sessions. */
|
|
131
|
+
getMutedCount(): number;
|
|
116
132
|
/**
|
|
117
133
|
* Add a bookmark at the current activity position.
|
|
118
134
|
* Returns the bookmark number (1-indexed) or 0 if buffer is empty.
|
|
@@ -136,7 +152,7 @@ export declare class TUI {
|
|
|
136
152
|
nextTickAt?: number;
|
|
137
153
|
pendingCount?: number;
|
|
138
154
|
}): void;
|
|
139
|
-
log(tag: string, text: string): void;
|
|
155
|
+
log(tag: string, text: string, sessionId?: string): void;
|
|
140
156
|
replayHistory(entries: HistoryEntry[]): void;
|
|
141
157
|
scrollUp(lines?: number): void;
|
|
142
158
|
scrollDown(lines?: number): void;
|
|
@@ -217,5 +233,5 @@ declare function formatSearchIndicator(pattern: string, matchCount: number, tota
|
|
|
217
233
|
* (row = headerHeight + 2 + i for 0-indexed session i)
|
|
218
234
|
*/
|
|
219
235
|
export declare function hitTestSession(row: number, headerHeight: number, sessionCount: number): number | null;
|
|
220
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline, sortSessions, nextSortMode, SORT_MODES, formatCompactRows, computeCompactRowCount, COMPACT_NAME_LEN, PIN_ICON };
|
|
236
|
+
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline, sortSessions, nextSortMode, SORT_MODES, formatCompactRows, computeCompactRowCount, COMPACT_NAME_LEN, PIN_ICON, MUTE_ICON };
|
|
221
237
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.js
CHANGED
|
@@ -87,10 +87,10 @@ const COMPACT_NAME_LEN = 10;
|
|
|
87
87
|
const PIN_ICON = "▲";
|
|
88
88
|
/**
|
|
89
89
|
* Format sessions as inline compact tokens, wrapped to fit maxWidth.
|
|
90
|
-
* Each token: "{idx}{pin?}{dot}{name}" — e.g. "1▲●Alpha" for pinned, "2
|
|
90
|
+
* Each token: "{idx}{pin?}{mute?}{dot}{name}" — e.g. "1▲●Alpha" for pinned, "2◌●Bravo" for muted.
|
|
91
91
|
* Returns array of formatted row strings (one per display row).
|
|
92
92
|
*/
|
|
93
|
-
function formatCompactRows(sessions, maxWidth, pinnedIds) {
|
|
93
|
+
function formatCompactRows(sessions, maxWidth, pinnedIds, mutedIds) {
|
|
94
94
|
if (sessions.length === 0)
|
|
95
95
|
return [`${DIM}no agents connected${RESET}`];
|
|
96
96
|
const tokens = [];
|
|
@@ -100,10 +100,12 @@ function formatCompactRows(sessions, maxWidth, pinnedIds) {
|
|
|
100
100
|
const idx = String(i + 1);
|
|
101
101
|
const dot = STATUS_DOT[s.status] ?? `${AMBER}${DOT.filled}${RESET}`;
|
|
102
102
|
const pinned = pinnedIds?.has(s.id) ?? false;
|
|
103
|
+
const muted = mutedIds?.has(s.id) ?? false;
|
|
103
104
|
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET}` : "";
|
|
105
|
+
const muteIcon = muted ? `${DIM}${MUTE_ICON}${RESET}` : "";
|
|
104
106
|
const name = truncatePlain(s.title, COMPACT_NAME_LEN);
|
|
105
|
-
tokens.push(`${SLATE}${idx}${RESET}${pin}${dot}${BOLD}${name}${RESET}`);
|
|
106
|
-
widths.push(idx.length + (pinned ? 1 : 0) + 1 + name.length);
|
|
107
|
+
tokens.push(`${SLATE}${idx}${RESET}${pin}${muteIcon}${dot}${BOLD}${name}${RESET}`);
|
|
108
|
+
widths.push(idx.length + (pinned ? 1 : 0) + (muted ? 1 : 0) + 1 + name.length);
|
|
107
109
|
}
|
|
108
110
|
const rows = [];
|
|
109
111
|
let currentRow = "";
|
|
@@ -152,6 +154,15 @@ function phaseDisplay(phase, paused, spinnerFrame) {
|
|
|
152
154
|
default: return `${SLATE}${phase}${RESET}`;
|
|
153
155
|
}
|
|
154
156
|
}
|
|
157
|
+
// ── Mute helpers ──────────────────────────────────────────────────────────────
|
|
158
|
+
/** Mute indicator for muted sessions (shown dim beside session card). */
|
|
159
|
+
const MUTE_ICON = "◌";
|
|
160
|
+
/** Determine if an activity entry should be hidden due to muting. */
|
|
161
|
+
export function shouldMuteEntry(entry, mutedIds) {
|
|
162
|
+
if (!entry.sessionId)
|
|
163
|
+
return false;
|
|
164
|
+
return mutedIds.has(entry.sessionId);
|
|
165
|
+
}
|
|
155
166
|
// ── TUI class ───────────────────────────────────────────────────────────────
|
|
156
167
|
export class TUI {
|
|
157
168
|
active = false;
|
|
@@ -182,6 +193,7 @@ export class TUI {
|
|
|
182
193
|
bookmarks = []; // saved positions in activity buffer
|
|
183
194
|
bellEnabled = false;
|
|
184
195
|
lastBellAt = 0;
|
|
196
|
+
mutedIds = new Set(); // muted session IDs (activity entries hidden)
|
|
185
197
|
// drill-down mode: show a single session's full output
|
|
186
198
|
viewMode = "overview";
|
|
187
199
|
drilldownSessionId = null;
|
|
@@ -337,6 +349,44 @@ export class TUI {
|
|
|
337
349
|
isBellEnabled() {
|
|
338
350
|
return this.bellEnabled;
|
|
339
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Toggle mute for a session (by 1-indexed number, ID, ID prefix, or title).
|
|
354
|
+
* Muted sessions' activity entries are hidden from the log (still buffered + persisted).
|
|
355
|
+
* Returns true if session found.
|
|
356
|
+
*/
|
|
357
|
+
toggleMute(sessionIdOrIndex) {
|
|
358
|
+
let sessionId;
|
|
359
|
+
if (typeof sessionIdOrIndex === "number") {
|
|
360
|
+
sessionId = this.sessions[sessionIdOrIndex - 1]?.id;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const needle = sessionIdOrIndex.toLowerCase();
|
|
364
|
+
const match = this.sessions.find((s) => s.id === sessionIdOrIndex || s.id.startsWith(needle) || s.title.toLowerCase() === needle);
|
|
365
|
+
sessionId = match?.id;
|
|
366
|
+
}
|
|
367
|
+
if (!sessionId)
|
|
368
|
+
return false;
|
|
369
|
+
if (this.mutedIds.has(sessionId)) {
|
|
370
|
+
this.mutedIds.delete(sessionId);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
this.mutedIds.add(sessionId);
|
|
374
|
+
}
|
|
375
|
+
// repaint sessions (mute icon) and activity (filter changes)
|
|
376
|
+
if (this.active) {
|
|
377
|
+
this.paintSessions();
|
|
378
|
+
this.repaintActivityRegion();
|
|
379
|
+
}
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
/** Check if a session ID is muted. */
|
|
383
|
+
isMuted(id) {
|
|
384
|
+
return this.mutedIds.has(id);
|
|
385
|
+
}
|
|
386
|
+
/** Return count of muted sessions. */
|
|
387
|
+
getMutedCount() {
|
|
388
|
+
return this.mutedIds.size;
|
|
389
|
+
}
|
|
340
390
|
/**
|
|
341
391
|
* Add a bookmark at the current activity position.
|
|
342
392
|
* Returns the bookmark number (1-indexed) or 0 if buffer is empty.
|
|
@@ -430,10 +480,11 @@ export class TUI {
|
|
|
430
480
|
}
|
|
431
481
|
// ── Activity log ────────────────────────────────────────────────────────
|
|
432
482
|
// push a new activity entry — this is the primary way to show output
|
|
433
|
-
|
|
483
|
+
// sessionId optionally ties the entry to a specific session (for mute filtering)
|
|
484
|
+
log(tag, text, sessionId) {
|
|
434
485
|
const now = new Date();
|
|
435
486
|
const time = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}`;
|
|
436
|
-
const entry = { time, tag, text };
|
|
487
|
+
const entry = { time, tag, text, ...(sessionId ? { sessionId } : {}) };
|
|
437
488
|
this.activityBuffer.push(entry);
|
|
438
489
|
this.activityTimestamps.push(now.getTime());
|
|
439
490
|
if (this.activityBuffer.length > this.maxActivity) {
|
|
@@ -449,7 +500,11 @@ export class TUI {
|
|
|
449
500
|
}
|
|
450
501
|
}
|
|
451
502
|
if (this.active) {
|
|
452
|
-
|
|
503
|
+
// muted entries are still buffered + persisted but hidden from display
|
|
504
|
+
if (shouldMuteEntry(entry, this.mutedIds)) {
|
|
505
|
+
// silently skip display — entry is in buffer for scroll-back if unmuted later
|
|
506
|
+
}
|
|
507
|
+
else if (this.searchPattern) {
|
|
453
508
|
// search active: only show new entry if it matches
|
|
454
509
|
if (matchesSearch(entry, this.searchPattern)) {
|
|
455
510
|
if (this.scrollOffset > 0) {
|
|
@@ -491,9 +546,12 @@ export class TUI {
|
|
|
491
546
|
return;
|
|
492
547
|
const visibleLines = this.scrollBottom - this.scrollTop + 1;
|
|
493
548
|
const n = lines ?? Math.max(1, Math.floor(visibleLines / 2));
|
|
549
|
+
let filtered = this.mutedIds.size > 0
|
|
550
|
+
? this.activityBuffer.filter((e) => !shouldMuteEntry(e, this.mutedIds))
|
|
551
|
+
: this.activityBuffer;
|
|
494
552
|
const entryCount = this.searchPattern
|
|
495
|
-
?
|
|
496
|
-
:
|
|
553
|
+
? filtered.filter((e) => matchesSearch(e, this.searchPattern)).length
|
|
554
|
+
: filtered.length;
|
|
497
555
|
const maxOffset = Math.max(0, entryCount - visibleLines);
|
|
498
556
|
this.scrollOffset = Math.min(maxOffset, this.scrollOffset + n);
|
|
499
557
|
this.repaintActivityRegion();
|
|
@@ -515,9 +573,12 @@ export class TUI {
|
|
|
515
573
|
if (!this.active)
|
|
516
574
|
return;
|
|
517
575
|
const visibleLines = this.scrollBottom - this.scrollTop + 1;
|
|
576
|
+
let filtered = this.mutedIds.size > 0
|
|
577
|
+
? this.activityBuffer.filter((e) => !shouldMuteEntry(e, this.mutedIds))
|
|
578
|
+
: this.activityBuffer;
|
|
518
579
|
const entryCount = this.searchPattern
|
|
519
|
-
?
|
|
520
|
-
:
|
|
580
|
+
? filtered.filter((e) => matchesSearch(e, this.searchPattern)).length
|
|
581
|
+
: filtered.length;
|
|
521
582
|
this.scrollOffset = Math.max(0, entryCount - visibleLines);
|
|
522
583
|
this.repaintActivityRegion();
|
|
523
584
|
this.paintSeparator();
|
|
@@ -776,7 +837,7 @@ export class TUI {
|
|
|
776
837
|
}
|
|
777
838
|
else if (this.compactMode) {
|
|
778
839
|
// compact: inline tokens, multiple per row (with pin indicators)
|
|
779
|
-
const compactRows = formatCompactRows(visibleSessions, innerWidth - 1, this.pinnedIds);
|
|
840
|
+
const compactRows = formatCompactRows(visibleSessions, innerWidth - 1, this.pinnedIds, this.mutedIds);
|
|
780
841
|
for (let r = 0; r < compactRows.length; r++) {
|
|
781
842
|
const line = `${SLATE}${BOX.v}${RESET} ${compactRows[r]}`;
|
|
782
843
|
const padded = padBoxLine(line, this.cols);
|
|
@@ -789,9 +850,12 @@ export class TUI {
|
|
|
789
850
|
const isHovered = this.hoverSessionIdx === i + 1; // 1-indexed
|
|
790
851
|
const bg = isHovered ? BG_HOVER : "";
|
|
791
852
|
const pinned = this.pinnedIds.has(s.id);
|
|
853
|
+
const muted = this.mutedIds.has(s.id);
|
|
792
854
|
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
|
|
793
|
-
const
|
|
794
|
-
const
|
|
855
|
+
const mute = muted ? `${DIM}${MUTE_ICON}${RESET} ` : "";
|
|
856
|
+
const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0); // each icon + space = 2 chars
|
|
857
|
+
const cardWidth = innerWidth - 1 - iconsWidth;
|
|
858
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${formatSessionCard(s, cardWidth)}`;
|
|
795
859
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
796
860
|
process.stderr.write(moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded);
|
|
797
861
|
}
|
|
@@ -822,9 +886,12 @@ export class TUI {
|
|
|
822
886
|
const isHovered = this.hoverSessionIdx === idx;
|
|
823
887
|
const bg = isHovered ? BG_HOVER : "";
|
|
824
888
|
const pinned = this.pinnedIds.has(s.id);
|
|
889
|
+
const muted = this.mutedIds.has(s.id);
|
|
825
890
|
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
|
|
826
|
-
const
|
|
827
|
-
const
|
|
891
|
+
const mute = muted ? `${DIM}${MUTE_ICON}${RESET} ` : "";
|
|
892
|
+
const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0);
|
|
893
|
+
const cardWidth = innerWidth - 1 - iconsWidth;
|
|
894
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${formatSessionCard(s, cardWidth)}`;
|
|
828
895
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
829
896
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded + RESTORE_CURSOR);
|
|
830
897
|
}
|
|
@@ -861,10 +928,13 @@ export class TUI {
|
|
|
861
928
|
}
|
|
862
929
|
repaintActivityRegion() {
|
|
863
930
|
const visibleLines = this.scrollBottom - this.scrollTop + 1;
|
|
864
|
-
//
|
|
865
|
-
|
|
866
|
-
? this.activityBuffer.filter((e) =>
|
|
931
|
+
// filter: muted entries first, then search on top
|
|
932
|
+
let source = this.mutedIds.size > 0
|
|
933
|
+
? this.activityBuffer.filter((e) => !shouldMuteEntry(e, this.mutedIds))
|
|
867
934
|
: this.activityBuffer;
|
|
935
|
+
if (this.searchPattern) {
|
|
936
|
+
source = source.filter((e) => matchesSearch(e, this.searchPattern));
|
|
937
|
+
}
|
|
868
938
|
const { start, end } = computeScrollSlice(source.length, visibleLines, this.scrollOffset);
|
|
869
939
|
const entries = source.slice(start, end);
|
|
870
940
|
for (let i = 0; i < visibleLines; i++) {
|
|
@@ -1191,5 +1261,5 @@ export function hitTestSession(row, headerHeight, sessionCount) {
|
|
|
1191
1261
|
return row - firstSessionRow + 1; // 1-indexed
|
|
1192
1262
|
}
|
|
1193
1263
|
// ── Exported pure helpers (for testing) ─────────────────────────────────────
|
|
1194
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline, sortSessions, nextSortMode, SORT_MODES, formatCompactRows, computeCompactRowCount, COMPACT_NAME_LEN, PIN_ICON };
|
|
1264
|
+
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline, sortSessions, nextSortMode, SORT_MODES, formatCompactRows, computeCompactRowCount, COMPACT_NAME_LEN, PIN_ICON, MUTE_ICON };
|
|
1195
1265
|
//# sourceMappingURL=tui.js.map
|