1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -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
UNUSED
.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.
## 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
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 Quickshell
import Quickshell.Io
import Quickshell.Services.UPower
Singleton {
id: root
@@ -545,11 +546,11 @@ Singleton {
function getPowerProfileIcon(profile) {
switch (profile) {
case "power-saver":
case PowerProfile.PowerSaver:
return "battery_saver";
case "balanced":
case PowerProfile.Balanced:
return "battery_std";
case "performance":
case PowerProfile.Performance:
return "flash_on";
default:
return "settings";
@@ -557,12 +558,13 @@ Singleton {
}
function getPowerProfileLabel(profile) {
console.log("Theme.getPowerProfileLabel called with profile:", profile);
switch (profile) {
case "power-saver":
case PowerProfile.PowerSaver:
return "Power Saver";
case "balanced":
case PowerProfile.Balanced:
return "Balanced";
case "performance":
case PowerProfile.Performance:
return "Performance";
default:
return profile.charAt(0).toUpperCase() + profile.slice(1);
@@ -571,11 +573,11 @@ Singleton {
function getPowerProfileDescription(profile) {
switch (profile) {
case "power-saver":
case PowerProfile.PowerSaver:
return "Extend battery life";
case "balanced":
case PowerProfile.Balanced:
return "Balance power and performance";
case "performance":
case PowerProfile.Performance:
return "Prioritize performance";
default:
return "Custom power profile";

View File

@@ -10,73 +10,44 @@ import "../Common/fuzzysort.js" as Fuzzy
Singleton {
id: root
property list<DesktopEntry> applications: []
property var applicationsByName: ({})
property var applicationsByExec: ({})
property bool ready: false
property list<DesktopEntry> applications: Array.from(DesktopEntries.applications.values)
.filter(app => !app.noDisplay)
.sort((a, b) => a.name.localeCompare(b.name))
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()
property var applicationsByName: {
var byName = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
byName[app.name.toLowerCase()] = app
}
return byName
}
function loadApplications() {
Qt.callLater(function() {
var allApps = Array.from(DesktopEntries.applications.values)
applications = allApps
.filter(app => !app.noDisplay)
.sort((a, b) => a.name.localeCompare(b.name))
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
}
property var applicationsByExec: {
var byExec = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
var execProp = app.execString || ""
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
if (cleanExec) {
byExec[cleanExec] = app
}
applicationsByName = byName
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)
})
}
return byExec
}
property var preppedApps: applications.map(app => ({
name: Fuzzy.prepare(app.name || ""),
comment: Fuzzy.prepare(app.comment || ""),
entry: app
}))
function searchApplications(query) {
if (!query || query.length === 0) {
return applications
}
if (!ready || preppedApps.length === 0) {
if (preppedApps.length === 0) {
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 {
id: root
property list<var> ddcMonitors: []
readonly property list<Monitor> monitors: variants.instances
property bool brightnessAvailable: laptopBacklightAvailable || ddcMonitors.length > 0
property bool brightnessAvailable: laptopBacklightAvailable || ddcAvailable
property bool laptopBacklightAvailable: false
property bool ddcAvailable: false
property int brightnessLevel: 75
property int laptopBrightnessLevel: 75
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;
}
}
property int maxBrightness: 100
property int currentRawBrightness: 0
function setBrightness(percentage) {
if (root.laptopBacklightAvailable) {
// 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;
}
brightnessLevel = Math.max(1, Math.min(100, percentage));
// 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);
if (laptopBacklightAvailable) {
laptopBrightnessProcess.command = ["brightnessctl", "set", brightnessLevel + "%"];
laptopBrightnessProcess.running = true;
} else if (ddcAvailable) {
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: {
ddcAvailabilityChecker.running = true;
laptopBacklightChecker.running = true;
}
Variants {
id: variants
model: Quickshell.screens
Monitor {}
onLaptopBacklightAvailableChanged: {
if (laptopBacklightAvailable) {
laptopBrightnessInitProcess.running = true;
}
}
onDdcAvailableChanged: {
if (ddcAvailable) {
ddcBrightnessInitProcess.running = true;
}
}
Process {
id: ddcAvailabilityChecker
command: ["which", "ddcutil"]
onExited: function(exitCode) {
root.brightnessAvailable = (exitCode === 0);
if (root.brightnessAvailable) {
ddcProc.running = true;
}
ddcAvailable = (exitCode === 0);
}
}
@@ -117,10 +64,7 @@ Singleton {
id: laptopBacklightChecker
command: ["brightnessctl", "--list"]
onExited: function(exitCode) {
root.laptopBacklightAvailable = (exitCode === 0);
if (root.laptopBacklightAvailable) {
laptopBrightnessInitProcess.running = true;
}
laptopBacklightAvailable = (exitCode === 0);
}
}
@@ -129,10 +73,7 @@ Singleton {
running: false
onExited: function(exitCode) {
if (exitCode === 0) {
// Update was successful
root.brightnessLevel = root.laptopBrightnessLevel;
} else {
if (exitCode !== 0) {
console.warn("Failed to set laptop brightness, exit code:", exitCode);
}
}
@@ -146,9 +87,8 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
// Get max brightness to calculate percentage
currentRawBrightness = parseInt(text.trim());
laptopMaxBrightnessProcess.running = true;
root.laptopBrightnessLevel = parseInt(text.trim());
}
}
}
@@ -168,10 +108,8 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
const maxBrightness = parseInt(text.trim());
const currentPercentage = Math.round((root.laptopBrightnessLevel / maxBrightness) * 100);
root.laptopBrightnessLevel = currentPercentage;
root.brightnessLevel = currentPercentage;
maxBrightness = parseInt(text.trim());
brightnessLevel = Math.round((currentRawBrightness / maxBrightness) * 100);
}
}
}
@@ -184,86 +122,28 @@ Singleton {
}
Process {
id: ddcProc
command: ["ddcutil", "detect", "--brief"]
id: ddcBrightnessInitProcess
command: ["ddcutil", "getvcp", "10", "--brief"]
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 {
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);
}
const parts = text.trim().split(" ");
if (parts.length >= 5) {
const current = parseInt(parts[3]) || 75;
const max = parseInt(parts[4]) || 100;
brightnessLevel = Math.round((current / max) * 100);
}
}
}
onExited: function(exitCode) {
if (exitCode !== 0) {
monitor.brightness = 0.75;
root.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()]);
onExited: function(exitCode) {
if (exitCode !== 0) {
console.warn("Failed to get DDC brightness, exit code:", exitCode);
brightnessLevel = 75;
}
}
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) {
if (!root.khalAvailable) {
console.warn("CalendarService: khal not available, skipping event loading");
return ;
}
if (eventsProcess.running) {
console.log("CalendarService: Event loading already in progress, skipping...");
return ;
}
// Store last requested date range for refresh timer
@@ -71,24 +69,9 @@ Singleton {
// Initialize on component completion
Component.onCompleted: {
console.log("CalendarService: Component completed, initializing...");
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 {
id: khalCheckProcess
@@ -97,17 +80,10 @@ Singleton {
running: false
onExited: (exitCode) => {
root.khalAvailable = (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
if (exitCode === 0) {
loadCurrentMonth();
}
}
onStarted: {
console.log("CalendarService: Checking khal configuration...");
}
}
// Process for loading events
@@ -123,7 +99,6 @@ Singleton {
root.isLoading = false;
if (exitCode !== 0) {
root.lastError = "Failed to load events (exit code: " + exitCode + ")";
console.warn("CalendarService:", root.lastError);
return ;
}
try {
@@ -249,19 +224,14 @@ Singleton {
}
root.eventsByDate = newEventsByDate;
root.lastError = "";
console.log("CalendarService: Loaded events for", Object.keys(newEventsByDate).length, "days");
} catch (error) {
root.lastError = "Failed to parse events JSON: " + error.toString();
console.error("CalendarService:", root.lastError);
root.eventsByDate = {
};
}
// Reset for next run
eventsProcess.rawOutput = "";
}
onStarted: {
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"), "to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"));
}
stdout: SplitParser {
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) {
root.changingPreference = false
root.targetPreference = ""
preferenceTimeoutTimer.stop()
console.log("Network preference change completed successfully")
}
}
@@ -379,26 +378,8 @@ Singleton {
}
function delayedRefreshNetworkStatus() {
console.log("Delayed network status refresh...")
refreshTimer.start()
}
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 = ""
}
console.log("Refreshing network status immediately...")
refreshNetworkStatus()
}
function setNetworkPreference(preference) {
@@ -406,7 +387,6 @@ Singleton {
root.userPreference = preference
root.changingPreference = true
root.targetPreference = preference
preferenceTimeoutTimer.start()
Prefs.setNetworkPreference(preference)
if (preference === "wifi") {

View File

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

View File

@@ -333,31 +333,6 @@ Singleton {
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 {
id: systemInfoProcess

View File

@@ -24,7 +24,6 @@ Singleton {
property string kernelVersion: ""
property string distribution: ""
property string hostname: ""
property string uptime: ""
property string scheduler: ""
property string architecture: ""
property string loadAverage: ""
@@ -61,7 +60,6 @@ Singleton {
kernelInfoProcess.running = true;
distributionProcess.running = true;
hostnameProcess.running = true;
uptimeProcess.running = true;
schedulerProcess.running = true;
architectureProcess.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 {
id: schedulerProcess
@@ -546,55 +524,27 @@ Singleton {
}
Timer {
id: cpuTimer
id: basicStatsTimer
interval: root.cpuUpdateInterval
interval: 5000
running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView) {
cpuUsageProcess.running = true;
cpuFrequencyProcess.running = true;
}
cpuUsageProcess.running = true;
cpuFrequencyProcess.running = true;
memoryUsageProcess.running = true;
}
}
Timer {
id: memoryTimer
id: detailedStatsTimer
interval: root.memoryUpdateInterval
running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView)
memoryUsageProcess.running = true;
}
}
Timer {
id: temperatureTimer
interval: root.temperatureUpdateInterval
interval: 15000
running: root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForDetailedView)
temperatureProcess.running = true;
}
}
Timer {
id: systemInfoTimer
interval: root.systemInfoUpdateInterval
running: root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForDetailedView)
updateSystemInfo();
temperatureProcess.running = true;
updateSystemInfo();
}
}

View File

@@ -44,7 +44,7 @@ Singleton {
currentLevel = levelInfo;
toastTimer.stop();
if (toastQueue.length > 0)
queueTimer.start();
processQueue();
}
@@ -56,7 +56,7 @@ Singleton {
currentMessage = toast.message;
currentLevel = toast.level;
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();
}
@@ -73,13 +73,5 @@ Singleton {
onTriggered: hideToast()
}
Timer {
id: queueTimer
interval: 500
running: false
repeat: false
onTriggered: processQueue()
}
}

View File

@@ -36,17 +36,6 @@ Singleton {
Component.onCompleted: {
getUserInfo();
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
@@ -96,7 +85,6 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
root.uptime = text.trim() || "Unknown";
console.log("UserInfoService: Uptime updated -", root.uptime);
}
}

View File

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

View File

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

View File

@@ -21,14 +21,12 @@ PanelWindow {
function setProfile(profile) {
if (typeof PowerProfiles === "undefined") {
errorToast.show();
ToastService.showError("power-profiles-daemon not available");
return ;
}
PowerProfiles.profile = profile;
if (PowerProfiles.profile !== profile)
errorToast.show();
else
console.log("Set power profile to: " + PowerProfile.toString(profile));
ToastService.showError("Failed to set power profile");
}
visible: batteryPopupVisible
@@ -367,7 +365,7 @@ PanelWindow {
spacing: Theme.spacingM
Text {
text: Theme.getPowerProfileIcon(PowerProfile.toString(modelData))
text: Theme.getPowerProfileIcon(modelData)
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
@@ -379,14 +377,14 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter
Text {
text: Theme.getPowerProfileLabel(PowerProfile.toString(modelData))
text: Theme.getPowerProfileLabel(modelData)
font.pixelSize: Theme.fontSizeMedium
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
font.weight: batteryControlPopup.isActiveProfile(modelData) ? Font.Medium : Font.Normal
}
Text {
text: Theme.getPowerProfileDescription(PowerProfile.toString(modelData))
text: Theme.getPowerProfileDescription(modelData)
font.pixelSize: Theme.fontSizeSmall
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();
}
Component.onCompleted: {
console.log("CalendarWidget: Component completed, CalendarService available:", !!CalendarService);
if (CalendarService)
console.log("CalendarWidget: khal available:", CalendarService.khalAvailable);
loadEventsForMonth();
}

View File

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

View File

@@ -23,9 +23,9 @@ Rectangle {
onActivePlayerChanged: {
if (!activePlayer)
clearCacheTimer.restart();
updateTimer.start();
else
clearCacheTimer.stop();
updateTimer.stop();
}
width: parent.width
height: parent.height
@@ -36,30 +36,28 @@ Rectangle {
layer.enabled: true
Timer {
id: clearCacheTimer
id: updateTimer
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: {
if (!activePlayer) {
// Clear cache when no player
lastValidTitle = "";
lastValidArtist = "";
lastValidAlbum = "";
lastValidArtUrl = "";
}
}
}
// 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)
stop(); // Stop after clearing cache
} else if (activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) {
// Update position when playing
currentPosition = activePlayer.position;
}
}
}

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
@@ -23,6 +24,10 @@ PanelWindow {
if (!visible && BluetoothService.adapter && BluetoothService.adapter.discovering) {
BluetoothService.adapter.discovering = false;
}
// Refresh uptime when opened
if (visible && UserInfoService) {
UserInfoService.getUptime();
}
}
implicitWidth: 600
implicitHeight: 500
@@ -120,7 +125,7 @@ PanelWindow {
}
]
Column {
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
@@ -698,7 +703,7 @@ PanelWindow {
// Tab content area
Rectangle {
width: parent.width
height: root.powerOptionsExpanded ? 240 : 300
Layout.fillHeight: true
radius: Theme.cornerRadius
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
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 {
width: parent.width
@@ -36,8 +46,23 @@ ScrollView {
rightIcon: "brightness_high"
enabled: BrightnessService.brightnessAvailable
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
z: 10
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
visible: networkTab.networkStatus !== "ethernet"
visible: NetworkService.networkStatus !== "ethernet" && NetworkService.wifiAvailable && NetworkService.wifiEnabled
Row {
anchors.centerIn: parent
@@ -330,197 +330,215 @@ Item {
id: wifiContent
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)
Rectangle {
width: parent.width
height: 80
height: 60
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
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
Row {
// WiFi icon
Text {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
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: 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
text: {
if (!NetworkService.wifiEnabled) {
return "WiFi is off";
} else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
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 {
spacing: 4
Text {
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
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: NetworkService.networkStatus === "wifi" ? (WifiService.currentWifiSSID || "Connected") : "Not Connected"
font.pixelSize: Theme.fontSizeLarge
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
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..." : "Prefer over Ethernet"
font.pixelSize: Theme.fontSizeSmall
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
anchors.verticalCenter: parent.verticalCenter
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
Rectangle {
width: 150
height: 30
color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surface
border.color: Theme.primary
border.width: 1
radius: 6
anchors.verticalCenter: parent.verticalCenter
opacity: networkTab.changingNetworkPreference ? 0.6 : 1
visible: networkTab.networkStatus !== "wifi"
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 {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
propagateComposedEvents: false
enabled: !networkTab.changingNetworkPreference
onClicked: {
console.log("Force WiFi preference clicked");
if (NetworkService.networkStatus !== "wifi")
NetworkService.setNetworkPreference("wifi");
else
NetworkService.setNetworkPreference("auto");
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
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
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
@@ -532,7 +550,7 @@ Item {
// Available WiFi Networks
Column {
width: parent.width
spacing: Theme.spacingM
spacing: Theme.spacingS
visible: NetworkService.wifiEnabled
Row {
@@ -607,7 +625,7 @@ Item {
// Connection status indicator
Rectangle {
width: parent.width
height: 40
height: 32
radius: Theme.cornerRadius
color: {
if (WifiService.connectionStatus === "connecting")
@@ -733,7 +751,7 @@ Item {
Rectangle {
width: parent.width
height: 50
height: 42
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"
border.color: modelData.connected ? Theme.primary : "transparent"
@@ -741,7 +759,7 @@ Item {
Item {
anchors.fill: parent
anchors.margins: Theme.spacingM
anchors.margins: Theme.spacingS
// Signal strength icon
Text {
@@ -758,9 +776,9 @@ Item {
// Network info
Column {
anchors.left: signalIcon.right
anchors.leftMargin: Theme.spacingM
anchors.leftMargin: Theme.spacingS
anchors.right: rightIcons.left
anchors.rightMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
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)
height: 80
property bool isDragging: false
Column {
anchors.fill: parent
@@ -139,7 +141,8 @@ Item {
preventStealing: true
onPressed: (mouse) => {
if (slider.enabled) {
isDragging = true;
slider.isDragging = true;
sliderMouseArea.isDragging = true;
let ratio = Math.max(0, Math.min(1, mouse.x / width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
slider.value = newValue;
@@ -148,12 +151,13 @@ Item {
}
onReleased: {
if (slider.enabled) {
isDragging = false;
slider.isDragging = false;
sliderMouseArea.isDragging = false;
slider.sliderDragFinished(slider.value);
}
}
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 newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
slider.value = newValue;
@@ -161,7 +165,7 @@ Item {
}
}
onClicked: (mouse) => {
if (slider.enabled) {
if (slider.enabled && !slider.isDragging) {
let ratio = Math.max(0, Math.min(1, mouse.x / width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
slider.value = newValue;
@@ -175,11 +179,11 @@ Item {
id: sliderGlobalMouseArea
anchors.fill: sliderContainer
enabled: sliderMouseArea.isDragging
enabled: slider.isDragging
visible: false
preventStealing: true
onPositionChanged: (mouse) => {
if (sliderMouseArea.isDragging && slider.enabled) {
if (slider.isDragging && slider.enabled) {
let globalPos = mapToItem(sliderTrack, mouse.x, mouse.y);
let ratio = Math.max(0, Math.min(1, globalPos.x / sliderTrack.width));
let newValue = Math.round(slider.minimum + ratio * (slider.maximum - slider.minimum));
@@ -188,7 +192,8 @@ Item {
}
}
onReleased: {
if (sliderMouseArea.isDragging && slider.enabled) {
if (slider.isDragging && slider.enabled) {
slider.isDragging = false;
sliderMouseArea.isDragging = false;
slider.sliderDragFinished(slider.value);
}

View File

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

View File

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

View File

@@ -5,13 +5,17 @@ import qs.Services
Rectangle {
id: root
signal clicked()
property bool isActive: false
readonly property bool nerdFontAvailable: Qt.fontFamilies()
.indexOf("Symbols Nerd Font") !== -1
width: 40
height: 30
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 {
anchors.centerIn: parent
@@ -28,9 +32,7 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
LauncherService.toggleAppLauncher();
}
onClicked: root.clicked()
}
Behavior on color {

View File

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

View File

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

View File

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