Files
asepharyana 1cdb82a76f 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
2026-09-23 15:19:12 +07:00

485 lines
15 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import qs.Commons
import qs.Ui
import "readout.js" as Readout
import "format.js" as Fmt
// Speed: live throughput, content-check history as paired bars, and the
// last peak result in full.
Column {
id: tab
required property var panel
spacing: Style.space(12)
Component.onCompleted: panel.requestTests()
readonly property var tests: panel.testsData && panel.testsData.tests
? panel.testsData.tests : []
readonly property var contentTests: {
var out = tests.filter(function(t) { return t.kind === "content" && t.ok })
out.reverse() // oldest first for the bars
return out.slice(-12)
}
readonly property var lastPeak: {
for (var i = 0; i < tests.length; i++)
if (tests[i].kind === "peak" && tests[i].ok) return tests[i]
return null
}
readonly property real bestRate: {
var best = 0
for (var i = 0; i < contentTests.length; i++) {
best = Math.max(best, contentTests[i].down_mbps || 0)
best = Math.max(best, contentTests[i].up_mbps || 0)
}
return Math.max(1, best)
}
// ---- live throughput ----------------------------------------------------
Text {
textFormat: Text.PlainText
text: "LIVE THROUGHPUT · LAST 3 MIN"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
font.letterSpacing: 1
}
// Download above the axis, upload mirrored below — the mockup's cluster
// shape. Both directions share one scale so the asymmetry is honest.
Canvas {
id: flowChart
width: parent.width
height: Style.space(84)
property real hoverX: -1
onHoverXChanged: requestPaint()
HoverHandler {
onPointChanged: flowChart.hoverX = hovered ? point.position.x : -1
onHoveredChanged: if (!hovered) flowChart.hoverX = -1
}
readonly property var pts: {
var cut = Date.now() / 1000 - 180
return tab.panel.recentPoints.filter(function(p) { return p.t >= cut })
}
onPtsChanged: requestPaint()
onWidthChanged: requestPaint()
onPaint: {
var ctx = getContext("2d")
ctx.reset()
ctx.clearRect(0, 0, width, height)
var mid = Math.round(height / 2)
ctx.strokeStyle = Qt.rgba(tab.panel.fg.r, tab.panel.fg.g,
tab.panel.fg.b, 0.22)
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(0, mid + 0.5)
ctx.lineTo(width, mid + 0.5)
ctx.stroke()
var p = pts
if (!p || p.length < 2) return
var peak = 10 * 1024
for (var i = 0; i < p.length; i++) {
if (p[i].rx !== null) peak = Math.max(peak, p[i].rx)
if (p[i].tx !== null) peak = Math.max(peak, p[i].tx)
}
peak *= 1.1
var t0 = p[0].t, span = Math.max(1, p[p.length - 1].t - t0)
var half = mid - 2
function draw(key, up, tint) {
var runs = [], cur = []
for (var i = 0; i < p.length; i++) {
var v = p[i][key]
if (v === null || v === undefined) {
if (cur.length > 1) runs.push(cur)
cur = []
} else {
cur.push([(p[i].t - t0) * (width - 1) / span,
mid + (up ? -1 : 1) * half * Math.min(1, v / peak)])
}
}
if (cur.length > 1) runs.push(cur)
for (var r = 0; r < runs.length; r++) {
var run = runs[r]
ctx.beginPath()
ctx.moveTo(run[0][0], mid)
for (var j = 0; j < run.length; j++) ctx.lineTo(run[j][0], run[j][1])
ctx.lineTo(run[run.length - 1][0], mid)
ctx.closePath()
ctx.fillStyle = Qt.rgba(tint.r, tint.g, tint.b, 0.2)
ctx.fill()
ctx.beginPath()
for (j = 0; j < run.length; j++) {
if (j === 0) ctx.moveTo(run[j][0], run[j][1])
else ctx.lineTo(run[j][0], run[j][1])
}
ctx.strokeStyle = tint
ctx.lineWidth = 1.4
ctx.stroke()
}
}
draw("rx", true, Color.accent)
draw("tx", false, tab.panel.warnTone)
// Top-of-scale label so the silhouette has magnitude.
ctx.font = "10px " + tab.panel.fontFamily
ctx.textBaseline = "top"
ctx.fillStyle = tab.panel.dim
ctx.fillText("\u2264 " + Fmt.rate(peak), 4, 3)
if (hoverX >= 0) {
var tAt = t0 + hoverX * span / (width - 1)
var best = null, bestD = Infinity
for (var h = 0; h < p.length; h++) {
var d = Math.abs(p[h].t - tAt)
if (d < bestD) { bestD = d; best = p[h] }
}
if (best) {
var cx = (best.t - t0) * (width - 1) / span
ctx.strokeStyle = Qt.rgba(tab.panel.fg.r, tab.panel.fg.g,
tab.panel.fg.b, 0.4)
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(cx + 0.5, 0)
ctx.lineTo(cx + 0.5, height)
ctx.stroke()
var label = Qt.formatTime(new Date(best.t * 1000), "HH:mm:ss")
+ " · \u2193 " + Fmt.rate(best.rx) + " · \u2191 " + Fmt.rate(best.tx)
Readout.draw(ctx, tab.panel.fontFamily, label, cx, width,
tab.panel.fg, Color.popups.background)
}
}
}
}
Row {
width: parent.width
spacing: Style.space(14)
readonly property real cell: (width - Style.space(14) * 3) / 4
component BigStat: Column {
property string label: ""
property string value: ""
property color tint: tab.panel.fg
spacing: Style.space(3)
Text {
textFormat: Text.PlainText
text: parent.label
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
font.letterSpacing: 1
}
Text {
textFormat: Text.PlainText
text: parent.value
color: parent.tint
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.title
font.weight: Font.Medium
}
}
BigStat {
width: parent.cell
label: "RECEIVING"
tint: Color.accent
value: Fmt.rate(tab.panel.live && tab.panel.live.rates
? tab.panel.live.rates.rx_bps : null)
}
BigStat {
width: parent.cell
label: "SENDING"
tint: tab.panel.warnTone
value: Fmt.rate(tab.panel.live && tab.panel.live.rates
? tab.panel.live.rates.tx_bps : null)
}
BigStat {
width: parent.cell
label: "DOWNLOADED"
value: Fmt.bytes(tab.panel.live && tab.panel.live.rates
? tab.panel.live.rates.rx_total : null)
}
BigStat {
width: parent.cell
label: "UPLOADED"
value: Fmt.bytes(tab.panel.live && tab.panel.live.rates
? tab.panel.live.rates.tx_total : null)
}
}
PanelSeparator { width: parent.width }
// ---- content-check history ---------------------------------------------
Item {
width: parent.width
height: historyLabel.implicitHeight
Text {
id: historyLabel
textFormat: Text.PlainText
text: "CONTENT SPEED · FEEDS YOUR SCORE"
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.contentTests.length + " checks kept"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
Item {
width: parent.width
height: Style.space(80)
visible: tab.contentTests.length > 0
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Qt.rgba(tab.panel.fg.r, tab.panel.fg.g, tab.panel.fg.b, 0.15)
}
Row {
anchors.fill: parent
readonly property real slot: width / Math.max(1, tab.contentTests.length)
Repeater {
model: tab.contentTests
Item {
id: barSlot
required property var modelData
width: parent.slot
height: parent.height
// The pair sits centered in its slot with capped widths, so a
// panel with two checks still reads as two paired results, not
// four unrelated bars.
Row {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
spacing: Style.space(3)
Rectangle {
anchors.bottom: parent.bottom
width: Math.min(Style.space(22), barSlot.width * 0.4)
height: Math.max(2, barSlot.height *
(barSlot.modelData.down_mbps || 0) / tab.bestRate)
color: Color.accent
}
Rectangle {
anchors.bottom: parent.bottom
width: Math.min(Style.space(12), barSlot.width * 0.25)
height: Math.max(2, barSlot.height *
(barSlot.modelData.up_mbps || 0) / tab.bestRate)
color: tab.panel.warnTone
}
}
}
}
}
}
Text {
textFormat: Text.PlainText
visible: tab.contentTests.length === 0
text: "No content checks yet — the first runs shortly after the daemon starts."
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
PanelSeparator { width: parent.width }
// ---- last peak ----------------------------------------------------------
Item {
width: parent.width
height: peakLabel.implicitHeight
Text {
id: peakLabel
textFormat: Text.PlainText
text: tab.lastPeak
? "PEAK · " + new Date(tab.lastPeak.ts * 1000).toLocaleString(Qt.locale(), "d MMM HH:mm").toUpperCase()
: "PEAK SPEED"
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: "informational, not scored"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
}
}
Grid {
width: parent.width
columns: 2
columnSpacing: Style.space(24)
rowSpacing: Style.space(6)
visible: tab.lastPeak !== null
readonly property real cell: (width - Style.space(24)) / 2
readonly property var p: tab.lastPeak
component KvRow: Item {
property string k: ""
property string v: ""
property color vColor: tab.panel.fg
height: kText.implicitHeight
Text {
id: kText
textFormat: Text.PlainText
text: parent.k
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
Text {
textFormat: Text.PlainText
anchors.right: parent.right
width: parent.width - kText.implicitWidth - Style.space(10)
horizontalAlignment: Text.AlignRight
elide: Text.ElideLeft
text: parent.v
color: parent.vColor
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
}
KvRow { width: parent.cell; k: "Download"
v: parent.p ? (parent.p.down_mbps || 0).toFixed(1) + " Mbps" : "" }
KvRow { width: parent.cell; k: "Upload"
v: parent.p && parent.p.up_mbps ? parent.p.up_mbps.toFixed(1) + " Mbps" : "--" }
KvRow { width: parent.cell; k: "Idle ping"
v: parent.p && parent.p.ping_idle ? Math.round(parent.p.ping_idle) + " ms" : "--" }
KvRow {
width: parent.cell; k: "Loaded ping"
v: parent.p && parent.p.ping_loaded ? Math.round(parent.p.ping_loaded) + " ms" : "--"
vColor: parent.p && parent.p.ping_loaded && parent.p.ping_idle
&& parent.p.ping_loaded > parent.p.ping_idle * 3
? tab.panel.warnTone : tab.panel.fg
}
KvRow { width: parent.cell; k: "Data used"
v: parent.p && parent.p.bytes ? (parent.p.bytes / 1e6).toFixed(0) + " MB" : "--" }
KvRow { width: parent.cell; k: "Engine"
v: parent.p ? parent.p.engine : "" }
}
Item {
width: parent.width
height: serverKey.implicitHeight
visible: tab.lastPeak !== null && !!tab.lastPeak.server
Text {
id: serverKey
textFormat: Text.PlainText
text: "Server"
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
Text {
textFormat: Text.PlainText
anchors.right: parent.right
width: parent.width - serverKey.implicitWidth - Style.space(10)
horizontalAlignment: Text.AlignRight
elide: Text.ElideLeft
text: tab.lastPeak && tab.lastPeak.server ? tab.lastPeak.server : ""
color: tab.panel.fg
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
}
Text {
textFormat: Text.PlainText
visible: tab.lastPeak === null
text: "No peak test yet."
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
PanelSeparator { width: parent.width }
Item {
width: parent.width
height: Style.space(28)
Text {
textFormat: Text.PlainText
anchors.verticalCenter: parent.verticalCenter
width: parent.width - runButton.width - Style.space(12)
text: "A peak test saturates the line for ~10 s each way — up to ~600 MB on a fast line. It only runs when you ask."
color: tab.panel.dim
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.caption
wrapMode: Text.WordWrap
}
Rectangle {
id: runButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: runText.implicitWidth + Style.space(24)
height: Style.space(28)
color: runHover.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: runText
textFormat: Text.PlainText
anchors.centerIn: parent
text: tab.panel.live && tab.panel.live.peak_running ? "󰓅 Testing…"
: tab.panel.peakArmed ? "󰓅 Uses data · press again"
: "󰓅 Run test"
color: tab.panel.fg
font.family: tab.panel.fontFamily
font.pixelSize: Style.font.bodySmall
}
HoverHandler { id: runHover }
TapHandler {
enabled: !(tab.panel.live && tab.panel.live.peak_running)
onTapped: { tab.panel.runPeakTest(); refreshTimer.start() }
}
}
}
// While a peak test runs, poll the tests list so the result appears.
Timer {
id: refreshTimer
interval: 3000
repeat: true
running: tab.panel.live && tab.panel.live.peak_running === true
onTriggered: tab.panel.requestTests()
onRunningChanged: if (!running) tab.panel.requestTests()
}
}