add captureSpans and getSpanLines for styled terminal output capture#574
add captureSpans and getSpanLines for styled terminal output capture#574kommander merged 7 commits intoanomalyco:mainfrom
Conversation
@opentui/core
@opentui/react
@opentui/solid
@opentui/core-darwin-arm64
@opentui/core-darwin-x64
@opentui/core-linux-arm64
@opentui/core-linux-x64
@opentui/core-win32-arm64
@opentui/core-win32-x64
commit: |
There was a problem hiding this comment.
Pull request overview
This PR exposes the contents of the terminal render buffer as structured span data compatible with the VTerm-based terminal rendering work from #440, and wires that into the test renderer for easier inspection in tests.
Changes:
- Add
VTermStyleFlags,VTermSpan,VTermLine, andVTermDatatypes to describe styled terminal span output. - Implement
OptimizedBuffer.getSpanLines()to group contiguous cells with identical styling into spans and convert RGBA + attribute data into the VTerm span format. - Extend the test renderer with
captureSpans()and addcapture-spans.test.tsto verify text, colors, attributes, cursor position, and span grouping behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/core/src/types.ts | Defines VTerm-related flag and data interfaces used to describe captured terminal span output. |
| packages/core/src/buffer.ts | Adds attribute-to-VTerm flag mapping and the getSpanLines() method that converts the optimized buffer into VTerm-style span lines. |
| packages/core/src/testing/test-renderer.ts | Exposes a new captureSpans() helper on the test renderer that returns VTermData from the current render buffer. |
| packages/core/src/testing/capture-spans.test.ts | Adds tests that exercise captureSpans() end-to-end, validating dimensions, colors, attributes, cursor position, and span grouping/splitting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/core/src/buffer.ts
Outdated
| const { char, fg, bg, attributes } = this.buffers | ||
| const lines: VTermLine[] = [] | ||
|
|
||
| const rgbaToHex = (r: number, g: number, b: number, a: number): string | null => { |
There was a problem hiding this comment.
There is a rgbToHex method already in RGBA.ts and I wouldn't redefine such a method inline.
There was a problem hiding this comment.
Fixed, using RGBA class and rgbToHex from lib now.
|
I am a little confused as to what this is being used for? The FrameBuffer has a format that allows each cell to be rendered as such with all the info needed to generate ANSI or an image. |
|
The raw buffer data is available via const { char, fg, bg, attributes } = buffer.buffers
for (let i = 0; i < buffer.width * buffer.height; i++) {
const codepoint = char[i]
const fgR = fg[i * 4], fgG = fg[i * 4 + 1], fgB = fg[i * 4 + 2], fgA = fg[i * 4 + 3]
// etc...
}This layout could change in the future. |
|
Another use case is tests - quickly finding styled text without manual buffer iteration: import { VTermStyleFlags } from "@opentui/core"
const { captureSpans, renderOnce } = await createTestRenderer({ width: 80, height: 24 })
// render components...
await renderOnce()
const { lines } = captureSpans()
// Find all bold text in the frame
const boldSpans = lines.flatMap(line =>
line.spans.filter(span => span.flags & VTermStyleFlags.BOLD)
)
expect(boldSpans.map(s => s.text.trim())).toContain("Important") |
|
I see, that makes sense. Any way we could re-use the styled-text types for this, like for the flags, instead of introducing additional types? It seems like the spans are close to what styled text/styled chunks describe kind of. |
|
Refactored to use existing types: removed |
| return `rgba(${this.r.toFixed(2)}, ${this.g.toFixed(2)}, ${this.b.toFixed(2)}, ${this.a.toFixed(2)})` | ||
| } | ||
|
|
||
| equals(other?: RGBA): boolean { |
There was a problem hiding this comment.
Probably needs some epsilon for comparing floats moving forward, but fine for now.
Uses the same VTerm data structures from #440 to expose terminal buffer content as structured span data.
Adds
getSpanLines()toOptimizedBufferandcaptureSpans()to test renderer.Use case: render opentui output to web pages or images. You can render a TUI in a test renderer, extract the spans, and serialize them for web rendering or image generation.