Problem
The TUI chat markdown output can sometimes be difficult to read depending on the content (e.g., long tables, deeply nested lists, or excessive whitespace). Currently, there is no mechanism for plugins to intercept and transform this text specifically for display purposes without affecting persistence or the Web UI.
Proposal
Add a new stable plugin hook (e.g., chat.display.transform) with a string -> string signature. This hook will allow plugins to perform display-only transformations on chat text before it is rendered in the TUI.
Scope & Constraints
- TUI only: Initial implementation targets the TUI
TextPart component.
- Display-only: Transformations are transient and not persisted to the session history.
- String -> String: Simple synchronous transformation.
- No Web UI / File Viewer changes: This hook specifically targets chat message rendering.
Timing Modes
To balance performance and UX, the hook should support three timing modes (configurable via opencode.json with CLI overrides):
final: Apply only once the message is complete.
throttle: Apply on streaming updates but throttled (default 100ms).
chunk: Apply on every streaming update (raw).
Safety & Reliability
- Sync-only: Prevents async plugins from blocking the render loop.
- Error Fallback: If a plugin throws or returns a non-string, the system logs the error and falls back to the original (or last successful) text, continuing the chain.
- Deterministic Ordering: Hooks run in the sequence plugins are defined in the config.
Acceptance Criteria
Problem
The TUI chat markdown output can sometimes be difficult to read depending on the content (e.g., long tables, deeply nested lists, or excessive whitespace). Currently, there is no mechanism for plugins to intercept and transform this text specifically for display purposes without affecting persistence or the Web UI.
Proposal
Add a new stable plugin hook (e.g.,
chat.display.transform) with astring -> stringsignature. This hook will allow plugins to perform display-only transformations on chat text before it is rendered in the TUI.Scope & Constraints
TextPartcomponent.Timing Modes
To balance performance and UX, the hook should support three timing modes (configurable via
opencode.jsonwith CLI overrides):final: Apply only once the message is complete.throttle: Apply on streaming updates but throttled (default 100ms).chunk: Apply on every streaming update (raw).Safety & Reliability
Acceptance Criteria
TextPartchokepoint.