Styled Text Content¶
Field |
Value |
|---|---|
Status |
Draft |
Related RFC |
|
Related drafts |
Content representation, Plain text content, Operations, Capability negotiation, Terminal-native behavior, Concrete message schemas, Error codes |
This document defines text/styled, the first optional content type: text
with per-range character styling. Capability Negotiation
governs how a terminal advertises it and how a TUI selects it; a TUI falls
back to text/plain when it is not confirmed.
1. Span List and Logical Text¶
A text/styled content value is an ordered list of spans. Each span carries
a non-empty text string and an optional style. A span without a style uses
the terminal’s default style. An empty span list is empty content.
The concatenation of all span texts in order is the Block’s logical text. Line breaks, tabs, visible control labels, and width follow Plain Text Content unchanged: styling annotates the displayed result of the logical text and never alters layout. A span’s style covers everything its text displays as, including the visible labels of control characters.
The representation is declarative and self-contained per Content Representation: it carries no escape sequences, cursor movement, or references to state outside the Block.
2. Style Attributes¶
A style may combine any of:
foregroundandbackground: the terminal’s current default color, a 256-palette index (indices 0–15 follow the terminal’s configured ANSI palette), or a 24-bit RGB value.bold,faint,italic,underline,strikethrough,inverse: booleans.
Every attribute is optional; an absent attribute means the terminal default for that attribute. No other attributes are defined. Blink is not defined.
3. Incremental Editing¶
Unlike the general optional-type default of complete snapshots, text/styled
adopts the incremental Operations:
Extend carries a non-empty span list appended once at the exact current logical tail. The content-state precondition, failure split, and recovery model are unchanged from Operation Semantics. The result is equivalent to concatenating the previous span list and the supplied one; whether adjacent identically styled spans are merged is an internal representation choice.
ReplaceSuffix carries the shared content-state base, a retained-prefix count, and a replacement span list that may be empty. The retained-prefix count uses the same unit as
text/plain: Unicode scalar values of the logical text, ignoring span boundaries. A boundary inside a span truncates that span; the retained part keeps its style.
An incremental Operation on a Block whose current content is text/styled
carries a span list; on a text/plain Block it carries text. A fragment or
replacement whose shape does not match the Block’s current content type is a
content-validity failure. Update may replace the complete value with any
confirmed type, including between text/plain and text/styled.
4. Terminal-Native Projections¶
Selection, copy, and search operate on the logical text exactly as they do
for text/plain, including the two-cell Han selection rule. Span boundaries
and styles are invisible to search matching. Copy produces the logical text;
offering styled clipboard formats in addition is a terminal-owned choice
outside this draft. Styling moves with its characters under reflow and never
changes a logical position, so reading-anchor semantics are unchanged.
5. Failure Reporting¶
Reporting follows Content Representation. For
text/styled:
A malformed type field, a non-array span list, a span missing
text, or a span carrying fields other thantextandstyleis a schema violation and reportsinvalid_message.A structurally valid value violating this type’s content rules reports
invalid_content: an empty span text, an unknown style attribute, a non-boolean emphasis attribute, or a malformed or out-of-range color.Receiving
text/styledcontent that was not confirmed during capability negotiation reportsunsupported_content_type.Incremental Operations keep the existing
content_state_mismatchandinvalid_content_boundarysplit; the boundary unit is the logical-text scalar count.
Every failure leaves the Block’s content, lifecycle, and content-state identifier unchanged.
6. JSON Mapping¶
In the initial JSON serialization, the content snapshot is:
{ "type": "text/styled", "data": [ span, ... ] }
span = { "text": Text, "style": style? }
style = {
"foreground": color?, "background": color?,
"bold": boolean?, "faint": boolean?, "italic": boolean?,
"underline": boolean?, "strikethrough": boolean?, "inverse": boolean?
}
color = "default" | { "palette": integer 0-255 } | { "rgb": [r, g, b] }
with r, g, b integers 0-255
For a Block whose current content is text/styled, block.extend’s
fragment is a non-empty span array and block.replace_suffix’s
replacement is a possibly empty span array; the field names are unchanged
from the baseline schemas in Concrete Message
Schemas. Schema-level validation checks span structure
only; the content rules of section 5 are content semantics, not schema.
Open Design Choices¶
Attribute extensibility and type versioning beyond the closed attribute set above.
Underline variants and other legacy emphasis attributes.
Styled clipboard formats.
Hyperlink range attributes and semantic theme colors, both deferred by RFC 0002.
Experimental Evidence¶
The codec tests round-trip styled snapshots, span fragments, and span replacements, and reject structural violations with their Operation identity.
The styled session tests exercise negotiation advertisement,
unsupported_content_typeandinvalid_contentreporting, span-aware Extend and ReplaceSuffix including mid-span boundaries, type switching through Update, and resource accounting on logical text.The pi-stock-ui trial streams a real application’s rendered styling (Pi’s markdown emphasis, thinking, and error styles) into
text/styledassistant Blocks while user and tool Blocks staytext/plainin the same Context, verifies the styled cell attributes in a headless xterm buffer, and checks that the logical text projection matches the plain mapping for the prose fixture. Its browser checks run every scenario with styled assistant Blocks through the real ConPTY host and confirm Pi’s bold and code styling in the painted xterm cells across a 60/36/60 resize.The browser endpoint scenarios verify every style attribute’s paint in the xterm DOM renderer, copy and search over the logical text across span boundaries, selection and paint through styled Extend, mid-span ReplaceSuffix, Seal, and Update to
text/plain, and styles moving with their characters through reflow while a reading anchor stays bound.The recorded-output ANSI tests convert a real Pi terminal recording into spans without sequence residue or text loss.