Sync config from arch

- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
This commit is contained in:
asepharyana
2026-09-23 15:19:12 +07:00
commit 1cdb82a76f
300 changed files with 78143 additions and 0 deletions
@@ -0,0 +1,580 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell.Io
import qs.Commons
import qs.Ui
// What happened: outages and disruptions with durations and the leg named,
// plus the copy-report affordance — the ISP-ticket artifact.
Column {
id: tab
required property var panel
spacing: Style.space(12)
Component.onCompleted: {
panel.requestEvents("24h")
ribbonProc.command = ["sh", "-c",
"cd \"$1\" && exec python3 -m nexthopd.cli query --window 24h --resolution minute",
"sh", panel.pluginDir]
ribbonProc.running = true
weekProc.command = ["sh", "-c",
"cd \"$1\" && exec python3 -m nexthopd.cli query --window 7d",
"sh", panel.pluginDir]
weekProc.running = true
}
property var ribbonRows: []
property var weekDays: []
Process {
id: ribbonProc
stdout: StdioCollector {
waitForEnd: true
onStreamFinished: {
try {
var d = JSON.parse(text)
tab.ribbonRows = d.rows || []
} catch (e) {}
}
}
}
Process {
id: weekProc
stdout: StdioCollector {
waitForEnd: true
onStreamFinished: {
try {
var d = JSON.parse(text)
tab.weekDays = tab.foldDays(d.rows || [])
} catch (e) {}
}
}
}
// Hourly rows -> trailing seven local days, averaged experience each.
function foldDays(rows) {
var byDay = {}
for (var i = 0; i < rows.length; i++) {
var r = rows[i]
if (r.idx === null || r.idx === undefined) continue
var d = new Date(r.ts * 1000)
var key = d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate()
var slot = byDay[key] || (byDay[key] = { sum: 0, n: 0, ts: r.ts })
slot.sum += r.idx
slot.n += 1
}
var out = []
var names = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
for (var day = 6; day >= 0; day--) {
var when = new Date(Date.now() - day * 86400 * 1000)
var k = when.getFullYear() * 10000 + (when.getMonth() + 1) * 100 + when.getDate()
var s = byDay[k]
out.push({
name: names[when.getDay()],
idx: s ? Math.round(s.sum / s.n) : null,
})
}
return out
}
function bandColor(idx) {
if (idx === null || idx === undefined)
return Qt.rgba(tab.panel.fg.r, tab.panel.fg.g, tab.panel.fg.b, 0.08)
if (idx >= 90) return "#9ece6a"
if (idx >= 80) return "#b9f27c"
if (idx >= 70) return "#e0af68"
if (idx >= 50) return "#eb927b"
return "#f7768e"
}
readonly property var events: panel.eventsData && panel.eventsData.events
? panel.eventsData.events : []
// Consecutive events of the same kind, this close together, are one
// episode rather than several: a laptop bouncing between two access
// points is a single story, and listing each hop separately buries the
// outages that actually matter under a wall of roams.
readonly property int episodeGapS: 600
// Beyond this the list stops being readable and the report is the right
// tool. Nothing is discarded — the count of what is not shown is stated.
readonly property int maxRows: 12
readonly property var episodes: foldEvents(events)
readonly property var shownEpisodes: episodes.slice(0, maxRows)
readonly property int hiddenEvents: {
var n = 0
for (var i = maxRows; i < episodes.length; i++) n += episodes[i].count
return n
}
// Outages and associations are never folded: each one is its own fact.
function groupable(kind) {
return kind === "roam" || kind === "kick" || kind === "drop"
|| kind === "rate-drop" || kind === "disruption"
|| kind === "icmp-quiet" || kind === "gateway-quiet"
}
function foldEvents(list) {
var out = []
for (var i = 0; i < list.length; i++) {
var e = list[i]
var g = out.length ? out[out.length - 1] : null
if (g && g.kind === e.kind && groupable(e.kind)
&& (g.oldestTs - e.ts) <= episodeGapS) {
g.count += 1
g.oldestTs = e.ts
g.members.push(e)
} else {
out.push({kind: e.kind, ts: e.ts, oldestTs: e.ts,
count: 1, members: [e], first: e})
}
}
return out
}
// The first four octets are the same across every access point on one
// site, so they carry no information — only the last two identify which
// radio this was. The stored event keeps the full address for the report.
function shortMac(text) {
return String(text).replace(
/\b(?:[0-9a-fA-F]{2}:){4}([0-9a-fA-F]{2}:[0-9a-fA-F]{2})\b/g, "…$1")
}
function roamTargets(members) {
var seen = []
for (var i = 0; i < members.length; i++) {
var m = /Roamed to ([0-9a-fA-F:]{17})/.exec(members[i].detail || "")
if (!m) continue
var short = shortMac(m[1])
if (seen.indexOf(short) < 0) seen.push(short)
}
return seen
}
// Which access points did the kicking, and every distinct reason given.
function kickSources(members) {
var aps = [], whys = []
for (var i = 0; i < members.length; i++) {
var m = /Kicked by AP ([0-9a-fA-F:]{17}) \((reason [^)]*)\)/
.exec(members[i].detail || "")
if (!m) continue
var short = shortMac(m[1])
if (aps.indexOf(short) < 0) aps.push(short)
if (whys.indexOf(m[2]) < 0) whys.push(m[2])
}
return {aps: aps, why: whys.length ? whys.join("; ") : null}
}
function lowestRate(members) {
var low = null
for (var i = 0; i < members.length; i++) {
var m = /dropped to (\d+)/.exec(members[i].detail || "")
if (m && (low === null || Number(m[1]) < low)) low = Number(m[1])
}
return low
}
function describeEpisode(g) {
if (g.count === 1) return describe(g.first)
if (g.kind === "roam") {
var aps = roamTargets(g.members)
return "Roamed " + g.count + "×"
+ (aps.length > 1 ? " between " + aps.join(" ↔ ")
: aps.length === 1 ? " to " + aps[0] : "")
}
if (g.kind === "kick") {
var k = kickSources(g.members)
return "Kicked by AP " + g.count + "\u00d7"
+ (k.aps.length ? " \u2014 " + k.aps.join(", ") : "")
+ (k.why ? " (" + k.why + ")" : "")
}
if (g.kind === "drop")
return "Dropped by this machine " + g.count + "\u00d7"
if (g.kind === "rate-drop") {
var low = lowestRate(g.members)
return "Tx rate dropped " + g.count + "×"
+ (low !== null ? ", lowest " + low + " Mbps" : "")
}
return describe(g.first) + " · " + g.count + "×"
}
// A folded episode reports how long it went on; a single event reports
// its own duration, which for an instant event is nothing.
function episodeDuration(g) {
if (g.count === 1) return duration(g.first)
var s = g.ts - g.oldestTs
if (s < 60) return s + "s"
if (s < 3600) return Math.floor(s / 60) + "m"
return Math.floor(s / 3600) + "h " + Math.floor((s % 3600) / 60) + "m"
}
property bool copied: false
function describe(e) {
if (e.kind === "outage" && e.leg === "wan")
return "No internet. The router still answered, so the fault was upstream."
if (e.kind === "outage" && e.leg === "local")
return "Router unreachable — nothing on the local network answered."
return shortMac(e.detail || e.kind)
}
function duration(e) {
if (e.ended_ts === e.ts) return "\u2014"
if (!e.ended_ts) return "ongoing"
var s = e.ended_ts - e.ts
if (s < 60) return s + "s"
if (s < 3600) return Math.floor(s / 60) + "m " + (s % 60) + "s"
return Math.floor(s / 3600) + "h " + Math.floor((s % 3600) / 60) + "m"
}
Item {
width: parent.width
height: ribbonLabel.implicitHeight
Text {
id: ribbonLabel
textFormat: Text.PlainText
text: "EXPERIENCE, LAST 24 HOURS"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
font.letterSpacing: 1
}
Text {
textFormat: Text.PlainText
anchors.right: parent.right
text: "1-minute buckets"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
Canvas {
id: ribbon
width: parent.width
height: Style.space(22)
readonly property var rows: tab.ribbonRows
onRowsChanged: requestPaint()
onWidthChanged: requestPaint()
onPaint: {
var ctx = getContext("2d")
ctx.reset()
ctx.clearRect(0, 0, width, height)
// The unmonitored floor: minutes with no data stay this dark strip.
ctx.fillStyle = Qt.rgba(tab.panel.fg.r, tab.panel.fg.g,
tab.panel.fg.b, 0.06)
ctx.fillRect(0, 0, width, height)
var start = Date.now() / 1000 - 86400
var slice = width / 1440
for (var i = 0; i < rows.length; i++) {
var r = rows[i]
if (r.idx === null || r.idx === undefined) continue
var x = (r.ts - start) / 86400 * width
if (x < 0 || x > width) continue
ctx.fillStyle = tab.bandColor(r.idx)
ctx.fillRect(x, 0, Math.max(1, slice + 0.5), height)
}
}
}
Item {
width: parent.width
height: axisLeft.implicitHeight
Text {
id: axisLeft
textFormat: Text.PlainText
text: {
var d = new Date(Date.now() - 86400 * 1000)
return d.toLocaleString(Qt.locale(), "HH:mm") + " yest."
}
color: Qt.darker(tab.panel.dim, 1.2)
font.family: tab.panel.fontFamily
font.pixelSize: Style.fontPx(0.75)
}
Text {
textFormat: Text.PlainText
anchors.right: parent.right
text: "now"
color: Qt.darker(tab.panel.dim, 1.2)
font.family: tab.panel.fontFamily
font.pixelSize: Style.fontPx(0.75)
}
}
Row {
spacing: Style.space(12)
component BandKey: Row {
property color tint: "white"
property string label: ""
spacing: Style.space(5)
Rectangle {
width: Style.space(8); height: Style.space(8)
color: parent.tint
anchors.verticalCenter: parent.verticalCenter
}
Text {
textFormat: Text.PlainText
text: parent.label
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
anchors.verticalCenter: parent.verticalCenter
}
}
BandKey { tint: "#9ece6a"; label: "90+" }
BandKey { tint: "#b9f27c"; label: "80" }
BandKey { tint: "#e0af68"; label: "70" }
BandKey { tint: "#eb927b"; label: "50" }
BandKey { tint: "#f7768e"; label: "under 50" }
}
PanelSeparator { width: parent.width }
Text {
textFormat: Text.PlainText
text: "LAST 7 DAYS"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
font.letterSpacing: 1
}
Row {
width: parent.width
spacing: Style.space(8)
readonly property real cell: (width - Style.space(8) * 6) / 7
Repeater {
model: tab.weekDays
Column {
id: dayCol
required property var modelData
width: parent.cell
spacing: Style.space(4)
Text {
textFormat: Text.PlainText
anchors.horizontalCenter: parent.horizontalCenter
text: dayCol.modelData.idx === null ? "·" : String(dayCol.modelData.idx)
color: dayCol.modelData.idx === null
? tab.panel.dim : tab.panel.fg
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
Rectangle {
width: parent.width
height: Style.space(30)
color: Qt.rgba(tab.panel.fg.r, tab.panel.fg.g, tab.panel.fg.b, 0.07)
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: dayCol.modelData.idx === null
? 0 : parent.height * Math.max(0.08, dayCol.modelData.idx / 100)
color: tab.bandColor(dayCol.modelData.idx)
}
}
Text {
textFormat: Text.PlainText
anchors.horizontalCenter: parent.horizontalCenter
text: dayCol.modelData.name
color: Qt.darker(tab.panel.dim, 1.2)
font.family: tab.panel.fontFamily
font.pixelSize: Style.fontPx(0.75)
}
}
}
}
PanelSeparator { width: parent.width }
Item {
width: parent.width
height: eventsLabel.implicitHeight
Text {
id: eventsLabel
textFormat: Text.PlainText
text: "WHAT HAPPENED · LAST 24 H"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
font.letterSpacing: 1
}
Text {
textFormat: Text.PlainText
anchors.right: parent.right
text: tab.events.length === 0 ? "" :
tab.events.length + (tab.events.length === 1 ? " event" : " events")
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
Text {
textFormat: Text.PlainText
visible: tab.events.length === 0
text: "Nothing to report. A quiet log is the good outcome."
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
Column {
width: parent.width
spacing: Style.space(9)
Repeater {
model: tab.shownEpisodes
Row {
id: eventRow
required property var modelData
width: parent.width
spacing: Style.space(10)
readonly property color tone: {
var k = modelData.kind
// Two link events keep colours of their own: a roam is purple
// because it is neither good nor bad, an association is the link
// coming up. Everything else takes the severity the daemon stored
// WITH the event, so a kind added on the daemon side arrives
// coloured. This used to be a second table keyed by kind, and
// 0.2.4's gateway-quiet shipped warn-toned in the database and
// accent-toned here because that table never learned of it.
if (k === "roam") return "#bb9af7"
if (k === "associate") return tab.panel.okTone
var s = modelData.first ? modelData.first.severity : undefined
if (s === "critical") return Color.urgent
if (s === "warn") return tab.panel.warnTone
// Rows written before 0.2.13 stored every link fault as "info".
if (k === "kick" || k === "drop" || k === "rate-drop")
return tab.panel.warnTone
return Color.accent
}
Text {
textFormat: Text.PlainText
width: Style.space(64)
text: {
var d = new Date(eventRow.modelData.ts * 1000)
return d.toLocaleString(Qt.locale(), "ddd HH:mm")
}
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
Rectangle {
width: Style.space(5)
height: Style.space(5)
color: eventRow.tone
anchors.verticalCenter: parent.verticalCenter
}
Text {
textFormat: Text.PlainText
width: parent.width - Style.space(64) - Style.space(5)
- Style.space(54) - Style.space(10) * 3
text: tab.describeEpisode(eventRow.modelData)
color: tab.panel.fg
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
wrapMode: Text.WordWrap
}
Text {
textFormat: Text.PlainText
width: Style.space(54)
horizontalAlignment: Text.AlignRight
text: tab.episodeDuration(eventRow.modelData)
color: eventRow.modelData.count === 1 && !eventRow.modelData.first.ended_ts
? Color.urgent : tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
}
}
Text {
width: parent.width
visible: tab.hiddenEvents > 0
textFormat: Text.PlainText
text: "+ " + tab.hiddenEvents + " earlier "
+ (tab.hiddenEvents === 1 ? "event" : "events")
+ " — the copied report has the full list."
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
wrapMode: Text.WordWrap
}
PanelSeparator { width: parent.width }
Item {
width: parent.width
height: Math.max(copyHint.implicitHeight, copyButton.height)
Text {
id: copyHint
textFormat: Text.PlainText
width: parent.width - copyButton.width - Style.space(12)
anchors.verticalCenter: parent.verticalCenter
text: "Copies a plain-text summary of the last 24 hours — timestamps, "
+ "both legs, loss and events. The thing an ISP asks for."
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
wrapMode: Text.WordWrap
}
Rectangle {
id: copyButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: copyText.implicitWidth + Style.space(24)
height: Style.space(28)
color: copyHover.hovered
? Style.hoverFillFor(tab.panel.fg, Color.accent)
: Style.normalFillFor(tab.panel.fg, Color.accent)
border.width: Style.normalBorderWidth
border.color: Style.normalBorderFor(tab.panel.fg, Color.accent)
Text {
id: copyText
textFormat: Text.PlainText
anchors.centerIn: parent
text: tab.copied ? "󰄬 Copied" : "󰆏 Copy report"
color: tab.panel.fg
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
HoverHandler { id: copyHover }
TapHandler {
onTapped: {
tab.panel.copyReport("24h")
tab.copied = true
copiedReset.restart()
}
}
Timer {
id: copiedReset
interval: 2000
onTriggered: tab.copied = false
}
}
}
}