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

qmlformat-all script

This commit is contained in:
bbedward
2025-07-17 18:15:35 -04:00
parent 26ad3810b4
commit 70b9a3cff4
14 changed files with 2163 additions and 2037 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -7,61 +7,56 @@ import Quickshell.Services.UPower
Singleton {
id: root
readonly property UPowerDevice device: UPower.displayDevice
readonly property bool batteryAvailable: device && device.ready && device.isLaptopBattery
readonly property int batteryLevel: batteryAvailable ? device.percentage * 100.0 : 0
readonly property int batteryLevel: batteryAvailable ? device.percentage * 100 : 0
readonly property bool isCharging: batteryAvailable && device.state === UPowerDeviceState.Charging
readonly property bool isLowBattery: batteryAvailable && batteryLevel <= 20
readonly property string batteryHealth: batteryAvailable && device.healthSupported ? Math.round(device.healthPercentage * 100.0) + "%" : "N/A"
readonly property string batteryHealth: batteryAvailable && device.healthSupported ? Math.round(device.healthPercentage * 100) + "%" : "N/A"
readonly property real batteryCapacity: batteryAvailable && device.energyCapacity > 0 ? device.energyCapacity : 0
readonly property string batteryStatus: {
if (!batteryAvailable) return "No Battery"
return UPowerDeviceState.toString(device.state)
if (!batteryAvailable)
return "No Battery";
return UPowerDeviceState.toString(device.state);
}
readonly property int timeRemaining: {
if (!batteryAvailable) return 0
return isCharging ? (device.timeToFull || 0) : (device.timeToEmpty || 0)
if (!batteryAvailable)
return 0;
return isCharging ? (device.timeToFull || 0) : (device.timeToEmpty || 0);
}
readonly property bool suggestPowerSaver: batteryAvailable && isLowBattery && UPower.onBattery && (typeof PowerProfiles !== "undefined" && PowerProfiles.profile !== PowerProfile.PowerSaver)
readonly property var bluetoothDevices: {
var btDevices = []
var btDevices = [];
for (var i = 0; i < UPower.devices.count; i++) {
var dev = UPower.devices.get(i)
if (dev && dev.ready && (dev.type === UPowerDeviceType.BluetoothGeneric ||
dev.type === UPowerDeviceType.Headphones ||
dev.type === UPowerDeviceType.Headset ||
dev.type === UPowerDeviceType.Keyboard ||
dev.type === UPowerDeviceType.Mouse ||
dev.type === UPowerDeviceType.Speakers)) {
var dev = UPower.devices.get(i);
if (dev && dev.ready && (dev.type === UPowerDeviceType.BluetoothGeneric || dev.type === UPowerDeviceType.Headphones || dev.type === UPowerDeviceType.Headset || dev.type === UPowerDeviceType.Keyboard || dev.type === UPowerDeviceType.Mouse || dev.type === UPowerDeviceType.Speakers))
btDevices.push({
name: dev.model || UPowerDeviceType.toString(dev.type),
percentage: Math.round(dev.percentage),
type: dev.type
})
}
"name": dev.model || UPowerDeviceType.toString(dev.type),
"percentage": Math.round(dev.percentage),
"type": dev.type
});
}
return btDevices
return btDevices;
}
function formatTimeRemaining() {
if (!batteryAvailable || timeRemaining <= 0) return "Unknown"
const hours = Math.floor(timeRemaining / 3600)
const minutes = Math.floor((timeRemaining % 3600) / 60)
if (hours > 0) {
return hours + "h " + minutes + "m"
} else {
return minutes + "m"
}
if (!batteryAvailable || timeRemaining <= 0)
return "Unknown";
const hours = Math.floor(timeRemaining / 3600);
const minutes = Math.floor((timeRemaining % 3600) / 60);
if (hours > 0)
return hours + "h " + minutes + "m";
else
return minutes + "m";
}
function getBluetoothDevices() {
return bluetoothDevices
return bluetoothDevices;
}
}
}

View File

@@ -7,162 +7,252 @@ import Quickshell.Bluetooth
Singleton {
id: root
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
readonly property bool available: adapter !== null
readonly property bool enabled: adapter?.enabled ?? false
readonly property bool discovering: adapter?.discovering ?? false
readonly property bool enabled: (adapter && adapter.enabled) ?? false
readonly property bool discovering: (adapter && adapter.discovering) ?? false
readonly property var devices: adapter ? adapter.devices : null
readonly property var pairedDevices: {
if (!adapter || !adapter.devices)
return [];
return adapter.devices.values.filter((dev) => {
return dev && dev.paired && isValidDevice(dev);
});
}
readonly property var availableDevices: {
if (!adapter || !adapter.discovering || !Bluetooth.devices)
return [];
var filtered = Bluetooth.devices.values.filter((dev) => {
return dev && !dev.paired && !dev.pairing && !dev.blocked && isValidDevice(dev) && (dev.rssi === undefined || dev.rssi !== 0);
});
return sortByRssi(filtered);
}
readonly property var allDevicesWithBattery: {
if (!adapter || !adapter.devices)
return [];
return adapter.devices.values.filter((dev) => {
return dev && dev.batteryAvailable && dev.battery > 0;
});
}
function sortByRssi(devices) {
return devices.sort((a, b) => {
var aRssi = (a.rssi !== undefined && a.rssi !== 0) ? a.rssi : -100
var bRssi = (b.rssi !== undefined && b.rssi !== 0) ? b.rssi : -100
return bRssi - aRssi
})
var aRssi = (a.rssi !== undefined && a.rssi !== 0) ? a.rssi : -100;
var bRssi = (b.rssi !== undefined && b.rssi !== 0) ? b.rssi : -100;
return bRssi - aRssi;
});
}
readonly property var pairedDevices: {
if (!adapter || !adapter.devices) return []
return adapter.devices.values.filter(dev => dev && dev.paired && isValidDevice(dev))
}
readonly property var availableDevices: {
if (!adapter || !adapter.discovering || !Bluetooth.devices) return []
var filtered = Bluetooth.devices.values
.filter(dev => dev && !dev.paired && !dev.pairing && !dev.blocked && isValidDevice(dev) && (dev.rssi === undefined || dev.rssi !== 0))
return sortByRssi(filtered)
}
readonly property var allDevicesWithBattery: {
if (!adapter || !adapter.devices) return []
return adapter.devices.values.filter(dev => dev && dev.batteryAvailable && dev.battery > 0)
}
function isValidDevice(device) {
if (!device) return false
var displayName = device.name || device.deviceName
if (!displayName || displayName.length < 2) return false
if (displayName.startsWith('/org/bluez') || displayName.includes('hci0')) return false
return displayName.length >= 3
if (!device)
return false;
var displayName = device.name || device.deviceName;
if (!displayName || displayName.length < 2)
return false;
if (displayName.startsWith('/org/bluez') || displayName.includes('hci0'))
return false;
return displayName.length >= 3;
}
function getDeviceIcon(device) {
if (!device) return "bluetooth"
var name = (device.name || device.deviceName || "").toLowerCase()
var icon = (device.icon || "").toLowerCase()
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") ||
name.includes("airpod") || name.includes("headset") || name.includes("arctis")) return "headset"
if (icon.includes("mouse") || name.includes("mouse")) return "mouse"
if (icon.includes("keyboard") || name.includes("keyboard")) return "keyboard"
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") ||
name.includes("android") || name.includes("samsung")) return "smartphone"
if (icon.includes("watch") || name.includes("watch")) return "watch"
if (icon.includes("speaker") || name.includes("speaker")) return "speaker"
if (icon.includes("display") || name.includes("tv")) return "tv"
return "bluetooth"
if (!device)
return "bluetooth";
var name = (device.name || device.deviceName || "").toLowerCase();
var icon = (device.icon || "").toLowerCase();
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") || name.includes("airpod") || name.includes("headset") || name.includes("arctis"))
return "headset";
if (icon.includes("mouse") || name.includes("mouse"))
return "mouse";
if (icon.includes("keyboard") || name.includes("keyboard"))
return "keyboard";
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung"))
return "smartphone";
if (icon.includes("watch") || name.includes("watch"))
return "watch";
if (icon.includes("speaker") || name.includes("speaker"))
return "speaker";
if (icon.includes("display") || name.includes("tv"))
return "tv";
return "bluetooth";
}
function getDeviceType(device) {
if (!device) return "bluetooth"
var name = (device.name || device.deviceName || "").toLowerCase()
var icon = (device.icon || "").toLowerCase()
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") ||
name.includes("airpod") || name.includes("headset") || name.includes("arctis")) return "headset"
if (icon.includes("mouse") || name.includes("mouse")) return "mouse"
if (icon.includes("keyboard") || name.includes("keyboard")) return "keyboard"
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") ||
name.includes("android") || name.includes("samsung")) return "phone"
if (icon.includes("watch") || name.includes("watch")) return "watch"
if (icon.includes("speaker") || name.includes("speaker")) return "speaker"
if (icon.includes("display") || name.includes("tv")) return "tv"
return "bluetooth"
if (!device)
return "bluetooth";
var name = (device.name || device.deviceName || "").toLowerCase();
var icon = (device.icon || "").toLowerCase();
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") || name.includes("airpod") || name.includes("headset") || name.includes("arctis"))
return "headset";
if (icon.includes("mouse") || name.includes("mouse"))
return "mouse";
if (icon.includes("keyboard") || name.includes("keyboard"))
return "keyboard";
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung"))
return "phone";
if (icon.includes("watch") || name.includes("watch"))
return "watch";
if (icon.includes("speaker") || name.includes("speaker"))
return "speaker";
if (icon.includes("display") || name.includes("tv"))
return "tv";
return "bluetooth";
}
function canPair(device) {
if (!device) return false
return !device.paired && !device.pairing && !device.blocked
if (!device)
return false;
return !device.paired && !device.pairing && !device.blocked;
}
function debugDevice(device) {
console.log("Device:", device.name, "paired:", device.paired, "connected:", device.connected, "rssi:", device.rssi)
console.log("Device:", device.name, "paired:", device.paired, "connected:", device.connected, "rssi:", device.rssi);
}
function getPairingStatus(device) {
if (!device) return "unknown"
if (device.pairing) return "pairing"
if (device.paired) return "paired"
if (device.blocked) return "blocked"
return "available"
if (!device)
return "unknown";
if (device.pairing)
return "pairing";
if (device.paired)
return "paired";
if (device.blocked)
return "blocked";
return "available";
}
function getSignalStrength(device) {
if (!device || device.rssi === undefined || device.rssi === 0) return "Unknown"
var rssi = device.rssi
if (rssi >= -50) return "Excellent"
if (rssi >= -60) return "Good"
if (rssi >= -70) return "Fair"
if (rssi >= -80) return "Poor"
return "Very Poor"
if (!device || device.rssi === undefined || device.rssi === 0)
return "Unknown";
var rssi = device.rssi;
if (rssi >= -50)
return "Excellent";
if (rssi >= -60)
return "Good";
if (rssi >= -70)
return "Fair";
if (rssi >= -80)
return "Poor";
return "Very Poor";
}
function getSignalIcon(device) {
if (!device || device.rssi === undefined || device.rssi === 0) return "signal_cellular_null"
var rssi = device.rssi
if (rssi >= -50) return "signal_cellular_4_bar"
if (rssi >= -60) return "signal_cellular_3_bar"
if (rssi >= -70) return "signal_cellular_2_bar"
if (rssi >= -80) return "signal_cellular_1_bar"
return "signal_cellular_0_bar"
if (!device || device.rssi === undefined || device.rssi === 0)
return "signal_cellular_null";
var rssi = device.rssi;
if (rssi >= -50)
return "signal_cellular_4_bar";
if (rssi >= -60)
return "signal_cellular_3_bar";
if (rssi >= -70)
return "signal_cellular_2_bar";
if (rssi >= -80)
return "signal_cellular_1_bar";
return "signal_cellular_0_bar";
}
function toggleAdapter() {
if (adapter) adapter.enabled = !adapter.enabled
if (adapter)
adapter.enabled = !adapter.enabled;
}
function startScan() {
if (adapter) adapter.discovering = true
if (adapter)
adapter.discovering = true;
}
function stopScan() {
if (adapter) adapter.discovering = false
if (adapter)
adapter.discovering = false;
}
function connect(address) {
var device = _findDevice(address)
if (device) device.connect()
var device = _findDevice(address);
if (device)
device.connect();
}
function disconnect(address) {
var device = _findDevice(address)
if (device) device.disconnect()
var device = _findDevice(address);
if (device)
device.disconnect();
}
function pair(address) {
var device = _findDevice(address)
if (device && canPair(device)) device.pair()
var device = _findDevice(address);
if (device && canPair(device))
device.pair();
}
function forget(address) {
var device = _findDevice(address)
if (device) device.forget()
var device = _findDevice(address);
if (device)
device.forget();
}
function toggle(address) {
var device = _findDevice(address)
var device = _findDevice(address);
if (device) {
if (device.connected) device.disconnect()
else device.connect()
if (device.connected)
device.disconnect();
else
device.connect();
}
}
function _findDevice(address) {
if (!adapter) return null
return adapter.devices.values.find(d => d && d.address === address) ||
(Bluetooth.devices ? Bluetooth.devices.values.find(d => d && d.address === address) : null)
if (!adapter)
return null;
return adapter.devices.values.find((d) => {
return d && d.address === address;
}) || (Bluetooth.devices ? Bluetooth.devices.values.find((d) => {
return d && d.address === address;
}) : null);
}
}
}

View File

@@ -7,303 +7,269 @@ import Quickshell.Io
Singleton {
id: root
property bool khalAvailable: false
property var eventsByDate: ({})
property var eventsByDate: ({
})
property bool isLoading: false
property string lastError: ""
property date lastStartDate
property date lastEndDate
function checkKhalAvailability() {
if (!khalCheckProcess.running)
khalCheckProcess.running = true;
}
function loadCurrentMonth() {
if (!root.khalAvailable)
return ;
let today = new Date();
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
// Add padding
let startDate = new Date(firstDay);
startDate.setDate(startDate.getDate() - firstDay.getDay() - 7);
let endDate = new Date(lastDay);
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7);
loadEvents(startDate, endDate);
}
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
root.lastStartDate = startDate;
root.lastEndDate = endDate;
root.isLoading = true;
// Format dates for khal (MM/dd/yyyy based on printformats)
let startDateStr = Qt.formatDate(startDate, "MM/dd/yyyy");
let endDateStr = Qt.formatDate(endDate, "MM/dd/yyyy");
eventsProcess.requestStartDate = startDate;
eventsProcess.requestEndDate = endDate;
eventsProcess.command = ["khal", "list", "--json", "title", "--json", "description", "--json", "start-date", "--json", "start-time", "--json", "end-date", "--json", "end-time", "--json", "all-day", "--json", "location", "--json", "url", startDateStr, endDateStr];
eventsProcess.running = true;
}
function getEventsForDate(date) {
let dateKey = Qt.formatDate(date, "yyyy-MM-dd");
return root.eventsByDate[dateKey] || [];
}
function hasEventsForDate(date) {
let events = getEventsForDate(date);
return events.length > 0;
}
// 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)
}
if (lastStartDate && lastEndDate)
loadEvents(lastStartDate, lastEndDate);
}
}
property date lastStartDate
property date lastEndDate
// Process for checking khal configuration
Process {
id: khalCheckProcess
command: ["khal", "list", "today"]
running: false
onExited: (exitCode) => {
root.khalAvailable = (exitCode === 0)
root.khalAvailable = (exitCode === 0);
if (exitCode !== 0) {
console.warn("CalendarService: khal not configured (exit code:", exitCode, ")")
console.warn("CalendarService: khal not configured (exit code:", exitCode, ")");
} else {
console.log("CalendarService: khal configured and available")
console.log("CalendarService: khal configured and available");
// Load current month events when khal becomes available
loadCurrentMonth()
loadCurrentMonth();
}
}
onStarted: {
console.log("CalendarService: Checking khal configuration...")
console.log("CalendarService: Checking khal configuration...");
}
}
// Process for loading events
Process {
id: eventsProcess
running: false
property date requestStartDate
property date requestEndDate
property string rawOutput: ""
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
eventsProcess.rawOutput += data + "\n"
}
}
running: false
onExited: (exitCode) => {
root.isLoading = false
root.isLoading = false;
if (exitCode !== 0) {
root.lastError = "Failed to load events (exit code: " + exitCode + ")"
console.warn("CalendarService:", root.lastError)
return
root.lastError = "Failed to load events (exit code: " + exitCode + ")";
console.warn("CalendarService:", root.lastError);
return ;
}
try {
let newEventsByDate = {}
let lines = eventsProcess.rawOutput.split('\n')
let newEventsByDate = {
};
let lines = eventsProcess.rawOutput.split('\n');
for (let line of lines) {
line = line.trim()
if (!line || line === "[]") continue
line = line.trim();
if (!line || line === "[]")
continue;
// Parse JSON line
let dayEvents = JSON.parse(line)
let dayEvents = JSON.parse(line);
// Process each event in this day's array
for (let event of dayEvents) {
if (!event.title) continue
if (!event.title)
continue;
// Parse start and end dates
let startDate, endDate
let startDate, endDate;
if (event['start-date']) {
let startParts = event['start-date'].split('/')
startDate = new Date(parseInt(startParts[2]), parseInt(startParts[0]) - 1, parseInt(startParts[1]))
let startParts = event['start-date'].split('/');
startDate = new Date(parseInt(startParts[2]), parseInt(startParts[0]) - 1, parseInt(startParts[1]));
} else {
startDate = new Date()
startDate = new Date();
}
if (event['end-date']) {
let endParts = event['end-date'].split('/')
endDate = new Date(parseInt(endParts[2]), parseInt(endParts[0]) - 1, parseInt(endParts[1]))
let endParts = event['end-date'].split('/');
endDate = new Date(parseInt(endParts[2]), parseInt(endParts[0]) - 1, parseInt(endParts[1]));
} else {
endDate = new Date(startDate)
endDate = new Date(startDate);
}
// Create start/end times
let startTime = new Date(startDate)
let endTime = new Date(endDate)
let startTime = new Date(startDate);
let endTime = new Date(endDate);
if (event['start-time'] && event['all-day'] !== "True") {
// Parse time if available and not all-day
let timeStr = event['start-time']
let timeStr = event['start-time'];
if (timeStr) {
let timeParts = timeStr.match(/(\d+):(\d+)/)
let timeParts = timeStr.match(/(\d+):(\d+)/);
if (timeParts) {
startTime.setHours(parseInt(timeParts[1]), parseInt(timeParts[2]))
startTime.setHours(parseInt(timeParts[1]), parseInt(timeParts[2]));
if (event['end-time']) {
let endTimeParts = event['end-time'].match(/(\d+):(\d+)/)
if (endTimeParts) {
endTime.setHours(parseInt(endTimeParts[1]), parseInt(endTimeParts[2]))
}
let endTimeParts = event['end-time'].match(/(\d+):(\d+)/);
if (endTimeParts)
endTime.setHours(parseInt(endTimeParts[1]), parseInt(endTimeParts[2]));
} else {
// Default to 1 hour duration on same day
endTime = new Date(startTime)
endTime.setHours(startTime.getHours() + 1)
endTime = new Date(startTime);
endTime.setHours(startTime.getHours() + 1);
}
}
}
}
// Create unique ID for this event (to track multi-day events)
let eventId = event.title + "_" + event['start-date'] + "_" + (event['start-time'] || 'allday')
let eventId = event.title + "_" + event['start-date'] + "_" + (event['start-time'] || 'allday');
// Create event object template
let eventTemplate = {
id: eventId,
title: event.title || "Untitled Event",
start: startTime,
end: endTime,
location: event.location || "",
description: event.description || "",
url: event.url || "",
calendar: "",
color: "",
allDay: event['all-day'] === "True",
isMultiDay: startDate.toDateString() !== endDate.toDateString()
}
"id": eventId,
"title": event.title || "Untitled Event",
"start": startTime,
"end": endTime,
"location": event.location || "",
"description": event.description || "",
"url": event.url || "",
"calendar": "",
"color": "",
"allDay": event['all-day'] === "True",
"isMultiDay": startDate.toDateString() !== endDate.toDateString()
};
// Add event to each day it spans
let currentDate = new Date(startDate)
let currentDate = new Date(startDate);
while (currentDate <= endDate) {
let dateKey = Qt.formatDate(currentDate, "yyyy-MM-dd")
if (!newEventsByDate[dateKey]) {
newEventsByDate[dateKey] = []
}
let dateKey = Qt.formatDate(currentDate, "yyyy-MM-dd");
if (!newEventsByDate[dateKey])
newEventsByDate[dateKey] = [];
// Check if this exact event is already added to this date (prevent duplicates)
let existingEvent = newEventsByDate[dateKey].find(e => e.id === eventId)
let existingEvent = newEventsByDate[dateKey].find((e) => {
return e.id === eventId;
});
if (existingEvent) {
// Move to next day without adding duplicate
currentDate.setDate(currentDate.getDate() + 1)
continue
currentDate.setDate(currentDate.getDate() + 1);
continue;
}
// Create a copy of the event for this date
let dayEvent = Object.assign({}, eventTemplate)
let dayEvent = Object.assign({
}, eventTemplate);
// For multi-day events, adjust the display time for this specific day
if (currentDate.getTime() === startDate.getTime()) {
// First day - use original start time
dayEvent.start = new Date(startTime)
dayEvent.start = new Date(startTime);
} else {
// Subsequent days - start at beginning of day for all-day events
dayEvent.start = new Date(currentDate)
if (!dayEvent.allDay) {
dayEvent.start.setHours(0, 0, 0, 0)
}
dayEvent.start = new Date(currentDate);
if (!dayEvent.allDay)
dayEvent.start.setHours(0, 0, 0, 0);
}
if (currentDate.getTime() === endDate.getTime()) {
// Last day - use original end time
dayEvent.end = new Date(endTime)
dayEvent.end = new Date(endTime);
} else {
// Earlier days - end at end of day for all-day events
dayEvent.end = new Date(currentDate)
if (!dayEvent.allDay) {
dayEvent.end.setHours(23, 59, 59, 999)
}
dayEvent.end = new Date(currentDate);
if (!dayEvent.allDay)
dayEvent.end.setHours(23, 59, 59, 999);
}
newEventsByDate[dateKey].push(dayEvent)
newEventsByDate[dateKey].push(dayEvent);
// Move to next day
currentDate.setDate(currentDate.getDate() + 1)
currentDate.setDate(currentDate.getDate() + 1);
}
}
}
// Sort events by start time within each date
for (let dateKey in newEventsByDate) {
newEventsByDate[dateKey].sort((a, b) => a.start.getTime() - b.start.getTime())
newEventsByDate[dateKey].sort((a, b) => {
return a.start.getTime() - b.start.getTime();
});
}
root.eventsByDate = newEventsByDate
root.lastError = ""
console.log("CalendarService: Loaded events for", Object.keys(newEventsByDate).length, "days")
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 = {}
root.lastError = "Failed to parse events JSON: " + error.toString();
console.error("CalendarService:", root.lastError);
root.eventsByDate = {
};
}
// Reset for next run
eventsProcess.rawOutput = ""
eventsProcess.rawOutput = "";
}
onStarted: {
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"),
"to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"))
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"), "to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"));
}
}
function checkKhalAvailability() {
if (!khalCheckProcess.running) {
khalCheckProcess.running = true
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
eventsProcess.rawOutput += data + "\n";
}
}
}
function loadCurrentMonth() {
if (!root.khalAvailable) return
let today = new Date()
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0)
// Add padding
let startDate = new Date(firstDay)
startDate.setDate(startDate.getDate() - firstDay.getDay() - 7)
let endDate = new Date(lastDay)
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7)
loadEvents(startDate, endDate)
}
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
root.lastStartDate = startDate
root.lastEndDate = endDate
root.isLoading = true
// Format dates for khal (MM/dd/yyyy based on printformats)
let startDateStr = Qt.formatDate(startDate, "MM/dd/yyyy")
let endDateStr = Qt.formatDate(endDate, "MM/dd/yyyy")
eventsProcess.requestStartDate = startDate
eventsProcess.requestEndDate = endDate
eventsProcess.command = [
"khal", "list",
"--json", "title",
"--json", "description",
"--json", "start-date",
"--json", "start-time",
"--json", "end-date",
"--json", "end-time",
"--json", "all-day",
"--json", "location",
"--json", "url",
startDateStr, endDateStr
]
eventsProcess.running = true
}
function getEventsForDate(date) {
let dateKey = Qt.formatDate(date, "yyyy-MM-dd")
return root.eventsByDate[dateKey] || []
}
function hasEventsForDate(date) {
let events = getEventsForDate(date)
return events.length > 0
}
// Initialize on component completion
Component.onCompleted: {
console.log("CalendarService: Component completed, initializing...")
checkKhalAvailability()
}
}
}

View File

@@ -7,92 +7,37 @@ import Quickshell.Io
Singleton {
id: root
property bool niriAvailable: false
property string focusedAppId: ""
property string focusedAppName: ""
property string focusedWindowTitle: ""
property int focusedWindowId: -1
Component.onCompleted: {
// Use the availability from NiriWorkspaceService to avoid duplicate checks
root.niriAvailable = NiriWorkspaceService.niriAvailable
// Connect to workspace service events
NiriWorkspaceService.onNiriAvailableChanged.connect(() => {
root.niriAvailable = NiriWorkspaceService.niriAvailable
if (root.niriAvailable) {
loadInitialFocusedWindow()
}
})
if (root.niriAvailable) {
loadInitialFocusedWindow()
}
}
// Listen to window focus changes from NiriWorkspaceService
Connections {
target: NiriWorkspaceService
function onFocusedWindowIdChanged() {
root.focusedWindowId = parseInt(NiriWorkspaceService.focusedWindowId) || -1
updateFocusedWindowData()
}
function onFocusedWindowTitleChanged() {
root.focusedWindowTitle = NiriWorkspaceService.focusedWindowTitle
}
}
// Process to get focused window info
Process {
id: focusedWindowQuery
command: ["niri", "msg", "--json", "focused-window"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
try {
const windowData = JSON.parse(text.trim())
root.focusedAppId = windowData.app_id || ""
root.focusedWindowTitle = windowData.title || ""
root.focusedAppName = getDisplayName(windowData.app_id || "")
root.focusedWindowId = parseInt(windowData.id) || -1
} catch (e) {
console.warn("FocusedWindowService: Failed to parse focused window data:", e)
clearFocusedWindow()
}
} else {
clearFocusedWindow()
}
}
}
}
function loadInitialFocusedWindow() {
if (root.niriAvailable) {
focusedWindowQuery.running = true
}
if (root.niriAvailable)
focusedWindowQuery.running = true;
}
function updateFocusedWindowData() {
if (root.niriAvailable && root.focusedWindowId !== -1) {
focusedWindowQuery.running = true
} else {
clearFocusedWindow()
}
if (root.niriAvailable && root.focusedWindowId !== -1)
focusedWindowQuery.running = true;
else
clearFocusedWindow();
}
function clearFocusedWindow() {
root.focusedAppId = ""
root.focusedAppName = ""
root.focusedWindowTitle = ""
root.focusedAppId = "";
root.focusedAppName = "";
root.focusedWindowTitle = "";
}
// Convert app_id to a more user-friendly display name
function getDisplayName(appId) {
if (!appId) return ""
if (!appId)
return "";
// Common app_id to display name mappings
const appNames = {
"com.mitchellh.ghostty": "Ghostty",
@@ -113,22 +58,74 @@ Singleton {
"discord": "Discord",
"slack": "Slack",
"zoom": "Zoom"
}
};
// Return mapped name or clean up the app_id
if (appNames[appId]) {
return appNames[appId]
}
if (appNames[appId])
return appNames[appId];
// Try to extract a clean name from the app_id
// Remove common prefixes and make first letter uppercase
let cleanName = appId
.replace(/^(org\.|com\.|net\.|io\.)/, '')
.replace(/\./g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
return cleanName
let cleanName = appId.replace(/^(org\.|com\.|net\.|io\.)/, '').replace(/\./g, ' ').split(' ').map((word) => {
return word.charAt(0).toUpperCase() + word.slice(1);
}).join(' ');
return cleanName;
}
}
Component.onCompleted: {
// Use the availability from NiriWorkspaceService to avoid duplicate checks
root.niriAvailable = NiriWorkspaceService.niriAvailable;
// Connect to workspace service events
NiriWorkspaceService.onNiriAvailableChanged.connect(() => {
root.niriAvailable = NiriWorkspaceService.niriAvailable;
if (root.niriAvailable)
loadInitialFocusedWindow();
});
if (root.niriAvailable)
loadInitialFocusedWindow();
}
// Listen to window focus changes from NiriWorkspaceService
Connections {
function onFocusedWindowIdChanged() {
root.focusedWindowId = parseInt(NiriWorkspaceService.focusedWindowId) || -1;
updateFocusedWindowData();
}
function onFocusedWindowTitleChanged() {
root.focusedWindowTitle = NiriWorkspaceService.focusedWindowTitle;
}
target: NiriWorkspaceService
}
// Process to get focused window info
Process {
id: focusedWindowQuery
command: ["niri", "msg", "--json", "focused-window"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text && text.trim()) {
try {
const windowData = JSON.parse(text.trim());
root.focusedAppId = windowData.app_id || "";
root.focusedWindowTitle = windowData.title || "";
root.focusedAppName = getDisplayName(windowData.app_id || "");
root.focusedWindowId = parseInt(windowData.id) || -1;
} catch (e) {
console.warn("FocusedWindowService: Failed to parse focused window data:", e);
clearFocusedWindow();
}
} else {
clearFocusedWindow();
}
}
}
}
}

View File

@@ -6,16 +6,14 @@ 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

@@ -9,14 +9,14 @@ Singleton {
id: root
// Workspace management
property list<var> allWorkspaces: []
property var allWorkspaces: []
property int focusedWorkspaceIndex: 0
property string focusedWorkspaceId: ""
property var currentOutputWorkspaces: []
property string currentOutput: ""
// Window management
property list<var> windows: []
property var windows: []
property int focusedWindowIndex: -1
property string focusedWindowTitle: "(No active window)"
property string focusedWindowId: ""

View File

@@ -7,106 +7,106 @@ import Quickshell.Io
Singleton {
id: root
property string osLogo: ""
property string osName: ""
// OS Detection using /etc/os-release
Process {
id: osDetector
command: ["sh", "-c", "grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '\"'"]
running: true
onExited: (exitCode) => {
if (exitCode !== 0) {
// Ultimate fallback - use generic apps icon (empty logo means fallback to "apps")
root.osLogo = "";
root.osName = "Linux";
console.log("OS detection failed, using generic icon");
}
}
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
if (data.trim()) {
let osId = data.trim().toLowerCase()
console.log("Detected OS from /etc/os-release:", osId)
let osId = data.trim().toLowerCase();
console.log("Detected OS from /etc/os-release:", osId);
// Set OS-specific Nerd Font icons and names
switch(osId) {
case "arch":
root.osLogo = "\uf303" // Arch Linux Nerd Font icon
root.osName = "Arch Linux"
break
case "ubuntu":
root.osLogo = "\uf31b" // Ubuntu Nerd Font icon
root.osName = "Ubuntu"
break
case "fedora":
root.osLogo = "\uf30a" // Fedora Nerd Font icon
root.osName = "Fedora"
break
case "debian":
root.osLogo = "\uf306" // Debian Nerd Font icon
root.osName = "Debian"
break
case "opensuse":
case "opensuse-leap":
case "opensuse-tumbleweed":
root.osLogo = "\uef6d" // openSUSE Nerd Font icon
root.osName = "openSUSE"
break
case "manjaro":
root.osLogo = "\uf312" // Manjaro Nerd Font icon
root.osName = "Manjaro"
break
case "nixos":
root.osLogo = "\uf313" // NixOS Nerd Font icon
root.osName = "NixOS"
break
case "rocky":
root.osLogo = "\uf32b" // Rocky Linux Nerd Font icon
root.osName = "Rocky Linux"
break
case "almalinux":
root.osLogo = "\uf31d" // AlmaLinux Nerd Font icon
root.osName = "AlmaLinux"
break
case "centos":
root.osLogo = "\uf304" // CentOS Nerd Font icon
root.osName = "CentOS"
break
case "rhel":
case "redhat":
root.osLogo = "\uf316" // Red Hat Nerd Font icon
root.osName = "Red Hat"
break
case "gentoo":
root.osLogo = "\uf30d" // Gentoo Nerd Font icon
root.osName = "Gentoo"
break
case "mint":
case "linuxmint":
root.osLogo = "\uf30e" // Linux Mint Nerd Font icon
root.osName = "Linux Mint"
break
case "elementary":
root.osLogo = "\uf309" // Elementary OS Nerd Font icon
root.osName = "Elementary OS"
break
case "pop":
root.osLogo = "\uf32a" // Pop!_OS Nerd Font icon
root.osName = "Pop!_OS"
break
default:
root.osLogo = "\uf17c" // Generic Linux Nerd Font icon
root.osName = "Linux"
switch (osId) {
case "arch":
root.osLogo = "\uf303"; // Arch Linux Nerd Font icon
root.osName = "Arch Linux";
break;
case "ubuntu":
root.osLogo = "\uf31b"; // Ubuntu Nerd Font icon
root.osName = "Ubuntu";
break;
case "fedora":
root.osLogo = "\uf30a"; // Fedora Nerd Font icon
root.osName = "Fedora";
break;
case "debian":
root.osLogo = "\uf306"; // Debian Nerd Font icon
root.osName = "Debian";
break;
case "opensuse":
case "opensuse-leap":
case "opensuse-tumbleweed":
root.osLogo = "\uef6d"; // openSUSE Nerd Font icon
root.osName = "openSUSE";
break;
case "manjaro":
root.osLogo = "\uf312"; // Manjaro Nerd Font icon
root.osName = "Manjaro";
break;
case "nixos":
root.osLogo = "\uf313"; // NixOS Nerd Font icon
root.osName = "NixOS";
break;
case "rocky":
root.osLogo = "\uf32b"; // Rocky Linux Nerd Font icon
root.osName = "Rocky Linux";
break;
case "almalinux":
root.osLogo = "\uf31d"; // AlmaLinux Nerd Font icon
root.osName = "AlmaLinux";
break;
case "centos":
root.osLogo = "\uf304"; // CentOS Nerd Font icon
root.osName = "CentOS";
break;
case "rhel":
case "redhat":
root.osLogo = "\uf316"; // Red Hat Nerd Font icon
root.osName = "Red Hat";
break;
case "gentoo":
root.osLogo = "\uf30d"; // Gentoo Nerd Font icon
root.osName = "Gentoo";
break;
case "mint":
case "linuxmint":
root.osLogo = "\uf30e"; // Linux Mint Nerd Font icon
root.osName = "Linux Mint";
break;
case "elementary":
root.osLogo = "\uf309"; // Elementary OS Nerd Font icon
root.osName = "Elementary OS";
break;
case "pop":
root.osLogo = "\uf32a"; // Pop!_OS Nerd Font icon
root.osName = "Pop!_OS";
break;
default:
root.osLogo = "\uf17c"; // Generic Linux Nerd Font icon
root.osName = "Linux";
}
console.log("Set OS logo:", root.osLogo, "Name:", root.osName)
console.log("Set OS logo:", root.osLogo, "Name:", root.osName);
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
// Ultimate fallback - use generic apps icon (empty logo means fallback to "apps")
root.osLogo = ""
root.osName = "Linux"
console.log("OS detection failed, using generic icon")
}
}
}
}
}

View File

@@ -6,263 +6,122 @@ import Quickshell
import Quickshell.Io
Singleton {
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
id: root
property var processes: []
property bool isUpdating: false
property int processUpdateInterval: 3000
property bool monitoringEnabled: false
property int totalMemoryKB: 0
property int usedMemoryKB: 0
property int totalSwapKB: 0
property int usedSwapKB: 0
property int cpuCount: 1
property real totalCpuUsage: 0.0
property real totalCpuUsage: 0
property bool systemInfoAvailable: false
property var cpuHistory: []
property var memoryHistory: []
property var networkHistory: ({rx: [], tx: []})
property var diskHistory: ({read: [], write: []})
property var networkHistory: ({
"rx": [],
"tx": []
})
property var diskHistory: ({
"read": [],
"write": []
})
property int historySize: 60
property var perCoreCpuUsage: []
property real networkRxRate: 0
property real networkTxRate: 0
property var lastNetworkStats: null
property real diskReadRate: 0
property real diskWriteRate: 0
property var lastDiskStats: null
property string sortBy: "cpu"
property bool sortDescending: true
property int maxProcesses: 20
Component.onCompleted: {
console.log("ProcessMonitorService: Starting initialization...")
updateProcessList()
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
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
parseSystemInfo(text.trim())
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("System info check failed with exit code:", exitCode)
root.systemInfoAvailable = false
}
}
}
Process {
id: networkStatsProcess
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
parseNetworkStats(text.trim())
}
}
}
}
Process {
id: diskStatsProcess
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
parseDiskStats(text.trim())
}
}
}
}
Process {
id: processListProcess
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
const lines = text.trim().split('\n')
const newProcesses = []
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim()
if (!line) continue
const parts = line.split(/\s+/)
if (parts.length >= 7) {
const pid = parseInt(parts[0])
const ppid = parseInt(parts[1])
const cpu = parseFloat(parts[2])
const memoryPercent = parseFloat(parts[3])
const memoryKB = parseInt(parts[4])
const command = parts[5]
const fullCmd = parts.slice(6).join(' ')
newProcesses.push({
pid: pid,
ppid: ppid,
cpu: cpu,
memoryPercent: memoryPercent,
memoryKB: memoryKB,
command: command,
fullCommand: fullCmd,
displayName: command.length > 15 ? command.substring(0, 15) + "..." : command
})
}
}
root.processes = newProcesses
root.isUpdating = false
}
}
}
onExited: (exitCode) => {
root.isUpdating = false
if (exitCode !== 0) {
console.warn("Process list check failed with exit code:", exitCode)
}
}
}
Timer {
id: processTimer
interval: root.processUpdateInterval
running: root.monitoringEnabled
repeat: true
onTriggered: {
if (root.monitoringEnabled) {
updateSystemInfo()
updateProcessList()
updateNetworkStats()
updateDiskStats()
}
}
}
function updateSystemInfo() {
if (!systemInfoProcess.running && root.monitoringEnabled) {
systemInfoProcess.running = true
}
if (!systemInfoProcess.running && root.monitoringEnabled)
systemInfoProcess.running = true;
}
function enableMonitoring(enabled) {
console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled")
root.monitoringEnabled = enabled
console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled");
root.monitoringEnabled = enabled;
if (enabled) {
root.cpuHistory = []
root.memoryHistory = []
root.networkHistory = ({rx: [], tx: []})
root.diskHistory = ({read: [], write: []})
updateSystemInfo()
updateProcessList()
updateNetworkStats()
updateDiskStats()
root.cpuHistory = [];
root.memoryHistory = [];
root.networkHistory = ({
"rx": [],
"tx": []
});
root.diskHistory = ({
"read": [],
"write": []
});
updateSystemInfo();
updateProcessList();
updateNetworkStats();
updateDiskStats();
}
}
function updateNetworkStats() {
if (!networkStatsProcess.running && root.monitoringEnabled) {
networkStatsProcess.running = true
}
if (!networkStatsProcess.running && root.monitoringEnabled)
networkStatsProcess.running = true;
}
function updateDiskStats() {
if (!diskStatsProcess.running && root.monitoringEnabled) {
diskStatsProcess.running = true
}
if (!diskStatsProcess.running && root.monitoringEnabled)
diskStatsProcess.running = true;
}
function updateProcessList() {
if (!root.isUpdating && root.monitoringEnabled) {
root.isUpdating = true
let sortOption = ""
root.isUpdating = true;
let sortOption = "";
switch (root.sortBy) {
case "cpu":
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu"
break
case "memory":
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem"
break
case "name":
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm"
break
case "pid":
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid"
break
default:
sortOption = "--sort=-pcpu"
case "cpu":
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu";
break;
case "memory":
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem";
break;
case "name":
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm";
break;
case "pid":
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid";
break;
default:
sortOption = "--sort=-pcpu";
}
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)]
processListProcess.running = true
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)];
processListProcess.running = true;
}
}
function setSortBy(newSortBy) {
if (newSortBy !== root.sortBy) {
root.sortBy = newSortBy
updateProcessList()
root.sortBy = newSortBy;
updateProcessList();
}
}
function toggleSortOrder() {
root.sortDescending = !root.sortDescending
updateProcessList()
root.sortDescending = !root.sortDescending;
updateProcessList();
}
function killProcess(pid) {
if (pid > 0) {
const killCmd = ["bash", "-c", "kill " + pid]
const killCmd = ["bash", "-c", "kill " + pid];
const killProcess = Qt.createQmlObject(`
import QtQuick
import Quickshell.Io
@@ -278,196 +137,346 @@ Singleton {
destroy()
}
}
`, root)
`, root);
}
}
function getProcessIcon(command) {
const cmd = command.toLowerCase()
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser")) return "web"
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim")) return "code"
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh")) return "terminal"
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify")) return "music_note"
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv")) return "play_circle"
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread")) return "settings"
return "memory"
const cmd = command.toLowerCase();
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
return "web";
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
return "code";
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
return "terminal";
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
return "music_note";
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
return "play_circle";
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread"))
return "settings";
return "memory";
}
function formatCpuUsage(cpu) {
return cpu.toFixed(1) + "%"
return cpu.toFixed(1) + "%";
}
function formatMemoryUsage(memoryKB) {
if (memoryKB < 1024) {
return memoryKB.toFixed(0) + " KB"
} else if (memoryKB < 1024 * 1024) {
return (memoryKB / 1024).toFixed(1) + " MB"
} else {
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB"
}
if (memoryKB < 1024)
return memoryKB.toFixed(0) + " KB";
else if (memoryKB < 1024 * 1024)
return (memoryKB / 1024).toFixed(1) + " MB";
else
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
}
function formatSystemMemory(memoryKB) {
if (memoryKB < 1024 * 1024) {
return (memoryKB / 1024).toFixed(0) + " MB"
} else {
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB"
}
if (memoryKB < 1024 * 1024)
return (memoryKB / 1024).toFixed(0) + " MB";
else
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
}
function parseSystemInfo(text) {
const lines = text.split('\n')
let section = 'memory'
const coreUsages = []
let memFree = 0
let memBuffers = 0
let memCached = 0
const lines = text.split('\n');
let section = 'memory';
const coreUsages = [];
let memFree = 0;
let memBuffers = 0;
let memCached = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
const line = lines[i].trim();
if (line === '---CPU---') {
section = 'cpucount'
continue
section = 'cpucount';
continue;
} else if (line === '---CPUSTAT---') {
section = 'cpustat'
continue
section = 'cpustat';
continue;
}
if (section === 'memory') {
if (line.startsWith('MemTotal:')) {
root.totalMemoryKB = parseInt(line.split(/\s+/)[1])
root.totalMemoryKB = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('MemFree:')) {
memFree = parseInt(line.split(/\s+/)[1])
memFree = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('Buffers:')) {
memBuffers = parseInt(line.split(/\s+/)[1])
memBuffers = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('Cached:')) {
memCached = parseInt(line.split(/\s+/)[1])
memCached = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('SwapTotal:')) {
root.totalSwapKB = parseInt(line.split(/\s+/)[1])
root.totalSwapKB = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('SwapFree:')) {
const freeSwapKB = parseInt(line.split(/\s+/)[1])
root.usedSwapKB = root.totalSwapKB - freeSwapKB
const freeSwapKB = parseInt(line.split(/\s+/)[1]);
root.usedSwapKB = root.totalSwapKB - freeSwapKB;
}
} else if (section === 'cpucount') {
const count = parseInt(line)
if (!isNaN(count)) {
root.cpuCount = count
}
const count = parseInt(line);
if (!isNaN(count))
root.cpuCount = count;
} else if (section === 'cpustat') {
if (line.startsWith('cpu ')) {
const parts = line.split(/\s+/)
const parts = line.split(/\s+/);
if (parts.length >= 8) {
const user = parseInt(parts[1])
const nice = parseInt(parts[2])
const system = parseInt(parts[3])
const idle = parseInt(parts[4])
const iowait = parseInt(parts[5])
const irq = parseInt(parts[6])
const softirq = parseInt(parts[7])
const total = user + nice + system + idle + iowait + irq + softirq
const used = total - idle - iowait
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0
const user = parseInt(parts[1]);
const nice = parseInt(parts[2]);
const system = parseInt(parts[3]);
const idle = parseInt(parts[4]);
const iowait = parseInt(parts[5]);
const irq = parseInt(parts[6]);
const softirq = parseInt(parts[7]);
const total = user + nice + system + idle + iowait + irq + softirq;
const used = total - idle - iowait;
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0;
}
} else if (line.match(/^cpu\d+/)) {
const parts = line.split(/\s+/)
const parts = line.split(/\s+/);
if (parts.length >= 8) {
const user = parseInt(parts[1])
const nice = parseInt(parts[2])
const system = parseInt(parts[3])
const idle = parseInt(parts[4])
const iowait = parseInt(parts[5])
const irq = parseInt(parts[6])
const softirq = parseInt(parts[7])
const total = user + nice + system + idle + iowait + irq + softirq
const used = total - idle - iowait
const usage = total > 0 ? (used / total) * 100 : 0
coreUsages.push(usage)
const user = parseInt(parts[1]);
const nice = parseInt(parts[2]);
const system = parseInt(parts[3]);
const idle = parseInt(parts[4]);
const iowait = parseInt(parts[5]);
const irq = parseInt(parts[6]);
const softirq = parseInt(parts[7]);
const total = user + nice + system + idle + iowait + irq + softirq;
const used = total - idle - iowait;
const usage = total > 0 ? (used / total) * 100 : 0;
coreUsages.push(usage);
}
}
}
}
// Calculate used memory as total minus free minus buffers minus cached
root.usedMemoryKB = root.totalMemoryKB - memFree - memBuffers - memCached
root.usedMemoryKB = root.totalMemoryKB - memFree - memBuffers - memCached;
// Update per-core usage
root.perCoreCpuUsage = coreUsages
root.perCoreCpuUsage = coreUsages;
// Update history
addToHistory(root.cpuHistory, root.totalCpuUsage)
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0
addToHistory(root.memoryHistory, memoryPercent)
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
root.systemInfoAvailable = true
addToHistory(root.cpuHistory, root.totalCpuUsage);
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0;
addToHistory(root.memoryHistory, memoryPercent);
root.systemInfoAvailable = true;
}
function parseNetworkStats(text) {
const lines = text.split('\n')
let totalRx = 0
let totalTx = 0
const lines = text.split('\n');
let totalRx = 0;
let totalTx = 0;
for (const line of lines) {
const parts = line.trim().split(/\s+/)
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
const rx = parseInt(parts[1])
const tx = parseInt(parts[2])
const rx = parseInt(parts[1]);
const tx = parseInt(parts[2]);
if (!isNaN(rx) && !isNaN(tx)) {
totalRx += rx
totalTx += tx
totalRx += rx;
totalTx += tx;
}
}
}
if (root.lastNetworkStats) {
const timeDiff = root.processUpdateInterval / 1000
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff)
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff)
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024)
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024)
const timeDiff = root.processUpdateInterval / 1000;
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff);
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff);
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024);
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024);
}
root.lastNetworkStats = { rx: totalRx, tx: totalTx }
root.lastNetworkStats = {
"rx": totalRx,
"tx": totalTx
};
}
function parseDiskStats(text) {
const lines = text.split('\n')
let totalRead = 0
let totalWrite = 0
const lines = text.split('\n');
let totalRead = 0;
let totalWrite = 0;
for (const line of lines) {
const parts = line.trim().split(/\s+/)
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
const readSectors = parseInt(parts[1])
const writeSectors = parseInt(parts[2])
const readSectors = parseInt(parts[1]);
const writeSectors = parseInt(parts[2]);
if (!isNaN(readSectors) && !isNaN(writeSectors)) {
totalRead += readSectors * 512
totalWrite += writeSectors * 512
totalRead += readSectors * 512;
totalWrite += writeSectors * 512;
}
}
}
if (root.lastDiskStats) {
const timeDiff = root.processUpdateInterval / 1000
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff)
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff)
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024))
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024))
const timeDiff = root.processUpdateInterval / 1000;
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff);
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff);
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024));
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024));
}
root.lastDiskStats = { read: totalRead, write: totalWrite }
root.lastDiskStats = {
"read": totalRead,
"write": totalWrite
};
}
function addToHistory(array, value) {
array.push(value)
if (array.length > root.historySize) {
array.shift()
array.push(value);
if (array.length > root.historySize)
array.shift();
}
Component.onCompleted: {
console.log("ProcessMonitorService: Starting initialization...");
updateProcessList();
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
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("System info check failed with exit code:", exitCode);
root.systemInfoAvailable = false;
}
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim())
parseSystemInfo(text.trim());
}
}
}
Process {
id: networkStatsProcess
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim())
parseNetworkStats(text.trim());
}
}
}
Process {
id: diskStatsProcess
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim())
parseDiskStats(text.trim());
}
}
}
Process {
id: processListProcess
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
running: false
onExited: (exitCode) => {
root.isUpdating = false;
if (exitCode !== 0)
console.warn("Process list check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
const lines = text.trim().split('\n');
const newProcesses = [];
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (!line)
continue;
const parts = line.split(/\s+/);
if (parts.length >= 7) {
const pid = parseInt(parts[0]);
const ppid = parseInt(parts[1]);
const cpu = parseFloat(parts[2]);
const memoryPercent = parseFloat(parts[3]);
const memoryKB = parseInt(parts[4]);
const command = parts[5];
const fullCmd = parts.slice(6).join(' ');
newProcesses.push({
"pid": pid,
"ppid": ppid,
"cpu": cpu,
"memoryPercent": memoryPercent,
"memoryKB": memoryKB,
"command": command,
"fullCommand": fullCmd,
"displayName": command.length > 15 ? command.substring(0, 15) + "..." : command
});
}
}
root.processes = newProcesses;
root.isUpdating = false;
}
}
}
}
Timer {
id: processTimer
interval: root.processUpdateInterval
running: root.monitoringEnabled
repeat: true
onTriggered: {
if (root.monitoringEnabled) {
updateSystemInfo();
updateProcessList();
updateNetworkStats();
updateDiskStats();
}
}
}
}

View File

@@ -7,24 +7,20 @@ import Quickshell.Io
Singleton {
id: root
property real cpuUsage: 0.0
property real cpuUsage: 0
property int cpuCores: 1
property string cpuModel: ""
property real cpuFrequency: 0.0
property real cpuFrequency: 0
property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0]
property real memoryUsage: 0.0
property real totalMemory: 0.0
property real usedMemory: 0.0
property real freeMemory: 0.0
property real availableMemory: 0.0
property real bufferMemory: 0.0
property real cacheMemory: 0.0
property real cpuTemperature: 0.0
property real memoryUsage: 0
property real totalMemory: 0
property real usedMemory: 0
property real freeMemory: 0
property real availableMemory: 0
property real bufferMemory: 0
property real cacheMemory: 0
property real cpuTemperature: 0
property string kernelVersion: ""
property string distribution: ""
property string hostname: ""
@@ -39,537 +35,567 @@ Singleton {
property string biosVersion: ""
property var diskMounts: []
property string diskUsage: ""
property int cpuUpdateInterval: 3000
property int memoryUpdateInterval: 5000
property int temperatureUpdateInterval: 10000
property int systemInfoUpdateInterval: 30000
property bool enabledForTopBar: true
property bool enabledForDetailedView: false
Component.onCompleted: {
console.log("SystemMonitorService: Starting initialization...")
getCpuInfo()
updateSystemStats()
updateSystemInfo()
console.log("SystemMonitorService: Initialization complete")
function getCpuInfo() {
cpuInfoProcess.running = true;
}
function updateSystemStats() {
if (root.enabledForTopBar || root.enabledForDetailedView) {
cpuUsageProcess.running = true;
memoryUsageProcess.running = true;
cpuFrequencyProcess.running = true;
if (root.enabledForDetailedView)
temperatureProcess.running = true;
}
}
function updateSystemInfo() {
kernelInfoProcess.running = true;
distributionProcess.running = true;
hostnameProcess.running = true;
uptimeProcess.running = true;
schedulerProcess.running = true;
architectureProcess.running = true;
loadAverageProcess.running = true;
processCountProcess.running = true;
threadCountProcess.running = true;
bootTimeProcess.running = true;
motherboardProcess.running = true;
biosProcess.running = true;
diskMountsProcess.running = true;
}
function enableTopBarMonitoring(enabled) {
root.enabledForTopBar = enabled;
}
function enableDetailedMonitoring(enabled) {
root.enabledForDetailedView = enabled;
}
function getCpuUsageColor() {
if (cpuUsage > 80)
return "#e74c3c";
if (cpuUsage > 60)
return "#f39c12";
return "#27ae60";
}
function getMemoryUsageColor() {
if (memoryUsage > 90)
return "#e74c3c";
if (memoryUsage > 75)
return "#f39c12";
return "#3498db";
}
function formatMemory(mb) {
if (mb >= 1024)
return (mb / 1024).toFixed(1) + " GB";
return mb.toFixed(0) + " MB";
}
function getTemperatureColor() {
if (cpuTemperature > 80)
return "#e74c3c";
if (cpuTemperature > 65)
return "#f39c12";
return "#27ae60";
}
Component.onCompleted: {
console.log("SystemMonitorService: Starting initialization...");
getCpuInfo();
updateSystemStats();
updateSystemInfo();
console.log("SystemMonitorService: Initialization complete");
}
Process {
id: cpuInfoProcess
command: ["bash", "-c", "lscpu | grep -E 'Model name|CPU\\(s\\):' | head -2"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU info check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
const lines = text.trim().split('\n')
const lines = text.trim().split('\n');
for (const line of lines) {
if (line.includes("Model name")) {
root.cpuModel = line.split(":")[1].trim()
} else if (line.includes("CPU(s):")) {
root.cpuCores = parseInt(line.split(":")[1].trim())
}
if (line.includes("Model name"))
root.cpuModel = line.split(":")[1].trim();
else if (line.includes("CPU(s):"))
root.cpuCores = parseInt(line.split(":")[1].trim());
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU info check failed with exit code:", exitCode)
}
}
}
Process {
id: cpuUsageProcess
command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU usage check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
const stats = text.trim().split(" ").map(x => parseInt(x))
const stats = text.trim().split(" ").map((x) => {
return parseInt(x);
});
if (root.prevCpuStats[0] > 0) {
let diffs = []
let diffs = [];
for (let i = 0; i < 8; i++) {
diffs[i] = stats[i] - root.prevCpuStats[i]
}
const totalTime = diffs.reduce((a, b) => a + b, 0)
const idleTime = diffs[3] + diffs[4]
if (totalTime > 0) {
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100))
diffs[i] = stats[i] - root.prevCpuStats[i];
}
const totalTime = diffs.reduce((a, b) => {
return a + b;
}, 0);
const idleTime = diffs[3] + diffs[4];
if (totalTime > 0)
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100));
}
root.prevCpuStats = stats
root.prevCpuStats = stats;
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU usage check failed with exit code:", exitCode)
}
}
}
Process {
id: memoryUsageProcess
command: ["bash", "-c", "free -m | awk 'NR==2{printf \"%.1f %.1f %.1f %.1f\", $3*100/$2, $2, $3, $7}'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Memory usage check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
const parts = text.trim().split(" ")
root.memoryUsage = parseFloat(parts[0])
root.totalMemory = parseFloat(parts[1])
root.usedMemory = parseFloat(parts[2])
root.availableMemory = parseFloat(parts[3])
root.freeMemory = root.totalMemory - root.usedMemory
const parts = text.trim().split(" ");
root.memoryUsage = parseFloat(parts[0]);
root.totalMemory = parseFloat(parts[1]);
root.usedMemory = parseFloat(parts[2]);
root.availableMemory = parseFloat(parts[3]);
root.freeMemory = root.totalMemory - root.usedMemory;
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Memory usage check failed with exit code:", exitCode)
}
}
}
Process {
id: cpuFrequencyProcess
command: ["bash", "-c", "cat /proc/cpuinfo | grep 'cpu MHz' | head -1 | awk '{print $4}'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU frequency check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.cpuFrequency = parseFloat(text.trim())
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU frequency check failed with exit code:", exitCode)
if (text.trim())
root.cpuFrequency = parseFloat(text.trim());
}
}
}
Process {
id: temperatureProcess
command: ["bash", "-c", "if [ -f /sys/class/thermal/thermal_zone0/temp ]; then cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1/1000}'; else sensors 2>/dev/null | grep 'Core 0' | awk '{print $3}' | sed 's/+//g;s/°C//g' | head -1; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU temperature check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.cpuTemperature = parseFloat(text.trim())
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU temperature check failed with exit code:", exitCode)
if (text.trim())
root.cpuTemperature = parseFloat(text.trim());
}
}
}
Process {
id: kernelInfoProcess
command: ["bash", "-c", "uname -r"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Kernel info check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.kernelVersion = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Kernel info check failed with exit code:", exitCode)
if (text.trim())
root.kernelVersion = text.trim();
}
}
}
Process {
id: distributionProcess
command: ["bash", "-c", "grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '\"' || echo 'Unknown'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Distribution check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.distribution = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Distribution check failed with exit code:", exitCode)
if (text.trim())
root.distribution = text.trim();
}
}
}
Process {
id: hostnameProcess
command: ["bash", "-c", "hostname"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Hostname check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.hostname = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Hostname check failed with exit code:", exitCode)
if (text.trim())
root.hostname = text.trim();
}
}
}
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()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Uptime check failed with exit code:", exitCode)
if (text.trim())
root.uptime = text.trim();
}
}
}
Process {
id: schedulerProcess
command: ["bash", "-c", "cat /sys/block/sda/queue/scheduler 2>/dev/null | grep -o '\\[.*\\]' | tr -d '[]' || echo 'Unknown'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Scheduler check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.scheduler = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Scheduler check failed with exit code:", exitCode)
if (text.trim())
root.scheduler = text.trim();
}
}
}
Process {
id: architectureProcess
command: ["bash", "-c", "uname -m"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Architecture check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.architecture = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Architecture check failed with exit code:", exitCode)
if (text.trim())
root.architecture = text.trim();
}
}
}
Process {
id: loadAverageProcess
command: ["bash", "-c", "cat /proc/loadavg | cut -d' ' -f1,2,3"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Load average check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.loadAverage = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Load average check failed with exit code:", exitCode)
if (text.trim())
root.loadAverage = text.trim();
}
}
}
Process {
id: processCountProcess
command: ["bash", "-c", "ps aux | wc -l"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Process count check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.processCount = parseInt(text.trim()) - 1
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Process count check failed with exit code:", exitCode)
if (text.trim())
root.processCount = parseInt(text.trim()) - 1;
}
}
}
Process {
id: threadCountProcess
command: ["bash", "-c", "cat /proc/stat | grep processes | awk '{print $2}'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Thread count check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.threadCount = parseInt(text.trim())
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Thread count check failed with exit code:", exitCode)
if (text.trim())
root.threadCount = parseInt(text.trim());
}
}
}
Process {
id: bootTimeProcess
command: ["bash", "-c", "who -b | awk '{print $3, $4}' || stat -c %w /proc/1 2>/dev/null | cut -d' ' -f1,2 || echo 'Unknown'"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Boot time check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.bootTime = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Boot time check failed with exit code:", exitCode)
if (text.trim())
root.bootTime = text.trim();
}
}
}
Process {
id: motherboardProcess
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/board_vendor ] && [ -r /sys/devices/virtual/dmi/id/board_name ]; then echo \"$(cat /sys/devices/virtual/dmi/id/board_vendor 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/board_name 2>/dev/null)\"; else echo 'Unknown'; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Motherboard check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.motherboard = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Motherboard check failed with exit code:", exitCode)
if (text.trim())
root.motherboard = text.trim();
}
}
}
Process {
id: biosProcess
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/bios_version ] && [ -r /sys/devices/virtual/dmi/id/bios_date ]; then echo \"$(cat /sys/devices/virtual/dmi/id/bios_version 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/bios_date 2>/dev/null)\"; else echo 'Unknown'; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("BIOS check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
root.biosVersion = text.trim()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("BIOS check failed with exit code:", exitCode)
if (text.trim())
root.biosVersion = text.trim();
}
}
}
Process {
id: diskMountsProcess
command: ["bash", "-c", "df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -v tmpfs | grep -v devtmpfs | head -10"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Disk mounts check failed with exit code:", exitCode);
}
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
let mounts = []
const lines = text.trim().split('\n')
let mounts = [];
const lines = text.trim().split('\n');
for (const line of lines) {
const parts = line.split(/\s+/)
if (parts.length >= 7) {
const parts = line.split(/\s+/);
if (parts.length >= 7)
mounts.push({
device: parts[0],
mount: parts[1],
fstype: parts[2],
size: parts[3],
used: parts[4],
avail: parts[5],
percent: parts[6]
})
}
"device": parts[0],
"mount": parts[1],
"fstype": parts[2],
"size": parts[3],
"used": parts[4],
"avail": parts[5],
"percent": parts[6]
});
}
root.diskMounts = mounts
root.diskMounts = mounts;
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Disk mounts check failed with exit code:", exitCode)
}
}
}
Timer {
id: cpuTimer
interval: root.cpuUpdateInterval
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;
}
}
}
Timer {
id: memoryTimer
interval: root.memoryUpdateInterval
running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView) {
memoryUsageProcess.running = true
}
if (root.enabledForTopBar || root.enabledForDetailedView)
memoryUsageProcess.running = true;
}
}
Timer {
id: temperatureTimer
interval: root.temperatureUpdateInterval
running: root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForDetailedView) {
temperatureProcess.running = true
}
if (root.enabledForDetailedView)
temperatureProcess.running = true;
}
}
Timer {
id: systemInfoTimer
interval: root.systemInfoUpdateInterval
running: root.enabledForDetailedView
repeat: true
onTriggered: {
if (root.enabledForDetailedView) {
updateSystemInfo()
}
if (root.enabledForDetailedView)
updateSystemInfo();
}
}
function getCpuInfo() {
cpuInfoProcess.running = true
}
function updateSystemStats() {
if (root.enabledForTopBar || root.enabledForDetailedView) {
cpuUsageProcess.running = true
memoryUsageProcess.running = true
cpuFrequencyProcess.running = true
if (root.enabledForDetailedView) {
temperatureProcess.running = true
}
}
}
function updateSystemInfo() {
kernelInfoProcess.running = true
distributionProcess.running = true
hostnameProcess.running = true
uptimeProcess.running = true
schedulerProcess.running = true
architectureProcess.running = true
loadAverageProcess.running = true
processCountProcess.running = true
threadCountProcess.running = true
bootTimeProcess.running = true
motherboardProcess.running = true
biosProcess.running = true
diskMountsProcess.running = true
}
function enableTopBarMonitoring(enabled) {
root.enabledForTopBar = enabled
}
function enableDetailedMonitoring(enabled) {
root.enabledForDetailedView = enabled
}
function getCpuUsageColor() {
if (cpuUsage > 80) return "#e74c3c"
if (cpuUsage > 60) return "#f39c12"
return "#27ae60"
}
function getMemoryUsageColor() {
if (memoryUsage > 90) return "#e74c3c"
if (memoryUsage > 75) return "#f39c12"
return "#3498db"
}
function formatMemory(mb) {
if (mb >= 1024) {
return (mb / 1024).toFixed(1) + " GB"
}
return mb.toFixed(0) + " MB"
}
function getTemperatureColor() {
if (cpuTemperature > 80) return "#e74c3c"
if (cpuTemperature > 65) return "#f39c12"
return "#27ae60"
}
}
}

View File

@@ -10,16 +10,63 @@ Singleton {
readonly property int levelInfo: 0
readonly property int levelWarn: 1
readonly property int levelError: 2
property string currentMessage: ""
property int currentLevel: levelInfo
property bool toastVisible: false
property var toastQueue: []
property string wallpaperErrorStatus: ""
function showToast(message, level = levelInfo) {
toastQueue.push({
"message": message,
"level": level
});
if (!toastVisible)
processQueue();
}
function showInfo(message) {
showToast(message, levelInfo);
}
function showWarning(message) {
showToast(message, levelWarn);
}
function showError(message) {
showToast(message, levelError);
}
function hideToast() {
toastVisible = false;
currentMessage = "";
currentLevel = levelInfo;
toastTimer.stop();
if (toastQueue.length > 0)
queueTimer.start();
}
function processQueue() {
if (toastQueue.length === 0)
return ;
const toast = toastQueue.shift();
currentMessage = toast.message;
currentLevel = toast.level;
toastVisible = true;
toastTimer.interval = toast.level === levelError ? 8000 : toast.level === levelWarn ? 6000 : 5000;
toastTimer.start();
}
function clearWallpaperError() {
wallpaperErrorStatus = "";
}
Timer {
id: toastTimer
interval: 5000
running: false
repeat: false
@@ -28,56 +75,11 @@ Singleton {
Timer {
id: queueTimer
interval: 500
running: false
repeat: false
onTriggered: processQueue()
}
function showToast(message, level = levelInfo) {
toastQueue.push({ message, level })
if (!toastVisible) {
processQueue()
}
}
function showInfo(message) {
showToast(message, levelInfo)
}
function showWarning(message) {
showToast(message, levelWarn)
}
function showError(message) {
showToast(message, levelError)
}
function hideToast() {
toastVisible = false
currentMessage = ""
currentLevel = levelInfo
toastTimer.stop()
if (toastQueue.length > 0) {
queueTimer.start()
}
}
function processQueue() {
if (toastQueue.length === 0) return
const toast = toastQueue.shift()
currentMessage = toast.message
currentLevel = toast.level
toastVisible = true
toastTimer.interval =
toast.level === levelError ? 8000 :
toast.level === levelWarn ? 6000 : 5000
toastTimer.start()
}
function clearWallpaperError() {
wallpaperErrorStatus = ""
}
}
}

View File

@@ -7,85 +7,105 @@ import Quickshell.Io
Singleton {
id: root
property string username: ""
property string fullName: ""
property string profilePicture: ""
property string uptime: ""
property string hostname: ""
property bool profileAvailable: false
Component.onCompleted: {
getUserInfo()
getUptime()
// Update uptime every minute
uptimeTimer.start()
function getUserInfo() {
userInfoProcess.running = true;
}
function getUptime() {
uptimeProcess.running = true;
}
function getProfilePicture() {
profilePictureProcess.running = true;
}
function refreshUserInfo() {
getUserInfo();
getUptime();
getProfilePicture();
}
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
Process {
id: userInfoProcess
command: ["bash", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to get user info");
root.username = "User";
root.fullName = "User";
root.hostname = "System";
}
}
stdout: StdioCollector {
onStreamFinished: {
const parts = text.trim().split("|")
const parts = text.trim().split("|");
if (parts.length >= 3) {
root.username = parts[0] || ""
root.fullName = parts[1] || parts[0] || ""
root.hostname = parts[2] || ""
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname)
root.username = parts[0] || "";
root.fullName = parts[1] || parts[0] || "";
root.hostname = parts[2] || "";
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname);
// Try to find profile picture
getProfilePicture()
getProfilePicture();
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to get user info")
root.username = "User"
root.fullName = "User"
root.hostname = "System"
}
}
}
// Get system uptime
Process {
id: uptimeProcess
command: ["bash", "-c", "uptime -p | sed 's/up //'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
root.uptime = text.trim() || "Unknown"
console.log("UserInfoService: Uptime updated -", root.uptime)
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to get uptime")
root.uptime = "Unknown"
console.warn("UserInfoService: Failed to get uptime");
root.uptime = "Unknown";
}
}
stdout: StdioCollector {
onStreamFinished: {
root.uptime = text.trim() || "Unknown";
console.log("UserInfoService: Uptime updated -", root.uptime);
}
}
}
// Look for profile picture in common locations
Process {
id: profilePictureProcess
command: ["bash", "-c", `
# Try common profile picture locations
for path in \
@@ -104,46 +124,29 @@ Singleton {
echo ""
`]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to find profile picture");
root.profilePicture = "";
root.profileAvailable = false;
}
}
stdout: StdioCollector {
onStreamFinished: {
const path = text.trim()
const path = text.trim();
if (path && path.length > 0) {
root.profilePicture = "file://" + path
root.profileAvailable = true
console.log("UserInfoService: Profile picture found at", path)
root.profilePicture = "file://" + path;
root.profileAvailable = true;
console.log("UserInfoService: Profile picture found at", path);
} else {
root.profilePicture = ""
root.profileAvailable = false
console.log("UserInfoService: No profile picture found, using default avatar")
root.profilePicture = "";
root.profileAvailable = false;
console.log("UserInfoService: No profile picture found, using default avatar");
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to find profile picture")
root.profilePicture = ""
root.profileAvailable = false
}
}
}
function getUserInfo() {
userInfoProcess.running = true
}
function getUptime() {
uptimeProcess.running = true
}
function getProfilePicture() {
profilePictureProcess.running = true
}
function refreshUserInfo() {
getUserInfo()
getUptime()
getProfilePicture()
}
}
}

View File

@@ -7,7 +7,7 @@ import Quickshell.Io
Singleton {
id: root
property string currentWifiSSID: ""
property string wifiSignalStrength: "excellent" // "excellent", "good", "fair", "poor"
property var wifiNetworks: []
@@ -15,146 +15,26 @@ Singleton {
property bool isScanning: false
property string connectionStatus: "" // "cosnnecting", "connected", "failed", ""
property string connectingSSID: ""
Process {
id: currentWifiInfo
command: ["bash", "-c", "nmcli -t -f ACTIVE,SSID,SIGNAL dev wifi | grep '^yes' | head -1"]
running: false
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
if (data.trim()) {
let parts = data.split(":")
if (parts.length >= 3 && parts[1].trim() !== "") {
root.currentWifiSSID = parts[1].trim()
let signal = parseInt(parts[2]) || 100
if (signal >= 75) root.wifiSignalStrength = "excellent"
else if (signal >= 50) root.wifiSignalStrength = "good"
else if (signal >= 25) root.wifiSignalStrength = "fair"
else root.wifiSignalStrength = "poor"
console.log("Active WiFi:", root.currentWifiSSID, "Signal:", signal + "%")
}
}
}
}
}
Process {
id: wifiScanner
command: ["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY", "dev", "wifi"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
let networks = []
let lines = text.trim().split('\n')
for (let line of lines) {
let parts = line.split(':')
if (parts.length >= 3 && parts[0].trim() !== "") {
let ssid = parts[0].trim()
let signal = parseInt(parts[1]) || 0
let security = parts[2].trim()
// Skip duplicates
if (!networks.find(n => n.ssid === ssid)) {
// Check if this network is saved
let isSaved = root.savedWifiNetworks.some(saved => saved.ssid === ssid)
networks.push({
ssid: ssid,
signal: signal,
secured: security !== "",
connected: ssid === root.currentWifiSSID,
saved: isSaved,
signalStrength: signal >= 75 ? "excellent" :
signal >= 50 ? "good" :
signal >= 25 ? "fair" : "poor"
})
}
}
}
// Sort by signal strength
networks.sort((a, b) => b.signal - a.signal)
root.wifiNetworks = networks
console.log("Found", networks.length, "WiFi networks")
// Stop scanning once we have results
if (networks.length > 0) {
root.isScanning = false
fallbackTimer.stop()
}
}
}
}
}
Process {
id: savedWifiScanner
command: ["nmcli", "-t", "-f", "NAME", "connection", "show"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
let saved = []
let lines = text.trim().split('\n')
for (let line of lines) {
let connectionName = line.trim()
if (connectionName &&
!connectionName.includes("ethernet") &&
!connectionName.includes("lo") &&
!connectionName.includes("Wired") &&
!connectionName.toLowerCase().includes("eth")) {
saved.push({
ssid: connectionName,
saved: true
})
}
}
root.savedWifiNetworks = saved
console.log("Found", saved.length, "saved WiFi networks")
}
}
}
}
// Auto-refresh timer for when control center is open
property bool autoRefreshEnabled: false
function scanWifi() {
if (root.isScanning) return
root.isScanning = true
console.log("Starting WiFi scan...")
wifiScanner.running = true
savedWifiScanner.running = true
currentWifiInfo.running = true
if (root.isScanning)
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()
fallbackTimer.start();
}
Timer {
id: fallbackTimer
interval: 5000
onTriggered: {
root.isScanning = false
console.log("WiFi scan timeout - no networks found")
}
}
function connectToWifi(ssid) {
console.log("Connecting to WiFi:", ssid)
root.connectionStatus = "connecting"
root.connectingSSID = ssid
console.log("Connecting to WiFi:", ssid);
root.connectionStatus = "connecting";
root.connectingSSID = ssid;
let connectProcess = Qt.createQmlObject(`
import Quickshell.Io
Process {
@@ -174,10 +54,10 @@ Singleton {
console.log("WiFi connection failed")
}
scanWifi()
statusResetTimer.start()
}
stderr: SplitParser {
splitMarker: "\\n"
onRead: (data) => {
@@ -185,15 +65,13 @@ Singleton {
}
}
}
`, root)
`, root);
}
function connectToWifiWithPassword(ssid, password) {
console.log("Connecting to WiFi with password:", ssid)
root.connectionStatus = "connecting"
root.connectingSSID = ssid
console.log("Connecting to WiFi with password:", ssid);
root.connectionStatus = "connecting";
root.connectingSSID = ssid;
let connectProcess = Qt.createQmlObject(`
import Quickshell.Io
Process {
@@ -213,10 +91,10 @@ Singleton {
console.log("WiFi connection with password failed")
}
scanWifi()
statusResetTimer.start()
}
stderr: SplitParser {
splitMarker: "\\n"
onRead: (data) => {
@@ -224,11 +102,11 @@ Singleton {
}
}
}
`, root)
`, root);
}
function forgetWifiNetwork(ssid) {
console.log("Forgetting WiFi network:", ssid)
console.log("Forgetting WiFi network:", ssid);
let forgetProcess = Qt.createQmlObject(`
import Quickshell.Io
Process {
@@ -243,7 +121,7 @@ Singleton {
}
scanWifi()
}
stderr: SplitParser {
splitMarker: "\\n"
onRead: (data) => {
@@ -251,35 +129,156 @@ Singleton {
}
}
}
`, root)
`, root);
}
function updateCurrentWifiInfo() {
console.log("Updating current WiFi info...");
currentWifiInfo.running = true;
}
Process {
id: currentWifiInfo
command: ["bash", "-c", "nmcli -t -f ACTIVE,SSID,SIGNAL dev wifi | grep '^yes' | head -1"]
running: false
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
if (data.trim()) {
let parts = data.split(":");
if (parts.length >= 3 && parts[1].trim() !== "") {
root.currentWifiSSID = parts[1].trim();
let signal = parseInt(parts[2]) || 100;
if (signal >= 75)
root.wifiSignalStrength = "excellent";
else if (signal >= 50)
root.wifiSignalStrength = "good";
else if (signal >= 25)
root.wifiSignalStrength = "fair";
else
root.wifiSignalStrength = "poor";
console.log("Active WiFi:", root.currentWifiSSID, "Signal:", signal + "%");
}
}
}
}
}
Process {
id: wifiScanner
command: ["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY", "dev", "wifi"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
let networks = [];
let lines = text.trim().split('\n');
for (let line of lines) {
let parts = line.split(':');
if (parts.length >= 3 && parts[0].trim() !== "") {
let ssid = parts[0].trim();
let signal = parseInt(parts[1]) || 0;
let security = parts[2].trim();
// Skip duplicates
if (!networks.find((n) => {
return n.ssid === ssid;
})) {
// Check if this network is saved
let isSaved = root.savedWifiNetworks.some((saved) => {
return saved.ssid === ssid;
});
networks.push({
"ssid": ssid,
"signal": signal,
"secured": security !== "",
"connected": ssid === root.currentWifiSSID,
"saved": isSaved,
"signalStrength": signal >= 75 ? "excellent" : signal >= 50 ? "good" : signal >= 25 ? "fair" : "poor"
});
}
}
}
// Sort by signal strength
networks.sort((a, b) => {
return b.signal - a.signal;
});
root.wifiNetworks = networks;
console.log("Found", networks.length, "WiFi networks");
// Stop scanning once we have results
if (networks.length > 0) {
root.isScanning = false;
fallbackTimer.stop();
}
}
}
}
}
Process {
id: savedWifiScanner
command: ["nmcli", "-t", "-f", "NAME", "connection", "show"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
let saved = [];
let lines = text.trim().split('\n');
for (let line of lines) {
let connectionName = line.trim();
if (connectionName && !connectionName.includes("ethernet") && !connectionName.includes("lo") && !connectionName.includes("Wired") && !connectionName.toLowerCase().includes("eth"))
saved.push({
"ssid": connectionName,
"saved": true
});
}
root.savedWifiNetworks = saved;
console.log("Found", saved.length, "saved WiFi networks");
}
}
}
}
Timer {
id: statusResetTimer
interval: 3000
id: fallbackTimer
interval: 5000
onTriggered: {
root.connectionStatus = ""
root.connectingSSID = ""
root.isScanning = false;
console.log("WiFi scan timeout - no networks found");
}
}
// Auto-refresh timer for when control center is open
property bool autoRefreshEnabled: false
Timer {
id: statusResetTimer
interval: 3000
onTriggered: {
root.connectionStatus = "";
root.connectingSSID = "";
}
}
Timer {
id: autoRefreshTimer
interval: 20000
running: root.autoRefreshEnabled
repeat: true
onTriggered: {
if (root.autoRefreshEnabled) {
root.scanWifi()
}
if (root.autoRefreshEnabled)
root.scanWifi();
}
}
function updateCurrentWifiInfo() {
console.log("Updating current WiFi info...")
currentWifiInfo.running = true
}
}
}

44
qmlformat-all.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
QMLFORMAT_BIN=${QMLFORMAT_BIN:-qmlformat}
TMPDIR=$(mktemp -d)
find . -type f -name "*.qml" | while read -r file; do
original="$file"
tmp="$TMPDIR/formatted.qml"
pragmas="$TMPDIR/pragmas.txt"
errfile="$TMPDIR/qmlformat.err"
grep '^pragma ' "$original" > "$pragmas" || true
grep -v '^pragma ' "$original" > "$tmp"
if ! "$QMLFORMAT_BIN" -i "$tmp" 2> "$errfile"; then
echo "$original:"
cat "$errfile"
echo
# Extract all line numbers from error log
grep -oE 'formatted\.qml:([0-9]+)' "$errfile" | cut -d: -f2 | sort -n | uniq | while read -r lineno; do
echo "---- formatted.qml line $lineno (with context) ----"
# Show 2 lines before and after, numbering all lines
start=$((lineno - 2))
end=$((lineno + 2))
sed -n "${start},${end}p" "$tmp" | nl -ba
echo
done
echo "---- end of $original ----"
echo
continue
fi
if [[ -s "$pragmas" ]]; then
{ cat "$pragmas"; echo; cat "$tmp"; } > "$original"
else
cat "$tmp" > "$original"
fi
done
rm -rf "$TMPDIR"