mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
qmlformat-all script
This commit is contained in:
1059
Common/Theme.qml
1059
Common/Theme.qml
File diff suppressed because it is too large
Load Diff
@@ -7,61 +7,56 @@ import Quickshell.Services.UPower
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
readonly property UPowerDevice device: UPower.displayDevice
|
||||
readonly property bool batteryAvailable: device && device.ready && device.isLaptopBattery
|
||||
readonly property int batteryLevel: batteryAvailable ? device.percentage * 100.0 : 0
|
||||
readonly property int batteryLevel: batteryAvailable ? device.percentage * 100 : 0
|
||||
readonly property bool isCharging: batteryAvailable && device.state === UPowerDeviceState.Charging
|
||||
readonly property bool isLowBattery: batteryAvailable && batteryLevel <= 20
|
||||
readonly property string batteryHealth: batteryAvailable && device.healthSupported ? Math.round(device.healthPercentage * 100.0) + "%" : "N/A"
|
||||
readonly property string batteryHealth: batteryAvailable && device.healthSupported ? Math.round(device.healthPercentage * 100) + "%" : "N/A"
|
||||
readonly property real batteryCapacity: batteryAvailable && device.energyCapacity > 0 ? device.energyCapacity : 0
|
||||
|
||||
readonly property string batteryStatus: {
|
||||
if (!batteryAvailable) return "No Battery"
|
||||
return UPowerDeviceState.toString(device.state)
|
||||
if (!batteryAvailable)
|
||||
return "No Battery";
|
||||
|
||||
return UPowerDeviceState.toString(device.state);
|
||||
}
|
||||
|
||||
readonly property int timeRemaining: {
|
||||
if (!batteryAvailable) return 0
|
||||
return isCharging ? (device.timeToFull || 0) : (device.timeToEmpty || 0)
|
||||
if (!batteryAvailable)
|
||||
return 0;
|
||||
|
||||
return isCharging ? (device.timeToFull || 0) : (device.timeToEmpty || 0);
|
||||
}
|
||||
|
||||
readonly property bool suggestPowerSaver: batteryAvailable && isLowBattery && UPower.onBattery && (typeof PowerProfiles !== "undefined" && PowerProfiles.profile !== PowerProfile.PowerSaver)
|
||||
|
||||
readonly property var bluetoothDevices: {
|
||||
var btDevices = []
|
||||
var btDevices = [];
|
||||
for (var i = 0; i < UPower.devices.count; i++) {
|
||||
var dev = UPower.devices.get(i)
|
||||
if (dev && dev.ready && (dev.type === UPowerDeviceType.BluetoothGeneric ||
|
||||
dev.type === UPowerDeviceType.Headphones ||
|
||||
dev.type === UPowerDeviceType.Headset ||
|
||||
dev.type === UPowerDeviceType.Keyboard ||
|
||||
dev.type === UPowerDeviceType.Mouse ||
|
||||
dev.type === UPowerDeviceType.Speakers)) {
|
||||
var dev = UPower.devices.get(i);
|
||||
if (dev && dev.ready && (dev.type === UPowerDeviceType.BluetoothGeneric || dev.type === UPowerDeviceType.Headphones || dev.type === UPowerDeviceType.Headset || dev.type === UPowerDeviceType.Keyboard || dev.type === UPowerDeviceType.Mouse || dev.type === UPowerDeviceType.Speakers))
|
||||
btDevices.push({
|
||||
name: dev.model || UPowerDeviceType.toString(dev.type),
|
||||
percentage: Math.round(dev.percentage),
|
||||
type: dev.type
|
||||
})
|
||||
}
|
||||
"name": dev.model || UPowerDeviceType.toString(dev.type),
|
||||
"percentage": Math.round(dev.percentage),
|
||||
"type": dev.type
|
||||
});
|
||||
|
||||
}
|
||||
return btDevices
|
||||
return btDevices;
|
||||
}
|
||||
|
||||
|
||||
function formatTimeRemaining() {
|
||||
if (!batteryAvailable || timeRemaining <= 0) return "Unknown"
|
||||
|
||||
const hours = Math.floor(timeRemaining / 3600)
|
||||
const minutes = Math.floor((timeRemaining % 3600) / 60)
|
||||
|
||||
if (hours > 0) {
|
||||
return hours + "h " + minutes + "m"
|
||||
} else {
|
||||
return minutes + "m"
|
||||
}
|
||||
if (!batteryAvailable || timeRemaining <= 0)
|
||||
return "Unknown";
|
||||
|
||||
const hours = Math.floor(timeRemaining / 3600);
|
||||
const minutes = Math.floor((timeRemaining % 3600) / 60);
|
||||
if (hours > 0)
|
||||
return hours + "h " + minutes + "m";
|
||||
else
|
||||
return minutes + "m";
|
||||
}
|
||||
|
||||
|
||||
function getBluetoothDevices() {
|
||||
return bluetoothDevices
|
||||
return bluetoothDevices;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,162 +7,252 @@ import Quickshell.Bluetooth
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
|
||||
readonly property bool available: adapter !== null
|
||||
readonly property bool enabled: adapter?.enabled ?? false
|
||||
readonly property bool discovering: adapter?.discovering ?? false
|
||||
|
||||
readonly property bool enabled: (adapter && adapter.enabled) ?? false
|
||||
readonly property bool discovering: (adapter && adapter.discovering) ?? false
|
||||
readonly property var devices: adapter ? adapter.devices : null
|
||||
|
||||
readonly property var pairedDevices: {
|
||||
if (!adapter || !adapter.devices)
|
||||
return [];
|
||||
|
||||
return adapter.devices.values.filter((dev) => {
|
||||
return dev && dev.paired && isValidDevice(dev);
|
||||
});
|
||||
}
|
||||
readonly property var availableDevices: {
|
||||
if (!adapter || !adapter.discovering || !Bluetooth.devices)
|
||||
return [];
|
||||
|
||||
var filtered = Bluetooth.devices.values.filter((dev) => {
|
||||
return dev && !dev.paired && !dev.pairing && !dev.blocked && isValidDevice(dev) && (dev.rssi === undefined || dev.rssi !== 0);
|
||||
});
|
||||
return sortByRssi(filtered);
|
||||
}
|
||||
readonly property var allDevicesWithBattery: {
|
||||
if (!adapter || !adapter.devices)
|
||||
return [];
|
||||
|
||||
return adapter.devices.values.filter((dev) => {
|
||||
return dev && dev.batteryAvailable && dev.battery > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function sortByRssi(devices) {
|
||||
return devices.sort((a, b) => {
|
||||
var aRssi = (a.rssi !== undefined && a.rssi !== 0) ? a.rssi : -100
|
||||
var bRssi = (b.rssi !== undefined && b.rssi !== 0) ? b.rssi : -100
|
||||
return bRssi - aRssi
|
||||
})
|
||||
var aRssi = (a.rssi !== undefined && a.rssi !== 0) ? a.rssi : -100;
|
||||
var bRssi = (b.rssi !== undefined && b.rssi !== 0) ? b.rssi : -100;
|
||||
return bRssi - aRssi;
|
||||
});
|
||||
}
|
||||
|
||||
readonly property var pairedDevices: {
|
||||
if (!adapter || !adapter.devices) return []
|
||||
return adapter.devices.values.filter(dev => dev && dev.paired && isValidDevice(dev))
|
||||
}
|
||||
|
||||
readonly property var availableDevices: {
|
||||
if (!adapter || !adapter.discovering || !Bluetooth.devices) return []
|
||||
var filtered = Bluetooth.devices.values
|
||||
.filter(dev => dev && !dev.paired && !dev.pairing && !dev.blocked && isValidDevice(dev) && (dev.rssi === undefined || dev.rssi !== 0))
|
||||
return sortByRssi(filtered)
|
||||
}
|
||||
|
||||
readonly property var allDevicesWithBattery: {
|
||||
if (!adapter || !adapter.devices) return []
|
||||
return adapter.devices.values.filter(dev => dev && dev.batteryAvailable && dev.battery > 0)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function isValidDevice(device) {
|
||||
if (!device) return false
|
||||
var displayName = device.name || device.deviceName
|
||||
if (!displayName || displayName.length < 2) return false
|
||||
if (displayName.startsWith('/org/bluez') || displayName.includes('hci0')) return false
|
||||
return displayName.length >= 3
|
||||
if (!device)
|
||||
return false;
|
||||
|
||||
var displayName = device.name || device.deviceName;
|
||||
if (!displayName || displayName.length < 2)
|
||||
return false;
|
||||
|
||||
if (displayName.startsWith('/org/bluez') || displayName.includes('hci0'))
|
||||
return false;
|
||||
|
||||
return displayName.length >= 3;
|
||||
}
|
||||
|
||||
|
||||
function getDeviceIcon(device) {
|
||||
if (!device) return "bluetooth"
|
||||
var name = (device.name || device.deviceName || "").toLowerCase()
|
||||
var icon = (device.icon || "").toLowerCase()
|
||||
|
||||
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") ||
|
||||
name.includes("airpod") || name.includes("headset") || name.includes("arctis")) return "headset"
|
||||
if (icon.includes("mouse") || name.includes("mouse")) return "mouse"
|
||||
if (icon.includes("keyboard") || name.includes("keyboard")) return "keyboard"
|
||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") ||
|
||||
name.includes("android") || name.includes("samsung")) return "smartphone"
|
||||
if (icon.includes("watch") || name.includes("watch")) return "watch"
|
||||
if (icon.includes("speaker") || name.includes("speaker")) return "speaker"
|
||||
if (icon.includes("display") || name.includes("tv")) return "tv"
|
||||
return "bluetooth"
|
||||
if (!device)
|
||||
return "bluetooth";
|
||||
|
||||
var name = (device.name || device.deviceName || "").toLowerCase();
|
||||
var icon = (device.icon || "").toLowerCase();
|
||||
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") || name.includes("airpod") || name.includes("headset") || name.includes("arctis"))
|
||||
return "headset";
|
||||
|
||||
if (icon.includes("mouse") || name.includes("mouse"))
|
||||
return "mouse";
|
||||
|
||||
if (icon.includes("keyboard") || name.includes("keyboard"))
|
||||
return "keyboard";
|
||||
|
||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung"))
|
||||
return "smartphone";
|
||||
|
||||
if (icon.includes("watch") || name.includes("watch"))
|
||||
return "watch";
|
||||
|
||||
if (icon.includes("speaker") || name.includes("speaker"))
|
||||
return "speaker";
|
||||
|
||||
if (icon.includes("display") || name.includes("tv"))
|
||||
return "tv";
|
||||
|
||||
return "bluetooth";
|
||||
}
|
||||
|
||||
|
||||
function getDeviceType(device) {
|
||||
if (!device) return "bluetooth"
|
||||
var name = (device.name || device.deviceName || "").toLowerCase()
|
||||
var icon = (device.icon || "").toLowerCase()
|
||||
|
||||
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") ||
|
||||
name.includes("airpod") || name.includes("headset") || name.includes("arctis")) return "headset"
|
||||
if (icon.includes("mouse") || name.includes("mouse")) return "mouse"
|
||||
if (icon.includes("keyboard") || name.includes("keyboard")) return "keyboard"
|
||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") ||
|
||||
name.includes("android") || name.includes("samsung")) return "phone"
|
||||
if (icon.includes("watch") || name.includes("watch")) return "watch"
|
||||
if (icon.includes("speaker") || name.includes("speaker")) return "speaker"
|
||||
if (icon.includes("display") || name.includes("tv")) return "tv"
|
||||
return "bluetooth"
|
||||
if (!device)
|
||||
return "bluetooth";
|
||||
|
||||
var name = (device.name || device.deviceName || "").toLowerCase();
|
||||
var icon = (device.icon || "").toLowerCase();
|
||||
if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") || name.includes("airpod") || name.includes("headset") || name.includes("arctis"))
|
||||
return "headset";
|
||||
|
||||
if (icon.includes("mouse") || name.includes("mouse"))
|
||||
return "mouse";
|
||||
|
||||
if (icon.includes("keyboard") || name.includes("keyboard"))
|
||||
return "keyboard";
|
||||
|
||||
if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung"))
|
||||
return "phone";
|
||||
|
||||
if (icon.includes("watch") || name.includes("watch"))
|
||||
return "watch";
|
||||
|
||||
if (icon.includes("speaker") || name.includes("speaker"))
|
||||
return "speaker";
|
||||
|
||||
if (icon.includes("display") || name.includes("tv"))
|
||||
return "tv";
|
||||
|
||||
return "bluetooth";
|
||||
}
|
||||
|
||||
|
||||
function canPair(device) {
|
||||
if (!device) return false
|
||||
return !device.paired && !device.pairing && !device.blocked
|
||||
if (!device)
|
||||
return false;
|
||||
|
||||
return !device.paired && !device.pairing && !device.blocked;
|
||||
}
|
||||
|
||||
|
||||
function debugDevice(device) {
|
||||
console.log("Device:", device.name, "paired:", device.paired, "connected:", device.connected, "rssi:", device.rssi)
|
||||
console.log("Device:", device.name, "paired:", device.paired, "connected:", device.connected, "rssi:", device.rssi);
|
||||
}
|
||||
|
||||
|
||||
function getPairingStatus(device) {
|
||||
if (!device) return "unknown"
|
||||
if (device.pairing) return "pairing"
|
||||
if (device.paired) return "paired"
|
||||
if (device.blocked) return "blocked"
|
||||
return "available"
|
||||
if (!device)
|
||||
return "unknown";
|
||||
|
||||
if (device.pairing)
|
||||
return "pairing";
|
||||
|
||||
if (device.paired)
|
||||
return "paired";
|
||||
|
||||
if (device.blocked)
|
||||
return "blocked";
|
||||
|
||||
return "available";
|
||||
}
|
||||
|
||||
|
||||
function getSignalStrength(device) {
|
||||
if (!device || device.rssi === undefined || device.rssi === 0) return "Unknown"
|
||||
var rssi = device.rssi
|
||||
if (rssi >= -50) return "Excellent"
|
||||
if (rssi >= -60) return "Good"
|
||||
if (rssi >= -70) return "Fair"
|
||||
if (rssi >= -80) return "Poor"
|
||||
return "Very Poor"
|
||||
if (!device || device.rssi === undefined || device.rssi === 0)
|
||||
return "Unknown";
|
||||
|
||||
var rssi = device.rssi;
|
||||
if (rssi >= -50)
|
||||
return "Excellent";
|
||||
|
||||
if (rssi >= -60)
|
||||
return "Good";
|
||||
|
||||
if (rssi >= -70)
|
||||
return "Fair";
|
||||
|
||||
if (rssi >= -80)
|
||||
return "Poor";
|
||||
|
||||
return "Very Poor";
|
||||
}
|
||||
|
||||
|
||||
function getSignalIcon(device) {
|
||||
if (!device || device.rssi === undefined || device.rssi === 0) return "signal_cellular_null"
|
||||
var rssi = device.rssi
|
||||
if (rssi >= -50) return "signal_cellular_4_bar"
|
||||
if (rssi >= -60) return "signal_cellular_3_bar"
|
||||
if (rssi >= -70) return "signal_cellular_2_bar"
|
||||
if (rssi >= -80) return "signal_cellular_1_bar"
|
||||
return "signal_cellular_0_bar"
|
||||
if (!device || device.rssi === undefined || device.rssi === 0)
|
||||
return "signal_cellular_null";
|
||||
|
||||
var rssi = device.rssi;
|
||||
if (rssi >= -50)
|
||||
return "signal_cellular_4_bar";
|
||||
|
||||
if (rssi >= -60)
|
||||
return "signal_cellular_3_bar";
|
||||
|
||||
if (rssi >= -70)
|
||||
return "signal_cellular_2_bar";
|
||||
|
||||
if (rssi >= -80)
|
||||
return "signal_cellular_1_bar";
|
||||
|
||||
return "signal_cellular_0_bar";
|
||||
}
|
||||
|
||||
|
||||
function toggleAdapter() {
|
||||
if (adapter) adapter.enabled = !adapter.enabled
|
||||
if (adapter)
|
||||
adapter.enabled = !adapter.enabled;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function startScan() {
|
||||
if (adapter) adapter.discovering = true
|
||||
if (adapter)
|
||||
adapter.discovering = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function stopScan() {
|
||||
if (adapter) adapter.discovering = false
|
||||
if (adapter)
|
||||
adapter.discovering = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function connect(address) {
|
||||
var device = _findDevice(address)
|
||||
if (device) device.connect()
|
||||
var device = _findDevice(address);
|
||||
if (device)
|
||||
device.connect();
|
||||
|
||||
}
|
||||
|
||||
|
||||
function disconnect(address) {
|
||||
var device = _findDevice(address)
|
||||
if (device) device.disconnect()
|
||||
var device = _findDevice(address);
|
||||
if (device)
|
||||
device.disconnect();
|
||||
|
||||
}
|
||||
|
||||
|
||||
function pair(address) {
|
||||
var device = _findDevice(address)
|
||||
if (device && canPair(device)) device.pair()
|
||||
var device = _findDevice(address);
|
||||
if (device && canPair(device))
|
||||
device.pair();
|
||||
|
||||
}
|
||||
|
||||
|
||||
function forget(address) {
|
||||
var device = _findDevice(address)
|
||||
if (device) device.forget()
|
||||
var device = _findDevice(address);
|
||||
if (device)
|
||||
device.forget();
|
||||
|
||||
}
|
||||
|
||||
|
||||
function toggle(address) {
|
||||
var device = _findDevice(address)
|
||||
var device = _findDevice(address);
|
||||
if (device) {
|
||||
if (device.connected) device.disconnect()
|
||||
else device.connect()
|
||||
if (device.connected)
|
||||
device.disconnect();
|
||||
else
|
||||
device.connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _findDevice(address) {
|
||||
if (!adapter) return null
|
||||
return adapter.devices.values.find(d => d && d.address === address) ||
|
||||
(Bluetooth.devices ? Bluetooth.devices.values.find(d => d && d.address === address) : null)
|
||||
if (!adapter)
|
||||
return null;
|
||||
|
||||
return adapter.devices.values.find((d) => {
|
||||
return d && d.address === address;
|
||||
}) || (Bluetooth.devices ? Bluetooth.devices.values.find((d) => {
|
||||
return d && d.address === address;
|
||||
}) : null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,303 +7,269 @@ import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
property bool khalAvailable: false
|
||||
property var eventsByDate: ({})
|
||||
property var eventsByDate: ({
|
||||
})
|
||||
property bool isLoading: false
|
||||
property string lastError: ""
|
||||
|
||||
property date lastStartDate
|
||||
property date lastEndDate
|
||||
|
||||
function checkKhalAvailability() {
|
||||
if (!khalCheckProcess.running)
|
||||
khalCheckProcess.running = true;
|
||||
|
||||
}
|
||||
|
||||
function loadCurrentMonth() {
|
||||
if (!root.khalAvailable)
|
||||
return ;
|
||||
|
||||
let today = new Date();
|
||||
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||
let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
|
||||
// Add padding
|
||||
let startDate = new Date(firstDay);
|
||||
startDate.setDate(startDate.getDate() - firstDay.getDay() - 7);
|
||||
let endDate = new Date(lastDay);
|
||||
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7);
|
||||
loadEvents(startDate, endDate);
|
||||
}
|
||||
|
||||
function loadEvents(startDate, endDate) {
|
||||
if (!root.khalAvailable) {
|
||||
console.warn("CalendarService: khal not available, skipping event loading");
|
||||
return ;
|
||||
}
|
||||
if (eventsProcess.running) {
|
||||
console.log("CalendarService: Event loading already in progress, skipping...");
|
||||
return ;
|
||||
}
|
||||
// Store last requested date range for refresh timer
|
||||
root.lastStartDate = startDate;
|
||||
root.lastEndDate = endDate;
|
||||
root.isLoading = true;
|
||||
// Format dates for khal (MM/dd/yyyy based on printformats)
|
||||
let startDateStr = Qt.formatDate(startDate, "MM/dd/yyyy");
|
||||
let endDateStr = Qt.formatDate(endDate, "MM/dd/yyyy");
|
||||
eventsProcess.requestStartDate = startDate;
|
||||
eventsProcess.requestEndDate = endDate;
|
||||
eventsProcess.command = ["khal", "list", "--json", "title", "--json", "description", "--json", "start-date", "--json", "start-time", "--json", "end-date", "--json", "end-time", "--json", "all-day", "--json", "location", "--json", "url", startDateStr, endDateStr];
|
||||
eventsProcess.running = true;
|
||||
}
|
||||
|
||||
function getEventsForDate(date) {
|
||||
let dateKey = Qt.formatDate(date, "yyyy-MM-dd");
|
||||
return root.eventsByDate[dateKey] || [];
|
||||
}
|
||||
|
||||
function hasEventsForDate(date) {
|
||||
let events = getEventsForDate(date);
|
||||
return events.length > 0;
|
||||
}
|
||||
|
||||
// Initialize on component completion
|
||||
Component.onCompleted: {
|
||||
console.log("CalendarService: Component completed, initializing...");
|
||||
checkKhalAvailability();
|
||||
}
|
||||
|
||||
// Periodic refresh timer
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
|
||||
interval: 60000
|
||||
running: root.khalAvailable
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (lastStartDate && lastEndDate) {
|
||||
loadEvents(lastStartDate, lastEndDate)
|
||||
}
|
||||
if (lastStartDate && lastEndDate)
|
||||
loadEvents(lastStartDate, lastEndDate);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
property date lastStartDate
|
||||
property date lastEndDate
|
||||
|
||||
|
||||
// Process for checking khal configuration
|
||||
Process {
|
||||
id: khalCheckProcess
|
||||
|
||||
command: ["khal", "list", "today"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
root.khalAvailable = (exitCode === 0)
|
||||
root.khalAvailable = (exitCode === 0);
|
||||
if (exitCode !== 0) {
|
||||
console.warn("CalendarService: khal not configured (exit code:", exitCode, ")")
|
||||
console.warn("CalendarService: khal not configured (exit code:", exitCode, ")");
|
||||
} else {
|
||||
console.log("CalendarService: khal configured and available")
|
||||
console.log("CalendarService: khal configured and available");
|
||||
// Load current month events when khal becomes available
|
||||
loadCurrentMonth()
|
||||
loadCurrentMonth();
|
||||
}
|
||||
}
|
||||
|
||||
onStarted: {
|
||||
console.log("CalendarService: Checking khal configuration...")
|
||||
console.log("CalendarService: Checking khal configuration...");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process for loading events
|
||||
Process {
|
||||
id: eventsProcess
|
||||
running: false
|
||||
|
||||
|
||||
property date requestStartDate
|
||||
property date requestEndDate
|
||||
property string rawOutput: ""
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
eventsProcess.rawOutput += data + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
root.isLoading = false
|
||||
|
||||
root.isLoading = false;
|
||||
if (exitCode !== 0) {
|
||||
root.lastError = "Failed to load events (exit code: " + exitCode + ")"
|
||||
console.warn("CalendarService:", root.lastError)
|
||||
return
|
||||
root.lastError = "Failed to load events (exit code: " + exitCode + ")";
|
||||
console.warn("CalendarService:", root.lastError);
|
||||
return ;
|
||||
}
|
||||
|
||||
try {
|
||||
let newEventsByDate = {}
|
||||
let lines = eventsProcess.rawOutput.split('\n')
|
||||
|
||||
let newEventsByDate = {
|
||||
};
|
||||
let lines = eventsProcess.rawOutput.split('\n');
|
||||
for (let line of lines) {
|
||||
line = line.trim()
|
||||
if (!line || line === "[]") continue
|
||||
|
||||
line = line.trim();
|
||||
if (!line || line === "[]")
|
||||
continue;
|
||||
|
||||
// Parse JSON line
|
||||
let dayEvents = JSON.parse(line)
|
||||
|
||||
let dayEvents = JSON.parse(line);
|
||||
// Process each event in this day's array
|
||||
for (let event of dayEvents) {
|
||||
if (!event.title) continue
|
||||
|
||||
if (!event.title)
|
||||
continue;
|
||||
|
||||
// Parse start and end dates
|
||||
let startDate, endDate
|
||||
let startDate, endDate;
|
||||
if (event['start-date']) {
|
||||
let startParts = event['start-date'].split('/')
|
||||
startDate = new Date(parseInt(startParts[2]), parseInt(startParts[0]) - 1, parseInt(startParts[1]))
|
||||
let startParts = event['start-date'].split('/');
|
||||
startDate = new Date(parseInt(startParts[2]), parseInt(startParts[0]) - 1, parseInt(startParts[1]));
|
||||
} else {
|
||||
startDate = new Date()
|
||||
startDate = new Date();
|
||||
}
|
||||
|
||||
if (event['end-date']) {
|
||||
let endParts = event['end-date'].split('/')
|
||||
endDate = new Date(parseInt(endParts[2]), parseInt(endParts[0]) - 1, parseInt(endParts[1]))
|
||||
let endParts = event['end-date'].split('/');
|
||||
endDate = new Date(parseInt(endParts[2]), parseInt(endParts[0]) - 1, parseInt(endParts[1]));
|
||||
} else {
|
||||
endDate = new Date(startDate)
|
||||
endDate = new Date(startDate);
|
||||
}
|
||||
|
||||
// Create start/end times
|
||||
let startTime = new Date(startDate)
|
||||
let endTime = new Date(endDate)
|
||||
|
||||
let startTime = new Date(startDate);
|
||||
let endTime = new Date(endDate);
|
||||
if (event['start-time'] && event['all-day'] !== "True") {
|
||||
// Parse time if available and not all-day
|
||||
let timeStr = event['start-time']
|
||||
let timeStr = event['start-time'];
|
||||
if (timeStr) {
|
||||
let timeParts = timeStr.match(/(\d+):(\d+)/)
|
||||
let timeParts = timeStr.match(/(\d+):(\d+)/);
|
||||
if (timeParts) {
|
||||
startTime.setHours(parseInt(timeParts[1]), parseInt(timeParts[2]))
|
||||
|
||||
startTime.setHours(parseInt(timeParts[1]), parseInt(timeParts[2]));
|
||||
if (event['end-time']) {
|
||||
let endTimeParts = event['end-time'].match(/(\d+):(\d+)/)
|
||||
if (endTimeParts) {
|
||||
endTime.setHours(parseInt(endTimeParts[1]), parseInt(endTimeParts[2]))
|
||||
}
|
||||
let endTimeParts = event['end-time'].match(/(\d+):(\d+)/);
|
||||
if (endTimeParts)
|
||||
endTime.setHours(parseInt(endTimeParts[1]), parseInt(endTimeParts[2]));
|
||||
|
||||
} else {
|
||||
// Default to 1 hour duration on same day
|
||||
endTime = new Date(startTime)
|
||||
endTime.setHours(startTime.getHours() + 1)
|
||||
endTime = new Date(startTime);
|
||||
endTime.setHours(startTime.getHours() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID for this event (to track multi-day events)
|
||||
let eventId = event.title + "_" + event['start-date'] + "_" + (event['start-time'] || 'allday')
|
||||
|
||||
let eventId = event.title + "_" + event['start-date'] + "_" + (event['start-time'] || 'allday');
|
||||
// Create event object template
|
||||
let eventTemplate = {
|
||||
id: eventId,
|
||||
title: event.title || "Untitled Event",
|
||||
start: startTime,
|
||||
end: endTime,
|
||||
location: event.location || "",
|
||||
description: event.description || "",
|
||||
url: event.url || "",
|
||||
calendar: "",
|
||||
color: "",
|
||||
allDay: event['all-day'] === "True",
|
||||
isMultiDay: startDate.toDateString() !== endDate.toDateString()
|
||||
}
|
||||
|
||||
"id": eventId,
|
||||
"title": event.title || "Untitled Event",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
"location": event.location || "",
|
||||
"description": event.description || "",
|
||||
"url": event.url || "",
|
||||
"calendar": "",
|
||||
"color": "",
|
||||
"allDay": event['all-day'] === "True",
|
||||
"isMultiDay": startDate.toDateString() !== endDate.toDateString()
|
||||
};
|
||||
// Add event to each day it spans
|
||||
let currentDate = new Date(startDate)
|
||||
let currentDate = new Date(startDate);
|
||||
while (currentDate <= endDate) {
|
||||
let dateKey = Qt.formatDate(currentDate, "yyyy-MM-dd")
|
||||
|
||||
if (!newEventsByDate[dateKey]) {
|
||||
newEventsByDate[dateKey] = []
|
||||
}
|
||||
|
||||
let dateKey = Qt.formatDate(currentDate, "yyyy-MM-dd");
|
||||
if (!newEventsByDate[dateKey])
|
||||
newEventsByDate[dateKey] = [];
|
||||
|
||||
// Check if this exact event is already added to this date (prevent duplicates)
|
||||
let existingEvent = newEventsByDate[dateKey].find(e => e.id === eventId)
|
||||
let existingEvent = newEventsByDate[dateKey].find((e) => {
|
||||
return e.id === eventId;
|
||||
});
|
||||
if (existingEvent) {
|
||||
// Move to next day without adding duplicate
|
||||
currentDate.setDate(currentDate.getDate() + 1)
|
||||
continue
|
||||
currentDate.setDate(currentDate.getDate() + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a copy of the event for this date
|
||||
let dayEvent = Object.assign({}, eventTemplate)
|
||||
|
||||
let dayEvent = Object.assign({
|
||||
}, eventTemplate);
|
||||
// For multi-day events, adjust the display time for this specific day
|
||||
if (currentDate.getTime() === startDate.getTime()) {
|
||||
// First day - use original start time
|
||||
dayEvent.start = new Date(startTime)
|
||||
dayEvent.start = new Date(startTime);
|
||||
} else {
|
||||
// Subsequent days - start at beginning of day for all-day events
|
||||
dayEvent.start = new Date(currentDate)
|
||||
if (!dayEvent.allDay) {
|
||||
dayEvent.start.setHours(0, 0, 0, 0)
|
||||
}
|
||||
dayEvent.start = new Date(currentDate);
|
||||
if (!dayEvent.allDay)
|
||||
dayEvent.start.setHours(0, 0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
if (currentDate.getTime() === endDate.getTime()) {
|
||||
// Last day - use original end time
|
||||
dayEvent.end = new Date(endTime)
|
||||
dayEvent.end = new Date(endTime);
|
||||
} else {
|
||||
// Earlier days - end at end of day for all-day events
|
||||
dayEvent.end = new Date(currentDate)
|
||||
if (!dayEvent.allDay) {
|
||||
dayEvent.end.setHours(23, 59, 59, 999)
|
||||
}
|
||||
dayEvent.end = new Date(currentDate);
|
||||
if (!dayEvent.allDay)
|
||||
dayEvent.end.setHours(23, 59, 59, 999);
|
||||
|
||||
}
|
||||
|
||||
newEventsByDate[dateKey].push(dayEvent)
|
||||
|
||||
newEventsByDate[dateKey].push(dayEvent);
|
||||
// Move to next day
|
||||
currentDate.setDate(currentDate.getDate() + 1)
|
||||
currentDate.setDate(currentDate.getDate() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort events by start time within each date
|
||||
for (let dateKey in newEventsByDate) {
|
||||
newEventsByDate[dateKey].sort((a, b) => a.start.getTime() - b.start.getTime())
|
||||
newEventsByDate[dateKey].sort((a, b) => {
|
||||
return a.start.getTime() - b.start.getTime();
|
||||
});
|
||||
}
|
||||
|
||||
root.eventsByDate = newEventsByDate
|
||||
root.lastError = ""
|
||||
|
||||
console.log("CalendarService: Loaded events for", Object.keys(newEventsByDate).length, "days")
|
||||
|
||||
root.eventsByDate = newEventsByDate;
|
||||
root.lastError = "";
|
||||
console.log("CalendarService: Loaded events for", Object.keys(newEventsByDate).length, "days");
|
||||
} catch (error) {
|
||||
root.lastError = "Failed to parse events JSON: " + error.toString()
|
||||
console.error("CalendarService:", root.lastError)
|
||||
root.eventsByDate = {}
|
||||
root.lastError = "Failed to parse events JSON: " + error.toString();
|
||||
console.error("CalendarService:", root.lastError);
|
||||
root.eventsByDate = {
|
||||
};
|
||||
}
|
||||
|
||||
// Reset for next run
|
||||
eventsProcess.rawOutput = ""
|
||||
eventsProcess.rawOutput = "";
|
||||
}
|
||||
|
||||
onStarted: {
|
||||
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"),
|
||||
"to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"))
|
||||
console.log("CalendarService: Loading events from", Qt.formatDate(requestStartDate, "yyyy-MM-dd"), "to", Qt.formatDate(requestEndDate, "yyyy-MM-dd"));
|
||||
}
|
||||
}
|
||||
|
||||
function checkKhalAvailability() {
|
||||
if (!khalCheckProcess.running) {
|
||||
khalCheckProcess.running = true
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
eventsProcess.rawOutput += data + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function loadCurrentMonth() {
|
||||
if (!root.khalAvailable) return
|
||||
|
||||
let today = new Date()
|
||||
let firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
|
||||
let lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0)
|
||||
|
||||
// Add padding
|
||||
let startDate = new Date(firstDay)
|
||||
startDate.setDate(startDate.getDate() - firstDay.getDay() - 7)
|
||||
|
||||
let endDate = new Date(lastDay)
|
||||
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()) + 7)
|
||||
|
||||
loadEvents(startDate, endDate)
|
||||
}
|
||||
|
||||
function loadEvents(startDate, endDate) {
|
||||
if (!root.khalAvailable) {
|
||||
console.warn("CalendarService: khal not available, skipping event loading")
|
||||
return
|
||||
}
|
||||
|
||||
if (eventsProcess.running) {
|
||||
console.log("CalendarService: Event loading already in progress, skipping...")
|
||||
return
|
||||
}
|
||||
|
||||
// Store last requested date range for refresh timer
|
||||
root.lastStartDate = startDate
|
||||
root.lastEndDate = endDate
|
||||
|
||||
root.isLoading = true
|
||||
|
||||
// Format dates for khal (MM/dd/yyyy based on printformats)
|
||||
let startDateStr = Qt.formatDate(startDate, "MM/dd/yyyy")
|
||||
let endDateStr = Qt.formatDate(endDate, "MM/dd/yyyy")
|
||||
|
||||
eventsProcess.requestStartDate = startDate
|
||||
eventsProcess.requestEndDate = endDate
|
||||
eventsProcess.command = [
|
||||
"khal", "list",
|
||||
"--json", "title",
|
||||
"--json", "description",
|
||||
"--json", "start-date",
|
||||
"--json", "start-time",
|
||||
"--json", "end-date",
|
||||
"--json", "end-time",
|
||||
"--json", "all-day",
|
||||
"--json", "location",
|
||||
"--json", "url",
|
||||
startDateStr, endDateStr
|
||||
]
|
||||
|
||||
eventsProcess.running = true
|
||||
}
|
||||
|
||||
function getEventsForDate(date) {
|
||||
let dateKey = Qt.formatDate(date, "yyyy-MM-dd")
|
||||
return root.eventsByDate[dateKey] || []
|
||||
}
|
||||
|
||||
function hasEventsForDate(date) {
|
||||
let events = getEventsForDate(date)
|
||||
return events.length > 0
|
||||
}
|
||||
|
||||
// Initialize on component completion
|
||||
Component.onCompleted: {
|
||||
console.log("CalendarService: Component completed, initializing...")
|
||||
checkKhalAvailability()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,92 +7,37 @@ import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
property bool niriAvailable: false
|
||||
property string focusedAppId: ""
|
||||
property string focusedAppName: ""
|
||||
property string focusedWindowTitle: ""
|
||||
property int focusedWindowId: -1
|
||||
|
||||
Component.onCompleted: {
|
||||
// Use the availability from NiriWorkspaceService to avoid duplicate checks
|
||||
root.niriAvailable = NiriWorkspaceService.niriAvailable
|
||||
|
||||
// Connect to workspace service events
|
||||
NiriWorkspaceService.onNiriAvailableChanged.connect(() => {
|
||||
root.niriAvailable = NiriWorkspaceService.niriAvailable
|
||||
if (root.niriAvailable) {
|
||||
loadInitialFocusedWindow()
|
||||
}
|
||||
})
|
||||
|
||||
if (root.niriAvailable) {
|
||||
loadInitialFocusedWindow()
|
||||
}
|
||||
}
|
||||
|
||||
// Listen to window focus changes from NiriWorkspaceService
|
||||
Connections {
|
||||
target: NiriWorkspaceService
|
||||
function onFocusedWindowIdChanged() {
|
||||
root.focusedWindowId = parseInt(NiriWorkspaceService.focusedWindowId) || -1
|
||||
updateFocusedWindowData()
|
||||
}
|
||||
function onFocusedWindowTitleChanged() {
|
||||
root.focusedWindowTitle = NiriWorkspaceService.focusedWindowTitle
|
||||
}
|
||||
}
|
||||
|
||||
// Process to get focused window info
|
||||
Process {
|
||||
id: focusedWindowQuery
|
||||
command: ["niri", "msg", "--json", "focused-window"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text && text.trim()) {
|
||||
try {
|
||||
const windowData = JSON.parse(text.trim())
|
||||
root.focusedAppId = windowData.app_id || ""
|
||||
root.focusedWindowTitle = windowData.title || ""
|
||||
root.focusedAppName = getDisplayName(windowData.app_id || "")
|
||||
root.focusedWindowId = parseInt(windowData.id) || -1
|
||||
} catch (e) {
|
||||
console.warn("FocusedWindowService: Failed to parse focused window data:", e)
|
||||
clearFocusedWindow()
|
||||
}
|
||||
} else {
|
||||
clearFocusedWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadInitialFocusedWindow() {
|
||||
if (root.niriAvailable) {
|
||||
focusedWindowQuery.running = true
|
||||
}
|
||||
if (root.niriAvailable)
|
||||
focusedWindowQuery.running = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function updateFocusedWindowData() {
|
||||
if (root.niriAvailable && root.focusedWindowId !== -1) {
|
||||
focusedWindowQuery.running = true
|
||||
} else {
|
||||
clearFocusedWindow()
|
||||
}
|
||||
if (root.niriAvailable && root.focusedWindowId !== -1)
|
||||
focusedWindowQuery.running = true;
|
||||
else
|
||||
clearFocusedWindow();
|
||||
}
|
||||
|
||||
|
||||
function clearFocusedWindow() {
|
||||
root.focusedAppId = ""
|
||||
root.focusedAppName = ""
|
||||
root.focusedWindowTitle = ""
|
||||
root.focusedAppId = "";
|
||||
root.focusedAppName = "";
|
||||
root.focusedWindowTitle = "";
|
||||
}
|
||||
|
||||
|
||||
// Convert app_id to a more user-friendly display name
|
||||
function getDisplayName(appId) {
|
||||
if (!appId) return ""
|
||||
|
||||
if (!appId)
|
||||
return "";
|
||||
|
||||
// Common app_id to display name mappings
|
||||
const appNames = {
|
||||
"com.mitchellh.ghostty": "Ghostty",
|
||||
@@ -113,22 +58,74 @@ Singleton {
|
||||
"discord": "Discord",
|
||||
"slack": "Slack",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
|
||||
};
|
||||
// Return mapped name or clean up the app_id
|
||||
if (appNames[appId]) {
|
||||
return appNames[appId]
|
||||
}
|
||||
|
||||
if (appNames[appId])
|
||||
return appNames[appId];
|
||||
|
||||
// Try to extract a clean name from the app_id
|
||||
// Remove common prefixes and make first letter uppercase
|
||||
let cleanName = appId
|
||||
.replace(/^(org\.|com\.|net\.|io\.)/, '')
|
||||
.replace(/\./g, ' ')
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
|
||||
return cleanName
|
||||
let cleanName = appId.replace(/^(org\.|com\.|net\.|io\.)/, '').replace(/\./g, ' ').split(' ').map((word) => {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||
}).join(' ');
|
||||
return cleanName;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Use the availability from NiriWorkspaceService to avoid duplicate checks
|
||||
root.niriAvailable = NiriWorkspaceService.niriAvailable;
|
||||
// Connect to workspace service events
|
||||
NiriWorkspaceService.onNiriAvailableChanged.connect(() => {
|
||||
root.niriAvailable = NiriWorkspaceService.niriAvailable;
|
||||
if (root.niriAvailable)
|
||||
loadInitialFocusedWindow();
|
||||
|
||||
});
|
||||
if (root.niriAvailable)
|
||||
loadInitialFocusedWindow();
|
||||
|
||||
}
|
||||
|
||||
// Listen to window focus changes from NiriWorkspaceService
|
||||
Connections {
|
||||
function onFocusedWindowIdChanged() {
|
||||
root.focusedWindowId = parseInt(NiriWorkspaceService.focusedWindowId) || -1;
|
||||
updateFocusedWindowData();
|
||||
}
|
||||
|
||||
function onFocusedWindowTitleChanged() {
|
||||
root.focusedWindowTitle = NiriWorkspaceService.focusedWindowTitle;
|
||||
}
|
||||
|
||||
target: NiriWorkspaceService
|
||||
}
|
||||
|
||||
// Process to get focused window info
|
||||
Process {
|
||||
id: focusedWindowQuery
|
||||
|
||||
command: ["niri", "msg", "--json", "focused-window"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text && text.trim()) {
|
||||
try {
|
||||
const windowData = JSON.parse(text.trim());
|
||||
root.focusedAppId = windowData.app_id || "";
|
||||
root.focusedWindowTitle = windowData.title || "";
|
||||
root.focusedAppName = getDisplayName(windowData.app_id || "");
|
||||
root.focusedWindowId = parseInt(windowData.id) || -1;
|
||||
} catch (e) {
|
||||
console.warn("FocusedWindowService: Failed to parse focused window data:", e);
|
||||
clearFocusedWindow();
|
||||
}
|
||||
} else {
|
||||
clearFocusedWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,16 +6,14 @@ import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
signal showAppLauncher()
|
||||
signal hideAppLauncher()
|
||||
signal toggleAppLauncher()
|
||||
|
||||
signal showSpotlight()
|
||||
signal hideSpotlight()
|
||||
signal toggleSpotlight()
|
||||
|
||||
signal showClipboardHistory()
|
||||
signal hideClipboardHistory()
|
||||
signal toggleClipboardHistory()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ Singleton {
|
||||
id: root
|
||||
|
||||
// Workspace management
|
||||
property list<var> allWorkspaces: []
|
||||
property var allWorkspaces: []
|
||||
property int focusedWorkspaceIndex: 0
|
||||
property string focusedWorkspaceId: ""
|
||||
property var currentOutputWorkspaces: []
|
||||
property string currentOutput: ""
|
||||
|
||||
// Window management
|
||||
property list<var> windows: []
|
||||
property var windows: []
|
||||
property int focusedWindowIndex: -1
|
||||
property string focusedWindowTitle: "(No active window)"
|
||||
property string focusedWindowId: ""
|
||||
|
||||
@@ -7,106 +7,106 @@ import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
property string osLogo: ""
|
||||
property string osName: ""
|
||||
|
||||
|
||||
// OS Detection using /etc/os-release
|
||||
Process {
|
||||
id: osDetector
|
||||
|
||||
command: ["sh", "-c", "grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '\"'"]
|
||||
running: true
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
// Ultimate fallback - use generic apps icon (empty logo means fallback to "apps")
|
||||
root.osLogo = "";
|
||||
root.osName = "Linux";
|
||||
console.log("OS detection failed, using generic icon");
|
||||
}
|
||||
}
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
if (data.trim()) {
|
||||
let osId = data.trim().toLowerCase()
|
||||
console.log("Detected OS from /etc/os-release:", osId)
|
||||
|
||||
let osId = data.trim().toLowerCase();
|
||||
console.log("Detected OS from /etc/os-release:", osId);
|
||||
// Set OS-specific Nerd Font icons and names
|
||||
switch(osId) {
|
||||
case "arch":
|
||||
root.osLogo = "\uf303" // Arch Linux Nerd Font icon
|
||||
root.osName = "Arch Linux"
|
||||
break
|
||||
case "ubuntu":
|
||||
root.osLogo = "\uf31b" // Ubuntu Nerd Font icon
|
||||
root.osName = "Ubuntu"
|
||||
break
|
||||
case "fedora":
|
||||
root.osLogo = "\uf30a" // Fedora Nerd Font icon
|
||||
root.osName = "Fedora"
|
||||
break
|
||||
case "debian":
|
||||
root.osLogo = "\uf306" // Debian Nerd Font icon
|
||||
root.osName = "Debian"
|
||||
break
|
||||
case "opensuse":
|
||||
case "opensuse-leap":
|
||||
case "opensuse-tumbleweed":
|
||||
root.osLogo = "\uef6d" // openSUSE Nerd Font icon
|
||||
root.osName = "openSUSE"
|
||||
break
|
||||
case "manjaro":
|
||||
root.osLogo = "\uf312" // Manjaro Nerd Font icon
|
||||
root.osName = "Manjaro"
|
||||
break
|
||||
case "nixos":
|
||||
root.osLogo = "\uf313" // NixOS Nerd Font icon
|
||||
root.osName = "NixOS"
|
||||
break
|
||||
case "rocky":
|
||||
root.osLogo = "\uf32b" // Rocky Linux Nerd Font icon
|
||||
root.osName = "Rocky Linux"
|
||||
break
|
||||
case "almalinux":
|
||||
root.osLogo = "\uf31d" // AlmaLinux Nerd Font icon
|
||||
root.osName = "AlmaLinux"
|
||||
break
|
||||
case "centos":
|
||||
root.osLogo = "\uf304" // CentOS Nerd Font icon
|
||||
root.osName = "CentOS"
|
||||
break
|
||||
case "rhel":
|
||||
case "redhat":
|
||||
root.osLogo = "\uf316" // Red Hat Nerd Font icon
|
||||
root.osName = "Red Hat"
|
||||
break
|
||||
case "gentoo":
|
||||
root.osLogo = "\uf30d" // Gentoo Nerd Font icon
|
||||
root.osName = "Gentoo"
|
||||
break
|
||||
case "mint":
|
||||
case "linuxmint":
|
||||
root.osLogo = "\uf30e" // Linux Mint Nerd Font icon
|
||||
root.osName = "Linux Mint"
|
||||
break
|
||||
case "elementary":
|
||||
root.osLogo = "\uf309" // Elementary OS Nerd Font icon
|
||||
root.osName = "Elementary OS"
|
||||
break
|
||||
case "pop":
|
||||
root.osLogo = "\uf32a" // Pop!_OS Nerd Font icon
|
||||
root.osName = "Pop!_OS"
|
||||
break
|
||||
default:
|
||||
root.osLogo = "\uf17c" // Generic Linux Nerd Font icon
|
||||
root.osName = "Linux"
|
||||
switch (osId) {
|
||||
case "arch":
|
||||
root.osLogo = "\uf303"; // Arch Linux Nerd Font icon
|
||||
root.osName = "Arch Linux";
|
||||
break;
|
||||
case "ubuntu":
|
||||
root.osLogo = "\uf31b"; // Ubuntu Nerd Font icon
|
||||
root.osName = "Ubuntu";
|
||||
break;
|
||||
case "fedora":
|
||||
root.osLogo = "\uf30a"; // Fedora Nerd Font icon
|
||||
root.osName = "Fedora";
|
||||
break;
|
||||
case "debian":
|
||||
root.osLogo = "\uf306"; // Debian Nerd Font icon
|
||||
root.osName = "Debian";
|
||||
break;
|
||||
case "opensuse":
|
||||
case "opensuse-leap":
|
||||
case "opensuse-tumbleweed":
|
||||
root.osLogo = "\uef6d"; // openSUSE Nerd Font icon
|
||||
root.osName = "openSUSE";
|
||||
break;
|
||||
case "manjaro":
|
||||
root.osLogo = "\uf312"; // Manjaro Nerd Font icon
|
||||
root.osName = "Manjaro";
|
||||
break;
|
||||
case "nixos":
|
||||
root.osLogo = "\uf313"; // NixOS Nerd Font icon
|
||||
root.osName = "NixOS";
|
||||
break;
|
||||
case "rocky":
|
||||
root.osLogo = "\uf32b"; // Rocky Linux Nerd Font icon
|
||||
root.osName = "Rocky Linux";
|
||||
break;
|
||||
case "almalinux":
|
||||
root.osLogo = "\uf31d"; // AlmaLinux Nerd Font icon
|
||||
root.osName = "AlmaLinux";
|
||||
break;
|
||||
case "centos":
|
||||
root.osLogo = "\uf304"; // CentOS Nerd Font icon
|
||||
root.osName = "CentOS";
|
||||
break;
|
||||
case "rhel":
|
||||
case "redhat":
|
||||
root.osLogo = "\uf316"; // Red Hat Nerd Font icon
|
||||
root.osName = "Red Hat";
|
||||
break;
|
||||
case "gentoo":
|
||||
root.osLogo = "\uf30d"; // Gentoo Nerd Font icon
|
||||
root.osName = "Gentoo";
|
||||
break;
|
||||
case "mint":
|
||||
case "linuxmint":
|
||||
root.osLogo = "\uf30e"; // Linux Mint Nerd Font icon
|
||||
root.osName = "Linux Mint";
|
||||
break;
|
||||
case "elementary":
|
||||
root.osLogo = "\uf309"; // Elementary OS Nerd Font icon
|
||||
root.osName = "Elementary OS";
|
||||
break;
|
||||
case "pop":
|
||||
root.osLogo = "\uf32a"; // Pop!_OS Nerd Font icon
|
||||
root.osName = "Pop!_OS";
|
||||
break;
|
||||
default:
|
||||
root.osLogo = "\uf17c"; // Generic Linux Nerd Font icon
|
||||
root.osName = "Linux";
|
||||
}
|
||||
|
||||
console.log("Set OS logo:", root.osLogo, "Name:", root.osName)
|
||||
console.log("Set OS logo:", root.osLogo, "Name:", root.osName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
// Ultimate fallback - use generic apps icon (empty logo means fallback to "apps")
|
||||
root.osLogo = ""
|
||||
root.osName = "Linux"
|
||||
console.log("OS detection failed, using generic icon")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,263 +6,122 @@ import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
|
||||
|
||||
id: root
|
||||
|
||||
|
||||
property var processes: []
|
||||
property bool isUpdating: false
|
||||
property int processUpdateInterval: 3000
|
||||
|
||||
property bool monitoringEnabled: false
|
||||
|
||||
property int totalMemoryKB: 0
|
||||
property int usedMemoryKB: 0
|
||||
property int totalSwapKB: 0
|
||||
property int usedSwapKB: 0
|
||||
property int cpuCount: 1
|
||||
property real totalCpuUsage: 0.0
|
||||
property real totalCpuUsage: 0
|
||||
property bool systemInfoAvailable: false
|
||||
|
||||
property var cpuHistory: []
|
||||
property var memoryHistory: []
|
||||
property var networkHistory: ({rx: [], tx: []})
|
||||
property var diskHistory: ({read: [], write: []})
|
||||
property var networkHistory: ({
|
||||
"rx": [],
|
||||
"tx": []
|
||||
})
|
||||
property var diskHistory: ({
|
||||
"read": [],
|
||||
"write": []
|
||||
})
|
||||
property int historySize: 60
|
||||
|
||||
property var perCoreCpuUsage: []
|
||||
|
||||
property real networkRxRate: 0
|
||||
property real networkTxRate: 0
|
||||
property var lastNetworkStats: null
|
||||
|
||||
property real diskReadRate: 0
|
||||
property real diskWriteRate: 0
|
||||
property var lastDiskStats: null
|
||||
|
||||
property string sortBy: "cpu"
|
||||
property bool sortDescending: true
|
||||
property int maxProcesses: 20
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("ProcessMonitorService: Starting initialization...")
|
||||
updateProcessList()
|
||||
console.log("ProcessMonitorService: Initialization complete")
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: testTimer
|
||||
interval: 3000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
console.log("ProcessMonitorService: Starting test monitoring...")
|
||||
enableMonitoring(true)
|
||||
stopTestTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stopTestTimer
|
||||
interval: 8000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
console.log("ProcessMonitorService: Stopping test monitoring...")
|
||||
enableMonitoring(false)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemInfoProcess
|
||||
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
parseSystemInfo(text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("System info check failed with exit code:", exitCode)
|
||||
root.systemInfoAvailable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: networkStatsProcess
|
||||
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
parseNetworkStats(text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: diskStatsProcess
|
||||
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
parseDiskStats(text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processListProcess
|
||||
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const lines = text.trim().split('\n')
|
||||
const newProcesses = []
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim()
|
||||
if (!line) continue
|
||||
|
||||
const parts = line.split(/\s+/)
|
||||
if (parts.length >= 7) {
|
||||
const pid = parseInt(parts[0])
|
||||
const ppid = parseInt(parts[1])
|
||||
const cpu = parseFloat(parts[2])
|
||||
const memoryPercent = parseFloat(parts[3])
|
||||
const memoryKB = parseInt(parts[4])
|
||||
const command = parts[5]
|
||||
const fullCmd = parts.slice(6).join(' ')
|
||||
|
||||
newProcesses.push({
|
||||
pid: pid,
|
||||
ppid: ppid,
|
||||
cpu: cpu,
|
||||
memoryPercent: memoryPercent,
|
||||
memoryKB: memoryKB,
|
||||
command: command,
|
||||
fullCommand: fullCmd,
|
||||
displayName: command.length > 15 ? command.substring(0, 15) + "..." : command
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
root.processes = newProcesses
|
||||
root.isUpdating = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
root.isUpdating = false
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Process list check failed with exit code:", exitCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: processTimer
|
||||
interval: root.processUpdateInterval
|
||||
running: root.monitoringEnabled
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
if (root.monitoringEnabled) {
|
||||
updateSystemInfo()
|
||||
updateProcessList()
|
||||
updateNetworkStats()
|
||||
updateDiskStats()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateSystemInfo() {
|
||||
if (!systemInfoProcess.running && root.monitoringEnabled) {
|
||||
systemInfoProcess.running = true
|
||||
}
|
||||
if (!systemInfoProcess.running && root.monitoringEnabled)
|
||||
systemInfoProcess.running = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function enableMonitoring(enabled) {
|
||||
console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled")
|
||||
root.monitoringEnabled = enabled
|
||||
console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled");
|
||||
root.monitoringEnabled = enabled;
|
||||
if (enabled) {
|
||||
root.cpuHistory = []
|
||||
root.memoryHistory = []
|
||||
root.networkHistory = ({rx: [], tx: []})
|
||||
root.diskHistory = ({read: [], write: []})
|
||||
updateSystemInfo()
|
||||
updateProcessList()
|
||||
updateNetworkStats()
|
||||
updateDiskStats()
|
||||
root.cpuHistory = [];
|
||||
root.memoryHistory = [];
|
||||
root.networkHistory = ({
|
||||
"rx": [],
|
||||
"tx": []
|
||||
});
|
||||
root.diskHistory = ({
|
||||
"read": [],
|
||||
"write": []
|
||||
});
|
||||
updateSystemInfo();
|
||||
updateProcessList();
|
||||
updateNetworkStats();
|
||||
updateDiskStats();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateNetworkStats() {
|
||||
if (!networkStatsProcess.running && root.monitoringEnabled) {
|
||||
networkStatsProcess.running = true
|
||||
}
|
||||
if (!networkStatsProcess.running && root.monitoringEnabled)
|
||||
networkStatsProcess.running = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function updateDiskStats() {
|
||||
if (!diskStatsProcess.running && root.monitoringEnabled) {
|
||||
diskStatsProcess.running = true
|
||||
}
|
||||
if (!diskStatsProcess.running && root.monitoringEnabled)
|
||||
diskStatsProcess.running = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function updateProcessList() {
|
||||
if (!root.isUpdating && root.monitoringEnabled) {
|
||||
root.isUpdating = true
|
||||
|
||||
let sortOption = ""
|
||||
root.isUpdating = true;
|
||||
let sortOption = "";
|
||||
switch (root.sortBy) {
|
||||
case "cpu":
|
||||
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu"
|
||||
break
|
||||
case "memory":
|
||||
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem"
|
||||
break
|
||||
case "name":
|
||||
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm"
|
||||
break
|
||||
case "pid":
|
||||
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid"
|
||||
break
|
||||
default:
|
||||
sortOption = "--sort=-pcpu"
|
||||
case "cpu":
|
||||
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu";
|
||||
break;
|
||||
case "memory":
|
||||
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem";
|
||||
break;
|
||||
case "name":
|
||||
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm";
|
||||
break;
|
||||
case "pid":
|
||||
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid";
|
||||
break;
|
||||
default:
|
||||
sortOption = "--sort=-pcpu";
|
||||
}
|
||||
|
||||
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)]
|
||||
processListProcess.running = true
|
||||
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)];
|
||||
processListProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setSortBy(newSortBy) {
|
||||
if (newSortBy !== root.sortBy) {
|
||||
root.sortBy = newSortBy
|
||||
updateProcessList()
|
||||
root.sortBy = newSortBy;
|
||||
updateProcessList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toggleSortOrder() {
|
||||
root.sortDescending = !root.sortDescending
|
||||
updateProcessList()
|
||||
root.sortDescending = !root.sortDescending;
|
||||
updateProcessList();
|
||||
}
|
||||
|
||||
|
||||
function killProcess(pid) {
|
||||
if (pid > 0) {
|
||||
const killCmd = ["bash", "-c", "kill " + pid]
|
||||
const killCmd = ["bash", "-c", "kill " + pid];
|
||||
const killProcess = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
@@ -278,196 +137,346 @@ Singleton {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
`, root)
|
||||
`, root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getProcessIcon(command) {
|
||||
const cmd = command.toLowerCase()
|
||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser")) return "web"
|
||||
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim")) return "code"
|
||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh")) return "terminal"
|
||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify")) return "music_note"
|
||||
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv")) return "play_circle"
|
||||
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread")) return "settings"
|
||||
return "memory"
|
||||
const cmd = command.toLowerCase();
|
||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
|
||||
return "web";
|
||||
|
||||
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
|
||||
return "code";
|
||||
|
||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
|
||||
return "terminal";
|
||||
|
||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
|
||||
return "music_note";
|
||||
|
||||
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
|
||||
return "play_circle";
|
||||
|
||||
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread"))
|
||||
return "settings";
|
||||
|
||||
return "memory";
|
||||
}
|
||||
|
||||
|
||||
function formatCpuUsage(cpu) {
|
||||
return cpu.toFixed(1) + "%"
|
||||
return cpu.toFixed(1) + "%";
|
||||
}
|
||||
|
||||
|
||||
function formatMemoryUsage(memoryKB) {
|
||||
if (memoryKB < 1024) {
|
||||
return memoryKB.toFixed(0) + " KB"
|
||||
} else if (memoryKB < 1024 * 1024) {
|
||||
return (memoryKB / 1024).toFixed(1) + " MB"
|
||||
} else {
|
||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB"
|
||||
}
|
||||
if (memoryKB < 1024)
|
||||
return memoryKB.toFixed(0) + " KB";
|
||||
else if (memoryKB < 1024 * 1024)
|
||||
return (memoryKB / 1024).toFixed(1) + " MB";
|
||||
else
|
||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
|
||||
function formatSystemMemory(memoryKB) {
|
||||
if (memoryKB < 1024 * 1024) {
|
||||
return (memoryKB / 1024).toFixed(0) + " MB"
|
||||
} else {
|
||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB"
|
||||
}
|
||||
if (memoryKB < 1024 * 1024)
|
||||
return (memoryKB / 1024).toFixed(0) + " MB";
|
||||
else
|
||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
|
||||
function parseSystemInfo(text) {
|
||||
const lines = text.split('\n')
|
||||
let section = 'memory'
|
||||
const coreUsages = []
|
||||
let memFree = 0
|
||||
let memBuffers = 0
|
||||
let memCached = 0
|
||||
|
||||
const lines = text.split('\n');
|
||||
let section = 'memory';
|
||||
const coreUsages = [];
|
||||
let memFree = 0;
|
||||
let memBuffers = 0;
|
||||
let memCached = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim()
|
||||
|
||||
const line = lines[i].trim();
|
||||
if (line === '---CPU---') {
|
||||
section = 'cpucount'
|
||||
continue
|
||||
section = 'cpucount';
|
||||
continue;
|
||||
} else if (line === '---CPUSTAT---') {
|
||||
section = 'cpustat'
|
||||
continue
|
||||
section = 'cpustat';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (section === 'memory') {
|
||||
if (line.startsWith('MemTotal:')) {
|
||||
root.totalMemoryKB = parseInt(line.split(/\s+/)[1])
|
||||
root.totalMemoryKB = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('MemFree:')) {
|
||||
memFree = parseInt(line.split(/\s+/)[1])
|
||||
memFree = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('Buffers:')) {
|
||||
memBuffers = parseInt(line.split(/\s+/)[1])
|
||||
memBuffers = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('Cached:')) {
|
||||
memCached = parseInt(line.split(/\s+/)[1])
|
||||
memCached = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('SwapTotal:')) {
|
||||
root.totalSwapKB = parseInt(line.split(/\s+/)[1])
|
||||
root.totalSwapKB = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('SwapFree:')) {
|
||||
const freeSwapKB = parseInt(line.split(/\s+/)[1])
|
||||
root.usedSwapKB = root.totalSwapKB - freeSwapKB
|
||||
const freeSwapKB = parseInt(line.split(/\s+/)[1]);
|
||||
root.usedSwapKB = root.totalSwapKB - freeSwapKB;
|
||||
}
|
||||
} else if (section === 'cpucount') {
|
||||
const count = parseInt(line)
|
||||
if (!isNaN(count)) {
|
||||
root.cpuCount = count
|
||||
}
|
||||
const count = parseInt(line);
|
||||
if (!isNaN(count))
|
||||
root.cpuCount = count;
|
||||
|
||||
} else if (section === 'cpustat') {
|
||||
if (line.startsWith('cpu ')) {
|
||||
const parts = line.split(/\s+/)
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 8) {
|
||||
const user = parseInt(parts[1])
|
||||
const nice = parseInt(parts[2])
|
||||
const system = parseInt(parts[3])
|
||||
const idle = parseInt(parts[4])
|
||||
const iowait = parseInt(parts[5])
|
||||
const irq = parseInt(parts[6])
|
||||
const softirq = parseInt(parts[7])
|
||||
|
||||
const total = user + nice + system + idle + iowait + irq + softirq
|
||||
const used = total - idle - iowait
|
||||
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0
|
||||
const user = parseInt(parts[1]);
|
||||
const nice = parseInt(parts[2]);
|
||||
const system = parseInt(parts[3]);
|
||||
const idle = parseInt(parts[4]);
|
||||
const iowait = parseInt(parts[5]);
|
||||
const irq = parseInt(parts[6]);
|
||||
const softirq = parseInt(parts[7]);
|
||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||
const used = total - idle - iowait;
|
||||
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0;
|
||||
}
|
||||
} else if (line.match(/^cpu\d+/)) {
|
||||
const parts = line.split(/\s+/)
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 8) {
|
||||
const user = parseInt(parts[1])
|
||||
const nice = parseInt(parts[2])
|
||||
const system = parseInt(parts[3])
|
||||
const idle = parseInt(parts[4])
|
||||
const iowait = parseInt(parts[5])
|
||||
const irq = parseInt(parts[6])
|
||||
const softirq = parseInt(parts[7])
|
||||
|
||||
const total = user + nice + system + idle + iowait + irq + softirq
|
||||
const used = total - idle - iowait
|
||||
const usage = total > 0 ? (used / total) * 100 : 0
|
||||
coreUsages.push(usage)
|
||||
const user = parseInt(parts[1]);
|
||||
const nice = parseInt(parts[2]);
|
||||
const system = parseInt(parts[3]);
|
||||
const idle = parseInt(parts[4]);
|
||||
const iowait = parseInt(parts[5]);
|
||||
const irq = parseInt(parts[6]);
|
||||
const softirq = parseInt(parts[7]);
|
||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||
const used = total - idle - iowait;
|
||||
const usage = total > 0 ? (used / total) * 100 : 0;
|
||||
coreUsages.push(usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate used memory as total minus free minus buffers minus cached
|
||||
root.usedMemoryKB = root.totalMemoryKB - memFree - memBuffers - memCached
|
||||
|
||||
root.usedMemoryKB = root.totalMemoryKB - memFree - memBuffers - memCached;
|
||||
// Update per-core usage
|
||||
root.perCoreCpuUsage = coreUsages
|
||||
|
||||
root.perCoreCpuUsage = coreUsages;
|
||||
// Update history
|
||||
addToHistory(root.cpuHistory, root.totalCpuUsage)
|
||||
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0
|
||||
addToHistory(root.memoryHistory, memoryPercent)
|
||||
|
||||
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
|
||||
|
||||
root.systemInfoAvailable = true
|
||||
addToHistory(root.cpuHistory, root.totalCpuUsage);
|
||||
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0;
|
||||
addToHistory(root.memoryHistory, memoryPercent);
|
||||
root.systemInfoAvailable = true;
|
||||
}
|
||||
|
||||
|
||||
function parseNetworkStats(text) {
|
||||
const lines = text.split('\n')
|
||||
let totalRx = 0
|
||||
let totalTx = 0
|
||||
|
||||
const lines = text.split('\n');
|
||||
let totalRx = 0;
|
||||
let totalTx = 0;
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts.length >= 3) {
|
||||
const rx = parseInt(parts[1])
|
||||
const tx = parseInt(parts[2])
|
||||
const rx = parseInt(parts[1]);
|
||||
const tx = parseInt(parts[2]);
|
||||
if (!isNaN(rx) && !isNaN(tx)) {
|
||||
totalRx += rx
|
||||
totalTx += tx
|
||||
totalRx += rx;
|
||||
totalTx += tx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root.lastNetworkStats) {
|
||||
const timeDiff = root.processUpdateInterval / 1000
|
||||
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff)
|
||||
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff)
|
||||
|
||||
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024)
|
||||
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024)
|
||||
const timeDiff = root.processUpdateInterval / 1000;
|
||||
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff);
|
||||
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff);
|
||||
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024);
|
||||
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024);
|
||||
}
|
||||
|
||||
root.lastNetworkStats = { rx: totalRx, tx: totalTx }
|
||||
root.lastNetworkStats = {
|
||||
"rx": totalRx,
|
||||
"tx": totalTx
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function parseDiskStats(text) {
|
||||
const lines = text.split('\n')
|
||||
let totalRead = 0
|
||||
let totalWrite = 0
|
||||
|
||||
const lines = text.split('\n');
|
||||
let totalRead = 0;
|
||||
let totalWrite = 0;
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts.length >= 3) {
|
||||
const readSectors = parseInt(parts[1])
|
||||
const writeSectors = parseInt(parts[2])
|
||||
const readSectors = parseInt(parts[1]);
|
||||
const writeSectors = parseInt(parts[2]);
|
||||
if (!isNaN(readSectors) && !isNaN(writeSectors)) {
|
||||
totalRead += readSectors * 512
|
||||
totalWrite += writeSectors * 512
|
||||
totalRead += readSectors * 512;
|
||||
totalWrite += writeSectors * 512;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root.lastDiskStats) {
|
||||
const timeDiff = root.processUpdateInterval / 1000
|
||||
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff)
|
||||
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff)
|
||||
|
||||
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024))
|
||||
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024))
|
||||
const timeDiff = root.processUpdateInterval / 1000;
|
||||
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff);
|
||||
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff);
|
||||
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024));
|
||||
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024));
|
||||
}
|
||||
|
||||
root.lastDiskStats = { read: totalRead, write: totalWrite }
|
||||
root.lastDiskStats = {
|
||||
"read": totalRead,
|
||||
"write": totalWrite
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function addToHistory(array, value) {
|
||||
array.push(value)
|
||||
if (array.length > root.historySize) {
|
||||
array.shift()
|
||||
array.push(value);
|
||||
if (array.length > root.historySize)
|
||||
array.shift();
|
||||
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("ProcessMonitorService: Starting initialization...");
|
||||
updateProcessList();
|
||||
console.log("ProcessMonitorService: Initialization complete");
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: testTimer
|
||||
|
||||
interval: 3000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
console.log("ProcessMonitorService: Starting test monitoring...");
|
||||
enableMonitoring(true);
|
||||
stopTestTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stopTestTimer
|
||||
|
||||
interval: 8000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
console.log("ProcessMonitorService: Stopping test monitoring...");
|
||||
enableMonitoring(false);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemInfoProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("System info check failed with exit code:", exitCode);
|
||||
root.systemInfoAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
parseSystemInfo(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: networkStatsProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
parseNetworkStats(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: diskStatsProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
parseDiskStats(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processListProcess
|
||||
|
||||
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
root.isUpdating = false;
|
||||
if (exitCode !== 0)
|
||||
console.warn("Process list check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const lines = text.trim().split('\n');
|
||||
const newProcesses = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 7) {
|
||||
const pid = parseInt(parts[0]);
|
||||
const ppid = parseInt(parts[1]);
|
||||
const cpu = parseFloat(parts[2]);
|
||||
const memoryPercent = parseFloat(parts[3]);
|
||||
const memoryKB = parseInt(parts[4]);
|
||||
const command = parts[5];
|
||||
const fullCmd = parts.slice(6).join(' ');
|
||||
newProcesses.push({
|
||||
"pid": pid,
|
||||
"ppid": ppid,
|
||||
"cpu": cpu,
|
||||
"memoryPercent": memoryPercent,
|
||||
"memoryKB": memoryKB,
|
||||
"command": command,
|
||||
"fullCommand": fullCmd,
|
||||
"displayName": command.length > 15 ? command.substring(0, 15) + "..." : command
|
||||
});
|
||||
}
|
||||
}
|
||||
root.processes = newProcesses;
|
||||
root.isUpdating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: processTimer
|
||||
|
||||
interval: root.processUpdateInterval
|
||||
running: root.monitoringEnabled
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.monitoringEnabled) {
|
||||
updateSystemInfo();
|
||||
updateProcessList();
|
||||
updateNetworkStats();
|
||||
updateDiskStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,24 +7,20 @@ import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property real cpuUsage: 0.0
|
||||
|
||||
property real cpuUsage: 0
|
||||
property int cpuCores: 1
|
||||
property string cpuModel: ""
|
||||
property real cpuFrequency: 0.0
|
||||
|
||||
property real cpuFrequency: 0
|
||||
property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
property real memoryUsage: 0.0
|
||||
property real totalMemory: 0.0
|
||||
property real usedMemory: 0.0
|
||||
property real freeMemory: 0.0
|
||||
property real availableMemory: 0.0
|
||||
property real bufferMemory: 0.0
|
||||
property real cacheMemory: 0.0
|
||||
|
||||
property real cpuTemperature: 0.0
|
||||
|
||||
property real memoryUsage: 0
|
||||
property real totalMemory: 0
|
||||
property real usedMemory: 0
|
||||
property real freeMemory: 0
|
||||
property real availableMemory: 0
|
||||
property real bufferMemory: 0
|
||||
property real cacheMemory: 0
|
||||
property real cpuTemperature: 0
|
||||
property string kernelVersion: ""
|
||||
property string distribution: ""
|
||||
property string hostname: ""
|
||||
@@ -39,537 +35,567 @@ Singleton {
|
||||
property string biosVersion: ""
|
||||
property var diskMounts: []
|
||||
property string diskUsage: ""
|
||||
|
||||
property int cpuUpdateInterval: 3000
|
||||
property int memoryUpdateInterval: 5000
|
||||
property int temperatureUpdateInterval: 10000
|
||||
property int systemInfoUpdateInterval: 30000
|
||||
|
||||
property bool enabledForTopBar: true
|
||||
property bool enabledForDetailedView: false
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("SystemMonitorService: Starting initialization...")
|
||||
getCpuInfo()
|
||||
updateSystemStats()
|
||||
updateSystemInfo()
|
||||
console.log("SystemMonitorService: Initialization complete")
|
||||
function getCpuInfo() {
|
||||
cpuInfoProcess.running = true;
|
||||
}
|
||||
|
||||
|
||||
function updateSystemStats() {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
||||
cpuUsageProcess.running = true;
|
||||
memoryUsageProcess.running = true;
|
||||
cpuFrequencyProcess.running = true;
|
||||
if (root.enabledForDetailedView)
|
||||
temperatureProcess.running = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function updateSystemInfo() {
|
||||
kernelInfoProcess.running = true;
|
||||
distributionProcess.running = true;
|
||||
hostnameProcess.running = true;
|
||||
uptimeProcess.running = true;
|
||||
schedulerProcess.running = true;
|
||||
architectureProcess.running = true;
|
||||
loadAverageProcess.running = true;
|
||||
processCountProcess.running = true;
|
||||
threadCountProcess.running = true;
|
||||
bootTimeProcess.running = true;
|
||||
motherboardProcess.running = true;
|
||||
biosProcess.running = true;
|
||||
diskMountsProcess.running = true;
|
||||
}
|
||||
|
||||
function enableTopBarMonitoring(enabled) {
|
||||
root.enabledForTopBar = enabled;
|
||||
}
|
||||
|
||||
function enableDetailedMonitoring(enabled) {
|
||||
root.enabledForDetailedView = enabled;
|
||||
}
|
||||
|
||||
function getCpuUsageColor() {
|
||||
if (cpuUsage > 80)
|
||||
return "#e74c3c";
|
||||
|
||||
if (cpuUsage > 60)
|
||||
return "#f39c12";
|
||||
|
||||
return "#27ae60";
|
||||
}
|
||||
|
||||
function getMemoryUsageColor() {
|
||||
if (memoryUsage > 90)
|
||||
return "#e74c3c";
|
||||
|
||||
if (memoryUsage > 75)
|
||||
return "#f39c12";
|
||||
|
||||
return "#3498db";
|
||||
}
|
||||
|
||||
function formatMemory(mb) {
|
||||
if (mb >= 1024)
|
||||
return (mb / 1024).toFixed(1) + " GB";
|
||||
|
||||
return mb.toFixed(0) + " MB";
|
||||
}
|
||||
|
||||
function getTemperatureColor() {
|
||||
if (cpuTemperature > 80)
|
||||
return "#e74c3c";
|
||||
|
||||
if (cpuTemperature > 65)
|
||||
return "#f39c12";
|
||||
|
||||
return "#27ae60";
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("SystemMonitorService: Starting initialization...");
|
||||
getCpuInfo();
|
||||
updateSystemStats();
|
||||
updateSystemInfo();
|
||||
console.log("SystemMonitorService: Initialization complete");
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cpuInfoProcess
|
||||
|
||||
command: ["bash", "-c", "lscpu | grep -E 'Model name|CPU\\(s\\):' | head -2"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU info check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().split('\n')
|
||||
const lines = text.trim().split('\n');
|
||||
for (const line of lines) {
|
||||
if (line.includes("Model name")) {
|
||||
root.cpuModel = line.split(":")[1].trim()
|
||||
} else if (line.includes("CPU(s):")) {
|
||||
root.cpuCores = parseInt(line.split(":")[1].trim())
|
||||
}
|
||||
if (line.includes("Model name"))
|
||||
root.cpuModel = line.split(":")[1].trim();
|
||||
else if (line.includes("CPU(s):"))
|
||||
root.cpuCores = parseInt(line.split(":")[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("CPU info check failed with exit code:", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: cpuUsageProcess
|
||||
|
||||
command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU usage check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const stats = text.trim().split(" ").map(x => parseInt(x))
|
||||
const stats = text.trim().split(" ").map((x) => {
|
||||
return parseInt(x);
|
||||
});
|
||||
if (root.prevCpuStats[0] > 0) {
|
||||
let diffs = []
|
||||
let diffs = [];
|
||||
for (let i = 0; i < 8; i++) {
|
||||
diffs[i] = stats[i] - root.prevCpuStats[i]
|
||||
}
|
||||
|
||||
const totalTime = diffs.reduce((a, b) => a + b, 0)
|
||||
const idleTime = diffs[3] + diffs[4]
|
||||
|
||||
if (totalTime > 0) {
|
||||
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100))
|
||||
diffs[i] = stats[i] - root.prevCpuStats[i];
|
||||
}
|
||||
const totalTime = diffs.reduce((a, b) => {
|
||||
return a + b;
|
||||
}, 0);
|
||||
const idleTime = diffs[3] + diffs[4];
|
||||
if (totalTime > 0)
|
||||
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100));
|
||||
|
||||
}
|
||||
root.prevCpuStats = stats
|
||||
root.prevCpuStats = stats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("CPU usage check failed with exit code:", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: memoryUsageProcess
|
||||
|
||||
command: ["bash", "-c", "free -m | awk 'NR==2{printf \"%.1f %.1f %.1f %.1f\", $3*100/$2, $2, $3, $7}'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Memory usage check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const parts = text.trim().split(" ")
|
||||
root.memoryUsage = parseFloat(parts[0])
|
||||
root.totalMemory = parseFloat(parts[1])
|
||||
root.usedMemory = parseFloat(parts[2])
|
||||
root.availableMemory = parseFloat(parts[3])
|
||||
root.freeMemory = root.totalMemory - root.usedMemory
|
||||
const parts = text.trim().split(" ");
|
||||
root.memoryUsage = parseFloat(parts[0]);
|
||||
root.totalMemory = parseFloat(parts[1]);
|
||||
root.usedMemory = parseFloat(parts[2]);
|
||||
root.availableMemory = parseFloat(parts[3]);
|
||||
root.freeMemory = root.totalMemory - root.usedMemory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Memory usage check failed with exit code:", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: cpuFrequencyProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/cpuinfo | grep 'cpu MHz' | head -1 | awk '{print $4}'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU frequency check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.cpuFrequency = parseFloat(text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("CPU frequency check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.cpuFrequency = parseFloat(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: temperatureProcess
|
||||
|
||||
command: ["bash", "-c", "if [ -f /sys/class/thermal/thermal_zone0/temp ]; then cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1/1000}'; else sensors 2>/dev/null | grep 'Core 0' | awk '{print $3}' | sed 's/+//g;s/°C//g' | head -1; fi"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU temperature check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.cpuTemperature = parseFloat(text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("CPU temperature check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.cpuTemperature = parseFloat(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: kernelInfoProcess
|
||||
|
||||
command: ["bash", "-c", "uname -r"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Kernel info check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.kernelVersion = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Kernel info check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.kernelVersion = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: distributionProcess
|
||||
|
||||
command: ["bash", "-c", "grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '\"' || echo 'Unknown'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Distribution check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.distribution = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Distribution check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.distribution = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: hostnameProcess
|
||||
|
||||
command: ["bash", "-c", "hostname"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Hostname check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.hostname = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Hostname check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.hostname = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: uptimeProcess
|
||||
|
||||
command: ["bash", "-c", "uptime -p | sed 's/up //'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Uptime check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.uptime = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Uptime check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.uptime = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: schedulerProcess
|
||||
|
||||
command: ["bash", "-c", "cat /sys/block/sda/queue/scheduler 2>/dev/null | grep -o '\\[.*\\]' | tr -d '[]' || echo 'Unknown'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Scheduler check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.scheduler = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Scheduler check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.scheduler = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: architectureProcess
|
||||
|
||||
command: ["bash", "-c", "uname -m"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Architecture check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.architecture = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Architecture check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.architecture = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: loadAverageProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/loadavg | cut -d' ' -f1,2,3"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Load average check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.loadAverage = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Load average check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.loadAverage = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: processCountProcess
|
||||
|
||||
command: ["bash", "-c", "ps aux | wc -l"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Process count check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.processCount = parseInt(text.trim()) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Process count check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.processCount = parseInt(text.trim()) - 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: threadCountProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/stat | grep processes | awk '{print $2}'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Thread count check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.threadCount = parseInt(text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Thread count check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.threadCount = parseInt(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: bootTimeProcess
|
||||
|
||||
command: ["bash", "-c", "who -b | awk '{print $3, $4}' || stat -c %w /proc/1 2>/dev/null | cut -d' ' -f1,2 || echo 'Unknown'"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Boot time check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.bootTime = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Boot time check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.bootTime = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: motherboardProcess
|
||||
|
||||
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/board_vendor ] && [ -r /sys/devices/virtual/dmi/id/board_name ]; then echo \"$(cat /sys/devices/virtual/dmi/id/board_vendor 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/board_name 2>/dev/null)\"; else echo 'Unknown'; fi"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Motherboard check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.motherboard = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Motherboard check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.motherboard = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: biosProcess
|
||||
|
||||
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/bios_version ] && [ -r /sys/devices/virtual/dmi/id/bios_date ]; then echo \"$(cat /sys/devices/virtual/dmi/id/bios_version 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/bios_date 2>/dev/null)\"; else echo 'Unknown'; fi"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("BIOS check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.biosVersion = text.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("BIOS check failed with exit code:", exitCode)
|
||||
if (text.trim())
|
||||
root.biosVersion = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: diskMountsProcess
|
||||
|
||||
command: ["bash", "-c", "df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -v tmpfs | grep -v devtmpfs | head -10"]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Disk mounts check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
let mounts = []
|
||||
const lines = text.trim().split('\n')
|
||||
let mounts = [];
|
||||
const lines = text.trim().split('\n');
|
||||
for (const line of lines) {
|
||||
const parts = line.split(/\s+/)
|
||||
if (parts.length >= 7) {
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 7)
|
||||
mounts.push({
|
||||
device: parts[0],
|
||||
mount: parts[1],
|
||||
fstype: parts[2],
|
||||
size: parts[3],
|
||||
used: parts[4],
|
||||
avail: parts[5],
|
||||
percent: parts[6]
|
||||
})
|
||||
}
|
||||
"device": parts[0],
|
||||
"mount": parts[1],
|
||||
"fstype": parts[2],
|
||||
"size": parts[3],
|
||||
"used": parts[4],
|
||||
"avail": parts[5],
|
||||
"percent": parts[6]
|
||||
});
|
||||
|
||||
}
|
||||
root.diskMounts = mounts
|
||||
root.diskMounts = mounts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Disk mounts check failed with exit code:", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: cpuTimer
|
||||
|
||||
interval: root.cpuUpdateInterval
|
||||
running: root.enabledForTopBar || root.enabledForDetailedView
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
||||
cpuUsageProcess.running = true
|
||||
cpuFrequencyProcess.running = true
|
||||
cpuUsageProcess.running = true;
|
||||
cpuFrequencyProcess.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: memoryTimer
|
||||
|
||||
interval: root.memoryUpdateInterval
|
||||
running: root.enabledForTopBar || root.enabledForDetailedView
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
||||
memoryUsageProcess.running = true
|
||||
}
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView)
|
||||
memoryUsageProcess.running = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: temperatureTimer
|
||||
|
||||
interval: root.temperatureUpdateInterval
|
||||
running: root.enabledForDetailedView
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
if (root.enabledForDetailedView) {
|
||||
temperatureProcess.running = true
|
||||
}
|
||||
if (root.enabledForDetailedView)
|
||||
temperatureProcess.running = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: systemInfoTimer
|
||||
|
||||
interval: root.systemInfoUpdateInterval
|
||||
running: root.enabledForDetailedView
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
if (root.enabledForDetailedView) {
|
||||
updateSystemInfo()
|
||||
}
|
||||
if (root.enabledForDetailedView)
|
||||
updateSystemInfo();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function getCpuInfo() {
|
||||
cpuInfoProcess.running = true
|
||||
}
|
||||
|
||||
function updateSystemStats() {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
||||
cpuUsageProcess.running = true
|
||||
memoryUsageProcess.running = true
|
||||
cpuFrequencyProcess.running = true
|
||||
if (root.enabledForDetailedView) {
|
||||
temperatureProcess.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateSystemInfo() {
|
||||
kernelInfoProcess.running = true
|
||||
distributionProcess.running = true
|
||||
hostnameProcess.running = true
|
||||
uptimeProcess.running = true
|
||||
schedulerProcess.running = true
|
||||
architectureProcess.running = true
|
||||
loadAverageProcess.running = true
|
||||
processCountProcess.running = true
|
||||
threadCountProcess.running = true
|
||||
bootTimeProcess.running = true
|
||||
motherboardProcess.running = true
|
||||
biosProcess.running = true
|
||||
diskMountsProcess.running = true
|
||||
}
|
||||
|
||||
function enableTopBarMonitoring(enabled) {
|
||||
root.enabledForTopBar = enabled
|
||||
}
|
||||
|
||||
function enableDetailedMonitoring(enabled) {
|
||||
root.enabledForDetailedView = enabled
|
||||
}
|
||||
|
||||
function getCpuUsageColor() {
|
||||
if (cpuUsage > 80) return "#e74c3c"
|
||||
if (cpuUsage > 60) return "#f39c12"
|
||||
return "#27ae60"
|
||||
}
|
||||
|
||||
function getMemoryUsageColor() {
|
||||
if (memoryUsage > 90) return "#e74c3c"
|
||||
if (memoryUsage > 75) return "#f39c12"
|
||||
return "#3498db"
|
||||
}
|
||||
|
||||
function formatMemory(mb) {
|
||||
if (mb >= 1024) {
|
||||
return (mb / 1024).toFixed(1) + " GB"
|
||||
}
|
||||
return mb.toFixed(0) + " MB"
|
||||
}
|
||||
|
||||
function getTemperatureColor() {
|
||||
if (cpuTemperature > 80) return "#e74c3c"
|
||||
if (cpuTemperature > 65) return "#f39c12"
|
||||
return "#27ae60"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,16 +10,63 @@ Singleton {
|
||||
readonly property int levelInfo: 0
|
||||
readonly property int levelWarn: 1
|
||||
readonly property int levelError: 2
|
||||
|
||||
property string currentMessage: ""
|
||||
property int currentLevel: levelInfo
|
||||
property bool toastVisible: false
|
||||
property var toastQueue: []
|
||||
|
||||
property string wallpaperErrorStatus: ""
|
||||
|
||||
function showToast(message, level = levelInfo) {
|
||||
toastQueue.push({
|
||||
"message": message,
|
||||
"level": level
|
||||
});
|
||||
if (!toastVisible)
|
||||
processQueue();
|
||||
|
||||
}
|
||||
|
||||
function showInfo(message) {
|
||||
showToast(message, levelInfo);
|
||||
}
|
||||
|
||||
function showWarning(message) {
|
||||
showToast(message, levelWarn);
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
showToast(message, levelError);
|
||||
}
|
||||
|
||||
function hideToast() {
|
||||
toastVisible = false;
|
||||
currentMessage = "";
|
||||
currentLevel = levelInfo;
|
||||
toastTimer.stop();
|
||||
if (toastQueue.length > 0)
|
||||
queueTimer.start();
|
||||
|
||||
}
|
||||
|
||||
function processQueue() {
|
||||
if (toastQueue.length === 0)
|
||||
return ;
|
||||
|
||||
const toast = toastQueue.shift();
|
||||
currentMessage = toast.message;
|
||||
currentLevel = toast.level;
|
||||
toastVisible = true;
|
||||
toastTimer.interval = toast.level === levelError ? 8000 : toast.level === levelWarn ? 6000 : 5000;
|
||||
toastTimer.start();
|
||||
}
|
||||
|
||||
function clearWallpaperError() {
|
||||
wallpaperErrorStatus = "";
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: toastTimer
|
||||
|
||||
interval: 5000
|
||||
running: false
|
||||
repeat: false
|
||||
@@ -28,56 +75,11 @@ Singleton {
|
||||
|
||||
Timer {
|
||||
id: queueTimer
|
||||
|
||||
interval: 500
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: processQueue()
|
||||
}
|
||||
|
||||
function showToast(message, level = levelInfo) {
|
||||
toastQueue.push({ message, level })
|
||||
if (!toastVisible) {
|
||||
processQueue()
|
||||
}
|
||||
}
|
||||
|
||||
function showInfo(message) {
|
||||
showToast(message, levelInfo)
|
||||
}
|
||||
|
||||
function showWarning(message) {
|
||||
showToast(message, levelWarn)
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
showToast(message, levelError)
|
||||
}
|
||||
|
||||
function hideToast() {
|
||||
toastVisible = false
|
||||
currentMessage = ""
|
||||
currentLevel = levelInfo
|
||||
toastTimer.stop()
|
||||
if (toastQueue.length > 0) {
|
||||
queueTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
function processQueue() {
|
||||
if (toastQueue.length === 0) return
|
||||
|
||||
const toast = toastQueue.shift()
|
||||
currentMessage = toast.message
|
||||
currentLevel = toast.level
|
||||
toastVisible = true
|
||||
|
||||
toastTimer.interval =
|
||||
toast.level === levelError ? 8000 :
|
||||
toast.level === levelWarn ? 6000 : 5000
|
||||
toastTimer.start()
|
||||
}
|
||||
|
||||
function clearWallpaperError() {
|
||||
wallpaperErrorStatus = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,85 +7,105 @@ import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
property string username: ""
|
||||
property string fullName: ""
|
||||
property string profilePicture: ""
|
||||
property string uptime: ""
|
||||
property string hostname: ""
|
||||
property bool profileAvailable: false
|
||||
|
||||
Component.onCompleted: {
|
||||
getUserInfo()
|
||||
getUptime()
|
||||
|
||||
// Update uptime every minute
|
||||
uptimeTimer.start()
|
||||
|
||||
function getUserInfo() {
|
||||
userInfoProcess.running = true;
|
||||
}
|
||||
|
||||
|
||||
function getUptime() {
|
||||
uptimeProcess.running = true;
|
||||
}
|
||||
|
||||
function getProfilePicture() {
|
||||
profilePictureProcess.running = true;
|
||||
}
|
||||
|
||||
function refreshUserInfo() {
|
||||
getUserInfo();
|
||||
getUptime();
|
||||
getProfilePicture();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
getUserInfo();
|
||||
getUptime();
|
||||
// Update uptime every minute
|
||||
uptimeTimer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: uptimeTimer
|
||||
|
||||
interval: 60000 // 1 minute
|
||||
running: false
|
||||
repeat: true
|
||||
onTriggered: getUptime()
|
||||
}
|
||||
|
||||
|
||||
// Get username and full name
|
||||
Process {
|
||||
id: userInfoProcess
|
||||
|
||||
command: ["bash", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("UserInfoService: Failed to get user info");
|
||||
root.username = "User";
|
||||
root.fullName = "User";
|
||||
root.hostname = "System";
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const parts = text.trim().split("|")
|
||||
const parts = text.trim().split("|");
|
||||
if (parts.length >= 3) {
|
||||
root.username = parts[0] || ""
|
||||
root.fullName = parts[1] || parts[0] || ""
|
||||
root.hostname = parts[2] || ""
|
||||
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname)
|
||||
|
||||
root.username = parts[0] || "";
|
||||
root.fullName = parts[1] || parts[0] || "";
|
||||
root.hostname = parts[2] || "";
|
||||
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname);
|
||||
// Try to find profile picture
|
||||
getProfilePicture()
|
||||
getProfilePicture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("UserInfoService: Failed to get user info")
|
||||
root.username = "User"
|
||||
root.fullName = "User"
|
||||
root.hostname = "System"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Get system uptime
|
||||
Process {
|
||||
id: uptimeProcess
|
||||
|
||||
command: ["bash", "-c", "uptime -p | sed 's/up //'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.uptime = text.trim() || "Unknown"
|
||||
console.log("UserInfoService: Uptime updated -", root.uptime)
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("UserInfoService: Failed to get uptime")
|
||||
root.uptime = "Unknown"
|
||||
console.warn("UserInfoService: Failed to get uptime");
|
||||
root.uptime = "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.uptime = text.trim() || "Unknown";
|
||||
console.log("UserInfoService: Uptime updated -", root.uptime);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Look for profile picture in common locations
|
||||
Process {
|
||||
id: profilePictureProcess
|
||||
|
||||
command: ["bash", "-c", `
|
||||
# Try common profile picture locations
|
||||
for path in \
|
||||
@@ -104,46 +124,29 @@ Singleton {
|
||||
echo ""
|
||||
`]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("UserInfoService: Failed to find profile picture");
|
||||
root.profilePicture = "";
|
||||
root.profileAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const path = text.trim()
|
||||
const path = text.trim();
|
||||
if (path && path.length > 0) {
|
||||
root.profilePicture = "file://" + path
|
||||
root.profileAvailable = true
|
||||
console.log("UserInfoService: Profile picture found at", path)
|
||||
root.profilePicture = "file://" + path;
|
||||
root.profileAvailable = true;
|
||||
console.log("UserInfoService: Profile picture found at", path);
|
||||
} else {
|
||||
root.profilePicture = ""
|
||||
root.profileAvailable = false
|
||||
console.log("UserInfoService: No profile picture found, using default avatar")
|
||||
root.profilePicture = "";
|
||||
root.profileAvailable = false;
|
||||
console.log("UserInfoService: No profile picture found, using default avatar");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("UserInfoService: Failed to find profile picture")
|
||||
root.profilePicture = ""
|
||||
root.profileAvailable = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getUserInfo() {
|
||||
userInfoProcess.running = true
|
||||
}
|
||||
|
||||
function getUptime() {
|
||||
uptimeProcess.running = true
|
||||
}
|
||||
|
||||
function getProfilePicture() {
|
||||
profilePictureProcess.running = true
|
||||
}
|
||||
|
||||
function refreshUserInfo() {
|
||||
getUserInfo()
|
||||
getUptime()
|
||||
getProfilePicture()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
|
||||
property string currentWifiSSID: ""
|
||||
property string wifiSignalStrength: "excellent" // "excellent", "good", "fair", "poor"
|
||||
property var wifiNetworks: []
|
||||
@@ -15,146 +15,26 @@ Singleton {
|
||||
property bool isScanning: false
|
||||
property string connectionStatus: "" // "cosnnecting", "connected", "failed", ""
|
||||
property string connectingSSID: ""
|
||||
|
||||
Process {
|
||||
id: currentWifiInfo
|
||||
command: ["bash", "-c", "nmcli -t -f ACTIVE,SSID,SIGNAL dev wifi | grep '^yes' | head -1"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
if (data.trim()) {
|
||||
let parts = data.split(":")
|
||||
if (parts.length >= 3 && parts[1].trim() !== "") {
|
||||
root.currentWifiSSID = parts[1].trim()
|
||||
let signal = parseInt(parts[2]) || 100
|
||||
|
||||
if (signal >= 75) root.wifiSignalStrength = "excellent"
|
||||
else if (signal >= 50) root.wifiSignalStrength = "good"
|
||||
else if (signal >= 25) root.wifiSignalStrength = "fair"
|
||||
else root.wifiSignalStrength = "poor"
|
||||
|
||||
console.log("Active WiFi:", root.currentWifiSSID, "Signal:", signal + "%")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: wifiScanner
|
||||
command: ["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY", "dev", "wifi"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
let networks = []
|
||||
let lines = text.trim().split('\n')
|
||||
|
||||
for (let line of lines) {
|
||||
let parts = line.split(':')
|
||||
if (parts.length >= 3 && parts[0].trim() !== "") {
|
||||
let ssid = parts[0].trim()
|
||||
let signal = parseInt(parts[1]) || 0
|
||||
let security = parts[2].trim()
|
||||
|
||||
// Skip duplicates
|
||||
if (!networks.find(n => n.ssid === ssid)) {
|
||||
// Check if this network is saved
|
||||
let isSaved = root.savedWifiNetworks.some(saved => saved.ssid === ssid)
|
||||
|
||||
networks.push({
|
||||
ssid: ssid,
|
||||
signal: signal,
|
||||
secured: security !== "",
|
||||
connected: ssid === root.currentWifiSSID,
|
||||
saved: isSaved,
|
||||
signalStrength: signal >= 75 ? "excellent" :
|
||||
signal >= 50 ? "good" :
|
||||
signal >= 25 ? "fair" : "poor"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by signal strength
|
||||
networks.sort((a, b) => b.signal - a.signal)
|
||||
root.wifiNetworks = networks
|
||||
console.log("Found", networks.length, "WiFi networks")
|
||||
|
||||
// Stop scanning once we have results
|
||||
if (networks.length > 0) {
|
||||
root.isScanning = false
|
||||
fallbackTimer.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: savedWifiScanner
|
||||
command: ["nmcli", "-t", "-f", "NAME", "connection", "show"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
let saved = []
|
||||
let lines = text.trim().split('\n')
|
||||
|
||||
for (let line of lines) {
|
||||
let connectionName = line.trim()
|
||||
if (connectionName &&
|
||||
!connectionName.includes("ethernet") &&
|
||||
!connectionName.includes("lo") &&
|
||||
!connectionName.includes("Wired") &&
|
||||
!connectionName.toLowerCase().includes("eth")) {
|
||||
saved.push({
|
||||
ssid: connectionName,
|
||||
saved: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
root.savedWifiNetworks = saved
|
||||
console.log("Found", saved.length, "saved WiFi networks")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-refresh timer for when control center is open
|
||||
property bool autoRefreshEnabled: false
|
||||
|
||||
function scanWifi() {
|
||||
if (root.isScanning) return
|
||||
|
||||
root.isScanning = true
|
||||
console.log("Starting WiFi scan...")
|
||||
|
||||
wifiScanner.running = true
|
||||
savedWifiScanner.running = true
|
||||
currentWifiInfo.running = true
|
||||
|
||||
if (root.isScanning)
|
||||
return ;
|
||||
|
||||
root.isScanning = true;
|
||||
console.log("Starting WiFi scan...");
|
||||
wifiScanner.running = true;
|
||||
savedWifiScanner.running = true;
|
||||
currentWifiInfo.running = true;
|
||||
// Fallback timer in case no networks are found
|
||||
fallbackTimer.start()
|
||||
fallbackTimer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fallbackTimer
|
||||
interval: 5000
|
||||
onTriggered: {
|
||||
root.isScanning = false
|
||||
console.log("WiFi scan timeout - no networks found")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function connectToWifi(ssid) {
|
||||
console.log("Connecting to WiFi:", ssid)
|
||||
|
||||
root.connectionStatus = "connecting"
|
||||
root.connectingSSID = ssid
|
||||
|
||||
console.log("Connecting to WiFi:", ssid);
|
||||
root.connectionStatus = "connecting";
|
||||
root.connectingSSID = ssid;
|
||||
let connectProcess = Qt.createQmlObject(`
|
||||
import Quickshell.Io
|
||||
Process {
|
||||
@@ -174,10 +54,10 @@ Singleton {
|
||||
console.log("WiFi connection failed")
|
||||
}
|
||||
scanWifi()
|
||||
|
||||
|
||||
statusResetTimer.start()
|
||||
}
|
||||
|
||||
|
||||
stderr: SplitParser {
|
||||
splitMarker: "\\n"
|
||||
onRead: (data) => {
|
||||
@@ -185,15 +65,13 @@ Singleton {
|
||||
}
|
||||
}
|
||||
}
|
||||
`, root)
|
||||
`, root);
|
||||
}
|
||||
|
||||
|
||||
function connectToWifiWithPassword(ssid, password) {
|
||||
console.log("Connecting to WiFi with password:", ssid)
|
||||
|
||||
root.connectionStatus = "connecting"
|
||||
root.connectingSSID = ssid
|
||||
|
||||
console.log("Connecting to WiFi with password:", ssid);
|
||||
root.connectionStatus = "connecting";
|
||||
root.connectingSSID = ssid;
|
||||
let connectProcess = Qt.createQmlObject(`
|
||||
import Quickshell.Io
|
||||
Process {
|
||||
@@ -213,10 +91,10 @@ Singleton {
|
||||
console.log("WiFi connection with password failed")
|
||||
}
|
||||
scanWifi()
|
||||
|
||||
|
||||
statusResetTimer.start()
|
||||
}
|
||||
|
||||
|
||||
stderr: SplitParser {
|
||||
splitMarker: "\\n"
|
||||
onRead: (data) => {
|
||||
@@ -224,11 +102,11 @@ Singleton {
|
||||
}
|
||||
}
|
||||
}
|
||||
`, root)
|
||||
`, root);
|
||||
}
|
||||
|
||||
|
||||
function forgetWifiNetwork(ssid) {
|
||||
console.log("Forgetting WiFi network:", ssid)
|
||||
console.log("Forgetting WiFi network:", ssid);
|
||||
let forgetProcess = Qt.createQmlObject(`
|
||||
import Quickshell.Io
|
||||
Process {
|
||||
@@ -243,7 +121,7 @@ Singleton {
|
||||
}
|
||||
scanWifi()
|
||||
}
|
||||
|
||||
|
||||
stderr: SplitParser {
|
||||
splitMarker: "\\n"
|
||||
onRead: (data) => {
|
||||
@@ -251,35 +129,156 @@ Singleton {
|
||||
}
|
||||
}
|
||||
}
|
||||
`, root)
|
||||
`, root);
|
||||
}
|
||||
|
||||
|
||||
function updateCurrentWifiInfo() {
|
||||
console.log("Updating current WiFi info...");
|
||||
currentWifiInfo.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: currentWifiInfo
|
||||
|
||||
command: ["bash", "-c", "nmcli -t -f ACTIVE,SSID,SIGNAL dev wifi | grep '^yes' | head -1"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
if (data.trim()) {
|
||||
let parts = data.split(":");
|
||||
if (parts.length >= 3 && parts[1].trim() !== "") {
|
||||
root.currentWifiSSID = parts[1].trim();
|
||||
let signal = parseInt(parts[2]) || 100;
|
||||
if (signal >= 75)
|
||||
root.wifiSignalStrength = "excellent";
|
||||
else if (signal >= 50)
|
||||
root.wifiSignalStrength = "good";
|
||||
else if (signal >= 25)
|
||||
root.wifiSignalStrength = "fair";
|
||||
else
|
||||
root.wifiSignalStrength = "poor";
|
||||
console.log("Active WiFi:", root.currentWifiSSID, "Signal:", signal + "%");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: wifiScanner
|
||||
|
||||
command: ["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY", "dev", "wifi"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
let networks = [];
|
||||
let lines = text.trim().split('\n');
|
||||
for (let line of lines) {
|
||||
let parts = line.split(':');
|
||||
if (parts.length >= 3 && parts[0].trim() !== "") {
|
||||
let ssid = parts[0].trim();
|
||||
let signal = parseInt(parts[1]) || 0;
|
||||
let security = parts[2].trim();
|
||||
// Skip duplicates
|
||||
if (!networks.find((n) => {
|
||||
return n.ssid === ssid;
|
||||
})) {
|
||||
// Check if this network is saved
|
||||
let isSaved = root.savedWifiNetworks.some((saved) => {
|
||||
return saved.ssid === ssid;
|
||||
});
|
||||
networks.push({
|
||||
"ssid": ssid,
|
||||
"signal": signal,
|
||||
"secured": security !== "",
|
||||
"connected": ssid === root.currentWifiSSID,
|
||||
"saved": isSaved,
|
||||
"signalStrength": signal >= 75 ? "excellent" : signal >= 50 ? "good" : signal >= 25 ? "fair" : "poor"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort by signal strength
|
||||
networks.sort((a, b) => {
|
||||
return b.signal - a.signal;
|
||||
});
|
||||
root.wifiNetworks = networks;
|
||||
console.log("Found", networks.length, "WiFi networks");
|
||||
// Stop scanning once we have results
|
||||
if (networks.length > 0) {
|
||||
root.isScanning = false;
|
||||
fallbackTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: savedWifiScanner
|
||||
|
||||
command: ["nmcli", "-t", "-f", "NAME", "connection", "show"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
let saved = [];
|
||||
let lines = text.trim().split('\n');
|
||||
for (let line of lines) {
|
||||
let connectionName = line.trim();
|
||||
if (connectionName && !connectionName.includes("ethernet") && !connectionName.includes("lo") && !connectionName.includes("Wired") && !connectionName.toLowerCase().includes("eth"))
|
||||
saved.push({
|
||||
"ssid": connectionName,
|
||||
"saved": true
|
||||
});
|
||||
|
||||
}
|
||||
root.savedWifiNetworks = saved;
|
||||
console.log("Found", saved.length, "saved WiFi networks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: statusResetTimer
|
||||
interval: 3000
|
||||
id: fallbackTimer
|
||||
|
||||
interval: 5000
|
||||
onTriggered: {
|
||||
root.connectionStatus = ""
|
||||
root.connectingSSID = ""
|
||||
root.isScanning = false;
|
||||
console.log("WiFi scan timeout - no networks found");
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-refresh timer for when control center is open
|
||||
property bool autoRefreshEnabled: false
|
||||
|
||||
|
||||
Timer {
|
||||
id: statusResetTimer
|
||||
|
||||
interval: 3000
|
||||
onTriggered: {
|
||||
root.connectionStatus = "";
|
||||
root.connectingSSID = "";
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoRefreshTimer
|
||||
|
||||
interval: 20000
|
||||
running: root.autoRefreshEnabled
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.autoRefreshEnabled) {
|
||||
root.scanWifi()
|
||||
}
|
||||
if (root.autoRefreshEnabled)
|
||||
root.scanWifi();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurrentWifiInfo() {
|
||||
console.log("Updating current WiFi info...")
|
||||
currentWifiInfo.running = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
44
qmlformat-all.sh
Executable file
44
qmlformat-all.sh
Executable 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"
|
||||
Reference in New Issue
Block a user