import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Services.SystemTray
import Quickshell.Widgets

ShellRoot {
  id: root

  property color bgColor: "#0f0f0f"
  property color fgColor: "#e0e0e0"
  property color dimColor: "#888888"
  property color accentColor: "#e67e22"
  property color okColor: "#2ecc71"
  property color warnColor: "#f1c40f"
  property color critColor: "#e74c3c"
  property color borderColor: "#333333"

  Process {
    id: shellExec
    running: false
  }

  function runShell(cmd) {
    shellExec.command = ["/usr/bin/env", "bash", "-lc", cmd]
    shellExec.running = true
  }

  function trim(s) {
    return (s || "").toString().trim()
  }

  property var wsNames: ({"1": "WEB", "2": "CODE", "3": "TERM", "4": "IDE", "5": "VM", "6": "GFX", "7": "DOC", "8": "GAME", "9": "MISC", "10": "TMP", "11": "AUX1", "12": "AUX2", "13": "AUX3", "14": "AUX4", "15": "AUX5", "special:termius": "s:term", "special:org": "s:org", "special:llm": "s:llm"})

  function setWsLabel(wsId, label) {
    var n = Object.assign({}, wsNames)
    n[wsId] = label
    wsNames = n
    wsNamesSaver.command = ["/usr/bin/env", "bash", "-c", "echo '" + JSON.stringify(n) + "' > ~/.config/quickshell/ws-names.json"]
    wsNamesSaver.running = true
  }

  Process {
    id: wsNamesLoader
    command: ["/usr/bin/env", "bash", "-c", "cat ~/.config/quickshell/ws-names.json 2>/dev/null || echo '{}'"]
    running: true
    stdout: StdioCollector {
      onStreamFinished: {
        try {
          var loaded = JSON.parse((this.text || "{}").trim())
          root.wsNames = Object.assign({}, root.wsNames, loaded)
        } catch(e) {}
      }
    }
  }

  Process {
    id: wsNamesSaver
    running: false
  }

  function wsIgnored(name) {
    return name === "chrome-sharing-indicator" || name === "special:chrome-sharing-indicator"
  }

  component ModuleBox: Rectangle {
    color: "#1a1a1a"
    border.width: 1
    border.color: root.borderColor
    radius: 0
    implicitHeight: 22
  }

  Variants {
    model: Quickshell.screens

    delegate: PanelWindow {
      id: panel
      required property var modelData

      screen: modelData
      color: "transparent"
      exclusionMode: ExclusionMode.Auto
      exclusiveZone: 34
      aboveWindows: true

      anchors {
        bottom: true
        left: true
        right: true
      }

      implicitHeight: 34

      property var hyprMonitor: Hyprland.monitorFor(modelData)
      visible: {{- if eq .deviceProfile "desktop" }}hyprMonitor && hyprMonitor.name === "{{ .primaryMonitor }}"{{- else }}true{{- end }}

      Rectangle {
        anchors.fill: parent
        anchors.margins: 4
        color: root.bgColor
        border.width: 1
        border.color: root.borderColor

        Row {
          id: mainRow
          anchors.fill: parent
          anchors.leftMargin: 6
          anchors.rightMargin: 6
          spacing: 0

          Row {
            id: leftGroup
            anchors.verticalCenter: parent.verticalCenter
            spacing: 6

            ModuleBox {
              id: workspacesBox
              implicitWidth: workspacesRow.implicitWidth + 12

              Row {
                id: workspacesRow
                anchors.centerIn: parent
                spacing: 6

                Repeater {
                  model: Hyprland.workspaces

                  delegate: Rectangle {
                    required property var modelData
                    property bool ignored: root.wsIgnored(modelData.name)

                    visible: !ignored
                    color: modelData.urgent
                      ? root.critColor
                      : (modelData.active ? root.accentColor : "transparent")
                    border.width: 1
                    border.color: modelData.urgent ? root.critColor : root.borderColor
                    radius: 0
                    implicitHeight: 18
                    implicitWidth: wsText.implicitWidth + 10

                    Text {
                      id: wsText
                      anchors.centerIn: parent
                      text: root.wsNames[modelData.name] || "WS"
                      color: modelData.active || modelData.urgent ? "#111111" : root.fgColor
                      font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                      font.pixelSize: 12
                    }

                    MouseArea {
                      anchors.fill: parent
                      acceptedButtons: Qt.LeftButton | Qt.RightButton
                      onClicked: function(mouse) {
                        if (mouse.button === Qt.RightButton) {
                          wsRenamePopup.wsId = modelData.name
                          wsRenamePopup.renameX = mapToItem(null, 0, 0).x
                          wsRenamePopup.visible = true
                          wsRenameInput.text = root.wsNames[modelData.name] || ""
                          wsRenameInput.selectAll()
                          wsRenameInput.forceActiveFocus()
                        } else {
                          modelData.activate()
                        }
                      }
                    }
                  }
                }
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.NoButton
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) {
                    Hyprland.dispatch("workspace e+1")
                  } else if (wheel.angleDelta.y < 0) {
                    Hyprland.dispatch("workspace e-1")
                  }
                }
              }
            }
          }

          Item {
            width: Math.max(0, panel.width - leftGroup.implicitWidth - rightGroup.implicitWidth - 12)
            height: 1
          }

          Row {
            id: rightGroup
            anchors.verticalCenter: parent.verticalCenter
            spacing: 6

            ModuleBox {
              id: vpnBox
              implicitWidth: vpnText.implicitWidth + 12
              property string vpnClass: "disconnected"
              property string wgRaw: ""
              property string nbRaw: ""
              property string tsRaw: ""
              property int vpnPending: 3

              function updateVpnStatus() {
                var parts = []
                var hasTunnel = false

                // Parse WireGuard interfaces (exclude NetBird wt*)
                var wgLines = wgRaw.split("\n")
                for (var i = 0; i < wgLines.length; i++) {
                  var m = wgLines[i].match(/^\d+:\s+([^:@\s]+)/)
                  if (m && !m[1].startsWith("wt")) {
                    parts.push("WG:" + m[1])
                    hasTunnel = true
                  }
                }

                // Parse NetBird
                try {
                  var nb = JSON.parse(nbRaw)
                  if (nb.management && nb.management.connected) {
                    var fqdn = nb.fqdn || ""
                    var segs = fqdn.split(".")
                    var net = segs.length >= 2 ? segs[segs.length - 2] : "nb"
                    var conn = (nb.peers && nb.peers.connected) || 0
                    var tot = (nb.peers && nb.peers.total) || 0
                    parts.push("NB:" + net + "(" + conn + "/" + tot + ")")
                  }
                } catch(e) {}

                // Parse Tailscale
                try {
                  var ts = JSON.parse(tsRaw)
                  if (ts.BackendState === "Running") {
                    var tailnet = (ts.CurrentTailnet && ts.CurrentTailnet.Name) || ""
                    var shortNet = tailnet.replace(/\.ts\.net$/, "").replace(/\.tail.*$/, "")
                    var exitId = (ts.ExitNodeStatus && ts.ExitNodeStatus.ID) || ""
                    var exitOnline = (ts.ExitNodeStatus && ts.ExitNodeStatus.Online) || false

                    if (exitId && exitOnline) {
                      var exitHost = "exit"
                      if (ts.Peer) {
                        var keys = Object.keys(ts.Peer)
                        for (var j = 0; j < keys.length; j++) {
                          if (ts.Peer[keys[j]].ExitNode) {
                            exitHost = ts.Peer[keys[j]].HostName || "exit"
                            break
                          }
                        }
                      }
                      parts.push("TS:" + shortNet + "→" + exitHost)
                      hasTunnel = true
                    } else {
                      parts.push("TS:" + shortNet)
                    }
                  }
                } catch(e) {}

                if (parts.length > 0) {
                  vpnText.text = parts.join(" | ")
                  vpnClass = hasTunnel ? "connected" : "connected-no-tunnel"
                } else {
                  vpnText.text = "VPN off"
                  vpnClass = "disconnected"
                }
              }

              Text {
                id: vpnText
                anchors.centerIn: parent
                text: "VPN off"
                color: vpnBox.vpnClass === "connected"
                  ? root.accentColor
                  : (vpnBox.vpnClass === "disconnected" ? root.dimColor : root.fgColor)
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: wgProc
                command: ["/usr/bin/env", "bash", "-c", "ip link show type wireguard 2>/dev/null || true"]
                running: true
                stdout: StdioCollector {
                  onStreamFinished: {
                    vpnBox.wgRaw = (this.text || "").trim()
                    vpnBox.vpnPending--
                    if (vpnBox.vpnPending <= 0) vpnBox.updateVpnStatus()
                  }
                }
              }

              Process {
                id: nbProc
                command: ["/usr/bin/env", "bash", "-c", "command -v netbird &>/dev/null && netbird status --json 2>/dev/null || echo '{}'"]
                running: true
                stdout: StdioCollector {
                  onStreamFinished: {
                    vpnBox.nbRaw = (this.text || "").trim()
                    vpnBox.vpnPending--
                    if (vpnBox.vpnPending <= 0) vpnBox.updateVpnStatus()
                  }
                }
              }

              Process {
                id: tsProc
                command: ["/usr/bin/env", "bash", "-c", "command -v tailscale &>/dev/null && tailscale status --json 2>/dev/null || echo '{}'"]
                running: true
                stdout: StdioCollector {
                  onStreamFinished: {
                    vpnBox.tsRaw = (this.text || "").trim()
                    vpnBox.vpnPending--
                    if (vpnBox.vpnPending <= 0) vpnBox.updateVpnStatus()
                  }
                }
              }

              Timer {
                interval: 5000
                running: true
                repeat: true
                onTriggered: {
                  vpnBox.vpnPending = 3
                  wgProc.running = true
                  nbProc.running = true
                  tsProc.running = true
                }
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton
                onClicked: {
                  vpnPopup.visible = !vpnPopup.visible
                  if (vpnPopup.visible) vpnPopup.refresh()
                }
              }
            }

            ModuleBox {
              id: spkBox
              implicitWidth: spkText.implicitWidth + 12

              Text {
                id: spkText
                anchors.centerIn: parent
                text: "VOL --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: spkProc
                command: ["/usr/bin/env", "bash", "-lc", "mute=$(pactl get-sink-mute @DEFAULT_SINK@ 2>/dev/null | awk '{print $2}'); vol=$(pactl get-sink-volume @DEFAULT_SINK@ 2>/dev/null | awk 'NR==1{print $5}'); if [ \"${mute}\" = \"yes\" ]; then echo 'VOL muted'; elif [ -n \"${vol}\" ]; then echo \"VOL ${vol}\"; else echo 'VOL --'; fi"]
                running: true
                stdout: StdioCollector { onStreamFinished: spkText.text = root.trim(this.text) || "VOL --" }
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: function(mouse) {
                  if (mouse.button === Qt.RightButton) {
                    root.runShell("pactl set-sink-mute @DEFAULT_SINK@ toggle")
                  } else {
                    sinkPopup.visible = !sinkPopup.visible
                    if (sinkPopup.visible) sinkPopup.refresh()
                  }
                }
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) {
                    root.runShell("pactl set-sink-volume @DEFAULT_SINK@ +2%")
                  } else if (wheel.angleDelta.y < 0) {
                    root.runShell("pactl set-sink-volume @DEFAULT_SINK@ -2%")
                  }
                }
              }
            }

            ModuleBox {
              id: micBox
              implicitWidth: micText.implicitWidth + 12

              Text {
                id: micText
                anchors.centerIn: parent
                text: "MIC --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: micProc
                command: ["/usr/bin/env", "bash", "-lc", "mute=$(pactl get-source-mute @DEFAULT_SOURCE@ 2>/dev/null | awk '{print $2}'); vol=$(pactl get-source-volume @DEFAULT_SOURCE@ 2>/dev/null | awk 'NR==1{print $5}'); if [ \"${mute}\" = \"yes\" ]; then echo 'MIC muted'; elif [ -n \"${vol}\" ]; then echo \"MIC ${vol}\"; else echo 'MIC --'; fi"]
                running: true
                stdout: StdioCollector { onStreamFinished: micText.text = root.trim(this.text) || "MIC --" }
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: function(mouse) {
                  if (mouse.button === Qt.RightButton) {
                    root.runShell("pactl set-source-mute @DEFAULT_SOURCE@ toggle")
                  } else {
                    sourcePopup.visible = !sourcePopup.visible
                    if (sourcePopup.visible) sourcePopup.refresh()
                  }
                }
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) {
                    root.runShell("pactl set-source-volume @DEFAULT_SOURCE@ +2%")
                  } else if (wheel.angleDelta.y < 0) {
                    root.runShell("pactl set-source-volume @DEFAULT_SOURCE@ -2%")
                  }
                }
              }
            }

            ModuleBox {
              id: netBox
              implicitWidth: netText.implicitWidth + 12

              Text {
                id: netText
                anchors.centerIn: parent
                text: "NET down"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: netProc
                command: ["/usr/bin/env", "bash", "-lc", "wifi=$(iwgetid -r 2>/dev/null || true); if [ -n \"$wifi\" ]; then sig=$(awk 'NR==3{gsub(/\./,\"\",$3); q=$3+0; printf \"%d\", int((q/70)*100)}' /proc/net/wireless 2>/dev/null); [ -z \"$sig\" ] && sig=0; echo \"NET wifi $wifi ${sig}%\"; exit; fi; iface=$(ip route | awk '/^default/{print $5; exit}'); if [ -n \"$iface\" ]; then ip4=$(ip -4 addr show dev \"$iface\" | awk '/inet /{print $2; exit}' | cut -d/ -f1); if [ -n \"$ip4\" ]; then echo \"NET eth $ip4\"; else echo 'NET link'; fi; else echo 'NET down'; fi"]
                running: true
                stdout: StdioCollector { onStreamFinished: netText.text = root.trim(this.text) || "NET down" }
              }

              Timer {
                interval: 5000
                running: true
                repeat: true
                onTriggered: netProc.running = true
              }
            }

{{- if .hasBattery }}
            ModuleBox {
              id: batteryBox
              implicitWidth: batteryText.implicitWidth + 12

              Text {
                id: batteryText
                anchors.centerIn: parent
                text: "BAT --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: batteryProc
                command: ["/usr/bin/env", "bash", "-lc", "cap=$(cat /sys/class/power_supply/BAT*/capacity 2>/dev/null | head -n1); stat=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1); if [ -z \"$cap\" ]; then echo 'BAT --'; exit; fi; if [ \"$stat\" = 'Charging' ]; then echo \"BAT+ ${cap}%\"; elif [ \"$stat\" = 'Full' ]; then echo 'BAT full'; elif [ \"$stat\" = 'Not charging' ] || [ \"$stat\" = 'Unknown' ]; then echo \"BAT= ${cap}%\"; else echo \"BAT ${cap}%\"; fi"]
                running: true
                stdout: StdioCollector {
                  onStreamFinished: {
                    const v = root.trim(this.text)
                    batteryText.text = v || "BAT --"
                    const m = v.match(/(\d+)%/)
                    if (v.startsWith("BAT+")) {
                      batteryText.color = root.okColor
                    } else if (m && Number(m[1]) <= 15) {
                      batteryText.color = root.critColor
                    } else if (m && Number(m[1]) <= 30) {
                      batteryText.color = root.warnColor
                    } else {
                      batteryText.color = root.fgColor
                    }
                  }
                }
              }

              Timer {
                interval: 10000
                running: true
                repeat: true
                onTriggered: batteryProc.running = true
              }
            }

            ModuleBox {
              id: brtBox
              implicitWidth: brtText.implicitWidth + 12

              Text {
                id: brtText
                anchors.centerIn: parent
                text: "BRT --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: brtProc
                command: ["/usr/bin/env", "bash", "-lc", "pct=$(brightnessctl -m 2>/dev/null | awk -F, '{print $4}' | tr -d '%'); if [ -n \"$pct\" ]; then echo \"BRT ${pct}%\"; else echo 'BRT --'; fi"]
                running: true
                stdout: StdioCollector { onStreamFinished: brtText.text = root.trim(this.text) || "BRT --" }
              }

              Timer {
                interval: 5000
                running: true
                repeat: true
                onTriggered: brtProc.running = true
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.NoButton
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) {
                    root.runShell("brightnessctl s 5%+")
                  } else if (wheel.angleDelta.y < 0) {
                    root.runShell("brightnessctl s 5%-")
                  }
                  brtProc.running = true
                }
              }
            }
{{- end }}

            ModuleBox {
              id: pomBox
              implicitWidth: pomText.implicitWidth + 12

              property string pomState: "idle"
              property int pomSeconds: 0
              property int pomCount: 0
              property int totalPoms: 0
              property int totalFocusMins: 0
              property bool pomBlink: false
              property int workMins: 25
              property int breakMins: 5
              property int longBreakMins: 15

              function fmt(s) {
                var m = Math.floor(s / 60)
                var ss = s % 60
                return (m < 10 ? "0" : "") + m + ":" + (ss < 10 ? "0" : "") + ss
              }

              function update() {
                if (pomState === "idle") {
                  pomText.text = "POM off"
                  pomText.color = root.dimColor
                  return
                }
                var c = pomState === "work" ? " " + (pomCount + 1) + "/4" : pomCount > 0 ? " " + pomCount + "/4" : ""
                if (pomState === "work") {
                  pomText.text = "POM " + fmt(pomSeconds) + c
                  pomText.color = pomSeconds <= 60 ? root.critColor : root.accentColor
                } else if (pomState === "break" || pomState === "longbreak") {
                  pomText.text = "BRK " + fmt(pomSeconds) + c
                  pomText.color = pomSeconds <= 60 ? root.warnColor : root.okColor
                } else if (pomState === "done") {
                  pomText.text = pomBlink ? ">>> GO <<<" : "    GO    "
                  pomText.color = root.critColor
                }
              }

              function startWork() {
                pomState = "work"
                pomSeconds = workMins * 60
                pomBlinkTimer.running = false
                pomBlink = false
                pomTimer.running = true
                pomFlash.dismiss()
                update()
              }

              function startBreak() {
                pomCount++
                totalPoms++
                totalFocusMins += workMins
                if (pomCount >= 4) {
                  pomState = "longbreak"
                  pomSeconds = longBreakMins * 60
                  pomCount = 0
                  pomFlash.show("LONG BREAK", root.okColor)
                } else {
                  pomState = "break"
                  pomSeconds = breakMins * 60
                  pomFlash.show("BREAK", root.okColor)
                }
                pomTimer.running = true
                update()
                root.runShell("notify-send -u normal 'Pomodoro' 'Take a break!'")
              }

              function goDone() {
                pomState = "done"
                pomTimer.running = false
                pomBlinkTimer.running = true
                pomFlash.show("GET BACK TO WORK", root.critColor)
                update()
                root.runShell("notify-send -u critical 'Pomodoro' 'Break over. Get back to work.'")
              }

              function reset() {
                pomState = "idle"
                pomSeconds = 0
                pomCount = 0
                pomBlink = false
                pomTimer.running = false
                pomBlinkTimer.running = false
                pomFlash.dismiss()
                update()
              }

              function saveSettings() {
                pomSettingsSaver.command = ["/usr/bin/env", "bash", "-c",
                  "echo '" + JSON.stringify({work: workMins, brk: breakMins, lng: longBreakMins}) + "' > ~/.config/quickshell/pom-settings.json"]
                pomSettingsSaver.running = true
              }

              Process {
                id: pomSettingsLoader
                command: ["/usr/bin/env", "bash", "-c", "cat ~/.config/quickshell/pom-settings.json 2>/dev/null || echo '{}'"]
                running: true
                stdout: StdioCollector {
                  onStreamFinished: {
                    try {
                      var s = JSON.parse((this.text || "{}").trim())
                      if (s.work) pomBox.workMins = s.work
                      if (s.brk) pomBox.breakMins = s.brk
                      if (s.lng) pomBox.longBreakMins = s.lng
                    } catch(e) {}
                  }
                }
              }

              Process {
                id: pomSettingsSaver
                running: false
              }

              Timer {
                id: pomTimer
                interval: 1000
                repeat: true
                onTriggered: {
                  pomBox.pomSeconds--
                  if (pomBox.pomSeconds <= 0) {
                    if (pomBox.pomState === "work") pomBox.startBreak()
                    else pomBox.goDone()
                  } else {
                    pomBox.update()
                  }
                }
              }

              Timer {
                id: pomBlinkTimer
                interval: 500
                repeat: true
                onTriggered: { pomBox.pomBlink = !pomBox.pomBlink; pomBox.update() }
              }

              Text {
                id: pomText
                anchors.centerIn: parent
                text: "POM off"
                color: root.dimColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: function(mouse) {
                  if (mouse.button === Qt.RightButton) {
                    pomPopup.visible = !pomPopup.visible
                  } else {
                    if (pomBox.pomState === "idle" || pomBox.pomState === "done")
                      pomBox.startWork()
                    else
                      pomTimer.running = !pomTimer.running
                  }
                }
              }
            }

            ModuleBox {
              id: memoBox
              implicitWidth: memoBoxText.implicitWidth + 12
              property int todayCount: 0

              Text {
                id: memoBoxText
                anchors.centerIn: parent
                text: memoBox.todayCount > 0 ? "MEMO " + memoBox.todayCount : "MEMO"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: memoCountProc
                command: ["/usr/bin/env", "bash", "-c", "today=$(date +%Y-%m-%d); grep -c \"$today\" ~/.local/share/quickmemo/notes.jsonl 2>/dev/null || echo 0"]
                running: true
                stdout: StdioCollector {
                  onStreamFinished: memoBox.todayCount = parseInt(root.trim(this.text)) || 0
                }
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: function(mouse) {
                  if (mouse.button === Qt.RightButton) {
                    memoReview.refresh()
                    memoReview.visible = !memoReview.visible
                  } else {
                    memoInput.visible = !memoInput.visible
                    if (memoInput.visible) {
                      memoInputField.text = ""
                      memoInputField.forceActiveFocus()
                    }
                  }
                }
              }
            }

            ModuleBox {
              id: cpuBox
              implicitWidth: cpuText.implicitWidth + 12

              Text {
                id: cpuText
                anchors.centerIn: parent
                text: "CPU --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: cpuProc
                command: ["/usr/bin/env", "bash", "-lc", "read -r _ u n s i _ < /proc/stat; used=$((u+n+s)); total=$((u+n+s+i)); if [ \"$total\" -gt 0 ]; then printf 'CPU %d%%\\n' $((used*100/total)); else echo 'CPU --'; fi"]
                running: true
                stdout: StdioCollector { onStreamFinished: cpuText.text = root.trim(this.text) || "CPU --" }
              }

              Timer {
                interval: 5000
                running: true
                repeat: true
                onTriggered: cpuProc.running = true
              }
            }

            ModuleBox {
              id: memBox
              implicitWidth: memText.implicitWidth + 12

              Text {
                id: memText
                anchors.centerIn: parent
                text: "MEM --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Process {
                id: memProc
                command: ["/usr/bin/env", "bash", "-lc", "free | awk '/Mem:/ { if ($2 > 0) printf \"MEM %d%%\\n\", int($3*100/$2); else print \"MEM --\"; }'"]
                running: true
                stdout: StdioCollector { onStreamFinished: memText.text = root.trim(this.text) || "MEM --" }
              }

              Timer {
                interval: 5000
                running: true
                repeat: true
                onTriggered: memProc.running = true
              }
            }

            ModuleBox {
              id: clockBox
              implicitWidth: clockText.implicitWidth + 12

              Text {
                id: clockText
                anchors.centerIn: parent
                text: "TIME --"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              Timer {
                interval: 1000
                running: true
                repeat: true
                onTriggered: {
                  const now = new Date()
                  clockText.text = Qt.formatDateTime(now, "'TIME' yyyy-MM-dd HH:mm")
                }
              }

              Component.onCompleted: {
                const now = new Date()
                clockText.text = Qt.formatDateTime(now, "'TIME' yyyy-MM-dd HH:mm")
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton
                onClicked: {
                  calPopup.visible = !calPopup.visible
                  if (calPopup.visible) calPopup.resetToToday()
                }
              }
            }

            ModuleBox {
              id: trayBox
              implicitWidth: trayRow.implicitWidth + 12

              Row {
                id: trayRow
                anchors.centerIn: parent
                spacing: 4

                Repeater {
                  model: SystemTray.items

                  delegate: Rectangle {
                    required property var modelData
                    color: "transparent"
                    border.width: 0
                    implicitWidth: 18
                    implicitHeight: 18

                    IconImage {
                      anchors.fill: parent
                      source: modelData.icon
                      asynchronous: true
                    }

                    MouseArea {
                      anchors.fill: parent
                      acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
                      onClicked: function(mouse) {
                        if (mouse.button === Qt.RightButton && modelData.hasMenu) {
                          modelData.display(panel, mouse.x, mouse.y)
                        } else if (mouse.button === Qt.MiddleButton) {
                          modelData.secondaryActivate()
                        } else {
                          modelData.activate()
                        }
                      }
                      onWheel: function(wheel) {
                        if (wheel.angleDelta.y > 0) {
                          modelData.scroll(1, false)
                        } else if (wheel.angleDelta.y < 0) {
                          modelData.scroll(-1, false)
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

      Process {
        id: paSubscribe
        command: ["/usr/bin/env", "bash", "-c", "pactl subscribe 2>/dev/null"]
        running: true
        stdout: SplitParser {
          onRead: function(line) {
            if (line.indexOf("sink") >= 0 || line.indexOf("server") >= 0)
              spkDebounce.restart()
            if (line.indexOf("source") >= 0 || line.indexOf("server") >= 0)
              micDebounce.restart()
          }
        }
        onRunningChanged: {
          if (!running) paResubscribe.running = true
        }
      }

      Timer {
        id: paResubscribe
        interval: 2000
        repeat: false
        onTriggered: paSubscribe.running = true
      }

      Timer {
        id: spkDebounce
        interval: 50
        repeat: false
        onTriggered: spkProc.running = true
      }

      Timer {
        id: micDebounce
        interval: 50
        repeat: false
        onTriggered: micProc.running = true
      }

      Timer {
        interval: 10000
        running: true
        repeat: true
        onTriggered: { spkProc.running = true; micProc.running = true }
      }

      Timer {
        interval: 150
        running: true
        repeat: true
        onTriggered: memoTriggerCheck.running = true
      }

      Process {
        id: memoTriggerCheck
        command: ["/usr/bin/env", "bash", "-c", "r=''; [ -f /tmp/qs-memo-input ] && rm /tmp/qs-memo-input && r=\"input\n\"; [ -f /tmp/qs-memo-clip ] && rm /tmp/qs-memo-clip && r=\"${r}clipboard\"; printf '%s' \"$r\""]
        running: false
        stdout: StdioCollector {
          onStreamFinished: {
            var t = (this.text || "").trim()
            if (t.indexOf("input") >= 0) {
              memoInput.visible = true
              memoInputField.text = ""
              memoInputField.forceActiveFocus()
            }
            if (t.indexOf("clipboard") >= 0)
              memoClipCapture.running = true
          }
        }
      }

      Process {
        id: memoClipCapture
        command: ["/usr/bin/env", "bash", "-c", "wl-paste 2>/dev/null || true"]
        running: false
        stdout: StdioCollector {
          onStreamFinished: {
            var text = (this.text || "").trim()
            if (text) {
              panel.memoSave(text, "clipboard")
              root.runShell("notify-send -r 91192 -t 2000 'Quick Memo' 'Clipboard saved'")
            }
          }
        }
      }

      function memoSave(text, src) {
        var entry = JSON.stringify({ts: new Date().toISOString(), text: text, src: src || "input"})
        var b64 = Qt.btoa(entry)
        memoSaveProc.command = ["/usr/bin/env", "bash", "-c",
          "mkdir -p ~/.local/share/quickmemo && printf '%s' " + b64 + " | base64 -d >> ~/.local/share/quickmemo/notes.jsonl && printf '\\n' >> ~/.local/share/quickmemo/notes.jsonl"]
        memoSaveProc.running = true
        memoBox.todayCount++
      }

      Process {
        id: memoSaveProc
        running: false
      }

      PanelWindow {
        id: sinkPopup
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property var sinks: []
        property string defaultSink: ""

        function refresh() {
          sinkListProc.running = true
        }

        function parseSinks(raw) {
          var sections = raw.split("---DEF---")
          var listRaw = (sections[0] || "").replace("---LIST---", "").trim()
          var defRaw = (sections[1] || "").trim()
          defaultSink = defRaw

          var items = []
          var lines = listRaw.split("\n")
          for (var i = 0; i < lines.length; i++) {
            var line = lines[i].trim()
            if (!line) continue
            var tabIdx = line.indexOf("\t")
            if (tabIdx < 0) continue
            var name = line.substring(0, tabIdx)
            var desc = line.substring(tabIdx + 1)
            items.push({ name: name, desc: desc, active: name === defRaw })
          }
          sinks = items
        }

        function setDefault(name) {
          sinkActionProc.command = ["/usr/bin/env", "bash", "-c", "pactl set-default-sink '" + name + "'"]
          sinkActionProc.running = true
          visible = false
          sinkRefreshTimer.running = true
        }

        Process {
          id: sinkListProc
          command: ["/usr/bin/env", "bash", "-c", "echo '---LIST---'; pactl list sinks 2>/dev/null | awk '/^\\tName:/{name=$2} /^\\tDescription:/{sub(/^\\tDescription: /,\"\"); print name\"\\t\"$0}'; echo '---DEF---'; pactl get-default-sink 2>/dev/null"]
          running: false
          stdout: StdioCollector {
            onStreamFinished: sinkPopup.parseSinks((this.text || "").trim())
          }
        }

        Process {
          id: sinkActionProc
          running: false
        }

        Timer {
          id: sinkRefreshTimer
          interval: 500
          running: false
          repeat: false
          onTriggered: spkProc.running = true
        }

        MouseArea {
          anchors.fill: parent
          onClicked: sinkPopup.visible = false
        }

        Rectangle {
          x: parent.width - 10 - rightGroup.implicitWidth + vpnBox.implicitWidth + 6
          y: parent.height - 38 - height
          width: Math.max(250, sinkCol.implicitWidth + 16)
          height: sinkCol.implicitHeight + 16
          color: root.bgColor
          border.width: 1
          border.color: root.borderColor

          MouseArea {
            anchors.fill: parent
          }

          Column {
            id: sinkCol
            anchors.fill: parent
            anchors.margins: 8
            spacing: 2
            z: 1

            Text {
              text: "━━ Output Devices ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 2
              bottomPadding: 2
            }

            Repeater {
              model: sinkPopup.sinks

              delegate: Rectangle {
                required property var modelData
                width: sinkCol.width
                height: 24
                color: sinkItemMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2

                Text {
                  anchors.verticalCenter: parent.verticalCenter
                  leftPadding: 8
                  text: (modelData.active ? "●" : "○") + "  " + modelData.desc
                  color: modelData.active ? root.accentColor : root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                  elide: Text.ElideRight
                  width: sinkCol.width - 8
                }

                MouseArea {
                  id: sinkItemMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: sinkPopup.setDefault(modelData.name)
                }
              }
            }
          }
        }
      }

      PanelWindow {
        id: sourcePopup
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property var sources: []
        property string defaultSource: ""

        function refresh() {
          sourceListProc.running = true
        }

        function parseSources(raw) {
          var sections = raw.split("---DEF---")
          var listRaw = (sections[0] || "").replace("---LIST---", "").trim()
          var defRaw = (sections[1] || "").trim()
          defaultSource = defRaw

          var items = []
          var lines = listRaw.split("\n")
          for (var i = 0; i < lines.length; i++) {
            var line = lines[i].trim()
            if (!line) continue
            var tabIdx = line.indexOf("\t")
            if (tabIdx < 0) continue
            var name = line.substring(0, tabIdx)
            var desc = line.substring(tabIdx + 1)
            items.push({ name: name, desc: desc, active: name === defRaw })
          }
          sources = items
        }

        function setDefault(name) {
          sourceActionProc.command = ["/usr/bin/env", "bash", "-c", "pactl set-default-source '" + name + "'"]
          sourceActionProc.running = true
          visible = false
          sourceRefreshTimer.running = true
        }

        Process {
          id: sourceListProc
          command: ["/usr/bin/env", "bash", "-c", "echo '---LIST---'; pactl list sources 2>/dev/null | awk '/^\\tName:/{name=$2} /^\\tDescription:/{sub(/^\\tDescription: /,\"\"); print name\"\\t\"$0}' | grep -v '\\.monitor'; echo '---DEF---'; pactl get-default-source 2>/dev/null"]
          running: false
          stdout: StdioCollector {
            onStreamFinished: sourcePopup.parseSources((this.text || "").trim())
          }
        }

        Process {
          id: sourceActionProc
          running: false
        }

        Timer {
          id: sourceRefreshTimer
          interval: 500
          running: false
          repeat: false
          onTriggered: micProc.running = true
        }

        MouseArea {
          anchors.fill: parent
          onClicked: sourcePopup.visible = false
        }

        Rectangle {
          x: parent.width - 10 - rightGroup.implicitWidth + vpnBox.implicitWidth + spkBox.implicitWidth + 12
          y: parent.height - 38 - height
          width: Math.max(250, sourceCol.implicitWidth + 16)
          height: sourceCol.implicitHeight + 16
          color: root.bgColor
          border.width: 1
          border.color: root.borderColor

          MouseArea {
            anchors.fill: parent
          }

          Column {
            id: sourceCol
            anchors.fill: parent
            anchors.margins: 8
            spacing: 2
            z: 1

            Text {
              text: "━━ Input Devices ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 2
              bottomPadding: 2
            }

            Repeater {
              model: sourcePopup.sources

              delegate: Rectangle {
                required property var modelData
                width: sourceCol.width
                height: 24
                color: srcItemMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2

                Text {
                  anchors.verticalCenter: parent.verticalCenter
                  leftPadding: 8
                  text: (modelData.active ? "●" : "○") + "  " + modelData.desc
                  color: modelData.active ? root.accentColor : root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                  elide: Text.ElideRight
                  width: sourceCol.width - 8
                }

                MouseArea {
                  id: srcItemMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: sourcePopup.setDefault(modelData.name)
                }
              }
            }
          }
        }
      }

      PanelWindow {
        id: calPopup
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property int calYear: new Date().getFullYear()
        property int calMonth: new Date().getMonth()
        property var calDays: []

        function resetToToday() {
          var now = new Date()
          calYear = now.getFullYear()
          calMonth = now.getMonth()
          buildDays()
        }

        function changeMonth(delta) {
          var m = calMonth + delta
          var y = calYear
          while (m < 0) { m += 12; y-- }
          while (m > 11) { m -= 12; y++ }
          calMonth = m
          calYear = y
          buildDays()
        }

        function buildDays() {
          var first = new Date(calYear, calMonth, 1)
          var startDow = (first.getDay() + 6) % 7
          var daysInMonth = new Date(calYear, calMonth + 1, 0).getDate()
          var prevMonthDays = new Date(calYear, calMonth, 0).getDate()

          var today = new Date()
          var isCurrentMonth = (today.getFullYear() === calYear && today.getMonth() === calMonth)
          var todayDate = today.getDate()

          var cells = []
          for (var i = 0; i < startDow; i++)
            cells.push({ day: prevMonthDays - startDow + 1 + i, current: false, today: false })
          for (var d = 1; d <= daysInMonth; d++)
            cells.push({ day: d, current: true, today: isCurrentMonth && d === todayDate })
          var remainder = 42 - cells.length
          for (var r = 1; r <= remainder; r++)
            cells.push({ day: r, current: false, today: false })

          calDays = cells
        }

        MouseArea {
          anchors.fill: parent
          onClicked: calPopup.visible = false
        }

        Rectangle {
          id: calRect
          x: parent.width - 16 - clockBox.implicitWidth - trayBox.implicitWidth
          y: parent.height - 38 - height
          width: 7 * 30 + 16
          height: calCol.implicitHeight + 16
          color: root.bgColor
          border.width: 1
          border.color: root.borderColor

          MouseArea {
            anchors.fill: parent
            onWheel: function(wheel) {
              if (wheel.angleDelta.y > 0) calPopup.changeMonth(-1)
              else if (wheel.angleDelta.y < 0) calPopup.changeMonth(1)
            }
          }

          Column {
            id: calCol
            anchors.fill: parent
            anchors.margins: 8
            spacing: 4
            z: 1

            Row {
              width: parent.width
              height: 24

              Rectangle {
                width: 30
                height: 24
                color: calPrevMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2

                Text {
                  anchors.centerIn: parent
                  text: "◄"
                  color: root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                }

                MouseArea {
                  id: calPrevMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: calPopup.changeMonth(-1)
                }
              }

              Item {
                width: parent.width - 60
                height: 24

                Text {
                  anchors.centerIn: parent
                  text: Qt.formatDate(new Date(calPopup.calYear, calPopup.calMonth, 1), "MMMM yyyy")
                  color: root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                }
              }

              Rectangle {
                width: 30
                height: 24
                color: calNextMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2

                Text {
                  anchors.centerIn: parent
                  text: "►"
                  color: root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                }

                MouseArea {
                  id: calNextMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: calPopup.changeMonth(1)
                }
              }
            }

            Grid {
              columns: 7
              spacing: 0
              width: parent.width

              Repeater {
                model: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]

                delegate: Item {
                  required property var modelData
                  width: 30
                  height: 20

                  Text {
                    anchors.centerIn: parent
                    text: modelData
                    color: root.dimColor
                    font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                    font.pixelSize: 11
                  }
                }
              }
            }

            Grid {
              columns: 7
              spacing: 0
              width: parent.width

              Repeater {
                model: calPopup.calDays

                delegate: Rectangle {
                  required property var modelData
                  width: 30
                  height: 24
                  radius: 2
                  color: modelData.today ? root.accentColor : "transparent"

                  Text {
                    anchors.centerIn: parent
                    text: modelData.day
                    color: modelData.today
                      ? "#111111"
                      : (modelData.current ? root.fgColor : root.dimColor)
                    font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                    font.pixelSize: 12
                  }
                }
              }
            }
          }
        }
      }

      PanelWindow {
        id: wsRenamePopup
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true
        focusable: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property string wsId: ""
        property real renameX: 0

        MouseArea {
          anchors.fill: parent
          onClicked: wsRenamePopup.visible = false
        }

        Rectangle {
          x: wsRenamePopup.renameX
          y: parent.height - 72
          width: 180
          height: 26
          color: root.bgColor
          border.width: 1
          border.color: root.accentColor
          radius: 0

          MouseArea {
            anchors.fill: parent
          }

          TextInput {
            id: wsRenameInput
            anchors.fill: parent
            anchors.leftMargin: 6
            anchors.rightMargin: 6
            verticalAlignment: TextInput.AlignVCenter
            color: root.fgColor
            selectionColor: root.accentColor
            selectedTextColor: "#111111"
            font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
            font.pixelSize: 12
            maximumLength: 12
            z: 1

            onAccepted: {
              if (text.trim())
                root.setWsLabel(wsRenamePopup.wsId, text.trim())
              wsRenamePopup.visible = false
            }

            Keys.onEscapePressed: wsRenamePopup.visible = false
          }
        }
      }

      PanelWindow {
        id: vpnPopup
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property var wgConfigs: []
        property var wgActive: []
        property var tsAccounts: []
        property var tsExits: []
        property bool tsRunning: false
        property string tsExitId: ""
        property int popupPending: 0

        function refresh() {
          popupPending = 2
          popupWgProc.running = true
          popupTsProc.running = true
        }

        function parseWgData(raw) {
          var sections = raw.split("---A---")
          var configsRaw = (sections[0] || "").replace("---C---", "").trim()
          var activeRaw = (sections[1] || "").trim()
          wgConfigs = configsRaw ? configsRaw.split("\n").filter(function(s) { return s.trim() }) : []
          wgActive = activeRaw ? activeRaw.split(/\s+/).filter(function(s) { return s.trim() }) : []
        }

        function parseTsData(raw) {
          var statusSplit = raw.split("---L---")
          var statusRaw = (statusSplit[0] || "").replace("---S---", "").trim()
          var rest = statusSplit[1] || ""
          var exitSplit = rest.split("---E---")
          var accountsRaw = (exitSplit[0] || "").trim()
          var exitsRaw = (exitSplit[1] || "").trim()

          try {
            var status = JSON.parse(statusRaw)
            tsRunning = status.BackendState === "Running"
            tsExitId = (status.ExitNodeStatus && status.ExitNodeStatus.ID) || ""
          } catch(e) {
            tsRunning = false
            tsExitId = ""
          }

          var accs = []
          var accLines = accountsRaw.split("\n")
          for (var i = 1; i < accLines.length; i++) {
            var parts = accLines[i].trim().split(/\s+/)
            if (parts.length >= 3) {
              var acct = parts[2]
              var isActive = acct.endsWith("*")
              if (isActive) acct = acct.slice(0, -1)
              accs.push({ id: parts[0], tailnet: parts[1], account: acct, active: isActive })
            }
          }
          tsAccounts = accs

          var exits = []
          var exitLines = exitsRaw.split("\n")
          for (var j = 0; j < exitLines.length; j++) {
            var line = exitLines[j].trim()
            if (!line || line.startsWith("IP") || line.startsWith("#") || line.startsWith("-")) continue
            var ep = line.split(/\s+/)
            if (ep.length >= 4) {
              var st = ep.slice(4).join(" ")
              if (st.toLowerCase().indexOf("offline") >= 0) continue
              exits.push({ ip: ep[0], hostname: ep[1], short: ep[1].split(".")[0], country: ep[2], city: ep[3] })
            }
          }
          tsExits = exits
        }

        function vpnAction(cmd) {
          vpnActionProc.command = ["/usr/bin/env", "bash", "-c", cmd]
          vpnActionProc.running = true
          visible = false
          vpnRefreshTimer.running = true
        }

        Process {
          id: vpnActionProc
          running: false
        }

        Process {
          id: popupWgProc
          command: ["/usr/bin/env", "bash", "-c", "echo '---C---'; vpndir='{{ .secretsPath }}/vpn'; [ -d \"$vpndir\" ] || vpndir=\"$HOME/cfg/vpn\"; for f in \"$vpndir\"/*.conf; do [ -f \"$f\" ] && basename \"$f\" .conf; done; echo '---A---'; wg show interfaces 2>/dev/null || true"]
          running: false
          stdout: StdioCollector {
            onStreamFinished: {
              vpnPopup.parseWgData((this.text || "").trim())
              vpnPopup.popupPending--
            }
          }
        }

        Process {
          id: popupTsProc
          command: ["/usr/bin/env", "bash", "-c", "echo '---S---'; command -v tailscale &>/dev/null && tailscale status --json 2>/dev/null || echo '{}'; echo '---L---'; command -v tailscale &>/dev/null && tailscale switch --list 2>/dev/null || true; echo '---E---'; command -v tailscale &>/dev/null && tailscale exit-node list 2>/dev/null || true"]
          running: false
          stdout: StdioCollector {
            onStreamFinished: {
              vpnPopup.parseTsData((this.text || "").trim())
              vpnPopup.popupPending--
            }
          }
        }

        Timer {
          id: vpnRefreshTimer
          interval: 1500
          running: false
          repeat: false
          onTriggered: {
            vpnBox.vpnPending = 3
            wgProc.running = true
            nbProc.running = true
            tsProc.running = true
          }
        }

        MouseArea {
          anchors.fill: parent
          onClicked: vpnPopup.visible = false
        }

        Rectangle {
          id: vpnPopupRect
          x: parent.width - 10 - rightGroup.implicitWidth
          y: parent.height - 38 - height
          width: 300
          height: vpnPopupCol.implicitHeight + 16
          color: root.bgColor
          border.width: 1
          border.color: root.borderColor

          MouseArea {
            anchors.fill: parent
          }

          Column {
            id: vpnPopupCol
            anchors.fill: parent
            anchors.margins: 8
            spacing: 2
            z: 1

            Text {
              text: "━━ WireGuard ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 2
              bottomPadding: 2
            }

            Repeater {
              model: vpnPopup.wgConfigs

              delegate: Rectangle {
                required property var modelData
                width: vpnPopupCol.width
                height: 24
                color: wgItemMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2
                property bool active: vpnPopup.wgActive.indexOf(modelData) >= 0

                Text {
                  anchors.verticalCenter: parent.verticalCenter
                  leftPadding: 8
                  text: (parent.active ? "●" : "○") + "  " + modelData
                  color: parent.active ? root.accentColor : root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                }

                MouseArea {
                  id: wgItemMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: {
                    var dir = "{{ .secretsPath }}/vpn"
                    if (parent.active) {
                      vpnPopup.vpnAction("sudo ~/.local/bin/vpn-helper wg-down \"" + dir + "/" + modelData + ".conf\"")
                    } else {
                      vpnPopup.vpnAction("for i in $(wg show interfaces 2>/dev/null); do sudo ~/.local/bin/vpn-helper wg-down \"$i\" 2>/dev/null; done; tailscale set --exit-node= 2>/dev/null; sudo ~/.local/bin/vpn-helper wg-up \"" + dir + "/" + modelData + ".conf\"")
                    }
                  }
                }
              }
            }

            Text {
              text: "━━ Tailscale ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 6
              bottomPadding: 2
            }

            Repeater {
              model: vpnPopup.tsAccounts

              delegate: Rectangle {
                required property var modelData
                width: vpnPopupCol.width
                height: 24
                color: tsAcctMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2

                Text {
                  anchors.verticalCenter: parent.verticalCenter
                  leftPadding: 8
                  text: (modelData.active ? "●" : "○") + "  " + modelData.tailnet + " (" + modelData.account + ")"
                  color: modelData.active ? root.accentColor : root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                }

                MouseArea {
                  id: tsAcctMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: {
                    if (!modelData.active)
                      vpnPopup.vpnAction("tailscale switch '" + modelData.id + "'")
                  }
                }
              }
            }

            Rectangle {
              visible: !vpnPopup.tsRunning
              width: vpnPopupCol.width
              height: 24
              color: tsStartMouse.containsMouse ? "#2a2a2a" : "transparent"
              radius: 2

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "○  Start Tailscale"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                id: tsStartMouse
                anchors.fill: parent
                hoverEnabled: true
                onClicked: vpnPopup.vpnAction("tailscale up")
              }
            }

            Text {
              visible: vpnPopup.tsRunning
              text: "━━ Exit Nodes ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 6
              bottomPadding: 2
            }

            Rectangle {
              visible: vpnPopup.tsRunning && vpnPopup.tsExitId !== ""
              width: vpnPopupCol.width
              height: 24
              color: exitOffMouse.containsMouse ? "#2a2a2a" : "transparent"
              radius: 2

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "●  Disconnect Exit Node"
                color: root.accentColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                id: exitOffMouse
                anchors.fill: parent
                hoverEnabled: true
                onClicked: vpnPopup.vpnAction("tailscale set --exit-node=")
              }
            }

            Repeater {
              model: vpnPopup.tsRunning ? vpnPopup.tsExits : []

              delegate: Rectangle {
                required property var modelData
                width: vpnPopupCol.width
                height: 24
                color: exitNodeMouse.containsMouse ? "#2a2a2a" : "transparent"
                radius: 2

                Text {
                  anchors.verticalCenter: parent.verticalCenter
                  leftPadding: 8
                  text: "○  " + modelData.short + " (" + modelData.country + "/" + modelData.city + ")"
                  color: root.fgColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: 12
                }

                MouseArea {
                  id: exitNodeMouse
                  anchors.fill: parent
                  hoverEnabled: true
                  onClicked: {
                    vpnPopup.vpnAction("for i in $(wg show interfaces 2>/dev/null); do sudo ~/.local/bin/vpn-helper wg-down \"$i\" 2>/dev/null; done; tailscale set --exit-node='" + modelData.ip + "'")
                  }
                }
              }
            }

            Rectangle {
              visible: vpnPopup.tsRunning
              width: vpnPopupCol.width
              height: 24
              color: tsStopMouse.containsMouse ? "#2a2a2a" : "transparent"
              radius: 2

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "⊘  Stop Tailscale"
                color: root.dimColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                id: tsStopMouse
                anchors.fill: parent
                hoverEnabled: true
                onClicked: vpnPopup.vpnAction("tailscale down")
              }
            }

            Rectangle {
              width: vpnPopupCol.width
              height: 1
              color: root.borderColor
            }

            Rectangle {
              width: vpnPopupCol.width
              height: 24
              color: disconnAllMouse.containsMouse ? "#2a2a2a" : "transparent"
              radius: 2

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "⊘  Disconnect All"
                color: root.critColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                id: disconnAllMouse
                anchors.fill: parent
                hoverEnabled: true
                onClicked: {
                  vpnPopup.vpnAction("for i in $(wg show interfaces 2>/dev/null); do sudo ~/.local/bin/vpn-helper wg-down \"$i\" 2>/dev/null; done; tailscale set --exit-node= 2>/dev/null || true")
                }
              }
            }
          }
        }
      }
      PanelWindow {
        id: pomFlash
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property string flashText: ""
        property color flashColor: root.critColor
        property bool flashOn: true

        function show(text, col) {
          flashText = text
          flashColor = col
          flashOn = true
          visible = true
          flashAnim.running = true
          flashAutoClose.running = true
        }

        function dismiss() {
          visible = false
          flashAnim.running = false
          flashAutoClose.running = false
        }

        Timer {
          id: flashAnim
          interval: 400
          repeat: true
          onTriggered: pomFlash.flashOn = !pomFlash.flashOn
        }

        Timer {
          id: flashAutoClose
          interval: 10000
          repeat: false
          onTriggered: pomFlash.dismiss()
        }

        Rectangle {
          anchors.fill: parent
          color: pomFlash.flashOn ? "#000000E0" : "#000000A0"

          MouseArea {
            anchors.fill: parent
            onClicked: {
              pomFlash.dismiss()
              if (pomBox.pomState === "done") pomBox.startWork()
            }
          }

          Column {
            anchors.centerIn: parent
            spacing: 16

            Text {
              anchors.horizontalCenter: parent.horizontalCenter
              text: pomFlash.flashText
              color: pomFlash.flashOn ? pomFlash.flashColor : "#333333"
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 72
            }

            Text {
              anchors.horizontalCenter: parent.horizontalCenter
              text: pomBox.pomState === "done" ? "click anywhere to start" : "click to dismiss"
              color: "#666666"
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 14
            }
          }
        }
      }

      PanelWindow {
        id: pomPopup
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        MouseArea {
          anchors.fill: parent
          onClicked: pomPopup.visible = false
        }

        Rectangle {
          x: parent.width - 10 - rightGroup.implicitWidth + vpnBox.implicitWidth + spkBox.implicitWidth + micBox.implicitWidth + netBox.implicitWidth + 24
{{- if .hasBattery }}
            + batteryBox.implicitWidth + brtBox.implicitWidth + 12
{{- end }}
          y: parent.height - 38 - height
          width: 220
          height: pomPopupCol.implicitHeight + 16
          color: root.bgColor
          border.width: 1
          border.color: root.borderColor

          MouseArea {
            anchors.fill: parent
          }

          Column {
            id: pomPopupCol
            anchors.fill: parent
            anchors.margins: 8
            spacing: 2
            z: 1

            Text {
              text: "━━ Settings ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 2
              bottomPadding: 2
            }

            Rectangle {
              width: pomPopupCol.width
              height: 24
              color: "transparent"

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "Work         " + pomBox.workMins + " min"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.NoButton
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) pomBox.workMins = Math.min(90, pomBox.workMins + 5)
                  else pomBox.workMins = Math.max(5, pomBox.workMins - 5)
                  pomBox.saveSettings()
                }
              }
            }

            Rectangle {
              width: pomPopupCol.width
              height: 24
              color: "transparent"

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "Break         " + pomBox.breakMins + " min"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.NoButton
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) pomBox.breakMins = Math.min(30, pomBox.breakMins + 1)
                  else pomBox.breakMins = Math.max(1, pomBox.breakMins - 1)
                  pomBox.saveSettings()
                }
              }
            }

            Rectangle {
              width: pomPopupCol.width
              height: 24
              color: "transparent"

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "Long Break   " + pomBox.longBreakMins + " min"
                color: root.fgColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.NoButton
                onWheel: function(wheel) {
                  if (wheel.angleDelta.y > 0) pomBox.longBreakMins = Math.min(45, pomBox.longBreakMins + 5)
                  else pomBox.longBreakMins = Math.max(5, pomBox.longBreakMins - 5)
                  pomBox.saveSettings()
                }
              }
            }

            Text {
              text: "━━ Session ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 6
              bottomPadding: 2
            }

            Text {
              leftPadding: 8
              text: "Completed    " + pomBox.totalPoms + " poms"
              color: root.fgColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 12
              height: 24
              verticalAlignment: Text.AlignVCenter
            }

            Text {
              leftPadding: 8
              text: "Focus time   " + pomBox.totalFocusMins + " min"
              color: root.fgColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 12
              height: 24
              verticalAlignment: Text.AlignVCenter
            }

            Text {
              leftPadding: 8
              text: "Current      " + (pomBox.pomState === "idle" ? "---" : pomBox.pomState)
              color: pomBox.pomState === "work" ? root.accentColor : pomBox.pomState === "idle" ? root.dimColor : root.okColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 12
              height: 24
              verticalAlignment: Text.AlignVCenter
            }

            Item {
              width: pomPopupCol.width
              height: 9
              Rectangle {
                anchors.centerIn: parent
                width: parent.width
                height: 1
                color: root.borderColor
              }
            }

            Rectangle {
              width: pomPopupCol.width
              height: 24
              color: pomResetMouse.containsMouse ? "#2a2a2a" : "transparent"
              radius: 2

              Text {
                anchors.verticalCenter: parent.verticalCenter
                leftPadding: 8
                text: "⊘  Reset"
                color: root.critColor
                font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                font.pixelSize: 12
              }

              MouseArea {
                id: pomResetMouse
                anchors.fill: parent
                hoverEnabled: true
                onClicked: {
                  pomBox.reset()
                  pomBox.totalPoms = 0
                  pomBox.totalFocusMins = 0
                  pomPopup.visible = false
                }
              }
            }
          }
        }
      }

      PanelWindow {
        id: memoInput
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true
        focusable: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property var notes: []
        property bool inDepthMode: false

        function refresh() {
          memoInlineLoadProc.running = true
        }

        function parseNotes(raw) {
          var lines = raw.split("\n").filter(function(l) { return l.trim() })
          var items = []
          for (var i = lines.length - 1; i >= 0 && items.length < 8; i--) {
            try { items.push(JSON.parse(lines[i])) } catch(e) {}
          }
          if (items.length === 0 && raw.trim().length > 0) {
            var stitched = raw.replace(/}\s*{/g, "}\n{")
            var fallbackLines = stitched.split("\n").filter(function(l) { return l.trim() })
            for (var j = fallbackLines.length - 1; j >= 0 && items.length < 8; j--) {
              try { items.push(JSON.parse(fallbackLines[j])) } catch(e2) {}
            }
          }
          notes = items
        }

        function fmtRelative(iso) {
          var d = new Date(iso)
          var now = new Date()
          var secs = Math.floor((now - d) / 1000)
          if (isNaN(secs) || secs < 0) return ""
          if (secs < 60) return "now"
          var mins = Math.floor(secs / 60)
          if (mins < 60) return mins + "m ago"
          var hrs = Math.floor(mins / 60)
          if (hrs < 48) return hrs + "h ago"
          var days = Math.floor(hrs / 24)
          return days + "d ago"
        }

        onVisibleChanged: {
          if (visible) {
            memoInputField.text = ""
            memoInputArea.text = ""
            memoInput.inDepthMode = false
            memoInput.refresh()
            memoInputField.forceActiveFocus()
          }
        }

        Process {
          id: memoInlineLoadProc
          command: ["/usr/bin/env", "bash", "-c", "cat ~/.local/share/quickmemo/notes.jsonl 2>/dev/null || true"]
          running: false
          stdout: StdioCollector {
            onStreamFinished: memoInput.parseNotes((this.text || "").trim())
          }
        }

        MouseArea {
          anchors.fill: parent
          onClicked: memoInput.visible = false
        }

        Rectangle {
          anchors.centerIn: parent
          width: Math.min(parent.width - 128, 784)
          height: Math.min(parent.height - 60, 432)
          color: "#111214"
          border.width: 1
          border.color: root.borderColor
          clip: true

          MouseArea { anchors.fill: parent }

          Item {
            anchors.fill: parent
            anchors.margins: 22
            z: 1

            Rectangle {
              id: memoEntryBox
              anchors.horizontalCenter: parent.horizontalCenter
              y: {
                if (!memoInput.inDepthMode && memoNotesPanel.visible) return 10
                var base = Math.floor((parent.height - memoEntryBox.height - (memoNotesPanel.visible ? (memoNotesPanel.height + 16) : 0)) / 2)
                return Math.max(0, base)
              }
              width: Math.floor(parent.width * 0.9)
              height: memoInput.inDepthMode ? Math.min(parent.height - 8, 340) : 56
              color: "#111214"
              border.width: 1
              border.color: "#303236"

              Row {
                anchors.fill: parent
                anchors.leftMargin: 14
                anchors.rightMargin: 14
                spacing: 10

                Text {
                  id: memoPrompt
                  anchors.verticalCenter: parent.verticalCenter
                  text: "›"
                  color: root.accentColor
                  font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                  font.pixelSize: memoInput.inDepthMode ? 18 : 22
                }

                Item {
                  width: Math.max(0, parent.width - memoPrompt.width - 10)
                  height: parent.height

                  TextInput {
                    id: memoInputField
                    anchors.fill: parent
                    anchors.topMargin: 12
                    anchors.bottomMargin: 12
                    anchors.leftMargin: 6
                    anchors.rightMargin: 6
                    color: root.fgColor
                    selectionColor: root.accentColor
                    selectedTextColor: "#111111"
                    font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                    font.pixelSize: 22
                    clip: true
                    visible: !memoInput.inDepthMode

                    Keys.onEscapePressed: memoInput.visible = false
                    Keys.onTabPressed: function(event) {
                      memoInput.inDepthMode = true
                      memoInputArea.forceActiveFocus()
                      event.accepted = true
                    }
                    Keys.onReturnPressed: {
                      var text = memoInputField.text.trim()
                      if (text) {
                        panel.memoSave(text, "input")
                        memoInputField.text = ""
                        memoInput.refresh()
                      }
                    }
                  }

                  Flickable {
                    id: memoInputAreaFlick
                    anchors.fill: parent
                    anchors.topMargin: 8
                    anchors.bottomMargin: 8
                    anchors.leftMargin: 6
                    anchors.rightMargin: 6
                    contentWidth: width
                    contentHeight: memoInputArea.implicitHeight
                    clip: true
                    flickableDirection: Flickable.VerticalFlick
                    visible: memoInput.inDepthMode

                    TextEdit {
                      id: memoInputArea
                      width: memoInputAreaFlick.width
                      color: root.fgColor
                      selectionColor: root.accentColor
                      selectedTextColor: "#111111"
                      font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                      font.pixelSize: 16
                      wrapMode: TextEdit.Wrap

                      Keys.onEscapePressed: memoInput.visible = false
                      Keys.onTabPressed: function(event) {
                        memoInput.inDepthMode = false
                        memoInputField.forceActiveFocus()
                        event.accepted = true
                      }
                      Keys.onPressed: function(event) {
                        if (event.key === Qt.Key_Return && (event.modifiers & Qt.ControlModifier)) {
                          var text = memoInputArea.text.trim()
                          if (text) {
                            panel.memoSave(text, "input")
                            memoInputArea.text = ""
                            memoInput.refresh()
                          }
                          event.accepted = true
                        }
                      }
                    }
                  }

                  Text {
                    anchors.fill: parent
                    anchors.topMargin: 12
                    anchors.bottomMargin: 12
                    anchors.leftMargin: 6
                    anchors.rightMargin: 6
                    text: memoInput.inDepthMode ? "In-depth note (Ctrl+Enter to save, Tab for quick mode)" : "Add memo"
                    color: root.dimColor
                    visible: memoInput.inDepthMode ? memoInputArea.text.length === 0 : memoInputField.text.length === 0
                    font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                    font.pixelSize: memoInput.inDepthMode ? 14 : 22
                    horizontalAlignment: Text.AlignLeft
                    verticalAlignment: memoInput.inDepthMode ? Text.AlignTop : Text.AlignVCenter
                  }
                }
              }
            }

            Rectangle {
              id: memoNotesPanel
              anchors.left: memoEntryBox.left
              anchors.right: memoEntryBox.right
              anchors.top: memoEntryBox.bottom
              anchors.topMargin: 16
              anchors.bottom: parent.bottom
              color: "#15171a"
              border.width: 1
              border.color: "#2b2d31"
              radius: 0
              visible: !memoInput.inDepthMode && memoInput.notes.length > 0

              Flickable {
                id: memoNotesFlick
                anchors.fill: parent
                anchors.margins: 9
                contentWidth: width
                contentHeight: memoNotesCol.implicitHeight
                clip: true
                boundsBehavior: Flickable.StopAtBounds
                flickableDirection: Flickable.VerticalFlick

                Column {
                  id: memoNotesCol
                  width: memoNotesFlick.width
                  spacing: 6

                  Repeater {
                    model: memoInput.notes

                    delegate: Rectangle {
                      required property var modelData
                      width: memoNotesCol.width
                      height: 32
                      color: memoItemHover.containsMouse ? "#1c1f23" : "transparent"
                      radius: 0

                      Text {
                        anchors.left: parent.left
                        anchors.right: noteAge.left
                        anchors.rightMargin: 10
                        anchors.verticalCenter: parent.verticalCenter
                        leftPadding: 4
                        text: "→ " + ((modelData.text || "").replace(/\n+/g, " ").trim())
                        color: root.fgColor
                        font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                        font.pixelSize: 16
                        elide: Text.ElideRight
                        verticalAlignment: Text.AlignVCenter
                      }

                      Text {
                        id: noteAge
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        rightPadding: 4
                        text: memoInput.fmtRelative(modelData.ts)
                        color: root.dimColor
                        font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                        font.pixelSize: 14
                        horizontalAlignment: Text.AlignRight
                      }

                      MouseArea {
                        id: memoItemHover
                        anchors.fill: parent
                        hoverEnabled: true
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

      PanelWindow {
        id: memoReview
        screen: panel.screen
        visible: false
        color: "transparent"
        exclusionMode: ExclusionMode.Ignore
        aboveWindows: true

        anchors {
          top: true
          bottom: true
          left: true
          right: true
        }

        property var notes: []

        function refresh() {
          memoLoadProc.running = true
        }

        function parseNotes(raw) {
          var lines = raw.split("\n").filter(function(l) { return l.trim() })
          var items = []
          for (var i = lines.length - 1; i >= 0 && items.length < 50; i--) {
            try { items.push(JSON.parse(lines[i])) } catch(e) {}
          }
          notes = items
        }

        function fmtTs(iso) {
          var d = new Date(iso)
          return Qt.formatDateTime(d, "yyyy-MM-dd HH:mm")
        }

        Process {
          id: memoLoadProc
          command: ["/usr/bin/env", "bash", "-c", "cat ~/.local/share/quickmemo/notes.jsonl 2>/dev/null || true"]
          running: false
          stdout: StdioCollector {
            onStreamFinished: memoReview.parseNotes((this.text || "").trim())
          }
        }

        Process {
          id: memoClipCopy
          running: false
        }

        MouseArea {
          anchors.fill: parent
          onClicked: memoReview.visible = false
        }

        Rectangle {
          anchors.centerIn: parent
          width: 600
          height: Math.min(memoReviewFlick.contentHeight + 50, parent.height - 80)
          color: root.bgColor
          border.width: 1
          border.color: root.borderColor
          clip: true

          MouseArea {
            anchors.fill: parent
            onWheel: function(wheel) {}
          }

          Column {
            anchors.left: parent.left
            anchors.right: parent.right
            anchors.top: parent.top
            anchors.margins: 8
            z: 1

            Text {
              text: "━━ Recent Memos ━━"
              color: root.dimColor
              font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
              font.pixelSize: 11
              topPadding: 2
              bottomPadding: 4
            }

            Flickable {
              id: memoReviewFlick
              width: parent.width
              height: Math.min(contentHeight, panel.height - 130)
              contentHeight: memoReviewCol.implicitHeight
              clip: true
              flickableDirection: Flickable.VerticalFlick
              boundsBehavior: Flickable.StopAtBounds

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.NoButton
                onWheel: function(wheel) {
                  memoReviewFlick.contentY = Math.max(0,
                    Math.min(memoReviewFlick.contentHeight - memoReviewFlick.height,
                      memoReviewFlick.contentY - wheel.angleDelta.y))
                }
              }

              Column {
                id: memoReviewCol
                width: parent.width
                spacing: 0

                Repeater {
                  model: memoReview.notes

                  delegate: Rectangle {
                    required property var modelData
                    width: memoReviewCol.width
                    height: memoNoteCol.implicitHeight + 12
                    color: memoItemMouse.containsMouse ? "#2a2a2a" : "transparent"
                    radius: 2

                    Column {
                      id: memoNoteCol
                      anchors.left: parent.left
                      anchors.right: parent.right
                      anchors.verticalCenter: parent.verticalCenter
                      leftPadding: 8
                      rightPadding: 8
                      spacing: 2

                      Text {
                        text: memoReview.fmtTs(modelData.ts) + (modelData.src === "clipboard" ? "  [clip]" : "")
                        color: root.dimColor
                        font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                        font.pixelSize: 10
                      }

                      Text {
                        width: parent.width - 16
                        text: (modelData.text || "").substring(0, 200)
                        color: root.fgColor
                        font.family: "Terminus, IBM Plex Mono, JetBrainsMono Nerd Font, monospace"
                        font.pixelSize: 12
                        wrapMode: Text.Wrap
                        maximumLineCount: 3
                        elide: Text.ElideRight
                      }
                    }

                    MouseArea {
                      id: memoItemMouse
                      anchors.fill: parent
                      hoverEnabled: true
                      onClicked: {
                        memoClipCopy.command = ["/usr/bin/env", "bash", "-c",
                          "echo " + Qt.btoa(modelData.text) + " | base64 -d | wl-copy"]
                        memoClipCopy.running = true
                        root.runShell("notify-send -r 91192 -t 1500 'Quick Memo' 'Copied to clipboard'")
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
