1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-30 16: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

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

View File

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

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

View File

@@ -14,84 +14,29 @@ Singleton {
property string focusedWindowTitle: "" property string focusedWindowTitle: ""
property int focusedWindowId: -1 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() { function loadInitialFocusedWindow() {
if (root.niriAvailable) { if (root.niriAvailable)
focusedWindowQuery.running = true focusedWindowQuery.running = true;
}
} }
function updateFocusedWindowData() { function updateFocusedWindowData() {
if (root.niriAvailable && root.focusedWindowId !== -1) { if (root.niriAvailable && root.focusedWindowId !== -1)
focusedWindowQuery.running = true focusedWindowQuery.running = true;
} else { else
clearFocusedWindow() clearFocusedWindow();
}
} }
function clearFocusedWindow() { function clearFocusedWindow() {
root.focusedAppId = "" root.focusedAppId = "";
root.focusedAppName = "" root.focusedAppName = "";
root.focusedWindowTitle = "" root.focusedWindowTitle = "";
} }
// Convert app_id to a more user-friendly display name // Convert app_id to a more user-friendly display name
function getDisplayName(appId) { function getDisplayName(appId) {
if (!appId) return "" if (!appId)
return "";
// Common app_id to display name mappings // Common app_id to display name mappings
const appNames = { const appNames = {
@@ -113,22 +58,74 @@ Singleton {
"discord": "Discord", "discord": "Discord",
"slack": "Slack", "slack": "Slack",
"zoom": "Zoom" "zoom": "Zoom"
} };
// Return mapped name or clean up the app_id // Return mapped name or clean up the app_id
if (appNames[appId]) { if (appNames[appId])
return appNames[appId] return appNames[appId];
}
// Try to extract a clean name from the app_id // Try to extract a clean name from the app_id
// Remove common prefixes and make first letter uppercase // Remove common prefixes and make first letter uppercase
let cleanName = appId let cleanName = appId.replace(/^(org\.|com\.|net\.|io\.)/, '').replace(/\./g, ' ').split(' ').map((word) => {
.replace(/^(org\.|com\.|net\.|io\.)/, '') return word.charAt(0).toUpperCase() + word.slice(1);
.replace(/\./g, ' ') }).join(' ');
.split(' ') return cleanName;
.map(word => 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

@@ -10,11 +10,9 @@ Singleton {
signal showAppLauncher() signal showAppLauncher()
signal hideAppLauncher() signal hideAppLauncher()
signal toggleAppLauncher() signal toggleAppLauncher()
signal showSpotlight() signal showSpotlight()
signal hideSpotlight() signal hideSpotlight()
signal toggleSpotlight() signal toggleSpotlight()
signal showClipboardHistory() signal showClipboardHistory()
signal hideClipboardHistory() signal hideClipboardHistory()
signal toggleClipboardHistory() signal toggleClipboardHistory()

View File

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

View File

@@ -14,99 +14,99 @@ Singleton {
// OS Detection using /etc/os-release // OS Detection using /etc/os-release
Process { Process {
id: osDetector id: osDetector
command: ["sh", "-c", "grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '\"'"] command: ["sh", "-c", "grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '\"'"]
running: true 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 { stdout: SplitParser {
splitMarker: "\n" splitMarker: "\n"
onRead: (data) => { onRead: (data) => {
if (data.trim()) { if (data.trim()) {
let osId = data.trim().toLowerCase() let osId = data.trim().toLowerCase();
console.log("Detected OS from /etc/os-release:", osId) console.log("Detected OS from /etc/os-release:", osId);
// Set OS-specific Nerd Font icons and names // Set OS-specific Nerd Font icons and names
switch (osId) { switch (osId) {
case "arch": case "arch":
root.osLogo = "\uf303" // Arch Linux Nerd Font icon root.osLogo = "\uf303"; // Arch Linux Nerd Font icon
root.osName = "Arch Linux" root.osName = "Arch Linux";
break break;
case "ubuntu": case "ubuntu":
root.osLogo = "\uf31b" // Ubuntu Nerd Font icon root.osLogo = "\uf31b"; // Ubuntu Nerd Font icon
root.osName = "Ubuntu" root.osName = "Ubuntu";
break break;
case "fedora": case "fedora":
root.osLogo = "\uf30a" // Fedora Nerd Font icon root.osLogo = "\uf30a"; // Fedora Nerd Font icon
root.osName = "Fedora" root.osName = "Fedora";
break break;
case "debian": case "debian":
root.osLogo = "\uf306" // Debian Nerd Font icon root.osLogo = "\uf306"; // Debian Nerd Font icon
root.osName = "Debian" root.osName = "Debian";
break break;
case "opensuse": case "opensuse":
case "opensuse-leap": case "opensuse-leap":
case "opensuse-tumbleweed": case "opensuse-tumbleweed":
root.osLogo = "\uef6d" // openSUSE Nerd Font icon root.osLogo = "\uef6d"; // openSUSE Nerd Font icon
root.osName = "openSUSE" root.osName = "openSUSE";
break break;
case "manjaro": case "manjaro":
root.osLogo = "\uf312" // Manjaro Nerd Font icon root.osLogo = "\uf312"; // Manjaro Nerd Font icon
root.osName = "Manjaro" root.osName = "Manjaro";
break break;
case "nixos": case "nixos":
root.osLogo = "\uf313" // NixOS Nerd Font icon root.osLogo = "\uf313"; // NixOS Nerd Font icon
root.osName = "NixOS" root.osName = "NixOS";
break break;
case "rocky": case "rocky":
root.osLogo = "\uf32b" // Rocky Linux Nerd Font icon root.osLogo = "\uf32b"; // Rocky Linux Nerd Font icon
root.osName = "Rocky Linux" root.osName = "Rocky Linux";
break break;
case "almalinux": case "almalinux":
root.osLogo = "\uf31d" // AlmaLinux Nerd Font icon root.osLogo = "\uf31d"; // AlmaLinux Nerd Font icon
root.osName = "AlmaLinux" root.osName = "AlmaLinux";
break break;
case "centos": case "centos":
root.osLogo = "\uf304" // CentOS Nerd Font icon root.osLogo = "\uf304"; // CentOS Nerd Font icon
root.osName = "CentOS" root.osName = "CentOS";
break break;
case "rhel": case "rhel":
case "redhat": case "redhat":
root.osLogo = "\uf316" // Red Hat Nerd Font icon root.osLogo = "\uf316"; // Red Hat Nerd Font icon
root.osName = "Red Hat" root.osName = "Red Hat";
break break;
case "gentoo": case "gentoo":
root.osLogo = "\uf30d" // Gentoo Nerd Font icon root.osLogo = "\uf30d"; // Gentoo Nerd Font icon
root.osName = "Gentoo" root.osName = "Gentoo";
break break;
case "mint": case "mint":
case "linuxmint": case "linuxmint":
root.osLogo = "\uf30e" // Linux Mint Nerd Font icon root.osLogo = "\uf30e"; // Linux Mint Nerd Font icon
root.osName = "Linux Mint" root.osName = "Linux Mint";
break break;
case "elementary": case "elementary":
root.osLogo = "\uf309" // Elementary OS Nerd Font icon root.osLogo = "\uf309"; // Elementary OS Nerd Font icon
root.osName = "Elementary OS" root.osName = "Elementary OS";
break break;
case "pop": case "pop":
root.osLogo = "\uf32a" // Pop!_OS Nerd Font icon root.osLogo = "\uf32a"; // Pop!_OS Nerd Font icon
root.osName = "Pop!_OS" root.osName = "Pop!_OS";
break break;
default: default:
root.osLogo = "\uf17c" // Generic Linux Nerd Font icon root.osLogo = "\uf17c"; // Generic Linux Nerd Font icon
root.osName = "Linux" 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 import Quickshell.Io
Singleton { Singleton {
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
id: root id: root
property var processes: [] property var processes: []
property bool isUpdating: false property bool isUpdating: false
property int processUpdateInterval: 3000 property int processUpdateInterval: 3000
property bool monitoringEnabled: false property bool monitoringEnabled: false
property int totalMemoryKB: 0 property int totalMemoryKB: 0
property int usedMemoryKB: 0 property int usedMemoryKB: 0
property int totalSwapKB: 0 property int totalSwapKB: 0
property int usedSwapKB: 0 property int usedSwapKB: 0
property int cpuCount: 1 property int cpuCount: 1
property real totalCpuUsage: 0.0 property real totalCpuUsage: 0
property bool systemInfoAvailable: false property bool systemInfoAvailable: false
property var cpuHistory: [] property var cpuHistory: []
property var memoryHistory: [] property var memoryHistory: []
property var networkHistory: ({rx: [], tx: []}) property var networkHistory: ({
property var diskHistory: ({read: [], write: []}) "rx": [],
"tx": []
})
property var diskHistory: ({
"read": [],
"write": []
})
property int historySize: 60 property int historySize: 60
property var perCoreCpuUsage: [] property var perCoreCpuUsage: []
property real networkRxRate: 0 property real networkRxRate: 0
property real networkTxRate: 0 property real networkTxRate: 0
property var lastNetworkStats: null property var lastNetworkStats: null
property real diskReadRate: 0 property real diskReadRate: 0
property real diskWriteRate: 0 property real diskWriteRate: 0
property var lastDiskStats: null property var lastDiskStats: null
property string sortBy: "cpu" property string sortBy: "cpu"
property bool sortDescending: true property bool sortDescending: true
property int maxProcesses: 20 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() { function updateSystemInfo() {
if (!systemInfoProcess.running && root.monitoringEnabled) { if (!systemInfoProcess.running && root.monitoringEnabled)
systemInfoProcess.running = true systemInfoProcess.running = true;
}
} }
function enableMonitoring(enabled) { function enableMonitoring(enabled) {
console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled") console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled");
root.monitoringEnabled = enabled root.monitoringEnabled = enabled;
if (enabled) { if (enabled) {
root.cpuHistory = [] root.cpuHistory = [];
root.memoryHistory = [] root.memoryHistory = [];
root.networkHistory = ({rx: [], tx: []}) root.networkHistory = ({
root.diskHistory = ({read: [], write: []}) "rx": [],
updateSystemInfo() "tx": []
updateProcessList() });
updateNetworkStats() root.diskHistory = ({
updateDiskStats() "read": [],
"write": []
});
updateSystemInfo();
updateProcessList();
updateNetworkStats();
updateDiskStats();
} }
} }
function updateNetworkStats() { function updateNetworkStats() {
if (!networkStatsProcess.running && root.monitoringEnabled) { if (!networkStatsProcess.running && root.monitoringEnabled)
networkStatsProcess.running = true networkStatsProcess.running = true;
}
} }
function updateDiskStats() { function updateDiskStats() {
if (!diskStatsProcess.running && root.monitoringEnabled) { if (!diskStatsProcess.running && root.monitoringEnabled)
diskStatsProcess.running = true diskStatsProcess.running = true;
}
} }
function updateProcessList() { function updateProcessList() {
if (!root.isUpdating && root.monitoringEnabled) { if (!root.isUpdating && root.monitoringEnabled) {
root.isUpdating = true root.isUpdating = true;
let sortOption = "";
let sortOption = ""
switch (root.sortBy) { switch (root.sortBy) {
case "cpu": case "cpu":
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu" sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu";
break break;
case "memory": case "memory":
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem" sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem";
break break;
case "name": case "name":
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm" sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm";
break break;
case "pid": case "pid":
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid" sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid";
break break;
default: default:
sortOption = "--sort=-pcpu" sortOption = "--sort=-pcpu";
} }
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)];
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)] processListProcess.running = true;
processListProcess.running = true
} }
} }
function setSortBy(newSortBy) { function setSortBy(newSortBy) {
if (newSortBy !== root.sortBy) { if (newSortBy !== root.sortBy) {
root.sortBy = newSortBy root.sortBy = newSortBy;
updateProcessList() updateProcessList();
} }
} }
function toggleSortOrder() { function toggleSortOrder() {
root.sortDescending = !root.sortDescending root.sortDescending = !root.sortDescending;
updateProcessList() updateProcessList();
} }
function killProcess(pid) { function killProcess(pid) {
if (pid > 0) { if (pid > 0) {
const killCmd = ["bash", "-c", "kill " + pid] const killCmd = ["bash", "-c", "kill " + pid];
const killProcess = Qt.createQmlObject(` const killProcess = Qt.createQmlObject(`
import QtQuick import QtQuick
import Quickshell.Io import Quickshell.Io
@@ -278,196 +137,346 @@ Singleton {
destroy() destroy()
} }
} }
`, root) `, root);
} }
} }
function getProcessIcon(command) { function getProcessIcon(command) {
const cmd = command.toLowerCase() const cmd = command.toLowerCase();
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser")) return "web" if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim")) return "code" return "web";
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("code") || cmd.includes("editor") || cmd.includes("vim"))
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv")) return "play_circle" return "code";
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread")) return "settings"
return "memory" 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) { function formatCpuUsage(cpu) {
return cpu.toFixed(1) + "%" return cpu.toFixed(1) + "%";
} }
function formatMemoryUsage(memoryKB) { function formatMemoryUsage(memoryKB) {
if (memoryKB < 1024) { if (memoryKB < 1024)
return memoryKB.toFixed(0) + " KB" return memoryKB.toFixed(0) + " KB";
} else if (memoryKB < 1024 * 1024) { else if (memoryKB < 1024 * 1024)
return (memoryKB / 1024).toFixed(1) + " MB" return (memoryKB / 1024).toFixed(1) + " MB";
} else { else
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB" return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
}
} }
function formatSystemMemory(memoryKB) { function formatSystemMemory(memoryKB) {
if (memoryKB < 1024 * 1024) { if (memoryKB < 1024 * 1024)
return (memoryKB / 1024).toFixed(0) + " MB" return (memoryKB / 1024).toFixed(0) + " MB";
} else { else
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB" return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
}
} }
function parseSystemInfo(text) { function parseSystemInfo(text) {
const lines = text.split('\n') const lines = text.split('\n');
let section = 'memory' let section = 'memory';
const coreUsages = [] const coreUsages = [];
let memFree = 0 let memFree = 0;
let memBuffers = 0 let memBuffers = 0;
let memCached = 0 let memCached = 0;
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim() const line = lines[i].trim();
if (line === '---CPU---') { if (line === '---CPU---') {
section = 'cpucount' section = 'cpucount';
continue continue;
} else if (line === '---CPUSTAT---') { } else if (line === '---CPUSTAT---') {
section = 'cpustat' section = 'cpustat';
continue continue;
} }
if (section === 'memory') { if (section === 'memory') {
if (line.startsWith('MemTotal:')) { if (line.startsWith('MemTotal:')) {
root.totalMemoryKB = parseInt(line.split(/\s+/)[1]) root.totalMemoryKB = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('MemFree:')) { } else if (line.startsWith('MemFree:')) {
memFree = parseInt(line.split(/\s+/)[1]) memFree = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('Buffers:')) { } else if (line.startsWith('Buffers:')) {
memBuffers = parseInt(line.split(/\s+/)[1]) memBuffers = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('Cached:')) { } else if (line.startsWith('Cached:')) {
memCached = parseInt(line.split(/\s+/)[1]) memCached = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('SwapTotal:')) { } else if (line.startsWith('SwapTotal:')) {
root.totalSwapKB = parseInt(line.split(/\s+/)[1]) root.totalSwapKB = parseInt(line.split(/\s+/)[1]);
} else if (line.startsWith('SwapFree:')) { } else if (line.startsWith('SwapFree:')) {
const freeSwapKB = parseInt(line.split(/\s+/)[1]) const freeSwapKB = parseInt(line.split(/\s+/)[1]);
root.usedSwapKB = root.totalSwapKB - freeSwapKB root.usedSwapKB = root.totalSwapKB - freeSwapKB;
} }
} else if (section === 'cpucount') { } else if (section === 'cpucount') {
const count = parseInt(line) const count = parseInt(line);
if (!isNaN(count)) { if (!isNaN(count))
root.cpuCount = count root.cpuCount = count;
}
} else if (section === 'cpustat') { } else if (section === 'cpustat') {
if (line.startsWith('cpu ')) { if (line.startsWith('cpu ')) {
const parts = line.split(/\s+/) const parts = line.split(/\s+/);
if (parts.length >= 8) { if (parts.length >= 8) {
const user = parseInt(parts[1]) const user = parseInt(parts[1]);
const nice = parseInt(parts[2]) const nice = parseInt(parts[2]);
const system = parseInt(parts[3]) const system = parseInt(parts[3]);
const idle = parseInt(parts[4]) const idle = parseInt(parts[4]);
const iowait = parseInt(parts[5]) const iowait = parseInt(parts[5]);
const irq = parseInt(parts[6]) const irq = parseInt(parts[6]);
const softirq = parseInt(parts[7]) const softirq = parseInt(parts[7]);
const total = user + nice + system + idle + iowait + irq + softirq;
const total = user + nice + system + idle + iowait + irq + softirq const used = total - idle - iowait;
const used = total - idle - iowait root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0;
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0
} }
} else if (line.match(/^cpu\d+/)) { } else if (line.match(/^cpu\d+/)) {
const parts = line.split(/\s+/) const parts = line.split(/\s+/);
if (parts.length >= 8) { if (parts.length >= 8) {
const user = parseInt(parts[1]) const user = parseInt(parts[1]);
const nice = parseInt(parts[2]) const nice = parseInt(parts[2]);
const system = parseInt(parts[3]) const system = parseInt(parts[3]);
const idle = parseInt(parts[4]) const idle = parseInt(parts[4]);
const iowait = parseInt(parts[5]) const iowait = parseInt(parts[5]);
const irq = parseInt(parts[6]) const irq = parseInt(parts[6]);
const softirq = parseInt(parts[7]) const softirq = parseInt(parts[7]);
const total = user + nice + system + idle + iowait + irq + softirq;
const total = user + nice + system + idle + iowait + irq + softirq const used = total - idle - iowait;
const used = total - idle - iowait const usage = total > 0 ? (used / total) * 100 : 0;
const usage = total > 0 ? (used / total) * 100 : 0 coreUsages.push(usage);
coreUsages.push(usage)
} }
} }
} }
} }
// Calculate used memory as total minus free minus buffers minus cached // 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 // Update per-core usage
root.perCoreCpuUsage = coreUsages root.perCoreCpuUsage = coreUsages;
// Update history // Update history
addToHistory(root.cpuHistory, root.totalCpuUsage) addToHistory(root.cpuHistory, root.totalCpuUsage);
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0 const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0;
addToHistory(root.memoryHistory, memoryPercent) addToHistory(root.memoryHistory, memoryPercent);
root.systemInfoAvailable = true;
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
root.systemInfoAvailable = true
} }
function parseNetworkStats(text) { function parseNetworkStats(text) {
const lines = text.split('\n') const lines = text.split('\n');
let totalRx = 0 let totalRx = 0;
let totalTx = 0 let totalTx = 0;
for (const line of lines) { for (const line of lines) {
const parts = line.trim().split(/\s+/) const parts = line.trim().split(/\s+/);
if (parts.length >= 3) { if (parts.length >= 3) {
const rx = parseInt(parts[1]) const rx = parseInt(parts[1]);
const tx = parseInt(parts[2]) const tx = parseInt(parts[2]);
if (!isNaN(rx) && !isNaN(tx)) { if (!isNaN(rx) && !isNaN(tx)) {
totalRx += rx totalRx += rx;
totalTx += tx totalTx += tx;
} }
} }
} }
if (root.lastNetworkStats) { if (root.lastNetworkStats) {
const timeDiff = root.processUpdateInterval / 1000 const timeDiff = root.processUpdateInterval / 1000;
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff) root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff);
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff) root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff);
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024);
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024) addToHistory(root.networkHistory.tx, root.networkTxRate / 1024);
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024)
} }
root.lastNetworkStats = {
root.lastNetworkStats = { rx: totalRx, tx: totalTx } "rx": totalRx,
"tx": totalTx
};
} }
function parseDiskStats(text) { function parseDiskStats(text) {
const lines = text.split('\n') const lines = text.split('\n');
let totalRead = 0 let totalRead = 0;
let totalWrite = 0 let totalWrite = 0;
for (const line of lines) { for (const line of lines) {
const parts = line.trim().split(/\s+/) const parts = line.trim().split(/\s+/);
if (parts.length >= 3) { if (parts.length >= 3) {
const readSectors = parseInt(parts[1]) const readSectors = parseInt(parts[1]);
const writeSectors = parseInt(parts[2]) const writeSectors = parseInt(parts[2]);
if (!isNaN(readSectors) && !isNaN(writeSectors)) { if (!isNaN(readSectors) && !isNaN(writeSectors)) {
totalRead += readSectors * 512 totalRead += readSectors * 512;
totalWrite += writeSectors * 512 totalWrite += writeSectors * 512;
} }
} }
} }
if (root.lastDiskStats) { if (root.lastDiskStats) {
const timeDiff = root.processUpdateInterval / 1000 const timeDiff = root.processUpdateInterval / 1000;
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff) root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff);
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff) root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff);
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024));
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024)) addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024));
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024))
} }
root.lastDiskStats = {
root.lastDiskStats = { read: totalRead, write: totalWrite } "read": totalRead,
"write": totalWrite
};
} }
function addToHistory(array, value) { function addToHistory(array, value) {
array.push(value) array.push(value);
if (array.length > root.historySize) { if (array.length > root.historySize)
array.shift() 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

@@ -8,23 +8,19 @@ import Quickshell.Io
Singleton { Singleton {
id: root id: root
property real cpuUsage: 0.0 property real cpuUsage: 0
property int cpuCores: 1 property int cpuCores: 1
property string cpuModel: "" property string cpuModel: ""
property real cpuFrequency: 0.0 property real cpuFrequency: 0
property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0] property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0]
property real memoryUsage: 0
property real memoryUsage: 0.0 property real totalMemory: 0
property real totalMemory: 0.0 property real usedMemory: 0
property real usedMemory: 0.0 property real freeMemory: 0
property real freeMemory: 0.0 property real availableMemory: 0
property real availableMemory: 0.0 property real bufferMemory: 0
property real bufferMemory: 0.0 property real cacheMemory: 0
property real cacheMemory: 0.0 property real cpuTemperature: 0
property real cpuTemperature: 0.0
property string kernelVersion: "" property string kernelVersion: ""
property string distribution: "" property string distribution: ""
property string hostname: "" property string hostname: ""
@@ -39,537 +35,567 @@ Singleton {
property string biosVersion: "" property string biosVersion: ""
property var diskMounts: [] property var diskMounts: []
property string diskUsage: "" property string diskUsage: ""
property int cpuUpdateInterval: 3000 property int cpuUpdateInterval: 3000
property int memoryUpdateInterval: 5000 property int memoryUpdateInterval: 5000
property int temperatureUpdateInterval: 10000 property int temperatureUpdateInterval: 10000
property int systemInfoUpdateInterval: 30000 property int systemInfoUpdateInterval: 30000
property bool enabledForTopBar: true property bool enabledForTopBar: true
property bool enabledForDetailedView: false property bool enabledForDetailedView: false
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: { Component.onCompleted: {
console.log("SystemMonitorService: Starting initialization...") console.log("SystemMonitorService: Starting initialization...");
getCpuInfo() getCpuInfo();
updateSystemStats() updateSystemStats();
updateSystemInfo() updateSystemInfo();
console.log("SystemMonitorService: Initialization complete") console.log("SystemMonitorService: Initialization complete");
} }
Process { Process {
id: cpuInfoProcess id: cpuInfoProcess
command: ["bash", "-c", "lscpu | grep -E 'Model name|CPU\\(s\\):' | head -2"] command: ["bash", "-c", "lscpu | grep -E 'Model name|CPU\\(s\\):' | head -2"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU info check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
const lines = text.trim().split('\n') const lines = text.trim().split('\n');
for (const line of lines) { for (const line of lines) {
if (line.includes("Model name")) { if (line.includes("Model name"))
root.cpuModel = line.split(":")[1].trim() root.cpuModel = line.split(":")[1].trim();
} else if (line.includes("CPU(s):")) { else if (line.includes("CPU(s):"))
root.cpuCores = parseInt(line.split(":")[1].trim()) root.cpuCores = parseInt(line.split(":")[1].trim());
}
} }
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU info check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: cpuUsageProcess id: cpuUsageProcess
command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"] command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU usage check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { 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) { if (root.prevCpuStats[0] > 0) {
let diffs = [] let diffs = [];
for (let i = 0; i < 8; i++) { for (let i = 0; i < 8; i++) {
diffs[i] = stats[i] - root.prevCpuStats[i] 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));
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))
} }
} root.prevCpuStats = stats;
root.prevCpuStats = stats
} }
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU usage check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: memoryUsageProcess id: memoryUsageProcess
command: ["bash", "-c", "free -m | awk 'NR==2{printf \"%.1f %.1f %.1f %.1f\", $3*100/$2, $2, $3, $7}'"] command: ["bash", "-c", "free -m | awk 'NR==2{printf \"%.1f %.1f %.1f %.1f\", $3*100/$2, $2, $3, $7}'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Memory usage check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim()) {
const parts = text.trim().split(" ") const parts = text.trim().split(" ");
root.memoryUsage = parseFloat(parts[0]) root.memoryUsage = parseFloat(parts[0]);
root.totalMemory = parseFloat(parts[1]) root.totalMemory = parseFloat(parts[1]);
root.usedMemory = parseFloat(parts[2]) root.usedMemory = parseFloat(parts[2]);
root.availableMemory = parseFloat(parts[3]) root.availableMemory = parseFloat(parts[3]);
root.freeMemory = root.totalMemory - root.usedMemory root.freeMemory = root.totalMemory - root.usedMemory;
} }
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Memory usage check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: cpuFrequencyProcess id: cpuFrequencyProcess
command: ["bash", "-c", "cat /proc/cpuinfo | grep 'cpu MHz' | head -1 | awk '{print $4}'"] command: ["bash", "-c", "cat /proc/cpuinfo | grep 'cpu MHz' | head -1 | awk '{print $4}'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU frequency check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.cpuFrequency = parseFloat(text.trim()) root.cpuFrequency = parseFloat(text.trim());
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU frequency check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: temperatureProcess 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"] 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 running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("CPU temperature check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.cpuTemperature = parseFloat(text.trim()) root.cpuTemperature = parseFloat(text.trim());
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("CPU temperature check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: kernelInfoProcess id: kernelInfoProcess
command: ["bash", "-c", "uname -r"] command: ["bash", "-c", "uname -r"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Kernel info check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.kernelVersion = text.trim() root.kernelVersion = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Kernel info check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: distributionProcess id: distributionProcess
command: ["bash", "-c", "grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '\"' || echo 'Unknown'"] command: ["bash", "-c", "grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '\"' || echo 'Unknown'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Distribution check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.distribution = text.trim() root.distribution = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Distribution check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: hostnameProcess id: hostnameProcess
command: ["bash", "-c", "hostname"] command: ["bash", "-c", "hostname"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Hostname check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.hostname = text.trim() root.hostname = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Hostname check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: uptimeProcess id: uptimeProcess
command: ["bash", "-c", "uptime -p | sed 's/up //'"] command: ["bash", "-c", "uptime -p | sed 's/up //'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Uptime check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.uptime = text.trim() root.uptime = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Uptime check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: schedulerProcess id: schedulerProcess
command: ["bash", "-c", "cat /sys/block/sda/queue/scheduler 2>/dev/null | grep -o '\\[.*\\]' | tr -d '[]' || echo 'Unknown'"] command: ["bash", "-c", "cat /sys/block/sda/queue/scheduler 2>/dev/null | grep -o '\\[.*\\]' | tr -d '[]' || echo 'Unknown'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Scheduler check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.scheduler = text.trim() root.scheduler = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Scheduler check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: architectureProcess id: architectureProcess
command: ["bash", "-c", "uname -m"] command: ["bash", "-c", "uname -m"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Architecture check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.architecture = text.trim() root.architecture = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Architecture check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: loadAverageProcess id: loadAverageProcess
command: ["bash", "-c", "cat /proc/loadavg | cut -d' ' -f1,2,3"] command: ["bash", "-c", "cat /proc/loadavg | cut -d' ' -f1,2,3"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Load average check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.loadAverage = text.trim() root.loadAverage = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Load average check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: processCountProcess id: processCountProcess
command: ["bash", "-c", "ps aux | wc -l"] command: ["bash", "-c", "ps aux | wc -l"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Process count check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.processCount = parseInt(text.trim()) - 1 root.processCount = parseInt(text.trim()) - 1;
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Process count check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: threadCountProcess id: threadCountProcess
command: ["bash", "-c", "cat /proc/stat | grep processes | awk '{print $2}'"] command: ["bash", "-c", "cat /proc/stat | grep processes | awk '{print $2}'"]
running: false running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Thread count check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.threadCount = parseInt(text.trim()) root.threadCount = parseInt(text.trim());
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Thread count check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: bootTimeProcess 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'"] 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 running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Boot time check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.bootTime = text.trim() root.bootTime = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Boot time check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: motherboardProcess 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"] 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 running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Motherboard check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.motherboard = text.trim() root.motherboard = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Motherboard check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: biosProcess 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"] 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 running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("BIOS check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim())
root.biosVersion = text.trim() root.biosVersion = text.trim();
}
} }
} }
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("BIOS check failed with exit code:", exitCode)
}
}
} }
Process { Process {
id: diskMountsProcess 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"] 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 running: false
onExited: (exitCode) => {
if (exitCode !== 0)
console.warn("Disk mounts check failed with exit code:", exitCode);
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
if (text.trim()) { if (text.trim()) {
let mounts = [] let mounts = [];
const lines = text.trim().split('\n') const lines = text.trim().split('\n');
for (const line of lines) { for (const line of lines) {
const parts = line.split(/\s+/) const parts = line.split(/\s+/);
if (parts.length >= 7) { if (parts.length >= 7)
mounts.push({ mounts.push({
device: parts[0], "device": parts[0],
mount: parts[1], "mount": parts[1],
fstype: parts[2], "fstype": parts[2],
size: parts[3], "size": parts[3],
used: parts[4], "used": parts[4],
avail: parts[5], "avail": parts[5],
percent: parts[6] "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 { Timer {
id: cpuTimer id: cpuTimer
interval: root.cpuUpdateInterval interval: root.cpuUpdateInterval
running: root.enabledForTopBar || root.enabledForDetailedView running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView) { if (root.enabledForTopBar || root.enabledForDetailedView) {
cpuUsageProcess.running = true cpuUsageProcess.running = true;
cpuFrequencyProcess.running = true cpuFrequencyProcess.running = true;
} }
} }
} }
Timer { Timer {
id: memoryTimer id: memoryTimer
interval: root.memoryUpdateInterval interval: root.memoryUpdateInterval
running: root.enabledForTopBar || root.enabledForDetailedView running: root.enabledForTopBar || root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForTopBar || root.enabledForDetailedView) { if (root.enabledForTopBar || root.enabledForDetailedView)
memoryUsageProcess.running = true memoryUsageProcess.running = true;
}
} }
} }
Timer { Timer {
id: temperatureTimer id: temperatureTimer
interval: root.temperatureUpdateInterval interval: root.temperatureUpdateInterval
running: root.enabledForDetailedView running: root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForDetailedView) { if (root.enabledForDetailedView)
temperatureProcess.running = true temperatureProcess.running = true;
}
} }
} }
Timer { Timer {
id: systemInfoTimer id: systemInfoTimer
interval: root.systemInfoUpdateInterval interval: root.systemInfoUpdateInterval
running: root.enabledForDetailedView running: root.enabledForDetailedView
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.enabledForDetailedView) { if (root.enabledForDetailedView)
updateSystemInfo() 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 levelInfo: 0
readonly property int levelWarn: 1 readonly property int levelWarn: 1
readonly property int levelError: 2 readonly property int levelError: 2
property string currentMessage: "" property string currentMessage: ""
property int currentLevel: levelInfo property int currentLevel: levelInfo
property bool toastVisible: false property bool toastVisible: false
property var toastQueue: [] property var toastQueue: []
property string wallpaperErrorStatus: "" 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 { Timer {
id: toastTimer id: toastTimer
interval: 5000 interval: 5000
running: false running: false
repeat: false repeat: false
@@ -28,56 +75,11 @@ Singleton {
Timer { Timer {
id: queueTimer id: queueTimer
interval: 500 interval: 500
running: false running: false
repeat: false repeat: false
onTriggered: processQueue() 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

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

@@ -15,146 +15,26 @@ Singleton {
property bool isScanning: false property bool isScanning: false
property string connectionStatus: "" // "cosnnecting", "connected", "failed", "" property string connectionStatus: "" // "cosnnecting", "connected", "failed", ""
property string connectingSSID: "" property string connectingSSID: ""
// Auto-refresh timer for when control center is open
Process { property bool autoRefreshEnabled: false
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")
}
}
}
}
function scanWifi() { function scanWifi() {
if (root.isScanning) return if (root.isScanning)
return ;
root.isScanning = true
console.log("Starting WiFi scan...")
wifiScanner.running = true
savedWifiScanner.running = true
currentWifiInfo.running = true
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 // 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) { function connectToWifi(ssid) {
console.log("Connecting to WiFi:", ssid) console.log("Connecting to WiFi:", ssid);
root.connectionStatus = "connecting";
root.connectionStatus = "connecting" root.connectingSSID = ssid;
root.connectingSSID = ssid
let connectProcess = Qt.createQmlObject(` let connectProcess = Qt.createQmlObject(`
import Quickshell.Io import Quickshell.Io
Process { Process {
@@ -185,15 +65,13 @@ Singleton {
} }
} }
} }
`, root) `, root);
} }
function connectToWifiWithPassword(ssid, password) { function connectToWifiWithPassword(ssid, password) {
console.log("Connecting to WiFi with password:", ssid) console.log("Connecting to WiFi with password:", ssid);
root.connectionStatus = "connecting";
root.connectionStatus = "connecting" root.connectingSSID = ssid;
root.connectingSSID = ssid
let connectProcess = Qt.createQmlObject(` let connectProcess = Qt.createQmlObject(`
import Quickshell.Io import Quickshell.Io
Process { Process {
@@ -224,11 +102,11 @@ Singleton {
} }
} }
} }
`, root) `, root);
} }
function forgetWifiNetwork(ssid) { function forgetWifiNetwork(ssid) {
console.log("Forgetting WiFi network:", ssid) console.log("Forgetting WiFi network:", ssid);
let forgetProcess = Qt.createQmlObject(` let forgetProcess = Qt.createQmlObject(`
import Quickshell.Io import Quickshell.Io
Process { Process {
@@ -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: fallbackTimer
interval: 5000
onTriggered: {
root.isScanning = false;
console.log("WiFi scan timeout - no networks found");
}
} }
Timer { Timer {
id: statusResetTimer id: statusResetTimer
interval: 3000 interval: 3000
onTriggered: { onTriggered: {
root.connectionStatus = "" root.connectionStatus = "";
root.connectingSSID = "" root.connectingSSID = "";
} }
} }
// Auto-refresh timer for when control center is open
property bool autoRefreshEnabled: false
Timer { Timer {
id: autoRefreshTimer id: autoRefreshTimer
interval: 20000 interval: 20000
running: root.autoRefreshEnabled running: root.autoRefreshEnabled
repeat: true repeat: true
onTriggered: { onTriggered: {
if (root.autoRefreshEnabled) { if (root.autoRefreshEnabled)
root.scanWifi() 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"