Skip to content

Native /view: read-only viewer for files and completed output (#397) - #413

Merged
raiseCatError merged 3 commits into
devfrom
feature/v021-view
Oct 18, 2026
Merged

raiseCatError merged 3 commits into
devfrom
feature/v021-view

Conversation

@raiseCatError

Copy link
Copy Markdown
Owner

Stacked on #412 (reliability), which is stacked on #411 (design). Merge in order; GitHub retargets each PR to dev when its base merges.

What

/view, a read-only native viewer in a full-height panel (the existing panel slot and key chain; it owns keys only while open):

  • /view <path>: a file relative to the shell's directory (~ for home). /view N: completed output N, numbered as /copy numbers it. /view: the latest output. Block Actions gain View output. Open in pager stays.
  • Markdown files render through the existing src/markdown renderer; m shows the source. Output is never treated as Markdown.
  • Source files get the existing syntax roles and line numbers. .diff/.patch files and git diff/git show/diff -u output read as diffs.
  • Keys:
    • / find (smart case), n/N next/previous (wrapping with a note), : go to line.
    • g/G, PageUp/PageDown and the mouse wheel scroll.
    • w wrap on/off (Left/Right scroll sideways with ‹/› marks), l line numbers.
    • c copies the whole text (exactly what /copy gives for output). Esc clears the search, then closes.

Safety and limits

  • Files: lstat, then open(O_RDONLY|O_NONBLOCK) and fstat. Only regular files are read: directories, devices and FIFOs are refused, and a FIFO is refused at once. At most 8 MiB, cut at a UTF-8 boundary, and 200,000 lines, with a note naming the rest. Binary (NUL) and UTF-16 files are refused. Invalid UTF-8 is shown with � and a note. Symlinks are followed, as less does, and the header shows the target. Nothing is written.
  • Drawing: every control or format character (escape sequences, bidi overrides, zero-width marks) is drawn as a visible mark. File names are made inert in headers and in transcript notices; a name containing ESC[2J would otherwise have cleared the transcript. Markdown links are links only through the renderer's existing allowed targets.
  • Bounded work: only visible lines are laid out. A wrapped line takes at most 200 rows, then a summary. One 1 MB line takes about 100 ms; before bounding it took 17 s. A 200,000-line document opens and jumps to its end in about 130 ms. Syntax roles cover the first 20,000 lines; Markdown renders up to 1 MiB.

Tests

  • viewer.test.ts: file refusals (directory, missing, binary, UTF-16, FIFO, permission), UTF-8 cuts, line caps, symlinks, hostile content and hostile file names at 32/80/120 columns, Markdown Rendered/Source, diff detection, search, navigation, bounds, tiny panels.
  • viewerApp.test.ts: /view, /view N and the block action; keys owned while open; the transcript unchanged; missing-file notice.
  • viewerLive.test.ts: a real PTY through the binary covering Markdown, latest output, find, go-to-line, copy all via a stand-in clipboard, and Esc back to the composer.
  • Ask catalog and guide suites updated for /view.

Not verified

Physical terminal checks: docs/testing/v021-physical-qa.md section 2, not run.

Refs #397, #402, #410

/view <path> reads a regular file relative to the shell's directory, /view N
completed output N (numbered as /copy), /view alone the latest output; block
Actions gain View output. Markdown files render (m shows the source), source
files get syntax roles and line numbers, diffs read as diffs. Find, go to
line, wrap or horizontal scroll, copy all.

Files are opened non-blocking and only regular files are read: at most 8 MiB
and 200,000 lines, binary and UTF-16 refused, invalid UTF-8 shown with a note.
Drawing is bounded to what is visible, and every control or format character
is drawn as a visible mark. Viewing never writes to the file or transcript.
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