1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-14 17:52:10 -04: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: ({})
property var applicationsByExec: ({})
property bool ready: false
property var preppedApps: []
Component.onCompleted: {
loadApplications()
}
Connections {
target: DesktopEntries
function onApplicationsChanged() {
console.log("AppSearchService: DesktopEntries applicationsChanged signal received")
console.log("AppSearchService: Current applications count before reload:", applications.length)
loadApplications()
}
}
function loadApplications() {
Qt.callLater(function() {
var allApps = Array.from(DesktopEntries.applications.values)
applications = allApps
.filter(app => !app.noDisplay) .filter(app => !app.noDisplay)
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => a.name.localeCompare(b.name))
property var applicationsByName: {
var byName = {} var byName = {}
var byExec = {}
for (var i = 0; i < applications.length; i++) { for (var i = 0; i < applications.length; i++) {
var app = applications[i] var app = applications[i]
byName[app.name.toLowerCase()] = app byName[app.name.toLowerCase()] = app
}
return byName
}
property var applicationsByExec: {
var byExec = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
var execProp = app.execString || "" var execProp = app.execString || ""
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : "" var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
if (cleanExec) { if (cleanExec) {
byExec[cleanExec] = app byExec[cleanExec] = app
} }
} }
return byExec
}
applicationsByName = byName property var preppedApps: applications.map(app => ({
applicationsByExec = byExec
preppedApps = applications.map(app => ({
name: Fuzzy.prepare(app.name || ""), name: Fuzzy.prepare(app.name || ""),
comment: Fuzzy.prepare(app.comment || ""), comment: Fuzzy.prepare(app.comment || ""),
entry: app 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)
})
}
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,87 +8,31 @@ 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; if (laptopBacklightAvailable) {
laptopDebounceTimer.pendingValue = percentage; laptopBrightnessProcess.command = ["brightnessctl", "set", brightnessLevel + "%"];
laptopDebounceTimer.restart(); laptopBrightnessProcess.running = true;
} else { } else if (ddcAvailable) {
// Use external monitor control console.log("Setting DDC brightness to:", brightnessLevel);
root.brightnessLevel = percentage; Quickshell.execDetached(["ddcutil", "setvcp", "10", brightnessLevel.toString()]);
debounceTimer.pendingValue = percentage;
debounceTimer.restart();
} }
} }
function increaseBrightness(): void { function increaseBrightness() {
if (root.laptopBacklightAvailable) { setBrightness(brightnessLevel + 10);
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 { function decreaseBrightness() {
if (root.laptopBacklightAvailable) { setBrightness(brightnessLevel - 10);
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 (root.laptopBacklightAvailable) {
// Laptop brightness is already set by laptopBrightnessInitProcess
} else if (monitors.length > 0) {
root.brightnessLevel = Math.round(monitors[0].brightness * 100);
}
} }
Component.onCompleted: { Component.onCompleted: {
@@ -96,20 +40,23 @@ Singleton {
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,37 +122,10 @@ Singleton {
} }
Process { Process {
id: ddcProc id: ddcBrightnessInitProcess
command: ["ddcutil", "detect", "--brief"] command: ["ddcutil", "getvcp", "10", "--brief"]
running: false running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.ddcMonitors = text.trim().split("\n\n").filter(function(d) { return d.startsWith("Display "); }).map(function(d) { return ({
model: d.match(/Monitor:.*:(.*):.*/)?.[1] || "Unknown",
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)?.[1] || "0"
}); });
} else {
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 { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim()) {
@@ -222,48 +133,17 @@ Singleton {
if (parts.length >= 5) { if (parts.length >= 5) {
const current = parseInt(parts[3]) || 75; const current = parseInt(parts[3]) || 75;
const max = parseInt(parts[4]) || 100; const max = parseInt(parts[4]) || 100;
monitor.brightness = current / max; brightnessLevel = Math.round((current / max) * 100);
root.brightnessLevel = Math.round(monitor.brightness * 100);
} }
} }
} }
} }
onExited: function(exitCode) { onExited: function(exitCode) {
if (exitCode !== 0) { if (exitCode !== 0) {
monitor.brightness = 0.75; console.warn("Failed to get DDC brightness, exit code:", exitCode);
root.brightnessLevel = 75; brightnessLevel = 75;
} }
} }
} }
function setBrightness(value: real): void {
value = Math.max(0, Math.min(1, value));
const rounded = Math.round(value * 100);
if (Math.round(brightness * 100) === rounded)
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,34 +378,15 @@ Singleton {
} }
function delayedRefreshNetworkStatus() { function delayedRefreshNetworkStatus() {
console.log("Delayed network status refresh...") console.log("Refreshing network status immediately...")
refreshTimer.start()
}
Timer {
id: refreshTimer
interval: 1000
onTriggered: {
refreshNetworkStatus() 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) {
console.log("Setting network preference to:", preference) console.log("Setting network preference to:", preference)
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;
}
}
}
Timer {
id: memoryTimer
interval: root.memoryUpdateInterval
running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView)
memoryUsageProcess.running = true; memoryUsageProcess.running = true;
} }
} }
Timer { Timer {
id: temperatureTimer id: detailedStatsTimer
interval: root.temperatureUpdateInterval interval: 15000
running: root.enabledForDetailedView running: root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForDetailedView)
temperatureProcess.running = true; temperatureProcess.running = true;
}
}
Timer {
id: systemInfoTimer
interval: root.systemInfoUpdateInterval
running: root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForDetailedView)
updateSystemInfo(); 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

@@ -12,6 +12,16 @@ ScrollView {
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
spacing: Theme.spacingL spacing: Theme.spacingL
@@ -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,61 +330,115 @@ Item {
id: wifiContent id: wifiContent
width: parent.width width: parent.width
spacing: Theme.spacingL
// 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 spacing: Theme.spacingM
Text {
id: wifiToggleIcon
text: NetworkService.wifiToggling ? "sync" : "power_settings_new" // Current WiFi connection (if connected)
Rectangle {
width: parent.width
height: 60
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5)
border.color: NetworkService.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: NetworkService.networkStatus === "wifi" ? 2 : 1
visible: NetworkService.wifiAvailable
// WiFi icon
Text {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
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.family: Theme.iconFont
font.pixelSize: Theme.iconSize font.pixelSize: Theme.iconSize
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5) 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 anchors.verticalCenter: parent.verticalCenter
rotation: NetworkService.wifiToggling ? wifiToggleIcon.rotation : 0 spacing: 4
RotationAnimation { Text {
target: wifiToggleIcon text: {
property: "rotation" if (!NetworkService.wifiEnabled) {
running: NetworkService.wifiToggling return "WiFi is off";
from: 0 } else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
to: 360 return WifiService.currentWifiSSID || "Connected";
duration: 1000 } else {
loops: Animation.Infinite return "Not Connected";
} }
Behavior on rotation {
RotationAnimation {
duration: 200
easing.type: Easing.OutQuad
} }
font.pixelSize: Theme.fontSizeMedium
} color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
font.weight: Font.Medium
} }
Text { Text {
text: NetworkService.wifiToggling ? "Switching WiFi..." : (NetworkService.wifiEnabled ? "Turn WiFi Off" : "Turn WiFi On") text: {
font.pixelSize: Theme.fontSizeMedium if (!NetworkService.wifiEnabled) {
color: Theme.surfaceText return "Turn on WiFi to see available networks";
font.weight: Font.Medium } else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
anchors.verticalCenter: parent.verticalCenter 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
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 { MouseArea {
@@ -395,6 +449,15 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
NetworkService.toggleWifiRadio(); 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
} }
} }
@@ -403,52 +466,7 @@ Item {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
}
// Current WiFi connection (if connected)
Rectangle {
width: parent.width
height: 80
radius: Theme.cornerRadius
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.width: networkTab.networkStatus === "wifi" ? 2 : 1
visible: NetworkService.wifiAvailable && NetworkService.wifiEnabled
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
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"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSizeLarge
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 4
anchors.verticalCenter: parent.verticalCenter
Text {
text: NetworkService.networkStatus === "wifi" ? (WifiService.currentWifiSSID || "Connected") : "Not Connected"
font.pixelSize: Theme.fontSizeLarge
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
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 // Force WiFi preference button
@@ -459,9 +477,11 @@ Item {
border.color: Theme.primary border.color: Theme.primary
border.width: 1 border.width: 1
radius: 6 radius: 6
anchors.right: parent.right
anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
opacity: networkTab.changingNetworkPreference ? 0.6 : 1 opacity: networkTab.changingNetworkPreference ? 0.6 : 1
visible: networkTab.networkStatus !== "wifi" visible: NetworkService.networkStatus !== "wifi" && NetworkService.ethernetConnected && NetworkService.wifiEnabled
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
@@ -491,7 +511,7 @@ Item {
} }
Text { Text {
text: NetworkService.changingNetworkPreference ? "Switching..." : (NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet") text: NetworkService.changingNetworkPreference ? "Switching..." : "Prefer over Ethernet"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -527,12 +547,10 @@ 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

@@ -18,6 +18,8 @@ Item {
height: 80 height: 80
property bool isDragging: false
Column { Column {
anchors.fill: parent anchors.fill: parent
spacing: Theme.spacingM spacing: Theme.spacingM
@@ -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,7 +207,6 @@ 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";
}); });
@@ -223,13 +216,12 @@ PanelWindow {
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