Label

A read-only text display. Shows static text or synced dynamic text. Stores a .string value when synced.

Type

"label"

Relevant Fields

Inherits all Control Definition. Key fields:

Field Type Default Description
text string Static display text (overridden by sync)
label string Alternative to text
icon string SF Symbol before text
style string "default" See Label Styles
tint string white Text and icon color (terminal: default text colour over the dark backdrop)
align string "leading" "leading", "center", "trailing"
scrollable bool false Fixed-height scrolling terminal/log view (pair with controlHeight)
formatValue string Value display format (see formatValue table below)
valueMap object Incoming value → display text — see Value maps
iconMap object Incoming value → SF Symbol name
colorMap object Incoming value → hex tint

Value maps

A feed you don't control emits codes — a WMO weather code, a device state, an alarm level — and the label should read as words, not 61. valueMap, iconMap, and colorMap say what each value looks like, declaratively:

"valueMap": { "0": "Clear", "3": "Overcast", "61": "Rain", "default": "—" },
"iconMap":  { "0": "sun.max.fill", "3": "cloud.fill", "61": "cloud.rain.fill" },
"colorMap": { "61": "#0A84FF" }
  • Keys match the stringified value, so 0, "0", true, and "rain" all work from the same map. true/false also match yes/no/on/off/1/0, and a number matches whether it arrives as 61 or 61.00. Key lookup falls back to a case-insensitive match.
  • "default" is the catch-all for any value with no entry.
  • No match and no "default" ⇒ the label degrades gracefully: the raw value for valueMap, the control's own icon/tint for iconMap/colorMap. It never blanks out.
  • A valueMap hit is the text as written — formatValue is not applied on top.
  • The maps apply to the synced value; before the first sync the static text placeholder still shows.

The same three fields work on Image, where valueMap maps to an image URL.

Format Values

Value Output Example
"decimal" "72" or "3.5" Rounds to integer
"decimal:N" "3.14" Fixed N decimal places (e.g., "decimal:2")
"percent" "80%" Integer percentage
"time" "3:45" Minutes:seconds from total seconds
"duration" "1h 23m" Human-readable duration from seconds
"bytes" "1.2 GB" Auto-scaled byte units
"bps" "54 Mbps" Auto-scaled bits-per-second
"suffix:X" "72°F" Append custom suffix (e.g., "suffix:°F")
"none" (hidden) No value display

Styles

Value Font
"default" .subheadline
"headline" .headline.bold
"title" .title2.bold
"caption" .caption
"mono" .subheadline monospaced
"large-mono" .title3 monospaced medium
"terminal" Monospaced ANSI terminal — see Terminal style

Terminal style

style: "terminal" (and, as a shortcut, mono/large-mono + scrollable) renders the text as a real terminal instead of a plain mono label — purpose-built for piping a captured shell/tmux pane to the phone:

  • ANSI styling — colour (16 / 256 / truecolor), bold, dim, italic, underline, and reverse are parsed from the escape codes and rendered. Plain text (no escapes) renders normally.
  • Pane-matched scaling — the font auto-scales so the source's column width exactly fills the view, so box-drawing and ASCII line up like the real pane. If a server prepends a CSI 8;rows;cols t size report, that column count is used; otherwise the widest line is.
  • Dark backdrop — drawn on its own dark background (independent of the app theme) so ANSI colours have the contrast they were designed for.
  • Pinch to zoom — pinch to scale the text past the fitted size; double-tap snaps back to fit. Content wider/taller than the view scrolls both ways.
  • Sizing — set controlHeight for the on-screen height; fontSize caps the fitted font before zoom. Keeps the latest line in view and auto-scrolls on update.

Feed it like any synced label (a .string value); see the tmux-bridge server for a complete example.

Examples

Static info label

{
  "type": "label",
  "id": "temp-display",
  "position": [0, 0],
  "span": [1, 2],
  "text": "Current: 72°F"
}

Synced label with icon

{
  "type": "label",
  "id": "track-artist",
  "position": [1, 2],
  "span": [1, 2],
  "text": "—",
  "icon": "person.fill",
  "tint": "#AAAAAA",
  "sync": [{ "method": "meshsocket", "type": "listen", "event": "broadcast", "filter": { "msg_type": "player_state" }, "valuePath": "artist" }]
}

Headline style

{
  "type": "label",
  "id": "track-title",
  "position": [0, 2],
  "span": [1, 2],
  "text": "No Track",
  "style": "headline",
  "sync": [{ "method": "meshsocket", "type": "listen", "event": "broadcast", "filter": { "msg_type": "player_state" }, "valuePath": "title" }]
}

Terminal snapshot (live pane)

{
  "type": "label",
  "id": "pane-snapshot",
  "position": [1, 0],
  "span": [5, 4],
  "style": "terminal",
  "scrollable": true,
  "controlHeight": 360,
  "text": "(waiting for pane…)",
  "sync": [{ "method": "meshsocket", "type": "listen", "event": "broadcast", "filter": { "msg_type": "snapshot" }, "valuePath": "text" }]
}

Weather code → words, no server

{
  "type": "label",
  "id": "wx-text",
  "position": [0, 0],
  "span": [1, 2],
  "text": "…",
  "style": "headline",
  "align": "center",
  "valueMap": { "0": "Clear", "1": "Mainly clear", "2": "Partly cloudy", "3": "Overcast", "45": "Fog", "51": "Drizzle", "61": "Rain", "71": "Snow", "80": "Showers", "95": "Thunderstorm", "default": "—" },
  "iconMap": { "0": "sun.max.fill", "1": "sun.max.fill", "2": "cloud.sun.fill", "3": "cloud.fill", "45": "cloud.fog.fill", "51": "cloud.drizzle.fill", "61": "cloud.rain.fill", "71": "cloud.snow.fill", "80": "cloud.heavyrain.fill", "95": "cloud.bolt.rain.fill" },
  "sync": [{ "method": "http", "url": "https://api.open-meteo.com/v1/forecast?latitude=42.36&longitude=-71.06&current=weather_code", "interval": 900, "valuePath": "current.weather_code" }]
}

Boolean state as words and colour

{
  "type": "label",
  "id": "pump-state",
  "position": [1, 0],
  "span": [1, 2],
  "text": "—",
  "align": "center",
  "valueMap": { "true": "Running", "false": "Stopped" },
  "iconMap": { "true": "bolt.fill", "false": "bolt.slash" },
  "colorMap": { "true": "#30D158", "false": "#8E8E93" },
  "sync": [{ "method": "meshsocket", "type": "listen", "event": "broadcast", "filter": { "msg_type": "pump" }, "valuePath": "running" }]
}

Behavior

  • When synced, the text field serves as a placeholder until the first sync value arrives
  • Text updates animate with a smooth numeric content transition
  • Labels have no action — they are display-only
  • valueMap/iconMap/colorMap translate the synced value on the way to the screen — see Value maps