Make Shell a category, the bar legible, and the dock a real dock

Desktop & Dock becomes Shell — Bar, Dock, Control Center, Tiling,
Workspaces — the home for everything Quickshell draws. The settings-
management cluster moves to System as Sync & Backup, Appearance's
Shell tab dissolves, and 24-hour time finally lives on Date & Time,
which always owned it.

The bar gets what it never had: a way to survive the wallpaper. A
second neutral text family (follow theme, or forced light or dark),
a one-layer shadow under every glyph, and a gradient scrim for
wallpapers nothing else survives — all off by default, pixel-identical
until asked. Widgets earn toggles (weather, media, clipboard, calendar
countdown), the vitals cluster stops leaving a dead pill behind, and
Control Center's sections learn to step aside.

The dock graduates from MVP: a context menu with window rows, pin,
unpin, quit and new-window; scroll an icon to cycle its windows; drag
to reorder on the dock itself; hover previews with one-shot captures;
and "Add App to Dock" in the launcher. Three real bugs died en route —
menus that slid away with the autohide, a readonly-property crash on
every menu open, and a drag that drifted half a slot per icon on side
docks. The pinned-apps editor in Settings becomes a drag strip.

166 contracts; the full suite is green except two live display and
switcher tests that cannot run behind a locked session — re-verified
on unlock.

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 04:28:20 -04:00
parent 4d7a194300
commit f8f5b25510
77 changed files with 2982 additions and 800 deletions
@@ -52,11 +52,74 @@ Singleton {
readonly property var entries: [
// ── Clock ───────────────────────────────────────────────────────────
// Group datetime, not clock: this drives the date menu, notification
// timestamps, and the lock screen — Date & Time owns it.
{
key: "use24Hour", type: "bool", def: false, group: "clock",
key: "use24Hour", type: "bool", def: false, group: "datetime",
label: "24-hour time",
detail: "Use 18:30 instead of 6:30 PM"
},
// ── Bar ─────────────────────────────────────────────────────────────
// The bar floats directly on the wallpaper; these keep it legible on
// grounds the theme never met, and choose which widgets earn a place.
{
key: "barTextTone", type: "enum", def: "theme", group: "bar",
label: "Bar text",
detail: "Follow the theme, or force a light or dark tone for the wallpaper you actually use",
options: [
{ value: "theme", label: "Follow theme" },
{ value: "light", label: "Light" },
{ value: "dark", label: "Dark" }
]
},
{
key: "barTextShadow", type: "bool", def: false, group: "bar",
label: "Bar text shadow",
detail: "A soft dark halo under every glyph and label in the bar"
},
{
key: "barBackdrop", type: "bool", def: false, group: "bar",
label: "Bar backdrop",
detail: "A subtle scrim fading down from the top edge"
},
{
key: "showWeatherWidget", type: "bool", def: true, group: "bar",
label: "Weather in the bar",
detail: "Beside the clock, once a forecast has been fetched"
},
{
key: "showMediaWidget", type: "bool", def: true, group: "bar",
label: "Media in the bar",
detail: "Now playing, click to pause"
},
{
key: "showClipboardButton", type: "bool", def: true, group: "bar",
label: "Clipboard button",
detail: "The history stays on Super+V either way"
},
{
key: "showCalendarCountdown", type: "bool", def: true, group: "bar",
label: "Calendar countdown",
detail: "Appears in the bar fifteen minutes before an event"
},
// ── Control Center ──────────────────────────────────────────────────
{
key: "ccShowFocus", type: "bool", def: true, group: "controlCenter",
label: "Focus in Control Center",
detail: "The session row at the top of the panel"
},
{
key: "ccShowHome", type: "bool", def: true, group: "controlCenter",
label: "Home in Control Center",
detail: "Your accessory shelf"
},
{
key: "ccShowPhone", type: "bool", def: true, group: "controlCenter",
label: "Phone in Control Center",
detail: "Vitals and reach-it actions"
},
{
key: "showSeconds", type: "bool", def: true, group: "clock",
label: "Show seconds",
@@ -811,31 +874,31 @@ Singleton {
hypr: { path: ["general", "snap", "enabled"], option: "general:snap:enabled", readAs: "bool" }
},
{
key: "workspaceBackAndForth", type: "bool", def: false, group: "multitasking",
key: "workspaceBackAndForth", type: "bool", def: false, group: "workspaces",
label: "Switch back and forth",
detail: "Selecting the workspace you are already on returns you to the previous one",
hypr: { path: ["binds", "workspace_back_and_forth"], option: "binds:workspace_back_and_forth", readAs: "bool" }
},
{
key: "allowWorkspaceCycles", type: "bool", def: false, group: "multitasking",
key: "allowWorkspaceCycles", type: "bool", def: false, group: "workspaces",
label: "Wrap around at the ends",
detail: "Moving past the last workspace continues from the first",
hypr: { path: ["binds", "allow_workspace_cycles"], option: "binds:allow_workspace_cycles", readAs: "bool" }
},
{
key: "focusOnActivate", type: "bool", def: false, group: "multitasking",
key: "focusOnActivate", type: "bool", def: false, group: "workspaces",
label: "Let applications take focus",
detail: "An application asking for attention is switched to, rather than only highlighted",
hypr: { path: ["misc", "focus_on_activate"], option: "misc:focus_on_activate", readAs: "bool" }
},
{
key: "windowSwallow", type: "bool", def: false, group: "multitasking",
key: "windowSwallow", type: "bool", def: false, group: "workspaces",
label: "Hide the terminal that launched a window",
detail: "A terminal disappears while an application started from it is open, and returns when it closes",
hypr: { path: ["misc", "enable_swallow"], option: "misc:enable_swallow", readAs: "bool" }
},
{
key: "mouseMoveFocusesMonitor", type: "bool", def: true, group: "multitasking",
key: "mouseMoveFocusesMonitor", type: "bool", def: true, group: "workspaces",
label: "Pointer changes active display",
detail: "Moving the pointer to another display makes it the active one",
hypr: { path: ["misc", "mouse_move_focuses_monitor"], option: "misc:mouse_move_focuses_monitor", readAs: "bool" }
@@ -980,10 +1043,10 @@ Singleton {
label: "Video wallpaper folder",
detail: "Where the picker looks for videos. Relative to your home folder unless it starts with /"
},
// Its own key, not wallpaperPath: the still pipeline persists its
// policy transactionally and once clobbered a stored video path.
// Two owners, two keys.
{
// Its own key, not wallpaperPath: the still pipeline persists its
// policy transactionally and once clobbered a stored video path.
// Two owners, two keys.
key: "videoWallpaperPath", type: "string", def: "", group: "wallpaper", internal: true,
pattern: "^(|/[^,\n]+)$",
label: "Video wallpaper",
+20
View File
@@ -19,6 +19,26 @@ Singleton {
readonly property bool showSeconds: DesktopPreferences.get("showSeconds")
readonly property bool showWeekday: DesktopPreferences.get("showWeekday")
// ── Bar ─────────────────────────────────────────────────────────────────
// Legibility first: the bar sits on the wallpaper, so it may need a tone,
// a shadow or a scrim the theme cannot know about. `barTextTone` itself is
// read by Theme, which turns it into the barFg family the widgets bind to.
readonly property bool barTextShadow: DesktopPreferences.get("barTextShadow")
readonly property bool barBackdrop: DesktopPreferences.get("barBackdrop")
// Which widgets earn a place. Each is ANDed with the widget's own state
// condition, so turning one on never conjures a pill with nothing in it.
readonly property bool showWeatherWidget: DesktopPreferences.get("showWeatherWidget")
readonly property bool showMediaWidget: DesktopPreferences.get("showMediaWidget")
readonly property bool showClipboardButton: DesktopPreferences.get("showClipboardButton")
readonly property bool showCalendarCountdown: DesktopPreferences.get("showCalendarCountdown")
// ── Control Center ──────────────────────────────────────────────────────
// One bool per section of the quick settings panel that is worth hiding.
readonly property bool ccShowFocus: DesktopPreferences.get("ccShowFocus")
readonly property bool ccShowHome: DesktopPreferences.get("ccShowHome")
readonly property bool ccShowPhone: DesktopPreferences.get("ccShowPhone")
// ── Weather ─────────────────────────────────────────────────────────────
// Coordinates taken from the GNOME night-light setting, which had already
// resolved the location. Uses Open-Meteo, which needs no API key.
+37
View File
@@ -46,6 +46,43 @@ Singleton {
readonly property color fgMuted: root.palette.fgMuted
readonly property color gutter: root.palette.gutter
// ── Bar text ────────────────────────────────────────────────────────────
// Every other surface in the shell draws on a ground the theme chose. The
// bar draws on the wallpaper, which the theme has never seen — so a
// photograph with a bright sky can leave the palette's own fg unreadable
// exactly where the clock is.
//
// Hence a second neutral family, used by the bar and nowhere else. Left
// alone it *is* the fg family, so nothing changes for anyone who never
// asks. Forced light or dark, it becomes a run that survives whatever is
// underneath it, and the two dims are mixed toward the opposite end rather
// than picked by hand, so the three stay a family either way.
readonly property string barTextTone: DesktopPreferences.get("barTextTone")
readonly property color barFg: {
if (root.barTextTone === "light")
return "#f4f6ff";
if (root.barTextTone === "dark")
return "#1b2130";
return root.fg;
}
readonly property color barFgDim: {
if (root.barTextTone === "light")
return root.mix(root.barFg, "#20242f", 0.35);
if (root.barTextTone === "dark")
return root.mix(root.barFg, "#ffffff", 0.35);
return root.fgDim;
}
readonly property color barFgMuted: {
if (root.barTextTone === "light")
return root.mix(root.barFg, "#20242f", 0.5);
if (root.barTextTone === "dark")
return root.mix(root.barFg, "#ffffff", 0.5);
return root.fgMuted;
}
// ── The accent ──────────────────────────────────────────────────────────
//
// `accent` is the primary and carries every state meaning (focused, active,