aoaoe 0.77.0 → 0.79.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 +27 -3
- package/dist/input.d.ts +6 -0
- package/dist/input.js +30 -0
- package/dist/tui.d.ts +29 -1
- package/dist/tui.js +138 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { wakeableSleep } from "./wake.js";
|
|
|
15
15
|
import { classifyMessages, formatUserMessages, buildReceipts, shouldSkipSleep, hasPendingFile, isInsistMessage, stripInsistPrefix } from "./message.js";
|
|
16
16
|
import { TaskManager, loadTaskDefinitions, loadTaskState, formatTaskTable } from "./task-manager.js";
|
|
17
17
|
import { runTaskCli, handleTaskSlashCommand } from "./task-cli.js";
|
|
18
|
-
import { TUI, hitTestSession } from "./tui.js";
|
|
18
|
+
import { TUI, hitTestSession, nextSortMode, SORT_MODES } from "./tui.js";
|
|
19
19
|
import { isDaemonRunningFromState } from "./chat.js";
|
|
20
20
|
import { sendNotification, sendTestNotification } from "./notify.js";
|
|
21
21
|
import { startHealthServer } from "./health.js";
|
|
@@ -318,6 +318,9 @@ async function main() {
|
|
|
318
318
|
tui.log("system", "returned to overview");
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
|
+
// compact mode: no per-session click targeting (use quick-switch or /view)
|
|
322
|
+
if (tui.isCompact())
|
|
323
|
+
return;
|
|
321
324
|
const sessionIdx = hitTestSession(row, 1, tui.getSessionCount());
|
|
322
325
|
if (sessionIdx !== null) {
|
|
323
326
|
const ok = tui.enterDrilldown(sessionIdx);
|
|
@@ -354,9 +357,30 @@ async function main() {
|
|
|
354
357
|
tui.log("system", "search cleared");
|
|
355
358
|
}
|
|
356
359
|
});
|
|
357
|
-
// wire
|
|
360
|
+
// wire /sort command to TUI session sort
|
|
361
|
+
input.onSort((mode) => {
|
|
362
|
+
if (mode && SORT_MODES.includes(mode)) {
|
|
363
|
+
tui.setSortMode(mode);
|
|
364
|
+
tui.log("system", `sort: ${mode}`);
|
|
365
|
+
}
|
|
366
|
+
else if (!mode) {
|
|
367
|
+
const next = nextSortMode(tui.getSortMode());
|
|
368
|
+
tui.setSortMode(next);
|
|
369
|
+
tui.log("system", `sort: ${next}`);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
tui.log("system", `unknown sort mode: ${mode} (try: status, name, activity, default)`);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
// wire /compact toggle
|
|
376
|
+
input.onCompact(() => {
|
|
377
|
+
const enabled = !tui.isCompact();
|
|
378
|
+
tui.setCompact(enabled);
|
|
379
|
+
tui.log("system", `compact mode: ${enabled ? "on" : "off"}`);
|
|
380
|
+
});
|
|
381
|
+
// wire mouse move to hover highlight on session cards (disabled in compact)
|
|
358
382
|
input.onMouseMove((row, _col) => {
|
|
359
|
-
if (tui.getViewMode() === "overview") {
|
|
383
|
+
if (tui.getViewMode() === "overview" && !tui.isCompact()) {
|
|
360
384
|
const sessionIdx = hitTestSession(row, 1, tui.getSessionCount());
|
|
361
385
|
tui.setHoverSession(sessionIdx);
|
|
362
386
|
}
|
package/dist/input.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const INSIST_PREFIX = "__INSIST__";
|
|
|
3
3
|
export type ViewHandler = (target: string | null) => void;
|
|
4
4
|
export type SearchHandler = (pattern: string | null) => void;
|
|
5
5
|
export type QuickSwitchHandler = (sessionNum: number) => void;
|
|
6
|
+
export type SortHandler = (mode: string | null) => void;
|
|
7
|
+
export type CompactHandler = () => void;
|
|
6
8
|
export interface MouseEvent {
|
|
7
9
|
button: number;
|
|
8
10
|
col: number;
|
|
@@ -28,6 +30,8 @@ export declare class InputReader {
|
|
|
28
30
|
private lastMoveRow;
|
|
29
31
|
private searchHandler;
|
|
30
32
|
private quickSwitchHandler;
|
|
33
|
+
private sortHandler;
|
|
34
|
+
private compactHandler;
|
|
31
35
|
private mouseDataListener;
|
|
32
36
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
33
37
|
onQueueChange(handler: (count: number) => void): void;
|
|
@@ -37,6 +41,8 @@ export declare class InputReader {
|
|
|
37
41
|
onMouseMove(handler: MouseMoveHandler): void;
|
|
38
42
|
onSearch(handler: SearchHandler): void;
|
|
39
43
|
onQuickSwitch(handler: QuickSwitchHandler): void;
|
|
44
|
+
onSort(handler: SortHandler): void;
|
|
45
|
+
onCompact(handler: CompactHandler): void;
|
|
40
46
|
private notifyQueueChange;
|
|
41
47
|
start(): void;
|
|
42
48
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -35,6 +35,8 @@ export class InputReader {
|
|
|
35
35
|
lastMoveRow = 0; // debounce: only fire move handler when row changes
|
|
36
36
|
searchHandler = null;
|
|
37
37
|
quickSwitchHandler = null;
|
|
38
|
+
sortHandler = null;
|
|
39
|
+
compactHandler = null;
|
|
38
40
|
mouseDataListener = null;
|
|
39
41
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
40
42
|
onScroll(handler) {
|
|
@@ -68,6 +70,14 @@ export class InputReader {
|
|
|
68
70
|
onQuickSwitch(handler) {
|
|
69
71
|
this.quickSwitchHandler = handler;
|
|
70
72
|
}
|
|
73
|
+
// register a callback for sort commands (/sort <mode> or /sort to cycle)
|
|
74
|
+
onSort(handler) {
|
|
75
|
+
this.sortHandler = handler;
|
|
76
|
+
}
|
|
77
|
+
// register a callback for compact mode toggle (/compact)
|
|
78
|
+
onCompact(handler) {
|
|
79
|
+
this.compactHandler = handler;
|
|
80
|
+
}
|
|
71
81
|
notifyQueueChange() {
|
|
72
82
|
this.queueChangeHandler?.(this.queue.length);
|
|
73
83
|
}
|
|
@@ -245,6 +255,8 @@ ${BOLD}navigation:${RESET}
|
|
|
245
255
|
1-9 quick-switch: jump to session N (type digit + Enter)
|
|
246
256
|
/view [N|name] drill into a session's live output (default: 1)
|
|
247
257
|
/back return to overview from drill-down
|
|
258
|
+
/sort [mode] sort sessions: status, name, activity, default (or cycle)
|
|
259
|
+
/compact toggle compact mode (dense session panel)
|
|
248
260
|
/search <pattern> filter activity entries by substring (case-insensitive)
|
|
249
261
|
/search clear active search filter
|
|
250
262
|
click session click an agent card to drill down (click again to go back)
|
|
@@ -324,6 +336,24 @@ ${BOLD}other:${RESET}
|
|
|
324
336
|
console.error(`${DIM}already in overview${RESET}`);
|
|
325
337
|
}
|
|
326
338
|
break;
|
|
339
|
+
case "/sort": {
|
|
340
|
+
const sortArg = line.slice("/sort".length).trim().toLowerCase();
|
|
341
|
+
if (this.sortHandler) {
|
|
342
|
+
this.sortHandler(sortArg || null); // empty = cycle to next mode
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
console.error(`${DIM}sort not available (no TUI)${RESET}`);
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
case "/compact":
|
|
350
|
+
if (this.compactHandler) {
|
|
351
|
+
this.compactHandler();
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
console.error(`${DIM}compact mode not available (no TUI)${RESET}`);
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
327
357
|
case "/search": {
|
|
328
358
|
const searchArg = line.slice("/search".length).trim();
|
|
329
359
|
if (this.searchHandler) {
|
package/dist/tui.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { DaemonSessionState, DaemonPhase } from "./types.js";
|
|
2
2
|
import type { HistoryEntry } from "./tui-history.js";
|
|
3
|
+
export type SortMode = "default" | "status" | "name" | "activity";
|
|
4
|
+
declare const SORT_MODES: SortMode[];
|
|
5
|
+
/** Sort sessions by mode. Returns a new array (never mutates). */
|
|
6
|
+
declare function sortSessions(sessions: DaemonSessionState[], mode: SortMode, lastChangeAt?: Map<string, number>): DaemonSessionState[];
|
|
7
|
+
/** Cycle to the next sort mode. */
|
|
8
|
+
declare function nextSortMode(current: SortMode): SortMode;
|
|
9
|
+
/** Max name length in compact token. */
|
|
10
|
+
declare const COMPACT_NAME_LEN = 10;
|
|
11
|
+
/**
|
|
12
|
+
* Format sessions as inline compact tokens, wrapped to fit maxWidth.
|
|
13
|
+
* Each token: "{idx}{dot}{name}" — e.g. "1●Alpha" with colored dot and bold name.
|
|
14
|
+
* Returns array of formatted row strings (one per display row).
|
|
15
|
+
*/
|
|
16
|
+
declare function formatCompactRows(sessions: DaemonSessionState[], maxWidth: number): string[];
|
|
17
|
+
/** Compute how many display rows compact mode needs (minimum 1). */
|
|
18
|
+
declare function computeCompactRowCount(sessions: DaemonSessionState[], maxWidth: number): number;
|
|
3
19
|
declare function phaseDisplay(phase: DaemonPhase, paused: boolean, spinnerFrame: number): string;
|
|
4
20
|
export interface ActivityEntry {
|
|
5
21
|
time: string;
|
|
@@ -26,6 +42,10 @@ export declare class TUI {
|
|
|
26
42
|
private searchPattern;
|
|
27
43
|
private hoverSessionIdx;
|
|
28
44
|
private activityTimestamps;
|
|
45
|
+
private sortMode;
|
|
46
|
+
private lastChangeAt;
|
|
47
|
+
private prevLastActivity;
|
|
48
|
+
private compactMode;
|
|
29
49
|
private viewMode;
|
|
30
50
|
private drilldownSessionId;
|
|
31
51
|
private sessionOutputs;
|
|
@@ -43,6 +63,14 @@ export declare class TUI {
|
|
|
43
63
|
isActive(): boolean;
|
|
44
64
|
/** Return the current number of sessions (for mouse hit testing) */
|
|
45
65
|
getSessionCount(): number;
|
|
66
|
+
/** Set the session sort mode and repaint. */
|
|
67
|
+
setSortMode(mode: SortMode): void;
|
|
68
|
+
/** Return the current sort mode. */
|
|
69
|
+
getSortMode(): SortMode;
|
|
70
|
+
/** Toggle or set compact mode. Recomputes layout and repaints. */
|
|
71
|
+
setCompact(enabled: boolean): void;
|
|
72
|
+
/** Return whether compact mode is enabled. */
|
|
73
|
+
isCompact(): boolean;
|
|
46
74
|
updateState(opts: {
|
|
47
75
|
phase?: DaemonPhase;
|
|
48
76
|
pollCount?: number;
|
|
@@ -133,5 +161,5 @@ declare function formatSearchIndicator(pattern: string, matchCount: number, tota
|
|
|
133
161
|
* (row = headerHeight + 2 + i for 0-indexed session i)
|
|
134
162
|
*/
|
|
135
163
|
export declare function hitTestSession(row: number, headerHeight: number, sessionCount: number): number | null;
|
|
136
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline };
|
|
164
|
+
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 };
|
|
137
165
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.js
CHANGED
|
@@ -21,6 +21,77 @@ const MOUSE_OFF = `${CSI}?1003l${CSI}?1006l`;
|
|
|
21
21
|
const moveTo = (row, col) => `${CSI}${row};${col}H`;
|
|
22
22
|
const setScrollRegion = (top, bottom) => `${CSI}${top};${bottom}r`;
|
|
23
23
|
const resetScrollRegion = () => `${CSI}r`;
|
|
24
|
+
const SORT_MODES = ["default", "status", "name", "activity"];
|
|
25
|
+
const STATUS_PRIORITY = {
|
|
26
|
+
error: 0, waiting: 1, working: 2, running: 2,
|
|
27
|
+
idle: 3, done: 4, stopped: 5, unknown: 6,
|
|
28
|
+
};
|
|
29
|
+
/** Sort sessions by mode. Returns a new array (never mutates). */
|
|
30
|
+
function sortSessions(sessions, mode, lastChangeAt) {
|
|
31
|
+
const copy = sessions.slice();
|
|
32
|
+
switch (mode) {
|
|
33
|
+
case "status":
|
|
34
|
+
copy.sort((a, b) => (STATUS_PRIORITY[a.status] ?? 6) - (STATUS_PRIORITY[b.status] ?? 6));
|
|
35
|
+
break;
|
|
36
|
+
case "name":
|
|
37
|
+
copy.sort((a, b) => a.title.toLowerCase().localeCompare(b.title.toLowerCase()));
|
|
38
|
+
break;
|
|
39
|
+
case "activity":
|
|
40
|
+
copy.sort((a, b) => (lastChangeAt?.get(b.id) ?? 0) - (lastChangeAt?.get(a.id) ?? 0));
|
|
41
|
+
break;
|
|
42
|
+
// "default" — preserve original order
|
|
43
|
+
}
|
|
44
|
+
return copy;
|
|
45
|
+
}
|
|
46
|
+
/** Cycle to the next sort mode. */
|
|
47
|
+
function nextSortMode(current) {
|
|
48
|
+
const idx = SORT_MODES.indexOf(current);
|
|
49
|
+
return SORT_MODES[(idx + 1) % SORT_MODES.length];
|
|
50
|
+
}
|
|
51
|
+
// ── Compact mode ────────────────────────────────────────────────────────────
|
|
52
|
+
/** Max name length in compact token. */
|
|
53
|
+
const COMPACT_NAME_LEN = 10;
|
|
54
|
+
/**
|
|
55
|
+
* Format sessions as inline compact tokens, wrapped to fit maxWidth.
|
|
56
|
+
* Each token: "{idx}{dot}{name}" — e.g. "1●Alpha" with colored dot and bold name.
|
|
57
|
+
* Returns array of formatted row strings (one per display row).
|
|
58
|
+
*/
|
|
59
|
+
function formatCompactRows(sessions, maxWidth) {
|
|
60
|
+
if (sessions.length === 0)
|
|
61
|
+
return [`${DIM}no agents connected${RESET}`];
|
|
62
|
+
const tokens = [];
|
|
63
|
+
const widths = [];
|
|
64
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
65
|
+
const s = sessions[i];
|
|
66
|
+
const idx = String(i + 1);
|
|
67
|
+
const dot = STATUS_DOT[s.status] ?? `${AMBER}${DOT.filled}${RESET}`;
|
|
68
|
+
const name = truncatePlain(s.title, COMPACT_NAME_LEN);
|
|
69
|
+
tokens.push(`${SLATE}${idx}${RESET}${dot}${BOLD}${name}${RESET}`);
|
|
70
|
+
widths.push(idx.length + 1 + name.length); // idx chars + dot char + name chars
|
|
71
|
+
}
|
|
72
|
+
const rows = [];
|
|
73
|
+
let currentRow = "";
|
|
74
|
+
let currentWidth = 0;
|
|
75
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
76
|
+
const gap = currentWidth > 0 ? 2 : 0;
|
|
77
|
+
if (currentWidth + gap + widths[i] > maxWidth && currentWidth > 0) {
|
|
78
|
+
rows.push(currentRow);
|
|
79
|
+
currentRow = tokens[i];
|
|
80
|
+
currentWidth = widths[i];
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
currentRow += (currentWidth > 0 ? " " : "") + tokens[i];
|
|
84
|
+
currentWidth += gap + widths[i];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (currentRow)
|
|
88
|
+
rows.push(currentRow);
|
|
89
|
+
return rows;
|
|
90
|
+
}
|
|
91
|
+
/** Compute how many display rows compact mode needs (minimum 1). */
|
|
92
|
+
function computeCompactRowCount(sessions, maxWidth) {
|
|
93
|
+
return Math.max(1, formatCompactRows(sessions, maxWidth).length);
|
|
94
|
+
}
|
|
24
95
|
// ── Status rendering ────────────────────────────────────────────────────────
|
|
25
96
|
const STATUS_DOT = {
|
|
26
97
|
working: `${LIME}${DOT.filled}${RESET}`,
|
|
@@ -66,6 +137,10 @@ export class TUI {
|
|
|
66
137
|
searchPattern = null; // active search filter pattern
|
|
67
138
|
hoverSessionIdx = null; // 1-indexed session under mouse cursor (null = none)
|
|
68
139
|
activityTimestamps = []; // epoch ms of each log() call for sparkline
|
|
140
|
+
sortMode = "default";
|
|
141
|
+
lastChangeAt = new Map(); // session ID → epoch ms of last activity change
|
|
142
|
+
prevLastActivity = new Map(); // session ID → previous lastActivity string
|
|
143
|
+
compactMode = false;
|
|
69
144
|
// drill-down mode: show a single session's full output
|
|
70
145
|
viewMode = "overview";
|
|
71
146
|
drilldownSessionId = null;
|
|
@@ -122,6 +197,35 @@ export class TUI {
|
|
|
122
197
|
getSessionCount() {
|
|
123
198
|
return this.sessions.length;
|
|
124
199
|
}
|
|
200
|
+
/** Set the session sort mode and repaint. */
|
|
201
|
+
setSortMode(mode) {
|
|
202
|
+
if (mode === this.sortMode)
|
|
203
|
+
return;
|
|
204
|
+
this.sortMode = mode;
|
|
205
|
+
// re-sort current sessions and repaint
|
|
206
|
+
this.sessions = sortSessions(this.sessions, this.sortMode, this.lastChangeAt);
|
|
207
|
+
if (this.active) {
|
|
208
|
+
this.paintSessions();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Return the current sort mode. */
|
|
212
|
+
getSortMode() {
|
|
213
|
+
return this.sortMode;
|
|
214
|
+
}
|
|
215
|
+
/** Toggle or set compact mode. Recomputes layout and repaints. */
|
|
216
|
+
setCompact(enabled) {
|
|
217
|
+
if (enabled === this.compactMode)
|
|
218
|
+
return;
|
|
219
|
+
this.compactMode = enabled;
|
|
220
|
+
if (this.active) {
|
|
221
|
+
this.computeLayout(this.sessions.length);
|
|
222
|
+
this.paintAll();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** Return whether compact mode is enabled. */
|
|
226
|
+
isCompact() {
|
|
227
|
+
return this.compactMode;
|
|
228
|
+
}
|
|
125
229
|
// ── State updates ───────────────────────────────────────────────────────
|
|
126
230
|
updateState(opts) {
|
|
127
231
|
if (opts.phase !== undefined)
|
|
@@ -137,8 +241,19 @@ export class TUI {
|
|
|
137
241
|
if (opts.pendingCount !== undefined)
|
|
138
242
|
this.pendingCount = opts.pendingCount;
|
|
139
243
|
if (opts.sessions !== undefined) {
|
|
140
|
-
|
|
141
|
-
|
|
244
|
+
// track activity changes for sort-by-activity
|
|
245
|
+
const now = Date.now();
|
|
246
|
+
for (const s of opts.sessions) {
|
|
247
|
+
const prev = this.prevLastActivity.get(s.id);
|
|
248
|
+
if (s.lastActivity !== undefined && s.lastActivity !== prev) {
|
|
249
|
+
this.lastChangeAt.set(s.id, now);
|
|
250
|
+
}
|
|
251
|
+
if (s.lastActivity !== undefined)
|
|
252
|
+
this.prevLastActivity.set(s.id, s.lastActivity);
|
|
253
|
+
}
|
|
254
|
+
const sorted = sortSessions(opts.sessions, this.sortMode, this.lastChangeAt);
|
|
255
|
+
const sessionCountChanged = sorted.length !== this.sessions.length;
|
|
256
|
+
this.sessions = sorted;
|
|
142
257
|
if (sessionCountChanged) {
|
|
143
258
|
this.computeLayout(this.sessions.length);
|
|
144
259
|
this.paintAll();
|
|
@@ -404,7 +519,9 @@ export class TUI {
|
|
|
404
519
|
}
|
|
405
520
|
else {
|
|
406
521
|
// overview: header (1) + sessions box + separator + activity + input
|
|
407
|
-
const sessBodyRows =
|
|
522
|
+
const sessBodyRows = this.compactMode
|
|
523
|
+
? computeCompactRowCount(this.sessions, this.cols - 2)
|
|
524
|
+
: Math.max(sessionCount, 1);
|
|
408
525
|
this.sessionRows = sessBodyRows + 2; // + top/bottom borders
|
|
409
526
|
this.separatorRow = this.headerHeight + this.sessionRows + 1;
|
|
410
527
|
this.inputRow = this.rows;
|
|
@@ -464,8 +581,11 @@ export class TUI {
|
|
|
464
581
|
paintSessions() {
|
|
465
582
|
const startRow = this.headerHeight + 1;
|
|
466
583
|
const innerWidth = this.cols - 2; // inside the box borders
|
|
467
|
-
// top border with label
|
|
468
|
-
const
|
|
584
|
+
// top border with label (includes compact/sort mode tags)
|
|
585
|
+
const sortTag = this.sortMode !== "default" ? this.sortMode : "";
|
|
586
|
+
const compactTag = this.compactMode ? "compact" : "";
|
|
587
|
+
const tags = [compactTag, sortTag].filter(Boolean).join(", ");
|
|
588
|
+
const label = tags ? ` agents (${tags}) ` : " agents ";
|
|
469
589
|
const borderAfterLabel = Math.max(0, innerWidth - label.length);
|
|
470
590
|
const topBorder = `${SLATE}${BOX.rtl}${BOX.h}${RESET}${SLATE}${label}${RESET}${SLATE}${BOX.h.repeat(borderAfterLabel)}${BOX.rtr}${RESET}`;
|
|
471
591
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow, 1) + CLEAR_LINE + truncateAnsi(topBorder, this.cols));
|
|
@@ -475,6 +595,15 @@ export class TUI {
|
|
|
475
595
|
const padded = padBoxLine(empty, this.cols);
|
|
476
596
|
process.stderr.write(moveTo(startRow + 1, 1) + CLEAR_LINE + padded);
|
|
477
597
|
}
|
|
598
|
+
else if (this.compactMode) {
|
|
599
|
+
// compact: inline tokens, multiple per row
|
|
600
|
+
const compactRows = formatCompactRows(this.sessions, innerWidth - 1);
|
|
601
|
+
for (let r = 0; r < compactRows.length; r++) {
|
|
602
|
+
const line = `${SLATE}${BOX.v}${RESET} ${compactRows[r]}`;
|
|
603
|
+
const padded = padBoxLine(line, this.cols);
|
|
604
|
+
process.stderr.write(moveTo(startRow + 1 + r, 1) + CLEAR_LINE + padded);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
478
607
|
else {
|
|
479
608
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
480
609
|
const s = this.sessions[i];
|
|
@@ -486,7 +615,9 @@ export class TUI {
|
|
|
486
615
|
}
|
|
487
616
|
}
|
|
488
617
|
// bottom border
|
|
489
|
-
const bodyRows =
|
|
618
|
+
const bodyRows = this.compactMode
|
|
619
|
+
? computeCompactRowCount(this.sessions, innerWidth)
|
|
620
|
+
: Math.max(this.sessions.length, 1);
|
|
490
621
|
const bottomRow = startRow + 1 + bodyRows;
|
|
491
622
|
const bottomBorder = `${SLATE}${BOX.rbl}${BOX.h.repeat(Math.max(0, this.cols - 2))}${BOX.rbr}${RESET}`;
|
|
492
623
|
process.stderr.write(moveTo(bottomRow, 1) + CLEAR_LINE + truncateAnsi(bottomBorder, this.cols));
|
|
@@ -875,5 +1006,5 @@ export function hitTestSession(row, headerHeight, sessionCount) {
|
|
|
875
1006
|
return row - firstSessionRow + 1; // 1-indexed
|
|
876
1007
|
}
|
|
877
1008
|
// ── Exported pure helpers (for testing) ─────────────────────────────────────
|
|
878
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline };
|
|
1009
|
+
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 };
|
|
879
1010
|
//# sourceMappingURL=tui.js.map
|