feat(quickshell): cap the rail's open chamfers and trace between them
A chamfer with no neighbour behind it now carries the corner it removed, put back OUTSIDE the panel as a detached accent triangle. capGap is the perpendicular distance from the cut, hence the per-axis shift of capGap over root 2: the cap moves along the cut's normal, not along an axis. The trace joining two caps belongs to the RAIL, not the panel — the run it draws is the gap BETWEEN two panels, which no panel can see. HyprChromeBar filters the row down to panels (a slack Item has no `rightChamfer`, so spacers drop out, and dropping them is exactly what makes a trace span them), then joins each right cap to the next left cap: straight, one 45° step at the midpoint of the gap, straight. It meets the middle of each cap's outward face rather than its tip. TestPanel is a staging slot between two spacers. It counts seconds since load, which is the cheapest proof the panel is live, and standing alone it exercises a cap and a trace at both ends — something a rail of butted panels never does. Restructures the module in the same commit, since the moves and the edits above land in the same files: folders are PascalCase, the bar and its widgets moved under Widgets/Bar, and DebugWindow takes the HyprChrome Theme instead of the legacy one. The two singletons are identical today, so that is not a visual fix — it is a palette edit reaching the debug stage in future. Rename detection needs -M40% to follow HyprChromeBar, which grew past the default similarity threshold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U19X5LGTxtq4pb4jdNVivv
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
// amdgpu utilisation, straight off sysfs.
|
||||
//
|
||||
// node_exporter's hwmon collector carries the card's temperatures, power and
|
||||
// clocks — which is where VitalsData gets them — but not its busy percentage,
|
||||
// so this is the one vital that cannot come from the same scrape.
|
||||
//
|
||||
// The card number is globbed rather than pinned: it is card1 on terra today,
|
||||
// but it depends on probe order and moves when a GPU is added or removed.
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
// Poll only while something is showing it, like VitalsData.
|
||||
property bool active: false
|
||||
property int interval: 2000
|
||||
|
||||
property real value: 0 // 0..1 busy
|
||||
property bool ready: false
|
||||
|
||||
onActiveChanged: {
|
||||
if (!root.active)
|
||||
root.ready = false;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: probe
|
||||
|
||||
command: ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | head -n1"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const busy = parseInt(this.text.trim(), 10);
|
||||
if (isFinite(busy)) {
|
||||
root.value = Math.max(0, Math.min(1, busy / 100));
|
||||
root.ready = true;
|
||||
} else {
|
||||
// No amdgpu (or no permission) — leave the meter blank
|
||||
// rather than pinning it at zero, which would read as idle.
|
||||
root.ready = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: root.interval
|
||||
running: root.active
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
if (!probe.running)
|
||||
probe.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user