aoaoe 0.78.0 → 0.80.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 +22 -2
- package/dist/input.d.ts +6 -0
- package/dist/input.js +35 -0
- package/dist/tui.d.ts +30 -3
- package/dist/tui.js +138 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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);
|
|
@@ -369,9 +372,26 @@ async function main() {
|
|
|
369
372
|
tui.log("system", `unknown sort mode: ${mode} (try: status, name, activity, default)`);
|
|
370
373
|
}
|
|
371
374
|
});
|
|
372
|
-
// wire
|
|
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 /pin toggle
|
|
382
|
+
input.onPin((target) => {
|
|
383
|
+
const num = /^\d+$/.test(target) ? parseInt(target, 10) : undefined;
|
|
384
|
+
const ok = tui.togglePin(num ?? target);
|
|
385
|
+
if (ok) {
|
|
386
|
+
tui.log("system", `pin toggled: ${target}`);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
tui.log("system", `session not found: ${target}`);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
// wire mouse move to hover highlight on session cards (disabled in compact)
|
|
373
393
|
input.onMouseMove((row, _col) => {
|
|
374
|
-
if (tui.getViewMode() === "overview") {
|
|
394
|
+
if (tui.getViewMode() === "overview" && !tui.isCompact()) {
|
|
375
395
|
const sessionIdx = hitTestSession(row, 1, tui.getSessionCount());
|
|
376
396
|
tui.setHoverSession(sessionIdx);
|
|
377
397
|
}
|
package/dist/input.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ 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
6
|
export type SortHandler = (mode: string | null) => void;
|
|
7
|
+
export type CompactHandler = () => void;
|
|
8
|
+
export type PinHandler = (target: string) => void;
|
|
7
9
|
export interface MouseEvent {
|
|
8
10
|
button: number;
|
|
9
11
|
col: number;
|
|
@@ -30,6 +32,8 @@ export declare class InputReader {
|
|
|
30
32
|
private searchHandler;
|
|
31
33
|
private quickSwitchHandler;
|
|
32
34
|
private sortHandler;
|
|
35
|
+
private compactHandler;
|
|
36
|
+
private pinHandler;
|
|
33
37
|
private mouseDataListener;
|
|
34
38
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
35
39
|
onQueueChange(handler: (count: number) => void): void;
|
|
@@ -40,6 +44,8 @@ export declare class InputReader {
|
|
|
40
44
|
onSearch(handler: SearchHandler): void;
|
|
41
45
|
onQuickSwitch(handler: QuickSwitchHandler): void;
|
|
42
46
|
onSort(handler: SortHandler): void;
|
|
47
|
+
onCompact(handler: CompactHandler): void;
|
|
48
|
+
onPin(handler: PinHandler): void;
|
|
43
49
|
private notifyQueueChange;
|
|
44
50
|
start(): void;
|
|
45
51
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -36,6 +36,8 @@ export class InputReader {
|
|
|
36
36
|
searchHandler = null;
|
|
37
37
|
quickSwitchHandler = null;
|
|
38
38
|
sortHandler = null;
|
|
39
|
+
compactHandler = null;
|
|
40
|
+
pinHandler = null;
|
|
39
41
|
mouseDataListener = null;
|
|
40
42
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
41
43
|
onScroll(handler) {
|
|
@@ -73,6 +75,14 @@ export class InputReader {
|
|
|
73
75
|
onSort(handler) {
|
|
74
76
|
this.sortHandler = handler;
|
|
75
77
|
}
|
|
78
|
+
// register a callback for compact mode toggle (/compact)
|
|
79
|
+
onCompact(handler) {
|
|
80
|
+
this.compactHandler = handler;
|
|
81
|
+
}
|
|
82
|
+
// register a callback for pin/unpin commands (/pin <target>)
|
|
83
|
+
onPin(handler) {
|
|
84
|
+
this.pinHandler = handler;
|
|
85
|
+
}
|
|
76
86
|
notifyQueueChange() {
|
|
77
87
|
this.queueChangeHandler?.(this.queue.length);
|
|
78
88
|
}
|
|
@@ -251,6 +261,8 @@ ${BOLD}navigation:${RESET}
|
|
|
251
261
|
/view [N|name] drill into a session's live output (default: 1)
|
|
252
262
|
/back return to overview from drill-down
|
|
253
263
|
/sort [mode] sort sessions: status, name, activity, default (or cycle)
|
|
264
|
+
/compact toggle compact mode (dense session panel)
|
|
265
|
+
/pin [N|name] pin/unpin a session to the top (toggle)
|
|
254
266
|
/search <pattern> filter activity entries by substring (case-insensitive)
|
|
255
267
|
/search clear active search filter
|
|
256
268
|
click session click an agent card to drill down (click again to go back)
|
|
@@ -340,6 +352,29 @@ ${BOLD}other:${RESET}
|
|
|
340
352
|
}
|
|
341
353
|
break;
|
|
342
354
|
}
|
|
355
|
+
case "/compact":
|
|
356
|
+
if (this.compactHandler) {
|
|
357
|
+
this.compactHandler();
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
console.error(`${DIM}compact mode not available (no TUI)${RESET}`);
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
case "/pin": {
|
|
364
|
+
const pinArg = line.slice("/pin".length).trim();
|
|
365
|
+
if (this.pinHandler) {
|
|
366
|
+
if (pinArg) {
|
|
367
|
+
this.pinHandler(pinArg);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
console.error(`${DIM}usage: /pin <N|name> — toggle pin for a session${RESET}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
console.error(`${DIM}pin not available (no TUI)${RESET}`);
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
343
378
|
case "/search": {
|
|
344
379
|
const searchArg = line.slice("/search".length).trim();
|
|
345
380
|
if (this.searchHandler) {
|
package/dist/tui.d.ts
CHANGED
|
@@ -2,10 +2,22 @@ import type { DaemonSessionState, DaemonPhase } from "./types.js";
|
|
|
2
2
|
import type { HistoryEntry } from "./tui-history.js";
|
|
3
3
|
export type SortMode = "default" | "status" | "name" | "activity";
|
|
4
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[];
|
|
5
|
+
/** Sort sessions by mode. Pinned sessions always sort first (stable). Returns a new array (never mutates). */
|
|
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
|
+
/** Max name length in compact token. */
|
|
10
|
+
declare const COMPACT_NAME_LEN = 10;
|
|
11
|
+
/** Pin indicator for pinned sessions. */
|
|
12
|
+
declare const PIN_ICON = "\u25B2";
|
|
13
|
+
/**
|
|
14
|
+
* Format sessions as inline compact tokens, wrapped to fit maxWidth.
|
|
15
|
+
* Each token: "{idx}{pin?}{dot}{name}" — e.g. "1▲●Alpha" for pinned, "2●Bravo" for unpinned.
|
|
16
|
+
* Returns array of formatted row strings (one per display row).
|
|
17
|
+
*/
|
|
18
|
+
declare function formatCompactRows(sessions: DaemonSessionState[], maxWidth: number, pinnedIds?: Set<string>): string[];
|
|
19
|
+
/** Compute how many display rows compact mode needs (minimum 1). */
|
|
20
|
+
declare function computeCompactRowCount(sessions: DaemonSessionState[], maxWidth: number): number;
|
|
9
21
|
declare function phaseDisplay(phase: DaemonPhase, paused: boolean, spinnerFrame: number): string;
|
|
10
22
|
export interface ActivityEntry {
|
|
11
23
|
time: string;
|
|
@@ -35,6 +47,8 @@ export declare class TUI {
|
|
|
35
47
|
private sortMode;
|
|
36
48
|
private lastChangeAt;
|
|
37
49
|
private prevLastActivity;
|
|
50
|
+
private compactMode;
|
|
51
|
+
private pinnedIds;
|
|
38
52
|
private viewMode;
|
|
39
53
|
private drilldownSessionId;
|
|
40
54
|
private sessionOutputs;
|
|
@@ -56,6 +70,19 @@ export declare class TUI {
|
|
|
56
70
|
setSortMode(mode: SortMode): void;
|
|
57
71
|
/** Return the current sort mode. */
|
|
58
72
|
getSortMode(): SortMode;
|
|
73
|
+
/** Toggle or set compact mode. Recomputes layout and repaints. */
|
|
74
|
+
setCompact(enabled: boolean): void;
|
|
75
|
+
/** Return whether compact mode is enabled. */
|
|
76
|
+
isCompact(): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Toggle pin for a session (by 1-indexed number, ID, ID prefix, or title).
|
|
79
|
+
* Pinned sessions always sort to the top. Returns true if session found.
|
|
80
|
+
*/
|
|
81
|
+
togglePin(sessionIdOrIndex: string | number): boolean;
|
|
82
|
+
/** Check if a session ID is pinned. */
|
|
83
|
+
isPinned(id: string): boolean;
|
|
84
|
+
/** Return count of pinned sessions. */
|
|
85
|
+
getPinnedCount(): number;
|
|
59
86
|
updateState(opts: {
|
|
60
87
|
phase?: DaemonPhase;
|
|
61
88
|
pollCount?: number;
|
|
@@ -146,5 +173,5 @@ declare function formatSearchIndicator(pattern: string, matchCount: number, tota
|
|
|
146
173
|
* (row = headerHeight + 2 + i for 0-indexed session i)
|
|
147
174
|
*/
|
|
148
175
|
export declare function hitTestSession(row: number, headerHeight: number, sessionCount: number): number | null;
|
|
149
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline, sortSessions, nextSortMode, SORT_MODES };
|
|
176
|
+
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 };
|
|
150
177
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.js
CHANGED
|
@@ -26,8 +26,8 @@ const STATUS_PRIORITY = {
|
|
|
26
26
|
error: 0, waiting: 1, working: 2, running: 2,
|
|
27
27
|
idle: 3, done: 4, stopped: 5, unknown: 6,
|
|
28
28
|
};
|
|
29
|
-
/** Sort sessions by mode. Returns a new array (never mutates). */
|
|
30
|
-
function sortSessions(sessions, mode, lastChangeAt) {
|
|
29
|
+
/** Sort sessions by mode. Pinned sessions always sort first (stable). Returns a new array (never mutates). */
|
|
30
|
+
function sortSessions(sessions, mode, lastChangeAt, pinnedIds) {
|
|
31
31
|
const copy = sessions.slice();
|
|
32
32
|
switch (mode) {
|
|
33
33
|
case "status":
|
|
@@ -41,6 +41,10 @@ function sortSessions(sessions, mode, lastChangeAt) {
|
|
|
41
41
|
break;
|
|
42
42
|
// "default" — preserve original order
|
|
43
43
|
}
|
|
44
|
+
// stable-sort pinned to top (preserves mode order within each group)
|
|
45
|
+
if (pinnedIds && pinnedIds.size > 0) {
|
|
46
|
+
copy.sort((a, b) => (pinnedIds.has(a.id) ? 0 : 1) - (pinnedIds.has(b.id) ? 0 : 1));
|
|
47
|
+
}
|
|
44
48
|
return copy;
|
|
45
49
|
}
|
|
46
50
|
/** Cycle to the next sort mode. */
|
|
@@ -48,6 +52,54 @@ function nextSortMode(current) {
|
|
|
48
52
|
const idx = SORT_MODES.indexOf(current);
|
|
49
53
|
return SORT_MODES[(idx + 1) % SORT_MODES.length];
|
|
50
54
|
}
|
|
55
|
+
// ── Compact mode ────────────────────────────────────────────────────────────
|
|
56
|
+
/** Max name length in compact token. */
|
|
57
|
+
const COMPACT_NAME_LEN = 10;
|
|
58
|
+
/** Pin indicator for pinned sessions. */
|
|
59
|
+
const PIN_ICON = "▲";
|
|
60
|
+
/**
|
|
61
|
+
* Format sessions as inline compact tokens, wrapped to fit maxWidth.
|
|
62
|
+
* Each token: "{idx}{pin?}{dot}{name}" — e.g. "1▲●Alpha" for pinned, "2●Bravo" for unpinned.
|
|
63
|
+
* Returns array of formatted row strings (one per display row).
|
|
64
|
+
*/
|
|
65
|
+
function formatCompactRows(sessions, maxWidth, pinnedIds) {
|
|
66
|
+
if (sessions.length === 0)
|
|
67
|
+
return [`${DIM}no agents connected${RESET}`];
|
|
68
|
+
const tokens = [];
|
|
69
|
+
const widths = [];
|
|
70
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
71
|
+
const s = sessions[i];
|
|
72
|
+
const idx = String(i + 1);
|
|
73
|
+
const dot = STATUS_DOT[s.status] ?? `${AMBER}${DOT.filled}${RESET}`;
|
|
74
|
+
const pinned = pinnedIds?.has(s.id) ?? false;
|
|
75
|
+
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET}` : "";
|
|
76
|
+
const name = truncatePlain(s.title, COMPACT_NAME_LEN);
|
|
77
|
+
tokens.push(`${SLATE}${idx}${RESET}${pin}${dot}${BOLD}${name}${RESET}`);
|
|
78
|
+
widths.push(idx.length + (pinned ? 1 : 0) + 1 + name.length);
|
|
79
|
+
}
|
|
80
|
+
const rows = [];
|
|
81
|
+
let currentRow = "";
|
|
82
|
+
let currentWidth = 0;
|
|
83
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
84
|
+
const gap = currentWidth > 0 ? 2 : 0;
|
|
85
|
+
if (currentWidth + gap + widths[i] > maxWidth && currentWidth > 0) {
|
|
86
|
+
rows.push(currentRow);
|
|
87
|
+
currentRow = tokens[i];
|
|
88
|
+
currentWidth = widths[i];
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
currentRow += (currentWidth > 0 ? " " : "") + tokens[i];
|
|
92
|
+
currentWidth += gap + widths[i];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (currentRow)
|
|
96
|
+
rows.push(currentRow);
|
|
97
|
+
return rows;
|
|
98
|
+
}
|
|
99
|
+
/** Compute how many display rows compact mode needs (minimum 1). */
|
|
100
|
+
function computeCompactRowCount(sessions, maxWidth) {
|
|
101
|
+
return Math.max(1, formatCompactRows(sessions, maxWidth).length);
|
|
102
|
+
}
|
|
51
103
|
// ── Status rendering ────────────────────────────────────────────────────────
|
|
52
104
|
const STATUS_DOT = {
|
|
53
105
|
working: `${LIME}${DOT.filled}${RESET}`,
|
|
@@ -96,6 +148,8 @@ export class TUI {
|
|
|
96
148
|
sortMode = "default";
|
|
97
149
|
lastChangeAt = new Map(); // session ID → epoch ms of last activity change
|
|
98
150
|
prevLastActivity = new Map(); // session ID → previous lastActivity string
|
|
151
|
+
compactMode = false;
|
|
152
|
+
pinnedIds = new Set(); // pinned session IDs (always sort to top)
|
|
99
153
|
// drill-down mode: show a single session's full output
|
|
100
154
|
viewMode = "overview";
|
|
101
155
|
drilldownSessionId = null;
|
|
@@ -158,7 +212,7 @@ export class TUI {
|
|
|
158
212
|
return;
|
|
159
213
|
this.sortMode = mode;
|
|
160
214
|
// re-sort current sessions and repaint
|
|
161
|
-
this.sessions = sortSessions(this.sessions, this.sortMode, this.lastChangeAt);
|
|
215
|
+
this.sessions = sortSessions(this.sessions, this.sortMode, this.lastChangeAt, this.pinnedIds);
|
|
162
216
|
if (this.active) {
|
|
163
217
|
this.paintSessions();
|
|
164
218
|
}
|
|
@@ -167,6 +221,57 @@ export class TUI {
|
|
|
167
221
|
getSortMode() {
|
|
168
222
|
return this.sortMode;
|
|
169
223
|
}
|
|
224
|
+
/** Toggle or set compact mode. Recomputes layout and repaints. */
|
|
225
|
+
setCompact(enabled) {
|
|
226
|
+
if (enabled === this.compactMode)
|
|
227
|
+
return;
|
|
228
|
+
this.compactMode = enabled;
|
|
229
|
+
if (this.active) {
|
|
230
|
+
this.computeLayout(this.sessions.length);
|
|
231
|
+
this.paintAll();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** Return whether compact mode is enabled. */
|
|
235
|
+
isCompact() {
|
|
236
|
+
return this.compactMode;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Toggle pin for a session (by 1-indexed number, ID, ID prefix, or title).
|
|
240
|
+
* Pinned sessions always sort to the top. Returns true if session found.
|
|
241
|
+
*/
|
|
242
|
+
togglePin(sessionIdOrIndex) {
|
|
243
|
+
let sessionId;
|
|
244
|
+
if (typeof sessionIdOrIndex === "number") {
|
|
245
|
+
sessionId = this.sessions[sessionIdOrIndex - 1]?.id;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
const needle = sessionIdOrIndex.toLowerCase();
|
|
249
|
+
const match = this.sessions.find((s) => s.id === sessionIdOrIndex || s.id.startsWith(needle) || s.title.toLowerCase() === needle);
|
|
250
|
+
sessionId = match?.id;
|
|
251
|
+
}
|
|
252
|
+
if (!sessionId)
|
|
253
|
+
return false;
|
|
254
|
+
if (this.pinnedIds.has(sessionId)) {
|
|
255
|
+
this.pinnedIds.delete(sessionId);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.pinnedIds.add(sessionId);
|
|
259
|
+
}
|
|
260
|
+
// re-sort and repaint
|
|
261
|
+
this.sessions = sortSessions(this.sessions, this.sortMode, this.lastChangeAt, this.pinnedIds);
|
|
262
|
+
if (this.active) {
|
|
263
|
+
this.paintSessions();
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
/** Check if a session ID is pinned. */
|
|
268
|
+
isPinned(id) {
|
|
269
|
+
return this.pinnedIds.has(id);
|
|
270
|
+
}
|
|
271
|
+
/** Return count of pinned sessions. */
|
|
272
|
+
getPinnedCount() {
|
|
273
|
+
return this.pinnedIds.size;
|
|
274
|
+
}
|
|
170
275
|
// ── State updates ───────────────────────────────────────────────────────
|
|
171
276
|
updateState(opts) {
|
|
172
277
|
if (opts.phase !== undefined)
|
|
@@ -192,7 +297,7 @@ export class TUI {
|
|
|
192
297
|
if (s.lastActivity !== undefined)
|
|
193
298
|
this.prevLastActivity.set(s.id, s.lastActivity);
|
|
194
299
|
}
|
|
195
|
-
const sorted = sortSessions(opts.sessions, this.sortMode, this.lastChangeAt);
|
|
300
|
+
const sorted = sortSessions(opts.sessions, this.sortMode, this.lastChangeAt, this.pinnedIds);
|
|
196
301
|
const sessionCountChanged = sorted.length !== this.sessions.length;
|
|
197
302
|
this.sessions = sorted;
|
|
198
303
|
if (sessionCountChanged) {
|
|
@@ -460,7 +565,9 @@ export class TUI {
|
|
|
460
565
|
}
|
|
461
566
|
else {
|
|
462
567
|
// overview: header (1) + sessions box + separator + activity + input
|
|
463
|
-
const sessBodyRows =
|
|
568
|
+
const sessBodyRows = this.compactMode
|
|
569
|
+
? computeCompactRowCount(this.sessions, this.cols - 2)
|
|
570
|
+
: Math.max(sessionCount, 1);
|
|
464
571
|
this.sessionRows = sessBodyRows + 2; // + top/bottom borders
|
|
465
572
|
this.separatorRow = this.headerHeight + this.sessionRows + 1;
|
|
466
573
|
this.inputRow = this.rows;
|
|
@@ -520,8 +627,11 @@ export class TUI {
|
|
|
520
627
|
paintSessions() {
|
|
521
628
|
const startRow = this.headerHeight + 1;
|
|
522
629
|
const innerWidth = this.cols - 2; // inside the box borders
|
|
523
|
-
// top border with label (includes sort mode
|
|
524
|
-
const
|
|
630
|
+
// top border with label (includes compact/sort mode tags)
|
|
631
|
+
const sortTag = this.sortMode !== "default" ? this.sortMode : "";
|
|
632
|
+
const compactTag = this.compactMode ? "compact" : "";
|
|
633
|
+
const tags = [compactTag, sortTag].filter(Boolean).join(", ");
|
|
634
|
+
const label = tags ? ` agents (${tags}) ` : " agents ";
|
|
525
635
|
const borderAfterLabel = Math.max(0, innerWidth - label.length);
|
|
526
636
|
const topBorder = `${SLATE}${BOX.rtl}${BOX.h}${RESET}${SLATE}${label}${RESET}${SLATE}${BOX.h.repeat(borderAfterLabel)}${BOX.rtr}${RESET}`;
|
|
527
637
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow, 1) + CLEAR_LINE + truncateAnsi(topBorder, this.cols));
|
|
@@ -531,18 +641,32 @@ export class TUI {
|
|
|
531
641
|
const padded = padBoxLine(empty, this.cols);
|
|
532
642
|
process.stderr.write(moveTo(startRow + 1, 1) + CLEAR_LINE + padded);
|
|
533
643
|
}
|
|
644
|
+
else if (this.compactMode) {
|
|
645
|
+
// compact: inline tokens, multiple per row (with pin indicators)
|
|
646
|
+
const compactRows = formatCompactRows(this.sessions, innerWidth - 1, this.pinnedIds);
|
|
647
|
+
for (let r = 0; r < compactRows.length; r++) {
|
|
648
|
+
const line = `${SLATE}${BOX.v}${RESET} ${compactRows[r]}`;
|
|
649
|
+
const padded = padBoxLine(line, this.cols);
|
|
650
|
+
process.stderr.write(moveTo(startRow + 1 + r, 1) + CLEAR_LINE + padded);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
534
653
|
else {
|
|
535
654
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
536
655
|
const s = this.sessions[i];
|
|
537
656
|
const isHovered = this.hoverSessionIdx === i + 1; // 1-indexed
|
|
538
657
|
const bg = isHovered ? BG_HOVER : "";
|
|
539
|
-
const
|
|
658
|
+
const pinned = this.pinnedIds.has(s.id);
|
|
659
|
+
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
|
|
660
|
+
const cardWidth = pinned ? innerWidth - 3 : innerWidth - 1; // pin takes 2 extra chars
|
|
661
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${formatSessionCard(s, cardWidth)}`;
|
|
540
662
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
541
663
|
process.stderr.write(moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded);
|
|
542
664
|
}
|
|
543
665
|
}
|
|
544
666
|
// bottom border
|
|
545
|
-
const bodyRows =
|
|
667
|
+
const bodyRows = this.compactMode
|
|
668
|
+
? computeCompactRowCount(this.sessions, innerWidth)
|
|
669
|
+
: Math.max(this.sessions.length, 1);
|
|
546
670
|
const bottomRow = startRow + 1 + bodyRows;
|
|
547
671
|
const bottomBorder = `${SLATE}${BOX.rbl}${BOX.h.repeat(Math.max(0, this.cols - 2))}${BOX.rbr}${RESET}`;
|
|
548
672
|
process.stderr.write(moveTo(bottomRow, 1) + CLEAR_LINE + truncateAnsi(bottomBorder, this.cols));
|
|
@@ -564,7 +688,10 @@ export class TUI {
|
|
|
564
688
|
const s = this.sessions[i];
|
|
565
689
|
const isHovered = this.hoverSessionIdx === idx;
|
|
566
690
|
const bg = isHovered ? BG_HOVER : "";
|
|
567
|
-
const
|
|
691
|
+
const pinned = this.pinnedIds.has(s.id);
|
|
692
|
+
const pin = pinned ? `${AMBER}${PIN_ICON}${RESET} ` : "";
|
|
693
|
+
const cardWidth = pinned ? innerWidth - 3 : innerWidth - 1;
|
|
694
|
+
const line = `${bg}${SLATE}${BOX.v}${RESET}${bg} ${pin}${formatSessionCard(s, cardWidth)}`;
|
|
568
695
|
const padded = padBoxLineHover(line, this.cols, isHovered);
|
|
569
696
|
process.stderr.write(SAVE_CURSOR + moveTo(startRow + 1 + i, 1) + CLEAR_LINE + padded + RESTORE_CURSOR);
|
|
570
697
|
}
|
|
@@ -931,5 +1058,5 @@ export function hitTestSession(row, headerHeight, sessionCount) {
|
|
|
931
1058
|
return row - firstSessionRow + 1; // 1-indexed
|
|
932
1059
|
}
|
|
933
1060
|
// ── Exported pure helpers (for testing) ─────────────────────────────────────
|
|
934
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padBoxLineHover, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator, computeSparkline, formatSparkline, sortSessions, nextSortMode, SORT_MODES };
|
|
1061
|
+
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 };
|
|
935
1062
|
//# sourceMappingURL=tui.js.map
|