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/falsealso matchyes/no/on/off/1/0, and a number matches whether it arrives as61or61.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 forvalueMap, the control's ownicon/tintforiconMap/colorMap. It never blanks out. - A
valueMaphit is the text as written —formatValueis not applied on top. - The maps apply to the synced value; before the first sync the static
textplaceholder 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 tsize 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
controlHeightfor the on-screen height;fontSizecaps 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¤t=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
textfield 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/colorMaptranslate the synced value on the way to the screen — see Value maps
Related
- Control Definition — Base fields
- Image — the same value maps, mapping to symbols and image URLs
- Label Styles — Style variants
- Sync — Live value updates