aoaoe 0.73.0 → 0.74.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 +10 -0
- package/dist/input.d.ts +3 -0
- package/dist/input.js +17 -0
- package/dist/tui.d.ts +10 -1
- package/dist/tui.js +64 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -325,6 +325,16 @@ async function main() {
|
|
|
325
325
|
tui.log("system", `viewing session #${sessionIdx}`);
|
|
326
326
|
}
|
|
327
327
|
});
|
|
328
|
+
// wire /search command to TUI activity filter
|
|
329
|
+
input.onSearch((pattern) => {
|
|
330
|
+
tui.setSearch(pattern);
|
|
331
|
+
if (pattern) {
|
|
332
|
+
tui.log("system", `search: "${pattern}"`);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
tui.log("system", "search cleared");
|
|
336
|
+
}
|
|
337
|
+
});
|
|
328
338
|
// wire mouse wheel to scroll (3 lines per tick for smooth scrolling)
|
|
329
339
|
input.onMouseWheel((direction) => {
|
|
330
340
|
if (tui.getViewMode() === "drilldown") {
|
package/dist/input.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type ScrollDirection = "up" | "down" | "top" | "bottom";
|
|
2
2
|
export declare const INSIST_PREFIX = "__INSIST__";
|
|
3
3
|
export type ViewHandler = (target: string | null) => void;
|
|
4
|
+
export type SearchHandler = (pattern: string | null) => void;
|
|
4
5
|
export interface MouseEvent {
|
|
5
6
|
button: number;
|
|
6
7
|
col: number;
|
|
@@ -21,12 +22,14 @@ export declare class InputReader {
|
|
|
21
22
|
private viewHandler;
|
|
22
23
|
private mouseClickHandler;
|
|
23
24
|
private mouseWheelHandler;
|
|
25
|
+
private searchHandler;
|
|
24
26
|
private mouseDataListener;
|
|
25
27
|
onScroll(handler: (dir: ScrollDirection) => void): void;
|
|
26
28
|
onQueueChange(handler: (count: number) => void): void;
|
|
27
29
|
onView(handler: ViewHandler): void;
|
|
28
30
|
onMouseClick(handler: MouseClickHandler): void;
|
|
29
31
|
onMouseWheel(handler: MouseWheelHandler): void;
|
|
32
|
+
onSearch(handler: SearchHandler): void;
|
|
30
33
|
private notifyQueueChange;
|
|
31
34
|
start(): void;
|
|
32
35
|
drain(): string[];
|
package/dist/input.js
CHANGED
|
@@ -31,6 +31,7 @@ export class InputReader {
|
|
|
31
31
|
viewHandler = null;
|
|
32
32
|
mouseClickHandler = null;
|
|
33
33
|
mouseWheelHandler = null;
|
|
34
|
+
searchHandler = null;
|
|
34
35
|
mouseDataListener = null;
|
|
35
36
|
// register a callback for scroll key events (PgUp/PgDn/Home/End)
|
|
36
37
|
onScroll(handler) {
|
|
@@ -52,6 +53,10 @@ export class InputReader {
|
|
|
52
53
|
onMouseWheel(handler) {
|
|
53
54
|
this.mouseWheelHandler = handler;
|
|
54
55
|
}
|
|
56
|
+
// register a callback for search commands (/search <pattern> or /search to clear)
|
|
57
|
+
onSearch(handler) {
|
|
58
|
+
this.searchHandler = handler;
|
|
59
|
+
}
|
|
55
60
|
notifyQueueChange() {
|
|
56
61
|
this.queueChangeHandler?.(this.queue.length);
|
|
57
62
|
}
|
|
@@ -215,6 +220,8 @@ ${BOLD}controls:${RESET}
|
|
|
215
220
|
${BOLD}navigation:${RESET}
|
|
216
221
|
/view [N|name] drill into a session's live output (default: 1)
|
|
217
222
|
/back return to overview from drill-down
|
|
223
|
+
/search <pattern> filter activity entries by substring (case-insensitive)
|
|
224
|
+
/search clear active search filter
|
|
218
225
|
click session click an agent card to drill down (click again to go back)
|
|
219
226
|
mouse wheel scroll activity (overview) or session output (drill-down)
|
|
220
227
|
PgUp / PgDn scroll through activity or session output
|
|
@@ -292,6 +299,16 @@ ${BOLD}other:${RESET}
|
|
|
292
299
|
console.error(`${DIM}already in overview${RESET}`);
|
|
293
300
|
}
|
|
294
301
|
break;
|
|
302
|
+
case "/search": {
|
|
303
|
+
const searchArg = line.slice("/search".length).trim();
|
|
304
|
+
if (this.searchHandler) {
|
|
305
|
+
this.searchHandler(searchArg || null); // empty = clear search
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
console.error(`${DIM}search not available (no TUI)${RESET}`);
|
|
309
|
+
}
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
295
312
|
case "/clear":
|
|
296
313
|
process.stderr.write("\x1b[2J\x1b[H");
|
|
297
314
|
break;
|
package/dist/tui.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare class TUI {
|
|
|
23
23
|
private scrollOffset;
|
|
24
24
|
private newWhileScrolled;
|
|
25
25
|
private pendingCount;
|
|
26
|
+
private searchPattern;
|
|
26
27
|
private viewMode;
|
|
27
28
|
private drilldownSessionId;
|
|
28
29
|
private sessionOutputs;
|
|
@@ -70,6 +71,10 @@ export declare class TUI {
|
|
|
70
71
|
getViewMode(): "overview" | "drilldown";
|
|
71
72
|
/** Get drill-down session ID (or null) */
|
|
72
73
|
getDrilldownSessionId(): string | null;
|
|
74
|
+
/** Set or clear the search filter. Resets scroll and repaints. */
|
|
75
|
+
setSearch(pattern: string | null): void;
|
|
76
|
+
/** Get the current search pattern (or null if no active search). */
|
|
77
|
+
getSearchPattern(): string | null;
|
|
73
78
|
private updateDimensions;
|
|
74
79
|
private computeLayout;
|
|
75
80
|
private onResize;
|
|
@@ -103,6 +108,10 @@ declare function computeScrollSlice(bufferLen: number, visibleLines: number, scr
|
|
|
103
108
|
};
|
|
104
109
|
declare function formatScrollIndicator(offset: number, totalEntries: number, visibleLines: number, newCount: number): string;
|
|
105
110
|
declare function formatDrilldownScrollIndicator(offset: number, totalLines: number, visibleLines: number, newCount: number): string;
|
|
111
|
+
/** Case-insensitive substring match against an activity entry's tag, text, and time. */
|
|
112
|
+
declare function matchesSearch(entry: ActivityEntry, pattern: string): boolean;
|
|
113
|
+
/** Format the search indicator text for the separator bar. */
|
|
114
|
+
declare function formatSearchIndicator(pattern: string, matchCount: number, totalCount: number): string;
|
|
106
115
|
/**
|
|
107
116
|
* Hit-test a mouse click row against the session panel.
|
|
108
117
|
* Returns 1-indexed session number if the click hit a session card, null otherwise.
|
|
@@ -111,5 +120,5 @@ declare function formatDrilldownScrollIndicator(offset: number, totalLines: numb
|
|
|
111
120
|
* (row = headerHeight + 2 + i for 0-indexed session i)
|
|
112
121
|
*/
|
|
113
122
|
export declare function hitTestSession(row: number, headerHeight: number, sessionCount: number): number | null;
|
|
114
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader };
|
|
123
|
+
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator };
|
|
115
124
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.js
CHANGED
|
@@ -61,6 +61,7 @@ export class TUI {
|
|
|
61
61
|
scrollOffset = 0; // 0 = live (bottom), >0 = scrolled back N entries
|
|
62
62
|
newWhileScrolled = 0; // entries added while user is scrolled back
|
|
63
63
|
pendingCount = 0; // queued user messages awaiting next tick
|
|
64
|
+
searchPattern = null; // active search filter pattern
|
|
64
65
|
// drill-down mode: show a single session's full output
|
|
65
66
|
viewMode = "overview";
|
|
66
67
|
drilldownSessionId = null;
|
|
@@ -158,7 +159,20 @@ export class TUI {
|
|
|
158
159
|
this.activityBuffer = this.activityBuffer.slice(-this.maxActivity);
|
|
159
160
|
}
|
|
160
161
|
if (this.active) {
|
|
161
|
-
if (this.
|
|
162
|
+
if (this.searchPattern) {
|
|
163
|
+
// search active: only show new entry if it matches
|
|
164
|
+
if (matchesSearch(entry, this.searchPattern)) {
|
|
165
|
+
if (this.scrollOffset > 0) {
|
|
166
|
+
this.newWhileScrolled++;
|
|
167
|
+
this.paintSeparator();
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.writeActivityLine(entry);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// non-matching entries are silently buffered — visible when search is cleared
|
|
174
|
+
}
|
|
175
|
+
else if (this.scrollOffset > 0) {
|
|
162
176
|
// user is scrolled back — don't auto-scroll, just show indicator
|
|
163
177
|
this.newWhileScrolled++;
|
|
164
178
|
this.paintSeparator();
|
|
@@ -187,7 +201,10 @@ export class TUI {
|
|
|
187
201
|
return;
|
|
188
202
|
const visibleLines = this.scrollBottom - this.scrollTop + 1;
|
|
189
203
|
const n = lines ?? Math.max(1, Math.floor(visibleLines / 2));
|
|
190
|
-
const
|
|
204
|
+
const entryCount = this.searchPattern
|
|
205
|
+
? this.activityBuffer.filter((e) => matchesSearch(e, this.searchPattern)).length
|
|
206
|
+
: this.activityBuffer.length;
|
|
207
|
+
const maxOffset = Math.max(0, entryCount - visibleLines);
|
|
191
208
|
this.scrollOffset = Math.min(maxOffset, this.scrollOffset + n);
|
|
192
209
|
this.repaintActivityRegion();
|
|
193
210
|
this.paintSeparator();
|
|
@@ -208,7 +225,10 @@ export class TUI {
|
|
|
208
225
|
if (!this.active)
|
|
209
226
|
return;
|
|
210
227
|
const visibleLines = this.scrollBottom - this.scrollTop + 1;
|
|
211
|
-
|
|
228
|
+
const entryCount = this.searchPattern
|
|
229
|
+
? this.activityBuffer.filter((e) => matchesSearch(e, this.searchPattern)).length
|
|
230
|
+
: this.activityBuffer.length;
|
|
231
|
+
this.scrollOffset = Math.max(0, entryCount - visibleLines);
|
|
212
232
|
this.repaintActivityRegion();
|
|
213
233
|
this.paintSeparator();
|
|
214
234
|
}
|
|
@@ -325,6 +345,21 @@ export class TUI {
|
|
|
325
345
|
getDrilldownSessionId() {
|
|
326
346
|
return this.drilldownSessionId;
|
|
327
347
|
}
|
|
348
|
+
// ── Search ──────────────────────────────────────────────────────────────
|
|
349
|
+
/** Set or clear the search filter. Resets scroll and repaints. */
|
|
350
|
+
setSearch(pattern) {
|
|
351
|
+
this.searchPattern = pattern && pattern.length > 0 ? pattern : null;
|
|
352
|
+
this.scrollOffset = 0;
|
|
353
|
+
this.newWhileScrolled = 0;
|
|
354
|
+
if (this.active && this.viewMode === "overview") {
|
|
355
|
+
this.repaintActivityRegion();
|
|
356
|
+
this.paintSeparator();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Get the current search pattern (or null if no active search). */
|
|
360
|
+
getSearchPattern() {
|
|
361
|
+
return this.searchPattern;
|
|
362
|
+
}
|
|
328
363
|
// ── Layout computation ──────────────────────────────────────────────────
|
|
329
364
|
updateDimensions() {
|
|
330
365
|
this.cols = process.stderr.columns || 80;
|
|
@@ -435,7 +470,11 @@ export class TUI {
|
|
|
435
470
|
paintSeparator() {
|
|
436
471
|
const prefix = `${BOX.h}${BOX.h} activity `;
|
|
437
472
|
let hints;
|
|
438
|
-
if (this.
|
|
473
|
+
if (this.searchPattern) {
|
|
474
|
+
const filtered = this.activityBuffer.filter((e) => matchesSearch(e, this.searchPattern));
|
|
475
|
+
hints = formatSearchIndicator(this.searchPattern, filtered.length, this.activityBuffer.length);
|
|
476
|
+
}
|
|
477
|
+
else if (this.scrollOffset > 0) {
|
|
439
478
|
hints = formatScrollIndicator(this.scrollOffset, this.activityBuffer.length, this.scrollBottom - this.scrollTop + 1, this.newWhileScrolled);
|
|
440
479
|
}
|
|
441
480
|
else {
|
|
@@ -459,8 +498,12 @@ export class TUI {
|
|
|
459
498
|
}
|
|
460
499
|
repaintActivityRegion() {
|
|
461
500
|
const visibleLines = this.scrollBottom - this.scrollTop + 1;
|
|
462
|
-
|
|
463
|
-
const
|
|
501
|
+
// when search is active, filter entries first, then paginate
|
|
502
|
+
const source = this.searchPattern
|
|
503
|
+
? this.activityBuffer.filter((e) => matchesSearch(e, this.searchPattern))
|
|
504
|
+
: this.activityBuffer;
|
|
505
|
+
const { start, end } = computeScrollSlice(source.length, visibleLines, this.scrollOffset);
|
|
506
|
+
const entries = source.slice(start, end);
|
|
464
507
|
for (let i = 0; i < visibleLines; i++) {
|
|
465
508
|
const row = this.scrollTop + i;
|
|
466
509
|
if (i < entries.length) {
|
|
@@ -709,6 +752,20 @@ function formatDrilldownScrollIndicator(offset, totalLines, visibleLines, newCou
|
|
|
709
752
|
const newTag = newCount > 0 ? ` ${newCount} new ↓` : "";
|
|
710
753
|
return ` ↑ ${offset} lines │ ${position}/${totalLines} │ scroll: navigate End=live${newTag} `;
|
|
711
754
|
}
|
|
755
|
+
// ── Search helpers (pure, exported for testing) ─────────────────────────────
|
|
756
|
+
/** Case-insensitive substring match against an activity entry's tag, text, and time. */
|
|
757
|
+
function matchesSearch(entry, pattern) {
|
|
758
|
+
if (!pattern)
|
|
759
|
+
return true;
|
|
760
|
+
const lower = pattern.toLowerCase();
|
|
761
|
+
return (entry.tag.toLowerCase().includes(lower) ||
|
|
762
|
+
entry.text.toLowerCase().includes(lower) ||
|
|
763
|
+
entry.time.toLowerCase().includes(lower));
|
|
764
|
+
}
|
|
765
|
+
/** Format the search indicator text for the separator bar. */
|
|
766
|
+
function formatSearchIndicator(pattern, matchCount, totalCount) {
|
|
767
|
+
return ` search: "${pattern}" │ ${matchCount} of ${totalCount} │ /search: clear `;
|
|
768
|
+
}
|
|
712
769
|
// ── Mouse hit testing (pure, exported for testing) ──────────────────────────
|
|
713
770
|
/**
|
|
714
771
|
* Hit-test a mouse click row against the session panel.
|
|
@@ -727,5 +784,5 @@ export function hitTestSession(row, headerHeight, sessionCount) {
|
|
|
727
784
|
return row - firstSessionRow + 1; // 1-indexed
|
|
728
785
|
}
|
|
729
786
|
// ── Exported pure helpers (for testing) ─────────────────────────────────────
|
|
730
|
-
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader };
|
|
787
|
+
export { formatActivity, formatSessionCard, truncateAnsi, truncatePlain, padBoxLine, padToWidth, stripAnsiForLen, phaseDisplay, computeScrollSlice, formatScrollIndicator, formatDrilldownScrollIndicator, formatPrompt, formatDrilldownHeader, matchesSearch, formatSearchIndicator };
|
|
731
788
|
//# sourceMappingURL=tui.js.map
|