1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00

meta cleanup and refactors

This commit is contained in:
bbedward
2025-07-18 11:40:17 -04:00
parent 06607fa25e
commit 3a3f18c298
30 changed files with 473 additions and 783 deletions

3
.gitignore vendored
View File

@@ -54,3 +54,6 @@ compile_commands.json
*_qmlcache.qrc *_qmlcache.qrc
UNUSED UNUSED
.qmlls.ini .qmlls.ini
CLAUDE-activeContext.md
CLAUDE-temp.md

View File

@@ -2,6 +2,40 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## AI Guidance
* Ignore GEMINI.md and GEMINI-*.md files
* After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
* For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
* Before you finish, please verify your solution
* Do what has been asked; nothing more, nothing less.
* NEVER create files unless they're absolutely necessary for achieving your goal.
* ALWAYS prefer editing an existing file to creating a new one.
* NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
* When you update or modify core context files, also update markdown documentation and memory bank
* When asked to commit changes, exclude CLAUDE.md and CLAUDE-*.md referenced memory bank system files from any commits.
## Memory Bank System
This project uses a structured memory bank system with specialized context files. Always check these files for relevant information before starting work:
### Core Context Files
* **CLAUDE-activeContext.md** - Current session state, goals, and progress (if exists)
* **CLAUDE-patterns.md** - Established code patterns and conventions (if exists)
* **CLAUDE-decisions.md** - Architecture decisions and rationale (if exists)
* **CLAUDE-troubleshooting.md** - Common issues and proven solutions (if exists)
* **CLAUDE-config-variables.md** - Configuration variables reference (if exists)
* **CLAUDE-temp.md** - Temporary scratch pad (only read when referenced)
**Important:** Always reference the active context file first to understand what's currently being worked on and maintain session continuity.
### Memory Bank System Backups
When asked to backup Memory Bank System files, you will copy the core context files above and @.claude settings directory to directory @/path/to/backup-directory. If files already exist in the backup directory, you will overwrite them.
## Project Overview
## Project Overview ## Project Overview
This is a Quickshell-based desktop shell implementation with Material Design 3 dark theme. The shell provides a complete desktop environment experience with panels, widgets, and system integration services. This is a Quickshell-based desktop shell implementation with Material Design 3 dark theme. The shell provides a complete desktop environment experience with panels, widgets, and system integration services.

View File

@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Services.UPower
Singleton { Singleton {
id: root id: root
@@ -545,11 +546,11 @@ Singleton {
function getPowerProfileIcon(profile) { function getPowerProfileIcon(profile) {
switch (profile) { switch (profile) {
case "power-saver": case PowerProfile.PowerSaver:
return "battery_saver"; return "battery_saver";
case "balanced": case PowerProfile.Balanced:
return "battery_std"; return "battery_std";
case "performance": case PowerProfile.Performance:
return "flash_on"; return "flash_on";
default: default:
return "settings"; return "settings";
@@ -557,12 +558,13 @@ Singleton {
} }
function getPowerProfileLabel(profile) { function getPowerProfileLabel(profile) {
console.log("Theme.getPowerProfileLabel called with profile:", profile);
switch (profile) { switch (profile) {
case "power-saver": case PowerProfile.PowerSaver:
return "Power Saver"; return "Power Saver";
case "balanced": case PowerProfile.Balanced:
return "Balanced"; return "Balanced";
case "performance": case PowerProfile.Performance:
return "Performance"; return "Performance";
default: default:
return profile.charAt(0).toUpperCase() + profile.slice(1); return profile.charAt(0).toUpperCase() + profile.slice(1);
@@ -571,11 +573,11 @@ Singleton {
function getPowerProfileDescription(profile) { function getPowerProfileDescription(profile) {
switch (profile) { switch (profile) {
case "power-saver": case PowerProfile.PowerSaver:
return "Extend battery life"; return "Extend battery life";
case "balanced": case PowerProfile.Balanced:
return "Balance power and performance"; return "Balance power and performance";
case "performance": case PowerProfile.Performance:
return "Prioritize performance"; return "Prioritize performance";
default: default:
return "Custom power profile"; return "Custom power profile";

View File

@@ -10,73 +10,44 @@ import "../Common/fuzzysort.js" as Fuzzy
Singleton { Singleton {
id: root id: root
property list<DesktopEntry> applications: [] property list<DesktopEntry> applications: Array.from(DesktopEntries.applications.values)
property var applicationsByName: ({}) .filter(app => !app.noDisplay)
property var applicationsByExec: ({}) .sort((a, b) => a.name.localeCompare(b.name))
property bool ready: false
property var preppedApps: [] property var applicationsByName: {
var byName = {}
for (var i = 0; i < applications.length; i++) {
Component.onCompleted: { var app = applications[i]
loadApplications() byName[app.name.toLowerCase()] = app
}
Connections {
target: DesktopEntries
function onApplicationsChanged() {
console.log("AppSearchService: DesktopEntries applicationsChanged signal received")
console.log("AppSearchService: Current applications count before reload:", applications.length)
loadApplications()
} }
return byName
} }
property var applicationsByExec: {
function loadApplications() { var byExec = {}
Qt.callLater(function() { for (var i = 0; i < applications.length; i++) {
var allApps = Array.from(DesktopEntries.applications.values) var app = applications[i]
var execProp = app.execString || ""
applications = allApps var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
.filter(app => !app.noDisplay) if (cleanExec) {
.sort((a, b) => a.name.localeCompare(b.name)) byExec[cleanExec] = app
var byName = {}
var byExec = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
byName[app.name.toLowerCase()] = app
var execProp = app.execString || ""
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
if (cleanExec) {
byExec[cleanExec] = app
}
} }
}
applicationsByName = byName return byExec
applicationsByExec = byExec
preppedApps = applications.map(app => ({
name: Fuzzy.prepare(app.name || ""),
comment: Fuzzy.prepare(app.comment || ""),
entry: app
}))
ready = true
console.log("AppSearchService: Loaded", applications.length, "applications")
console.log("AppSearchService: Prepared", preppedApps.length, "apps for fuzzy search")
console.log("AppSearchService: Ready status:", ready)
})
} }
property var preppedApps: applications.map(app => ({
name: Fuzzy.prepare(app.name || ""),
comment: Fuzzy.prepare(app.comment || ""),
entry: app
}))
function searchApplications(query) { function searchApplications(query) {
if (!query || query.length === 0) { if (!query || query.length === 0) {
return applications return applications
} }
if (!ready || preppedApps.length === 0) { if (preppedApps.length === 0) {
return [] return []
} }
@@ -167,18 +138,4 @@ Singleton {
}) })
} }
function launchApp(app) {
if (!app) {
console.warn("AppSearchService: Cannot launch app, app is null")
return false
}
if (typeof app.execute === "function") {
app.execute()
return true
}
console.warn("AppSearchService: Cannot launch app, no execute method")
return false
}
} }

View File

@@ -8,108 +8,55 @@ import Quickshell.Io
Singleton { Singleton {
id: root id: root
property list<var> ddcMonitors: [] property bool brightnessAvailable: laptopBacklightAvailable || ddcAvailable
readonly property list<Monitor> monitors: variants.instances
property bool brightnessAvailable: laptopBacklightAvailable || ddcMonitors.length > 0
property bool laptopBacklightAvailable: false property bool laptopBacklightAvailable: false
property bool ddcAvailable: false
property int brightnessLevel: 75 property int brightnessLevel: 75
property int laptopBrightnessLevel: 75 property int maxBrightness: 100
property int currentRawBrightness: 0
function getMonitorForScreen(screen: ShellScreen): var {
return monitors.find(function(m) { return m.modelData === screen; });
}
property var debounceTimer: Timer {
id: debounceTimer
interval: 50
repeat: false
property int pendingValue: 0
onTriggered: {
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
if (focusedMonitor) {
focusedMonitor.setBrightness(pendingValue / 100);
}
}
}
property var laptopDebounceTimer: Timer {
id: laptopDebounceTimer
interval: 50
repeat: false
property int pendingValue: 0
onTriggered: {
laptopBrightnessProcess.command = ["brightnessctl", "set", pendingValue + "%"];
laptopBrightnessProcess.running = true;
}
}
function setBrightness(percentage) { function setBrightness(percentage) {
if (root.laptopBacklightAvailable) { brightnessLevel = Math.max(1, Math.min(100, percentage));
// Use laptop backlight control
root.laptopBrightnessLevel = percentage;
laptopDebounceTimer.pendingValue = percentage;
laptopDebounceTimer.restart();
} else {
// Use external monitor control
root.brightnessLevel = percentage;
debounceTimer.pendingValue = percentage;
debounceTimer.restart();
}
}
function increaseBrightness(): void {
if (root.laptopBacklightAvailable) {
setBrightness(Math.min(100, root.laptopBrightnessLevel + 10));
} else {
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
if (focusedMonitor)
focusedMonitor.setBrightness(focusedMonitor.brightness + 0.1);
}
}
function decreaseBrightness(): void {
if (root.laptopBacklightAvailable) {
setBrightness(Math.max(1, root.laptopBrightnessLevel - 10));
} else {
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
if (focusedMonitor)
focusedMonitor.setBrightness(focusedMonitor.brightness - 0.1);
}
}
onMonitorsChanged: {
ddcMonitors = [];
if (root.brightnessAvailable) {
ddcProc.running = true;
}
// Update brightness level from laptop or first monitor if (laptopBacklightAvailable) {
if (root.laptopBacklightAvailable) { laptopBrightnessProcess.command = ["brightnessctl", "set", brightnessLevel + "%"];
// Laptop brightness is already set by laptopBrightnessInitProcess laptopBrightnessProcess.running = true;
} else if (monitors.length > 0) { } else if (ddcAvailable) {
root.brightnessLevel = Math.round(monitors[0].brightness * 100); console.log("Setting DDC brightness to:", brightnessLevel);
Quickshell.execDetached(["ddcutil", "setvcp", "10", brightnessLevel.toString()]);
} }
} }
function increaseBrightness() {
setBrightness(brightnessLevel + 10);
}
function decreaseBrightness() {
setBrightness(brightnessLevel - 10);
}
Component.onCompleted: { Component.onCompleted: {
ddcAvailabilityChecker.running = true; ddcAvailabilityChecker.running = true;
laptopBacklightChecker.running = true; laptopBacklightChecker.running = true;
} }
Variants { onLaptopBacklightAvailableChanged: {
id: variants if (laptopBacklightAvailable) {
model: Quickshell.screens laptopBrightnessInitProcess.running = true;
Monitor {} }
}
onDdcAvailableChanged: {
if (ddcAvailable) {
ddcBrightnessInitProcess.running = true;
}
} }
Process { Process {
id: ddcAvailabilityChecker id: ddcAvailabilityChecker
command: ["which", "ddcutil"] command: ["which", "ddcutil"]
onExited: function(exitCode) { onExited: function(exitCode) {
root.brightnessAvailable = (exitCode === 0); ddcAvailable = (exitCode === 0);
if (root.brightnessAvailable) {
ddcProc.running = true;
}
} }
} }
@@ -117,10 +64,7 @@ Singleton {
id: laptopBacklightChecker id: laptopBacklightChecker
command: ["brightnessctl", "--list"] command: ["brightnessctl", "--list"]
onExited: function(exitCode) { onExited: function(exitCode) {
root.laptopBacklightAvailable = (exitCode === 0); laptopBacklightAvailable = (exitCode === 0);
if (root.laptopBacklightAvailable) {
laptopBrightnessInitProcess.running = true;
}
} }
} }
@@ -129,10 +73,7 @@ Singleton {
running: false running: false
onExited: function(exitCode) { onExited: function(exitCode) {
if (exitCode === 0) { if (exitCode !== 0) {
// Update was successful
root.brightnessLevel = root.laptopBrightnessLevel;
} else {
console.warn("Failed to set laptop brightness, exit code:", exitCode); console.warn("Failed to set laptop brightness, exit code:", exitCode);
} }
} }
@@ -146,9 +87,8 @@ Singleton {
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim()) {
// Get max brightness to calculate percentage currentRawBrightness = parseInt(text.trim());
laptopMaxBrightnessProcess.running = true; laptopMaxBrightnessProcess.running = true;
root.laptopBrightnessLevel = parseInt(text.trim());
} }
} }
} }
@@ -168,10 +108,8 @@ Singleton {
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim()) {
const maxBrightness = parseInt(text.trim()); maxBrightness = parseInt(text.trim());
const currentPercentage = Math.round((root.laptopBrightnessLevel / maxBrightness) * 100); brightnessLevel = Math.round((currentRawBrightness / maxBrightness) * 100);
root.laptopBrightnessLevel = currentPercentage;
root.brightnessLevel = currentPercentage;
} }
} }
} }
@@ -184,86 +122,28 @@ Singleton {
} }
Process { Process {
id: ddcProc id: ddcBrightnessInitProcess
command: ["ddcutil", "detect", "--brief"] command: ["ddcutil", "getvcp", "10", "--brief"]
running: false running: false
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim()) {
root.ddcMonitors = text.trim().split("\n\n").filter(function(d) { return d.startsWith("Display "); }).map(function(d) { return ({ const parts = text.trim().split(" ");
model: d.match(/Monitor:.*:(.*):.*/)?.[1] || "Unknown", if (parts.length >= 5) {
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)?.[1] || "0" const current = parseInt(parts[3]) || 75;
}); }); const max = parseInt(parts[4]) || 100;
} else { brightnessLevel = Math.round((current / max) * 100);
root.ddcMonitors = [];
}
}
}
onExited: function(exitCode) {
if (exitCode !== 0) {
root.ddcMonitors = [];
}
}
}
component Monitor: QtObject {
id: monitor
required property ShellScreen modelData
readonly property bool isDdc: root.ddcMonitors.some(function(m) { return m.model === modelData.model; })
readonly property string busNum: root.ddcMonitors.find(function(m) { return m.model === modelData.model; })?.busNum ?? ""
property real brightness: 0.75
readonly property Process initProc: Process {
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
const parts = text.trim().split(" ");
if (parts.length >= 5) {
const current = parseInt(parts[3]) || 75;
const max = parseInt(parts[4]) || 100;
monitor.brightness = current / max;
root.brightnessLevel = Math.round(monitor.brightness * 100);
}
} }
} }
} }
onExited: function(exitCode) {
if (exitCode !== 0) {
monitor.brightness = 0.75;
root.brightnessLevel = 75;
}
}
} }
function setBrightness(value: real): void { onExited: function(exitCode) {
value = Math.max(0, Math.min(1, value)); if (exitCode !== 0) {
const rounded = Math.round(value * 100); console.warn("Failed to get DDC brightness, exit code:", exitCode);
if (Math.round(brightness * 100) === rounded) brightnessLevel = 75;
return;
brightness = value;
root.brightnessLevel = rounded;
if (isDdc && busNum) {
Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded.toString()]);
} }
} }
onBusNumChanged: {
if (isDdc && busNum) {
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
initProc.running = true;
}
}
Component.onCompleted: {
Qt.callLater(function() {
if (isDdc && busNum) {
initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
initProc.running = true;
}
});
}
} }
} }

View File

@@ -39,11 +39,9 @@ Singleton {
function loadEvents(startDate, endDate) { function loadEvents(startDate, endDate) {
if (!root.khalAvailable) { if (!root.khalAvailable) {
console.warn("CalendarService: khal not available, skipping event loading");
return ; return ;
} }
if (eventsProcess.running) { if (eventsProcess.running) {
console.log("CalendarService: Event loading already in progress, skipping...");
return ; return ;
} }
// Store last requested date range for refresh timer // Store last requested date range for refresh timer
@@ -71,24 +69,9 @@ Singleton {
// Initialize on component completion // Initialize on component completion
Component.onCompleted: { Component.onCompleted: {
console.log("CalendarService: Component completed, initializing...");
checkKhalAvailability(); checkKhalAvailability();
} }
// Periodic refresh timer
Timer {
id: refreshTimer
interval: 60000
running: root.khalAvailable
repeat: true
onTriggered: {
if (lastStartDate && lastEndDate)
loadEvents(lastStartDate, lastEndDate);
}
}
// Process for checking khal configuration // Process for checking khal configuration
Process { Process {
id: khalCheckProcess id: khalCheckProcess
@@ -97,17 +80,10 @@ Singleton {
running: false running: false
onExited: (exitCode) => { onExited: (exitCode) => {
root.khalAvailable = (exitCode === 0); root.khalAvailable = (exitCode === 0);
if (exitCode !== 0) { if (exitCode === 0) {
console.warn("CalendarService: khal not configured (exit code:", exitCode, ")");
} else {
console.log("CalendarService: khal configured and available");
// Load current month events when khal becomes available
loadCurrentMonth(); loadCurrentMonth();
} }
} }
onStarted: {
console.log("CalendarService: Checking khal configuration...");
}
} }
// Process for loading events // Process for loading events
@@ -123,7 +99,6 @@ Singleton {
root.isLoading = false; root.isLoading = false;
if (exitCode !== 0) { if (exitCode !== 0) {
root.lastError = "Failed to load events (exit code: " + exitCode + ")"; root.lastError = "Failed to load events (exit code: " + exitCode + ")";
console.warn("CalendarService:", root.lastError);
return ; return ;
} }
try { try {
@@ -249,19 +224,14 @@ Singleton {
} }
root.eventsByDate = newEventsByDate; root.eventsByDate = newEventsByDate;
root.lastError = ""; root.lastError = "";
console.log("CalendarService: Loaded events for", Object.keys(newEventsByDate).length, "days");
} catch (error) { } catch (error) {
root.lastError = "Failed to parse events JSON: " + error.toString(); root.lastError = "Failed to parse events JSON: " + error.toString();
console.error("CalendarService:", root.lastError);
root.eventsByDate = { root.eventsByDate = {
}; };
} }
// Reset for next run // Reset for next run
eventsProcess.rawOutput = ""; eventsProcess.rawOutput = "";
} }
onStarted: {
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"), "to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"));
}
stdout: SplitParser { stdout: SplitParser {
splitMarker: "\n" splitMarker: "\n"

View File

@@ -1,19 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
Singleton {
id: root
signal showAppLauncher()
signal hideAppLauncher()
signal toggleAppLauncher()
signal showSpotlight()
signal hideSpotlight()
signal toggleSpotlight()
signal showClipboardHistory()
signal hideClipboardHistory()
signal toggleClipboardHistory()
}

View File

@@ -139,7 +139,6 @@ Singleton {
if (preferenceComplete) { if (preferenceComplete) {
root.changingPreference = false root.changingPreference = false
root.targetPreference = "" root.targetPreference = ""
preferenceTimeoutTimer.stop()
console.log("Network preference change completed successfully") console.log("Network preference change completed successfully")
} }
} }
@@ -379,26 +378,8 @@ Singleton {
} }
function delayedRefreshNetworkStatus() { function delayedRefreshNetworkStatus() {
console.log("Delayed network status refresh...") console.log("Refreshing network status immediately...")
refreshTimer.start() refreshNetworkStatus()
}
Timer {
id: refreshTimer
interval: 1000
onTriggered: {
refreshNetworkStatus()
}
}
Timer {
id: preferenceTimeoutTimer
interval: 15000 // 15 seconds timeout
onTriggered: {
console.log("Network preference change timeout - resetting changingPreference state")
root.changingPreference = false
root.targetPreference = ""
}
} }
function setNetworkPreference(preference) { function setNetworkPreference(preference) {
@@ -406,7 +387,6 @@ Singleton {
root.userPreference = preference root.userPreference = preference
root.changingPreference = true root.changingPreference = true
root.targetPreference = preference root.targetPreference = preference
preferenceTimeoutTimer.start()
Prefs.setNetworkPreference(preference) Prefs.setNetworkPreference(preference)
if (preference === "wifi") { if (preference === "wifi") {

View File

@@ -99,23 +99,13 @@ Singleton {
onExited: (exitCode) => { onExited: (exitCode) => {
if (exitCode !== 0 && root.niriAvailable) { if (exitCode !== 0 && root.niriAvailable) {
console.warn("NiriWorkspaceService: Event stream exited with code", exitCode, "restarting in 2 seconds") console.warn("NiriWorkspaceService: Event stream exited with code", exitCode, "restarting immediately")
restartTimer.start()
}
}
}
// Restart timer for event stream
Timer {
id: restartTimer
interval: 2000
onTriggered: {
if (root.niriAvailable) {
eventStreamProcess.running = true eventStreamProcess.running = true
} }
} }
} }
function handleNiriEvent(event) { function handleNiriEvent(event) {
if (event.WorkspacesChanged) { if (event.WorkspacesChanged) {
handleWorkspacesChanged(event.WorkspacesChanged) handleWorkspacesChanged(event.WorkspacesChanged)
@@ -173,6 +163,9 @@ Singleton {
currentOutput = activatedWs.output || "" currentOutput = activatedWs.output || ""
updateCurrentOutputWorkspaces() updateCurrentOutputWorkspaces()
// Force property change notifications
allWorkspacesChanged()
} else { } else {
focusedWorkspaceIndex = 0 focusedWorkspaceIndex = 0
} }

View File

@@ -333,31 +333,6 @@ Singleton {
console.log("ProcessMonitorService: Initialization complete"); console.log("ProcessMonitorService: Initialization complete");
} }
Timer {
id: testTimer
interval: 3000
running: false
repeat: false
onTriggered: {
console.log("ProcessMonitorService: Starting test monitoring...");
enableMonitoring(true);
stopTestTimer.start();
}
}
Timer {
id: stopTestTimer
interval: 8000
running: false
repeat: false
onTriggered: {
console.log("ProcessMonitorService: Stopping test monitoring...");
enableMonitoring(false);
}
}
Process { Process {
id: systemInfoProcess id: systemInfoProcess

View File

@@ -24,7 +24,6 @@ Singleton {
property string kernelVersion: "" property string kernelVersion: ""
property string distribution: "" property string distribution: ""
property string hostname: "" property string hostname: ""
property string uptime: ""
property string scheduler: "" property string scheduler: ""
property string architecture: "" property string architecture: ""
property string loadAverage: "" property string loadAverage: ""
@@ -61,7 +60,6 @@ Singleton {
kernelInfoProcess.running = true; kernelInfoProcess.running = true;
distributionProcess.running = true; distributionProcess.running = true;
hostnameProcess.running = true; hostnameProcess.running = true;
uptimeProcess.running = true;
schedulerProcess.running = true; schedulerProcess.running = true;
architectureProcess.running = true; architectureProcess.running = true;
loadAverageProcess.running = true; loadAverageProcess.running = true;
@@ -319,26 +317,6 @@ Singleton {
} }
Process {
id: uptimeProcess
command: ["bash", "-c", "uptime -p | sed 's/up //'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Uptime check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim())
root.uptime = text.trim();
}
}
}
Process { Process {
id: schedulerProcess id: schedulerProcess
@@ -546,55 +524,27 @@ Singleton {
} }
Timer { Timer {
id: cpuTimer id: basicStatsTimer
interval: root.cpuUpdateInterval interval: 5000
running: root.enabledForTopBar || root.enabledForDetailedView running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView) { cpuUsageProcess.running = true;
cpuUsageProcess.running = true; cpuFrequencyProcess.running = true;
cpuFrequencyProcess.running = true; memoryUsageProcess.running = true;
}
} }
} }
Timer { Timer {
id: memoryTimer id: detailedStatsTimer
interval: root.memoryUpdateInterval interval: 15000
running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView)
memoryUsageProcess.running = true;
}
}
Timer {
id: temperatureTimer
interval: root.temperatureUpdateInterval
running: root.enabledForDetailedView running: root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForDetailedView) temperatureProcess.running = true;
temperatureProcess.running = true; updateSystemInfo();
}
}
Timer {
id: systemInfoTimer
interval: root.systemInfoUpdateInterval
running: root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForDetailedView)
updateSystemInfo();
} }
} }

View File

@@ -44,7 +44,7 @@ Singleton {
currentLevel = levelInfo; currentLevel = levelInfo;
toastTimer.stop(); toastTimer.stop();
if (toastQueue.length > 0) if (toastQueue.length > 0)
queueTimer.start(); processQueue();
} }
@@ -56,7 +56,7 @@ Singleton {
currentMessage = toast.message; currentMessage = toast.message;
currentLevel = toast.level; currentLevel = toast.level;
toastVisible = true; toastVisible = true;
toastTimer.interval = toast.level === levelError ? 8000 : toast.level === levelWarn ? 6000 : 5000; toastTimer.interval = toast.level === levelError ? 5000 : toast.level === levelWarn ? 4000 : 3000;
toastTimer.start(); toastTimer.start();
} }
@@ -73,13 +73,5 @@ Singleton {
onTriggered: hideToast() onTriggered: hideToast()
} }
Timer {
id: queueTimer
interval: 500
running: false
repeat: false
onTriggered: processQueue()
}
} }

View File

@@ -36,17 +36,6 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
getUserInfo(); getUserInfo();
getUptime(); getUptime();
// Update uptime every minute
uptimeTimer.start();
}
Timer {
id: uptimeTimer
interval: 60000 // 1 minute
running: false
repeat: true
onTriggered: getUptime()
} }
// Get username and full name // Get username and full name
@@ -96,7 +85,6 @@ Singleton {
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
root.uptime = text.trim() || "Unknown"; root.uptime = text.trim() || "Unknown";
console.log("UserInfoService: Uptime updated -", root.uptime);
} }
} }

View File

@@ -23,11 +23,9 @@ Singleton {
return ; return ;
root.isScanning = true; root.isScanning = true;
console.log("Starting WiFi scan...");
wifiScanner.running = true; wifiScanner.running = true;
savedWifiScanner.running = true; savedWifiScanner.running = true;
currentWifiInfo.running = true; currentWifiInfo.running = true;
// Fallback timer in case no networks are found
fallbackTimer.start(); fallbackTimer.start();
} }
@@ -133,7 +131,6 @@ Singleton {
} }
function updateCurrentWifiInfo() { function updateCurrentWifiInfo() {
console.log("Updating current WiFi info...");
currentWifiInfo.running = true; currentWifiInfo.running = true;
} }
@@ -254,7 +251,6 @@ Singleton {
interval: 5000 interval: 5000
onTriggered: { onTriggered: {
root.isScanning = false; root.isScanning = false;
console.log("WiFi scan timeout - no networks found");
} }
} }
@@ -274,11 +270,7 @@ Singleton {
interval: 20000 interval: 20000
running: root.autoRefreshEnabled running: root.autoRefreshEnabled
repeat: true repeat: true
onTriggered: { onTriggered: root.scanWifi()
if (root.autoRefreshEnabled)
root.scanWifi();
}
} }
} }

View File

@@ -24,11 +24,6 @@ PanelWindow {
property int selectedIndex: 0 property int selectedIndex: 0
function updateFilteredModel() { function updateFilteredModel() {
if (!AppSearchService.ready) {
filteredModel.clear();
selectedIndex = 0;
return ;
}
filteredModel.clear(); filteredModel.clear();
selectedIndex = 0; selectedIndex = 0;
var apps = []; var apps = [];
@@ -118,7 +113,7 @@ PanelWindow {
var selectedApp = filteredModel.get(selectedIndex); var selectedApp = filteredModel.get(selectedIndex);
if (selectedApp.desktopEntry) { if (selectedApp.desktopEntry) {
Prefs.addRecentApp(selectedApp.desktopEntry); Prefs.addRecentApp(selectedApp.desktopEntry);
AppSearchService.launchApp(selectedApp.desktopEntry); selectedApp.desktopEntry.execute();
} else { } else {
launcher.launchApp(selectedApp.exec); launcher.launchApp(selectedApp.exec);
} }
@@ -130,7 +125,7 @@ PanelWindow {
// Try to find the desktop entry // Try to find the desktop entry
var app = AppSearchService.getAppByExec(exec); var app = AppSearchService.getAppByExec(exec);
if (app) { if (app) {
AppSearchService.launchApp(app); app.execute();
} else { } else {
// Fallback to direct execution // Fallback to direct execution
var cleanExec = exec.replace(/%[fFuU]/g, "").trim(); var cleanExec = exec.replace(/%[fFuU]/g, "").trim();
@@ -171,14 +166,12 @@ PanelWindow {
visible: isVisible visible: isVisible
color: "transparent" color: "transparent"
Component.onCompleted: { Component.onCompleted: {
if (AppSearchService.ready) { var allCategories = AppSearchService.getAllCategories();
var allCategories = AppSearchService.getAllCategories(); // Insert "Recents" after "All"
// Insert "Recents" after "All" categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
categories = ["All", "Recents"].concat(allCategories.filter((cat) => { return cat !== "All";
return cat !== "All"; }));
})); updateFilteredModel();
updateFilteredModel();
}
recentApps = Prefs.getRecentApps(); // Load recent apps on startup recentApps = Prefs.getRecentApps(); // Load recent apps on startup
} }
@@ -194,7 +187,7 @@ PanelWindow {
Timer { Timer {
id: searchDebounceTimer id: searchDebounceTimer
interval: 100 interval: 50
repeat: false repeat: false
onTriggered: updateFilteredModel() onTriggered: updateFilteredModel()
} }
@@ -226,56 +219,21 @@ PanelWindow {
} }
Connections {
function onReadyChanged() {
if (AppSearchService.ready) {
var allCategories = AppSearchService.getAllCategories();
// Insert "Recents" after "All"
categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
return cat !== "All";
}));
updateFilteredModel();
}
}
target: AppSearchService
}
Connections { Connections {
function onApplicationsChanged() { function onApplicationsChanged() {
console.log("AppLauncher: DesktopEntries.applicationsChanged signal received"); console.log("AppLauncher: DesktopEntries.applicationsChanged signal received");
// Update categories when applications change // Update categories when applications change
if (AppSearchService.ready) { console.log("AppLauncher: Updating categories and model due to applicationsChanged");
console.log("AppLauncher: Updating categories and model due to applicationsChanged"); var allCategories = AppSearchService.getAllCategories();
var allCategories = AppSearchService.getAllCategories(); categories = ["All", "Recents"].concat(allCategories.filter((cat) => {
categories = ["All", "Recents"].concat(allCategories.filter((cat) => { return cat !== "All";
return cat !== "All"; }));
})); updateFilteredModel();
updateFilteredModel();
} else {
console.log("AppLauncher: AppSearchService not ready, skipping update");
}
} }
target: DesktopEntries target: DesktopEntries
} }
Connections {
function onShowAppLauncher() {
launcher.show();
}
function onHideAppLauncher() {
launcher.hide();
}
function onToggleAppLauncher() {
launcher.toggle();
}
target: LauncherService
}
Connections { Connections {
function onRecentlyUsedAppsChanged() { function onRecentlyUsedAppsChanged() {
recentApps = Prefs.getRecentApps(); recentApps = Prefs.getRecentApps();
@@ -530,7 +488,7 @@ PanelWindow {
var firstApp = filteredModel.get(0); var firstApp = filteredModel.get(0);
if (firstApp.desktopEntry) { if (firstApp.desktopEntry) {
Prefs.addRecentApp(firstApp.desktopEntry); Prefs.addRecentApp(firstApp.desktopEntry);
AppSearchService.launchApp(firstApp.desktopEntry); firstApp.desktopEntry.execute();
} else { } else {
launcher.launchApp(firstApp.exec); launcher.launchApp(firstApp.exec);
} }
@@ -1033,7 +991,7 @@ PanelWindow {
onClicked: { onClicked: {
if (model.desktopEntry) { if (model.desktopEntry) {
Prefs.addRecentApp(model.desktopEntry); Prefs.addRecentApp(model.desktopEntry);
AppSearchService.launchApp(model.desktopEntry); model.desktopEntry.execute();
} else { } else {
launcher.launchApp(model.exec); launcher.launchApp(model.exec);
} }
@@ -1107,7 +1065,7 @@ PanelWindow {
onClicked: { onClicked: {
if (model.desktopEntry) { if (model.desktopEntry) {
Prefs.addRecentApp(model.desktopEntry); Prefs.addRecentApp(model.desktopEntry);
AppSearchService.launchApp(model.desktopEntry); model.desktopEntry.execute();
} else { } else {
launcher.launchApp(model.exec); launcher.launchApp(model.exec);
} }

View File

@@ -21,14 +21,12 @@ PanelWindow {
function setProfile(profile) { function setProfile(profile) {
if (typeof PowerProfiles === "undefined") { if (typeof PowerProfiles === "undefined") {
errorToast.show(); ToastService.showError("power-profiles-daemon not available");
return ; return ;
} }
PowerProfiles.profile = profile; PowerProfiles.profile = profile;
if (PowerProfiles.profile !== profile) if (PowerProfiles.profile !== profile)
errorToast.show(); ToastService.showError("Failed to set power profile");
else
console.log("Set power profile to: " + PowerProfile.toString(profile));
} }
visible: batteryPopupVisible visible: batteryPopupVisible
@@ -367,7 +365,7 @@ PanelWindow {
spacing: Theme.spacingM spacing: Theme.spacingM
Text { Text {
text: Theme.getPowerProfileIcon(PowerProfile.toString(modelData)) text: Theme.getPowerProfileIcon(modelData)
font.family: Theme.iconFont font.family: Theme.iconFont
font.pixelSize: Theme.iconSize font.pixelSize: Theme.iconSize
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
@@ -379,14 +377,14 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
Text { Text {
text: Theme.getPowerProfileLabel(PowerProfile.toString(modelData)) text: Theme.getPowerProfileLabel(modelData)
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
font.weight: batteryControlPopup.isActiveProfile(modelData) ? Font.Medium : Font.Normal font.weight: batteryControlPopup.isActiveProfile(modelData) ? Font.Medium : Font.Normal
} }
Text { Text {
text: Theme.getPowerProfileDescription(PowerProfile.toString(modelData)) text: Theme.getPowerProfileDescription(modelData)
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
} }
@@ -483,40 +481,5 @@ PanelWindow {
} }
// Error toast
Rectangle {
id: errorToast
function show() {
visible = true;
hideTimer.restart();
}
width: Math.min(300, parent.width - Theme.spacingL * 2)
height: 50
radius: Theme.cornerRadius
color: Theme.error
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Theme.spacingL
visible: false
z: 1000
Text {
anchors.centerIn: parent
text: "power-profiles-daemon not available"
color: "white"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
}
Timer {
id: hideTimer
interval: 3000
onTriggered: errorToast.visible = false
}
}
} }

View File

@@ -31,10 +31,6 @@ Column {
loadEventsForMonth(); loadEventsForMonth();
} }
Component.onCompleted: { Component.onCompleted: {
console.log("CalendarWidget: Component completed, CalendarService available:", !!CalendarService);
if (CalendarService)
console.log("CalendarWidget: khal available:", CalendarService.khalAvailable);
loadEventsForMonth(); loadEventsForMonth();
} }

View File

@@ -15,6 +15,11 @@ PanelWindow {
property bool calendarVisible: false property bool calendarVisible: false
visible: calendarVisible visible: calendarVisible
onVisibleChanged: {
if (visible && CalendarService) {
CalendarService.loadCurrentMonth();
}
}
implicitWidth: 480 implicitWidth: 480
implicitHeight: 600 implicitHeight: 600
WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.layer: WlrLayershell.Overlay

View File

@@ -23,9 +23,9 @@ Rectangle {
onActivePlayerChanged: { onActivePlayerChanged: {
if (!activePlayer) if (!activePlayer)
clearCacheTimer.restart(); updateTimer.start();
else else
clearCacheTimer.stop(); updateTimer.stop();
} }
width: parent.width width: parent.width
height: parent.height height: parent.height
@@ -36,30 +36,28 @@ Rectangle {
layer.enabled: true layer.enabled: true
Timer { Timer {
id: clearCacheTimer id: updateTimer
interval: 2000 interval: 2000
running: {
// Run when no active player (for cache clearing) OR when playing (for position updates)
return (!activePlayer) ||
(activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing &&
activePlayer.length > 0 && !progressMouseArea.isSeeking);
}
repeat: true
onTriggered: { onTriggered: {
if (!activePlayer) { if (!activePlayer) {
// Clear cache when no player
lastValidTitle = ""; lastValidTitle = "";
lastValidArtist = ""; lastValidArtist = "";
lastValidAlbum = ""; lastValidAlbum = "";
lastValidArtUrl = ""; lastValidArtUrl = "";
} stop(); // Stop after clearing cache
} } else if (activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
} // Update position when playing
// Updates progress bar every 2 seconds when playing
Timer {
id: positionTimer
interval: 2000
running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking
repeat: true
onTriggered: {
if (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking)
currentPosition = activePlayer.position; currentPosition = activePlayer.position;
}
} }
} }

View File

@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
@@ -23,6 +24,10 @@ PanelWindow {
if (!visible && BluetoothService.adapter && BluetoothService.adapter.discovering) { if (!visible && BluetoothService.adapter && BluetoothService.adapter.discovering) {
BluetoothService.adapter.discovering = false; BluetoothService.adapter.discovering = false;
} }
// Refresh uptime when opened
if (visible && UserInfoService) {
UserInfoService.getUptime();
}
} }
implicitWidth: 600 implicitWidth: 600
implicitHeight: 500 implicitHeight: 500
@@ -120,7 +125,7 @@ PanelWindow {
} }
] ]
Column { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
spacing: Theme.spacingM spacing: Theme.spacingM
@@ -698,7 +703,7 @@ PanelWindow {
// Tab content area // Tab content area
Rectangle { Rectangle {
width: parent.width width: parent.width
height: root.powerOptionsExpanded ? 240 : 300 Layout.fillHeight: true
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1) color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1)

View File

@@ -11,6 +11,16 @@ ScrollView {
id: displayTab id: displayTab
clip: true clip: true
property var brightnessDebounceTimer: Timer {
interval: BrightnessService.ddcAvailable ? 500 : 50 // 500ms for slow DDC (i2c), 50ms for fast laptop backlight
repeat: false
property int pendingValue: 0
onTriggered: {
console.log("Debounce timer fired, setting brightness to:", pendingValue);
BrightnessService.setBrightness(pendingValue);
}
}
Column { Column {
width: parent.width width: parent.width
@@ -36,8 +46,23 @@ ScrollView {
rightIcon: "brightness_high" rightIcon: "brightness_high"
enabled: BrightnessService.brightnessAvailable enabled: BrightnessService.brightnessAvailable
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
BrightnessService.setBrightness(newValue); console.log("Slider changed to:", newValue);
brightnessDebounceTimer.pendingValue = newValue;
brightnessDebounceTimer.restart();
} }
onSliderDragFinished: function(finalValue) {
console.log("Drag finished, immediate set:", finalValue);
brightnessDebounceTimer.stop();
BrightnessService.setBrightness(finalValue);
}
}
Text {
text: "using ddc - changes may take a moment to apply"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
visible: BrightnessService.ddcAvailable && !BrightnessService.laptopBacklightAvailable
anchors.horizontalCenter: parent.horizontalCenter
} }
} }

View File

@@ -191,7 +191,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
z: 10 z: 10
opacity: networkTab.changingNetworkPreference ? 0.6 : 1 opacity: networkTab.changingNetworkPreference ? 0.6 : 1
visible: networkTab.networkStatus !== "ethernet" visible: NetworkService.networkStatus !== "ethernet" && NetworkService.wifiAvailable && NetworkService.wifiEnabled
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
@@ -330,197 +330,215 @@ Item {
id: wifiContent id: wifiContent
width: parent.width width: parent.width
spacing: Theme.spacingL spacing: Theme.spacingM
// WiFi toggle control (only show if WiFi hardware is available)
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: wifiToggleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
visible: NetworkService.wifiAvailable
opacity: NetworkService.wifiToggling ? 0.6 : 1
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Text {
id: wifiToggleIcon
text: NetworkService.wifiToggling ? "sync" : "power_settings_new"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
anchors.verticalCenter: parent.verticalCenter
rotation: NetworkService.wifiToggling ? wifiToggleIcon.rotation : 0
RotationAnimation {
target: wifiToggleIcon
property: "rotation"
running: NetworkService.wifiToggling
from: 0
to: 360
duration: 1000
loops: Animation.Infinite
}
Behavior on rotation {
RotationAnimation {
duration: 200
easing.type: Easing.OutQuad
}
}
}
Text {
text: NetworkService.wifiToggling ? "Switching WiFi..." : (NetworkService.wifiEnabled ? "Turn WiFi Off" : "Turn WiFi On")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: wifiToggleArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
NetworkService.toggleWifiRadio();
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Current WiFi connection (if connected) // Current WiFi connection (if connected)
Rectangle { Rectangle {
width: parent.width width: parent.width
height: 80 height: 60
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5) color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: networkTab.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: NetworkService.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: networkTab.networkStatus === "wifi" ? 2 : 1 border.width: NetworkService.networkStatus === "wifi" ? 2 : 1
visible: NetworkService.wifiAvailable && NetworkService.wifiEnabled visible: NetworkService.wifiAvailable
Row { // WiFi icon
Text {
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Theme.spacingL anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM text: {
if (!NetworkService.wifiEnabled) {
return "wifi_off";
} else if (NetworkService.networkStatus === "wifi") {
return WifiService.wifiSignalStrength === "excellent" ? "wifi" : WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" : WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" : WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi";
} else {
return "wifi";
}
}
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
}
// WiFi info text
Column {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL + Theme.iconSize + Theme.spacingM
anchors.right: parent.right
anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: 4
Text { Text {
text: networkTab.networkStatus === "wifi" ? (WifiService.wifiSignalStrength === "excellent" ? "wifi" : WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" : WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" : WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi") : "wifi" text: {
font.family: Theme.iconFont if (!NetworkService.wifiEnabled) {
font.pixelSize: Theme.iconSizeLarge return "WiFi is off";
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText } else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
anchors.verticalCenter: parent.verticalCenter return WifiService.currentWifiSSID || "Connected";
} else {
return "Not Connected";
}
}
font.pixelSize: Theme.fontSizeMedium
color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
font.weight: Font.Medium
} }
Column { Text {
spacing: 4 text: {
if (!NetworkService.wifiEnabled) {
return "Turn on WiFi to see available networks";
} else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
return NetworkService.wifiIP || "Connected";
} else {
return "Select a network below";
}
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
}
// WiFi toggle switch
Rectangle {
width: 48
height: 24
radius: 12
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
anchors.right: parent.right
anchors.rightMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
opacity: NetworkService.wifiToggling ? 0.6 : 1
Rectangle {
id: toggleHandle
width: 20
height: 20
radius: 10
color: Theme.surface
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
x: NetworkService.wifiEnabled ? parent.width - width - 2 : 2
Behavior on x {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
// Subtle shadow/glow effect
Rectangle {
anchors.centerIn: parent
width: parent.width + 2
height: parent.height + 2
radius: (parent.width + 2) / 2
color: "transparent"
border.color: Qt.rgba(0, 0, 0, 0.1)
border.width: 1
z: -1
}
}
MouseArea {
id: wifiToggleArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
NetworkService.toggleWifiRadio();
// Refresh network status and WiFi info after toggle with delay
refreshTimer.triggered = true;
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Force WiFi preference button
Rectangle {
width: 150
height: 30
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surface
border.color: Theme.primary
border.width: 1
radius: 6
anchors.right: parent.right
anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
visible: NetworkService.networkStatus !== "wifi" && NetworkService.ethernetConnected && NetworkService.wifiEnabled
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text { Text {
text: NetworkService.networkStatus === "wifi" ? (WifiService.currentWifiSSID || "Connected") : "Not Connected" id: wifiPreferenceIcon
font.pixelSize: Theme.fontSizeLarge
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText text: networkTab.changingNetworkPreference ? "sync" : ""
font.family: Theme.iconFont
font.pixelSize: Theme.fontSizeSmall
color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary
visible: networkTab.changingNetworkPreference
anchors.verticalCenter: parent.verticalCenter
rotation: networkTab.changingNetworkPreference ? wifiPreferenceIcon.rotation : 0
RotationAnimation {
target: wifiPreferenceIcon
property: "rotation"
running: networkTab.changingNetworkPreference
from: 0
to: 360
duration: 1000
loops: Animation.Infinite
}
}
Text {
text: NetworkService.changingNetworkPreference ? "Switching..." : "Prefer over Ethernet"
font.pixelSize: Theme.fontSizeSmall
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
anchors.verticalCenter: parent.verticalCenter
font.weight: Font.Medium font.weight: Font.Medium
} }
Text {
text: NetworkService.networkStatus === "wifi" ? (NetworkService.wifiIP || "Connected") : "Select a network below"
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
}
} }
// Force WiFi preference button MouseArea {
Rectangle { anchors.fill: parent
width: 150 hoverEnabled: true
height: 30 cursorShape: Qt.PointingHandCursor
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surface propagateComposedEvents: false
border.color: Theme.primary enabled: !networkTab.changingNetworkPreference
border.width: 1 onClicked: {
radius: 6 console.log("Force WiFi preference clicked");
anchors.verticalCenter: parent.verticalCenter if (NetworkService.networkStatus !== "wifi")
opacity: networkTab.changingNetworkPreference ? 0.6 : 1 NetworkService.setNetworkPreference("wifi");
visible: networkTab.networkStatus !== "wifi" else
NetworkService.setNetworkPreference("auto");
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
id: wifiPreferenceIcon
text: networkTab.changingNetworkPreference ? "sync" : ""
font.family: Theme.iconFont
font.pixelSize: Theme.fontSizeSmall
color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary
visible: networkTab.changingNetworkPreference
anchors.verticalCenter: parent.verticalCenter
rotation: networkTab.changingNetworkPreference ? wifiPreferenceIcon.rotation : 0
RotationAnimation {
target: wifiPreferenceIcon
property: "rotation"
running: networkTab.changingNetworkPreference
from: 0
to: 360
duration: 1000
loops: Animation.Infinite
}
}
Text {
text: NetworkService.changingNetworkPreference ? "Switching..." : (NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
font.pixelSize: Theme.fontSizeSmall
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
anchors.verticalCenter: parent.verticalCenter
font.weight: Font.Medium
}
} }
}
MouseArea { Behavior on opacity {
anchors.fill: parent NumberAnimation {
hoverEnabled: true duration: Theme.shortDuration
cursorShape: Qt.PointingHandCursor easing.type: Theme.standardEasing
propagateComposedEvents: false
enabled: !networkTab.changingNetworkPreference
onClicked: {
console.log("Force WiFi preference clicked");
if (NetworkService.networkStatus !== "wifi")
NetworkService.setNetworkPreference("wifi");
else
NetworkService.setNetworkPreference("auto");
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
} }
} }
@@ -532,7 +550,7 @@ Item {
// Available WiFi Networks // Available WiFi Networks
Column { Column {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingS
visible: NetworkService.wifiEnabled visible: NetworkService.wifiEnabled
Row { Row {
@@ -607,7 +625,7 @@ Item {
// Connection status indicator // Connection status indicator
Rectangle { Rectangle {
width: parent.width width: parent.width
height: 40 height: 32
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (WifiService.connectionStatus === "connecting") if (WifiService.connectionStatus === "connecting")
@@ -733,7 +751,7 @@ Item {
Rectangle { Rectangle {
width: parent.width width: parent.width
height: 50 height: 42
radius: Theme.cornerRadiusSmall radius: Theme.cornerRadiusSmall
color: networkArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: networkArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
border.color: modelData.connected ? Theme.primary : "transparent" border.color: modelData.connected ? Theme.primary : "transparent"
@@ -741,7 +759,7 @@ Item {
Item { Item {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingM anchors.margins: Theme.spacingS
// Signal strength icon // Signal strength icon
Text { Text {
@@ -758,9 +776,9 @@ Item {
// Network info // Network info
Column { Column {
anchors.left: signalIcon.right anchors.left: signalIcon.right
anchors.leftMargin: Theme.spacingM anchors.leftMargin: Theme.spacingS
anchors.right: rightIcons.left anchors.right: rightIcons.left
anchors.rightMargin: Theme.spacingM anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: 2 spacing: 2
@@ -916,4 +934,30 @@ Item {
} }
// Timer for refreshing network status after WiFi toggle
Timer {
id: refreshTimer
interval: 2000
running: networkTab.visible && refreshTimer.triggered
property bool triggered: false
onTriggered: {
NetworkService.refreshNetworkStatus();
if (NetworkService.wifiEnabled) {
WifiService.scanWifi();
}
triggered = false;
}
}
// Auto-refresh when WiFi state changes
Connections {
target: NetworkService
function onWifiEnabledChanged() {
if (NetworkService.wifiEnabled && networkTab.visible) {
// When WiFi is enabled, scan and update info (only if tab is visible)
WifiService.scanWifi();
}
}
}
} }

View File

@@ -17,6 +17,8 @@ Item {
signal sliderDragFinished(int finalValue) signal sliderDragFinished(int finalValue)
height: 80 height: 80
property bool isDragging: false
Column { Column {
anchors.fill: parent anchors.fill: parent
@@ -139,7 +141,8 @@ Item {
preventStealing: true preventStealing: true
onPressed: (mouse) => { onPressed: (mouse) => {
if (slider.enabled) { if (slider.enabled) {
isDragging = true; slider.isDragging = true;
sliderMouseArea.isDragging = true;
let ratio = Math.max(0, Math.min(1, mouse.x / width)); let ratio = Math.max(0, Math.min(1, mouse.x / width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum)); let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
slider.value = newValue; slider.value = newValue;
@@ -148,12 +151,13 @@ Item {
} }
onReleased: { onReleased: {
if (slider.enabled) { if (slider.enabled) {
isDragging = false; slider.isDragging = false;
sliderMouseArea.isDragging = false;
slider.sliderDragFinished(slider.value); slider.sliderDragFinished(slider.value);
} }
} }
onPositionChanged: (mouse) => { onPositionChanged: (mouse) => {
if (pressed && isDragging && slider.enabled) { if (pressed && slider.isDragging && slider.enabled) {
let ratio = Math.max(0, Math.min(1, mouse.x / width)); let ratio = Math.max(0, Math.min(1, mouse.x / width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum)); let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
slider.value = newValue; slider.value = newValue;
@@ -161,7 +165,7 @@ Item {
} }
} }
onClicked: (mouse) => { onClicked: (mouse) => {
if (slider.enabled) { if (slider.enabled && !slider.isDragging) {
let ratio = Math.max(0, Math.min(1, mouse.x / width)); let ratio = Math.max(0, Math.min(1, mouse.x / width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum)); let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
slider.value = newValue; slider.value = newValue;
@@ -175,11 +179,11 @@ Item {
id: sliderGlobalMouseArea id: sliderGlobalMouseArea
anchors.fill: sliderContainer anchors.fill: sliderContainer
enabled: sliderMouseArea.isDragging enabled: slider.isDragging
visible: false visible: false
preventStealing: true preventStealing: true
onPositionChanged: (mouse) => { onPositionChanged: (mouse) => {
if (sliderMouseArea.isDragging && slider.enabled) { if (slider.isDragging && slider.enabled) {
let globalPos = mapToItem(sliderTrack, mouse.x, mouse.y); let globalPos = mapToItem(sliderTrack, mouse.x, mouse.y);
let ratio = Math.max(0, Math.min(1, globalPos.x / sliderTrack.width)); let ratio = Math.max(0, Math.min(1, globalPos.x / sliderTrack.width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum)); let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
@@ -188,7 +192,8 @@ Item {
} }
} }
onReleased: { onReleased: {
if (sliderMouseArea.isDragging && slider.enabled) { if (slider.isDragging && slider.enabled) {
slider.isDragging = false;
sliderMouseArea.isDragging = false; sliderMouseArea.isDragging = false;
slider.sliderDragFinished(slider.value); slider.sliderDragFinished(slider.value);
} }

View File

@@ -22,6 +22,7 @@ PanelWindow {
ProcessMonitorService.updateProcessList(); ProcessMonitorService.updateProcessList();
SystemMonitorService.enableDetailedMonitoring(true); SystemMonitorService.enableDetailedMonitoring(true);
SystemMonitorService.updateSystemInfo(); SystemMonitorService.updateSystemInfo();
UserInfoService.getUptime();
} }
function hide() { function hide() {
@@ -1479,7 +1480,7 @@ PanelWindow {
} }
Text { Text {
text: SystemMonitorService.uptime text: UserInfoService.uptime
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Bold font.weight: Font.Bold
color: Theme.surfaceText color: Theme.surfaceText

View File

@@ -58,12 +58,6 @@ PanelWindow {
} }
function updateFilteredApps() { function updateFilteredApps() {
if (!AppSearchService.ready) {
filteredApps = [];
selectedIndex = 0;
filteredModel.clear();
return ;
}
filteredApps = []; filteredApps = [];
selectedIndex = 0; selectedIndex = 0;
var apps = []; var apps = [];
@@ -149,7 +143,7 @@ PanelWindow {
function launchApp(app) { function launchApp(app) {
Prefs.addRecentApp(app); Prefs.addRecentApp(app);
if (app.desktopEntry) { if (app.desktopEntry) {
AppSearchService.launchApp(app.desktopEntry); app.desktopEntry.execute();
} else { } else {
var cleanExec = app.exec.replace(/%[fFuU]/g, "").trim(); var cleanExec = app.exec.replace(/%[fFuU]/g, "").trim();
console.log("Spotlight: Launching app directly:", cleanExec); console.log("Spotlight: Launching app directly:", cleanExec);
@@ -213,23 +207,21 @@ PanelWindow {
color: "transparent" color: "transparent"
Component.onCompleted: { Component.onCompleted: {
console.log("SpotlightLauncher: Component.onCompleted called - component loaded successfully!"); console.log("SpotlightLauncher: Component.onCompleted called - component loaded successfully!");
if (AppSearchService.ready) { var allCategories = AppSearchService.getAllCategories().filter((cat) => {
var allCategories = AppSearchService.getAllCategories().filter((cat) => { return cat !== "Education" && cat !== "Science";
return cat !== "Education" && cat !== "Science"; });
}); // Insert "Recents" after "All"
// Insert "Recents" after "All" var result = ["All", "Recents"];
var result = ["All", "Recents"]; categories = result.concat(allCategories.filter((cat) => {
categories = result.concat(allCategories.filter((cat) => { return cat !== "All";
return cat !== "All"; }));
}));
}
} }
// Search debouncing // Search debouncing
Timer { Timer {
id: searchDebounceTimer id: searchDebounceTimer
interval: 100 interval: 50
repeat: false repeat: false
onTriggered: updateFilteredApps() onTriggered: updateFilteredApps()
} }
@@ -262,7 +254,7 @@ PanelWindow {
} }
} }
target: AppSearchService target: DesktopEntries
} }
Connections { Connections {

View File

@@ -5,13 +5,17 @@ import qs.Services
Rectangle { Rectangle {
id: root id: root
signal clicked()
property bool isActive: false
readonly property bool nerdFontAvailable: Qt.fontFamilies() readonly property bool nerdFontAvailable: Qt.fontFamilies()
.indexOf("Symbols Nerd Font") !== -1 .indexOf("Symbols Nerd Font") !== -1
width: 40 width: 40
height: 30 height: 30
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: launcherArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) color: launcherArea.containsMouse || isActive ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08)
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
@@ -28,9 +32,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: root.clicked()
LauncherService.toggleAppLauncher();
}
} }
Behavior on color { Behavior on color {

View File

@@ -67,7 +67,7 @@ Rectangle {
trayItem.activate(); trayItem.activate();
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
if (trayItem.hasMenu) if (trayItem && trayItem.hasMenu)
customTrayMenu.showMenu(mouse.x, mouse.y); customTrayMenu.showMenu(mouse.x, mouse.y);
} }

View File

@@ -126,6 +126,10 @@ PanelWindow {
LauncherButton { LauncherButton {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
isActive: launcher.isVisible
onClicked: {
appLauncher.toggle();
}
} }
WorkspaceSwitcher { WorkspaceSwitcher {

View File

@@ -95,7 +95,7 @@ Rectangle {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", sequentialNumber.toString()]); Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", modelData.toString()]);
} }
} }

View File

@@ -61,7 +61,7 @@ PanelWindow {
QsMenuOpener { QsMenuOpener {
id: menuOpener id: menuOpener
menu: currentTrayItem ? currentTrayItem.menu : null menu: currentTrayItem && currentTrayItem.hasMenu ? currentTrayItem.menu : null
} }
// Custom menu styling using ListView // Custom menu styling using ListView
@@ -93,12 +93,7 @@ PanelWindow {
text: "M" text: "M"
} }
model: ScriptModel { model: menuOpener.children
values: menuOpener.children ? [pe_unknown].filter((item) => {
// Filter out empty items and separators
return item && item.text && item.text.trim().length > 0 && !item.isSeparator;
}) : []
}
delegate: Rectangle { delegate: Rectangle {
width: ListView.view.width width: ListView.view.width