Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/ConnectivityPanel.qml

Raw
pragma 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);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}