App Studio is coming soon: it is in development and testing and is not part of release 3.0. This reference describes the SDK as it is being built and may change before release.
Generated from the SDK sources by scripts/appstudio-sdk-docs.cjs for runtime-v1 — do not edit; run npm run appstudio:docs:sdk.
@maestrohub/app-ui
The App Studio UI kit: layout, display, input, table and chart components.
Runtime: runtime-v1. 53 exports (37 values, 16 types). Import with:
import { ... } from '@maestrohub/app-ui';
Hooks
usePalette
function usePalette(): Palette
usePalette returns the kit colours for the current theme — the host's own surface / ink / muted / border / subtle / primary / danger when the shell sent them, the kit's palette otherwise.
useTheme
function useTheme(): ThemeMode
useTheme returns the live theme mode the shell set (light when unset).
Components
Badge
function Badge({ tone, children }: { tone?: "ok" | "warn" | "critical" | "neutral"; children?: ReactNode; }): JSX.Element
A small rounded pill coloured by tone (default neutral) from the palette's badge pairs.
BarChart
function BarChart({ items, height, unit, max }: { items: { label: string; value: number; tone?: Tone; }[]; height?: number; unit?: string; max?: number; }): JSX.Element
A responsive SVG bar chart of items (label, value, optional tone).
The scale tops out at max or the largest value; negative values are
drawn as zero-height bars, labels longer than 12 characters are
truncated. Renders "No data." for an empty list.
Button
function Button({ intent, disabled, reason, confirm, onClick, children }: ButtonProps): JSX.Element
The kit's action button. When disabled and reason are both set the
reason renders as the button's title and as a caption beneath it, so
a dead control always says why. With confirm, the first click swaps
the label for the confirm text and shows a Cancel button; only the
second click fires onClick.
Card
function Card({ title, children }: { title?: string; children?: ReactNode; }): JSX.Element
A bordered, themed panel with an optional title heading above
children. Adds a bottom margin so stacked cards space themselves.
CheckboxField
function CheckboxField({ label, checked, onChange, disabled }: { label: string; checked: boolean; onChange: (v: boolean) => void; disabled?: boolean; }): JSX.Element
A checkbox with its label inline; reports onChange(boolean). Unlike the other fields it has no hint.
DateField
function DateField({ label, value, onChange, hint, disabled, withTime }: { label: string; value: string; onChange: (iso: string) => void; hint?: string; disabled?: boolean; withTime?: boolean; }): JSX.Element
DateField / DateTimeField take and return ISO strings (empty = unset).
EmptyState
function EmptyState({ title, description, action }: { title: string; description?: string; action?: ReactNode; }): JSX.Element
A dashed-border placeholder for "nothing here yet": bold title, muted description, and an optional action slot below.
ErrorNotice
function ErrorNotice({ error, title, onRetry, retryLabel }: ErrorNoticeProps): JSX.Element | null
Renders a caught error as an alert: the title (or the platform code
when no title is given), the reason, the remediation when the platform
supplied one, and a Retry button when onRetry is set. Understands
ApiError, LiveError, shell refusals and plain errors via
describeError; renders nothing for null/undefined.
Example
const t = useTopic('plant/line1/oee');
return t.error ? <ErrorNotice error={t.error} /> : <MetricCard label="OEE" value={t.value} />;
FilterBar
function FilterBar({ children, actions }: { children?: ReactNode; actions?: ReactNode; }): JSX.Element
FilterBar is the horizontal strip that holds a report's parameters.
Gauge
function Gauge({ value, max, threshold, unit }: { value: number; max?: number; threshold?: number; unit?: string; }): JSX.Element
A large numeric readout (value + unit, default %) over a
horizontal fill bar sized by value / max (default max 100, clamped to
0–100 %). When threshold is set and value is below it, both the
number and the bar turn the danger colour.
Grid
function Grid({ columns, gap, minColumnWidth, children }: { columns?: number; gap?: number; minColumnWidth?: number; children?: ReactNode; }): JSX.Element
Grid lays children out in columns equal columns (1 on narrow screens).
KeyValueList
function KeyValueList({ items }: { items: { key: string; value: ReactNode; }[]; }): JSX.Element
A two-column definition list (dl) of items; each item's key is the muted term and value the description. Keys must be unique — they are the React keys.
Loading
function Loading({ label }: { label?: string; }): JSX.Element
A muted one-line status text (role="status"), default "Loading…". No spinner.
MetricCard
function MetricCard({ label, value, unit, delta, tone, hint }: { label: string; value: ReactNode; unit?: string; delta?: number; tone?: Tone; hint?: string; }): JSX.Element
A KPI tile: uppercase label, a large value with optional unit,
and an optional hint line. tone colours the value (ok / critical);
delta renders a badge with an up/down arrow whose tone is derived
from the sign of the delta, not from tone.
NumberField
function NumberField({ label, value, onChange, min, max, step, unit, hint, disabled }: { label: string; value: number | null; onChange: (v: number | null) => void; min?: number; max?: number; step?: number; unit?: string; hint?: string; disabled?: boolean; }): JSX.Element
A labelled type="number" input. value is number | null; an empty
field reports null, anything else Number(text). unit is appended
to the label as "(unit)"; min/max/step pass through to the input.
Page
function Page({ title, subtitle, actions, children }: { title?: string; subtitle?: string; actions?: ReactNode; children?: ReactNode; }): JSX.Element
The top-level app frame: a centred column (max 1400 px) with the kit
font and ink colour. Renders a header with title (h1), subtitle
and right-aligned actions only when a title or actions are given.
Row
function Row({ gap, align, wrap, children }: { gap?: number; align?: CSSProperties["alignItems"]; wrap?: boolean; children?: ReactNode; }): JSX.Element
A horizontal flex row of children; gap (12), align (center) and wrap (true) map to the flex properties.
Section
function Section({ title, description, actions, children }: { title?: string; description?: string; actions?: ReactNode; children?: ReactNode; }): JSX.Element
A titled block inside a Page: an h2 title, muted description,
and actions on the right, above children. The header row is
omitted when neither title nor actions are set.
SelectField
function SelectField({ label, value, onChange, options, placeholder, hint, disabled }: { label: string; value: string; onChange: (v: string) => void; options: SelectOption[]; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element
A labelled select over options. When placeholder is set it is
rendered as a leading option with an empty value, so choosing it
reports ''.
Sparkline
function Sparkline({ values, width, height, tone }: { values: number[]; width?: number; height?: number; tone?: Tone | "primary"; }): JSX.Element
An inline, unlabelled SVG line of values scaled to its own min/max
(width 120 × height 28 by default). Non-finite values are dropped;
fewer than two remaining renders an em dash instead.
Stack
function Stack({ children, gap }: { children?: ReactNode; gap?: number; }): JSX.Element
A vertical flex column of children separated by gap pixels (default 12).
StatusDot
function StatusDot({ tone, label }: { tone?: Tone; label?: string; }): JSX.Element
A 10 px coloured dot for tone (default neutral) followed by an optional label.
Table
function Table({ columns, rows, rowKey, pageSize, filterable, sort: initialSort, exportName, emptyText, onRowClick, caption }: TableProps): JSX.Element
The data table: client-side text filter (filterable), click-to-sort
headers (numeric when both values parse as numbers, else locale string
compare), pagination by pageSize, and CSV export of every filtered
and sorted row via the shell when exportName is set. An export the
shell or the size cap refuses is shown inline as an ErrorNotice.
Cells render through formatCell.
Tabs
function Tabs({ items, initial }: { items: TabItem[]; initial?: string; }): JSX.Element
A tab strip over a single panel. Holds the active id in local state,
starting at initial or the first item; only the active item's
content is rendered. Uses tablist/tab/tabpanel roles.
Text
function Text({ tone, size, children }: { tone?: "body" | "muted" | "strong"; size?: "sm" | "md" | "lg"; children?: ReactNode; }): JSX.Element
Text is the kit's paragraph: the one place body copy is styled.
TextArea
function TextArea({ label, value, onChange, rows, placeholder, hint, disabled }: { label: string; value: string; onChange: (v: string) => void; rows?: number; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element
A labelled multi-line text input (rows default 3, vertically resizable). Controlled via value / onChange(string).
TextField
function TextField({ label, value, onChange, placeholder, hint, disabled }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element
A labelled single-line text input. Controlled: value in, onChange(string) out; optional placeholder, hint caption and disabled.
TimeRangeField
function TimeRangeField({ label, value, onChange }: { label?: string; value: TimeRange; onChange: (r: TimeRange) => void; }): JSX.Element
TimeRangeField: the "last 24 h / last 7 d / custom" control every report needs.
TimeSeriesChart
function TimeSeriesChart({ series, bands, height, yLabel, yMin, yMax, unit }: { series: Series[]; bands?: Band[]; height?: number; yLabel?: string; yMin?: number; yMax?: number; unit?: string; }): JSX.Element
A responsive SVG line chart of one or more Series with optional
shaded bands, four y grid lines, a legend, and the first and last x
values as axis labels (formatted as times when any x is not a number).
The y range auto-fits the data plus the bands unless yMin/yMax are
given; point markers are drawn only for series with 60 points or
fewer. Renders "No data." when there are no points.
Timeline
function Timeline({ items }: { items: { at: string; title: ReactNode; detail?: ReactNode; tone?: Tone; }[]; }): JSX.Element
Timeline lists dated events newest first — the log/handover/alarm history shape every operations app ends up drawing.
Functions
describeError
function describeError(error: unknown): ErrorDescription | null
describeError turns any caught value into {reason, remediation?, code?}. Exported so an app (or a test) can read the explanation without the DOM.
formatCell
function formatCell(value: unknown, column: Column, row: TableRow, context: { columnValues: number[]; }, dark: boolean): { text: string; node?: ReactNode; style?: React.CSSProperties; }
Applies a column's CellFormatter to one value and returns the plain
text (used for filtering, sorting and CSV), an optional node to
render instead of the text (badges, progress bars), and an optional
cell style (heatmap background). context.columnValues supplies the
column's numeric values for the percentile heatmap; dark picks the
heat scale. Non-numeric input to a numeric formatter renders "—".
Example
formatCell(0.8734, { key: 'oee', label: 'OEE', format: { kind: 'number', digits: 1, unit: '%' } }, row, { columnValues: [] }, false)
// → { text: '0.9 %' }
heatColor
function heatColor(t: number, dark: boolean): string
heatColor maps 0..1 to a cool→warm scale readable on both themes.
percentileOf
function percentileOf(v: number, all: number[]): number
percentileOf returns the fraction of all that is ≤ v (0..1).
toCSV
function toCSV(columns: Column[], rows: TableRow[], format: (value: unknown, column: Column, row: TableRow) => string): string
toCSV renders rows with the columns' formatted text (RFC 4180 quoting).
Types
Band
interface Band
A horizontal shaded band on a TimeSeriesChart (a target range, an alarm limit).
Members
from:number— Lower y bound.label?:string | undefined— Text drawn at the band's top-right.to:number— Upper y bound.tone?:Tone | undefined— Fill colour (defaultok).
ButtonProps
interface ButtonProps
Props for Button.
Members
children?:React.ReactNode— Button label.confirm?:string | undefined— confirm asks before firing — the G3 rung-2 affordance.disabled?:boolean | undefined— Disables the control; pair withreasonso the button explains itself.intent?:"neutral" | "primary" | "danger" | undefined— Colour intent:primary(accent),danger, orneutral(subtle fill). Defaultprimary.onClick?:(() => void) | undefined— Fired on click — after the confirm step whenconfirmis set; never while disabled.reason?:string | undefined— reason renders the disabled explanation (FR-42's honest-UI half): a disabled control always says why (house rule 36).
CellFormatter
type CellFormatter = {
kind: 'number';
digits?: number;
unit?: string;
} | {
kind: 'time';
} | {
kind: 'badge';
map?: Record<string, Tone>;
} | {
kind: 'percentile-heatmap';
invert?: boolean;
} | {
kind: 'threshold';
bands: {
max: number;
tone: Tone;
}[];
above?: Tone;
} | {
kind: 'category-color';
map: Record<string, Tone>;
} | {
kind: 'compare';
baseline: string;
percent?: boolean;
digits?: number;
} | {
kind: 'progress-bar';
max: number;
tone?: Tone;
};
The formatter catalogue — closed, schema-shaped, pure.
Column
interface Column
One column of a Table.
Members
align?:"left" | "right" | undefined— Cell alignment; when omitted numeric cells align right, others left.format?:CellFormatter | undefined— Declarative cell formatter; plainString(value)when omitted.key:string— The row property to read; also the sort key.label:string— Header text.sortable?:boolean | undefined— Set tofalseto make the header inert; every other value keeps click-to-sort on.width?:string | number | undefined— Header width, passed to thethstyle.
ErrorDescription
interface ErrorDescription
The rendered explanation of an error, independent of the DOM.
Members
code?:string | undefined— The platform's code (APP_RATE_LIMITED, ACTION_NOT_DECLARED, …).reason:string— The headline: what was refused or what failed.remediation?:string | undefined— What to do next, when the platform said.retryAfterSeconds?:number | undefined— Seconds the platform asked the app to wait (429 only).
ErrorNoticeProps
interface ErrorNoticeProps
Props for ErrorNotice.
Members
error:unknown— Whatever was caught;null/undefinedrenders nothing.onRetry?:(() => void) | undefined— Offers a "Retry" button; the notice does not retry by itself.retryLabel?:string | undefined— Label for the retry button (default "Retry").title?:string | undefined— Optional heading above the explanation.
Palette
interface Palette
The kit's colour set for one ThemeMode, returned by usePalette.
Members
badge:Record<"critical" | "ok" | "warn" | "neutral", CSSProperties>— Background + text pair perTone, asBadgerenders them.border:string— Borders and rules.chart:string— Data accent for charts and sparklines — the kit's own, never the host's button colour (a shell whose primary is near-white on dark would draw every series in white).danger:string— Critical / error colour.ink:string— Primary text.muted:string— Secondary text.ok:string— Positive colour.primary:string— Accent (primary buttons, active tab). The host's button colour when tokens were sent.primaryInk:string— Text ON the accent — the host's--primary-foregroundwhen tokens were sent. Never hardcode white onprimary: the shell's default dark theme uses an inverted-mono accent (near-white primary, dark navy text), so white-on-primary renders an unreadable button.subtle:string— Subtle fill (table header, neutral button, track backgrounds).surface:string— Card / control background.
Point
interface Point
One sample in a Series.
Members
x:string | number | Date— A number, or a date / date string (parsed withnew Date); mixing kinds makes the chart treat the axis as time.y:number | null— The value;nullleaves a gap in the line.
SelectOption
interface SelectOption
One choice in a SelectField: the value reported by onChange and the label shown.
Members
label:string— Text shown in the dropdown.value:string— Reported byonChangewhen chosen; also the React key, so values must be unique.
Series
interface Series
One line in a TimeSeriesChart.
Members
dashed?:boolean | undefined— Draw the line dashed.name:string— Legend label; also the React key, so names must be unique.points:Point[]— The samples, in x order.tone?:Tone | "primary" | undefined— Line colour (defaultprimary).
TabItem
interface TabItem
One tab for Tabs: a stable id, the button label, and the panel content.
Members
content:React.ReactNode— Rendered in the panel while the tab is active.id:string— Identifies the tab;Tabs.initialrefers to it.label:string— Text on the tab button.
TableProps
interface TableProps
Props for Table.
Members
caption?:React.ReactNode— Muted text in the toolbar, left of the filter.columns:Column[]— Column definitions, in display order.emptyText?:string | undefined— Shown in the body when no rows are visible (default "No rows.").exportName?:string | undefined— Export: the file name (CSV of ALL rows after filter+sort, not the page).filterable?:boolean | undefined— Text filter over every displayed cell.onRowClick?:((row: TableRow) => void) | undefined— Makes rows clickable and receives the clicked row.pageSize?:number | undefined— Rows per page (default 25); a pager appears when there is more than one page.rowKey?:string | undefined— Stable row id — a column key (default: the row index).rows:TableRow[]— All rows; the table filters, sorts and pages them itself.sort?:{ key: string; dir: "asc" | "desc"; } | undefined— Initial sort.
TableRow
type TableRow = Record<string, unknown>;
One data row: a plain object keyed by Column.key. Cell values are formatted by formatCell.
ThemeMode
type ThemeMode = 'light' | 'dark';
The two colour modes; read from html[data-theme], which the SDK runtime stamps from the shell.
TimeRange
interface TimeRange
An ISO-8601 from / to pair, as TimeRangeField reads and reports it.
Members
from:string— Start instant, ISO-8601.to:string— End instant, ISO-8601.
Tone
type Tone = 'ok' | 'warn' | 'critical' | 'neutral';
The semantic colour a status-bearing component maps to a palette colour (ok, warn, critical, neutral).