repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/ConnectivityPanel.qml
Rawpragma ComponentBehavior: Bound
import QtQuick
import "Util.js" as Util
import "sysinfo.js" as Sys
Column {
id: root
required property Theme theme
readonly property Flickable scrollView: networkScroll
required property Resources resourceService
required property Network networkService
required property Bluetooth bluetoothService
property var networkState: networkService ? Util.parseJson(networkService.stateJson(), {
"networks": [],
"knownNetworks": [],
"wifi": {},
"compact": {},
"pending": false,
"error": ""
}) : ({
"networks": [],
"knownNetworks": [],
"wifi": {},
"compact": {},
"pending": false,
"error": ""
})
property var bluetoothState: bluetoothService ? Util.parseJson(bluetoothService.stateJson(), {
"devices": [],
"adapter": {}
}) : ({
"devices": [],
"adapter": {}
})
// ssid whose inline passphrase field is currently revealed ("" = none).
// Holds only an ssid (not a secret); the passphrase lives solely in the
// TextInput.text and is cleared on submit, never bound into networkState.
property string expandedSsid: ""
function refreshNetwork(text) {
networkState = Util.parseJson(text, networkState);
}
function refreshBluetooth(text) {
bluetoothState = Util.parseJson(text, bluetoothState);
}
function formatRate(value) {
return Sys.formatRate(value);
}
function toggleWifi() {
if (networkService)
refreshNetwork(networkService.toggleWifi(!(networkState.wifi && networkState.wifi.enabled)));
}
function scanWifi() {
if (networkService)
refreshNetwork(networkService.scan());
}
function selectNetwork(id) {
if (networkService)
refreshNetwork(networkService.select(id));
}
function forgetNetwork(id) {
if (networkService)
refreshNetwork(networkService.forget(id));
}
// Secured-connect for a new network: connect(ssid, passphrase) is the
// in-process entry (NOT an IPC handler). The passphrase is a transient
// argument only — never stored on root / networkState / any logged field.
function connectSecured(ssid, passphrase) {
if (networkService)
refreshNetwork(networkService.connect(ssid, passphrase));
}
function disconnectNetwork() {
if (networkService)
refreshNetwork(networkService.disconnect());
}
// Toggle a row's detail drawer. Opening the CONNECTED row triggers a
// lazy StationDiagnostic read (bssid/band/rate) that fills in async.
function expandRow(row) {
const willOpen = expandedSsid !== row.ssid;
expandedSsid = willOpen ? row.ssid : "";
if (willOpen && row.connected && networkService)
refreshNetwork(networkService.diagnostics());
}
function toggleBluetooth() {
if (bluetoothService)
refreshBluetooth(bluetoothService.toggle(!(bluetoothState.adapter && bluetoothState.adapter.state === "powered")));
}
function scanBluetooth() {
if (bluetoothService)
refreshBluetooth(bluetoothService.scan());
}
function bluetoothAction(kind, id) {
if (bluetoothService)
refreshBluetooth(bluetoothService.deviceAction(kind, id));
}
function bluetoothDeviceIcon(type) {
const t = String(type || "");
if (t.indexOf("gaming") >= 0)
return "\uF11B";
if (t.indexOf("headset") >= 0 || t.indexOf("headphone") >= 0)
return "\uF025";
if (t.indexOf("card") >= 0 || t.indexOf("speaker") >= 0 || t.indexOf("audio") >= 0)
return "\uF028";
if (t.indexOf("keyboard") >= 0)
return "\uF11C";
return "\uF293";
}
function bluetoothBatteryLabel(device) {
return device && device.battery >= 0 ? "\uF240 " + device.battery + "%" : "";
}
function networkFeedback() {
if (networkState.pending)
return "network action pending…";
if (networkState.scanning)
return "scanning for networks…";
const error = String(networkState.error || "");
if (error === "no-wifi-device")
return "no wireless device";
if (error === "scan-failed")
return "network scan failed";
if (error === "action-unavailable")
return "network action unavailable";
if (error.length > 0)
return "network action failed";
return "";
}
function bluetoothFeedback() {
if (bluetoothState.adapter && bluetoothState.adapter.discovering)
return "scanning for bluetooth devices…";
return String(bluetoothState.error || "").length > 0 ? "bluetooth action failed" : "";
}
function roleColor(role) {
if (!theme)
return "white";
if (role === "warning")
return theme.warning;
if (role === "error")
return theme.error;
if (role === "ui.minor")
return theme.ui.minor;
if (role === "content.accent")
return theme.content.accent;
return theme.ui.accent;
}
spacing: 10
width: parent ? parent.width : 0
StableListModel {
id: wifiRows
rows: root.networkState.networks || []
}
// Async reads (scan retries, post-action refresh) update the service
// model and emit refreshed(); re-pull stateJson so the scan spinner
// clears and new/connected rows land without a user action.
Connections {
target: root.networkService
function onRefreshed() {
if (root.networkService)
root.refreshNetwork(root.networkService.stateJson());
}
}
Connections {
target: root.bluetoothService
function onRefreshed() {
if (root.bluetoothService)
root.refreshBluetooth(root.bluetoothService.stateJson());
}
}
// One scroll pane for the whole panel: a long Wi-Fi scan + the
// Bluetooth list scroll together, and an expanded row's detail drawer
// grows inside it without clipping. Idle (no overflow) => not interactive.
Flickable {
id: networkScroll
width: parent.width
height: root.height
contentWidth: width
contentHeight: panelCol.height
clip: true
interactive: contentHeight > height
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick
Column {
id: panelCol
width: parent.width
spacing: 10
Row {
objectName: "networkRateRow"
width: parent.width
spacing: 8
NuguText {
theme: root.theme
text: root.resourceService ? `↓ ${root.formatRate(root.resourceService.networkRxRate)}` : "↓ 0 B/s"
color: root.theme ? root.theme.ui.accent : "white"
}
NuguText {
theme: root.theme
text: root.resourceService ? `↑ ${root.formatRate(root.resourceService.networkTxRate)}` : "↑ 0 B/s"
color: root.theme ? root.theme.ui.focus : "white"
}
}
NuguText {
visible: root.networkFeedback().length > 0
width: parent.width
theme: root.theme
variant: "meta"
text: root.networkFeedback()
color: root.networkState.error ? root.roleColor("error") : root.roleColor("ui.minor")
wrapMode: Text.WordWrap
}
Column {
objectName: "wifiToggleScanList"
width: parent.width
spacing: 6
Row {
width: parent.width
spacing: 6
NuguText {
theme: root.theme
variant: "title"
text: "wireless"
width: parent.width - 132
}
PopupButton {
theme: root.theme
width: 58
height: 26
text: root.networkState.wifi && root.networkState.wifi.enabled ? "on" : "off"
checked: root.networkState.wifi && root.networkState.wifi.enabled
onClicked: root.toggleWifi()
}
PopupButton {
theme: root.theme
width: 58
height: 26
text: root.networkState.scanning ? "scan…" : "scan"
checked: root.networkState.scanning
onClicked: root.scanWifi()
}
}
Column {
width: parent.width
spacing: 6
Repeater {
model: wifiRows
delegate: Column {
id: wifiRow
required property var rowData
readonly property bool active: rowData.connected === true
readonly property bool revealed: root.expandedSsid === rowData.ssid
onRevealedChanged: if (!revealed)
wifiPp.text = ""
readonly property var detail: root.networkState.connectionDetail || ({})
width: parent.width
spacing: 4
PopupCard {
theme: root.theme
padding: 0
active: wifiRow.active
revealed: wifiRow.revealed
attention: wifiRow.rowData.state === "connecting"
interactive: true
accessibleName: wifiRow.revealed ? `collapse ${wifiRow.rowData.ssid}` : `expand ${wifiRow.rowData.ssid}`
tooltip: accessibleName
width: parent.width
height: 44
onClicked: root.expandRow(wifiRow.rowData)
Text {
id: wifiGlyph
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
font.family: "Symbols Nerd Font Mono"
font.pixelSize: 18
text: wifiRow.rowData.signalGlyph || ""
color: root.roleColor(wifiRow.rowData.signalRole || "ui.minor")
}
// chevron hints the row expands; rotates when open
Text {
id: wifiChevron
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
font.family: "Symbols Nerd Font Mono"
font.pixelSize: 16
text: ""
rotation: wifiRow.revealed ? 90 : 0
color: root.roleColor(wifiRow.active ? "content.accent" : "ui.minor")
}
NuguText {
id: wifiLabel
anchors.right: wifiChevron.left
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
variant: "meta"
text: wifiRow.active ? "connected" : (wifiRow.rowData.securityLabel || "")
color: root.roleColor(wifiRow.active ? "content.accent" : "ui.minor")
}
NuguText {
anchors.left: wifiGlyph.right
anchors.leftMargin: 10
anchors.right: wifiLabel.left
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
text: wifiRow.rowData.ssid
elide: Text.ElideRight
}
}
// --- detail + actions drawer (revealed on tap) -----------
PopupCard {
id: wifiDrawer
visible: wifiRow.revealed
theme: root.theme
padding: 0
revealed: true
width: parent.width
height: visible ? drawerCol.height + 16 : 0
Column {
id: drawerCol
x: 12
y: 8
width: parent.width - 24
spacing: 6
// signal + security — available for every row
NuguText {
theme: root.theme
variant: "meta"
text: {
const dbm = wifiRow.rowData.signalDbm;
const sig = (dbm === null || dbm === undefined) ? `${wifiRow.rowData.signal}/4 bars` : `${dbm} dBm`;
return `${wifiRow.rowData.securityLabel || "open"} · ${sig}`;
}
color: root.theme ? root.theme.content.normal : "white"
}
// connected-only BSS detail (StationDiagnostic)
Column {
visible: wifiRow.active && !!wifiRow.detail.bssid
width: parent.width
spacing: 2
NuguText {
theme: root.theme
variant: "meta"
text: `${wifiRow.detail.band || "?"}${wifiRow.detail.channel ? " · ch " + wifiRow.detail.channel : ""}${wifiRow.detail.mode ? " · " + wifiRow.detail.mode : ""}`
opacity: 0.8
}
NuguText {
theme: root.theme
variant: "meta"
visible: !!wifiRow.detail.rxRate || !!wifiRow.detail.txRate
text: `↓ ${wifiRow.detail.rxRate || "?"} ↑ ${wifiRow.detail.txRate || "?"}`
opacity: 0.8
}
NuguText {
theme: root.theme
variant: "meta"
text: wifiRow.detail.bssid || ""
opacity: 0.6
}
}
// action pills — hidden when a passphrase is needed
Row {
visible: !wifiRow.rowData.needsPassphrase
spacing: 8
PopupButton {
visible: !wifiRow.active
theme: root.theme
width: 96
height: 28
text: "connect"
role: "primary"
onClicked: {
root.expandedSsid = "";
root.selectNetwork(wifiRow.rowData.id);
}
}
PopupButton {
visible: wifiRow.active
theme: root.theme
width: 96
height: 28
text: "disconnect"
role: "primary"
onClicked: {
root.expandedSsid = "";
root.disconnectNetwork();
}
}
PopupButton {
visible: wifiRow.active || wifiRow.rowData.known
theme: root.theme
width: 96
height: 28
text: "forget"
role: "destructive"
onClicked: {
root.expandedSsid = "";
root.forgetNetwork(wifiRow.rowData.id);
}
}
}
// --- inline passphrase entry for a NEW secured network.
// echoMode Password; text lives only here, cleared on
// submit, never bound into networkState / any logged field.
Item {
id: wifiPpRow
function submit() {
root.connectSecured(wifiRow.rowData.ssid, wifiPp.text);
wifiPp.text = "";
root.expandedSsid = "";
}
visible: wifiRow.rowData.needsPassphrase
width: parent.width
height: visible ? 38 : 0
onVisibleChanged: {
if (visible && wifiRow.revealed)
Qt.callLater(wifiPp.input.forceActiveFocus);
else
wifiPp.text = "";
}
PopupTextField {
id: wifiPp
anchors.left: parent.left
anchors.right: wifiPpConfirm.left
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
placeholderText: "passphrase"
echoMode: TextInput.Password
onAccepted: wifiPpRow.submit()
onCancelled: {
text = "";
root.expandedSsid = "";
}
}
PopupButton {
id: wifiPpConfirm
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
width: 64
height: 32
text: "join"
role: "primary"
onClicked: wifiPpRow.submit()
}
}
}
}
}
}
}
NuguText {
visible: !!(root.networkState.wifi && root.networkState.wifi.enabled) && (!root.networkState.networks || root.networkState.networks.length === 0)
theme: root.theme
variant: "meta"
text: root.networkState.scanning ? "scanning…" : "no networks"
opacity: 0.7
}
}
NuguText {
visible: root.bluetoothFeedback().length > 0
width: parent.width
theme: root.theme
variant: "meta"
text: root.bluetoothFeedback()
color: root.bluetoothState.error ? root.roleColor("error") : root.roleColor("ui.minor")
wrapMode: Text.WordWrap
}
PopupDivider {
theme: root.theme
width: parent.width
}
Column {
objectName: "bluetoothToggleScanList"
width: parent.width
spacing: 6
Row {
width: parent.width
spacing: 6
NuguText {
theme: root.theme
variant: "title"
text: "bluetooth"
width: parent.width - 132
}
PopupButton {
theme: root.theme
width: 58
height: 26
text: root.bluetoothState.adapter && root.bluetoothState.adapter.state === "powered" ? "on" : "off"
checked: root.bluetoothState.adapter && root.bluetoothState.adapter.state === "powered"
onClicked: root.toggleBluetooth()
}
PopupButton {
theme: root.theme
width: 58
height: 26
text: root.bluetoothState.adapter && root.bluetoothState.adapter.discovering ? "scan…" : "scan"
checked: root.bluetoothState.adapter && root.bluetoothState.adapter.discovering
onClicked: root.scanBluetooth()
}
}
Column {
width: parent.width
spacing: 6
Row {
id: bluetoothHeader
width: parent.width
height: 22
spacing: 6
Item {
width: 34
height: 1
}
NuguText {
theme: root.theme
variant: "meta"
text: "device"
width: bluetoothHeader.width - 34 - 78 - 84 - 96 - 68 - bluetoothHeader.spacing * 5
opacity: 0.75
}
NuguText {
theme: root.theme
variant: "meta"
text: "trust"
width: 78
horizontalAlignment: Text.AlignHCenter
opacity: 0.75
}
NuguText {
theme: root.theme
variant: "meta"
text: "state"
width: 84
horizontalAlignment: Text.AlignHCenter
opacity: 0.75
}
NuguText {
theme: root.theme
variant: "meta"
text: "battery"
width: 96
horizontalAlignment: Text.AlignRight
opacity: 0.75
}
NuguText {
theme: root.theme
variant: "meta"
text: "actions"
width: 68
horizontalAlignment: Text.AlignHCenter
opacity: 0.75
}
}
PopupDivider {
theme: root.theme
width: parent.width
}
Repeater {
model: root.bluetoothState.devices || []
delegate: PopupCard {
id: deviceRow
required property var modelData
theme: root.theme
padding: 0
active: modelData.connected
destructive: modelData.blocked
interactive: true
accessibleName: modelData.connected ? `disconnect ${modelData.name}` : `connect ${modelData.name}`
tooltip: accessibleName
width: root.width
height: 46
onClicked: root.bluetoothAction(modelData.connected ? "disconnect" : "connect", modelData.id)
Row {
id: deviceLine
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 6
Text {
width: 26
anchors.verticalCenter: parent.verticalCenter
font.family: "Symbols Nerd Font Mono"
font.pixelSize: 17
color: root.roleColor(deviceRow.modelData.connected ? "ui.accent" : "ui.minor")
text: root.bluetoothDeviceIcon(deviceRow.modelData.type)
horizontalAlignment: Text.AlignHCenter
}
NuguText {
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
text: deviceRow.modelData.name
width: deviceLine.width - 26 - 78 - 84 - 96 - 68 - deviceLine.spacing * 5
elide: Text.ElideRight
}
PopupBadge {
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
width: 78
text: deviceRow.modelData.trusted ? "trusted" : "untrusted"
icon: deviceRow.modelData.trusted ? "✓" : ""
role: deviceRow.modelData.trusted ? "active" : "normal"
}
PopupBadge {
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
width: 84
text: deviceRow.modelData.connected ? "linked" : "off"
icon: deviceRow.modelData.connected ? "●" : "○"
role: deviceRow.modelData.connected ? "active" : "normal"
}
NuguText {
objectName: "batteryBadges"
anchors.verticalCenter: parent.verticalCenter
theme: root.theme
variant: "mono"
width: 96
horizontalAlignment: Text.AlignRight
font.family: "Symbols Nerd Font Mono"
color: root.roleColor(deviceRow.modelData.battery >= 0 ? "content.accent" : "ui.minor")
text: root.bluetoothBatteryLabel(deviceRow.modelData)
}
Row {
anchors.verticalCenter: parent.verticalCenter
width: 68
spacing: 4
PopupButton {
theme: root.theme
width: 32
height: 28
role: "ghost"
showText: false
icon: deviceRow.modelData.trusted ? "" : ""
accessibleName: deviceRow.modelData.trusted ? "block device" : "trust device"
tooltip: accessibleName
onClicked: root.bluetoothAction(deviceRow.modelData.trusted ? "block" : "trust", deviceRow.modelData.id)
}
PopupButton {
theme: root.theme
width: 32
height: 28
role: "ghost"
showText: false
icon: ""
accessibleName: "forget device"
tooltip: accessibleName
onClicked: root.bluetoothAction("forget", deviceRow.modelData.id)
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.MiddleButton | Qt.RightButton
propagateComposedEvents: true
onClicked: event => {
if (event.button === Qt.MiddleButton)
root.bluetoothAction("forget", deviceRow.modelData.id);
else
root.bluetoothAction(deviceRow.modelData.trusted ? "block" : "trust", deviceRow.modelData.id);
}
}
}
}
}
}
}
}
}