Give the desktop real themes, video wallpapers, and honest titlebars

Appearance now opens on Themes: light and dark side by side, each
remembering its own choice, over galleries of ten shipped themes —
Tokyo Moon and Day joined by Moon Rose, Catppuccin, Nord, Gruvbox and
Everforest in both modes. A theme is a complete palette: the catalog
lives in themes.json, Theme.qml reads every color token from the
active record, and one render pipeline carries it to kitty, tmux,
btop, GTK, Vicinae, Firefox's chrome, and the lock screen. The Theme
editor builds new ones from four wells — wheel, hex, or eyedropper —
with derived surfaces, a saturation slider, debounced fine-tune, and
effects that save with the theme. Custom edits finally keep GNOME's
accent, kitty's border, and hyprlock in sync.

Wallpapers can be video: mpvpaper per output, hardware-decoded, muted
and looped, supervised and respawned. Panama owns the pausing — games,
battery, and a bar pill for right now — because the compositor
rebuilds full-screen blur for every frame a video wallpaper draws.
The lock screen gets a still frame.

Titlebars stop lying. GNOME apps get close-only on your chosen side,
the maximize and double-click settings are gone, the Settings window
obeys the same rules, and its titlebar can be turned off entirely.
Typography becomes five labeled dropdowns instead of a wall of
samples.

Contracts updated and written throughout (165 now); per the redesign
workflow none were executed — the full sweep runs once at the end.

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-23 23:39:04 -04:00
parent 7578348db1
commit cb7c09d208
68 changed files with 6115 additions and 1138 deletions
@@ -971,6 +971,20 @@ Singleton {
key: "wallpaperPerMonitor", type: "json", def: ({}), group: "wallpaper", internal: true,
label: "Per-display backgrounds", detail: "Background assigned to each connected display"
},
// Video wallpapers play through mpvpaper (services/VideoWallpaper.qml);
// the active video rides wallpaperPath like any still, routed by
// extension. These two only shape discovery and the battery policy.
{
key: "videoWallpaperDir", type: "string", def: "Videos/Wallpapers", group: "wallpaper",
pattern: "^~?/?[A-Za-z0-9 ._/+@'-]{1,160}$",
label: "Video wallpaper folder",
detail: "Where the picker looks for videos. Relative to your home folder unless it starts with /"
},
{
key: "videoWallpaperPauseOnBattery", type: "bool", def: true, group: "wallpaper",
label: "Pause video wallpaper on battery",
detail: "Freezes on the current frame and resumes on wall power"
},
// ── Lock-screen appearance ─────────────────────────────────────────
// scripts/panama-lock validates these again before generating a state
@@ -1129,6 +1143,22 @@ Singleton {
detail: "Named custom colour schemes and accent pairs",
internal: true
},
// The remembered theme per scheme: flipping light/dark lands on the
// theme you last chose for that side, never a forced default.
{
key: "themeDark", type: "string", def: "moon", group: "appearance",
pattern: "^[a-z0-9][a-z0-9-]{0,63}$",
label: "Dark theme",
detail: "The theme applied while the desktop is dark",
internal: true
},
{
key: "themeLight", type: "string", def: "day", group: "appearance",
pattern: "^[a-z0-9][a-z0-9-]{0,63}$",
label: "Light theme",
detail: "The theme applied while the desktop is light",
internal: true
},
// ── Application themes ─────────────────────────────────────────────
// ColorScheme owns GTK's light/dark theme. These are the two theme
@@ -1248,20 +1278,18 @@ Singleton {
{ value: "right", label: "Right" }
]
},
// Panama's own windows (Settings) draw their own titlebar and follow
// the same rules as GNOME apps. Off is pure Hyprland: Super+Q closes,
// Super+drag moves, Escape still works.
{
key: "titlebarMaximizeButton", type: "bool", def: false, group: "titlebar",
label: "Maximize button",
detail: "Show a maximize button in application titlebars that support it"
},
{
key: "titlebarDoubleClick", type: "enum", def: "toggle-maximize", group: "titlebar",
label: "Double-click titlebar",
detail: "Choose what a double-click on an application titlebar does",
options: [
{ value: "toggle-maximize", label: "Toggle maximize" },
{ value: "none", label: "Do nothing" }
]
key: "panamaTitlebar", type: "bool", def: true, group: "titlebar",
label: "Titlebar on Panama windows",
detail: "Hide it and the window is pure Hyprland — Super+Q closes, Super+drag moves"
},
// titlebarMaximizeButton and titlebarDoubleClick used to live here.
// Removed on purpose: Hyprland has no minimize, maximize is noise in a
// tiler, and DesktopStyle now pushes a close-only button-layout and
// leaves GNOME's double-click default alone.
// ── Accessibility ───────────────────────────────────────────────────
// Backed by gsettings so GTK applications agree with the shell, and
+29 -26
View File
@@ -20,28 +20,31 @@ Singleton {
id: root
// ── Color scheme ───────────────────────────────────────────────────────
// Tokyo Night ships an official light variant (Day), so light mode is that
// rather than a palette invented to merely not be dark. The two share the
// same hues at different lightness, which is what lets the Prism identity
// survive the switch: blue still leads into orchid, it is simply a darker
// blue on a lighter ground.
// The scheme still exists as a boolean because alphas and a handful of
// mix directions depend on which side of the ground the theme sits — but
// the palette itself now comes from the active theme record.
//
// Every token below is a binding on this, so flipping it repaints the whole
// Every token below is a binding, so selecting a theme repaints the whole
// shell without anything needing to know it happened.
readonly property bool dark: DesktopPreferences.get("colorScheme") !== "light"
// ── Palette ─────────────────────────────────────────────────────────────
// Canonical Tokyo Night Moon. `accent` matches the GNOME accent exactly.
readonly property color bg: root.dark ? "#222436" : "#e1e2e7"
readonly property color bgDark: root.dark ? "#1e2030" : "#d3d5de"
readonly property color bgHighlight: root.dark ? "#2f334d" : "#c4c8da"
readonly property color bgPanel: root.dark ? "#2e2f3d" : "#d9dae3" // dock glass surface
readonly property color bgPopover: root.dark ? "#21212f" : "#eaeaee" // Openbar submenu background
// The active theme's palette — ThemeProfiles resolves it and it is always
// complete: a shipped theme carries its own, a custom theme without one
// inherits its scheme's default (Tokyo Moon or Tokyo Day) from the
// catalog. Nothing here needs a per-token fallback.
readonly property var palette: ThemeProfiles.activePalette
readonly property color fg: root.dark ? "#c8d3f5" : "#3760bf"
readonly property color fgDim: root.dark ? "#828bb8" : "#6172b0"
readonly property color fgMuted: root.dark ? "#636da6" : "#848cb5"
readonly property color gutter: root.dark ? "#3b4261" : "#a8aecb"
readonly property color bg: root.palette.bg
readonly property color bgDark: root.palette.bgDark
readonly property color bgHighlight: root.palette.bgHighlight
readonly property color bgPanel: root.palette.bgPanel // dock glass surface
readonly property color bgPopover: root.palette.bgPopover // Openbar submenu background
readonly property color fg: root.palette.fg
readonly property color fgDim: root.palette.fgDim
readonly property color fgMuted: root.palette.fgMuted
readonly property color gutter: root.palette.gutter
// ── The accent ──────────────────────────────────────────────────────────
//
@@ -75,16 +78,16 @@ Singleton {
// shell-wide colour roles.
readonly property color accent: root.activeProfile.accent
readonly property color accentSecondary: root.activeProfile.secondary
readonly property color accentAlt: root.dark ? "#65bcff" : "#007197" // blue1, a lighter blue
readonly property color cyan: root.dark ? "#86e1fc" : "#007197"
readonly property color teal: root.dark ? "#4fd6be" : "#118c74"
readonly property color green: root.dark ? "#c3e88d" : "#587539"
readonly property color yellow: root.dark ? "#ffc777" : "#8c6c3e"
readonly property color orange: root.dark ? "#ff966c" : "#b15c00"
readonly property color red: root.dark ? "#ff757f" : "#f52a65"
readonly property color redDeep: root.dark ? "#c53b53" : "#c64343"
readonly property color magenta: root.dark ? "#c099ff" : "#9854f1"
readonly property color pink: root.dark ? "#fca7ea" : "#d20065"
readonly property color accentAlt: root.palette.accentAlt
readonly property color cyan: root.palette.cyan
readonly property color teal: root.palette.teal
readonly property color green: root.palette.green
readonly property color yellow: root.palette.yellow
readonly property color orange: root.palette.orange
readonly property color red: root.palette.red
readonly property color redDeep: root.palette.redDeep
readonly property color magenta: root.palette.magenta
readonly property color pink: root.palette.pink
// Semantic aliases — prefer these in widgets so intent survives a repaint.
readonly property color ok: green
+217
View File
@@ -0,0 +1,217 @@
{
"comment": "The shipped theme catalog — the one source of truth for full palettes. services/ThemeCatalog.qml reads it with FileView, scripts read it with jq, contracts read it with fs. The 19 palette keys are exactly Theme.qml's color tokens; ansi's 16 feed generated terminal themes. moon and day must stay byte-identical to the shell's pre-theme literals: theme-catalog-contract pins that.",
"defaultDark": "moon",
"defaultLight": "day",
"themes": [
{
"id": "moon", "name": "Tokyo Moon", "scheme": "dark",
"accent": "#82aaff", "secondary": "#b172b0",
"palette": {
"bg": "#222436", "bgDark": "#1e2030", "bgHighlight": "#2f334d",
"bgPanel": "#2e2f3d", "bgPopover": "#21212f",
"fg": "#c8d3f5", "fgDim": "#828bb8", "fgMuted": "#636da6",
"gutter": "#3b4261", "accentAlt": "#65bcff",
"cyan": "#86e1fc", "teal": "#4fd6be", "green": "#c3e88d",
"yellow": "#ffc777", "orange": "#ff966c", "red": "#ff757f",
"redDeep": "#c53b53", "magenta": "#c099ff", "pink": "#fca7ea"
},
"ansi": {
"black": "#1b1d2b", "red": "#ff757f", "green": "#c3e88d",
"yellow": "#ffc777", "blue": "#82aaff", "magenta": "#c099ff",
"cyan": "#86e1fc", "white": "#828bb8",
"brightBlack": "#444a73", "brightRed": "#ff8d94", "brightGreen": "#c7fb6d",
"brightYellow": "#ffd8ab", "brightBlue": "#9ab8ff", "brightMagenta": "#caabff",
"brightCyan": "#b2ebff", "brightWhite": "#c8d3f5"
}
},
{
"id": "moon-rose", "name": "Moon Rose", "scheme": "dark",
"accent": "#ff757f", "secondary": "#c099ff",
"palette": {
"bg": "#222436", "bgDark": "#1e2030", "bgHighlight": "#2f334d",
"bgPanel": "#2e2f3d", "bgPopover": "#21212f",
"fg": "#c8d3f5", "fgDim": "#828bb8", "fgMuted": "#636da6",
"gutter": "#3b4261", "accentAlt": "#fca7ea",
"cyan": "#86e1fc", "teal": "#4fd6be", "green": "#c3e88d",
"yellow": "#ffc777", "orange": "#ff966c", "red": "#ff757f",
"redDeep": "#c53b53", "magenta": "#c099ff", "pink": "#fca7ea"
},
"ansi": {
"black": "#1b1d2b", "red": "#ff757f", "green": "#c3e88d",
"yellow": "#ffc777", "blue": "#82aaff", "magenta": "#c099ff",
"cyan": "#86e1fc", "white": "#828bb8",
"brightBlack": "#444a73", "brightRed": "#ff8d94", "brightGreen": "#c7fb6d",
"brightYellow": "#ffd8ab", "brightBlue": "#9ab8ff", "brightMagenta": "#caabff",
"brightCyan": "#b2ebff", "brightWhite": "#c8d3f5"
}
},
{
"id": "mocha", "name": "Catppuccin Mocha", "scheme": "dark",
"accent": "#cba6f7", "secondary": "#f5c2e7",
"palette": {
"bg": "#1e1e2e", "bgDark": "#181825", "bgHighlight": "#313244",
"bgPanel": "#27273a", "bgPopover": "#1b1b2a",
"fg": "#cdd6f4", "fgDim": "#9399b2", "fgMuted": "#7f849c",
"gutter": "#45475a", "accentAlt": "#89dceb",
"cyan": "#89dceb", "teal": "#94e2d5", "green": "#a6e3a1",
"yellow": "#f9e2af", "orange": "#fab387", "red": "#f38ba8",
"redDeep": "#b04a5a", "magenta": "#cba6f7", "pink": "#f5c2e7"
},
"ansi": {
"black": "#45475a", "red": "#f38ba8", "green": "#a6e3a1",
"yellow": "#f9e2af", "blue": "#89b4fa", "magenta": "#f5c2e7",
"cyan": "#94e2d5", "white": "#bac2de",
"brightBlack": "#585b70", "brightRed": "#f38ba8", "brightGreen": "#a6e3a1",
"brightYellow": "#f9e2af", "brightBlue": "#89b4fa", "brightMagenta": "#f5c2e7",
"brightCyan": "#94e2d5", "brightWhite": "#a6adc8"
}
},
{
"id": "nord", "name": "Nord", "scheme": "dark",
"accent": "#88c0d0", "secondary": "#81a1c1",
"palette": {
"bg": "#2e3440", "bgDark": "#272c36", "bgHighlight": "#3b4252",
"bgPanel": "#353c4a", "bgPopover": "#2a303c",
"fg": "#d8dee9", "fgDim": "#9aa4b8", "fgMuted": "#7b8598",
"gutter": "#4c566a", "accentAlt": "#8fbcbb",
"cyan": "#88c0d0", "teal": "#8fbcbb", "green": "#a3be8c",
"yellow": "#ebcb8b", "orange": "#d08770", "red": "#bf616a",
"redDeep": "#a54e57", "magenta": "#b48ead", "pink": "#c9a3c0"
},
"ansi": {
"black": "#3b4252", "red": "#bf616a", "green": "#a3be8c",
"yellow": "#ebcb8b", "blue": "#81a1c1", "magenta": "#b48ead",
"cyan": "#88c0d0", "white": "#e5e9f0",
"brightBlack": "#4c566a", "brightRed": "#bf616a", "brightGreen": "#a3be8c",
"brightYellow": "#ebcb8b", "brightBlue": "#81a1c1", "brightMagenta": "#b48ead",
"brightCyan": "#8fbcbb", "brightWhite": "#eceff4"
}
},
{
"id": "gruvbox", "name": "Gruvbox", "scheme": "dark",
"accent": "#fabd2f", "secondary": "#fe8019",
"palette": {
"bg": "#282828", "bgDark": "#1d2021", "bgHighlight": "#3c3836",
"bgPanel": "#32302f", "bgPopover": "#211f1e",
"fg": "#ebdbb2", "fgDim": "#a89984", "fgMuted": "#928374",
"gutter": "#504945", "accentAlt": "#83a598",
"cyan": "#83a598", "teal": "#8ec07c", "green": "#b8bb26",
"yellow": "#fabd2f", "orange": "#fe8019", "red": "#fb4934",
"redDeep": "#cc241d", "magenta": "#d3869b", "pink": "#dfa7be"
},
"ansi": {
"black": "#282828", "red": "#cc241d", "green": "#98971a",
"yellow": "#d79921", "blue": "#458588", "magenta": "#b16286",
"cyan": "#689d6a", "white": "#a89984",
"brightBlack": "#928374", "brightRed": "#fb4934", "brightGreen": "#b8bb26",
"brightYellow": "#fabd2f", "brightBlue": "#83a598", "brightMagenta": "#d3869b",
"brightCyan": "#8ec07c", "brightWhite": "#ebdbb2"
}
},
{
"id": "everforest", "name": "Everforest", "scheme": "dark",
"accent": "#a7c080", "secondary": "#e69875",
"palette": {
"bg": "#2d353b", "bgDark": "#272e33", "bgHighlight": "#3d484d",
"bgPanel": "#343f44", "bgPopover": "#293136",
"fg": "#d3c6aa", "fgDim": "#9da9a0", "fgMuted": "#7a8478",
"gutter": "#475258", "accentAlt": "#7fbbb3",
"cyan": "#7fbbb3", "teal": "#83c092", "green": "#a7c080",
"yellow": "#dbbc7f", "orange": "#e69875", "red": "#e67e80",
"redDeep": "#c05a5e", "magenta": "#d699b6", "pink": "#dfa7c1"
},
"ansi": {
"black": "#3d484d", "red": "#e67e80", "green": "#a7c080",
"yellow": "#dbbc7f", "blue": "#7fbbb3", "magenta": "#d699b6",
"cyan": "#83c092", "white": "#d3c6aa",
"brightBlack": "#475258", "brightRed": "#e67e80", "brightGreen": "#a7c080",
"brightYellow": "#dbbc7f", "brightBlue": "#7fbbb3", "brightMagenta": "#d699b6",
"brightCyan": "#83c092", "brightWhite": "#d3c6aa"
}
},
{
"id": "day", "name": "Tokyo Day", "scheme": "light",
"accent": "#2e7de9", "secondary": "#9854f1",
"palette": {
"bg": "#e1e2e7", "bgDark": "#d3d5de", "bgHighlight": "#c4c8da",
"bgPanel": "#d9dae3", "bgPopover": "#eaeaee",
"fg": "#3760bf", "fgDim": "#6172b0", "fgMuted": "#848cb5",
"gutter": "#a8aecb", "accentAlt": "#007197",
"cyan": "#007197", "teal": "#118c74", "green": "#587539",
"yellow": "#8c6c3e", "orange": "#b15c00", "red": "#f52a65",
"redDeep": "#c64343", "magenta": "#9854f1", "pink": "#d20065"
},
"ansi": {
"black": "#b4b5b9", "red": "#f52a65", "green": "#587539",
"yellow": "#8c6c3e", "blue": "#2e7de9", "magenta": "#9854f1",
"cyan": "#007197", "white": "#6172b0",
"brightBlack": "#a1a6c5", "brightRed": "#ff4774", "brightGreen": "#5c8524",
"brightYellow": "#a27629", "brightBlue": "#358aff", "brightMagenta": "#a463ff",
"brightCyan": "#007ea8", "brightWhite": "#3760bf"
}
},
{
"id": "latte", "name": "Catppuccin Latte", "scheme": "light",
"accent": "#8839ef", "secondary": "#ea76cb",
"palette": {
"bg": "#eff1f5", "bgDark": "#e6e9ef", "bgHighlight": "#ccd0da",
"bgPanel": "#e2e6ee", "bgPopover": "#f5f7fa",
"fg": "#4c4f69", "fgDim": "#6c6f85", "fgMuted": "#8c8fa1",
"gutter": "#acb0be", "accentAlt": "#209fb5",
"cyan": "#04a5e5", "teal": "#179299", "green": "#40a02b",
"yellow": "#df8e1d", "orange": "#fe640b", "red": "#d20f39",
"redDeep": "#e64553", "magenta": "#8839ef", "pink": "#ea76cb"
},
"ansi": {
"black": "#5c5f77", "red": "#d20f39", "green": "#40a02b",
"yellow": "#df8e1d", "blue": "#1e66f5", "magenta": "#ea76cb",
"cyan": "#179299", "white": "#acb0be",
"brightBlack": "#6c6f85", "brightRed": "#d20f39", "brightGreen": "#40a02b",
"brightYellow": "#df8e1d", "brightBlue": "#1e66f5", "brightMagenta": "#ea76cb",
"brightCyan": "#179299", "brightWhite": "#bcc0cc"
}
},
{
"id": "gruvbox-light", "name": "Gruvbox Light", "scheme": "light",
"accent": "#b57614", "secondary": "#af3a03",
"palette": {
"bg": "#fbf1c7", "bgDark": "#f2e5bc", "bgHighlight": "#ebdbb2",
"bgPanel": "#f0e6c0", "bgPopover": "#fdf6d8",
"fg": "#3c3836", "fgDim": "#665c54", "fgMuted": "#7c6f64",
"gutter": "#bdae93", "accentAlt": "#076678",
"cyan": "#076678", "teal": "#427b58", "green": "#79740e",
"yellow": "#b57614", "orange": "#af3a03", "red": "#9d0006",
"redDeep": "#cc241d", "magenta": "#8f3f71", "pink": "#b16286"
},
"ansi": {
"black": "#fbf1c7", "red": "#cc241d", "green": "#98971a",
"yellow": "#d79921", "blue": "#458588", "magenta": "#b16286",
"cyan": "#689d6a", "white": "#7c6f64",
"brightBlack": "#928374", "brightRed": "#9d0006", "brightGreen": "#79740e",
"brightYellow": "#b57614", "brightBlue": "#076678", "brightMagenta": "#8f3f71",
"brightCyan": "#427b58", "brightWhite": "#3c3836"
}
},
{
"id": "everforest-light", "name": "Everforest Light", "scheme": "light",
"accent": "#8da101", "secondary": "#f57d26",
"palette": {
"bg": "#fdf6e3", "bgDark": "#f4f0d9", "bgHighlight": "#e6e2cc",
"bgPanel": "#efebd4", "bgPopover": "#faf3dc",
"fg": "#5c6a72", "fgDim": "#829181", "fgMuted": "#939f91",
"gutter": "#a6b0a0", "accentAlt": "#3a94c5",
"cyan": "#3a94c5", "teal": "#35a77c", "green": "#8da101",
"yellow": "#dfa000", "orange": "#f57d26", "red": "#f85552",
"redDeep": "#e66868", "magenta": "#df69ba", "pink": "#df69ba"
},
"ansi": {
"black": "#5c6a72", "red": "#f85552", "green": "#8da101",
"yellow": "#dfa000", "blue": "#3a94c5", "magenta": "#df69ba",
"cyan": "#35a77c", "white": "#e0dcc7",
"brightBlack": "#829181", "brightRed": "#f85552", "brightGreen": "#8da101",
"brightYellow": "#dfa000", "brightBlue": "#3a94c5", "brightMagenta": "#df69ba",
"brightCyan": "#35a77c", "brightWhite": "#fdf6e3"
}
}
]
}