updated pi, quickshell, and ollama

This commit is contained in:
2026-08-31 20:17:31 -07:00
parent 1f3dde6de2
commit e09304b0b6
16 changed files with 2290 additions and 315 deletions

View File

@@ -0,0 +1,190 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import "../theme"
import ".."
PanelWindow {
id: quickMenu
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
anchors {
top: true
left: true
}
margins {
top: 0
left: Theme.sidebarWidth
}
visible: GlobalState.appleMenuVisible
implicitWidth: 220
implicitHeight: menuContainer.implicitHeight
color: "transparent"
// Close on escape
Item {
anchors.fill: parent
focus: true
Keys.onEscapePressed: GlobalState.appleMenuVisible = false
}
Rectangle {
id: menuContainer
anchors.fill: parent
color: Theme.glassBg
border.color: Theme.glassBorder
border.width: 1
Column {
width: parent.width
padding: 8
component MenuItem: Item {
id: menuItem
width: parent.width
height: 28
property string text: ""
property string shortcut: ""
signal triggered()
Rectangle {
anchors.fill: parent
color: menuItemMouseArea.containsMouse ? Qt.rgba(Theme.accentVolt.r, Theme.accentVolt.g, Theme.accentVolt.b, 0.1) : "transparent"
}
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 2
color: Theme.accentVolt
visible: menuItemMouseArea.containsMouse
}
Text {
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: menuItem.text
font.family: Theme.fontSans
font.pixelSize: 13
color: menuItemMouseArea.containsMouse ? Theme.textPrimary : Theme.textSecondary
}
Text {
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: menuItem.shortcut
font.family: Theme.fontMono
font.pixelSize: 11
color: Theme.textMuted
visible: text !== ""
}
MouseArea {
id: menuItemMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
menuItem.triggered()
GlobalState.appleMenuVisible = false
}
}
}
component MenuSeparator: Item {
width: parent.width
height: 17
Rectangle {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 12
anchors.rightMargin: 12
height: 1
color: Theme.glassBorder
}
}
MenuItem {
text: "System Info..."
onTriggered: GlobalState.aboutMacVisible = true
}
MenuItem {
text: "System Preferences..."
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["pavucontrol"] }', quickMenu)
p.running = true
}
}
MenuItem {
text: "Activity Monitor"
shortcut: "btop"
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["foot", "-e", "btop"] }', quickMenu)
p.running = true
}
}
MenuSeparator {}
MenuItem {
text: "Sleep"
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["systemctl", "suspend"] }', quickMenu)
p.running = true
}
}
MenuItem {
text: "Restart..."
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["systemctl", "reboot"] }', quickMenu)
p.running = true
}
}
MenuItem {
text: "Shut Down..."
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["systemctl", "poweroff"] }', quickMenu)
p.running = true
}
}
MenuSeparator {}
MenuItem {
text: "Lock Screen"
shortcut: "Super+L"
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["hyprlock"] }', quickMenu)
p.running = true
}
}
MenuItem {
text: "Log Out"
shortcut: "Super+Shift+Q"
onTriggered: {
let p = Qt.createQmlObject('import Quickshell.Io; Process { command: ["hyprctl", "dispatch", "exit"] }', quickMenu)
p.running = true
}
}
}
}
}

View File

@@ -0,0 +1,531 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import "../theme"
import ".."
PanelWindow {
id: root
property var screen
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "quickshell-sidepanel"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
anchors {
top: true
bottom: true
left: true
right: false
}
exclusiveZone: Theme.sidebarWidth
implicitWidth: Theme.sidebarWidth
color: Theme.bgPrimary
// Right edge hairline border
Rectangle {
width: 1
color: Theme.glassBorder
anchors {
right: parent.right
top: parent.top
bottom: parent.bottom
}
}
component Separator: Rectangle {
width: 36
height: 1
color: Theme.glassBorder
Layout.alignment: Qt.AlignHCenter
}
component DockIcon: Item {
id: dockIconRoot
width: 36
height: 36
Layout.alignment: Qt.AlignHCenter
property string appClass: ""
property string execCmd: ""
property string iconSource: ""
property string tooltipText: ""
property bool isRunning: (appClass !== "" && GlobalState.activeWindowClass === appClass) || (GlobalState.runningApps && GlobalState.runningApps.indexOf(appClass) !== -1)
Image {
id: img
anchors.centerIn: parent
width: 24
height: 24
source: iconSource ? Qt.resolvedUrl("../assets/" + iconSource) : ""
sourceSize: Qt.size(24, 24)
fillMode: Image.PreserveAspectFit
opacity: mouseArea.containsMouse ? 1.0 : 0.8
scale: mouseArea.containsMouse ? 1.15 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
Behavior on opacity {
NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
}
Rectangle {
id: runningDot
width: 4
height: 4
radius: 2
color: Theme.accentVolt
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
visible: isRunning
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (execCmd !== "") {
GlobalState.launchApp(execCmd)
}
}
}
// Tooltip
Rectangle {
id: tooltip
visible: mouseArea.containsMouse && tooltipText !== ""
color: Theme.glassBg
radius: 4
width: tooltipLabel.implicitWidth + 16
height: tooltipLabel.implicitHeight + 8
anchors.left: parent.right
anchors.leftMargin: 24
anchors.verticalCenter: parent.verticalCenter
Text {
id: tooltipLabel
anchors.centerIn: parent
text: tooltipText
color: Theme.textPrimary
font.family: Theme.fontSans
font.pixelSize: 11
}
}
}
ColumnLayout {
anchors.fill: parent
spacing: 8
// Top spacer
Item { height: 4; Layout.fillWidth: true }
// 1. AG Logo Button
Rectangle {
width: 36
height: 36
radius: 4
color: logoMouse.containsMouse ? Qt.rgba(1,1,1,0.05) : "transparent"
Layout.alignment: Qt.AlignHCenter
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
Text {
anchors.centerIn: parent
text: "AG"
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: 16
color: Theme.accentVolt
}
MouseArea {
id: logoMouse
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.appleMenuVisible = !GlobalState.appleMenuVisible
}
}
Separator {}
// 3. Workspace Indicators
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 2
Repeater {
model: GlobalState.workspaceList
delegate: Rectangle {
width: 52
height: 36
color: wsMouse.containsMouse ? Qt.rgba(1,1,1,0.04) : "transparent"
property bool isActive: GlobalState.activeWorkspace === modelData
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
Rectangle {
width: 2
height: 20
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: Theme.accentVolt
visible: isActive
}
Column {
anchors.centerIn: parent
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: (modelData < 10 ? "0" : "") + modelData
color: isActive ? Theme.accentVolt : Theme.textMuted
font.family: Theme.fontMono
font.pixelSize: 12
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: (GlobalState.workspaceNames && GlobalState.workspaceNames[modelData]) ? GlobalState.workspaceNames[modelData] : ""
color: Theme.textMuted
font.family: Theme.fontSans
font.pixelSize: 8
opacity: 0.7
}
}
MouseArea {
id: wsMouse
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.switchWorkspace(modelData)
}
}
}
}
Separator {}
// 5. Spotlight trigger
Item {
width: 36
height: 36
Layout.alignment: Qt.AlignHCenter
Text {
anchors.centerIn: parent
text: "⌕"
font.pixelSize: 20
color: spotlightMouse.containsMouse ? Theme.accentVolt : Theme.textMuted
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
}
MouseArea {
id: spotlightMouse
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.toggleSpotlight()
}
}
Separator {}
// 7. Pinned Apps
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 8
DockIcon {
appClass: ""
execCmd: "nautilus"
iconSource: "finder.svg"
tooltipText: "Finder"
}
DockIcon {
appClass: "brave-browser"
execCmd: "brave"
iconSource: "browser.svg"
tooltipText: "Brave Browser"
}
DockIcon {
appClass: "foot"
execCmd: "foot"
iconSource: "terminal.svg"
tooltipText: "Terminal"
}
DockIcon {
appClass: "nvim"
execCmd: "foot -e nvim"
iconSource: "code.svg"
tooltipText: "Code"
}
DockIcon {
appClass: "pavucontrol"
execCmd: "pavucontrol"
iconSource: "preferences.svg"
tooltipText: "Settings"
}
DockIcon {
appClass: ""
execCmd: "spotify"
iconSource: "music.svg"
tooltipText: "Music"
}
}
Separator {}
// 9. Running Apps (non-pinned)
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 8
Repeater {
model: {
var pinned = ["foot", "brave-browser", "pavucontrol", "nvim", "nautilus", "spotify", ""]
var running = []
var seen = {}
if (GlobalState.runningApps) {
for (var i = 0; i < GlobalState.runningApps.length; i++) {
var cls = GlobalState.runningApps[i]
if (cls && pinned.indexOf(cls) === -1 && !seen[cls]) {
seen[cls] = true
running.push(cls)
}
}
}
return running
}
delegate: Item {
width: 36
height: 36
Rectangle {
anchors.centerIn: parent
width: 24
height: 24
radius: 12
color: Qt.rgba(1, 1, 1, 0.1)
Text {
anchors.centerIn: parent
text: modelData.charAt(0).toUpperCase()
color: Theme.textPrimary
font.family: Theme.fontSans
font.pixelSize: 12
}
}
Rectangle {
width: 4
height: 4
radius: 2
color: Theme.accentVolt
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
// Focus app logic here
}
}
}
}
}
// 10. Flexible spacer
Item { Layout.fillHeight: true }
Separator {}
// 12. Telemetry mini-readouts
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "CPU" + (GlobalState.cpuPercent !== undefined ? GlobalState.cpuPercent : "0") + "%"
font.family: Theme.fontMono
font.pixelSize: 11
color: (GlobalState.cpuPercent !== undefined && GlobalState.cpuPercent > 80) ? Theme.accentCoral : Theme.textMuted
Behavior on color { ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "RAM" + (GlobalState.memUsedGb !== undefined ? GlobalState.memUsedGb : "0") + "G"
font.family: Theme.fontMono
font.pixelSize: 11
color: (GlobalState.memPercent !== undefined && GlobalState.memPercent > 85) ? Theme.accentCoral : Theme.textMuted
Behavior on color { ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "VRM" + (GlobalState.gpuVramUsed !== undefined ? GlobalState.gpuVramUsed : "0") + "G"
font.family: Theme.fontMono
font.pixelSize: 11
color: Theme.textMuted
}
}
Separator {}
// 14. System tray icons
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 8
// Volume
Item {
width: 36
height: 36
Image {
anchors.centerIn: parent
width: 16
height: 16
source: Qt.resolvedUrl("../assets/volume.svg")
sourceSize: Qt.size(16, 16)
opacity: volMouse.containsMouse ? 1.0 : 0.7
Behavior on opacity { NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
MouseArea {
id: volMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "volume"
GlobalState.systemHudVisible = true
}
}
}
// WiFi
Item {
width: 36
height: 36
Image {
anchors.centerIn: parent
width: 16
height: 16
source: Qt.resolvedUrl("../assets/wifi.svg")
sourceSize: Qt.size(16, 16)
opacity: GlobalState.wifiConnected ? 0.7 : 0.3
Behavior on opacity { NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "network"
GlobalState.systemHudVisible = true
}
}
}
// Battery
Item {
width: 36
height: 44
visible: GlobalState.batteryPresent !== undefined ? GlobalState.batteryPresent : false
Column {
anchors.centerIn: parent
spacing: 2
Image {
anchors.horizontalCenter: parent.horizontalCenter
width: 16
height: 16
source: Qt.resolvedUrl("../assets/battery.svg")
sourceSize: Qt.size(16, 16)
opacity: (GlobalState.batteryPercent !== undefined && GlobalState.batteryPercent < 20) ? 1.0 : 0.7
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: (GlobalState.batteryPercent !== undefined ? GlobalState.batteryPercent : "100") + "%"
font.family: Theme.fontMono
font.pixelSize: 9
color: (GlobalState.batteryPercent !== undefined && GlobalState.batteryPercent < 20) ? Theme.accentCoral : Theme.textSecondary
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "battery"
GlobalState.systemHudVisible = true
}
}
}
}
Separator {}
// 16. Clock
Item {
width: 36
height: 44
Layout.alignment: Qt.AlignHCenter
Column {
anchors.centerIn: parent
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: GlobalState.timeString !== undefined ? GlobalState.timeString : "00:00"
font.family: Theme.fontMono
font.pixelSize: 12
color: Theme.textPrimary
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: GlobalState.dateString !== undefined ? GlobalState.dateString : "Jan 01"
font.family: Theme.fontMono
font.pixelSize: 9
color: Theme.textMuted
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "calendar"
GlobalState.systemHudVisible = true
}
}
}
// Bottom spacer
Item { height: 4; Layout.fillWidth: true }
}
}

View File

@@ -18,7 +18,7 @@ PanelWindow {
bottom: true
}
color: Qt.rgba(0, 0, 0, 0.35)
color: Qt.rgba(0, 0, 0, 0.45)
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "quickshell-spotlight"
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
@@ -35,7 +35,7 @@ PanelWindow {
stdout: StdioCollector {
onDataChanged: {
try {
const parsed = JSON.parse(data.trim());
const parsed = JSON.parse(String(data).trim());
if (Array.isArray(parsed)) {
spotWindow.allApps = parsed;
}
@@ -67,223 +67,246 @@ PanelWindow {
}
// Centered Spotlight Search Modal
Rectangle {
id: modalBox
Item {
id: modalContainer
anchors.horizontalCenter: parent.horizontalCenter
y: 140
width: 640
implicitHeight: searchBarRow.height + (resultsListView.count > 0 || mathResultText !== "" ? 340 : 0)
radius: Theme.radiusModal
color: Theme.windowBg
border.color: Theme.windowBorder
border.width: 1
height: modalBox.implicitHeight
scale: spotWindow.visible ? 1.0 : 0.96
opacity: spotWindow.visible ? 1.0 : 0.0
Behavior on implicitHeight {
NumberAnimation { duration: 180; easing.type: Easing.OutQuad }
Behavior on scale {
NumberAnimation { duration: Theme.animSmooth; easing.type: Easing.OutQuint }
}
Behavior on opacity {
NumberAnimation { duration: Theme.animSmooth; easing.type: Easing.OutQuint }
}
// Prevent clicking inside from closing modal
MouseArea {
Rectangle {
id: modalBox
anchors.fill: parent
onClicked: {}
}
implicitHeight: searchBarRowContainer.height + (resultsListView.count > 0 || mathResultText !== "" ? 340 : 0)
radius: 0
color: Theme.bgPrimary
border.color: Theme.glassBorder
border.width: 1
ColumnLayout {
anchors.fill: parent
anchors.margins: 14
spacing: 12
Behavior on implicitHeight {
NumberAnimation { duration: 180; easing.type: Easing.OutQuad }
}
// Top Search Bar
RowLayout {
id: searchBarRow
Layout.fillWidth: true
Layout.preferredHeight: 44
spacing: 12
// Prevent clicking inside from closing modal
MouseArea {
anchors.fill: parent
onClicked: {}
}
Image {
width: 24
height: 24
source: "../assets/spotlight.svg"
sourceSize: Qt.size(24, 24)
}
ColumnLayout {
anchors.fill: parent
spacing: 0
TextInput {
id: searchInput
// Top Search Bar Area
Item {
id: searchBarRowContainer
Layout.fillWidth: true
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSpotlight
font.weight: Font.Light
color: Theme.textPrimary
focus: true
clip: true
Layout.preferredHeight: 64
Text {
RowLayout {
id: searchBarRow
anchors.fill: parent
visible: searchInput.text === ""
text: "Spotlight Search"
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSpotlight
font.weight: Font.Light
color: Theme.textTertiary
anchors.margins: 14
spacing: 12
Text {
text: "⌕"
font.family: Theme.fontSans
font.pixelSize: 24
color: Theme.accentVolt
Layout.alignment: Qt.AlignVCenter
}
TextInput {
id: searchInput
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
font.family: Theme.fontSans
font.pixelSize: 20
color: Theme.textPrimary
focus: true
clip: true
Text {
anchors.fill: parent
visible: searchInput.text === ""
text: "Spotlight Search"
font.family: Theme.fontSans
font.pixelSize: 20
color: Theme.textMuted
}
Keys.onEscapePressed: GlobalState.spotlightVisible = false
Keys.onDownPressed: {
if (resultsListView.count > 0) {
spotWindow.selectedIndex = Math.min(resultsListView.count - 1, spotWindow.selectedIndex + 1);
}
}
Keys.onUpPressed: {
if (resultsListView.count > 0) {
spotWindow.selectedIndex = Math.max(0, spotWindow.selectedIndex - 1);
}
}
Keys.onReturnPressed: {
if (filteredModel.count > 0 && spotWindow.selectedIndex < filteredModel.count) {
const item = filteredModel.get(spotWindow.selectedIndex);
GlobalState.launchApp(item.exec);
}
}
}
// Clear button
Rectangle {
visible: searchInput.text !== ""
width: 24
height: 24
radius: 0
color: "transparent"
Text {
anchors.centerIn: parent
text: "✕"
font.pixelSize: 16
color: clearMouseArea.containsMouse ? Theme.textPrimary : Theme.textMuted
}
MouseArea {
id: clearMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: searchInput.text = ""
}
}
}
Keys.onEscapePressed: GlobalState.spotlightVisible = false
Keys.onDownPressed: {
if (resultsListView.count > 0) {
spotWindow.selectedIndex = Math.min(resultsListView.count - 1, spotWindow.selectedIndex + 1);
}
}
Keys.onUpPressed: {
if (resultsListView.count > 0) {
spotWindow.selectedIndex = Math.max(0, spotWindow.selectedIndex - 1);
}
}
Keys.onReturnPressed: {
if (filteredModel.count > 0 && spotWindow.selectedIndex < filteredModel.count) {
const item = filteredModel.get(spotWindow.selectedIndex);
GlobalState.launchApp(item.exec);
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: Theme.glassBorder
}
}
// Clear button
// Math Calculation Result Banner (if valid formula)
Rectangle {
visible: searchInput.text !== ""
width: 20
height: 20
radius: 10
color: Qt.rgba(1.0, 1.0, 1.0, 0.2)
Text {
anchors.centerIn: parent
text: "✕"
font.pixelSize: 11
color: Theme.textPrimary
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: searchInput.text = ""
}
}
}
// Math Calculation Result Banner (if valid formula)
Rectangle {
visible: mathResultText !== ""
Layout.fillWidth: true
Layout.preferredHeight: 36
radius: Theme.radiusSmall
color: Qt.rgba(0.04, 0.52, 1.0, 0.2)
border.color: Theme.accentBlue
border.width: 1
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 8
Text {
text: "="
font.family: Theme.fontFamily
font.pixelSize: 16
font.weight: Font.Bold
color: Theme.accentBlue
}
Text {
text: mathResultText
font.family: Theme.fontFamily
font.pixelSize: 16
font.weight: Font.DemiBold
color: "#FFFFFF"
Layout.fillWidth: true
}
}
}
// Divider Line
Rectangle {
visible: resultsListView.count > 0
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Qt.rgba(1.0, 1.0, 1.0, 0.12)
}
// Search Results List View
ListView {
id: resultsListView
visible: count > 0
Layout.fillWidth: true
Layout.preferredHeight: Math.min(270, count * 42)
clip: true
spacing: 2
model: filteredModel
delegate: Rectangle {
id: itemDelegate
required property int index
required property string name
required property string exec
required property string comment
width: resultsListView.width
height: 40
radius: Theme.radiusSmall
color: spotWindow.selectedIndex === index ? Theme.menuHoverBlue : "transparent"
visible: mathResultText !== ""
Layout.fillWidth: true
Layout.preferredHeight: 36
Layout.margins: 14
radius: 0
color: "transparent"
RowLayout {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
spacing: 10
Image {
width: 22
height: 22
source: "../assets/terminal.svg"
sourceSize: Qt.size(22, 22)
}
ColumnLayout {
Layout.fillWidth: true
spacing: 1
Text {
text: itemDelegate.name
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeNormal
font.weight: Font.Medium
color: "#FFFFFF"
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: itemDelegate.comment || itemDelegate.exec
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
color: spotWindow.selectedIndex === itemDelegate.index
? Qt.rgba(1,1,1,0.85) : Theme.textTertiary
elide: Text.ElideRight
Layout.fillWidth: true
}
}
anchors.margins: 8
spacing: 8
Text {
text: "↩"
font.pixelSize: 14
color: spotWindow.selectedIndex === itemDelegate.index
? "#FFFFFF" : "transparent"
text: "="
font.family: Theme.fontMono
font.pixelSize: 16
font.weight: Font.Bold
color: Theme.accentVolt
}
Text {
text: mathResultText
font.family: Theme.fontMono
font.pixelSize: 16
color: Theme.accentVolt
Layout.fillWidth: true
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: spotWindow.selectedIndex = itemDelegate.index
onClicked: GlobalState.launchApp(itemDelegate.exec)
// Search Results List View
ListView {
id: resultsListView
visible: count > 0
Layout.fillWidth: true
Layout.preferredHeight: Math.min(270, count * 40)
Layout.topMargin: 8
Layout.bottomMargin: 8
clip: true
spacing: 0
model: filteredModel
delegate: Rectangle {
id: itemDelegate
required property int index
required property string name
required property string exec
required property string comment
width: resultsListView.width
height: 40
radius: 0
color: spotWindow.selectedIndex === index ? Qt.rgba(0.812, 0.988, 0.0, 0.08) : "transparent"
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 2
color: Theme.accentVolt
visible: spotWindow.selectedIndex === index
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 14
anchors.rightMargin: 14
spacing: 12
ColumnLayout {
Layout.fillWidth: true
spacing: 2
Text {
text: itemDelegate.name
font.family: Theme.fontSans
font.pixelSize: 14
color: Theme.textPrimary
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: itemDelegate.comment || itemDelegate.exec
font.family: Theme.fontMono
font.pixelSize: 11
color: Theme.textMuted
elide: Text.ElideRight
Layout.fillWidth: true
}
}
Text {
text: "↩"
font.pixelSize: 14
color: spotWindow.selectedIndex === itemDelegate.index
? Theme.textPrimary : "transparent"
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: spotWindow.selectedIndex = itemDelegate.index
onClicked: GlobalState.launchApp(itemDelegate.exec)
}
}
}
}

View File

@@ -0,0 +1,685 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import "../theme"
import ".."
PanelWindow {
id: root
anchors {
top: true
bottom: true
left: true
}
margins {
left: Theme.sidebarWidth
top: Theme.topStripHeight
}
implicitWidth: Theme.hudWidth
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "quickshell-systemhud"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
visible: GlobalState.systemHudVisible
color: "transparent"
property real slideOffset: GlobalState.systemHudVisible ? 0 : -Theme.hudWidth
Behavior on slideOffset {
NumberAnimation {
duration: Theme.animSmooth
easing.type: Easing.OutQuint
}
}
Shortcut {
sequence: "Escape"
onActivated: GlobalState.systemHudVisible = false
}
component SectionHeader: Item {
property string sectionNum: "00"
property string title: "Section"
implicitWidth: parent.width
implicitHeight: 24
RowLayout {
anchors.fill: parent
spacing: 8
Text {
text: sectionNum + " //"
font.family: Theme.fontMono
font.pixelSize: 12
color: Theme.accentVolt
}
Text {
text: title
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: 16
color: Theme.textPrimary
Layout.fillWidth: true
}
}
}
component ToggleCard: Rectangle {
property string title: ""
property string icon: ""
property bool active: false
signal clicked()
Layout.preferredWidth: 160
Layout.preferredHeight: 56
color: Theme.bgPrimary
border.color: active ? Qt.rgba(Theme.accentVolt.r, Theme.accentVolt.g, Theme.accentVolt.b, 0.3) : Theme.glassBorder
border.width: 1
radius: 4
Behavior on border.color { ColorAnimation { duration: Theme.animFast } }
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: clicked()
onEntered: if (!active) border.color = Theme.glassBorderHover
onExited: if (!active) border.color = Theme.glassBorder
}
RowLayout {
anchors.fill: parent
anchors.margins: 12
spacing: 12
Image {
source: icon ? Qt.resolvedUrl("../" + icon) : ""
Layout.preferredWidth: 24
Layout.preferredHeight: 24
sourceSize: Qt.size(24, 24)
visible: parent.icon !== ""
}
Text {
text: title
color: Theme.textPrimary
font.family: Theme.fontSans
font.pixelSize: 13
Layout.fillWidth: true
elide: Text.ElideRight
}
}
}
component CustomSlider: Item {
property real value: 0
signal valueChangedUser(real newVal)
implicitWidth: parent.width
implicitHeight: 24
Rectangle {
id: track
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 40
height: 6
color: Theme.bgSecondary
radius: 3
Rectangle {
id: fill
width: track.width * (parent.parent.value / 100)
height: track.height
color: Theme.accentVolt
radius: 3
}
Rectangle {
width: 14
height: 14
radius: 7
color: Theme.textPrimary
anchors.verticalCenter: parent.verticalCenter
x: Math.max(0, fill.width - 7)
}
MouseArea {
anchors.fill: parent
anchors.margins: -10
function updateValue(mouseX) {
let p = Math.max(0, Math.min(1, mouseX / track.width))
parent.parent.valueChangedUser(p * 100)
}
onPressed: (mouse) => updateValue(mouse.x)
onPositionChanged: (mouse) => updateValue(mouse.x)
}
}
Text {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: Math.round(parent.value) + "%"
color: Theme.textPrimary
font.family: Theme.fontMono
font.pixelSize: 12
}
}
component ProgressBarItem: ColumnLayout {
property string label: ""
property real percent: 0
property string valueText: ""
property color barColor: Theme.accentVolt
Layout.fillWidth: true
spacing: 4
RowLayout {
Layout.fillWidth: true
Text {
text: parent.parent.label
color: Theme.textPrimary
font.family: Theme.fontMono
font.pixelSize: 12
}
Item { Layout.fillWidth: true }
Text {
text: parent.parent.valueText
color: Theme.textSecondary
font.family: Theme.fontMono
font.pixelSize: 12
}
}
Rectangle {
Layout.fillWidth: true
height: 8
color: Theme.bgSecondary
radius: 4
Rectangle {
width: parent.width * (parent.parent.percent / 100)
height: parent.height
color: parent.parent.barColor
radius: 4
Behavior on width { NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
Behavior on color { ColorAnimation { duration: Theme.animFast } }
}
}
}
Rectangle {
id: panelContent
anchors.fill: parent
color: Theme.glassBg
opacity: GlobalState.systemHudVisible ? 1 : 0
transform: Translate { x: slideOffset }
Behavior on opacity {
NumberAnimation {
duration: Theme.animSmooth
easing.type: Easing.OutQuint
}
}
Rectangle {
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 1
color: Theme.glassBorder
}
Item {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 60
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.glassBorder
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 20
anchors.rightMargin: 20
Text {
text: "System HUD"
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: 19
color: Theme.textPrimary
Layout.fillWidth: true
}
Text {
text: "✕"
color: Theme.textMuted
font.pixelSize: 16
MouseArea {
anchors.fill: parent
anchors.margins: -10
hoverEnabled: true
onEntered: parent.color = Theme.textPrimary
onExited: parent.color = Theme.textMuted
onClicked: GlobalState.systemHudVisible = false
}
}
}
}
ScrollView {
id: scrollView
anchors.top: header.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentWidth: availableWidth
clip: true
ColumnLayout {
id: contentLayout
width: parent.width - 40
x: 20
spacing: 32
Item { Layout.preferredHeight: 16 } // Top padding
// Section 1: Toggles
ColumnLayout {
id: sectionToggles
Layout.fillWidth: true
spacing: 16
SectionHeader { sectionNum: "01"; title: "Toggles" }
GridLayout {
columns: 2
columnSpacing: 12
rowSpacing: 12
Layout.fillWidth: true
ToggleCard {
title: GlobalState.wifiSsid !== "" ? GlobalState.wifiSsid : "Wi-Fi"
icon: "assets/wifi.svg"
active: GlobalState.wifiConnected
onClicked: {
let proc = Quickshell.createProcess("sh", ["-c", "nm-connection-editor || foot -e nmtui"])
proc.start()
}
}
ToggleCard {
title: "Bluetooth"
icon: "assets/bluetooth.svg"
active: false // Adjust if there's a bluetooth state
onClicked: {
let proc = Quickshell.createProcess("sh", ["-c", "foot -e bluetoothctl"])
proc.start()
}
}
ToggleCard {
property bool dndActive: false
title: "Do Not Disturb"
icon: "" // Optional DND icon
active: dndActive
onClicked: dndActive = !dndActive
}
ToggleCard {
title: GlobalState.volumeMuted ? "Muted" : "Sound"
icon: "" // Optional sound icon
active: !GlobalState.volumeMuted
onClicked: GlobalState.toggleVolumeMute()
}
}
}
// Section 2: Volume
ColumnLayout {
id: sectionVolume
Layout.fillWidth: true
spacing: 16
SectionHeader { sectionNum: "02"; title: "Volume" }
CustomSlider {
Layout.fillWidth: true
value: GlobalState.volume
onValueChangedUser: (val) => GlobalState.volume = val // Adjust to actual volume setter if needed
}
RowLayout {
Layout.fillWidth: true
Text {
text: GlobalState.volumeMuted ? "Unmute" : "Mute"
color: Theme.textSecondary
font.family: Theme.fontSans
font.pixelSize: 13
MouseArea {
anchors.fill: parent
onClicked: GlobalState.toggleVolumeMute()
}
}
Item { Layout.fillWidth: true }
Text {
text: "Sound Preferences..."
color: Theme.textMuted
font.family: Theme.fontSans
font.pixelSize: 13
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Theme.textPrimary
onExited: parent.color = Theme.textMuted
onClicked: {
let proc = Quickshell.createProcess("pavucontrol", [])
proc.start()
}
}
}
}
}
// Section 3: Brightness
ColumnLayout {
id: sectionBrightness
Layout.fillWidth: true
spacing: 16
SectionHeader { sectionNum: "03"; title: "Brightness" }
CustomSlider {
Layout.fillWidth: true
value: GlobalState.brightness
onValueChangedUser: (val) => GlobalState.setBrightness(val)
}
}
// Section 4: Performance
ColumnLayout {
id: sectionPerformance
Layout.fillWidth: true
spacing: 16
SectionHeader { sectionNum: "04"; title: "Performance" }
ProgressBarItem {
label: "CPU"
percent: GlobalState.cpuPercent
valueText: GlobalState.cpuPercent + "%"
barColor: GlobalState.cpuPercent > 80 ? Theme.accentCoral : Theme.accentVolt
}
ProgressBarItem {
label: "RAM"
percent: (GlobalState.memUsedGb / Math.max(1, GlobalState.memTotalGb)) * 100
valueText: GlobalState.memUsedGb + " / " + GlobalState.memTotalGb + " GB"
}
ProgressBarItem {
label: "VRAM"
percent: (GlobalState.gpuVramUsed / Math.max(1, GlobalState.gpuVramTotal)) * 100
valueText: GlobalState.gpuVramUsed + " / " + GlobalState.gpuVramTotal + " GB"
}
}
// Section 5: Network
ColumnLayout {
id: sectionNetwork
Layout.fillWidth: true
spacing: 16
SectionHeader { sectionNum: "05"; title: "Network" }
RowLayout {
spacing: 8
Rectangle {
width: 8
height: 8
radius: 4
color: GlobalState.wifiConnected ? Theme.accentVolt : Theme.accentCoral
}
Text {
text: GlobalState.wifiConnected ? "Connected" : "Disconnected"
color: Theme.textPrimary
font.family: Theme.fontSans
font.pixelSize: 14
}
}
Text {
text: "SSID: " + (GlobalState.wifiSsid !== "" ? GlobalState.wifiSsid : "None")
color: Theme.textSecondary
font.family: Theme.fontSans
font.pixelSize: 13
}
Text {
text: "IP: " + GlobalState.ipAddress
color: Theme.textSecondary
font.family: Theme.fontMono
font.pixelSize: 13
}
Text {
text: "Network Preferences..."
color: Theme.textMuted
font.family: Theme.fontSans
font.pixelSize: 13
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Theme.textPrimary
onExited: parent.color = Theme.textMuted
onClicked: {
let proc = Quickshell.createProcess("sh", ["-c", "nm-connection-editor || foot -e nmtui"])
proc.start()
}
}
}
}
// Section 6: Battery
ColumnLayout {
id: sectionBattery
Layout.fillWidth: true
spacing: 16
visible: GlobalState.batteryPresent
SectionHeader { sectionNum: "06"; title: "Battery" }
RowLayout {
spacing: 16
Text {
text: GlobalState.batteryPercent + "%"
font.family: Theme.fontMono
font.pixelSize: 24
color: GlobalState.batteryStatus === "Charging" ? Theme.accentVolt :
(GlobalState.batteryPercent < 20 ? Theme.accentCoral : Theme.textPrimary)
}
Text {
text: GlobalState.batteryStatus
color: Theme.textSecondary
font.family: Theme.fontSans
font.pixelSize: 14
}
}
}
// Section 7: Now Playing
ColumnLayout {
id: sectionNowPlaying
Layout.fillWidth: true
spacing: 16
visible: GlobalState.mediaTitle !== ""
SectionHeader { sectionNum: "07"; title: "Now Playing" }
ColumnLayout {
spacing: 4
Text {
text: GlobalState.mediaTitle
color: Theme.textPrimary
font.family: Theme.fontSans
font.bold: true
font.pixelSize: 14
Layout.fillWidth: true
elide: Text.ElideRight
}
Text {
text: GlobalState.mediaArtist
color: Theme.textSecondary
font.family: Theme.fontSans
font.pixelSize: 12
Layout.fillWidth: true
elide: Text.ElideRight
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 16
Text {
text: "⏮"
font.pixelSize: 20
color: Theme.textSecondary
MouseArea {
anchors.fill: parent
anchors.margins: -10
hoverEnabled: true
onEntered: parent.color = Theme.accentVolt
onExited: parent.color = Theme.textSecondary
onClicked: GlobalState.mediaPrevious()
}
}
Text {
text: GlobalState.mediaStatus === "Playing" ? "⏸" : "▶"
font.pixelSize: 24
color: Theme.textSecondary
MouseArea {
anchors.fill: parent
anchors.margins: -10
hoverEnabled: true
onEntered: parent.color = Theme.accentVolt
onExited: parent.color = Theme.textSecondary
onClicked: GlobalState.mediaPlayPause()
}
}
Text {
text: "⏭"
font.pixelSize: 20
color: Theme.textSecondary
MouseArea {
anchors.fill: parent
anchors.margins: -10
hoverEnabled: true
onEntered: parent.color = Theme.accentVolt
onExited: parent.color = Theme.textSecondary
onClicked: GlobalState.mediaNext()
}
}
}
}
// Section 8: Calendar
ColumnLayout {
id: sectionCalendar
Layout.fillWidth: true
spacing: 16
SectionHeader { sectionNum: "08"; title: "Calendar" }
RowLayout {
spacing: 12
Text {
text: GlobalState.timeString
font.family: Theme.fontMono
font.pixelSize: 22
color: Theme.accentVolt
}
Text {
text: GlobalState.fullDateString
font.family: Theme.fontSans
font.pixelSize: 13
color: Theme.textSecondary
}
}
GridLayout {
columns: 7
columnSpacing: 4
rowSpacing: 4
Layout.alignment: Qt.AlignHCenter
Repeater {
model: ["S", "M", "T", "W", "T", "F", "S"]
Text {
text: modelData
font.family: Theme.fontMono
font.pixelSize: 11
color: Theme.textMuted
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: 32
}
}
Repeater {
model: 31
Rectangle {
property bool isToday: (index + 1) === new Date().getDate()
Layout.preferredWidth: 32
Layout.preferredHeight: 32
color: isToday ? Theme.accentVolt : "transparent"
radius: isToday ? 0 : 4
Text {
anchors.centerIn: parent
text: index + 1
color: parent.isToday ? "#ffffff" : Theme.textSecondary
font.family: Theme.fontMono
font.pixelSize: 12
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: if (!parent.isToday) parent.color = Qt.rgba(1, 1, 1, 0.04)
onExited: if (!parent.isToday) parent.color = "transparent"
}
}
}
}
}
Item { Layout.preferredHeight: 20 } // Bottom padding
}
}
}
// Auto-scroll to active section when changed
Connections {
target: GlobalState
function onSystemHudActiveSectionChanged() {
if (!GlobalState.systemHudVisible) return;
let targetY = 0;
if (GlobalState.systemHudActiveSection === "volume") targetY = sectionVolume.y;
else if (GlobalState.systemHudActiveSection === "brightness") targetY = sectionBrightness.y;
else if (GlobalState.systemHudActiveSection === "performance") targetY = sectionPerformance.y;
else if (GlobalState.systemHudActiveSection === "network") targetY = sectionNetwork.y;
else if (GlobalState.systemHudActiveSection === "battery") targetY = sectionBattery.y;
else if (GlobalState.systemHudActiveSection === "nowplaying") targetY = sectionNowPlaying.y;
else if (GlobalState.systemHudActiveSection === "calendar") targetY = sectionCalendar.y;
if (targetY > 0) {
// Ensure targetY doesn't scroll past max possible scroll
let maxY = Math.max(0, contentLayout.height - scrollView.height);
scrollView.ScrollBar.vertical.position = Math.min(targetY, maxY) / contentLayout.height;
}
}
}
}

View File

@@ -0,0 +1,257 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import "../theme"
import ".."
PanelWindow {
id: systemInfoDialog
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
anchors {
top: true
bottom: true
left: true
right: true
}
visible: GlobalState.aboutMacVisible
color: Qt.rgba(0, 0, 0, 0.35)
// Close on click outside or Escape
MouseArea {
anchors.fill: parent
onClicked: GlobalState.aboutMacVisible = false
}
Item {
anchors.fill: parent
focus: true
Keys.onEscapePressed: GlobalState.aboutMacVisible = false
}
property string cpuName: ""
property string gpuName: ""
// Process to get CPU info
Process {
id: cpuProcess
command: ["sh", "-c", "grep 'model name' /proc/cpuinfo | head -n1 | cut -d':' -f2 | xargs"]
running: true
stdout: StdioCollector {
onDataChanged: {
const val = String(data).trim();
if (val.length > 0) systemInfoDialog.cpuName = val;
}
}
}
// Process to get GPU info
Process {
id: gpuProcess
command: ["sh", "-c", "nvidia-smi --query-gpu=name --format=csv,noheader | head -n1"]
running: true
stdout: StdioCollector {
onDataChanged: {
const val = String(data).trim();
if (val.length > 0) systemInfoDialog.gpuName = val;
}
}
}
Rectangle {
id: dialogBox
width: 480
height: 320
anchors.centerIn: parent
color: Theme.glassBg
border.color: Theme.glassBorder
border.width: 1
radius: 0
MouseArea {
anchors.fill: parent
// Eat clicks so they don't close the dialog
}
ColumnLayout {
anchors.fill: parent
spacing: 0
// Header bar
Item {
Layout.fillWidth: true
Layout.preferredHeight: 40
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: Theme.glassBorder
}
Rectangle {
id: closeBtn
width: 40
height: 40
anchors.left: parent.left
color: closeMouseArea.containsMouse ? Theme.accentCoral : "transparent"
Text {
anchors.centerIn: parent
text: "✕"
color: closeMouseArea.containsMouse ? Theme.textPrimary : Theme.textSecondary
font.pixelSize: 14
}
MouseArea {
id: closeMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.aboutMacVisible = false
}
}
Text {
anchors.centerIn: parent
text: "System Info"
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: 15
color: Theme.textPrimary
}
}
// Body
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Column {
anchors.centerIn: parent
spacing: 8
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "Silicon Volt"
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: 24
color: Theme.accentVolt
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "NixOS // Hyprland"
font.family: Theme.fontMono
font.pixelSize: 12
color: Theme.textMuted
bottomPadding: 16
}
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 2
spacing: 12
rowSpacing: 8
// HOST
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "HOST"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { text: "hades"; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
// CPU
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "CPU"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { id: cpuText; text: systemInfoDialog.cpuName || "Loading..."; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
// MEMORY
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "MEMORY"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { text: GlobalState.memUsedGb + " / " + GlobalState.memTotalGb + " GB"; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
// GPU
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "GPU"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { id: gpuText; text: systemInfoDialog.gpuName || "Loading..."; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
// VRAM
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "VRAM"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { text: GlobalState.gpuVramUsed + " / " + GlobalState.gpuVramTotal + " GB"; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
// WM
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "WM"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { text: "Hyprland"; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
// OS
Text { width: 100; horizontalAlignment: Text.AlignRight; text: "OS"; font.family: Theme.fontMono; font.pixelSize: 11; color: Theme.textMuted }
Text { text: "NixOS"; font.family: Theme.fontSans; font.pixelSize: 13; color: Theme.textSecondary }
}
}
}
// Footer bar
Item {
Layout.fillWidth: true
Layout.preferredHeight: 44
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: Theme.glassBorder
}
component FooterButton: Rectangle {
id: btn
property string text: ""
property alias command: proc.command
width: btnText.width + 24
height: 28
anchors.verticalCenter: parent.verticalCenter
color: btnMouse.containsMouse ? Qt.rgba(Theme.accentVolt.r, Theme.accentVolt.g, Theme.accentVolt.b, 0.1) : "transparent"
border.color: Theme.glassBorder
border.width: 1
radius: 0
Text {
id: btnText
anchors.centerIn: parent
text: btn.text
font.family: Theme.fontSans
font.pixelSize: 12
color: btnMouse.containsMouse ? Theme.accentVolt : Theme.textMuted
}
MouseArea {
id: btnMouse
anchors.fill: parent
hoverEnabled: true
onClicked: proc.running = true
}
Process {
id: proc
}
}
FooterButton {
anchors.left: parent.left
anchors.leftMargin: 16
text: "System Report..."
command: ["foot", "-e", "btop"]
}
FooterButton {
anchors.right: parent.right
anchors.rightMargin: 16
text: "Software Update..."
command: ["foot", "-e", "sudo", "nixos-rebuild", "dry-activate"]
}
}
}
}
}

View File

@@ -0,0 +1,101 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import "../theme"
import ".."
PanelWindow {
id: root
property var modelData
screen: modelData
anchors {
top: true
left: true
right: true
}
margins.left: Theme.sidebarWidth
implicitHeight: Theme.topStripHeight
color: Qt.rgba(Theme.bgPrimary.r, Theme.bgPrimary.g, Theme.bgPrimary.b, 0.92)
exclusiveZone: Theme.topStripHeight
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "quickshell-topstrip"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
Rectangle {
id: bottomBorder
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: 1
color: Theme.glassBorder
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16
// Left Section
Text {
id: workspaceIndicator
Layout.alignment: Qt.AlignVCenter
font.family: Theme.fontMono
font.pixelSize: 12
color: Theme.textMuted
function pad(num) {
if (num === undefined || num === null) return "00";
return num < 10 ? "0" + num : num.toString();
}
text: pad(GlobalState.activeWorkspace) + " // " + (GlobalState.workspaceNames[GlobalState.activeWorkspace] !== undefined ? GlobalState.workspaceNames[GlobalState.activeWorkspace] : "main")
Behavior on color {
ColorAnimation { duration: 150 }
}
}
// Center Section
Text {
id: windowTitle
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: Theme.fontSizeLarge
color: Theme.textSecondary
elide: Text.ElideRight
maximumLineCount: 1
text: GlobalState.activeWindowTitle
Behavior on color {
ColorAnimation { duration: 150 }
}
}
// Right Section
Text {
id: clock
Layout.alignment: Qt.AlignVCenter
font.family: Theme.fontMono
font.pixelSize: 12
color: Theme.textPrimary
text: GlobalState.timeString
Behavior on color {
ColorAnimation { duration: 150 }
}
}
}
}