@develemit/web-hooks 0.4.0 → 0.5.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/CHANGELOG.md +6 -0
- package/README.md +63 -0
- package/dist/{chunk-TE2FRIPF.js → chunk-IVLRQ23V.js} +2 -135
- package/dist/chunk-JTLQQSLO.js +143 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -3
- package/dist/log-helpers-rkbgYqZW.d.cts +25 -0
- package/dist/log-helpers-rkbgYqZW.d.ts +25 -0
- package/dist/terminal-output.d.cts +2 -25
- package/dist/terminal-output.d.ts +2 -25
- package/dist/terminal-output.js +5 -3
- package/dist/virtual-list.cjs +499 -0
- package/dist/virtual-list.d.cts +116 -0
- package/dist/virtual-list.d.ts +116 -0
- package/dist/virtual-list.js +287 -0
- package/package.json +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-11-06
|
|
4
|
+
|
|
5
|
+
- **New optional peer dependency:** `@tanstack/react-virtual` (`^3.13.0`), needed only by apps that import `@develemit/web-hooks/virtual-list`. Every other entry, including the root, works without it.
|
|
6
|
+
- `virtual-list`: `useVirtualLog({ lines, scrollRef, query, estimateSize, overscan, resetKey, filename })` combines `useVirtualList` with `useFollowTail`, a deferred `filterLines` search, incremental `findFailures` with `jumpToFailure` / `nextFailure` / `previousFailure` by index, and `copyText` / `download` over the whole log. Also exports `logToText` and `downloadText`. Budgets measured in WebKit on a 20,765-line, 1.4 MB log: first render about 20 ms with 40 rows in the page, filter keystrokes under 50 ms, about 3 ms of render per batch of appended lines.
|
|
7
|
+
- `virtual-list`: new subpath with `useVirtualList({ count, estimateSize, scrollRef | windowScroll, scrollMargin, overscan = 10, measure = true, getItemKey, initialCount = 30 })` returning `{ items, totalSize, measureRef, scrollToIndex, scrollToOffset, virtualizer }`, plus the pure `measuredOrEstimate`, `shouldRealign`, `targetOffset` and `initialVisibleCount`. Rows are measured by default (a 0 reading falls back to the estimate), it can scroll an element or the window, `scrollToIndex` re-aligns for up to 3 frames after measuring so it lands on unrendered rows, and the server render includes the first rows. `@tanstack/react-virtual` (`^3.13.0`) is an **optional** peer dependency used only by this subpath; the root index does not re-export it.
|
|
8
|
+
|
|
3
9
|
## 0.4.0 - 2026-11-06
|
|
4
10
|
|
|
5
11
|
- `terminal-output`: `useFollowTail` detaches on a user scroll up even when output streams every frame. It now detects intent (wheel up, a finger dragging down, a press on the scrollbar gutter, or a scroll event above where the hook last scrolled to) instead of ignoring scroll events in the frame after its own scroll. New pure `isUserScrollUp`.
|
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ that every app ends up hand-copying from the last one.
|
|
|
25
25
|
| `@develemit/web-hooks/keyboard-inset` | On-screen keyboard height, without the phantom gap in standalone iPhone PWAs |
|
|
26
26
|
| `@develemit/web-hooks/push` | Web push subscription, with iOS install detection and silent re-subscribe |
|
|
27
27
|
| `@develemit/web-hooks/terminal-output` | Strip and parse ANSI, buffer streamed lines, search logs and find failures, and follow the tail of a scrolling log |
|
|
28
|
+
| `@develemit/web-hooks/virtual-list` | Virtualize long lists on `@tanstack/react-virtual` with phone-safe defaults (optional peer) |
|
|
28
29
|
|
|
29
30
|
Behavior only. No styling and no components: each app keeps its own look.
|
|
30
31
|
|
|
@@ -457,3 +458,65 @@ const COLORS = ['text-black', 'text-red', 'text-green', 'text-yellow', 'text-blu
|
|
|
457
458
|
</div>
|
|
458
459
|
))}
|
|
459
460
|
```
|
|
461
|
+
|
|
462
|
+
### virtual-list
|
|
463
|
+
|
|
464
|
+
`@develemit/web-hooks/virtual-list` wraps `@tanstack/react-virtual` (v3.13+). Install it yourself (`pnpm add @tanstack/react-virtual`); it is an **optional** peer, needed only by apps that import this subpath. The root `@develemit/web-hooks` import does not re-export it, so root imports never need the peer.
|
|
465
|
+
|
|
466
|
+
`useVirtualList({ count, estimateSize, scrollRef? | windowScroll: true, scrollMargin = 0, overscan = 10, measure = true, getItemKey?, initialCount = 30 })` returns `{ items, totalSize, measureRef, scrollToIndex(i, { align }), scrollToOffset, virtualizer }`; `items` are `{ index, key, start, size }`, and `virtualizer` is the raw escape hatch.
|
|
467
|
+
|
|
468
|
+
- Rows are measured by default; a row that reads 0 before layout keeps its estimate, so the list never collapses. Pass `measure: false` if your own maths relies on flat estimates.
|
|
469
|
+
- `windowScroll: true` scrolls with the page (use `scrollMargin` for the list's distance from the page top; item `start` is relative to the list).
|
|
470
|
+
- `scrollToIndex` to a row that isn't rendered yet scrolls to the estimate, then re-aligns after measuring, for up to 3 frames.
|
|
471
|
+
- The server render includes the first `initialCount` rows.
|
|
472
|
+
|
|
473
|
+
```tsx
|
|
474
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
475
|
+
const { items, totalSize, measureRef } = useVirtualList({ count: lines.length, estimateSize: () => 20, scrollRef: ref });
|
|
476
|
+
|
|
477
|
+
<div ref={ref} className="h-96 overflow-auto">
|
|
478
|
+
<div style={{ height: totalSize, position: 'relative' }}>
|
|
479
|
+
{items.map((it) => (
|
|
480
|
+
<div key={it.key} data-index={it.index} ref={measureRef}
|
|
481
|
+
style={{ position: 'absolute', top: 0, left: 0, width: '100%', transform: `translateY(${it.start}px)` }}>
|
|
482
|
+
{lines[it.index]}
|
|
483
|
+
</div>
|
|
484
|
+
))}
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Each row needs `data-index={it.index}` and `ref={measureRef}`.
|
|
490
|
+
|
|
491
|
+
#### useVirtualLog
|
|
492
|
+
|
|
493
|
+
`useVirtualLog({ lines, scrollRef, query = '', estimateSize = () => 18, overscan = 12, resetKey?, filename = 'log.txt' })` joins `useVirtualList` with the `terminal-output` helpers for a live, searchable log. Pass the lines as strings (strip or keep escape codes; search and failure scans run on what you pass, so give it plain text if you want to search plain text).
|
|
494
|
+
|
|
495
|
+
It returns `rows` (`{ index, lineIndex, key, start, size }`, where `lineIndex` is the row's place in `lines`), `totalSize`, `measureRef`, `isFiltering`, `shownCount`, `atBottom`, `unread`, `jumpToBottom`, `failures`, `failurePosition`, `jumpToFailure(n)`, `nextFailure()`, `previousFailure()`, `text()`, `copyText()` and `download(filename?)`.
|
|
496
|
+
|
|
497
|
+
- Search goes through `useDeferredValue`, so typing stays instant on 20k lines. Rows are the matches; map them back with `lines[row.lineIndex]` for highlighting with `splitByMatch`.
|
|
498
|
+
- It follows the tail and stops when the user scrolls up. While a filter is on, following pauses and `unread` counts matches that arrived since the filter began.
|
|
499
|
+
- `failures` is scanned once per content change, and only on appended rows. `jumpToFailure(n)` scrolls by index (so it works for rows that aren't rendered) and returns `false` if that failure is hidden by the filter.
|
|
500
|
+
- `copyText()` and `download()` use the whole log with escape codes stripped, however few rows are in the page. `logToText(lines, { stripAnsi })` and `downloadText(filename, text)` are exported on their own.
|
|
501
|
+
- `lines` is an array, not a stream: keep your own state (for example `createLineBuffer` + `createAnsiStream`) and pass its rows.
|
|
502
|
+
|
|
503
|
+
```tsx
|
|
504
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
505
|
+
const [query, setQuery] = useState('');
|
|
506
|
+
const log = useVirtualLog({ lines, scrollRef: ref, query });
|
|
507
|
+
|
|
508
|
+
<div ref={ref} className="h-96 overflow-auto">
|
|
509
|
+
<div style={{ height: log.totalSize, position: 'relative' }}>
|
|
510
|
+
{log.rows.map((row) => (
|
|
511
|
+
<div key={row.key} data-index={row.index} ref={log.measureRef}
|
|
512
|
+
style={{ position: 'absolute', top: 0, left: 0, width: '100%', transform: `translateY(${row.start}px)` }}>
|
|
513
|
+
{log.isFiltering
|
|
514
|
+
? splitByMatch(lines[row.lineIndex] ?? '', query).map((s, j) => (s.match ? <mark key={j}>{s.text}</mark> : <span key={j}>{s.text}</span>))
|
|
515
|
+
: lines[row.lineIndex]}
|
|
516
|
+
</div>
|
|
517
|
+
))}
|
|
518
|
+
</div>
|
|
519
|
+
</div>
|
|
520
|
+
{!log.atBottom && <button onClick={log.jumpToBottom}>{log.unread} new</button>}
|
|
521
|
+
<button onClick={log.nextFailure}>Next failure</button>
|
|
522
|
+
```
|
|
@@ -241,139 +241,6 @@ function createAnsiParser(options = {}) {
|
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
// src/terminal-output/line-split.ts
|
|
245
|
-
var INCOMPLETE_ESCAPE = /\x1b(?:\[[0-?]*[ -/]*|\][^\x07\x1b]*\x1b?|[ -/]*)$/;
|
|
246
|
-
function splitIncompleteEscape(text) {
|
|
247
|
-
const match = INCOMPLETE_ESCAPE.exec(text);
|
|
248
|
-
if (!match) return { complete: text, held: "" };
|
|
249
|
-
return { complete: text.slice(0, match.index), held: match[0] };
|
|
250
|
-
}
|
|
251
|
-
function splitLines(text) {
|
|
252
|
-
const pieces = text.split("\n");
|
|
253
|
-
const rest = pieces.pop() ?? "";
|
|
254
|
-
const lines = pieces.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
255
|
-
return { lines, rest };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// src/terminal-output/line-buffer.ts
|
|
259
|
-
function createLineBuffer({ maxLines }) {
|
|
260
|
-
let decoder = new TextDecoder("utf-8");
|
|
261
|
-
let lines = [];
|
|
262
|
-
let pending = "";
|
|
263
|
-
let cursor;
|
|
264
|
-
let dropped = 0;
|
|
265
|
-
function keep(added) {
|
|
266
|
-
lines.push(...added);
|
|
267
|
-
const over = Math.max(0, lines.length - maxLines);
|
|
268
|
-
if (over > 0) lines.splice(0, over);
|
|
269
|
-
dropped += over;
|
|
270
|
-
return over;
|
|
271
|
-
}
|
|
272
|
-
function update(added, over) {
|
|
273
|
-
return { lines: added, partial: splitIncompleteEscape(pending).complete, dropped: over };
|
|
274
|
-
}
|
|
275
|
-
return {
|
|
276
|
-
push(chunk, options) {
|
|
277
|
-
const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
278
|
-
if (options?.cursor !== void 0) cursor = options.cursor;
|
|
279
|
-
const { lines: added, rest } = splitLines(pending + text);
|
|
280
|
-
pending = rest;
|
|
281
|
-
return update(added, keep(added));
|
|
282
|
-
},
|
|
283
|
-
flush() {
|
|
284
|
-
const tail = pending + decoder.decode();
|
|
285
|
-
pending = "";
|
|
286
|
-
const added = tail === "" ? [] : [tail];
|
|
287
|
-
return update(added, keep(added));
|
|
288
|
-
},
|
|
289
|
-
reset() {
|
|
290
|
-
decoder = new TextDecoder("utf-8");
|
|
291
|
-
lines = [];
|
|
292
|
-
pending = "";
|
|
293
|
-
cursor = void 0;
|
|
294
|
-
dropped = 0;
|
|
295
|
-
},
|
|
296
|
-
get lines() {
|
|
297
|
-
return lines;
|
|
298
|
-
},
|
|
299
|
-
get partial() {
|
|
300
|
-
return splitIncompleteEscape(pending).complete;
|
|
301
|
-
},
|
|
302
|
-
get cursor() {
|
|
303
|
-
return cursor;
|
|
304
|
-
},
|
|
305
|
-
get dropped() {
|
|
306
|
-
return dropped;
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// src/terminal-output/ansi-stream.ts
|
|
312
|
-
var DEFAULT_TAIL = 50;
|
|
313
|
-
var CURSOR_UP = /\x1b\[(\d*)A/g;
|
|
314
|
-
function largestCursorUp(text) {
|
|
315
|
-
let largest = 0;
|
|
316
|
-
for (const m of text.matchAll(CURSOR_UP)) largest = Math.max(largest, Number(m[1] || 1));
|
|
317
|
-
return largest;
|
|
318
|
-
}
|
|
319
|
-
function sameLine(a, b) {
|
|
320
|
-
return a !== void 0 && (a === b || JSON.stringify(a) === JSON.stringify(b));
|
|
321
|
-
}
|
|
322
|
-
function createAnsiStream(options = {}) {
|
|
323
|
-
let tail = options.tailLines ?? DEFAULT_TAIL;
|
|
324
|
-
let screen = new Screen();
|
|
325
|
-
let state = { style: {} };
|
|
326
|
-
let frozen = 0;
|
|
327
|
-
let shown = [];
|
|
328
|
-
let lastPartial = "";
|
|
329
|
-
let finished = false;
|
|
330
|
-
function render(partial) {
|
|
331
|
-
tail = Math.max(tail, largestCursorUp(partial));
|
|
332
|
-
screen.lines();
|
|
333
|
-
const draft = screen.clone();
|
|
334
|
-
feedAnsi(draft, { ...state }, partial, options);
|
|
335
|
-
const rows = draft.lines();
|
|
336
|
-
const first = rows.findIndex((row, i) => !sameLine(shown[i], row));
|
|
337
|
-
const from = first === -1 ? rows.length < shown.length ? rows.length : -1 : first;
|
|
338
|
-
shown = rows;
|
|
339
|
-
const update = from === -1 ? {} : { replaced: { from: frozen + from, lines: rows.slice(from) } };
|
|
340
|
-
const freeze = screen.cursorRow - tail;
|
|
341
|
-
if (freeze > 0) {
|
|
342
|
-
const dropped = screen.dropTop(freeze);
|
|
343
|
-
shown = shown.slice(dropped);
|
|
344
|
-
frozen += dropped;
|
|
345
|
-
}
|
|
346
|
-
return update;
|
|
347
|
-
}
|
|
348
|
-
return {
|
|
349
|
-
push(lines, partial = "") {
|
|
350
|
-
if (finished) return {};
|
|
351
|
-
for (const line of lines) {
|
|
352
|
-
tail = Math.max(tail, largestCursorUp(line));
|
|
353
|
-
feedAnsi(screen, state, `${line}
|
|
354
|
-
`, options);
|
|
355
|
-
}
|
|
356
|
-
lastPartial = partial;
|
|
357
|
-
return render(partial);
|
|
358
|
-
},
|
|
359
|
-
finish(partial = lastPartial) {
|
|
360
|
-
if (finished) return {};
|
|
361
|
-
const update = render(partial);
|
|
362
|
-
finished = true;
|
|
363
|
-
return update;
|
|
364
|
-
},
|
|
365
|
-
reset() {
|
|
366
|
-
finished = false;
|
|
367
|
-
lastPartial = "";
|
|
368
|
-
screen = new Screen();
|
|
369
|
-
state = { style: {} };
|
|
370
|
-
frozen = 0;
|
|
371
|
-
shown = [];
|
|
372
|
-
tail = options.tailLines ?? DEFAULT_TAIL;
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
|
|
377
244
|
// src/terminal-output/log-helpers.ts
|
|
378
245
|
var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
379
246
|
function queryMatcher(query) {
|
|
@@ -593,11 +460,11 @@ function useFollowTail({
|
|
|
593
460
|
}
|
|
594
461
|
|
|
595
462
|
export {
|
|
463
|
+
Screen,
|
|
596
464
|
stripAnsi,
|
|
465
|
+
feedAnsi,
|
|
597
466
|
parseAnsi,
|
|
598
467
|
createAnsiParser,
|
|
599
|
-
createLineBuffer,
|
|
600
|
-
createAnsiStream,
|
|
601
468
|
filterLines,
|
|
602
469
|
splitByMatch,
|
|
603
470
|
FAILURE_PATTERNS,
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
Screen,
|
|
4
|
+
feedAnsi
|
|
5
|
+
} from "./chunk-IVLRQ23V.js";
|
|
6
|
+
|
|
7
|
+
// src/terminal-output/line-split.ts
|
|
8
|
+
var INCOMPLETE_ESCAPE = /\x1b(?:\[[0-?]*[ -/]*|\][^\x07\x1b]*\x1b?|[ -/]*)$/;
|
|
9
|
+
function splitIncompleteEscape(text) {
|
|
10
|
+
const match = INCOMPLETE_ESCAPE.exec(text);
|
|
11
|
+
if (!match) return { complete: text, held: "" };
|
|
12
|
+
return { complete: text.slice(0, match.index), held: match[0] };
|
|
13
|
+
}
|
|
14
|
+
function splitLines(text) {
|
|
15
|
+
const pieces = text.split("\n");
|
|
16
|
+
const rest = pieces.pop() ?? "";
|
|
17
|
+
const lines = pieces.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
18
|
+
return { lines, rest };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/terminal-output/line-buffer.ts
|
|
22
|
+
function createLineBuffer({ maxLines }) {
|
|
23
|
+
let decoder = new TextDecoder("utf-8");
|
|
24
|
+
let lines = [];
|
|
25
|
+
let pending = "";
|
|
26
|
+
let cursor;
|
|
27
|
+
let dropped = 0;
|
|
28
|
+
function keep(added) {
|
|
29
|
+
lines.push(...added);
|
|
30
|
+
const over = Math.max(0, lines.length - maxLines);
|
|
31
|
+
if (over > 0) lines.splice(0, over);
|
|
32
|
+
dropped += over;
|
|
33
|
+
return over;
|
|
34
|
+
}
|
|
35
|
+
function update(added, over) {
|
|
36
|
+
return { lines: added, partial: splitIncompleteEscape(pending).complete, dropped: over };
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
push(chunk, options) {
|
|
40
|
+
const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
41
|
+
if (options?.cursor !== void 0) cursor = options.cursor;
|
|
42
|
+
const { lines: added, rest } = splitLines(pending + text);
|
|
43
|
+
pending = rest;
|
|
44
|
+
return update(added, keep(added));
|
|
45
|
+
},
|
|
46
|
+
flush() {
|
|
47
|
+
const tail = pending + decoder.decode();
|
|
48
|
+
pending = "";
|
|
49
|
+
const added = tail === "" ? [] : [tail];
|
|
50
|
+
return update(added, keep(added));
|
|
51
|
+
},
|
|
52
|
+
reset() {
|
|
53
|
+
decoder = new TextDecoder("utf-8");
|
|
54
|
+
lines = [];
|
|
55
|
+
pending = "";
|
|
56
|
+
cursor = void 0;
|
|
57
|
+
dropped = 0;
|
|
58
|
+
},
|
|
59
|
+
get lines() {
|
|
60
|
+
return lines;
|
|
61
|
+
},
|
|
62
|
+
get partial() {
|
|
63
|
+
return splitIncompleteEscape(pending).complete;
|
|
64
|
+
},
|
|
65
|
+
get cursor() {
|
|
66
|
+
return cursor;
|
|
67
|
+
},
|
|
68
|
+
get dropped() {
|
|
69
|
+
return dropped;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/terminal-output/ansi-stream.ts
|
|
75
|
+
var DEFAULT_TAIL = 50;
|
|
76
|
+
var CURSOR_UP = /\x1b\[(\d*)A/g;
|
|
77
|
+
function largestCursorUp(text) {
|
|
78
|
+
let largest = 0;
|
|
79
|
+
for (const m of text.matchAll(CURSOR_UP)) largest = Math.max(largest, Number(m[1] || 1));
|
|
80
|
+
return largest;
|
|
81
|
+
}
|
|
82
|
+
function sameLine(a, b) {
|
|
83
|
+
return a !== void 0 && (a === b || JSON.stringify(a) === JSON.stringify(b));
|
|
84
|
+
}
|
|
85
|
+
function createAnsiStream(options = {}) {
|
|
86
|
+
let tail = options.tailLines ?? DEFAULT_TAIL;
|
|
87
|
+
let screen = new Screen();
|
|
88
|
+
let state = { style: {} };
|
|
89
|
+
let frozen = 0;
|
|
90
|
+
let shown = [];
|
|
91
|
+
let lastPartial = "";
|
|
92
|
+
let finished = false;
|
|
93
|
+
function render(partial) {
|
|
94
|
+
tail = Math.max(tail, largestCursorUp(partial));
|
|
95
|
+
screen.lines();
|
|
96
|
+
const draft = screen.clone();
|
|
97
|
+
feedAnsi(draft, { ...state }, partial, options);
|
|
98
|
+
const rows = draft.lines();
|
|
99
|
+
const first = rows.findIndex((row, i) => !sameLine(shown[i], row));
|
|
100
|
+
const from = first === -1 ? rows.length < shown.length ? rows.length : -1 : first;
|
|
101
|
+
shown = rows;
|
|
102
|
+
const update = from === -1 ? {} : { replaced: { from: frozen + from, lines: rows.slice(from) } };
|
|
103
|
+
const freeze = screen.cursorRow - tail;
|
|
104
|
+
if (freeze > 0) {
|
|
105
|
+
const dropped = screen.dropTop(freeze);
|
|
106
|
+
shown = shown.slice(dropped);
|
|
107
|
+
frozen += dropped;
|
|
108
|
+
}
|
|
109
|
+
return update;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
push(lines, partial = "") {
|
|
113
|
+
if (finished) return {};
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
tail = Math.max(tail, largestCursorUp(line));
|
|
116
|
+
feedAnsi(screen, state, `${line}
|
|
117
|
+
`, options);
|
|
118
|
+
}
|
|
119
|
+
lastPartial = partial;
|
|
120
|
+
return render(partial);
|
|
121
|
+
},
|
|
122
|
+
finish(partial = lastPartial) {
|
|
123
|
+
if (finished) return {};
|
|
124
|
+
const update = render(partial);
|
|
125
|
+
finished = true;
|
|
126
|
+
return update;
|
|
127
|
+
},
|
|
128
|
+
reset() {
|
|
129
|
+
finished = false;
|
|
130
|
+
lastPartial = "";
|
|
131
|
+
screen = new Screen();
|
|
132
|
+
state = { style: {} };
|
|
133
|
+
frozen = 0;
|
|
134
|
+
shown = [];
|
|
135
|
+
tail = options.tailLines ?? DEFAULT_TAIL;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
createLineBuffer,
|
|
142
|
+
createAnsiStream
|
|
143
|
+
};
|
package/dist/index.d.cts
CHANGED
|
@@ -17,5 +17,6 @@ export { ServiceWorkerState, UpdateStateInput, UseServiceWorkerOptions, deriveUp
|
|
|
17
17
|
export { ShareInput, ShareResult, ShareStatus, canShareFiles, isShareCancel, shareOrCopy, shareTextFor, useShareFeedback } from './share.cjs';
|
|
18
18
|
export { InsetReading, UseKeyboardInsetOptions, computeKeyboardInset, isEditableElement, useKeyboardInset } from './keyboard-inset.cjs';
|
|
19
19
|
export { PushEnv, PushPermission, PushSubscriptionState, PushSupport, ResubscribeInput, UsePushSubscriptionOptions, getPushSupport, shouldResubscribe, urlBase64ToUint8Array, usePushSubscription } from './push.cjs';
|
|
20
|
-
export { AnsiColor, AnsiLine, AnsiSegment, AnsiStream, AnsiStreamOptions, AnsiStreamUpdate, AnsiStyle, DEFAULT_TAIL_THRESHOLD,
|
|
20
|
+
export { AnsiColor, AnsiLine, AnsiSegment, AnsiStream, AnsiStreamOptions, AnsiStreamUpdate, AnsiStyle, DEFAULT_TAIL_THRESHOLD, LineBuffer, LineBufferOptions, LineBufferUpdate, ParseAnsiOptions, ScrollEventInfo, ScrollEventKind, ScrollMetrics, StreamCursor, UseFollowTailOptions, UseFollowTailResult, createAnsiParser, createAnsiStream, createLineBuffer, distanceFromEnd, isNearEnd, isUserScrollUp, parseAnsi, shouldStick, stripAnsi, unreadDelta, useFollowTail } from './terminal-output.cjs';
|
|
21
|
+
export { F as FAILURE_PATTERNS, a as Failure, b as FailurePattern, c as FailureTool, N as NOISE_PATTERNS, T as TextSegment, f as filterLines, d as findFailures, e as firstFailureIndex, i as isNoise, s as splitByMatch, t as tailMeaningful } from './log-helpers-rkbgYqZW.cjs';
|
|
21
22
|
import 'react';
|
package/dist/index.d.ts
CHANGED
|
@@ -17,5 +17,6 @@ export { ServiceWorkerState, UpdateStateInput, UseServiceWorkerOptions, deriveUp
|
|
|
17
17
|
export { ShareInput, ShareResult, ShareStatus, canShareFiles, isShareCancel, shareOrCopy, shareTextFor, useShareFeedback } from './share.js';
|
|
18
18
|
export { InsetReading, UseKeyboardInsetOptions, computeKeyboardInset, isEditableElement, useKeyboardInset } from './keyboard-inset.js';
|
|
19
19
|
export { PushEnv, PushPermission, PushSubscriptionState, PushSupport, ResubscribeInput, UsePushSubscriptionOptions, getPushSupport, shouldResubscribe, urlBase64ToUint8Array, usePushSubscription } from './push.js';
|
|
20
|
-
export { AnsiColor, AnsiLine, AnsiSegment, AnsiStream, AnsiStreamOptions, AnsiStreamUpdate, AnsiStyle, DEFAULT_TAIL_THRESHOLD,
|
|
20
|
+
export { AnsiColor, AnsiLine, AnsiSegment, AnsiStream, AnsiStreamOptions, AnsiStreamUpdate, AnsiStyle, DEFAULT_TAIL_THRESHOLD, LineBuffer, LineBufferOptions, LineBufferUpdate, ParseAnsiOptions, ScrollEventInfo, ScrollEventKind, ScrollMetrics, StreamCursor, UseFollowTailOptions, UseFollowTailResult, createAnsiParser, createAnsiStream, createLineBuffer, distanceFromEnd, isNearEnd, isUserScrollUp, parseAnsi, shouldStick, stripAnsi, unreadDelta, useFollowTail } from './terminal-output.js';
|
|
21
|
+
export { F as FAILURE_PATTERNS, a as Failure, b as FailurePattern, c as FailureTool, N as NOISE_PATTERNS, T as TextSegment, f as filterLines, d as findFailures, e as firstFailureIndex, i as isNoise, s as splitByMatch, t as tailMeaningful } from './log-helpers-rkbgYqZW.js';
|
|
21
22
|
import 'react';
|
package/dist/index.js
CHANGED
|
@@ -22,13 +22,15 @@ import {
|
|
|
22
22
|
urlBase64ToUint8Array,
|
|
23
23
|
usePushSubscription
|
|
24
24
|
} from "./chunk-R2PFNE3U.js";
|
|
25
|
+
import {
|
|
26
|
+
createAnsiStream,
|
|
27
|
+
createLineBuffer
|
|
28
|
+
} from "./chunk-JTLQQSLO.js";
|
|
25
29
|
import {
|
|
26
30
|
DEFAULT_TAIL_THRESHOLD,
|
|
27
31
|
FAILURE_PATTERNS,
|
|
28
32
|
NOISE_PATTERNS,
|
|
29
33
|
createAnsiParser,
|
|
30
|
-
createAnsiStream,
|
|
31
|
-
createLineBuffer,
|
|
32
34
|
distanceFromEnd,
|
|
33
35
|
filterLines,
|
|
34
36
|
findFailures,
|
|
@@ -43,7 +45,7 @@ import {
|
|
|
43
45
|
tailMeaningful,
|
|
44
46
|
unreadDelta,
|
|
45
47
|
useFollowTail
|
|
46
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-IVLRQ23V.js";
|
|
47
49
|
import {
|
|
48
50
|
nextDelay,
|
|
49
51
|
useVisibilityPolling
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface TextSegment {
|
|
2
|
+
text: string;
|
|
3
|
+
match: boolean;
|
|
4
|
+
}
|
|
5
|
+
type FailureTool = 'vitest' | 'nx' | 'tsc' | 'pnpm' | 'npm' | 'docker' | 'generic';
|
|
6
|
+
interface FailurePattern {
|
|
7
|
+
tool: FailureTool;
|
|
8
|
+
kind: string;
|
|
9
|
+
pattern: RegExp;
|
|
10
|
+
}
|
|
11
|
+
interface Failure {
|
|
12
|
+
index: number;
|
|
13
|
+
tool: FailureTool;
|
|
14
|
+
kind: string;
|
|
15
|
+
}
|
|
16
|
+
declare function filterLines(lines: readonly string[], query: string): number[];
|
|
17
|
+
declare function splitByMatch(line: string, query: string): TextSegment[];
|
|
18
|
+
declare const FAILURE_PATTERNS: readonly FailurePattern[];
|
|
19
|
+
declare function findFailures(lines: readonly string[], patterns?: readonly FailurePattern[]): Failure[];
|
|
20
|
+
declare function firstFailureIndex(lines: readonly string[], patterns?: readonly FailurePattern[]): number;
|
|
21
|
+
declare const NOISE_PATTERNS: readonly RegExp[];
|
|
22
|
+
declare function isNoise(line: string, patterns?: readonly RegExp[]): boolean;
|
|
23
|
+
declare function tailMeaningful(lines: readonly string[], n: number, patterns?: readonly RegExp[]): string[];
|
|
24
|
+
|
|
25
|
+
export { FAILURE_PATTERNS as F, NOISE_PATTERNS as N, type TextSegment as T, type Failure as a, type FailurePattern as b, type FailureTool as c, findFailures as d, firstFailureIndex as e, filterLines as f, isNoise as i, splitByMatch as s, tailMeaningful as t };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface TextSegment {
|
|
2
|
+
text: string;
|
|
3
|
+
match: boolean;
|
|
4
|
+
}
|
|
5
|
+
type FailureTool = 'vitest' | 'nx' | 'tsc' | 'pnpm' | 'npm' | 'docker' | 'generic';
|
|
6
|
+
interface FailurePattern {
|
|
7
|
+
tool: FailureTool;
|
|
8
|
+
kind: string;
|
|
9
|
+
pattern: RegExp;
|
|
10
|
+
}
|
|
11
|
+
interface Failure {
|
|
12
|
+
index: number;
|
|
13
|
+
tool: FailureTool;
|
|
14
|
+
kind: string;
|
|
15
|
+
}
|
|
16
|
+
declare function filterLines(lines: readonly string[], query: string): number[];
|
|
17
|
+
declare function splitByMatch(line: string, query: string): TextSegment[];
|
|
18
|
+
declare const FAILURE_PATTERNS: readonly FailurePattern[];
|
|
19
|
+
declare function findFailures(lines: readonly string[], patterns?: readonly FailurePattern[]): Failure[];
|
|
20
|
+
declare function firstFailureIndex(lines: readonly string[], patterns?: readonly FailurePattern[]): number;
|
|
21
|
+
declare const NOISE_PATTERNS: readonly RegExp[];
|
|
22
|
+
declare function isNoise(line: string, patterns?: readonly RegExp[]): boolean;
|
|
23
|
+
declare function tailMeaningful(lines: readonly string[], n: number, patterns?: readonly RegExp[]): string[];
|
|
24
|
+
|
|
25
|
+
export { FAILURE_PATTERNS as F, NOISE_PATTERNS as N, type TextSegment as T, type Failure as a, type FailurePattern as b, type FailureTool as c, findFailures as d, firstFailureIndex as e, filterLines as f, isNoise as i, splitByMatch as s, tailMeaningful as t };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { F as FAILURE_PATTERNS, a as Failure, b as FailurePattern, c as FailureTool, N as NOISE_PATTERNS, T as TextSegment, f as filterLines, d as findFailures, e as firstFailureIndex, i as isNoise, s as splitByMatch, t as tailMeaningful } from './log-helpers-rkbgYqZW.cjs';
|
|
1
2
|
import { RefObject } from 'react';
|
|
2
3
|
|
|
3
4
|
type AnsiColor = {
|
|
@@ -110,30 +111,6 @@ interface AnsiStream {
|
|
|
110
111
|
*/
|
|
111
112
|
declare function createAnsiStream(options?: AnsiStreamOptions): AnsiStream;
|
|
112
113
|
|
|
113
|
-
interface TextSegment {
|
|
114
|
-
text: string;
|
|
115
|
-
match: boolean;
|
|
116
|
-
}
|
|
117
|
-
type FailureTool = 'vitest' | 'nx' | 'tsc' | 'pnpm' | 'npm' | 'docker' | 'generic';
|
|
118
|
-
interface FailurePattern {
|
|
119
|
-
tool: FailureTool;
|
|
120
|
-
kind: string;
|
|
121
|
-
pattern: RegExp;
|
|
122
|
-
}
|
|
123
|
-
interface Failure {
|
|
124
|
-
index: number;
|
|
125
|
-
tool: FailureTool;
|
|
126
|
-
kind: string;
|
|
127
|
-
}
|
|
128
|
-
declare function filterLines(lines: readonly string[], query: string): number[];
|
|
129
|
-
declare function splitByMatch(line: string, query: string): TextSegment[];
|
|
130
|
-
declare const FAILURE_PATTERNS: readonly FailurePattern[];
|
|
131
|
-
declare function findFailures(lines: readonly string[], patterns?: readonly FailurePattern[]): Failure[];
|
|
132
|
-
declare function firstFailureIndex(lines: readonly string[], patterns?: readonly FailurePattern[]): number;
|
|
133
|
-
declare const NOISE_PATTERNS: readonly RegExp[];
|
|
134
|
-
declare function isNoise(line: string, patterns?: readonly RegExp[]): boolean;
|
|
135
|
-
declare function tailMeaningful(lines: readonly string[], n: number, patterns?: readonly RegExp[]): string[];
|
|
136
|
-
|
|
137
114
|
interface ScrollMetrics {
|
|
138
115
|
scrollTop: number;
|
|
139
116
|
scrollHeight: number;
|
|
@@ -183,4 +160,4 @@ interface UseFollowTailResult {
|
|
|
183
160
|
}
|
|
184
161
|
declare function useFollowTail({ scrollRef, itemCount, scrollToEnd, resetKey, threshold, }: UseFollowTailOptions): UseFollowTailResult;
|
|
185
162
|
|
|
186
|
-
export { type AnsiColor, type AnsiLine, type AnsiSegment, type AnsiStream, type AnsiStreamOptions, type AnsiStreamUpdate, type AnsiStyle, DEFAULT_TAIL_THRESHOLD,
|
|
163
|
+
export { type AnsiColor, type AnsiLine, type AnsiSegment, type AnsiStream, type AnsiStreamOptions, type AnsiStreamUpdate, type AnsiStyle, DEFAULT_TAIL_THRESHOLD, type LineBuffer, type LineBufferOptions, type LineBufferUpdate, type ParseAnsiOptions, type ScrollEventInfo, type ScrollEventKind, type ScrollMetrics, type StreamCursor, type UseFollowTailOptions, type UseFollowTailResult, createAnsiParser, createAnsiStream, createLineBuffer, distanceFromEnd, isNearEnd, isUserScrollUp, parseAnsi, shouldStick, stripAnsi, unreadDelta, useFollowTail };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { F as FAILURE_PATTERNS, a as Failure, b as FailurePattern, c as FailureTool, N as NOISE_PATTERNS, T as TextSegment, f as filterLines, d as findFailures, e as firstFailureIndex, i as isNoise, s as splitByMatch, t as tailMeaningful } from './log-helpers-rkbgYqZW.js';
|
|
1
2
|
import { RefObject } from 'react';
|
|
2
3
|
|
|
3
4
|
type AnsiColor = {
|
|
@@ -110,30 +111,6 @@ interface AnsiStream {
|
|
|
110
111
|
*/
|
|
111
112
|
declare function createAnsiStream(options?: AnsiStreamOptions): AnsiStream;
|
|
112
113
|
|
|
113
|
-
interface TextSegment {
|
|
114
|
-
text: string;
|
|
115
|
-
match: boolean;
|
|
116
|
-
}
|
|
117
|
-
type FailureTool = 'vitest' | 'nx' | 'tsc' | 'pnpm' | 'npm' | 'docker' | 'generic';
|
|
118
|
-
interface FailurePattern {
|
|
119
|
-
tool: FailureTool;
|
|
120
|
-
kind: string;
|
|
121
|
-
pattern: RegExp;
|
|
122
|
-
}
|
|
123
|
-
interface Failure {
|
|
124
|
-
index: number;
|
|
125
|
-
tool: FailureTool;
|
|
126
|
-
kind: string;
|
|
127
|
-
}
|
|
128
|
-
declare function filterLines(lines: readonly string[], query: string): number[];
|
|
129
|
-
declare function splitByMatch(line: string, query: string): TextSegment[];
|
|
130
|
-
declare const FAILURE_PATTERNS: readonly FailurePattern[];
|
|
131
|
-
declare function findFailures(lines: readonly string[], patterns?: readonly FailurePattern[]): Failure[];
|
|
132
|
-
declare function firstFailureIndex(lines: readonly string[], patterns?: readonly FailurePattern[]): number;
|
|
133
|
-
declare const NOISE_PATTERNS: readonly RegExp[];
|
|
134
|
-
declare function isNoise(line: string, patterns?: readonly RegExp[]): boolean;
|
|
135
|
-
declare function tailMeaningful(lines: readonly string[], n: number, patterns?: readonly RegExp[]): string[];
|
|
136
|
-
|
|
137
114
|
interface ScrollMetrics {
|
|
138
115
|
scrollTop: number;
|
|
139
116
|
scrollHeight: number;
|
|
@@ -183,4 +160,4 @@ interface UseFollowTailResult {
|
|
|
183
160
|
}
|
|
184
161
|
declare function useFollowTail({ scrollRef, itemCount, scrollToEnd, resetKey, threshold, }: UseFollowTailOptions): UseFollowTailResult;
|
|
185
162
|
|
|
186
|
-
export { type AnsiColor, type AnsiLine, type AnsiSegment, type AnsiStream, type AnsiStreamOptions, type AnsiStreamUpdate, type AnsiStyle, DEFAULT_TAIL_THRESHOLD,
|
|
163
|
+
export { type AnsiColor, type AnsiLine, type AnsiSegment, type AnsiStream, type AnsiStreamOptions, type AnsiStreamUpdate, type AnsiStyle, DEFAULT_TAIL_THRESHOLD, type LineBuffer, type LineBufferOptions, type LineBufferUpdate, type ParseAnsiOptions, type ScrollEventInfo, type ScrollEventKind, type ScrollMetrics, type StreamCursor, type UseFollowTailOptions, type UseFollowTailResult, createAnsiParser, createAnsiStream, createLineBuffer, distanceFromEnd, isNearEnd, isUserScrollUp, parseAnsi, shouldStick, stripAnsi, unreadDelta, useFollowTail };
|
package/dist/terminal-output.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
createAnsiStream,
|
|
4
|
+
createLineBuffer
|
|
5
|
+
} from "./chunk-JTLQQSLO.js";
|
|
2
6
|
import {
|
|
3
7
|
DEFAULT_TAIL_THRESHOLD,
|
|
4
8
|
FAILURE_PATTERNS,
|
|
5
9
|
NOISE_PATTERNS,
|
|
6
10
|
createAnsiParser,
|
|
7
|
-
createAnsiStream,
|
|
8
|
-
createLineBuffer,
|
|
9
11
|
distanceFromEnd,
|
|
10
12
|
filterLines,
|
|
11
13
|
findFailures,
|
|
@@ -20,7 +22,7 @@ import {
|
|
|
20
22
|
tailMeaningful,
|
|
21
23
|
unreadDelta,
|
|
22
24
|
useFollowTail
|
|
23
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-IVLRQ23V.js";
|
|
24
26
|
export {
|
|
25
27
|
DEFAULT_TAIL_THRESHOLD,
|
|
26
28
|
FAILURE_PATTERNS,
|