Widgets
Every dashboard.toml widget type — required and optional keys, and a working example for each.
Every widget is a [[widgets]] entry
dashboard.toml renders one view per [[widgets]] table. Every entry, of
every type below, needs the same three keys:
Prop
Type
Everything else is type-specific — covered per type below. A widget with an
unknown type, or missing one of its type's required keys, is dropped from
the rendered dashboard and reported by harumi dashboard validate rather than breaking the page.
Beyond widgets
dashboard.toml can also declare reusable datasets, define new KPIs as SQL,
and add clock-driven playback — none of which are covered here. See
Dashboard datasets, metrics, and the clock.
Layout
[layout]
columns = 2[layout] is optional; columns (the grid's column count at the desktop
breakpoint) defaults to 2 when omitted. It only affects widgets that lay out
in the main grid — most types below. Three types opt out of the grid
entirely and always render in a fixed position instead:
kpi-railrenders in a full-width strip above the grid.detailandfilterrender in a narrow sidebar beside it, reacting to (or driving) a selection made by another widget — typically atimeline's row click.
Every other type below renders in the main grid and is full-width, except
metric, which is narrow enough for two to share a row.
KPI tiles
metric
A single KPI tile — a value with an optional delta.
Prop
Type
[[widgets]]
type = "metric"
id = "revenue"
title = "Total revenue"
value_key = "totals.revenue"
format = "currency"kpi-rail
Several KPI tiles in one horizontal row — for a handful of related numbers
that read better side by side than as separate metric widgets competing for
grid columns. Always renders as a full-width strip above the grid, regardless
of where it appears in [[widgets]] declaration order.
Prop
Type
[[widgets]]
type = "kpi-rail"
id = "summary"
title = "Run summary"
items = [
{ label = "Total cost", value_key = "totals.cost", format = "currency" },
{ label = "Makespan", value_key = "totals.makespan", unit = "min" },
{ label = "Utilization", value_key = "totals.utilization", format = "percent" },
{ label = "Setup time", rows_key = "machines", progress_key = "progress" },
]Table
table
A sortable data grid bound to an array of row objects.
Prop
Type
[[widgets]]
type = "table"
id = "breakdown"
title = "Breakdown"
rows_key = "breakdown"
columns = [
{ key = "name", label = "Name" },
{ key = "value", label = "Value" },
]Charts
chart
A line or bar chart, picked by variant — folds the legacy line-chart and
bar-chart types (below) into one, since they only ever differed in that.
Prop
Type
[[widgets]]
type = "chart"
id = "trend"
title = "Objective value over time"
variant = "line"
data_key = "timeseries"
x_key = "label"
series = [{ key = "value", label = "Objective value" }]Timelines & schedules
timeline
A resource-row timeline on a numeric time axis — the current, recommended
type for scheduling/job-shop solver results (one row per machine/resource,
one bar per task). Supersedes gantt-chart (below): interrupted work folds
into one item drawn with gaps, non-working spans render as background bands,
the axis is drag-to-zoom, and a [clock] playhead (see Dashboard datasets,
metrics, and the clock) shows as a now-marker.
Prop
Type
[[widgets]]
type = "timeline"
id = "schedule"
title = "Machine schedule"
items_key = "schedule"
id_key = "op"
regions_key = "breaks"
time_unit = "min"gantt-chart
A resource-row Gantt chart on a numeric time axis — the same shape timeline
covers, kept so dashboards written before timeline existed keep rendering.
Prefer timeline for a new dashboard.
Prop
Type
[[widgets]]
type = "gantt-chart"
id = "schedule"
title = "Machine schedule"
tasks_key = "schedule"
time_unit = "min"Breakdowns
treemap
A flat treemap — one rectangle per row, area proportional to value_key,
labeled from name_key. Good for a breakdown where relative size matters
more than exact values (e.g. cost by machine).
Prop
Type
[[widgets]]
type = "treemap"
id = "cost-breakdown"
title = "Cost by machine"
items_key = "breakdown"
value_key = "cost"
name_key = "machine"heatmap
An entity x time-bucket grid, shaded by intensity — one row per resource, one
column per bucket (e.g. hour of day). A (resource, bucket) pair with no
matching row renders as a distinct unavailable cell rather than a zero.
Prop
Type
[[widgets]]
type = "heatmap"
id = "hourly-cycles"
title = "Cycles per hour"
items_key = "hourly_cycles"
resource_key = "resource"
bucket_key = "bucket"
value_key = "value"Selection & detail
filter
A picker that sets the dashboard's selection — one option per distinct
id_key value in items_key. An alternative to clicking a timeline bar,
for choosing an item by name. Always renders in the sidebar.
Prop
Type
[[widgets]]
type = "filter"
id = "task-filter"
title = "Choose a task"
items_key = "schedule"
id_key = "op"
label_key = "task"detail
Shows the fields of whichever row is currently selected (via a timeline
click, a filter pick, or any other selecting view) — looked up by matching
id_key against the selection id. Always renders in the sidebar.
Prop
Type
[[widgets]]
type = "detail"
id = "task-detail"
title = "Task details"
items_key = "schedule"
id_key = "op"
fields = [
{ key = "task", label = "Task" },
{ key = "resource", label = "Machine" },
]Legacy chart types
line-chart and bar-chart predate chart (above), which folds both into
one type picked by variant. Both are still fully supported — so a dashboard
written before chart existed keeps rendering — but prefer chart for
anything new.
line-chart
A line chart — good for a value trending over time.
Prop
Type
[[widgets]]
type = "line-chart"
id = "trend"
title = "Objective value over time"
data_key = "timeseries"
x_key = "label"
series = [{ key = "value", label = "Objective value" }]bar-chart
A bar chart — good for comparing values across categories.
Prop
Type
[[widgets]]
type = "bar-chart"
id = "breakdown-chart"
title = "Cost by warehouse"
data_key = "breakdown"
x_key = "name"
series = [{ key = "value", label = "Cost" }]Checking your widgets before you commit
harumi dashboard widgets prints this same
reference from your terminal (optionally filtered with --type), and
harumi dashboard validate checks a real dashboard.toml against it —
flagging an unknown type, a missing required key, or (given --against,
--run, or --latest) a dot-path that doesn't resolve against an actual
output.json.