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