-
Notifications
You must be signed in to change notification settings - Fork 326
add captureSpans and getSpanLines for styled terminal output capture #574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
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.