Observability widgets
The point of a widget library is rendering content, but the point of an
observability widget is rendering the library itself. FrankenTUI ships a
handful of diagnostic overlays you drop into a running screen when
something is wrong (or when you want to prove it is right). Each is a
normal widget — you can place one in a pane, wrap it in a Block, toggle
it with a keybinding.
This page is a quick-reference for eight of them. For the full widget table see the catalog; for what each data source means, follow the cross-links into the intelligence layer.
Inspector — the big one
Source: inspector.rs:283
(100+ KB; one of the largest widgets in the tree).
Inspector is a live widget-tree inspector: you see the hierarchy of
widgets that rendered this frame, their allocated rects, their registered
hit regions, and their accessibility tree contribution. Hover over a
widget in your UI and the inspector highlights the rect that produced it.
When to use it:
- Layout is doing something unexpected and you want to see the concrete
Rectevery widget was given. - A hit region isn’t triggering and you suspect it was never registered.
- The a11y tree for a screen reader looks wrong and you want to see what FrankenTUI is publishing.
Toggle: typically bound to F1 or a dev-mode keybinding.
DebugOverlay — live performance metrics
Source: debug_overlay.rs:92.
A small panel with:
- Frame time histogram (p50 / p95 / p99)
- Dirty rows per frame
- Buffer statistics (total cells, styled cells, wide cells)
- Current degradation level
- Render-budget remaining
Useful for identifying which frames are blowing the budget and for correlating frame time with gestures (drags, scrolls, resizes).
ConstraintOverlay — layout in living colour
Source: constraint_overlay.rs:115.
Overlays every allocated Rect with a coloured outline and its input
constraint (Length(4), Min(20), Percentage(50), etc.). Answers the
question “why did this column end up 7 cells wide?” in one glance.
Pairs with the LayoutDebugger
which shows the pre-solve constraint tree.
LogViewer — in-app tail
Source: log_viewer.rs:193.
Scrollable, filterable display of log entries. Backed by a ring buffer
(log_ring), so it is bounded in memory regardless of runtime duration.
Supports:
- Regex filter
- Level filter (
INFO+,WARN+, etc.) - Auto-scroll with pause-on-hover
- Jump to newest / oldest
Typical integration: pipe tracing output into the log ring via the
ftui-extras logging subscriber, then render the viewer in a pane.
HistoryPanel — undo / redo visualised
Source: history_panel.rs:54.
Renders the undo stack as a tree, including divergent branches when the
user undoes and then makes a new change. Useful for interactive editors;
pairs with the
UndoSupport
generic undo plumbing.
DecisionCard — the Bayesian ledger, inline
Source: decision_card.rs:28.
A progressive-disclosure UI that renders a Bayesian evidence ledger — the same structure emitted by the command-palette scorer. Collapsed it shows prior odds and the final posterior; expanded it lists every factor and its rationale.
Useful anywhere a decision needs an auditable trail — hint ranking,
capability detection, VOI sampling results. See
/intelligence/bayesian-inference/command-palette-ledger
for the data shape.
VoiDebugOverlay — measurement budget
Source: voi_debug_overlay.rs:178.
Renders the current state of a VOI (Value of Information) sampler: which candidates are being measured, what their posteriors look like, and the expected information gain from each pending measurement. You see why the sampler chose the action it did.
DriftVisualization — per-domain confidence
Source: drift_visualization.rs:56.
One sparkline per domain (capability, height prediction, palette ranking, …) showing the conformal prediction interval’s width over recent frames. Growing intervals mean the model is less confident — a drift signal.
Pair with DebugOverlay to correlate drift with frame-time spikes.
When to reach for each
| Situation | Widget |
|---|---|
| ”Layout is wrong” | ConstraintOverlay + Inspector |
| ”We’re dropping frames” | DebugOverlay |
| ”Where did that log message go?” | LogViewer |
| ”My undo is buggy” | HistoryPanel |
| ”Why did the command palette rank X first?” | DecisionCard |
| ”Why is VOI sampling this cell?” | VoiDebugOverlay |
| ”Is my conformal predictor drifting?” | DriftVisualization |
| ”What is the a11y tree?” | Inspector |
Composition pattern
A common pattern is a dev-mode toggle that overlays a whole observability panel on top of the normal view:
use ftui_widgets::{
Widget, StatefulWidget,
debug_overlay::DebugOverlay,
log_viewer::{LogViewer, LogViewerState},
constraint_overlay::ConstraintOverlay,
};
pub struct Model {
dev_mode: bool,
log_state: LogViewerState,
// … real model
}
// `Model::view` is `&self`; this helper uses `&mut self` so
// `StatefulWidget::render` can borrow `&mut self.log_state`. Wrap
// `log_state` in `RefCell<LogViewerState>` to call from a real `view`.
fn render_with_overlays(&mut self, frame: &mut ftui_render::frame::Frame) {
render_normal_view(frame);
if self.dev_mode {
let area = frame.buffer.bounds();
// Right-side strip for logs + metrics
let [_, right] = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Min(40), Constraint::Length(40)])
.split(area);
let [metrics, logs] = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(12), Constraint::Min(10)])
.split(right);
DebugOverlay::default().render(metrics, frame);
StatefulWidget::render(
&LogViewer::default(), logs, frame, &mut self.log_state,
);
ConstraintOverlay::default().render(area, frame);
}
}Pitfalls
- Leaving observability widgets in production builds. They are debug tools. Feature-gate them or hide behind a dev-mode flag so they never render to end users.
- Overlapping overlays. Multiple observability widgets rendered on top of each other make the screen unreadable. Pick one or arrange them in a dedicated panel.
- Relying on
LogViewerfor anything persistent. The backing ring buffer is bounded; old entries are evicted. Use a file sink in parallel for durable logs. - Misreading
DriftVisualization. A widening interval is not a bug — it is the predictor saying “I am less sure now”. Correlate with input rate changes before panicking.
Where next
The source of the Bayesian ledger DecisionCard renders.
What VoiDebugOverlay is showing you.
What a conformal interval width means when DriftVisualization
widens it.
How DebugOverlay’s numbers map to the degradation ladder.
How this piece fits in widgets.
Widgets overview