Skip to content

[P0-06] Replace ANSI string buffers with a terminal cell, style, and surface model #11

Description

@phranck

Problem

Rendering mixes visible content and ANSI control sequences in Strings, so width, clipping, compositing, and performance are inconsistent:

  • Character.terminalWidth misclassifies a base character plus combining mark as width 2.
  • ANSI handling covers only a subset of CSI and does not safely model OSC, DCS, or other zero-width control sequences.
  • Text and text-field paths mix String.count, character counts, and terminal-cell width.
  • FrameBuffer.appendVertically repeatedly copies growing arrays in Sources/TUIkitCore/Rendering/FrameBuffer.swift:126-140.
  • Compositing reparses and splits styled Strings in FrameBuffer.swift:202-240,282-312.
  • ZStack alignment and overlay replacement semantics do not match the documented result.

Proposed solution

  • Introduce an internal cell/style/surface representation with grapheme ownership, display width, wide-cell continuation, foreground/background/style state, and explicit transparency.
  • Quantize and clip only at cell boundaries; never split a grapheme cluster or wide-character continuation.
  • Keep ANSI parsing/encoding at trusted terminal boundaries and reject or neutralize unsupported control sequences.
  • Implement linear builders, clipping, stacking, overlays, and diff preparation over surfaces.
  • Classify FrameBuffer through [P0-02] Establish an executable SwiftUI compatibility manifest and API gate #7 before choosing compatibility work. If it remains public, preserve FrameBuffer.lines through a reviewed adapter or migration; if it is implementation-only, move it to package/SPI in [P2-30] Reduce the public implementation surface and namespace TUI-only APIs #35 and avoid a throwaway public adapter.

Acceptance criteria

  • Combining marks, ZWJ emoji, East Asian wide characters, ANSI zero-width state, clipping, wrapping, overlays, and style resets have cross-platform golden tests.
  • Unsupported OSC/DCS/control input cannot inject terminal commands through rendered content.
  • ZStack honors alignment and transparent overlays preserve unaffected cells.
  • Surface construction and compositing are linear in produced cell count for the covered operations.
  • macOS and Linux produce identical cell layouts under Swift 6.0.
  • Build, tests, lint, and DocC finish with zero warnings and errors.

Dependencies

Depends on the public-surface decision in #7 and characterization/golden-fixture support from #6. The full surface migration may proceed in parallel with identity and keyed-collection work; only a minimal render-result boundary must be coordinated before integration.

Parallelization

Can run in parallel with structural identity after runtime boundaries are fixed. Reserve FrameBuffer, terminal-width, compositing, and diff-adapter files for this issue.

Commit structure

  • Split this issue into small, thematic, independently revertible commits wherever the work can remain coherent.
  • Every commit must build and keep its applicable tests/gates green. Do not commit an intentionally failing regression test; use local/known-issue characterization or land the test with the smallest fix.
  • Keep characterization/fixtures, mechanical renames or moves, semantic changes, and documentation/migration updates separate when each step remains green.
  • The issue boundary is not a commit boundary; multiple commits are expected for independently reversible changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: criticalBlocks runtime correctness or the release-quality gate

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions