Widget Reference¶
Widgets are runtime components that satisfy the Node concept — each provides
a build() method returning an Element, plus an implicit operator Element()
conversion. Interactive widgets integrate with the focus system and accept
KeyEvent via a handle() method. All types live in namespace maya and
headers are under include/maya/widget/.
Using Widgets¶
Widgets work in both API styles. In simple run(), use them directly in the render lambda:
Input<> prompt;
prompt.set_placeholder("Type here...");
run({.title = "app"}, event_fn, [&] {
return v(prompt, t<"[Enter] submit"> | Dim) | pad<1>;
});
In Program apps, widgets are typically stored in the Model or as globals, and rendered in view():
Interactive widgets need their handle(KeyEvent) method called from the event handler (simple run) or from subscribe()/update() (Program).
Input Widgets¶
Input¶
Single-line text input with cursor, editing, history, and password masking.
Header: widget/input.hpp
struct InputConfig {
bool multiline = false;
bool history = true;
int max_length = 0; // 0 = unlimited
bool password = false;
};
template <InputConfig Cfg = InputConfig{}>
class Input;
Key methods: on_submit(F), on_change(F), set_placeholder(sv),
set_value(sv), clear(), handle(KeyEvent), handle_paste(PasteEvent),
value(), cursor(), focus_node().
Input<> prompt;
prompt.set_placeholder("Type a message...");
prompt.on_submit([](std::string_view text) { process(text); });
auto ui = dsl::h(text("> ") | Dim, prompt);
TextArea¶
Multi-line text editor with line numbers, scrolling, and vertical cursor movement.
Header: widget/textarea.hpp
struct TextAreaConfig {
bool line_numbers = true;
bool show_line_count = true;
int visible_lines = 0; // 0 = show all
int max_length = 0;
};
class TextArea;
// TextArea()
// TextArea(TextAreaConfig cfg)
Key methods: on_change(F), on_submit(F), set_placeholder(sv),
set_value(sv), clear(), handle(KeyEvent), handle_paste(PasteEvent),
value(), cursor(), focus_node().
TextArea editor(TextAreaConfig{.visible_lines = 10});
editor.on_change([](std::string_view text) { save(text); });
auto ui = editor.build();
Checkbox / ToggleSwitch¶
Toggle checkbox ([x]/[ ]) and sliding toggle switch.
Header: widget/checkbox.hpp
class Checkbox;
// Checkbox()
// Checkbox(std::string label, bool initial = false)
class ToggleSwitch;
// ToggleSwitch()
// ToggleSwitch(std::string label, bool initial = false)
Key methods (both): on_change(F), handle(KeyEvent), focus_node().
Checkbox: checked(), set_checked(bool).
ToggleSwitch: on(), set_on(bool).
Checkbox cb("Enable dark mode");
cb.on_change([](bool checked) { update_theme(checked); });
ToggleSwitch ts("Notifications", true);
ts.on_change([](bool on) { toggle_notifs(on); });
Radio¶
Radio button group with single selection. Arrow-key navigable.
Header: widget/radio.hpp
struct RadioConfig {
std::string selected_indicator; // default "● "
std::string unselected_indicator; // default "○ "
Style selected_style, unselected_style;
int visible_count = 0;
};
class Radio;
// Radio(std::vector<std::string> items, RadioConfig cfg = {})
// Radio(std::initializer_list<std::string_view> items, RadioConfig cfg = {})
Key methods: on_change(F) where F(int idx, std::string_view label),
selected(), selected_label(), set_selected(int), set_items(...),
handle(KeyEvent), focus_node().
Radio theme({"Light mode", "Dark mode", "System default"});
theme.on_change([](int idx, std::string_view label) {
apply_theme(label);
});
auto ui = dsl::v(text("Theme:") | Bold, theme);
Button¶
Clickable button with variant styles: Default, Primary, Danger, Ghost.
Header: widget/button.hpp
enum class ButtonVariant : uint8_t { Default, Primary, Danger, Ghost };
class Button;
// Button()
// Button(std::string label, std::move_only_function<void()> on_click = {},
// ButtonVariant variant = ButtonVariant::Default)
Key methods: on_click(F), set_label(sv), set_variant(v),
handle(KeyEvent), focus_node().
Button submit("Submit", [] { save(); }, ButtonVariant::Primary);
Button cancel("Cancel", [] { close(); }, ButtonVariant::Ghost);
auto ui = dsl::h(submit, text(" "), cancel);
Select¶
Interactive selection menu with arrow-key navigation and mouse support.
Header: widget/select.hpp
struct SelectConfig {
std::string indicator; // default "❯ "
std::string inactive_prefix; // default " "
Style active_style, inactive_style;
int visible_count = 0;
};
class Select;
// Select(std::vector<std::string> items, SelectConfig cfg = {})
// Select(std::initializer_list<std::string_view> items, SelectConfig cfg = {})
Key methods: on_select(F) where F(int, std::string_view),
selected_label(), set_items(...), handle(KeyEvent),
handle_mouse(MouseEvent, int render_y_start), focus_node().
Select menu({"Option A", "Option B", "Option C"});
menu.on_select([](int idx, std::string_view label) {
handle_selection(idx);
});
Slider¶
Numeric range slider with visual track. Left/Right or h/l to adjust.
Header: widget/slider.hpp
struct SliderConfig {
float min = 0.0f, max = 1.0f, step = 0.01f;
int width = 0; // 0 = fill available width
Color fill_color, track_color;
bool show_percent = true;
};
class Slider;
// Slider()
// Slider(std::string label, SliderConfig cfg = {})
Key methods: on_change(F), set_value(float), set_label(sv),
value(), handle(KeyEvent), focus_node().
Slider volume("Volume", {.min = 0, .max = 100, .step = 5});
volume.on_change([](float v) { set_volume(v); });
auto ui = volume.build();
Data Display¶
Table¶
Formatted data table with configurable columns, alignment, striping, and optional bordered card.
Header: widget/table.hpp
enum class ColumnAlign : uint8_t { Left, Center, Right };
struct ColumnDef {
std::string header;
int width = 0; // 0 = auto-fit
ColumnAlign align = ColumnAlign::Left;
};
struct TableConfig {
Style header_style, row_style, alt_row_style, separator_style;
bool stripe_rows = true;
int cell_padding = 1;
bool show_border = false;
std::string title;
Color border_color;
};
class Table;
// Table(std::vector<ColumnDef> columns)
// Table(std::vector<ColumnDef> columns, TableConfig cfg)
Key methods: add_row(vector<string>), set_rows(...), clear_rows(),
row_count(), set_title(sv), set_bordered(bool).
Table tbl({{"Name", 20}, {"Status", 10}});
tbl.add_row({"main.cpp", "modified"});
tbl.add_row({"README.md", "staged"});
auto ui = tbl.build();
List¶
Scrollable selectable list with icon, label, description, and optional
filtering via / key.
Header: widget/list.hpp
struct ListItem {
std::string label, description, icon;
};
struct ListConfig {
std::string indicator; // default "▸ "
Style active_style, inactive_style, desc_style, filter_style;
int visible_count = 10;
bool filterable = false;
};
class List;
// List(std::vector<ListItem> items, ListConfig cfg = {})
Key methods: on_select(F) where F(int, std::string_view),
selected_label(), set_items(...), set_filter(sv), is_filtering(),
handle(KeyEvent), focus_node().
List menu({
{"Add new item", "Create a new entry", "+"},
{"Remove item", "Delete selected entry", "-"},
});
menu.on_select([](int idx, std::string_view label) { /* ... */ });
Tree¶
Hierarchical tree view with expand/collapse and keyboard navigation.
Header: widget/tree.hpp
struct TreeNode {
std::string label;
std::vector<TreeNode> children;
bool expanded = false;
bool selected = false;
};
struct TreeConfig {
std::string expanded_icon; // "▾ "
std::string collapsed_icon; // "▸ "
int indent_width = 2;
Style active_style, branch_style, leaf_style, count_style;
};
class Tree;
// Tree(TreeNode root, TreeConfig cfg = {})
Key methods: on_select(F) where F(std::string_view), root(),
cursor(), handle(KeyEvent), focus_node().
TreeNode root{"src", {
{"widget", {{"list.hpp"}, {"tree.hpp"}}},
{"core", {{"types.hpp"}, {"signal.hpp"}}},
{"main.cpp"},
}};
Tree tree(std::move(root));
tree.on_select([](std::string_view label) { open_file(label); });
Sparkline¶
Inline mini chart using Unicode block characters (single-line).
Header: widget/sparkline.hpp
struct SparklineConfig {
Color color;
Style label_style, value_style;
bool show_min_max = false;
bool show_last = false;
};
class Sparkline;
// Sparkline(std::vector<float> data, SparklineConfig cfg = {})
Key methods: set_data(...), set_label(sv), set_min(float),
set_max(float), set_color(Color), set_show_min_max(bool),
set_show_last(bool).
Sparkline spark({0.1f, 0.3f, 0.5f, 0.8f, 1.0f, 0.6f});
spark.set_label("CPU");
spark.set_show_min_max(true);
auto ui = spark.build();
Navigation¶
Tabs¶
Tabbed content switcher. Left/Right, h/l, and 1-9 number key navigation.
Header: widget/tabs.hpp
class Tabs;
// Tabs(std::vector<std::string> labels)
// Tabs(std::initializer_list<std::string_view> labels)
Key methods: on_change(F) where F(int), active(), set_active(int),
add_tab(string), handle(KeyEvent), focus_node().
Tabs tabs({"General", "Editor", "Keybindings"});
tabs.on_change([](int idx) { switch_content(idx); });
auto ui = dsl::v(tabs, content);
Breadcrumb¶
Passive path/context breadcrumb trail. Last segment is highlighted.
Header: widget/breadcrumb.hpp
class Breadcrumb;
// Breadcrumb(std::vector<std::string> segments)
// Breadcrumb(std::initializer_list<std::string_view> segments)
Key methods: set_segments(...), push(string), pop(), size().
Menu¶
Vertical menu with keybind hints, separators, and disabled items.
Header: widget/menu.hpp
struct MenuItem {
std::string label, shortcut;
bool enabled = true;
bool separator = false;
};
class Menu;
// Menu(std::vector<MenuItem> items)
Key methods: on_select(F) where F(int), set_items(...),
handle(KeyEvent), focus_node().
Menu menu({
{.label = "New File", .shortcut = "Ctrl+N"},
{.label = "Open File", .shortcut = "Ctrl+O"},
{.separator = true},
{.label = "Quit", .shortcut = "Ctrl+Q"},
});
menu.on_select([](int idx) { handle_menu(idx); });
CommandPalette¶
Fuzzy-search command launcher with embedded Input and highlighted match results.
Header: widget/command_palette.hpp
struct Command {
std::string name, description, shortcut;
};
class CommandPalette;
// CommandPalette(std::vector<Command> commands)
Key methods: show(), hide(), toggle(), visible(),
on_execute(F) where F(int), handle(KeyEvent),
handle_paste(PasteEvent), focus_node().
CommandPalette palette({
{.name = "Open File", .description = "Browse files", .shortcut = "Ctrl+O"},
{.name = "Toggle Theme", .description = "Switch dark/light"},
});
palette.on_execute([](int idx) { run_command(idx); });
palette.show();
Display¶
Badge¶
Inline bracketed label/chip with preset color themes.
Header: widget/badge.hpp
struct Badge {
std::string label;
Config config; // style, left_cap, right_cap, bracket_style
Badge(std::string label);
Badge(std::string label, Config cfg);
static Badge success(std::string label);
static Badge error(std::string label);
static Badge warning(std::string label);
static Badge info(std::string label);
static Badge tool(std::string label); // purple
};
Callout¶
Severity-based alert/notification with icon, title, and optional description body with left border.
Header: widget/callout.hpp
enum class Severity : uint8_t { Info, Success, Warning, Error };
class Callout;
// Callout(Config cfg) — Config{severity, title, description}
// Callout(Severity, string title, string desc = "")
static Callout info(string title, string desc = "");
static Callout success(string title, string desc = "");
static Callout warning(string title, string desc = "");
static Callout error(string title, string desc = "");
auto err = Callout::error("Failed to read file", "permission denied");
auto info = Callout::info("3 files modified");
auto ui = dsl::v(err, info);
ToastManager¶
Notification toast stack with timed auto-dismissal and severity styling.
Header: widget/toast.hpp
enum class ToastLevel : uint8_t { Info, Success, Warning, Error };
struct ToastManager {
struct Config {
float duration = 3.0f, fade_time = 0.5f;
int max_visible = 3;
};
// ToastManager()
// ToastManager(Config cfg)
};
Key methods: push(string message, ToastLevel level), advance(float dt),
empty().
ToastManager toasts;
toasts.push("Response complete", ToastLevel::Success);
toasts.advance(dt);
auto ui = toasts.build();
FileRef¶
File path reference display with dimmed directory, underlined filename, and optional line number.
Header: widget/file_ref.hpp
struct FileRef {
std::string path;
int line = 0;
FileRef(std::string path, int line = 0);
Element build() const;
Element build(Config cfg) const; // Config: path_style, name_style, lineno_style, icon
};
UserMessage / AssistantMessage¶
Chat message containers. User messages get a bordered box; assistant messages get clean padding.
Header: widget/message.hpp
struct UserMessage {
static Element build(std::string_view content);
static Element build(Element content);
};
struct AssistantMessage {
static Element build(Element content);
};
auto user_msg = UserMessage::build("show me the project structure");
auto asst_msg = AssistantMessage::build(markdown_element);
ProgressBar¶
Progress bar with label, percentage, sub-character precision, and elapsed time.
Header: widget/progress.hpp
struct ProgressConfig {
int width = 0; // 0 = fill available
Color fill_color, bg_color;
bool show_track = true, show_percentage = true;
};
class ProgressBar;
// ProgressBar()
// ProgressBar(ProgressConfig cfg)
Key methods: set(float v), set_elapsed(float seconds),
set_label(sv), value().
ProgressBar bar;
bar.set_label("Indexing files...");
bar.set(0.68f);
bar.set_elapsed(2.3f);
auto ui = bar.build();
ActivityBar¶
Bottom status/activity bar displaying model, tokens, cost, context usage, and custom sections.
Header: widget/activity_bar.hpp
struct ActivityBar {
struct Config { /* separator_style, label_style, value_style, accent_style */ };
// ActivityBar()
// ActivityBar(Config cfg)
};
Key methods: set_model(sv), set_tokens(int in, int out),
set_cost(float usd), set_context_percent(int), set_status(sv),
add_section(string icon, string label, Style), clear_sections().
ActivityBar bar;
bar.set_model("claude-sonnet-4");
bar.set_tokens(1200, 3400);
bar.set_cost(0.03f);
bar.set_context_percent(45);
auto ui = bar.build();
Divider¶
Horizontal separator with optional label. Fills available width.
Header: widget/divider.hpp
struct DividerConfig {
BorderStyle line = BorderStyle::Single;
Style line_style, label_style;
};
class Divider;
// Divider()
// Divider(std::string_view label, DividerConfig cfg = {})
// Divider(DividerConfig cfg)
Key methods: set_label(sv).
Spinner¶
Animated frame-based spinner with 9 built-in styles. Updates via elapsed time.
Header: widget/spinner.hpp
enum class SpinnerStyle : uint8_t {
Dots, Line, Arc, Arrow, Bounce, Bar, Clock, Star, Pulse
};
template <SpinnerStyle S = SpinnerStyle::Dots>
class Spinner;
// Spinner()
// Spinner(Style s)
Key methods: advance(float dt), set_style(Style).
Spinner spin;
spin.advance(dt);
auto ui = dsl::h(spin, text(" Loading..."));
Spinner<SpinnerStyle::Braille> s2;
Overlay¶
Modal¶
Modal dialog with title, content, and Tab-navigable action buttons. Enter to activate, Escape to dismiss.
Header: widget/modal.hpp
struct ModalButton {
enum Variant : uint8_t { Default, Primary, Danger };
std::string label;
Variant variant = Default;
std::move_only_function<void()> callback;
};
class Modal;
// Modal()
// Modal(std::string title, Element content, std::vector<ModalButton> buttons)
Key methods: show(), hide(), visible(), set_content(Element),
set_title(sv), handle(KeyEvent), focus_node().
Modal modal("Confirm", text("Are you sure?"), {
{"Cancel", ModalButton::Default, [&]{ modal.hide(); }},
{"OK", ModalButton::Primary, [&]{ do_thing(); modal.hide(); }},
});
modal.show();
auto ui = modal.build();
Popup¶
Non-interactive bordered message box with severity-based styling.
Header: widget/popup.hpp
enum class PopupStyle : uint8_t { Info, Warning, Error };
class Popup;
// Popup()
// Popup(std::string content, PopupStyle style = PopupStyle::Info)
Key methods: show(string_view msg, PopupStyle), hide(), visible().
Scrollable¶
Scrollable container with viewport clipping and scroll indicator.
Header: widget/scrollable.hpp
struct ScrollConfig {
int height = 10;
int scroll_amount = 1;
bool show_indicator = true;
Color indicator_color, indicator_active;
};
class Scrollable;
// Scrollable(ScrollConfig cfg = {})
Key methods: set_content(Element), set_content_height(int),
scroll_up(int n = 0), scroll_down(int n = 0), scroll_to_top(),
scroll_to_bottom(), offset(), height(), content_height(),
handle(KeyEvent), handle_mouse(MouseEvent).
Scrollable scroll({.height = 10});
scroll.set_content(my_tall_element);
scroll.set_content_height(50);
auto ui = scroll.build();
Visualization¶
BarChart¶
Non-interactive horizontal bar chart with proportional block bars.
Header: widget/bar_chart.hpp
struct Bar {
std::string label;
float value = 0.0f;
std::optional<Color> color;
};
class BarChart;
// BarChart()
// BarChart(std::vector<Bar> bars, float max_value = 0.0f)
Key methods: set_bars(...), set_max_value(float),
set_default_color(Color).
BarChart chart({
{"CPU", 0.75f},
{"Memory", 0.45f},
{"Disk", 0.90f, Color::rgb(224, 108, 117)},
});
auto ui = chart.build();
LineChart¶
Braille-dot line chart with 2x4 sub-cell resolution per terminal cell.
Header: widget/line_chart.hpp
Key methods: set_data(...), set_height(int), set_label(sv),
set_color(Color).
LineChart chart({1.0f, 3.5f, 2.0f, 7.0f, 4.5f, 6.0f});
chart.set_height(8);
chart.set_label("Requests/s");
auto ui = chart.build();
Gauge¶
Arc or bar meter for displaying a 0-1 value. Arc mode renders a bordered box with percentage; Bar mode renders a vertical block column.
Header: widget/gauge.hpp
enum class GaugeStyle : uint8_t { Arc, Bar };
class Gauge;
// Gauge()
// Gauge(float value, std::string label = {},
// Color color = ..., GaugeStyle style = GaugeStyle::Arc)
Key methods: set_value(float), set_label(sv), set_color(Color),
set_style(GaugeStyle), value().
Heatmap¶
2D color grid with interpolated cell colors between a low and high color.
Header: widget/heatmap.hpp
Key methods: set_data(...), set_x_labels(vector<string>),
set_y_labels(vector<string>), set_low_color(Color),
set_high_color(Color).
Heatmap hm({
{0.1f, 0.5f, 0.9f},
{0.3f, 0.7f, 0.2f},
});
hm.set_x_labels({"Mon", "Tue", "Wed"});
hm.set_y_labels({"AM", "PM"});
auto ui = hm.build();
PixelCanvas¶
Freeform drawing surface using half-block characters for double vertical
resolution. Resolution: width x (height * 2) pixels.
Header: widget/canvas.hpp
Key methods: set_pixel(int x, int y, Color), clear(), fill(Color),
line(x1, y1, x2, y2, Color), rect(x, y, w, h, Color),
width(), height(), pixel_height().
PixelCanvas c(40, 10);
c.set_pixel(5, 3, Color::rgb(224, 108, 117));
c.line(0, 0, 39, 19, Color::rgb(97, 175, 239));
auto ui = c.build();
Specialized¶
DiffView¶
Unified diff display with colored +/- lines, hunk markers, and optional line numbers.
Header: widget/diff_view.hpp
struct DiffView {
struct Config {
Style add_style, remove_style, context_style, hunk_style, header_style;
Color border_color;
bool show_border = true, show_line_numbers = true;
};
std::string file_path, diff_text;
Config config;
DiffView(std::string path, std::string diff);
DiffView(std::string path, std::string diff, Config cfg);
};
auto diff = DiffView("src/main.cpp",
"@@ -12,4 +12,6 @@\n"
" void Canvas::begin_frame() {\n"
"- damage_ = {0, 0, width_, height_};\n"
"+ damage_ = {0, 0, 0, 0};\n"
" }\n");
auto ui = diff.build();
PlanView¶
Task/plan display with status icons (pending, in-progress, completed).
Header: widget/plan_view.hpp
enum class TaskStatus : uint8_t { Pending, InProgress, Completed };
struct PlanView {
struct Task { std::string label; TaskStatus status; };
std::vector<Task> tasks;
};
Key methods: add(string label, TaskStatus status = Pending),
set_status(size_t index, TaskStatus).
PlanView plan;
plan.add("Analyze codebase", TaskStatus::InProgress);
plan.add("Read configuration files", TaskStatus::Completed);
plan.add("Write implementation");
auto ui = plan.build();
LogViewer¶
Streaming log viewer with scroll, auto-scroll, and level filtering.
Header: widget/log_viewer.hpp
enum class LogLevel : uint8_t { Debug, Info, Warn, Error };
struct LogEntry { std::string timestamp, message; LogLevel level; };
class LogViewer;
// LogViewer()
// LogViewer(int visible, int max_entries = 1000)
Key methods: push(LogEntry), clear(), set_visible(int),
set_max_entries(int), entry_count(), auto_scroll(),
handle(KeyEvent), focus_node().
LogViewer viewer(20);
viewer.push({.timestamp = "12:00:01", .message = "Started", .level = LogLevel::Info});
viewer.push({.timestamp = "12:00:02", .message = "Cache miss", .level = LogLevel::Warn});
auto ui = viewer.build();
Calendar¶
Interactive month calendar grid with arrow-key day selection and today highlighting.
Header: widget/calendar.hpp
class Calendar;
// Calendar(int year, int month)
// Calendar(int year, int month, int today_y, int today_m, int today_d)
Key methods: on_select(F) where F(int y, int m, int d),
next_month(), prev_month(), set_today(y, m, d), selected_day(),
year(), month(), handle(KeyEvent), focus_node().
Calendar cal(2026, 4, 2026, 4, 9);
cal.on_select([](int y, int m, int d) { set_date(y, m, d); });
auto ui = cal.build();
KeyHelp¶
Keyboard shortcut reference panel. Adapts between 1-column and 2-column layout based on available width.
Header: widget/key_help.hpp
struct KeyBinding { std::string key, description, group; };
class KeyHelp;
// KeyHelp()
// KeyHelp(std::vector<KeyBinding> bindings)
Key methods: add(string key, string description, string group = ""),
set_title(sv), clear().
KeyHelp help;
help.add("Up/Down", "Move cursor", "Navigation");
help.add("Enter", "Select item", "Navigation");
help.add("Ctrl+S", "Save file", "Editing");
auto ui = help.build();
Disclosure¶
Chevron-based collapse/expand control with optional content body.
Header: widget/disclosure.hpp
struct Disclosure {
struct Config {
std::string label;
Style label_style, icon_style;
std::string open_icon, closed_icon; // default "▼", "▶"
};
// Disclosure(Config cfg)
};
Key methods: is_open(), set_open(bool), toggle(), build() (header
only), build(Element content) (header + content when open).
Disclosure d({.label = "Advanced Settings"});
d.set_open(true);
auto ui = d.build(settings_content);
ThinkingBlock¶
Collapsible thinking/reasoning block with spinner and gutter-bordered content.
Header: widget/thinking.hpp
Key methods: set_active(bool), set_expanded(bool), toggle(),
set_content(sv), append(sv), advance(float dt),
set_max_visible_lines(int), is_active(), is_expanded().
ThinkingBlock thinking;
thinking.set_active(true);
thinking.set_content("Analyzing the request...\nReading project structure...");
thinking.advance(dt);
auto ui = thinking.build();
Tool Widgets¶
These widgets render tool execution cards in a Claude Code / Zed agent style: bordered boxes with status icons, elapsed time, and collapsible content.
ToolCall¶
Generic collapsible tool execution card.
Header: widget/tool_call.hpp
enum class ToolCallStatus : uint8_t { Pending, Running, Completed, Failed, Confirmation };
enum class ToolCallKind : uint8_t {
Read, Edit, Execute, Search, Delete, Move, Fetch, Think, Agent, Other
};
class ToolCall;
// ToolCall(Config cfg) — Config{tool_name, kind, description}
Key methods: set_status(ToolCallStatus), set_elapsed(float),
set_expanded(bool), toggle(), set_content(Element), status().
ToolCall tc({.tool_name = "Read", .kind = ToolCallKind::Read,
.description = "src/main.cpp"});
tc.set_status(ToolCallStatus::Completed);
tc.set_elapsed(0.3f);
auto ui = tc.build();
BashTool¶
Command execution card with $ command display, output, and exit code.
Header: widget/bash_tool.hpp
enum class BashStatus : uint8_t { Pending, Running, Success, Failed };
class BashTool;
// BashTool()
// BashTool(std::string command)
Key methods: set_command(sv), set_output(sv), append_output(sv),
set_status(BashStatus), set_elapsed(float), set_exit_code(int),
set_expanded(bool), toggle(), set_max_output_lines(int).
BashTool bash("npm install --save-dev typescript");
bash.set_status(BashStatus::Success);
bash.set_elapsed(2.3f);
bash.set_output("added 1 package in 2.3s");
bash.set_expanded(true);
auto ui = bash.build();
EditTool¶
File edit operation card with search/replace diff display.
Header: widget/edit_tool.hpp
enum class EditStatus : uint8_t { Pending, Applying, Applied, Failed };
class EditTool;
// EditTool()
// EditTool(std::string path)
Key methods: set_file_path(sv), set_old_text(sv), set_new_text(sv),
set_status(EditStatus), set_elapsed(float), set_expanded(bool),
toggle().
EditTool edit("src/render/canvas.cpp");
edit.set_old_text("damage_ = {0, 0, width_, height_};");
edit.set_new_text("damage_ = {0, 0, 0, 0};");
edit.set_status(EditStatus::Applied);
edit.set_expanded(true);
auto ui = edit.build();
WriteTool¶
File creation/write card with content preview.
Header: widget/write_tool.hpp
enum class WriteStatus : uint8_t { Pending, Writing, Written, Failed };
class WriteTool;
// WriteTool()
// WriteTool(std::string path)
Key methods: set_file_path(sv), set_content(sv),
set_status(WriteStatus), set_elapsed(float), set_expanded(bool),
toggle(), set_max_preview_lines(int).
WriteTool wt("src/utils/helpers.ts");
wt.set_content("export function debounce(...) { ... }");
wt.set_status(WriteStatus::Written);
wt.set_expanded(true);
auto ui = wt.build();
ReadTool¶
File content preview card with line numbers and truncation.
Header: widget/read_tool.hpp
enum class ReadStatus : uint8_t { Pending, Reading, Success, Failed };
class ReadTool;
// ReadTool()
// ReadTool(std::string path)
Key methods: set_file_path(sv), set_content(sv),
set_status(ReadStatus), set_elapsed(float), set_expanded(bool),
toggle(), set_start_line(int), set_max_lines(int),
set_total_lines(int).
ReadTool rt("src/main.cpp");
rt.set_content("#include <iostream>\nint main() { return 0; }");
rt.set_status(ReadStatus::Success);
rt.set_elapsed(0.2f);
rt.set_expanded(true);
auto ui = rt.build();
FetchTool¶
Web fetch result card with URL, HTTP status, content type, and body preview.
Header: widget/fetch_tool.hpp
enum class FetchStatus : uint8_t { Pending, Fetching, Done, Failed };
class FetchTool;
// FetchTool()
// FetchTool(std::string url)
Key methods: set_url(sv), set_body(sv), set_content_type(sv),
set_status_code(int), set_status(FetchStatus), set_elapsed(float),
set_expanded(bool), toggle(), set_max_body_lines(int).
FetchTool ft("https://api.example.com/v1");
ft.set_status(FetchStatus::Done);
ft.set_status_code(200);
ft.set_content_type("application/json");
ft.set_elapsed(1.2f);
ft.set_body("{\"data\": [...]}");
ft.set_expanded(true);
auto ui = ft.build();
SearchResult¶
Search results display (Grep/Glob) with grouped file matches.
Header: widget/search_result.hpp
enum class SearchKind : uint8_t { Grep, Glob };
enum class SearchStatus : uint8_t { Pending, Searching, Done, Failed };
struct SearchMatch { int line; std::string content; };
struct SearchFileGroup { std::string file_path; std::vector<SearchMatch> matches; };
class SearchResult;
// SearchResult()
// SearchResult(SearchKind kind, std::string pattern = "")
Key methods: set_pattern(sv), set_status(SearchStatus),
set_elapsed(float), set_expanded(bool), toggle(), clear(),
add_group(SearchFileGroup), total_matches(), file_count(),
set_max_matches_per_file(int).
SearchResult sr(SearchKind::Grep, "TODO");
sr.add_group({"src/main.cpp", {{12, "// TODO: fix this"}, {45, "// TODO: refactor"}}});
sr.set_status(SearchStatus::Done);
sr.set_elapsed(0.4f);
sr.set_expanded(true);
auto ui = sr.build();
AgentTool¶
Sub-agent card with nested tool calls, spinner, and model badge.
Header: widget/agent_tool.hpp
enum class AgentStatus : uint8_t { Pending, Running, Completed, Failed };
class AgentTool;
// AgentTool()
// AgentTool(std::string description)
Key methods: set_description(sv), set_model(sv),
set_status(AgentStatus), set_elapsed(float), set_expanded(bool),
toggle(), advance(float dt), add_tool(Element), clear_tools().
AgentTool agent("Exploring codebase structure");
agent.set_model("claude-sonnet-4");
agent.set_status(AgentStatus::Running);
agent.advance(dt);
agent.add_tool(read_tool.build());
agent.add_tool(grep_tool.build());
auto ui = agent.build();
Permission¶
Tool permission prompt card with key hints for allow/deny.
Header: widget/permission.hpp
enum class PermissionResult : uint8_t { Pending, Allow, AllowAlways, Deny };
class Permission;
// Permission(Config cfg) — Config{tool_name, description, show_always_allow}
// Permission(std::string tool_name, std::string description)
Key methods: result(), set_result(PermissionResult).
Permission perm("bash", "rm -rf node_modules && npm install");
auto ui = perm.build();
// renders: [y] allow [n] deny key hints
Status Bar Widgets¶
The streaming-status family renders the single-line status bar an agent shows
while a turn is in flight: a leading breadcrumb / phase chip on the left, the
live token-rate spark in the middle, and the model badge + context gauge on the
right. StatusBar composes them; the pieces are also usable standalone.
StatusBar¶
Three-row status bar: a top/bottom PhaseAccent rail sandwiching a
hard-locked single-line activity row. Each activity-row piece drops (widest
*_min_width first) as the terminal narrows, so the row is always exactly one
line and the phase chip — what's happening now — is the last survivor. When
status_banner.text is set the middle row becomes a full-width toast instead.
Header: widget/status_bar.hpp
StatusBar::Config cfg;
cfg.phase_color = Color::cyan();
cfg.breadcrumb = { .title = "maya › docs" };
cfg.phase = { /* verb, elapsed, breathing */ };
cfg.token_stream = { .rate = 23.4f, .history = hist, .live = true,
.adaptive = true };
cfg.model_badge = ModelBadge("claude-opus-4").build();
cfg.context = { .used = 42000, .max = 200000 };
auto ui = StatusBar{cfg}.build();
TokenStreamSparkline¶
Compact ⚡ rate t/s ▁▂▃▄▅▆▇█ chip for the status bar: a fixed-width tok/s
rate, a right-pinned block-glyph sparkline of the rate history, coloured by
rate (red / yellow / green). Every segment is a fixed display width so ticking
numbers never shove the neighbouring chips leftward. When live is false the
rate + spark dim, signalling “frozen at last sample” during tool execution.
Header: widget/token_stream_sparkline.hpp
struct Config {
float rate = 0.0f; // current tok/s
int total = 0;
std::vector<float> history; // rate samples, newest last
Color color = Color::cyan();
bool live = false; // false = dim (frozen)
bool adaptive = false;
int max_spark_cells = 64;
};
maya::TokenStreamSparkline{{
.rate = 23.4f,
.history = rate_history,
.color = Color::cyan(),
.live = is_streaming,
}}.build();
Adaptive width. With .adaptive = true the widget returns a grow(1)
component that defers the spark-cell count to paint time and sizes the
sparkline to fill its allocated slot — the status bar hands it the dead space
between the phase chip and the model badge. The count is clamped to
[8, max_spark_cells] and right-pinned so the newest sample stays glued to the
chips on its right. With .adaptive = false (default) the layout is the legacy
fixed 16-cell chip, byte-identical to before.