Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| A very long step log stays fast. Each chunk of a job's log is read once, into its step's log, as it arrives (`appendLog` in lib/log-lines.ts), where before every chunk joined the step's text again and read all of it from the start: a group a chunk leaves open holds the next chunk's lines, a line still being written shows until it ends, and a search reads the lines already read, once a change. A step of more than 2,000 rows draws only those on screen and 800 pixels either side (lib/log-window.ts), each row one line high until it is drawn and measured, so the space kept above and below is right however lines wrap; groups fold in the page's state rather than in a `details`, and search, following the newest line and Jump to latest work as on a short log. Each line's number links to it as GitHub's do (`#step:3:12`), opening its step and its group, scrolling to it and marking it. With a synthetic 50,000-line log in 500 chunks of 100 lines, reading it took 24.6 s in all and 105 ms for the last chunk before, and 0.5 s and 1.3 ms now; in Chromium the page drew the whole log in 2.2 s and each new chunk in 213 ms (157,000 elements), and now in 51 ms and 17 ms (385 elements). The Actions guide says so. | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import { ROW_HEIGHT, rowAt, rowStarts, windowOf } from "./log-window.ts"; | |
| 5 | ||
| 6 | test("rows start where the rows above them end, measured or not", () => { | |
| 7 | const starts = rowStarts(4, (index) => (index === 1 ? 60 : undefined)); | |
| 8 | assert.deepEqual([...starts], [0, ROW_HEIGHT, ROW_HEIGHT + 60, 2 * ROW_HEIGHT + 60, 3 * ROW_HEIGHT + 60]); | |
| 9 | assert.equal(rowAt(starts, 0), 0); | |
| 10 | assert.equal(rowAt(starts, ROW_HEIGHT), 1); | |
| 11 | assert.equal(rowAt(starts, ROW_HEIGHT + 59), 1); | |
| 12 | assert.equal(rowAt(starts, ROW_HEIGHT + 60), 2); | |
| 13 | assert.equal(rowAt(starts, 10_000), 3, "past the end is the last row"); | |
| 14 | }); | |
| 15 | ||
| 16 | test("only the rows on screen and a little around them are drawn", () => { | |
| 17 | const starts = rowStarts(50_000, () => undefined); | |
| 18 | // The screen shows rows 1000 to 1040. | |
| 19 | const [from, to] = windowOf(starts, 1000 * ROW_HEIGHT, 1040 * ROW_HEIGHT, 200); | |
| 20 | assert.deepEqual([from, to], [990, 1051]); | |
| 21 | // At the very end, following the newest line. | |
| 22 | const end = starts[50_000]!; | |
| 23 | assert.deepEqual(windowOf(starts, end - 800, end, 200), [49_950, 50_000]); | |
| 24 | // The log above or below the screen draws a few rows at the near end. | |
| 25 | assert.deepEqual(windowOf(starts, -5000, -4000, 200), [0, 20]); | |
| 26 | assert.deepEqual(windowOf(starts, end + 4000, end + 5000, 200), [49_980, 50_000]); | |
| 27 | assert.deepEqual(windowOf(rowStarts(0, () => undefined), 0, 100), [0, 0]); | |
| 28 | }); |