aoaoe 0.80.0 → 0.82.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 +12 -0
- package/dist/input.d.ts +6 -0
- package/dist/input.js +28 -0
- package/dist/tui.d.ts +18 -1
- package/dist/tui.js +86 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -378,6 +378,18 @@ async function main() {
|
|
|
378
378
|
tui.setCompact(enabled);
|
|
379
379
|
tui.log("system", `compact mode: ${enabled ? "on" : "off"}`);
|
|
380
380
|
});
|
|
381
|
+
// wire /focus toggle
|
|
382
|
+
input.onFocus(() => {
|
|
383
|
+
const enabled = !tui.isFocused();
|
|
384
|
+
tui.setFocus(enabled);
|
|
385
|
+
tui.log("system", `focus mode: ${enabled ? "on (pinned only)" : "off (all sessions)"}`);
|
|
386
|
+
});
|
|
387
|
+
// wire /bell toggle
|
|
388
|
+
input.onBell(() => {
|
|
389
|
+
const enabled = !tui.isBellEnabled();
|
|
390
|
+
tui.setBell(enabled);
|
|
391
|
+
tui.log("system", `bell notifications: ${enabled ? "on" : "off"}`);
|
|
392
|
+
});
|
|
381
393
|
// wire /pin toggle
|
|
382
394
|
input.onPin((target) => {
|
|
383
395
|
const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
|
package/dist/input.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type QuickSwitchHandler = (sessionNum: number) => void;
|
|
|
6
6
|
export type SortHandler = (mode: string | null) => void;
|
|
7
7
|
export type CompactHandler = () => void;
|
|
8
8
|
export type PinHandler = (target: string) => void;
|
|
9
|
+
export type BellHandler = () => void;
|
|
10
|
+
export type FocusHandler = () => void;
|
|
9
11
|
export interface MouseEvent {
|
|
10
12
|
button: number;
|
|
11
13
|
col: number;
|
|
@@ -34,6 +36,8 @@ export declare class InputReader {
|
|
|
34
36
|
private sortHandler;
|
|
35
37
|
private compactHandler;
|
|
36
38
|
private pinHandler;
|
|
39
|
+
private bellHandler;
|
|
40
|
+
private focusHandler;
|
|
37
41
|
private mouseDataListener;
|
|
38
42
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
39
43
|
onQueueChange(handler: (count: number) => void): void;
|
|
@@ -46,6 +50,8 @@ export declare class InputReader {
|
|
|
46
50
|
onSort(handler: SortHandler): void;
|
|
47
51
|
onCompact(handler: CompactHandler): void;
|
|
48
52
|
onPin(handler: PinHandler): void;
|
|
53
|
+
onBell(handler: BellHandler): void;
|
|
54
|
+
onFocus(handler: FocusHandler): void;
|
|
49
55
|
private notifyQueueChange;
|
|
50
56
|
start(): void;
|
|
51
57
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -38,6 +38,8 @@ export class InputReader {
|
|
|
38
38
|
sortHandler = null;
|
|
39
39
|
compactHandler = null;
|
|
40
40
|
pinHandler = null;
|
|
41
|
+
bellHandler = null;
|
|
42
|
+
focusHandler = null;
|
|
41
43
|
mouseDataListener = null;
|
|
42
44
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
43
45
|
onScroll(handler) {
|
|
@@ -83,6 +85,14 @@ export class InputReader {
|
|
|
83
85
|
onPin(handler) {
|
|
84
86
|
this.pinHandler = handler;
|
|
85
87
|
}
|
|
88
|
+
// register a callback for bell toggle (/bell)
|
|
89
|
+
onBell(handler) {
|
|
90
|
+
this.bellHandler = handler;
|
|
91
|
+
}
|
|
92
|
+
// register a callback for focus mode toggle (/focus)
|
|
93
|
+
onFocus(handler) {
|
|
94
|
+
this.focusHandler = handler;
|
|
95
|
+
}
|
|
86
96
|
notifyQueueChange() {
|
|
87
97
|
this.queueChangeHandler?.(this.queue.length);
|
|
88
98
|
}
|
|
@@ -263,6 +273,8 @@ ${BOLD}navigation:${RESET}
|
|
|
263
273
|
/sort [mode] sort sessions: status, name, activity, default (or cycle)
|
|
264
274
|
/compact toggle compact mode (dense session panel)
|
|
265
275
|
/pin [N|name] pin/unpin a session to the top (toggle)
|
|
276
|
+
/bell toggle terminal bell on errors/completions
|
|
277
|
+
/focus toggle focus mode (show only pinned sessions)
|
|
266
278
|
/search <pattern> filter activity entries by substring (case-insensitive)
|
|
267
279
|
/search clear active search filter
|
|
268
280
|
click session click an agent card to drill down (click again to go back)
|
|
@@ -375,6 +387,22 @@ ${BOLD}other:${RESET}
|
|
|
375
387
|
}
|
|
376
388
|
break;
|
|
377
389
|
}
|
|
390
|
+
case "/bell":
|
|
391
|
+
if (this.bellHandler) {
|
|
392
|
+
this.bellHandler();
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
console.error(`${DIM}bell not available (no TUI)${RESET}`);
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
case "/focus":
|
|
399
|
+
if (this.focusHandler) {
|
|
400
|
+
this.focusHandler();
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
console.error(`${DIM}focus not available (no TUI)${RESET}`);
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
378
406
|
case "/search": {
|
|
379
407
|
const searchArg = line.slice("/search".length).trim();
|
|
380
408
|
if (this.searchHandler) {
|
package/dist/tui.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ declare const SORT_MODES: SortMode[];
|
|
|
6
6
|
declare function sortSessions(sessions: DaemonSessionState[], mode: SortMode, lastChangeAt?: Map<string, number>, pinnedIds?: Set<string>): DaemonSessionState[];
|
|
7
7
|
/** Cycle to the next sort mode. */
|
|
8
8
|
declare function nextSortMode(current: SortMode): SortMode;
|
|
9
|
+
/** Cooldown between terminal bells to avoid buzzing. */
|
|
10
|
+
export declare const BELL_COOLDOWN_MS = 5000;
|
|
11
|
+
/** Determine if an activity entry should trigger a terminal bell. High-signal events only. */
|
|
12
|
+
export declare function shouldBell(tag: string, text: string): boolean;
|
|
9
13
|
/** Max name length in compact token. */
|
|
10
14
|
declare const COMPACT_NAME_LEN = 10;
|
|
11
15
|
/** Pin indicator for pinned sessions. */
|
|
@@ -49,6 +53,9 @@ export declare class TUI {
|
|
|
49
53
|
private prevLastActivity;
|
|
50
54
|
private compactMode;
|
|
51
55
|
private pinnedIds;
|
|
56
|
+
private focusMode;
|
|
57
|
+
private bellEnabled;
|
|
58
|
+
private lastBellAt;
|
|
52
59
|
private viewMode;
|
|
53
60
|
private drilldownSessionId;
|
|
54
61
|
private sessionOutputs;
|
|
@@ -64,7 +71,7 @@ export declare class TUI {
|
|
|
64
71
|
start(version: string): void;
|
|
65
72
|
stop(): void;
|
|
66
73
|
isActive(): boolean;
|
|
67
|
-
/** Return the current number of sessions (for mouse hit testing) */
|
|
74
|
+
/** Return the current number of visible sessions (for mouse hit testing) */
|
|
68
75
|
getSessionCount(): number;
|
|
69
76
|
/** Set the session sort mode and repaint. */
|
|
70
77
|
setSortMode(mode: SortMode): void;
|
|
@@ -83,6 +90,16 @@ export declare class TUI {
|
|
|
83
90
|
isPinned(id: string): boolean;
|
|
84
91
|
/** Return count of pinned sessions. */
|
|
85
92
|
getPinnedCount(): number;
|
|
93
|
+
/** Enable or disable focus mode. When focused, only pinned sessions are visible. */
|
|
94
|
+
setFocus(enabled: boolean): void;
|
|
95
|
+
/** Return whether focus mode is enabled. */
|
|
96
|
+
isFocused(): boolean;
|
|
97
|
+
/** Return count of visible sessions (all in normal mode, pinned-only in focus mode). */
|
|
98
|
+
private getVisibleCount;
|
|
99
|
+
/** Enable or disable terminal bell notifications. */
|
|
100
|
+
setBell(enabled: boolean): void;
|
|
101
|
+
/** Return whether terminal bell is enabled. */
|
|
102
|
+
isBellEnabled(): boolean;
|
|
86
103
|
updateState(opts: {
|
|
87
104
|
phase?: DaemonPhase;
|
|
88
105
|
pollCount?: number;
|
package/dist/tui.js
CHANGED
|
@@ -52,6 +52,17 @@ function nextSortMode(current) {
|
|
|
52
52
|
const idx = SORT_MODES.indexOf(current);
|
|
53
53
|
return SORT_MODES[(idx + 1) % SORT_MODES.length];
|
|
54
54
|
}
|
|
55
|
+
// ── Bell notifications ──────────────────────────────────────────────────────
|
|
56
|
+
/** Cooldown between terminal bells to avoid buzzing. */
|
|
57
|
+
export const BELL_COOLDOWN_MS = 5000;
|
|
58
|
+
/** Determine if an activity entry should trigger a terminal bell. High-signal events only. */
|
|
59
|
+
export function shouldBell(tag, text) {
|
|
60
|
+
if (tag === "! action" || tag === "error")
|
|
61
|
+
return true;
|
|
62
|
+
if (tag === "+ action" && text.toLowerCase().includes("complete"))
|
|
63
|
+
return true;
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
55
66
|
// ── Compact mode ────────────────────────────────────────────────────────────
|
|
56
67
|
/** Max name length in compact token. */
|
|
57
68
|
const COMPACT_NAME_LEN = 10;
|
|
@@ -150,6 +161,9 @@ export class TUI {
|
|
|
150
161
|
prevLastActivity = new Map(); // session ID → previous lastActivity string
|
|
151
162
|
compactMode = false;
|
|
152
163
|
pinnedIds = new Set(); // pinned session IDs (always sort to top)
|
|
164
|
+
focusMode = false; // focus mode: hide all sessions except pinned
|
|
165
|
+
bellEnabled = false;
|
|
166
|
+
lastBellAt = 0;
|
|
153
167
|
// drill-down mode: show a single session's full output
|
|
154
168
|
viewMode = "overview";
|
|
155
169
|
drilldownSessionId = null;
|
|
@@ -202,9 +216,9 @@ export class TUI {
|
|
|
202
216
|
isActive() {
|
|
203
217
|
return this.active;
|
|
204
218
|
}
|
|
205
|
-
/** Return the current number of sessions (for mouse hit testing) */
|
|
219
|
+
/** Return the current number of visible sessions (for mouse hit testing) */
|
|
206
220
|
getSessionCount() {
|
|
207
|
-
return this.
|
|
221
|
+
return this.getVisibleCount();
|
|
208
222
|
}
|
|
209
223
|
/** Set the session sort mode and repaint. */
|
|
210
224
|
setSortMode(mode) {
|
|
@@ -227,7 +241,7 @@ export class TUI {
|
|
|
227
241
|
return;
|
|
228
242
|
this.compactMode = enabled;
|
|
229
243
|
if (this.active) {
|
|
230
|
-
this.computeLayout(this.
|
|
244
|
+
this.computeLayout(this.getVisibleCount());
|
|
231
245
|
this.paintAll();
|
|
232
246
|
}
|
|
233
247
|
}
|
|
@@ -272,6 +286,39 @@ export class TUI {
|
|
|
272
286
|
getPinnedCount() {
|
|
273
287
|
return this.pinnedIds.size;
|
|
274
288
|
}
|
|
289
|
+
/** Enable or disable focus mode. When focused, only pinned sessions are visible. */
|
|
290
|
+
setFocus(enabled) {
|
|
291
|
+
if (enabled === this.focusMode)
|
|
292
|
+
return;
|
|
293
|
+
this.focusMode = enabled;
|
|
294
|
+
if (this.active) {
|
|
295
|
+
this.computeLayout(this.getVisibleCount());
|
|
296
|
+
this.paintAll();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/** Return whether focus mode is enabled. */
|
|
300
|
+
isFocused() {
|
|
301
|
+
return this.focusMode;
|
|
302
|
+
}
|
|
303
|
+
/** Return count of visible sessions (all in normal mode, pinned-only in focus mode). */
|
|
304
|
+
getVisibleCount() {
|
|
305
|
+
if (!this.focusMode)
|
|
306
|
+
return this.sessions.length;
|
|
307
|
+
let count = 0;
|
|
308
|
+
for (const s of this.sessions) {
|
|
309
|
+
if (this.pinnedIds.has(s.id))
|
|
310
|
+
count++;
|
|
311
|
+
}
|
|
312
|
+
return count;
|
|
313
|
+
}
|
|
314
|
+
/** Enable or disable terminal bell notifications. */
|
|
315
|
+
setBell(enabled) {
|
|
316
|
+
this.bellEnabled = enabled;
|
|
317
|
+
}
|
|
318
|
+
/** Return whether terminal bell is enabled. */
|
|
319
|
+
isBellEnabled() {
|
|
320
|
+
return this.bellEnabled;
|
|
321
|
+
}
|
|
275
322
|
// ── State updates ───────────────────────────────────────────────────────
|
|
276
323
|
updateState(opts) {
|
|
277
324
|
if (opts.phase !== undefined)
|
|
@@ -298,10 +345,11 @@ export class TUI {
|
|
|
298
345
|
this.prevLastActivity.set(s.id, s.lastActivity);
|
|
299
346
|
}
|
|
300
347
|
const sorted = sortSessions(opts.sessions, this.sortMode, this.lastChangeAt, this.pinnedIds);
|
|
301
|
-
const
|
|
348
|
+
const prevVisibleCount = this.getVisibleCount();
|
|
302
349
|
this.sessions = sorted;
|
|
303
|
-
|
|
304
|
-
|
|
350
|
+
const newVisibleCount = this.getVisibleCount();
|
|
351
|
+
if (newVisibleCount !== prevVisibleCount) {
|
|
352
|
+
this.computeLayout(newVisibleCount);
|
|
305
353
|
this.paintAll();
|
|
306
354
|
return;
|
|
307
355
|
}
|
|
@@ -325,6 +373,14 @@ export class TUI {
|
|
|
325
373
|
this.activityBuffer = this.activityBuffer.slice(-this.maxActivity);
|
|
326
374
|
this.activityTimestamps = this.activityTimestamps.slice(-this.maxActivity);
|
|
327
375
|
}
|
|
376
|
+
// terminal bell for high-signal events (with cooldown)
|
|
377
|
+
if (this.bellEnabled && shouldBell(tag, text)) {
|
|
378
|
+
const nowMs = now.getTime();
|
|
379
|
+
if (nowMs - this.lastBellAt >= BELL_COOLDOWN_MS) {
|
|
380
|
+
this.lastBellAt = nowMs;
|
|
381
|
+
process.stderr.write("\x07");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
328
384
|
if (this.active) {
|
|
329
385
|
if (this.searchPattern) {
|
|
330
386
|
// search active: only show new entry if it matches
|
|
@@ -487,7 +543,7 @@ export class TUI {
|
|
|
487
543
|
this.drilldownNewWhileScrolled = 0;
|
|
488
544
|
this.hoverSessionIdx = null;
|
|
489
545
|
if (this.active) {
|
|
490
|
-
this.computeLayout(this.
|
|
546
|
+
this.computeLayout(this.getVisibleCount());
|
|
491
547
|
this.paintAll();
|
|
492
548
|
}
|
|
493
549
|
return true;
|
|
@@ -502,7 +558,7 @@ export class TUI {
|
|
|
502
558
|
this.drilldownNewWhileScrolled = 0;
|
|
503
559
|
this.hoverSessionIdx = null;
|
|
504
560
|
if (this.active) {
|
|
505
|
-
this.computeLayout(this.
|
|
561
|
+
this.computeLayout(this.getVisibleCount());
|
|
506
562
|
this.paintAll();
|
|
507
563
|
}
|
|
508
564
|
}
|
|
@@ -565,8 +621,9 @@ export class TUI {
|
|
|
565
621
|
}
|
|
566
622
|
else {
|
|
567
623
|
// overview: header (1) + sessions box + separator + activity + input
|
|
624
|
+
const visibleSessions = this.sessions.slice(0, this.getVisibleCount());
|
|
568
625
|
const sessBodyRows = this.compactMode
|
|
569
|
-
? computeCompactRowCount(
|
|
626
|
+
? computeCompactRowCount(visibleSessions, this.cols - 2)
|
|
570
627
|
: Math.max(sessionCount, 1);
|
|
571
628
|
this.sessionRows = sessBodyRows + 2; // + top/bottom borders
|
|
572
629
|
this.separatorRow = this.headerHeight + this.sessionRows + 1;
|
|
@@ -579,7 +636,7 @@ export class TUI {
|
|
|
579
636
|
}
|
|
580
637
|
}
|
|
581
638
|
onResize() {
|
|
582
|
-
this.computeLayout(this.
|
|
639
|
+
this.computeLayout(this.getVisibleCount());
|
|
583
640
|
this.paintAll();
|
|
584
641
|
}
|
|
585
642
|
// ── Painting ────────────────────────────────────────────────────────────
|
|
@@ -607,7 +664,10 @@ export class TUI {
|
|
|
607
664
|
}
|
|
608
665
|
else {
|
|
609
666
|
const phaseText = phaseDisplay(this.phase, this.paused, this.spinnerFrame);
|
|
610
|
-
const
|
|
667
|
+
const visCount = this.getVisibleCount();
|
|
668
|
+
const sessCount = this.focusMode
|
|
669
|
+
? `${visCount}/${this.sessions.length} agent${this.sessions.length !== 1 ? "s" : ""}`
|
|
670
|
+
: `${this.sessions.length} agent${this.sessions.length !== 1 ? "s" : ""}`;
|
|
611
671
|
const activeCount = this.sessions.filter((s) => s.userActive).length;
|
|
612
672
|
const activeTag = activeCount > 0 ? ` ${SLATE}│${RESET} ${AMBER}${activeCount} user${RESET}` : "";
|
|
613
673
|
// countdown to next tick (only in sleeping phase)
|
|
@@ -627,23 +687,29 @@ export class TUI {
|
|
|
627
687
|
paintSessions() {
|
|
628
688
|
const startRow = this.headerHeight + 1;
|
|
629
689
|
const innerWidth = this.cols - 2; // inside the box borders
|
|
630
|
-
|
|
690
|
+
const visibleCount = this.getVisibleCount();
|
|
691
|
+
const visibleSessions = this.sessions.slice(0, visibleCount); // pinned sort to top
|
|
692
|
+
// top border with label (includes focus/compact/sort mode tags)
|
|
693
|
+
const focusTag = this.focusMode ? "focus" : "";
|
|
631
694
|
const sortTag = this.sortMode !== "default" ? this.sortMode : "";
|
|
632
695
|
const compactTag = this.compactMode ? "compact" : "";
|
|
633
|
-
const tags = [compactTag, sortTag].filter(Boolean).join(", ");
|
|
696
|
+
const tags = [focusTag, compactTag, sortTag].filter(Boolean).join(", ");
|
|
634
697
|
const label = tags ? ` agents (${tags}) ` : " agents ";
|
|
635
698
|
const borderAfterLabel = Math.max(0, innerWidth - label.length);
|
|
636
699
|
const topBorder = `${SLATE}${BOX.rtl}${BOX.h}${RESET}${SLATE}${label}${RESET}${SLATE}${BOX.h.repeat(borderAfterLabel)}${BOX.rtr}${RESET}`;
|
|
637
700
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow, 1) + CLEAR_LINE + truncateAnsi(topBorder, this.cols));
|
|
638
|
-
if (
|
|
701
|
+
if (visibleSessions.length === 0) {
|
|
639
702
|
// empty state
|
|
640
|
-
const
|
|
703
|
+
const msg = this.focusMode && this.sessions.length > 0
|
|
704
|
+
? `${DIM}no pinned agents — /pin to add, /focus to exit${RESET}`
|
|
705
|
+
: `${DIM}no agents connected${RESET}`;
|
|
706
|
+
const empty = `${SLATE}${BOX.v}${RESET} ${msg}`;
|
|
641
707
|
const padded = padBoxLine(empty, this.cols);
|
|
642
708
|
process.stderr.write(moveTo(startRow + 1, 1) + CLEAR_LINE + padded);
|
|
643
709
|
}
|
|
644
710
|
else if (this.compactMode) {
|
|
645
711
|
// compact: inline tokens, multiple per row (with pin indicators)
|
|
646
|
-
const compactRows = formatCompactRows(
|
|
712
|
+
const compactRows = formatCompactRows(visibleSessions, innerWidth - 1, this.pinnedIds);
|
|
647
713
|
for (let r = 0; r < compactRows.length; r++) {
|
|
648
714
|
const line = `${SLATE}${BOX.v}${RESET} ${compactRows[r]}`;
|
|
649
715
|
const padded = padBoxLine(line, this.cols);
|
|
@@ -651,8 +717,8 @@ export class TUI {
|
|
|
651
717
|
}
|
|
652
718
|
}
|
|
653
719
|
else {
|
|
654
|
-
for (let i = 0; i <
|
|
655
|
-
const s =
|
|
720
|
+
for (let i = 0; i < visibleSessions.length; i++) {
|
|
721
|
+
const s = visibleSessions[i];
|
|
656
722
|
const isHovered = this.hoverSessionIdx === i + 1; // 1-indexed
|
|
657
723
|
const bg = isHovered ? BG_HOVER : "";
|
|
658
724
|
const pinned = this.pinnedIds.has(s.id);
|
|
@@ -665,8 +731,8 @@ export class TUI {
|
|
|
665
731
|
}
|
|
666
732
|
// bottom border
|
|
667
733
|
const bodyRows = this.compactMode
|
|
668
|
-
? computeCompactRowCount(
|
|
669
|
-
: Math.max(
|
|
734
|
+
? computeCompactRowCount(visibleSessions, innerWidth)
|
|
735
|
+
: Math.max(visibleCount, 1);
|
|
670
736
|
const bottomRow = startRow + 1 + bodyRows;
|
|
671
737
|
const bottomBorder = `${SLATE}${BOX.rbl}${BOX.h.repeat(Math.max(0, this.cols - 2))}${BOX.rbr}${RESET}`;
|
|
672
738
|
process.stderr.write(moveTo(bottomRow, 1) + CLEAR_LINE + truncateAnsi(bottomBorder, this.cols));
|