Skip to content

feat(theme): tailTUI theme schema, Omarchy template and live reload - #4

Merged
Phundahl merged 2 commits into
mainfrom
feat/theme-template
Aug 21, 2026
Merged

feat(theme): tailTUI theme schema, Omarchy template and live reload#4
Phundahl merged 2 commits into
mainfrom
feat/theme-template

Conversation

@Phundahl

Copy link
Copy Markdown
Owner

Why

Phase 27 taught the loader to read Omarchy's palette, but left one problem unsolved: some mappings are genuinely undecidable in code.

osaka-jade defines yellow = #459451 — a green. So mapping yellow → Warning makes exit-node markers nearly indistinguishable from the online color. Preferring orange instead breaks retro-82, where orange is the accent. There is no rule that is right for every theme.

The fix is to stop guessing and hand the decision to the person looking at the screen.

What

1. tailTUI's own theme schema. One key per Theme field — mode, primary, secondary, background, surface, surface_bright, border, text, text_dim, warning, error. Tried ahead of both Omarchy schemas, and probed as tailtui.toml before colors.toml within each theme directory, since it is opt-in and therefore the more deliberate statement.

Its keys are deliberately disjoint from the Omarchy vocabularies — only mode/background are shared, and both are excluded from hasMarkers() — so three-way detection stays purely a question of which keys appear, never the filename.

2. contrib/tailtui.toml.tpl. Installed into ~/.config/omarchy/themed/, Omarchy renders it into the active theme directory on every theme switch. That happens to be the same directory the loader already probes, so no new path was needed. The template is fully commented, including the osaka-jade case and how to fix it.

3. Live reload. styles.ThemeStamp() reports the active file's path and mtime; checkThemeCmd rides the existing refresh tick — no fsnotify, no new dependency. Switch your desktop theme and a running tailTUI re-colors within one tick.

The concurrency detail worth reviewing

checkThemeCmd returns the parsed Theme and never calls styles.Apply itself. Apply rewrites package-level vars that View reads, so applying it inside the command's goroutine would race the renderer. Application happens in Update, on the single Elm goroutine.

Two related subtleties:

  • The stamp is recorded even when nothing changed, so a theme file appearing or vanishing is picked up next tick.
  • On change, viewport-backed overlays are rebuilt via resizeOverlay(). The dashboard re-renders every frame, but overlay content is a pre-rendered string with the old colors baked in as ANSI codes.

Compatibility

Zero-config behavior is untouched. With no template installed, colors.toml is read and mapped exactly as before — this is purely additive, and the template is optional on every distro.

Verification

Beyond the unit tests, I rendered the shipped template through Omarchy's own substitution pipeline against the live osaka-jade theme and confirmed the output parses into the expected Theme, with no unsubstituted placeholders.

Note the overlay test forces TrueColor: a TTY-less test strips every SGR, so both renders would compare equal and the assertion would pass vacuously.

Phundahl and others added 2 commits August 21, 2026 10:38
Phase 27 taught the loader to read Omarchy's palette, but some mappings
are genuinely undecidable in code. osaka-jade defines yellow as #459451
-- a green -- so mapping yellow to the warning role makes exit-node
markers nearly indistinguishable from the online color. Preferring
orange instead breaks retro-82, where orange is the accent. There is no
rule that is right for every theme, so hand the decision to the person
looking at the screen.

- Add tailTUI's own theme schema: one key per Theme field, tried ahead
  of both Omarchy schemas, and probed as tailtui.toml before colors.toml
  in each theme directory. Its keys are disjoint from the Omarchy
  vocabularies, so detection stays a question of which keys appear.

- Ship contrib/tailtui.toml.tpl. Installed into ~/.config/omarchy/themed/,
  Omarchy renders it into the active theme directory on every theme
  switch -- the same directory the loader already probes, so no new path
  was needed.

- Reload the palette live. styles.ThemeStamp() reports the active file's
  path and mtime; checkThemeCmd rides the existing refresh tick, so
  there is no watcher dependency. Switching the desktop theme re-colors
  a running tailTUI within one tick.

The command returns the parsed Theme rather than applying it:
styles.Apply rewrites package-level vars that View reads, so applying it
off-thread would race the renderer. Update applies it on the Elm
goroutine, records the stamp even when unchanged (so a theme file
appearing or vanishing is noticed), and rebuilds viewport-backed
overlays, whose content would otherwise keep stale ANSI colors.

Zero-config behavior is unchanged: with no template installed,
colors.toml is read exactly as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Omarchy seds the whole file, comments included, so instructional text
written with {{ placeholders }} got replaced too. The guidance in a
generated tailtui.toml then read "use #a2734b here instead", which does
not tell anyone which placeholder to write.

Name the slots in prose instead. The one {{ placeholder }} left in a
comment is deliberate: no theme defines a key by that name, so it
renders literally and demonstrates the syntax.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Phundahl
Phundahl merged commit f4f95c9 into main Aug 21, 2026
2 checks passed
@Phundahl
Phundahl deleted the feat/theme-template branch August 21, 2026 09:27
@Phundahl Phundahl mentioned this pull request Aug 21, 2026
Phundahl added a commit that referenced this pull request Aug 21, 2026
* chore(release): v1.3.0

Live theme switching and tailTUI's own theme schema (#4).

Also fixes a packaging gap the release surfaced: the README told users
to copy the template from contrib/, which does not exist for anyone who
installed the AUR, .deb or .rpm package. The template is now installed
to /usr/share/tailtui/tailtui.toml.tpl and the README documents both
paths. Any future user-facing file under contrib/ needs the same
treatment or the docs silently break for package users.

version_test.go no longer asserts on the real version string, so a
release bump does not look like it requires a test edit -- the test
covers SetVersion's normalization, not the current version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(release): ship the Omarchy template in the packages

The README now points users at /usr/share/tailtui/tailtui.toml.tpl, but
nothing installed it there -- the docs promised a path that did not
exist in any artifact.

- nfpm installs the template to /usr/share/tailtui/, plus README.md to
  /usr/share/doc/tailtui/.
- The tar.gz carries contrib/tailtui.toml.tpl. Specifying `files`
  replaces goreleaser's defaults, so LICENSE and README are restated.

Verified by building a snapshot release and listing the payloads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: ignore goreleaser dist/ output

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant