pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Hyprland Item { id: root property var popups: null readonly property bool expanded: popups ? popups.current === "network" : false property bool wifiEnabled: true property var networks: [] property string activeSsid: "" property int activeSignal: 0 property var knownSsids: ({}) property string statusMsg: "" property string pendingSsid: "" implicitWidth: icon.implicitWidth implicitHeight: 30 function secured(sec) { return !!sec && sec.trim().length > 0; } function signalGlyph(s) { if (s >= 80) return "\u{F0928}"; if (s >= 55) return "\u{F0925}"; if (s >= 30) return "\u{F0922}"; return "\u{F091F}"; } function wifiGlyph() { if (!wifiEnabled) return "\u{F05AA}"; if (activeSsid === "") return "\u{F05A9}"; return signalGlyph(activeSignal); } // nmcli -t escapes ':' as '\:' and '\' as '\\'; split on unescaped colons. function splitFields(line) { const out = []; let cur = ""; for (let i = 0; i < line.length; i++) { const c = line[i]; if (c === "\\" && i + 1 < line.length) { cur += line[i + 1]; i++; } else if (c === ":") { out.push(cur); cur = ""; } else { cur += c; } } out.push(cur); return out; } function refresh() { statusProc.running = true; listProc.running = true; knownProc.running = true; } function setWifi(on) { radioProc.command = ["nmcli", "radio", "wifi", on ? "on" : "off"]; radioProc.running = true; root.wifiEnabled = on; Qt.callLater(root.refresh); } function rescan() { root.statusMsg = "Scanning…"; rescanProc.running = true; } function doConnect(ssid, password) { root.statusMsg = "Connecting to " + ssid + "…"; const cmd = ["nmcli", "device", "wifi", "connect", ssid]; if (password && password.length > 0) cmd.push("password", password); connectProc.command = cmd; connectProc.running = true; root.pendingSsid = ""; } function disconnect(ssid) { root.statusMsg = "Disconnecting…"; actionProc.command = ["nmcli", "connection", "down", "id", ssid]; actionProc.running = true; } function onNetworkClicked(net) { if (net.active) { root.disconnect(net.ssid); return; } if (root.secured(net.security) && !root.knownSsids[net.ssid]) { root.pendingSsid = (root.pendingSsid === net.ssid) ? "" : net.ssid; return; } root.doConnect(net.ssid, ""); } Process { id: statusProc command: ["nmcli", "-t", "-f", "WIFI", "radio"] stdout: StdioCollector { id: statusOut onStreamFinished: root.wifiEnabled = statusOut.text.trim() === "enabled" } } Process { id: knownProc command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] stdout: StdioCollector { id: knownOut onStreamFinished: { const map = ({}); const lines = knownOut.text.split("\n"); for (const line of lines) { if (!line) continue; const f = root.splitFields(line); if (f[1] && f[1].indexOf("wireless") >= 0) map[f[0]] = true; } root.knownSsids = map; } } } Process { id: listProc command: ["nmcli", "-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"] stdout: StdioCollector { id: listOut onStreamFinished: { const seen = ({}); const list = []; let actSsid = ""; let actSig = 0; const lines = listOut.text.split("\n"); for (const line of lines) { if (!line) continue; const f = root.splitFields(line); const active = f[0] === "yes"; const ssid = f[1] || ""; const sig = parseInt(f[2] || "0") || 0; const sec = f[3] || ""; if (ssid === "") continue; if (active) { actSsid = ssid; actSig = sig; } if (seen[ssid] !== undefined) { const ex = list[seen[ssid]]; if (sig > ex.signal) ex.signal = sig; if (active) ex.active = true; continue; } seen[ssid] = list.length; list.push({ ssid: ssid, signal: sig, security: sec, active: active }); } list.sort((a, b) => (b.active - a.active) || (b.signal - a.signal)); root.networks = list; root.activeSsid = actSsid; root.activeSignal = actSig; } } } Process { id: radioProc } Process { id: rescanProc command: ["nmcli", "device", "wifi", "rescan"] onExited: { root.statusMsg = ""; root.refresh(); } } Process { id: connectProc stdout: StdioCollector { id: connectOut onStreamFinished: { root.statusMsg = ""; root.refresh(); } } stderr: StdioCollector { id: connectErr onStreamFinished: { const t = connectErr.text.trim(); if (t !== "") root.statusMsg = t.replace(/^Error:\s*/, ""); } } } Process { id: actionProc onExited: root.refresh() } onExpandedChanged: if (expanded) refresh() Timer { interval: 8000 running: root.expanded repeat: true onTriggered: root.refresh() } Component.onCompleted: refresh() Text { id: icon anchors.centerIn: parent font.family: "JetBrainsMono Nerd Font" font.pixelSize: 17 color: root.wifiEnabled ? (root.activeSsid !== "" ? "#89b4fa" : "#cdd6f4") : "#6c7086" text: root.wifiGlyph() } MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: if (root.popups) root.popups.toggle("network") } LazyLoader { active: root.expanded PanelWindow { id: win focusable: true anchors.top: true anchors.right: true margins.top: 6 margins.right: 8 implicitWidth: 360 implicitHeight: card.implicitHeight color: "transparent" exclusiveZone: 0 HyprlandFocusGrab { active: true windows: [win] onCleared: if (root.popups) root.popups.current = "" } Rectangle { id: card width: parent.width implicitHeight: col.implicitHeight + 24 radius: 16 color: "#f21e1e2e" border.color: "#45475a" border.width: 1 ColumnLayout { id: col anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.margins: 12 spacing: 8 RowLayout { Layout.fillWidth: true spacing: 8 Text { font.family: "JetBrainsMono Nerd Font" font.pixelSize: 16 color: "#cdd6f4" text: "\u{F05A9} Wi-Fi" } Item { Layout.fillWidth: true } Pill { visible: root.wifiEnabled text: "Rescan" onClicked: root.rescan() } Pill { active: root.wifiEnabled text: root.wifiEnabled ? "On" : "Off" onClicked: root.setWifi(!root.wifiEnabled) } } Rectangle { Layout.fillWidth: true implicitHeight: 1 color: "#313244" } Text { visible: !root.wifiEnabled color: "#6c7086" font.pixelSize: 13 text: "Wi-Fi is off" } Text { visible: root.statusMsg !== "" Layout.fillWidth: true wrapMode: Text.WordWrap color: "#f9e2af" font.pixelSize: 11 text: root.statusMsg } Repeater { model: root.wifiEnabled ? root.networks : [] delegate: ColumnLayout { id: nrow required property var modelData Layout.fillWidth: true spacing: 4 RowLayout { Layout.fillWidth: true spacing: 8 Text { font.family: "JetBrainsMono Nerd Font" font.pixelSize: 15 color: nrow.modelData.active ? "#a6e3a1" : "#cdd6f4" text: root.signalGlyph(nrow.modelData.signal) } Text { Layout.fillWidth: true elide: Text.ElideRight color: nrow.modelData.active ? "#a6e3a1" : "#cdd6f4" font.pixelSize: 13 text: nrow.modelData.ssid } Text { visible: root.secured(nrow.modelData.security) font.family: "JetBrainsMono Nerd Font" font.pixelSize: 12 color: "#6c7086" text: "\u{F033E}" } Pill { active: nrow.modelData.active text: nrow.modelData.active ? "Disconnect" : "Connect" onClicked: root.onNetworkClicked(nrow.modelData) } } RowLayout { id: pwRow visible: root.pendingSsid === nrow.modelData.ssid Layout.fillWidth: true spacing: 6 onVisibleChanged: if (visible) pwField.forceActiveFocus() Rectangle { Layout.fillWidth: true implicitHeight: 30 radius: 8 color: "#181825" border.color: pwField.activeFocus ? "#89b4fa" : "#45475a" border.width: 1 TextField { id: pwField anchors.fill: parent anchors.leftMargin: 8 anchors.rightMargin: 8 verticalAlignment: TextInput.AlignVCenter echoMode: TextInput.Password placeholderText: "Password" color: "#cdd6f4" placeholderTextColor: "#6c7086" font.pixelSize: 12 background: null onAccepted: root.doConnect(nrow.modelData.ssid, text) } } Pill { active: true text: "Join" onClicked: root.doConnect(nrow.modelData.ssid, pwField.text) } } } } } } } } component Pill: Rectangle { id: pill property string text: "" property bool active: false signal clicked implicitWidth: pillLabel.implicitWidth + 18 implicitHeight: pillLabel.implicitHeight + 8 radius: height / 2 color: pill.active ? "#89b4fa" : "#313244" Text { id: pillLabel anchors.centerIn: parent text: pill.text color: pill.active ? "#11111b" : "#cdd6f4" font.pixelSize: 12 } MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: pill.clicked() } } }