HarveyandClaude Opus 5 8364fe2141 Seconds per clock, and type that scales with the bar height
Seconds move from one bar-wide switch to a per-clock setting. A clock's own
`showSeconds` wins, the top-level one is the default for clocks with no
opinion, and the centre clock gets `local.showSeconds` since it has no entry in
`clocks[]`. An explicit false on a clock beats a true default, so the check is
for the property's presence rather than its truthiness.

The tick rate follows what is actually displayed: `anyShowsSeconds()` decides
between a per-second and a per-minute SystemClock, so a bar showing no seconds
does not wake the shell sixty times as often to redraw an identical minute.

`barSize` is now editable from the popup, and everything drawn on the bar is
sized against the theme's own bar height rather than pinned to the theme's
12px type. A taller bar carries proportionally larger text, gaps and rule; at
the theme's own height the scale is exactly 1, so nothing moves. Heights are
clamped to 12-120 rather than rejected, so a value the editor can produce can
never be one the config layer silently discards.

Fixes a latent bug this surfaced: updateRow() rebuilt a draft row from a fixed
pair of fields, so editing a clock's label would have thrown away its own
seconds setting. It now copies every key the row had.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MhxtL7WzL5XmuTd8GHbcTY
2026-09-04 18:02:36 +02:00

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:

omarchy plugin add <git-url-of-this-repo> --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:

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:

{
  "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

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=<zone> 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:

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
S
Description
No description provided
Readme
106 KiB
Languages
QML 59.2%
JavaScript 40.8%