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

meta cleanup and refactors

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

3
.gitignore vendored
View File

@@ -54,3 +54,6 @@ compile_commands.json
*_qmlcache.qrc
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 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
property list<DesktopEntry> applications: Array.from(DesktopEntries.applications.values)
.filter(app => !app.noDisplay)
.sort((a, b) => a.name.localeCompare(b.name))
property var applicationsByName: {
var byName = {}
var byExec = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
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 cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
if (cleanExec) {
byExec[cleanExec] = app
}
}
return byExec
}
applicationsByName = byName
applicationsByExec = byExec
preppedApps = applications.map(app => ({
property var 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)
})
}
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,87 +8,31 @@ 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();
brightnessLevel = Math.max(1, Math.min(100, percentage));
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(): 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 increaseBrightness() {
setBrightness(brightnessLevel + 10);
}
function decreaseBrightness(): void {
if (root.laptopBacklightAvailable) {
setBrightness(Math.max(1, root.laptopBrightnessLevel - 10));
} else {
const focusedMonitor = monitors.find(function(m) { return m.modelData === Quickshell.screens[0]; });
if (focusedMonitor)
focusedMonitor.setBrightness(focusedMonitor.brightness - 0.1);
}
}
onMonitorsChanged: {
ddcMonitors = [];
if (root.brightnessAvailable) {
ddcProc.running = true;
}
// Update brightness level from laptop or first monitor
if (root.laptopBacklightAvailable) {
// Laptop brightness is already set by laptopBrightnessInitProcess
} else if (monitors.length > 0) {
root.brightnessLevel = Math.round(monitors[0].brightness * 100);
}
function decreaseBrightness() {
setBrightness(brightnessLevel - 10);
}
Component.onCompleted: {
@@ -96,20 +40,23 @@ Singleton {
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,37 +122,10 @@ 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()) {
@@ -222,48 +133,17 @@ Singleton {
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);
brightnessLevel = Math.round((current / max) * 100);
}
}
}
}
onExited: function(exitCode) {
if (exitCode !== 0) {
monitor.brightness = 0.75;
root.brightnessLevel = 75;
console.warn("Failed to get DDC brightness, exit code:", exitCode);
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) {
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,34 +378,15 @@ Singleton {
}
function delayedRefreshNetworkStatus() {
console.log("Delayed network status refresh...")
refreshTimer.start()
}
Timer {
id: refreshTimer
interval: 1000
onTriggered: {
console.log("Refreshing network status immediately...")
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) {
console.log("Setting network preference to:", preference)
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;
}
}
}
Timer {
id: memoryTimer
interval: root.memoryUpdateInterval
running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView)
memoryUsageProcess.running = true;
}
}
Timer {
id: temperatureTimer
id: detailedStatsTimer
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();
}
}

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();
}
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");
}
}
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

@@ -12,6 +12,16 @@ ScrollView {
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
spacing: Theme.spacingL
@@ -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,61 +330,115 @@ Item {
id: wifiContent
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
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.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
rotation: NetworkService.wifiToggling ? wifiToggleIcon.rotation : 0
spacing: 4
RotationAnimation {
target: wifiToggleIcon
property: "rotation"
running: NetworkService.wifiToggling
from: 0
to: 360
duration: 1000
loops: Animation.Infinite
Text {
text: {
if (!NetworkService.wifiEnabled) {
return "WiFi is off";
} else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) {
return WifiService.currentWifiSSID || "Connected";
} else {
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: 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
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 {
@@ -395,6 +449,15 @@ Item {
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
}
}
@@ -403,52 +466,7 @@ Item {
duration: Theme.shortDuration
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
@@ -459,9 +477,11 @@ Item {
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: networkTab.networkStatus !== "wifi"
visible: NetworkService.networkStatus !== "wifi" && NetworkService.ethernetConnected && NetworkService.wifiEnabled
Row {
anchors.centerIn: parent
@@ -491,7 +511,7 @@ Item {
}
Text {
text: NetworkService.changingNetworkPreference ? "Switching..." : (NetworkService.networkStatus === "wifi" ? "" : "Prefer over Ethernet")
text: NetworkService.changingNetworkPreference ? "Switching..." : "Prefer over Ethernet"
font.pixelSize: Theme.fontSizeSmall
color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary
anchors.verticalCenter: parent.verticalCenter
@@ -527,12 +547,10 @@ 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

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