Live panel showing subagents fanned out from within js_eval calls.
When the agent writes code that calls the top-level task() global, each
dispatch runs as a subagent inside a single js_eval tool call which is
invisible to the normal message stream. The QuickJS task bridge emits
lifecycle events on the custom stream. This widget consumes them and renders
a docked, live-updating fan-out panel.
Trust note: description/subagent_type and error strings originate
from LLM-authored JavaScript executed in the sandbox, so they are untrusted.
We route every rendered string through sanitize_control_chars which strips
control/escape/bidi characters and only ever render via Content.styled /
markup=False Static updates, so embedded Textual markup and terminal
escapes cannot influence rendering or panel state.
Get the glyph set for the current charset mode.
Format a duration in seconds into a human-readable string.
Return the ThemeColors for the active Textual theme.
For custom themes (LangChain-branded and user-defined), the pre-built
ThemeColors from the registry is returned directly. For Textual built-in
themes, colors are resolved dynamically from the actual theme properties so
Python-side styling stays in sync with CSS variables.
Textual widget code should call this instead of reading the module-level ANSI constants, which are intended for Rich console output only.
Neutralize control characters and deceptive Unicode in untrusted text.
Untrusted strings (MCP server errors, config-file contents, tool output)
can carry ANSI escape sequences, other control characters, or invisible
Unicode that corrupts the terminal, breaks out of a layout, or injects fake
lines into logs and prompts. This first removes the invisible/bidi code
points flagged by strip_dangerous_unicode, then replaces every remaining
Unicode "Other" (control/format) character with a space.
Animated spinner using charset-appropriate frames.
Docked two-pane panel visualizing js_eval subagent fan-out by phase.
Hidden until the first spawn event. Phases (one per js_eval) list on the
left and the selected phase's subagents render as a scrollable table on the
right. Focus the panel and use up/down to revisit finished phases. Expands
while any phase runs, collapses to the header when the turn goes idle, and
re-expands when a new phase starts.