Widgets
Tachikoma includes dozens of widgets covering text display, input controls, data visualization, navigation, and containers. All widgets follow the render protocol: render(widget, area::Rect, buf::Buffer).
Value Protocol
Many widgets support a unified value interface:
value(widget) # get the widget's current value
set_value!(widget, v) # set the widget's value
valid(widget) # check if the current value is valid (default: true)Text Display
Block
Bordered panel with optional title. The workhorse container widget:
block = Block(; title="Panel", border_style=tstyle(:border),
title_style=tstyle(:title, bold=true), box=BOX_ROUNDED)
inner = render(block, area, buf) # returns inner Rect after drawing borders
Box styles: BOX_ROUNDED, BOX_HEAVY, BOX_DOUBLE, BOX_PLAIN.
Paragraph
Styled text with wrapping and alignment:
para = Paragraph([
Span("Bold text ", tstyle(:text, bold=true)),
Span("and dim text", tstyle(:text_dim)),
]; wrap=word_wrap, alignment=align_center)
render(para, area, buf)
para = Paragraph([Span("Hello ", tstyle(:text)), Span("world", tstyle(:accent))])
paragraph_line_count(para, 40) # count wrapped lines for a given widthWrap modes: no_wrap, word_wrap, char_wrap. Alignment: align_left, align_center, align_right.
Span
Inline styled text fragment, used inside Paragraph and StatusBar:
Span("text", tstyle(:primary, bold=true))
BigText
Large block-character text (5 rows tall):
bt = BigText("12:34"; style=tstyle(:primary, bold=true))
render(bt, area, buf)
intrinsic_size(BigText("12:34")) # (width, height) in terminal cellsStatusBar
Full-width bar with left and right aligned spans:
render(StatusBar(
left=[Span(" Status: OK ", tstyle(:success))],
right=[Span("[q] quit ", tstyle(:text_dim))],
), area, buf)
Separator
Visual divider line:
render(Separator(), area, buf)
Input Widgets
TextInput
Single-line text editor with optional validation:
input = TextInput(; text="initial", label="Name:", focused=true,
validator=s -> length(s) < 2 ? "Min 2 chars" : nothing)
handle_key!(input, evt) # returns true if consumed
text(input) # get current text
set_text!(input, "new") # set text
value(input) # same as text()
valid(input) # true if validator returns nothingThe validator function receives the current text and returns nothing (valid) or an error message string.
TextArea
Multi-line text editor:
area = TextArea(; text="", label="Bio:", focused=true)
handle_key!(area, evt)
handle_mouse!(area, evt, rect)
text(area)
set_text!(area, "multi\nline")CodeEditor
Syntax-highlighted code editor:
CodeEditor(; text="function greet(name)\n println(\"Hello, \$name!\")\nend",
focused=true, block=Block(title="editor.jl", border_style=tstyle(:border),
title_style=tstyle(:title)))
handle_key!(editor, evt)
editor_mode(editor) # current mode symbolSupports Julia syntax highlighting with token types: token_keyword, token_string, token_comment, token_number, token_plain.
Checkbox
Boolean toggle:
cb = Checkbox("Enable notifications"; focused=false)
handle_key!(cb, evt) # space toggles
value(cb) # true/false
set_value!(cb, true)RadioGroup
Mutually exclusive selection:
rg = RadioGroup(["Admin", "Editor", "Viewer"])
handle_key!(rg, evt) # up/down + space/enter to select
value(rg) # selected index (Int)
set_value!(rg, 2)DropDown
Select from a dropdown list:
dd = DropDown(["Tokyo", "Berlin", "NYC", "London"])
handle_key!(dd, evt) # enter opens, up/down navigates, enter selects
value(dd) # selected index (Int)Calendar
Date picker widget:
cal = Calendar(2026, 2; today=19)
render(cal, area, buf)
Selection & Navigation
SelectableList
Keyboard and mouse navigable list:
list = SelectableList(["Alpha", "Beta", "Gamma", "Delta", "Epsilon"];
selected=1, focused=true,
block=Block(title="Items"),
highlight_style=tstyle(:accent, bold=true),
marker=MARKER)
handle_key!(list, evt)
value(list) # selected index
set_value!(list, 2)
list_hit(list, x, y, area) # hit test → index or nothing
list_scroll(list, lines) # scroll by n linesWith styled items:
items = [ListItem("Item 1", tstyle(:text)),
ListItem("Item 2", tstyle(:warning))]
list = SelectableList(items; selected=1)TreeView / TreeNode
Hierarchical tree display:
root = TreeNode("Root", [
TreeNode("Child 1", [
TreeNode("Leaf A"),
TreeNode("Leaf B"),
]),
TreeNode("Child 2"),
])
tree = TreeView(root; block=Block(title="Tree"))
render(tree, area, buf)
handle_key!(tree, evt) # up/down navigate, enter expand/collapseTabBar
Tab switching:
tabs = TabBar(["Overview", "Details", "Settings"]; active=2)
render(tabs, area, buf)
handle_key!(tabs, evt) # left/right to switch
value(tabs) # selected tab indexModal
Confirmation dialog:
modal = Modal(; title="Delete?", message="This cannot be undone.",
confirm_label="Delete", cancel_label="Cancel",
selected=:cancel)
render(modal, area, buf)
Data Visualization
Sparkline
Mini line chart from a data vector:
Sparkline(data; style=tstyle(:accent))
Gauge
Progress bar (0.0 to 1.0):
Gauge(progress;
filled_style=tstyle(:primary),
empty_style=tstyle(:text_dim, dim=true),
tick=tick)
BarChart
Bar chart with labeled entries:
entries = [BarEntry("CPU", 65.0), BarEntry("MEM", 42.0), BarEntry("DSK", 78.0)]
render(BarChart(entries; block=Block(title="Usage")), area, buf)
Chart
Line and scatter plots with multiple data series:
cpu_data = Float64[0.3 + 0.2 * sin(i * 0.3) for i in 1:30]
mem_data = Float64[0.5 + 0.1 * cos(i * 0.2) for i in 1:30]
series = [
DataSeries(cpu_data; label="CPU", style=tstyle(:primary)),
DataSeries(mem_data; label="Mem", style=tstyle(:secondary)),
]
render(Chart(series; block=Block(title="System")), area, buf)
Chart types: chart_line, chart_scatter.
Table
Simple row/column table:
headers = ["Name", "Status", "CPU"]
rows = [["nginx", "running", "12%"],
["postgres", "running", "8%"]]
render(Table(headers, rows;
block=Block(title="Processes"),
header_style=tstyle(:title, bold=true),
row_style=tstyle(:text),
alt_row_style=tstyle(:text_dim)), area, buf)
DataTable
Sortable, filterable data table with pagination:
dt = DataTable([
DataColumn("Name", ["Alice", "Bob", "Carol"]),
DataColumn("Score", [95, 82, 91]; align=col_right),
DataColumn("Grade", ["A", "B", "A"]; align=col_center),
]; selected=1)
render(dt, area, buf)
Sort directions: sort_none, sort_asc, sort_desc. Column alignment: col_left, col_right, col_center.
With the Tables.jl extension, DataTable accepts any Tables.jl source:
using Tables
dt = DataTable(my_dataframe)Containers & Control
Form / FormField
Multi-field form with focus navigation and validation:
form = Form([
FormField("Name", TextInput(; validator=s -> isempty(s) ? "Required" : nothing);
required=true),
FormField("Bio", TextArea()),
FormField("Notify", Checkbox("Enable notifications")),
FormField("Role", RadioGroup(["Admin", "Editor", "Viewer"])),
FormField("City", DropDown(["Tokyo", "Berlin", "NYC"])),
]; submit_label="Submit",
block=Block(title="Registration"))
render(form, area, buf)
handle_key!(form, evt) # Tab/Shift-Tab navigation, widget key handling
value(form) # Dict{String, Any} of field label → value
valid(form) # true if all required fields are validButton
Clickable button:
btn = Button("Submit"; style=tstyle(:primary), focused=true)
render(btn, area, buf)
handle_key!(btn, evt) # enter/space triggersScrollPane
Scrollable container for content:
sp = ScrollPane(["Line 1", "Line 2", "Line 3"]; following=true)
push_line!(sp, "new line") # append content
render(sp, area, buf)
handle_mouse!(sp, evt, area) # scrollbar drag + scroll wheelScrollbar
Standalone scrollbar indicator:
sb = Scrollbar(100, 20, 0)
render(sb, area, buf)
ProgressList / ProgressItem
Task status list with status icons:
items = [
ProgressItem("Build"; status=task_done),
ProgressItem("Test"; status=task_running),
ProgressItem("Deploy"; status=task_pending),
]
render(ProgressList(items; tick=tick), area, buf)
Task statuses: task_pending, task_running, task_done, task_error, task_skipped.
FocusRing
Tab/Shift-Tab navigation manager — cycles focus between panes or widgets (see Input & Events for the full example):
ring = FocusRing([widget1, widget2, widget3])
handle_key!(ring, evt)
current(ring)
next!(ring)
prev!(ring)
Container
Group widgets with automatic layout:
container = Container(
[widget1, widget2, widget3],
Layout(Vertical, [Fixed(3), Fill(), Fixed(1)]),
Block(title="Metrics")
)
MarkdownPane
Scrollable CommonMark viewer with styled headings, bold/italic, inline code, code blocks with syntax highlighting, lists, block quotes, and horizontal rules. Requires the markdown extension (enable_markdown() or using CommonMark).
enable_markdown()
pane = MarkdownPane("# Hello\n\n**Bold**, *italic*, `code`.\n\n- Item 1\n- Item 2";
block=Block(title="Docs"))
render(pane, area, buf)
Update content dynamically with set_markdown!:
set_markdown!(pane, "# Updated\n\nNew content here.")Supports keyboard scrolling (↑/↓/Page Up/Page Down) and mouse wheel. Automatically reflows text when the render width changes.