aoaoe 0.84.0 → 0.86.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 +41 -0
- package/dist/input.d.ts +9 -0
- package/dist/input.js +57 -0
- package/dist/tui.d.ts +29 -2
- package/dist/tui.js +114 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -444,6 +444,47 @@ async function main() {
|
|
|
444
444
|
tui.log("system", `session not found: ${target}`);
|
|
445
445
|
}
|
|
446
446
|
});
|
|
447
|
+
// wire /unmute-all
|
|
448
|
+
input.onUnmuteAll(() => {
|
|
449
|
+
const count = tui.unmuteAll();
|
|
450
|
+
if (count > 0) {
|
|
451
|
+
tui.log("system", `unmuted ${count} session${count === 1 ? "" : "s"}`);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
tui.log("system", "no sessions are muted");
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
// wire /note set/clear
|
|
458
|
+
input.onNote((target, text) => {
|
|
459
|
+
const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
|
|
460
|
+
const ok = tui.setNote(num ?? target, text);
|
|
461
|
+
if (ok) {
|
|
462
|
+
if (text) {
|
|
463
|
+
tui.log("system", `note set for ${target}: "${text}"`);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
tui.log("system", `note cleared for ${target}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
tui.log("system", `session not found: ${target}`);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
// wire /notes listing
|
|
474
|
+
input.onNotes(() => {
|
|
475
|
+
const notes = tui.getAllNotes();
|
|
476
|
+
if (notes.size === 0) {
|
|
477
|
+
tui.log("system", "no notes — use /note <N|name> <text> to add one");
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
const sessions = tui.getSessions();
|
|
481
|
+
for (const [id, text] of notes) {
|
|
482
|
+
const session = sessions.find((s) => s.id === id);
|
|
483
|
+
const label = session ? session.title : id.slice(0, 8);
|
|
484
|
+
tui.log("system", ` ${label}: "${text}"`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
});
|
|
447
488
|
// wire mouse move to hover highlight on session cards (disabled in compact)
|
|
448
489
|
input.onMouseMove((row, _col) => {
|
|
449
490
|
if (tui.getViewMode() === "overview" && !tui.isCompact()) {
|
package/dist/input.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export type MarkHandler = () => void;
|
|
|
12
12
|
export type JumpHandler = (num: number) => void;
|
|
13
13
|
export type MarksHandler = () => void;
|
|
14
14
|
export type MuteHandler = (target: string) => void;
|
|
15
|
+
export type UnmuteAllHandler = () => void;
|
|
16
|
+
export type NoteHandler = (target: string, text: string) => void;
|
|
17
|
+
export type NotesHandler = () => void;
|
|
15
18
|
export interface MouseEvent {
|
|
16
19
|
button: number;
|
|
17
20
|
col: number;
|
|
@@ -46,6 +49,9 @@ export declare class InputReader {
|
|
|
46
49
|
private jumpHandler;
|
|
47
50
|
private marksHandler;
|
|
48
51
|
private muteHandler;
|
|
52
|
+
private unmuteAllHandler;
|
|
53
|
+
private noteHandler;
|
|
54
|
+
private notesHandler;
|
|
49
55
|
private mouseDataListener;
|
|
50
56
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
51
57
|
onQueueChange(handler: (count: number) => void): void;
|
|
@@ -64,6 +70,9 @@ export declare class InputReader {
|
|
|
64
70
|
onJump(handler: JumpHandler): void;
|
|
65
71
|
onMarks(handler: MarksHandler): void;
|
|
66
72
|
onMute(handler: MuteHandler): void;
|
|
73
|
+
onUnmuteAll(handler: UnmuteAllHandler): void;
|
|
74
|
+
onNote(handler: NoteHandler): void;
|
|
75
|
+
onNotes(handler: NotesHandler): void;
|
|
67
76
|
private notifyQueueChange;
|
|
68
77
|
start(): void;
|
|
69
78
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -44,6 +44,9 @@ export class InputReader {
|
|
|
44
44
|
jumpHandler = null;
|
|
45
45
|
marksHandler = null;
|
|
46
46
|
muteHandler = null;
|
|
47
|
+
unmuteAllHandler = null;
|
|
48
|
+
noteHandler = null;
|
|
49
|
+
notesHandler = null;
|
|
47
50
|
mouseDataListener = null;
|
|
48
51
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
49
52
|
onScroll(handler) {
|
|
@@ -113,6 +116,18 @@ export class InputReader {
|
|
|
113
116
|
onMute(handler) {
|
|
114
117
|
this.muteHandler = handler;
|
|
115
118
|
}
|
|
119
|
+
// register a callback for unmuting all sessions (/unmute-all)
|
|
120
|
+
onUnmuteAll(handler) {
|
|
121
|
+
this.unmuteAllHandler = handler;
|
|
122
|
+
}
|
|
123
|
+
// register a callback for note commands (/note <target> <text>)
|
|
124
|
+
onNote(handler) {
|
|
125
|
+
this.noteHandler = handler;
|
|
126
|
+
}
|
|
127
|
+
// register a callback for listing notes (/notes)
|
|
128
|
+
onNotes(handler) {
|
|
129
|
+
this.notesHandler = handler;
|
|
130
|
+
}
|
|
116
131
|
notifyQueueChange() {
|
|
117
132
|
this.queueChangeHandler?.(this.queue.length);
|
|
118
133
|
}
|
|
@@ -296,6 +311,9 @@ ${BOLD}navigation:${RESET}
|
|
|
296
311
|
/bell toggle terminal bell on errors/completions
|
|
297
312
|
/focus toggle focus mode (show only pinned sessions)
|
|
298
313
|
/mute [N|name] mute/unmute a session's activity entries (toggle)
|
|
314
|
+
/unmute-all unmute all sessions at once
|
|
315
|
+
/note N|name text attach a note to a session (no text = clear)
|
|
316
|
+
/notes list all session notes
|
|
299
317
|
/mark bookmark current activity position
|
|
300
318
|
/jump N jump to bookmark N
|
|
301
319
|
/marks list all bookmarks
|
|
@@ -442,6 +460,45 @@ ${BOLD}other:${RESET}
|
|
|
442
460
|
}
|
|
443
461
|
break;
|
|
444
462
|
}
|
|
463
|
+
case "/unmute-all":
|
|
464
|
+
if (this.unmuteAllHandler) {
|
|
465
|
+
this.unmuteAllHandler();
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
console.error(`${DIM}unmute-all not available (no TUI)${RESET}`);
|
|
469
|
+
}
|
|
470
|
+
break;
|
|
471
|
+
case "/note": {
|
|
472
|
+
const noteArg = line.slice("/note".length).trim();
|
|
473
|
+
if (this.noteHandler) {
|
|
474
|
+
// split: first word is target, rest is note text
|
|
475
|
+
const spaceIdx = noteArg.indexOf(" ");
|
|
476
|
+
if (spaceIdx > 0) {
|
|
477
|
+
const target = noteArg.slice(0, spaceIdx);
|
|
478
|
+
const text = noteArg.slice(spaceIdx + 1).trim();
|
|
479
|
+
this.noteHandler(target, text);
|
|
480
|
+
}
|
|
481
|
+
else if (noteArg) {
|
|
482
|
+
// target only, no text — clear note
|
|
483
|
+
this.noteHandler(noteArg, "");
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
console.error(`${DIM}usage: /note <N|name> <text> — set note, or /note <N|name> — clear${RESET}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
console.error(`${DIM}notes not available (no TUI)${RESET}`);
|
|
491
|
+
}
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
case "/notes":
|
|
495
|
+
if (this.notesHandler) {
|
|
496
|
+
this.notesHandler();
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
console.error(`${DIM}notes not available (no TUI)${RESET}`);
|
|
500
|
+
}
|
|
501
|
+
break;
|
|
445
502
|
case "/mark":
|
|
446
503
|
if (this.markHandler) {
|
|
447
504
|
this.markHandler();
|
package/dist/tui.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ declare const PIN_ICON = "\u25B2";
|
|
|
31
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>, mutedIds?: Set<string>): string[];
|
|
34
|
+
declare function formatCompactRows(sessions: DaemonSessionState[], maxWidth: number, pinnedIds?: Set<string>, mutedIds?: Set<string>, noteIds?: 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;
|
|
@@ -43,8 +43,16 @@ export interface ActivityEntry {
|
|
|
43
43
|
}
|
|
44
44
|
/** Mute indicator for muted sessions (shown dim beside session card). */
|
|
45
45
|
declare const MUTE_ICON = "\u25CC";
|
|
46
|
+
/** Note indicator for sessions with notes. */
|
|
47
|
+
declare const NOTE_ICON = "\u270E";
|
|
48
|
+
/** Max length for a session note (visible chars). */
|
|
49
|
+
export declare const MAX_NOTE_LEN = 80;
|
|
50
|
+
/** Truncate a note to the max length. */
|
|
51
|
+
export declare function truncateNote(text: string): string;
|
|
46
52
|
/** Determine if an activity entry should be hidden due to muting. */
|
|
47
53
|
export declare function shouldMuteEntry(entry: ActivityEntry, mutedIds: Set<string>): boolean;
|
|
54
|
+
/** Format a suppressed entry count badge for muted sessions. Returns empty string for 0. */
|
|
55
|
+
export declare function formatMuteBadge(count: number): string;
|
|
48
56
|
export declare class TUI {
|
|
49
57
|
private active;
|
|
50
58
|
private countdownTimer;
|
|
@@ -75,6 +83,8 @@ export declare class TUI {
|
|
|
75
83
|
private bellEnabled;
|
|
76
84
|
private lastBellAt;
|
|
77
85
|
private mutedIds;
|
|
86
|
+
private mutedEntryCounts;
|
|
87
|
+
private sessionNotes;
|
|
78
88
|
private viewMode;
|
|
79
89
|
private drilldownSessionId;
|
|
80
90
|
private sessionOutputs;
|
|
@@ -125,10 +135,27 @@ export declare class TUI {
|
|
|
125
135
|
* Returns true if session found.
|
|
126
136
|
*/
|
|
127
137
|
toggleMute(sessionIdOrIndex: string | number): boolean;
|
|
138
|
+
/** Unmute all sessions at once. Returns count of sessions unmuted. */
|
|
139
|
+
unmuteAll(): number;
|
|
128
140
|
/** Check if a session ID is muted. */
|
|
129
141
|
isMuted(id: string): boolean;
|
|
130
142
|
/** Return count of muted sessions. */
|
|
131
143
|
getMutedCount(): number;
|
|
144
|
+
/** Return count of suppressed entries for a muted session (0 if not muted). */
|
|
145
|
+
getMutedEntryCount(id: string): number;
|
|
146
|
+
/**
|
|
147
|
+
* Set a note on a session (by 1-indexed number, ID, ID prefix, or title).
|
|
148
|
+
* Returns true if session found. Pass empty text to clear.
|
|
149
|
+
*/
|
|
150
|
+
setNote(sessionIdOrIndex: string | number, text: string): boolean;
|
|
151
|
+
/** Get the note for a session ID (or undefined if none). */
|
|
152
|
+
getNote(id: string): string | undefined;
|
|
153
|
+
/** Return count of sessions with notes. */
|
|
154
|
+
getNoteCount(): number;
|
|
155
|
+
/** Return all session notes (for /notes listing). */
|
|
156
|
+
getAllNotes(): ReadonlyMap<string, string>;
|
|
157
|
+
/** Return the current sessions (read-only, for resolving IDs to titles in the UI). */
|
|
158
|
+
getSessions(): readonly DaemonSessionState[];
|
|
132
159
|
/**
|
|
133
160
|
* Add a bookmark at the current activity position.
|
|
134
161
|
* Returns the bookmark number (1-indexed) or 0 if buffer is empty.
|
|
@@ -233,5 +260,5 @@ declare function formatSearchIndicator(pattern: string, matchCount: number, tota
|
|
|
233
260
|
* (row = headerHeight + 2 + i for 0-indexed session i)
|
|
234
261
|
*/
|
|
235
262
|
export declare function hitTestSession(row: number, headerHeight: number, sessionCount: number): number | null;
|
|
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 };
|
|
263
|
+
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, NOTE_ICON };
|
|
237
264
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.js
CHANGED
|
@@ -90,7 +90,7 @@ const PIN_ICON = "▲";
|
|
|
90
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, mutedIds) {
|
|
93
|
+
function formatCompactRows(sessions, maxWidth, pinnedIds, mutedIds, noteIds) {
|
|
94
94
|
if (sessions.length === 0)
|
|
95
95
|
return [`${DIM}no agents connected${RESET}`];
|
|
96
96
|
const tokens = [];
|
|
@@ -101,11 +101,13 @@ function formatCompactRows(sessions, maxWidth, pinnedIds, mutedIds) {
|
|
|
101
101
|
const dot = STATUS_DOT[s.status] ?? `${AMBER}${DOT.filled}${RESET}`;
|
|
102
102
|
const pinned = pinnedIds?.has(s.id) ?? false;
|
|
103
103
|
const muted = mutedIds?.has(s.id) ?? false;
|
|
104
|
+
const noted = noteIds?.has(s.id) ?? false;
|
|
104
105
|
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET}` : "";
|
|
105
106
|
const muteIcon = muted ? `${DIM}${MUTE_ICON}${RESET}` : "";
|
|
107
|
+
const noteIcon = noted ? `${TEAL}${NOTE_ICON}${RESET}` : "";
|
|
106
108
|
const name = truncatePlain(s.title, COMPACT_NAME_LEN);
|
|
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);
|
|
109
|
+
tokens.push(`${SLATE}${idx}${RESET}${pin}${muteIcon}${noteIcon}${dot}${BOLD}${name}${RESET}`);
|
|
110
|
+
widths.push(idx.length + (pinned ? 1 : 0) + (muted ? 1 : 0) + (noted ? 1 : 0) + 1 + name.length);
|
|
109
111
|
}
|
|
110
112
|
const rows = [];
|
|
111
113
|
let currentRow = "";
|
|
@@ -157,12 +159,28 @@ function phaseDisplay(phase, paused, spinnerFrame) {
|
|
|
157
159
|
// ── Mute helpers ──────────────────────────────────────────────────────────────
|
|
158
160
|
/** Mute indicator for muted sessions (shown dim beside session card). */
|
|
159
161
|
const MUTE_ICON = "◌";
|
|
162
|
+
// ── Notes ─────────────────────────────────────────────────────────────────────
|
|
163
|
+
/** Note indicator for sessions with notes. */
|
|
164
|
+
const NOTE_ICON = "✎";
|
|
165
|
+
/** Max length for a session note (visible chars). */
|
|
166
|
+
export const MAX_NOTE_LEN = 80;
|
|
167
|
+
/** Truncate a note to the max length. */
|
|
168
|
+
export function truncateNote(text) {
|
|
169
|
+
return text.length > MAX_NOTE_LEN ? text.slice(0, MAX_NOTE_LEN - 2) + ".." : text;
|
|
170
|
+
}
|
|
160
171
|
/** Determine if an activity entry should be hidden due to muting. */
|
|
161
172
|
export function shouldMuteEntry(entry, mutedIds) {
|
|
162
173
|
if (!entry.sessionId)
|
|
163
174
|
return false;
|
|
164
175
|
return mutedIds.has(entry.sessionId);
|
|
165
176
|
}
|
|
177
|
+
/** Format a suppressed entry count badge for muted sessions. Returns empty string for 0. */
|
|
178
|
+
export function formatMuteBadge(count) {
|
|
179
|
+
if (count <= 0)
|
|
180
|
+
return "";
|
|
181
|
+
const label = count > 999 ? "999+" : String(count);
|
|
182
|
+
return `${DIM}(${label})${RESET}`;
|
|
183
|
+
}
|
|
166
184
|
// ── TUI class ───────────────────────────────────────────────────────────────
|
|
167
185
|
export class TUI {
|
|
168
186
|
active = false;
|
|
@@ -194,6 +212,8 @@ export class TUI {
|
|
|
194
212
|
bellEnabled = false;
|
|
195
213
|
lastBellAt = 0;
|
|
196
214
|
mutedIds = new Set(); // muted session IDs (activity entries hidden)
|
|
215
|
+
mutedEntryCounts = new Map(); // session ID → suppressed entry count since mute
|
|
216
|
+
sessionNotes = new Map(); // session ID → note text
|
|
197
217
|
// drill-down mode: show a single session's full output
|
|
198
218
|
viewMode = "overview";
|
|
199
219
|
drilldownSessionId = null;
|
|
@@ -368,9 +388,11 @@ export class TUI {
|
|
|
368
388
|
return false;
|
|
369
389
|
if (this.mutedIds.has(sessionId)) {
|
|
370
390
|
this.mutedIds.delete(sessionId);
|
|
391
|
+
this.mutedEntryCounts.delete(sessionId);
|
|
371
392
|
}
|
|
372
393
|
else {
|
|
373
394
|
this.mutedIds.add(sessionId);
|
|
395
|
+
this.mutedEntryCounts.set(sessionId, 0);
|
|
374
396
|
}
|
|
375
397
|
// repaint sessions (mute icon) and activity (filter changes)
|
|
376
398
|
if (this.active) {
|
|
@@ -379,6 +401,19 @@ export class TUI {
|
|
|
379
401
|
}
|
|
380
402
|
return true;
|
|
381
403
|
}
|
|
404
|
+
/** Unmute all sessions at once. Returns count of sessions unmuted. */
|
|
405
|
+
unmuteAll() {
|
|
406
|
+
const count = this.mutedIds.size;
|
|
407
|
+
if (count === 0)
|
|
408
|
+
return 0;
|
|
409
|
+
this.mutedIds.clear();
|
|
410
|
+
this.mutedEntryCounts.clear();
|
|
411
|
+
if (this.active) {
|
|
412
|
+
this.paintSessions();
|
|
413
|
+
this.repaintActivityRegion();
|
|
414
|
+
}
|
|
415
|
+
return count;
|
|
416
|
+
}
|
|
382
417
|
/** Check if a session ID is muted. */
|
|
383
418
|
isMuted(id) {
|
|
384
419
|
return this.mutedIds.has(id);
|
|
@@ -387,6 +422,56 @@ export class TUI {
|
|
|
387
422
|
getMutedCount() {
|
|
388
423
|
return this.mutedIds.size;
|
|
389
424
|
}
|
|
425
|
+
/** Return count of suppressed entries for a muted session (0 if not muted). */
|
|
426
|
+
getMutedEntryCount(id) {
|
|
427
|
+
return this.mutedEntryCounts.get(id) ?? 0;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Set a note on a session (by 1-indexed number, ID, ID prefix, or title).
|
|
431
|
+
* Returns true if session found. Pass empty text to clear.
|
|
432
|
+
*/
|
|
433
|
+
setNote(sessionIdOrIndex, text) {
|
|
434
|
+
let sessionId;
|
|
435
|
+
if (typeof sessionIdOrIndex === "number") {
|
|
436
|
+
sessionId = this.sessions[sessionIdOrIndex - 1]?.id;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
const needle = sessionIdOrIndex.toLowerCase();
|
|
440
|
+
const match = this.sessions.find((s) => s.id === sessionIdOrIndex || s.id.startsWith(needle) || s.title.toLowerCase() === needle);
|
|
441
|
+
sessionId = match?.id;
|
|
442
|
+
}
|
|
443
|
+
if (!sessionId)
|
|
444
|
+
return false;
|
|
445
|
+
if (text.trim() === "") {
|
|
446
|
+
this.sessionNotes.delete(sessionId);
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
this.sessionNotes.set(sessionId, truncateNote(text.trim()));
|
|
450
|
+
}
|
|
451
|
+
if (this.active) {
|
|
452
|
+
this.paintSessions();
|
|
453
|
+
if (this.viewMode === "drilldown" && this.drilldownSessionId === sessionId) {
|
|
454
|
+
this.paintDrilldownSeparator();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
/** Get the note for a session ID (or undefined if none). */
|
|
460
|
+
getNote(id) {
|
|
461
|
+
return this.sessionNotes.get(id);
|
|
462
|
+
}
|
|
463
|
+
/** Return count of sessions with notes. */
|
|
464
|
+
getNoteCount() {
|
|
465
|
+
return this.sessionNotes.size;
|
|
466
|
+
}
|
|
467
|
+
/** Return all session notes (for /notes listing). */
|
|
468
|
+
getAllNotes() {
|
|
469
|
+
return this.sessionNotes;
|
|
470
|
+
}
|
|
471
|
+
/** Return the current sessions (read-only, for resolving IDs to titles in the UI). */
|
|
472
|
+
getSessions() {
|
|
473
|
+
return this.sessions;
|
|
474
|
+
}
|
|
390
475
|
/**
|
|
391
476
|
* Add a bookmark at the current activity position.
|
|
392
477
|
* Returns the bookmark number (1-indexed) or 0 if buffer is empty.
|
|
@@ -499,6 +584,10 @@ export class TUI {
|
|
|
499
584
|
process.stderr.write("\x07");
|
|
500
585
|
}
|
|
501
586
|
}
|
|
587
|
+
// track suppressed entry counts regardless of active state (for badge accuracy)
|
|
588
|
+
if (shouldMuteEntry(entry, this.mutedIds) && entry.sessionId) {
|
|
589
|
+
this.mutedEntryCounts.set(entry.sessionId, (this.mutedEntryCounts.get(entry.sessionId) ?? 0) + 1);
|
|
590
|
+
}
|
|
502
591
|
if (this.active) {
|
|
503
592
|
// muted entries are still buffered + persisted but hidden from display
|
|
504
593
|
if (shouldMuteEntry(entry, this.mutedIds)) {
|
|
@@ -837,7 +926,8 @@ export class TUI {
|
|
|
837
926
|
}
|
|
838
927
|
else if (this.compactMode) {
|
|
839
928
|
// compact: inline tokens, multiple per row (with pin indicators)
|
|
840
|
-
const
|
|
929
|
+
const noteIdSet = new Set(this.sessionNotes.keys());
|
|
930
|
+
const compactRows = formatCompactRows(visibleSessions, innerWidth - 1, this.pinnedIds, this.mutedIds, noteIdSet);
|
|
841
931
|
for (let r = 0; r < compactRows.length; r++) {
|
|
842
932
|
const line = `${SLATE}${BOX.v}${RESET} ${compactRows[r]}`;
|
|
843
933
|
const padded = padBoxLine(line, this.cols);
|
|
@@ -851,11 +941,17 @@ export class TUI {
|
|
|
851
941
|
const bg = isHovered ? BG_HOVER : "";
|
|
852
942
|
const pinned = this.pinnedIds.has(s.id);
|
|
853
943
|
const muted = this.mutedIds.has(s.id);
|
|
944
|
+
const noted = this.sessionNotes.has(s.id);
|
|
945
|
+
const muteBadge = muted ? formatMuteBadge(this.mutedEntryCounts.get(s.id) ?? 0) : "";
|
|
946
|
+
const muteBadgeWidth = muted ? String(Math.min(this.mutedEntryCounts.get(s.id) ?? 0, 9999)).length + 2 : 0; // "(N)" visible chars, 0 when count is 0
|
|
947
|
+
const actualBadgeWidth = (this.mutedEntryCounts.get(s.id) ?? 0) > 0 ? muteBadgeWidth + 1 : 0; // +1 for trailing space
|
|
854
948
|
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
|
|
855
949
|
const mute = muted ? `${DIM}${MUTE_ICON}${RESET} ` : "";
|
|
856
|
-
const
|
|
950
|
+
const note = noted ? `${TEAL}${NOTE_ICON}${RESET} ` : "";
|
|
951
|
+
const badgeSuffix = muteBadge ? `${muteBadge} ` : "";
|
|
952
|
+
const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0) + actualBadgeWidth;
|
|
857
953
|
const cardWidth = innerWidth - 1 - iconsWidth;
|
|
858
|
-
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${formatSessionCard(s, cardWidth)}`;
|
|
954
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${badgeSuffix}${note}${formatSessionCard(s, cardWidth)}`;
|
|
859
955
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
860
956
|
process.stderr.write(moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded);
|
|
861
957
|
}
|
|
@@ -887,11 +983,17 @@ export class TUI {
|
|
|
887
983
|
const bg = isHovered ? BG_HOVER : "";
|
|
888
984
|
const pinned = this.pinnedIds.has(s.id);
|
|
889
985
|
const muted = this.mutedIds.has(s.id);
|
|
986
|
+
const noted = this.sessionNotes.has(s.id);
|
|
987
|
+
const muteBadge = muted ? formatMuteBadge(this.mutedEntryCounts.get(s.id) ?? 0) : "";
|
|
988
|
+
const actualBadgeWidth = (this.mutedEntryCounts.get(s.id) ?? 0) > 0
|
|
989
|
+
? String(Math.min(this.mutedEntryCounts.get(s.id) ?? 0, 9999)).length + 3 : 0; // "(N) " visible chars
|
|
890
990
|
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
|
|
891
991
|
const mute = muted ? `${DIM}${MUTE_ICON}${RESET} ` : "";
|
|
892
|
-
const
|
|
992
|
+
const note = noted ? `${TEAL}${NOTE_ICON}${RESET} ` : "";
|
|
993
|
+
const badgeSuffix = muteBadge ? `${muteBadge} ` : "";
|
|
994
|
+
const iconsWidth = (pinned ? 2 : 0) + (muted ? 2 : 0) + (noted ? 2 : 0) + actualBadgeWidth;
|
|
893
995
|
const cardWidth = innerWidth - 1 - iconsWidth;
|
|
894
|
-
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${formatSessionCard(s, cardWidth)}`;
|
|
996
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${mute}${badgeSuffix}${note}${formatSessionCard(s, cardWidth)}`;
|
|
895
997
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
896
998
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded + RESTORE_CURSOR);
|
|
897
999
|
}
|
|
@@ -952,7 +1054,9 @@ export class TUI {
|
|
|
952
1054
|
paintDrilldownSeparator() {
|
|
953
1055
|
const session = this.sessions.find((s) => s.id === this.drilldownSessionId);
|
|
954
1056
|
const title = session ? session.title : this.drilldownSessionId ?? "?";
|
|
955
|
-
const
|
|
1057
|
+
const noteText = this.drilldownSessionId ? this.sessionNotes.get(this.drilldownSessionId) : undefined;
|
|
1058
|
+
const noteSuffix = noteText ? `"${noteText}" ` : "";
|
|
1059
|
+
const prefix = `${BOX.h}${BOX.h} ${title} ${noteSuffix}`;
|
|
956
1060
|
let hints;
|
|
957
1061
|
if (this.drilldownScrollOffset > 0) {
|
|
958
1062
|
const outputLines = this.sessionOutputs.get(this.drilldownSessionId ?? "") ?? [];
|
|
@@ -1261,5 +1365,5 @@ export function hitTestSession(row, headerHeight, sessionCount) {
|
|
|
1261
1365
|
return row - firstSessionRow + 1; // 1-indexed
|
|
1262
1366
|
}
|
|
1263
1367
|
// ── Exported pure helpers (for testing) ─────────────────────────────────────
|
|
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 };
|
|
1368
|
+
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, NOTE_ICON };
|
|
1265
1369
|
//# sourceMappingURL=tui.js.map
|