quickshell: add initial bar config with per-monitor workspaces
- Vertical bar on DP-2 with rounded-square pills throughout - Per-monitor workspace groups sorted by screen x position, with Nerd Font icons for named workspaces and apex-neon red active indicator - Bar layout: datetime+weather top, workspaces centered, gamemode+media+notif+system bottom - Popouts anchor to triggering icon (top-right for datetime/weather, bottom-right for media/notif/system) - Lock command switched from hyprlock to swaylock - Hyprland blur/ignore_alpha layerrules for quickshell namespace Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
62
dot_config/quickshell/shared/Config.qml.tmpl
Normal file
62
dot_config/quickshell/shared/Config.qml.tmpl
Normal file
@@ -0,0 +1,62 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
readonly property string home: Quickshell.env("HOME")
|
||||
|
||||
// Appearance
|
||||
// apexFlavor: "neon" (dark) or "aeon" (light) — synced from chezmoi data.theme
|
||||
readonly property string apexFlavor: "{{ trimPrefix "apex-" .chezmoi.config.data.theme }}"
|
||||
readonly property bool transparency: true // semi-transparent backgrounds (needs Hyprland blur layerrule)
|
||||
|
||||
// Monitor — quickshell bar lives on the rightmost monitor (portrait, DP-2)
|
||||
readonly property string monitor: "DP-2"
|
||||
|
||||
// Workspaces — show all 10; Hyprland IPC tracks per-monitor active workspace
|
||||
readonly property int workspaceCount: 10
|
||||
|
||||
// Weather
|
||||
readonly property string weatherLocation: "Nospelt"
|
||||
readonly property bool useCelsius: true
|
||||
readonly property string tempUnit: useCelsius ? "°C" : "°F"
|
||||
readonly property string windUnit: useCelsius ? "km/h" : "mph"
|
||||
readonly property int forecastDays: 5
|
||||
readonly property int weatherRefreshMs: 1800000
|
||||
|
||||
// Disk mounts to monitor
|
||||
readonly property string diskMount1: "/"
|
||||
readonly property string diskMount1Label: "/"
|
||||
readonly property string diskMount2: home + "/data"
|
||||
readonly property string diskMount2Label: "/data"
|
||||
|
||||
// Scripts
|
||||
readonly property string scriptsDir: home + "/.config/quickshell/scripts"
|
||||
readonly property string gpuScript: scriptsDir + "/gpu.sh"
|
||||
|
||||
// Idle daemon
|
||||
readonly property string idleProcess: "hypridle"
|
||||
readonly property string lockCommand: "swaylock"
|
||||
|
||||
// Power commands
|
||||
readonly property var powerActions: [
|
||||
{ command: ["swaylock"] },
|
||||
{ command: ["hyprshutdown"] },
|
||||
{ command: ["hyprshutdown", "-t", "Restarting...", "--post-cmd", "systemctl reboot"] },
|
||||
{ command: ["hyprshutdown", "-t", "Powering off...", "--post-cmd", "systemctl poweroff"] }
|
||||
]
|
||||
|
||||
// Network filter (interfaces to exclude from IP display)
|
||||
readonly property string netExcludePattern: "127.0.0\\|docker\\|br-\\|veth"
|
||||
|
||||
// DateTime / Calendar
|
||||
readonly property bool use24h: true
|
||||
readonly property string clockFormat: use24h ? "HH:mm" : "hh:mm A"
|
||||
readonly property string clockSecondsFormat: use24h ? "HH:mm:ss" : "hh:mm:ss A"
|
||||
readonly property string dateFormat: "dddd, d MMMM yyyy"
|
||||
readonly property string pillDateFormat: "ddd\nd"
|
||||
readonly property string pillTimeFormat: use24h ? "HH\nmm" : "hh\nmm"
|
||||
readonly property bool weekStartsMonday: true
|
||||
readonly property var dayHeaders: weekStartsMonday ? ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
|
||||
}
|
||||
22
dot_config/quickshell/shared/PopoutState.qml
Normal file
22
dot_config/quickshell/shared/PopoutState.qml
Normal file
@@ -0,0 +1,22 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
property string active: ""
|
||||
property real triggerY: 0
|
||||
|
||||
function toggle(name: string, y: real): void {
|
||||
if (active === name) {
|
||||
active = "";
|
||||
} else {
|
||||
active = name;
|
||||
triggerY = y;
|
||||
}
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
active = "";
|
||||
}
|
||||
}
|
||||
124
dot_config/quickshell/shared/Theme.qml
Normal file
124
dot_config/quickshell/shared/Theme.qml
Normal file
@@ -0,0 +1,124 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
// ─── Apex palettes ───────────────────────
|
||||
readonly property var palettes: ({
|
||||
neon: {
|
||||
// Backgrounds
|
||||
base: "#050505", mantle: "#0a0a0a", crust: "#000000",
|
||||
surface0: "#141414", surface1: "#1e1e1e", surface2: "#262626",
|
||||
overlay0: "#404040", overlay1: "#555555",
|
||||
// Text
|
||||
text: "#ededed", subtext0: "#b0b0b0", subtext1: "#d0d0d0",
|
||||
// Accent colors — apex-neon palette
|
||||
lavender: "#9d00ff", // sacred purple
|
||||
mauve: "#9d00ff", // sacred purple
|
||||
pink: "#ff0044", // razor red
|
||||
red: "#ff0044", // razor red
|
||||
peach: "#ff8899", // alert salmon
|
||||
yellow: "#ffb700", // amber warning
|
||||
green: "#00ff99", // toxic green
|
||||
teal: "#00ff99", // toxic green
|
||||
blue: "#00eaff", // tech cyan
|
||||
sky: "#00eaff" // tech cyan
|
||||
},
|
||||
aeon: {
|
||||
// Backgrounds
|
||||
base: "#f5f5f5", mantle: "#e8e8e8", crust: "#d0d0d0",
|
||||
surface0: "#e0e0e0", surface1: "#d4d4d4", surface2: "#c8c8c8",
|
||||
overlay0: "#737373", overlay1: "#555555",
|
||||
// Text
|
||||
text: "#0a0a0a", subtext0: "#444444", subtext1: "#333333",
|
||||
// Accent colors — apex-aeon palette
|
||||
lavender: "#7a3cff", // indigo purple
|
||||
mauve: "#7a3cff", // indigo purple
|
||||
pink: "#ff0044", // razor red
|
||||
red: "#ff0044", // razor red
|
||||
peach: "#ff4d6d", // rose error
|
||||
yellow: "#d18f00", // dark amber
|
||||
green: "#00b377", // forest green
|
||||
teal: "#00b377", // forest green
|
||||
blue: "#007a88", // deep teal
|
||||
sky: "#007a88" // deep teal
|
||||
}
|
||||
})
|
||||
|
||||
readonly property var p: palettes[Config.apexFlavor] || palettes.neon
|
||||
|
||||
// ─── Palette colors ──────────────────────
|
||||
readonly property color base: p.base
|
||||
readonly property color mantle: p.mantle
|
||||
readonly property color crust: p.crust
|
||||
readonly property color surface0: p.surface0
|
||||
readonly property color surface1: p.surface1
|
||||
readonly property color surface2: p.surface2
|
||||
readonly property color overlay0: p.overlay0
|
||||
readonly property color text: p.text
|
||||
readonly property color subtext0: p.subtext0
|
||||
readonly property color subtext1: p.subtext1
|
||||
readonly property color lavender: p.lavender
|
||||
readonly property color mauve: p.mauve
|
||||
readonly property color pink: p.pink
|
||||
readonly property color red: p.red
|
||||
readonly property color peach: p.peach
|
||||
readonly property color yellow: p.yellow
|
||||
readonly property color green: p.green
|
||||
readonly property color teal: p.teal
|
||||
readonly property color blue: p.blue
|
||||
readonly property color sky: p.sky
|
||||
|
||||
// ─── Semantic aliases ────────────────────
|
||||
readonly property color accent: blue // tech cyan / deep teal
|
||||
readonly property color success: green // toxic / forest
|
||||
readonly property color warning: yellow // amber
|
||||
readonly property color danger: red // razor red
|
||||
readonly property color info: sky // cyan / teal
|
||||
readonly property color muted: overlay0 // dim grey
|
||||
|
||||
// ─── Opacity tokens ──────────────────────
|
||||
readonly property real opacitySubtle: 0.08 // borders, faint dividers
|
||||
readonly property real opacityLight: 0.15 // tinted backgrounds
|
||||
readonly property real opacityMedium: 0.3 // active borders, overlays
|
||||
readonly property real opacityStrong: 0.45 // muted/disabled elements
|
||||
readonly property real opacityFill: 0.7 // progress bar fills
|
||||
|
||||
// ─── Border token ────────────────────────
|
||||
readonly property bool isDark: Config.apexFlavor === "neon"
|
||||
readonly property color borderSubtle: isDark ? Qt.rgba(1, 1, 1, opacitySubtle) : Qt.rgba(0, 0, 0, opacitySubtle)
|
||||
|
||||
// ─── Transparency ────────────────────────
|
||||
readonly property bool transparencyEnabled: Config.transparency
|
||||
readonly property color barBackground: transparencyEnabled ? Qt.alpha(mantle, 0.75) : mantle
|
||||
readonly property color popoutBackground: transparencyEnabled ? Qt.alpha(mantle, 0.82) : mantle
|
||||
|
||||
// ─── Layout ──────────────────────────────
|
||||
readonly property int barWidth: 52
|
||||
readonly property int barInnerWidth: 40
|
||||
readonly property int barPadding: 6
|
||||
readonly property int spacing: 10
|
||||
|
||||
// Popouts
|
||||
readonly property int popoutWidth: 320
|
||||
readonly property int popoutPadding: 16
|
||||
readonly property int popoutSpacing: 10
|
||||
|
||||
// Rounding
|
||||
readonly property int radiusSmall: 8
|
||||
readonly property int radiusNormal: 14
|
||||
readonly property int radiusPill: 1000
|
||||
|
||||
// ─── Typography ──────────────────────────
|
||||
readonly property int fontSmall: 11
|
||||
readonly property int fontSize: 13
|
||||
readonly property int fontLarge: 15
|
||||
readonly property string fontFamily: "GeistMono Nerd Font"
|
||||
readonly property string iconFont: "GeistMono Nerd Font"
|
||||
|
||||
// ─── Animation ───────────────────────────
|
||||
readonly property int animFast: 150
|
||||
readonly property int animNormal: 300
|
||||
readonly property int animSlow: 500
|
||||
}
|
||||
15
dot_config/quickshell/shared/Time.qml
Normal file
15
dot_config/quickshell/shared/Time.qml
Normal file
@@ -0,0 +1,15 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
readonly property string clock: Qt.formatDateTime(systemClock.date, Config.clockFormat)
|
||||
readonly property string clockSeconds: Qt.formatDateTime(systemClock.date, Config.clockSecondsFormat)
|
||||
readonly property date date: systemClock.date
|
||||
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
}
|
||||
85
dot_config/quickshell/shared/Weather.qml
Normal file
85
dot_config/quickshell/shared/Weather.qml
Normal file
@@ -0,0 +1,85 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property bool useCelsius: Config.useCelsius
|
||||
|
||||
property string location: Config.weatherLocation
|
||||
property string temp: "--"
|
||||
property string icon: "\u{f0590}"
|
||||
property string description: "--"
|
||||
property string feelsLike: "--"
|
||||
property string humidity: "--"
|
||||
property string wind: "--"
|
||||
property var forecast: []
|
||||
property string status: "loading" // "loading", "ok", "error"
|
||||
|
||||
function weatherIcon(code) {
|
||||
code = parseInt(code);
|
||||
if (code === 113) return "\u{f0599}";
|
||||
if (code === 116) return "\u{f0595}";
|
||||
if (code <= 122) return "\u{f0590}";
|
||||
if (code <= 260) return "\u{f0591}";
|
||||
if ([176,263,266,293,296,299,302,305,308,353,356,359].includes(code)) return "\u{f0597}";
|
||||
return "\u{f0598}";
|
||||
}
|
||||
|
||||
Process {
|
||||
id: weatherProc
|
||||
command: ["sh", "-c", "curl -sf 'wttr.in/" + Config.weatherLocation + "?format=j1' 2>/dev/null"]
|
||||
running: true
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
let data = JSON.parse(this.text);
|
||||
let cur = data.current_condition[0];
|
||||
let tempKey = root.useCelsius ? "temp_C" : "temp_F";
|
||||
let feelsKey = root.useCelsius ? "FeelsLikeC" : "FeelsLikeF";
|
||||
let windKey = root.useCelsius ? "windspeedKmph" : "windspeedMiles";
|
||||
let maxKey = root.useCelsius ? "maxtempC" : "maxtempF";
|
||||
let minKey = root.useCelsius ? "mintempC" : "mintempF";
|
||||
|
||||
root.temp = cur[tempKey] + Config.tempUnit;
|
||||
root.feelsLike = cur[feelsKey] + Config.tempUnit;
|
||||
root.humidity = cur.humidity + "%";
|
||||
root.wind = cur[windKey] + " " + Config.windUnit;
|
||||
root.description = cur.weatherDesc[0].value;
|
||||
root.location = data.nearest_area[0].areaName[0].value;
|
||||
root.icon = root.weatherIcon(cur.weatherCode);
|
||||
|
||||
let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
let fc = [];
|
||||
let weather = data.weather || [];
|
||||
for (let i = 0; i < Math.min(weather.length, Config.forecastDays); i++) {
|
||||
let w = weather[i];
|
||||
let d = new Date(w.date);
|
||||
fc.push({
|
||||
day: days[d.getDay()],
|
||||
high: w[maxKey],
|
||||
low: w[minKey],
|
||||
code: w.hourly[4].weatherCode,
|
||||
desc: w.hourly[4].weatherDesc[0].value
|
||||
});
|
||||
}
|
||||
root.forecast = fc;
|
||||
root.status = "ok";
|
||||
} catch(e) {
|
||||
console.warn("Weather: failed to parse response:", e);
|
||||
root.status = root.temp === "--" ? "error" : "stale";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: Config.weatherRefreshMs
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: weatherProc.running = true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user