Skip to content

refactor(cli): unify terminal presentation with semantic spans#56

Open
francoischalifour wants to merge 5 commits into
mainfrom
fc/semantic-presentation-spans
Open

refactor(cli): unify terminal presentation with semantic spans#56
francoischalifour wants to merge 5 commits into
mainfrom
fc/semantic-presentation-spans

Conversation

@francoischalifour

Copy link
Copy Markdown
Member

Context

CLI presentation had grown around terminal-specific formatting helpers. Views and feature code could produce already-styled strings, raw ANSI sequences, links, and table-specific styling instructions before output reached the terminal renderer.

That created several related problems:

  • Presentation intent and terminal encoding were mixed together.
  • Styling logic was distributed across features and formatting helpers.
  • Tables could measure semantic content differently from what they ultimately rendered.
  • Untrusted API, profile, catalog, query, and link data did not have one reliable terminal-safety boundary.
  • ANSI-aware string length was handled in some places while JavaScript string length was still used in others.
  • Adding or changing presentation semantics required coordinated renderer and feature-specific helpers.

The result was harder to reason about than necessary: feature code knew too much about terminal mechanics, while the renderer did not consistently own sanitization, styling, hyperlink behavior, measurement, and truncation.

Solution

Introduce one small semantic presentation model and make the terminal renderer the single boundary that converts it into terminal output.

A display value is now either plain text or a sequence of spans containing:

  • text
  • an optional semantic style
  • an optional hyperlink target

Styles describe intent—such as strong, muted, accent, success, warning, error, data types, headings, and HTTP methods—without embedding ANSI sequences in views.

Documents, rows, tables, text blocks, and trees carry these semantic values until rendering. The terminal renderer then owns the concrete ANSI colors, OSC hyperlinks, sanitization, visible-width calculation, padding, and truncation.

This deliberately does not widen the general output sink or introduce a second rendering framework. Semantic values exist only where presentation needs them, and the existing document and terminal rendering path remains the output boundary.

What changed

Semantic presentation

  • Added a compact DisplaySpan/DisplayText representation for styled text and links.
  • Updated document rows, table cells, text blocks, and tree nodes to retain presentation semantics.
  • Centralized the mapping from semantic styles to terminal colors and formatting.
  • Migrated feature views and CLI output paths away from legacy styling helpers and raw terminal formatting.
  • Removed the old table-style fields and styling callbacks.
  • Preserved styling for active profile, environment, management, and lakehouse context values.

Terminal safety

All display text now passes through the terminal renderer before emission.

The renderer:

  • Escapes C0 and C1 terminal control characters.
  • Escapes bidirectional override and isolation controls.
  • Accepts only safe HTTP and HTTPS hyperlink targets for OSC 8 output.
  • Emits a sanitized visible URL when hyperlinks are unavailable or a target is unsafe.
  • Measures the exact rendered link fallback, so tables reserve space for label (URL) rather than only the label.
  • Sanitizes query IDs in colored, no-color, redirected, and Markdown output.
  • Preserves trusted multiline error structure while sanitizing every individual line.

This establishes one presentation boundary for values originating from API responses, profiles, catalogs, queries, errors, and link targets.

Correct terminal layout

Visible-width handling is now shared by table and query rendering instead of mixing terminal width with JavaScript .length, .slice(), or .padEnd().

Measurement and truncation now account for:

  • ANSI styling sequences
  • OSC hyperlinks
  • CJK and other double-width characters
  • combining marks
  • variation selectors
  • emoji modifiers
  • zero-width joiner emoji sequences
  • regional-indicator flag emoji

Truncation operates on grapheme clusters, so it does not split joined emoji or other user-perceived characters. Query tables and expanded labels use visible width for truncation and padding, keeping their frames aligned for non-ASCII content.

Result

Feature code now declares presentation intent, while the terminal renderer exclusively handles terminal mechanics.

There are no remaining legacy styling helpers, raw ANSI generators, or old table-style fields in cli/src. The resulting path is:

  1. Feature code builds semantic display content.
  2. Document and layout code preserve those semantics.
  3. The terminal renderer sanitizes and renders the final output.

This reduces the number of presentation concepts, makes terminal behavior consistent, and provides a single place to enforce safety and display-width correctness.

@francoischalifour francoischalifour force-pushed the fc/semantic-presentation-spans branch from 3e7ae2c to e1e7298 Compare July 13, 2026 17:51
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