Skip to content

fe: dress ubuntu, kubuntu, mint and omarchy in their own colours, every linux wore the same theme - #267

Open
landsman wants to merge 13 commits into
mainfrom
worktree-linux-distro
Open

landsman wants to merge 13 commits into
mainfrom
worktree-linux-distro

Conversation

@landsman

@landsman landsman commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Every Linux wore the same theme, in colours no Linux desktop uses. LinuxDistro now tells Ubuntu, Kubuntu, Linux Mint and Omarchy apart, and each can have a stylesheet that Theme::cssMap() loads as soon as the file exists. Ubuntu, Kubuntu and Mint get one here, with colours read from Yaru, Breeze and Mint-Y's sources and no code copied from them. Omarchy's colours come from its current theme instead, read at each page load, and Auto follows that theme's scheme.

The distribution alone cannot decide this, because a look belongs to the desktop, so detection reads os-release and XDG_CURRENT_DESKTOP together. KDE on another distribution and GNOME outside Ubuntu keep the plain Linux look for now. make debug can simulate all four.

A value from colors.toml is six hex digits or it is dropped, since a theme comes from any git repository and lands in an inline <style>. Pinning Light or Dark against the theme's scheme leaves the page out of the theme.

What taking it to a real Omarchy machine cost

The checkout could not be built or debugged there at all, so six fixes ride along. Each was found by hitting it, in this order:

  • A warm cache outlived the version bump. .cache/adminer-src.zip carried no version, so make saw the file and called it up to date: a machine that had built before 6.1.1 kept its 5.5.1 zip forever and make fetch died in the extract, on a path no message explained. The name carries the version now.
  • The build needed a sudo someone ran months ago. webview asks pkg-config for webkit2gtk-4.0, which Ubuntu dropped in 24.04 and Arch never had; the shim onto 4.1 was written into /usr by linux-deps, which also refused any distro without apt. It goes to .cache/pkgconfig now, reached through an exported PKG_CONFIG_PATH — a build artifact, no root — and linux-deps asks whether the headers are installed before it asks which package manager is present, so Arch and Fedora build and are told what to install when they cannot.
  • -debug turned nothing on off macOS. enableInspector was a !darwin stub returning false, so the one instruction this repo gives for debugging — do not guess, open the inspector — had no meaning on Linux. WebKitGTK has an inspector of its own, and -debug now makes it available the way macOS does: available, not open. F12 or Ctrl+Shift+I opens it in a window of its own, taken on the GtkWindow so it works with the caret in a textarea. Console messages reach stdout, and so the log, whether it is open or not — which is what is left when the page dies before you get to it.
  • The window never opened. It is hidden until the loader calls loaderShown, and on WebKitGTK 2.54 nothing from the page reaches Go at all — so every make run there showed no window for the whole run while the server came up, served the page and logged a clean startup behind it. A two-second sync.Once deadline is the floor under that reveal, and says in the log when it fires.
  • kill-orphans was a no-op on Linux. It recognised an orphan by ppid 1, which is never true where systemd --user is a subreaper: it reported "no orphaned frankenphp server" while fourteen of them held fourteen ports, which is the one failure it exists to prevent.
  • The dresscode hook died on every PHP edit there. It started dresscode through PHP_BINARY, which the Linux frankenphp leaves empty and proc_open refuses, so the file was never checked and nothing said so.

All six are Linux-only in effect: the shim and linux-deps sit behind ifeq ($(UNAME_S),Linux), the inspector is one definition per platform (menu_darwin.go / inspector_linux.go / inspector_other.go, which make qa's platform-stub guard enforces), and the reaper set is still just init where there is no systemd.

Two things the real machine then showed

Running it on Omarchy is also the only reason these were found, and both are in here:

  • Save was a lime slab. The dialog's Save is filled in --ad-ok, and a palette replaces that token with the theme's own green — a colour chosen to be read as text on the background, not to be a surface (Tokyo Night's #9ece6a). It now wears what .message wears: the green as text over --ad-ok-bg. Measured across the 23 themes installed on that machine rather than judged on one — that pairing clears 4.5:1 on 19 of them, against 16 for a tint mixed from the theme's own background, which was the other candidate. The four it misses have a muted green and .message has the same contrast there today, so that is one fix for both and not this one.
  • The corners were round on a square desktop. Omarchy's default is rounding = 0 and a theme does not touch it, so the window around this one has hard corners while every panel, input and button inside carried a 7px radius. The two radius tokens carry most of it; the rest is a short list that bypasses them. Discs stay discs.

What is still not checked

The app builds, runs and is debuggable on a real Omarchy machine now, and the page has been looked at there — that is where the two above came from. The Ubuntu, Kubuntu and Mint sheets have still only been seen in Chromium on macOS through the simulation. No source shows what Kubuntu's os-release says, so ubuntu and kubuntu both count. Some window and border colours were worked out by hand from the themes' Sass, as the sheets' comments say. This changes colours only, not sizes, radii or fonts. White on Breeze's and Yaru's accent is low contrast on primary buttons, as it is in those themes.

make qa and make check pass on macOS and on Arch, and make e2e on macOS. tests/cli/OmarchyThemeTest.php covers reading a theme and refusing what is not a colour. No e2e scenario covers the new sheets, and none covers Theme::cssMap() honouring the theme's scheme.

make check used to fail on an Omarchy machine whose theme is dark, and that is fixed here. The boot check posts a light and a dark design and expects both back media-gated, which cannot hold once Auto follows the theme's scheme — so it was green in CI and on macOS, green on main, and red only for whoever ran it on Omarchy, which reads as a regression in whatever that person last touched. Its server now gets HOME pointed at the throwaway data dir it already makes and removes, so every machine answers as CI does; the e2e fixture, which boots the same app for the light and dark theme scenarios, takes the same line.

@landsman landsman changed the title fe: dress ubuntu, kubuntu and mint in their own colours, every linux wore the same theme fe: dress ubuntu, kubuntu, mint and omarchy in their own colours, every linux wore the same theme Nov 1, 2026
@landsman

landsman commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

To check on a real Omarchy machine. Everything so far ran on macOS, in Chromium, with the Omarchy colours pasted into the page by hand, so the whole path from file to page has never run in WebKitGTK.

Detection

  • grep '^ID' /etc/os-release; echo "$XDG_CURRENT_DESKTOP"; ls -d ~/.local/state/omarchy ~/.config/omarchy gives what LinuxDistro::detect expects: a Hyprland desktop and one of the two folders
  • XDG_CURRENT_DESKTOP still reaches frankenphp when the app starts from Omarchy's launcher, not only from a terminal
  • <body> has linux-os omarchy-linux

Colours (make debug, then the Web Inspector does not exist off macOS, so read the page source or Tracy)

  • The page's <style>:root{…} holds the values of ~/.local/state/omarchy/current/theme/colors.toml
  • omarchy-linux.css loads, and the sidebar's names are in the text colour
  • Switching theme reaches the next page load. An open window keeps its colours until then, which is expected for now

Light and dark

  • A dark theme with the system set to light: SQL is readable and selects have their chevron. This is the case that was broken and was not seen again after the fix
  • A light theme, for example mode = "light", looks right, including the sprite icons
  • In Settings, Light against a dark theme leaves the page out of the theme, and Auto returns to it
  • <meta name="color-scheme"> is pinned to the theme's side on Auto

Themes

  • Several themes that ship with Omarchy, and a community one from git: nothing unreadable, and no key missing that leaves a default showing through
  • A theme from before mode existed, marked by a light.mode file, is taken as dark. Fix it in OmarchyTheme::mode if such themes still occur

Decide after seeing it

  • Which palette keys fit which tokens: OmarchyTheme::TOKENS was mapped from one theme's file, without seeing it next to Omarchy's own apps
  • Whether muted should drive the borders, which are still derived from the text colour
  • Refreshing an open window through the theme-set hook. Where a user's hook lives was not read
  • Window chrome under Hyprland, and the sizes, radii and fonts, which this PR does not touch

Not for Omarchy, but open here: Ubuntu, Kubuntu and Mint have not run on a real machine either, and no source shows what Kubuntu's os-release says.

…tlived the bump

.cache/adminer-src.zip carried no version, so make saw the file, called it up to date
and never fetched again. Bumping ADMINER_VERSION then failed in the extract --
"cannot stat .cache/src-tmp/adminer-6.1.1" -- on every machine that had built once
before the bump, with nothing to do but work out that the cache was the problem and
delete it. A bump now names a file that is not there yet, and the zips of versions
we have left behind go with it.
…un months ago

webview is cgo and asks pkg-config for webkit2gtk-4.0, which no current distro ships --
Ubuntu dropped it in 24.04, Arch never had it, and both carry 4.1, the same API over
libsoup3 and what download_linux.go already names. The shim that bridges that went into
/usr with sudo tee from linux-deps, which made `go build` on a working checkout depend on
a command someone had run once, by hand, on that machine: a checkout where it had not
run failed with "Package webkit2gtk-4.0 was not found", pointing at a search path rather
than at the step that fills it. It is a build artifact like any other now, written to
.cache and reached through PKG_CONFIG_PATH, exported because every go command here
compiles the cgo launcher and not only the one that links it.

That also leaves nothing distro-specific but the headers themselves, so linux-deps asks
whether they are installed before it asks which package manager is present: Arch and
Fedora were turned away up front, and one of them is where this was found. Where apt is
missing it now names what to install and stops.
…sole there

The whole of "debug the running app, do not guess" was a macOS instruction: enableInspector
was real only there, and the !darwin stub returned false, so -debug on Linux printed "web
inspector unavailable" and left nothing to debug with -- no console, no DOM, no breakpoints,
on the platform where the app has no menu bar to offer them from either. WebKitGTK has an
inspector of its own; it only needed developer extras turned on and a show(), from an idle
callback because neither the main loop nor the window exists yet at that point.

Closing its window only closes it -- WebKit keeps the inspector alive and show() brings it
back -- but with no Develop menu there was no way to ask again, so F12 and Ctrl+Shift+I
reopen it, taken on the GtkWindow where GTK offers a key press before the focused widget
sees it, so it works with the caret in an SQL textarea. Console messages go to stdout as
well, and so to the log: that is what is left when the page dies before the inspector is up.

The hint in the log is per-platform now rather than naming Safari everywhere, and the two
diagnostics split the same way -- a real describeUIDelegate on Linux answers the question
worth asking there (is the window's child the WebKitWebView every install* hangs off), the
Windows stubs moving to inspector_other.go.
…den the whole run

The window is hidden on purpose until the loader has painted, and the only thing that
brought it back was loaderShown -- a call from the page over a JavaScript binding. That
binding is not guaranteed: on WebKitGTK 2.54 nothing from the page reaches Go at all, so
every run of `make run` on Arch opened no window ever, while the server came up, served
the page and logged a clean startup behind it. Nothing in the log said what was wrong,
because by every measure Go could take the app had started.

A reveal that can fail is not a reveal, so there is a deadline under it now: two seconds,
well past the loader's own paint anywhere it works (759ms at its worst here, cold), and
sync.Once so the good path still wins and this only ever fires where that path is broken --
where it also says so, since a hidden window is the symptom of a dead binding and that is
worth naming rather than quietly papering over.
…e on linux

An orphan was recognised by ppid 1, which is true on macOS and never true on Linux:
systemd --user marks itself a child subreaper, so a server whose launcher was killed is
reparented to that instead. So `make kill-orphans` and the SessionEnd hook that calls it
after every agent session both printed "no orphaned frankenphp server" while fourteen of
them held fourteen ports on this machine -- the exact failure the script exists to prevent,
reported as success. The reaper set is init plus any systemd --user now, and the test
covers a Linux reparent beside the macOS one.
…ARY empty on linux

The dresscode PostToolUse hook started dresscode through PHP_BINARY, which the mac
frankenphp fills in and the linux one leaves as "". proc_open refuses an empty program
name, so every PHP file an agent wrote on Linux ended in an uncaught ValueError: the file
was never checked, and the agent was told nothing -- the convention this hook exists to
enforce was simply not enforced there. bin/frankenphp beside the root is the same binary,
and asking the filesystem rather than the string also keeps phpstan from calling the
check dead code, its stub having PHP_BINARY as non-empty.
…omarchy theme

check.sh posts a light and a dark design and expects both back gated on
prefers-color-scheme, which is how the OS theme picks one. On Omarchy that cannot hold:
LinuxDistro finds ~/.local/state/omarchy, Auto then follows that machine's current theme
rather than the system, and a theme with mode = "dark" pins the page to one side, so only
the dark design comes back and the assertion fails. Nothing was broken -- the check was
reading the desktop it ran on.

That is the dearest shape a failing test has. It stays green in CI and on macOS, where no
such theme exists, and on main, which has no Omarchy code: red for one developer and green
everywhere else reads as a regression in whatever that person last touched, and costs an
investigation to find out it is not one. HOME into the throwaway data dir the script already
makes and removes gives every machine the answer CI gets, on every platform, and leaves the
assertion measuring what it is for -- that switching design works before login. The seam is
the one tests/cli/OmarchyThemeTest.php already uses, and that test covers the Omarchy
reading deterministically, so nothing is lost by keeping it out of here.

The e2e fixture inherits this process's environment and boots the same app for scenarios
that assert the theme light and dark, so it takes the same line.
…ody picked

The dialog's Save is filled in --ad-ok, and an Omarchy palette replaces that token with the
theme's own `green`. A terminal palette's green is chosen to be read as text against the
background, not to be a surface: Tokyo Night's #9ece6a arrives as a lime slab under dark
text, which is the one loud thing on an otherwise quiet window, and every theme whose green
is a terminal green does the same to it.

So Save wears what .message already wears on this side, the same two tokens the rest of the
page pairs: the green as text over --ad-ok-bg. It stays the filled one of the two buttons,
Reset being text and a border over nothing, so the pair still reads at a glance -- which is
the whole reason Save is green and not the accent.

Measured over the 23 themes installed here rather than judged on one: the theme's green on
--ad-ok-bg clears 4.5:1 on 19 of them (8.4:1 on Tokyo Night), against 16 for a tint mixed
from the theme's own background, which was the other candidate. The four it does not clear
are themes with a muted green -- miasma, retro-82 and two light ones -- and .message has
that same contrast there today, so it is one fix for both and not this one.
… at a running daemon

`make debug` brings the e2e's mysql up so there is something to log into, and when it
cannot it said "no mysql: docker is not running" -- the one explanation the Makefile had
for every way docker can be unusable. On a machine where the daemon is up but the user is
not in the docker group, that sends you to systemctl to look at a service that is already
active, and the socket is nowhere in the sentence. Found by being that machine.

The reason is docker's own now, because it already distinguishes the cases precisely, and
a permission denied also gets the fix for it: the group to join, and that a login is what
makes it take. The Makefile stops explaining a failure it cannot see the cause of.
…h hard corners

Omarchy's own default is rounding = 0 (default/hypr/looknfeel.lua) and a theme does not
touch it -- a theme ships colours, and only the user's own hypr config overrides the
rounding -- so the desktop around this window has hard corners while the app put a 7px
radius on every panel, input and button in the middle of it, which is the look of a web
page in a window rather than of a program on that desktop.

The two radius tokens carry most of it. The rest is a short list of corners that do not
come through them, found by grep and minus the two sets that cannot show here: window.css
is the macOS frameless chrome, and a gallery design never has this sheet loaded over it.
A list rather than a blanket * { border-radius: 0 }, which would put a declaration on
every cell of a table that can run to thousands. Discs stay discs -- the connection icon
and the environment dot are shapes, not corners.

Read it from the desktop rather than hardcode it? Only the user's hypr config knows, not
the theme, and it is Lua: the rounding is commented out in mine, which a reader would have
to not mistake for a value. Not worth it to track a number that is 0 by default.
…nly for the console

-debug turned the inspector on and opened it in the same breath, so every run it is good
for -- dressing the window as another OS, reading the console in the log, clicking the app
by hand -- began with an inspector window beside the app, to be closed before anything
could be seen. macOS has never done that: setInspectable puts the page in Safari's Develop
menu and opening it is yours.

So Linux does the same now. F12 and Ctrl+Shift+I were already the way back after a close;
they are the way in as well, which costs the one keystroke a Develop menu would have cost
anyway. Console messages still reach the log whether it is open or not, which is the half
of -debug that has to work without being asked for.

This branch has not been deployed

No deployments
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