pragma ComponentBehavior: Bound import Quickshell import Quickshell.Io import QtQuick import QtQuick.Layouts import qs.hyprchrome.theme import qs.hyprchrome.widgets.panels // Host identity in the hyprchrome panel chrome: the machine's name set large, // with its timezone and the current date and time. // // Expanded: name on the left, clock stack on the right. // Collapsed: name, time and zone abbreviation on the header line. // // The name and the zone come from one shell call at startup — neither changes // while the shell runs, so there is nothing to poll. The clock is a plain // Timer; `now` is the single source both densities read, so they always agree // down to the second. BarPanel { id: panel panelId: "HST" title: "HOST" meta: panel.zoneAbbrev property string hostName: "LOCAL" property string zoneName: "" // IANA, e.g. EUROPE/BERLIN property string userName: "" // whoever is logged in, e.g. DARMAN property date now: new Date() // Qt resolves the abbreviation ("CEST") against the same zone the offset // comes from, so the two can never disagree. readonly property string zoneAbbrev: Qt.formatDateTime(panel.now, "t") readonly property string utcOffset: { const hours = -panel.now.getTimezoneOffset() / 60; return "UTC" + (hours >= 0 ? "+" : "") + (Number.isInteger(hours) ? hours : hours.toFixed(1)); } function two(value) { return value < 10 ? "0" + value : String(value); } function timeText(value) { return panel.two(value.getHours()) + ":" + panel.two(value.getMinutes()) + ":" + panel.two(value.getSeconds()); } function dateText(value) { const days = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]; const months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]; return days[value.getDay()] + " // " + panel.two(value.getDate()) + " " + months[value.getMonth()] + " " + value.getFullYear(); } Timer { interval: 1000 running: true repeat: true triggeredOnStart: true onTriggered: panel.now = new Date() } Process { running: true // /etc/localtime is a symlink into the zoneinfo tree; its tail is the // IANA name, which no environment variable reliably carries. command: ["sh", "-c", "cat /proc/sys/kernel/hostname; readlink -f /etc/localtime; id -un"] stdout: StdioCollector { onStreamFinished: { const lines = this.text.trim().split("\n"); if (lines.length > 0 && lines[0].trim().length > 0) panel.hostName = lines[0].trim().toUpperCase(); if (lines.length > 1) { const zone = lines[1].match(/zoneinfo\/(.+)$/); if (zone) panel.zoneName = zone[1].toUpperCase(); } if (lines.length > 2 && lines[2].trim().length > 0) panel.userName = lines[2].trim().toUpperCase(); } } } // Collapsed: who and when, nothing else. summary: Row { spacing: 10 Text { id: hostLabel text: panel.hostName color: Theme.text font.family: Theme.displayFont font.pixelSize: 13 font.bold: true font.letterSpacing: 1.2 } // The pieces are set at two sizes. A Row positions its children at the // top and has no item alignment of its own (that is Grid), and a // vertical anchor inside a positioner is ignored — so the smaller // pieces take the tallest one's height and centre their text in it. Text { text: panel.timeText(panel.now) color: Theme.accent font.family: Theme.displayFont font.pixelSize: 13 font.bold: true height: hostLabel.implicitHeight verticalAlignment: Text.AlignVCenter } Text { text: panel.dateText(panel.now) color: Theme.text font.family: Theme.microFont font.pixelSize: 11 height: hostLabel.implicitHeight verticalAlignment: Text.AlignVCenter } Text { text: panel.zoneAbbrev color: Theme.muted font.family: Theme.microFont font.pixelSize: 11 height: hostLabel.implicitHeight verticalAlignment: Text.AlignVCenter } } // Expanded: name left, clock stack right. RowLayout { width: parent.width height: 48 spacing: 16 Column { Layout.fillWidth: true Layout.fillHeight: true Text { id: hostText text: panel.hostName color: Theme.text font.family: Theme.displayFont font.pixelSize: 25 font.bold: true font.letterSpacing: 2 elide: Text.ElideRight } Text { y: 32 text: (panel.userName || "NODE") + " // " + (panel.zoneName || "LOCAL") color: Theme.accent font.family: Theme.microFont font.pixelSize: 9 font.letterSpacing: 1.4 elide: Text.ElideRight } } // Rule between identity and clock, the same hairline the dense bar // puts between its host name and node readout. Rectangle { Layout.alignment: Qt.AlignCenter y: 2 width: 2 height: parent.height - 8 color: Theme.hair } Column { id: clock Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignRight width: 210 spacing: 2 Text { width: parent.width text: panel.timeText(panel.now) horizontalAlignment: Text.AlignRight color: Theme.text font.family: Theme.displayFont font.pixelSize: 18 font.bold: true font.letterSpacing: 1 } Text { width: parent.width text: panel.dateText(panel.now) horizontalAlignment: Text.AlignRight color: Theme.accent font.family: Theme.microFont font.pixelSize: 11 } Text { width: parent.width text: panel.zoneAbbrev + " // " + panel.utcOffset horizontalAlignment: Text.AlignRight color: Theme.muted font.family: Theme.microFont font.pixelSize: 9 font.letterSpacing: 0.7 } } } }