Skip to content

feat(runs): add inline job and step log viewer - #65

Open
Exless wants to merge 1 commit into
kitlangton:mainfrom
Exless:feat/runs-inline-logs
Open

Exless wants to merge 1 commit into
kitlangton:mainfrom
Exless:feat/runs-inline-logs

Conversation

@Exless

@Exless Exless commented Sep 14, 2026

Copy link
Copy Markdown

Why

The runs view (a) shows a PR's jobs and steps, but enter on a step hands off to the browser to read the log. I use ghui against a GitHub Enterprise instance where the web UI is sometimes unavailable while gh still works, so a log I can read without leaving the terminal is the difference between investigating a red check and waiting. The original runs plan also lists "on-demand full step-log fetch + scroll" as its first follow-up.

What Changes

An opt-in inline log viewer, added as a third sub-mode of the existing runs view. Nothing changes unless runLogView is set.

Setting and key Behavior
Default (no runLogView), enter on a job or step Unchanged: opens the job on GitHub.
runLogView: "inline", enter on a step Shows that step's log inside ghui. j/k, ctrl-d/ctrl-u, ctrl-f/ctrl-b, gg/G, and the mouse wheel scroll it; o opens the job on GitHub; esc returns to the steps.
runLogView: "inline", enter on a job header Shows the whole job log.
Any mode, ctrl-f/ctrl-b Full-page moves in the runs and steps lists, alongside the existing ctrl-d/ctrl-u half-page moves.

Logs come from a new getWorkflowJobLog (gh run view --job <id> --log), parsed by a pure parseJobLog helper. Current gh (2.98.0) labels every log line UNKNOWN STEP, so a step is selected by its start/end time window from --json jobs rather than by name; the two line up at second resolution, so a line logged in a boundary second can appear under both adjacent steps. The parser also strips the leading BOM, timestamps, and gh's caret-escaped ANSI codes (^[[36;1m). A running job shows gh's own "still in progress" message, since GitHub only exposes job logs after completion.

The preference follows showScrollbars / diffWhitespaceMode: read once at startup by loadStoredRunLogView, with the browser as the fallback for missing or unknown values. Mock mode generates job logs from the existing run fixtures.

One small unrelated fix: the run-detail atom family now returns an idle result for the empty sentinel key instead of spawning a doomed gh run view 0 --repo "" every time the runs view opens. The new log atom needed the same guard.

Scope

Runs view only. No changes to the runs list, run detail rows, failure navigation, or any other surface. README, the runs plan status, and a minor changeset are included.

Verification

bun install --frozen-lockfile
bun run format:check
bun run typecheck
bun run lint
bun run test
bun run test:keymap
bun run changeset:status
git diff --check

All pass: 583 tests in the full suite and 131 keymap tests. New unit tests cover parseJobLog (step window selection, BOM, timestamp and ANSI stripping) and loadStoredRunLogView (default, inline, unknown value).

Verified against a live job log from this repository's CI with gh 2.98.0: the step column is UNKNOWN STEP on every line, the time-window filter isolates a step correctly, and no ANSI residue remains. Also confirmed that gh run view --log and the jobs logs REST endpoint both refuse in-progress jobs, which is why the viewer shows gh's message rather than a partial log.

Driven end to end in mock mode inside tmux at 120x16 and 140x40: runs list, run detail, step log, whole-job log, ctrl-f/ctrl-b paging, wheel scrolling via SGR mouse sequences, and esc back with selection preserved.

Add an opt-in log viewer to the PR runs view. With `runLogView: "inline"`
in config.json, `enter` on a job or step renders its log inside ghui
instead of opening the browser: scroll with j/k, ctrl-d/u, ctrl-f/b,
gg/G, or the mouse wheel; `o` opens the job on GitHub; `esc` returns to
the steps. The default is unchanged and still opens the browser.

Logs come from `gh run view --job <id> --log`. Current gh (2.98) labels
every line "UNKNOWN STEP", so a step is selected by its start/end time
window from `--json jobs` instead of by name. Timestamps, the leading
BOM, and gh's caret-escaped ANSI codes are stripped before rendering.

Also bind ctrl-f/ctrl-b to full-page moves in the runs view, and
short-circuit the idle sentinel key for the run-detail atom so opening
the runs view no longer spawns a failing `gh` process.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant