Consolidate omarchy-shell colors into a single shell.toml surface

Five plugins each owned their own colors.toml watcher with subtly
different parsers, plus two ad-hoc per-theme override files
(notifications.json, image-picker-colors.json). This collapses all of
that into one source of truth: Commons/Color.qml watches colors.toml +
shell.toml and exposes Color.bar.*, Color.popups.*, Color.notifications.*,
Color.menu.*, Color.imagePicker.* for every surface to bind to.

shell.toml is generated by the existing template pipeline from
default/themed/shell.toml.tpl. Themes can ship their own shell.toml to
override individual keys; everything missing falls back to the
foundational palette via root.pick(). Settings panel is intentionally
not themable beyond the foundational tokens.

last-horizon and solitude ship a minimal shell.toml to preserve their
historical 'notification border matches Hyprland active border'
behavior, which previously came from parsing the theme's hyprland.conf
(now removed).
This commit is contained in:
Ryan Hughes
2026-05-16 00:30:27 -04:00
parent 227d32544f
commit f404a81c6a
14 changed files with 199 additions and 298 deletions
@@ -2,6 +2,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import QtQuick
import qs.Commons
Item {
id: root
@@ -15,22 +16,13 @@ Item {
// Plugin lifecycle hooks. The host calls open(payloadJson) after
// `omarchy-shell-ipc shell summon omarchy.menu ...` and close() when hidden.
property string pendingInitialMenu: "root"
property string pendingColorsRaw: ""
function decodeBase64(value) {
var s = String(value || "")
if (!s) return ""
try { return Qt.atob(s) } catch (e) { return s }
}
function open(payloadJson) {
var payload = ({})
try { payload = JSON.parse(payloadJson || "{}") } catch (e) { payload = ({}) }
root.pendingInitialMenu = payload.initialMenu || payload.menu || "root"
root.pendingColorsRaw = payload.colorsRawBase64 ? root.decodeBase64(payload.colorsRawBase64) : (payload.colorsRaw || "")
if (payload.fontFamily) root.fontFamily = payload.fontFamily
if (root.pendingColorsRaw) root.loadColors(root.pendingColorsRaw)
root.openExistingMenu(root.pendingInitialMenu)
}
@@ -40,7 +32,6 @@ Item {
}
property string fontFamily: Quickshell.env("OMARCHY_MENU_FONT") || "monospace"
property string colorsFile: Quickshell.env("OMARCHY_MENU_COLORS_FILE") || (Quickshell.env("HOME") + "/.config/omarchy/current/theme/colors.toml")
property string styleFile: Quickshell.env("OMARCHY_MENU_STYLE_FILE") || (Quickshell.env("HOME") + "/.local/state/omarchy/toggles/quickshell-menu.json")
// JSONC menu definitions. The shell parses both at startup and merges
// the user file on top of the defaults, so the keybind → IPC → visible
@@ -61,9 +52,10 @@ Item {
property var navStack: []
property var providersLoaded: ({})
property var providerQueue: []
property color accent: "#89b4fa"
property color background: "#101315"
property color foreground: "#cacccc"
// Bound to the central [menu] section in shell.toml via Color.qml.
property color accent: Color.menu.selected
property color background: Color.menu.background
property color foreground: Color.menu.text
property color border: foreground
property int cornerRadius: 0
property int contentMargin: 18
@@ -624,18 +616,6 @@ Item {
Qt.callLater(function() { keyCatcher.forceActiveFocus() })
}
function loadColors(raw) {
var lines = String(raw || "").split("\n")
for (var i = 0; i < lines.length; i++) {
var match = lines[i].match(/^\s*([A-Za-z0-9_-]+)\s*=\s*["']?(#[0-9A-Fa-f]{6})/)
if (!match) continue
if (match[1] === "foreground") foreground = match[2]
else if (match[1] === "background") background = match[2]
else if (match[1] === "color4" || match[1] === "accent") accent = match[2]
}
border = foreground
}
function loadStyle(raw) {
try {
var style = JSON.parse(raw || "{}")
@@ -811,13 +791,6 @@ Item {
}
}
FileView {
path: root.colorsFile
watchChanges: true
onLoaded: root.loadColors(text())
onFileChanged: { reload(); root.loadColors(text()) }
}
FileView {
path: root.styleFile
watchChanges: true