# Clock bar A second bar along the bottom edge of every screen, carrying world clocks laid out as **one timeline**: furthest behind your local time at the far left, furthest ahead at the far right, your own time dead centre. ``` LA 08:24 NYC 11:24 LDN 16:24 17:24 SG 23:24 TOK 00:24 +1 AUS 01:24 +1 NZ 03:24 +1 ``` An Omarchy 4 shell plugin (Quickshell). It reserves its own space, so Hyprland tiles windows above it exactly as it does for the top bar. ## What it does - **Timeline layout.** Side and position are derived from each zone's live UTC offset, not from config order. Reading left to right always moves forward in time, and a DST shift re-sorts the bar on its own. - **24-hour times**, with `+1` / `-1` against clocks on a different date to you. - **Labels** to the left of each time. The centre clock is your local time and carries no label. - **Dimmed outside working hours**, evaluated in each clock's *own* timezone (weekends included), so a glance tells you who is actually around. - **Seconds per clock.** Any clock can show `HH:MM:SS` on its own, including your local one. The bar only drops to a per-second tick when something is actually displaying seconds. - **Themed.** Colours, fonts, spacing and bar height all come from the active Omarchy theme; it follows a theme switch with no restart. Set your own bar height and the type and spacing scale with it. ## Install On any Omarchy 4 machine: ```bash omarchy plugin add --enable --yes ``` The plugin lands in `~/.config/omarchy/plugins/nerdnation.clockbar/`, and `~/.config/omarchy/clockbar.json` is seeded on first run. To update it later, and to remove it: ```bash omarchy plugin update nerdnation.clockbar omarchy plugin remove nerdnation.clockbar ``` > Plugins run as unsandboxed QML inside `omarchy-shell`, so `omarchy plugin add` > clones disabled by default and wants you to read the code first. `--enable` > skips that. ## Configuring it Two routes, one source of truth — both read and write `~/.config/omarchy/clockbar.json`, and the bar reloads from the file either way. **Click the bar.** Opens an editor with a searchable timezone picker: add, rename, retime and remove clocks, and set the working-hours window. Each row shows that zone's current offset so you can see the effect before saving. **Edit the file.** It hot-reloads on save, no restart: ```json { "version": 1, "clocks": [ { "label": "LA", "timezone": "America/Los_Angeles" }, { "label": "NYC", "timezone": "America/New_York" }, { "label": "SG", "timezone": "Asia/Singapore" }, { "label": "AUS", "timezone": "Australia/Sydney" }, { "label": "NZ", "timezone": "Pacific/Auckland", "showSeconds": true } ], "workingHours": { "start": 8, "end": 18, "workdays": [1, 2, 3, 4, 5] }, "dimOutsideWorkingHours": true, "showSeconds": false, "local": { "showSeconds": false }, "barSize": 0 } ``` | Key | Meaning | |---|---| | `clocks[].label` | Shown left of the time. Defaults to the last part of the zone name. | | `clocks[].timezone` | Any IANA zone (`timedatectl list-timezones`). | | `clocks[].showSeconds` | Seconds on this clock. Omit it to inherit the bar-wide default below. | | `workingHours.start` / `.end` | Hours, 0–24, in each clock's own zone. A window that wraps midnight (`22`–`6`) is understood. | | `workingHours.workdays` | Days counted as working. `0` = Sunday. | | `dimOutsideWorkingHours` | Mute clocks outside that window. | | `showSeconds` | The bar-wide default for seconds, for clocks that do not set their own. | | `local.showSeconds` | Seconds on your local clock, which has no entry in `clocks[]`. | | `barSize` | Bar height in px. `0` follows the theme's. Otherwise 12–120; text and spacing scale with it. | Seconds resolve per clock: a clock's own `showSeconds` wins, otherwise the bar-wide default applies. An explicit `false` on a clock beats a `true` default. The bar ticks once a minute unless something on it is showing seconds — nothing pays for a per-second redraw it does not use. There is no `left` / `right` setting: which side a clock lands on is a fact about its offset, not a preference. **When something is wrong**, the bar says so rather than lying. A zone tzdata cannot resolve renders as `LABEL --:--` in the theme's urgent colour instead of silently falling back to UTC. A `clockbar.json` that will not parse turns the bar's top hairline red and puts `config error` beside your local time, while the last good config keeps ticking — and the broken file is never overwritten. ## IPC ```bash omarchy-shell nerdnation.clockbar edit # open the editor omarchy-shell nerdnation.clockbar toggle omarchy-shell nerdnation.clockbar refresh # re-probe timezone offsets omarchy-shell nerdnation.clockbar reload # re-read clockbar.json ``` Bind the editor to a key in `~/.config/hypr/bindings.lua` if you want one. ## How it works, and why Qt's QML engine ships **no `Intl`**, so there is no way to render an IANA timezone from inside QML — the usual `Intl.DateTimeFormat({ timeZone })` route does not exist. Rather than bundle a copy of the tz database, the plugin asks the system's own tzdata for each zone's current UTC offset (`TZ= date +%z`) and then does the arithmetic itself. That keeps it correct across DST changes and tzdata updates without knowing any of the rules. The probe is one short-lived shell, and its answer stays valid until a transition, so it runs on a slow timer — every ten minutes, on each hour boundary (where transitions land), and whenever the config changes. In between, the clocks advance by arithmetic on the minute tick. `ClockModel.js` holds all of that logic and touches no QML types, so it runs unchanged under node: ```bash node tests/model.test.js ``` ## Layout | File | Role | |---|---| | `manifest.json` | Plugin manifest — `kind: panel`, `keepLoaded` | | `Clockbar.qml` | Entry point: the bottom layer-shell surface, one per screen | | `Editor.qml` | The click-to-edit popup | | `Config.qml` | `clockbar.json` — read, watch, write | | `Zones.qml` | Timezone offset probe and the zone list for the picker | | `ClockModel.js` | Ordering, formatting, day offsets, working hours (pure JS) | | `tests/model.test.js` | Unit tests for the above |