Widgets

Schema reference

Every node, modifier, primitive, and field in the widget YAML DSL.

The widget YAML schema is a recursive tree of nodes and modifiers, decoded by the runner and rendered through SwiftUI at runtime. Each *.yaml or *.yml file in ~/Library/Application Support/ApexDock/widgets/ is one widget.

Top-level entry

yaml
id: <string>                      # optional; defaults to filename without .yaml/.yml
command: <string>                 # required, shell command (/bin/sh -c)
interval: <seconds>               # default 60, clamped to >= 1
order: <int>                      # default 0, lower = leftmost
enabled: <bool>                   # default true
location: <string>                # tray | pinned | unpinned | agent | leading | trailing
tooltip: <string>                 # cell hover text
click:                            # cell click action (one of)
  shell: <string>
  url: <string>
  palette: <string>
symbol: <string>                  # SF Symbol — legacy mode (ignored when `view` is set)
icon: <string>                    # path / data: URL — legacy mode
tint: <string>                    # legacy mode
label: <string>                   # legacy mode
output: label | json              # legacy mode (ignored when `view` is set)
history:                          # ring buffers exposed under ${history.<field>}
  <field>: <int>                  # capacity (samples)
view: <Node>                      # rich rendering — see Nodes below
hover: <Node>                     # popover content; rendered with no size cap

When view is set, symbol/icon/tint/label/output are ignored. The script's stdout is parsed as JSON and bound into the tree.

Nodes

A node is a YAML mapping with exactly one of the following kind keys, plus any number of modifier siblings.

Layout

KindSpec
hstack{ children, spacing, alignment } — horizontal stack
vstack{ children, spacing, alignment } — vertical stack
zstack{ children, spacing, alignment } — depth stack (overlay)
spacer{ minLength } — flexible space
divider(no spec) — horizontal separator

Stack alignment values:

  • hstack: top, center (default), bottom, firstTextBaseline, lastTextBaseline
  • vstack: leading, center (default), trailing
  • zstack: topLeading, top, topTrailing, leading, center (default), trailing, bottomLeading, bottom, bottomTrailing

Display primitives

KindSpec
text{ content, size, weight, design, color, lineLimit, monospacedDigit }
symbol{ name, size, weight, color, renderingMode }
image{ path, width, height }
gauge{ value, min, max, tint, style, width, height }
progress{ value, min, max, tint, background, width, height, cornerRadius }
sparkline{ values, min, max, tint, width, height, lineWidth, fillOpacity }
shape{ kind, fill, stroke, lineWidth, width, height, cornerRadius }

Control flow

KindSpec
forEach{ in, template, stack, spacing, alignment, limit } — iterate an array
if{ when, then, else } — branch on condition

See Control flow for details.

Modifiers

Modifiers apply to the node they're declared on. All optional, all sibling fields of the kind key:

FieldTypeEffect
padding{ all?, horizontal?, vertical?, top?, bottom?, leading?, trailing? }Edge insets in pt
backgroundstringColor (#RRGGBB, named) or material (regular, thin, ultraThin)
cornerRadiusnumberCorner radius in pt
frame{ width?, height?, minWidth?, minHeight?, maxWidth?, maxHeight?, alignment? }Fixed/min/max size
colorstringForeground color (applies via foregroundStyle)
tooltipstringNative macOS hover help; supports ${} interpolation
opacitynumber0–1
onTap{ shell|url|palette }Click action; URL supports ${} interpolation
showIfstringVisibility predicate (same grammar as if.when)

Spec field details

TextSpec

FieldTypeNotes
contentstring (required)Supports ${} interpolation. Multi-binding strings welcome.
sizenumberpt
weightstringultralight, thin, light, regular, medium, semibold, bold, heavy, black
designstringdefault, rounded, monospaced, serif
colorstring#hex or named
lineLimitintDefaults to 1
monospacedDigitboolForces tabular figures so digits don't jitter

SymbolSpec

FieldTypeNotes
namestring (required)SF Symbol name. Supports ${} interpolation.
sizenumberpt; default 14
weightstringSame set as text
colorstring#hex or named
renderingModestringmonochrome, hierarchical, palette, multicolor

ImageSpec

FieldTypeNotes
pathstring (required)Filesystem path or data:image/...;base64,.... Supports ${} interpolation.
width, heightnumberpt

GaugeSpec

FieldTypeNotes
valuebindable number (required)The current value
minbindable numberDefault 0
maxbindable numberDefault 100
tintstring#hex or named; default accent
stylestringlinear (default) or circular
width, heightnumberLinear default 40×6; circular default 18×18

SparklineSpec

FieldTypeNotes
valuesbindable array (required)[Double] or a binding to one
min, maxbindable numberOptional fixed range; falls back to sample min/max
tintstringDefault accent
widthnumberDefault 40
heightnumberDefault 18
lineWidthnumberDefault 1
fillOpacitynumberDefault 0.18

ProgressSpec

FieldTypeNotes
valuebindable number (required)The current value
minbindable numberDefault 0
maxbindable numberDefault 100
tintstringFilled portion color; default accent
backgroundstringTrack color; default low-opacity primary
width, heightnumberDefault 40×6
cornerRadiusnumberDefault half the rendered height

ShapeSpec

FieldTypeNotes
kindstringroundedRectangle (default), rectangle, circle, or capsule
fillstringFill color; default low-opacity primary
strokestringOptional stroke color
lineWidthnumberDefault 1
width, heightnumberDefaults depend on shape
cornerRadiusnumberRounded rectangle corner radius; default 4

ForEachSpec

FieldTypeNotes
instring (required)Binding to an array, e.g. "${prs}"
templateNode (required)Rendered once per item; binds ${item} and ${index}
stackstringvstack (default), hstack, zstack
spacingnumberStack spacing
alignmentstringStack alignment
limitintCap; useful when source can grow

IfSpec

FieldTypeNotes
whenstring (required)Expression — see Control flow
thenNode (required)Rendered when when is truthy
elseNodeRendered when when is false; absent = render nothing

Bindable values

Numeric and array fields accept either a literal or a binding:

yaml
# Literal
value: 42
values: [1, 2, 3, 4, 5]

# Binding
value: "${pct}"
values: "${history.pct}"

Strings always pass through binding interpolation: "${city}, ${temp_f}°F""SF, 68°F".

Color values

#RRGGBB, #RRGGBBAA, or a named color: red, orange, yellow, green, mint, teal, cyan, blue, indigo, purple, pink, brown, gray, black, white, primary, secondary, accent.

Backgrounds also accept material values: regular, thin, ultraThin.

Click actions

Both top-level click: (cell-wide) and node-level onTap: accept the same shape:

yaml
click:
  shell: "open -a 'Activity Monitor'"

# or
click:
  url: "https://example.com"

# or
click:
  palette: "switch to Work"

Inside onTap:, all three fields support ${} interpolation, so url: "${item.html_url}" works for clickable list rows.

Output modes (legacy view-less widgets)

When view: is not set, the widget falls back to the legacy single-icon-with-label model:

outputBehavior
label (default)First line of stdout becomes the chip label, truncated to 8 chars
jsonstdout is parsed as a full widget descriptor — same shape as a file-drop JSON

These modes are useful when you don't need a rich tree. Most new widgets should use view:.

Limits

  • 32 widgets visible per zone (sorted by order, then id; tail dropped with a console warning).
  • 5-second wall-clock kill on each command tick.
  • Single-process per tick — overlapping ticks are dropped.
  • 8-character chip label cap (legacy mode).

Reload semantics

The runner watches ~/Library/Application Support/ApexDock/widgets/ via FSEvents and reloads direct child *.yaml / *.yml files on save (100ms debounce). YAML files outside that folder, hidden files, nested files, and the old parent widgets.yaml file are ignored. Diffing is by id:

  • New id → schedule + immediate first tick
  • Changed config for same id → restart with new config (preserves history rings if history: matches)
  • Removed id → remove from store, kill any in-flight subprocess

A YAML parse error keeps that file's previous valid widget running, leaves other widget files alone, and emits a red warning chip with the filename and parse error in its tooltip.