Use component-like naming without a repeating namespace

This commit is contained in:
David Heinemeier Hansson
2026-05-25 14:32:41 +02:00
parent 829c1fa4f7
commit e6d990e31d
24 changed files with 92 additions and 92 deletions
@@ -6,7 +6,7 @@ import Quickshell.Services.Mpris
import Quickshell.Services.Pipewire import Quickshell.Services.Pipewire
import qs.Ui import qs.Ui
import qs.Commons import qs.Commons
import "AudioModel.js" as AudioModel import "Model.js" as Model
Panel { Panel {
id: root id: root
@@ -62,11 +62,11 @@ Panel {
// playback streams consistently accept audio input from clients and publish // playback streams consistently accept audio input from clients and publish
// `isSink: true`; capture streams publish as stream sources. // `isSink: true`; capture streams publish as stream sources.
function isPlaybackStream(node) { function isPlaybackStream(node) {
return AudioModel.isPlaybackStream(node) return Model.isPlaybackStream(node)
} }
function isAudioSource(node) { function isAudioSource(node) {
return AudioModel.isAudioSource(node) return Model.isAudioSource(node)
} }
property var cachedAudioSinks: [] property var cachedAudioSinks: []
@@ -263,7 +263,7 @@ Panel {
onAudioStreamsChanged: scheduleDisplayAudioModelRefresh() onAudioStreamsChanged: scheduleDisplayAudioModelRefresh()
function listSnapshot(list) { function listSnapshot(list) {
return AudioModel.listSnapshot(list) return Model.listSnapshot(list)
} }
function refreshDisplayAudioModels() { function refreshDisplayAudioModels() {
@@ -354,7 +354,7 @@ Panel {
// panel's brightnessName ladder; bands are wide enough that small // panel's brightnessName ladder; bands are wide enough that small
// tweaks don't rename the room you're in. // tweaks don't rename the room you're in.
function outputVolumeName(volume, muted) { function outputVolumeName(volume, muted) {
return AudioModel.outputVolumeName(volume, muted) return Model.outputVolumeName(volume, muted)
} }
function setOutputVolume(v) { function setOutputVolume(v) {
@@ -406,83 +406,83 @@ Panel {
} }
function updateSinkAvailability(raw) { function updateSinkAvailability(raw) {
sinkAvailability = AudioModel.parseSinkAvailability(raw) sinkAvailability = Model.parseSinkAvailability(raw)
sinkAvailabilityLoaded = true sinkAvailabilityLoaded = true
} }
function friendlyDeviceLabel(text) { function friendlyDeviceLabel(text) {
return AudioModel.friendlyDeviceLabel(text) return Model.friendlyDeviceLabel(text)
} }
function nodeLabel(node) { function nodeLabel(node) {
return AudioModel.nodeLabel(node) return Model.nodeLabel(node)
} }
function nodeProps(node) { function nodeProps(node) {
return AudioModel.nodeProps(node) return Model.nodeProps(node)
} }
function isHeadphones(node) { function isHeadphones(node) {
return AudioModel.isHeadphones(node) return Model.isHeadphones(node)
} }
function sinkGlyph(node) { function sinkGlyph(node) {
return AudioModel.sinkGlyph(node) return Model.sinkGlyph(node)
} }
function sourceGlyph(node) { function sourceGlyph(node) {
return AudioModel.sourceGlyph(node) return Model.sourceGlyph(node)
} }
function friendlyStreamLabel(label) { function friendlyStreamLabel(label) {
return AudioModel.friendlyStreamLabel(label) return Model.friendlyStreamLabel(label)
} }
function streamLabelKey(label) { function streamLabelKey(label) {
return AudioModel.streamLabelKey(label) return Model.streamLabelKey(label)
} }
function streamLabelIsGeneric(label) { function streamLabelIsGeneric(label) {
return AudioModel.streamLabelIsGeneric(label) return Model.streamLabelIsGeneric(label)
} }
function rawStreamLabel(node) { function rawStreamLabel(node) {
return AudioModel.rawStreamLabel(node) return Model.rawStreamLabel(node)
} }
function mprisPlayerLabel(player) { function mprisPlayerLabel(player) {
return AudioModel.mprisPlayerLabel(player) return Model.mprisPlayerLabel(player)
} }
function mprisPlayerIsProxy(player) { function mprisPlayerIsProxy(player) {
return AudioModel.mprisPlayerIsProxy(player) return Model.mprisPlayerIsProxy(player)
} }
function streamRepresentsMprisPlayer(streamLabel, playerLabel) { function streamRepresentsMprisPlayer(streamLabel, playerLabel) {
return AudioModel.streamRepresentsMprisPlayer(streamLabel, playerLabel) return Model.streamRepresentsMprisPlayer(streamLabel, playerLabel)
} }
function mprisLabelsFor(predicate) { function mprisLabelsFor(predicate) {
return AudioModel.mprisLabelsFor(mprisPlayers, predicate) return Model.mprisLabelsFor(mprisPlayers, predicate)
} }
function matchingMprisStreamLabel(label) { function matchingMprisStreamLabel(label) {
return AudioModel.matchingMprisStreamLabel(label, mprisPlayers) return Model.matchingMprisStreamLabel(label, mprisPlayers)
} }
function unmatchedMprisStreamLabel(label) { function unmatchedMprisStreamLabel(label) {
// Spotify exposes its PipeWire stream as "audio-src". For generic stream // Spotify exposes its PipeWire stream as "audio-src". For generic stream
// names, use the one MPRIS player not already represented by another audio // names, use the one MPRIS player not already represented by another audio
// stream (e.g. Chromium, or ALSA apps like cliamp). // stream (e.g. Chromium, or ALSA apps like cliamp).
return AudioModel.unmatchedMprisStreamLabel(label, mprisPlayers, displayAudioStreams) return Model.unmatchedMprisStreamLabel(label, mprisPlayers, displayAudioStreams)
} }
function streamLabel(node) { function streamLabel(node) {
return AudioModel.streamLabel(node, mprisPlayers, displayAudioStreams) return Model.streamLabel(node, mprisPlayers, displayAudioStreams)
} }
function streamRepresentsPlayer(node, player) { function streamRepresentsPlayer(node, player) {
return AudioModel.streamRepresentsPlayer(node, player, mprisPlayers, displayAudioStreams) return Model.streamRepresentsPlayer(node, player, mprisPlayers, displayAudioStreams)
} }
implicitWidth: button.implicitWidth implicitWidth: button.implicitWidth
+1 -1
View File
@@ -9,7 +9,7 @@
"bar-widget" "bar-widget"
], ],
"entryPoints": { "entryPoints": {
"barWidget": "BarWidget.qml" "barWidget": "Panel.qml"
}, },
"barWidget": { "barWidget": {
"displayName": "Audio", "displayName": "Audio",
@@ -5,7 +5,7 @@ import Quickshell.Io
import Quickshell.Bluetooth import Quickshell.Bluetooth
import qs.Ui import qs.Ui
import qs.Commons import qs.Commons
import "BluetoothModel.js" as BluetoothModel import "Model.js" as Model
Panel { Panel {
id: root id: root
@@ -21,22 +21,22 @@ Panel {
readonly property var devices: Bluetooth.devices ? Bluetooth.devices.values : [] readonly property var devices: Bluetooth.devices ? Bluetooth.devices.values : []
function deviceLabel(device) { function deviceLabel(device) {
return BluetoothModel.deviceLabel(device) return Model.deviceLabel(device)
} }
function isUuidLike(value) { function isUuidLike(value) {
return BluetoothModel.isUuidLike(value) return Model.isUuidLike(value)
} }
function isAddressLike(value) { function isAddressLike(value) {
return BluetoothModel.isAddressLike(value) return Model.isAddressLike(value)
} }
function hasHumanName(device) { function hasHumanName(device) {
return BluetoothModel.hasHumanName(device) return Model.hasHumanName(device)
} }
readonly property var deviceGroups: BluetoothModel.deviceLists(devices) readonly property var deviceGroups: Model.deviceLists(devices)
readonly property var connectedDevices: deviceGroups.connected || [] readonly property var connectedDevices: deviceGroups.connected || []
readonly property var knownDevices: deviceGroups.known || [] readonly property var knownDevices: deviceGroups.known || []
readonly property var discoveredDevices: deviceGroups.discovered || [] readonly property var discoveredDevices: deviceGroups.discovered || []
@@ -105,11 +105,11 @@ Panel {
} }
readonly property var visibleSections: { readonly property var visibleSections: {
return BluetoothModel.visibleSections(deviceGroups, adapter && adapter.discovering) return Model.visibleSections(deviceGroups, adapter && adapter.discovering)
} }
function devicesForSection(section) { function devicesForSection(section) {
return BluetoothModel.sectionDevices(deviceGroups, section) return Model.sectionDevices(deviceGroups, section)
} }
function deviceAt(section, index) { function deviceAt(section, index) {
@@ -118,16 +118,16 @@ Panel {
} }
function cloneMap(map) { function cloneMap(map) {
return BluetoothModel.cloneMap(map) return Model.cloneMap(map)
} }
function pendingAction(address) { function pendingAction(address) {
return BluetoothModel.pendingAction(pendingActions, address) return Model.pendingAction(pendingActions, address)
} }
function setPendingAction(address, action) { function setPendingAction(address, action) {
if (!address) return if (!address) return
pendingActions = BluetoothModel.withPendingAction(pendingActions, address, action) pendingActions = Model.withPendingAction(pendingActions, address, action)
if (action) pendingTimeout.restart() if (action) pendingTimeout.restart()
} }
+1 -1
View File
@@ -9,7 +9,7 @@
"bar-widget" "bar-widget"
], ],
"entryPoints": { "entryPoints": {
"barWidget": "BarWidget.qml" "barWidget": "Panel.qml"
}, },
"barWidget": { "barWidget": {
"displayName": "Bluetooth", "displayName": "Bluetooth",
+1 -1
View File
@@ -407,7 +407,7 @@ Item {
color: Qt.darker(root.foreground, 1.4) color: Qt.darker(root.foreground, 1.4)
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: Style.font.bodySmall font.pixelSize: Style.font.bodySmall
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/audio/BarWidget.qml for the canonical recipe." text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/audio/Panel.qml for the canonical recipe."
} }
Text { Text {
width: parent.width width: parent.width
@@ -4,7 +4,7 @@ import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Ui import qs.Ui
import qs.Commons import qs.Commons
import "MonitorModel.js" as MonitorModel import "Model.js" as Model
Panel { Panel {
id: root id: root
@@ -203,7 +203,7 @@ Panel {
} }
function setBrightness(value) { function setBrightness(value) {
var percent = MonitorModel.clampBrightness(value) var percent = Model.clampBrightness(value)
root.brightnessPercent = percent root.brightnessPercent = percent
root.pendingBrightnessPercent = percent root.pendingBrightnessPercent = percent
@@ -218,23 +218,23 @@ Panel {
} }
function previewBrightness(value) { function previewBrightness(value) {
root.brightnessPercent = MonitorModel.clampBrightness(value) root.brightnessPercent = Model.clampBrightness(value)
brightnessDebounce.restart() brightnessDebounce.restart()
} }
function normalizeScale(scale) { function normalizeScale(scale) {
return MonitorModel.normalizeScale(scale) return Model.normalizeScale(scale)
} }
// Playful mood-name for a given brightness percent. Bands intentionally // Playful mood-name for a given brightness percent. Bands intentionally
// span ~1020 points so casual tweaks change the label, while small // span ~1020 points so casual tweaks change the label, while small
// nudges within one band don't. // nudges within one band don't.
function brightnessName(percent) { function brightnessName(percent) {
return MonitorModel.brightnessName(percent) return Model.brightnessName(percent)
} }
function updateDisplays(displaysJson) { function updateDisplays(displaysJson) {
var parsed = MonitorModel.parseDisplays(displaysJson) var parsed = Model.parseDisplays(displaysJson)
root.displays = parsed.displays root.displays = parsed.displays
root.enabledDisplayCount = parsed.enabledDisplayCount root.enabledDisplayCount = parsed.enabledDisplayCount
} }
+1 -1
View File
@@ -9,7 +9,7 @@
"bar-widget" "bar-widget"
], ],
"entryPoints": { "entryPoints": {
"barWidget": "BarWidget.qml" "barWidget": "Panel.qml"
}, },
"barWidget": { "barWidget": {
"displayName": "Display", "displayName": "Display",
@@ -5,7 +5,7 @@ import Quickshell.Io
import Quickshell.Networking import Quickshell.Networking
import qs.Ui import qs.Ui
import qs.Commons import qs.Commons
import "NetworkModel.js" as NetworkModel import "Model.js" as Model
Panel { Panel {
id: root id: root
@@ -229,7 +229,7 @@ Panel {
property string frequency: "" property string frequency: ""
function updateNetwork(raw) { function updateNetwork(raw) {
var parsed = NetworkModel.parseNetworkStatus(raw) var parsed = Model.parseNetworkStatus(raw)
kind = parsed.kind kind = parsed.kind
label = parsed.label label = parsed.label
signalStrength = parsed.signalStrength signalStrength = parsed.signalStrength
@@ -241,7 +241,7 @@ Panel {
Quickshell.execDetached(["bash", "-lc", "printf %s " + Util.shellQuote(value) + " | wl-copy"]) Quickshell.execDetached(["bash", "-lc", "printf %s " + Util.shellQuote(value) + " | wl-copy"])
} }
readonly property string icon: NetworkModel.connectionIcon(kind, signalStrength) readonly property string icon: Model.connectionIcon(kind, signalStrength)
function refresh(scanWifi) { function refresh(scanWifi) {
if (scanWifi === undefined) scanWifi = false if (scanWifi === undefined) scanWifi = false
@@ -263,25 +263,25 @@ Panel {
} }
function formatHeaderSpeed(mbps) { function formatHeaderSpeed(mbps) {
return NetworkModel.formatHeaderSpeed(mbps) return Model.formatHeaderSpeed(mbps)
} }
function formatHeaderFreq(mhz) { function formatHeaderFreq(mhz) {
return NetworkModel.formatHeaderFreq(mhz) return Model.formatHeaderFreq(mhz)
} }
function headerDetail() { function headerDetail() {
return NetworkModel.headerDetail(info) return Model.headerDetail(info)
} }
function updateDetails(raw) { function updateDetails(raw) {
var next = NetworkModel.parseKeyValue(raw) var next = Model.parseKeyValue(raw)
info = next info = next
updateThroughput(next) updateThroughput(next)
} }
function updateThroughput(next) { function updateThroughput(next) {
var state = NetworkModel.throughputState({ var state = Model.throughputState({
prevIface: prevIface, prevIface: prevIface,
prevRxBytes: prevRxBytes, prevRxBytes: prevRxBytes,
prevTxBytes: prevTxBytes, prevTxBytes: prevTxBytes,
@@ -299,11 +299,11 @@ Panel {
} }
function formatBytes(bytes) { function formatBytes(bytes) {
return NetworkModel.formatBytes(bytes) return Model.formatBytes(bytes)
} }
function formatRate(bytesPerSec) { function formatRate(bytesPerSec) {
return NetworkModel.formatRate(bytesPerSec) return Model.formatRate(bytesPerSec)
} }
function findDevice(type) { function findDevice(type) {
@@ -330,20 +330,20 @@ Panel {
var network = networks[i] var network = networks[i]
if (!network) continue if (!network) continue
checkActionCompletion(network) checkActionCompletion(network)
var row = NetworkModel.wifiRow(network) var row = Model.wifiRow(network)
if (row) nets.push(row) if (row) nets.push(row)
} }
wifiNetworks = NetworkModel.sortWifiRows(nets) wifiNetworks = Model.sortWifiRows(nets)
wifiStationAvailable = !!wifiDevice wifiStationAvailable = !!wifiDevice
scanning = false scanning = false
} }
function wifiSectionTitle(index) { function wifiSectionTitle(index) {
return NetworkModel.wifiSectionTitle(wifiNetworks, index) return Model.wifiSectionTitle(wifiNetworks, index)
} }
function wifiIconFor(strength) { function wifiIconFor(strength) {
return NetworkModel.wifiIconFor(strength) return Model.wifiIconFor(strength)
} }
function updateDns(raw) { function updateDns(raw) {
@@ -374,7 +374,7 @@ Panel {
} }
function isProtected(security) { function isProtected(security) {
return NetworkModel.isProtected(security, WifiSecurityType.Open) return Model.isProtected(security, WifiSecurityType.Open)
} }
function openPasswordPrompt(ssid) { function openPasswordPrompt(ssid) {
@@ -432,7 +432,7 @@ Panel {
} }
function networkFailureReason(reason) { function networkFailureReason(reason) {
return NetworkModel.networkFailureReason(reason, { return Model.networkFailureReason(reason, {
NoSecrets: ConnectionFailReason.NoSecrets, NoSecrets: ConnectionFailReason.NoSecrets,
WifiAuthTimeout: ConnectionFailReason.WifiAuthTimeout, WifiAuthTimeout: ConnectionFailReason.WifiAuthTimeout,
WifiNetworkLost: ConnectionFailReason.WifiNetworkLost, WifiNetworkLost: ConnectionFailReason.WifiNetworkLost,
+1 -1
View File
@@ -9,7 +9,7 @@
"bar-widget" "bar-widget"
], ],
"entryPoints": { "entryPoints": {
"barWidget": "BarWidget.qml" "barWidget": "Panel.qml"
}, },
"barWidget": { "barWidget": {
"displayName": "Network", "displayName": "Network",
@@ -4,7 +4,7 @@ import Quickshell.Io
import Quickshell.Services.UPower import Quickshell.Services.UPower
import qs.Commons import qs.Commons
import qs.Ui import qs.Ui
import "PowerModel.js" as PowerModel import "Model.js" as Model
Panel { Panel {
id: root id: root
@@ -31,7 +31,7 @@ Panel {
} }
function selectProfileByDelta(delta) { function selectProfileByDelta(delta) {
profileIndex = PowerModel.selectProfileIndex(profileIndex, delta, profiles) profileIndex = Model.selectProfileIndex(profileIndex, delta, profiles)
} }
function activateSelectedProfile() { function activateSelectedProfile() {
@@ -41,16 +41,16 @@ Panel {
function batteryIcon() { function batteryIcon() {
var device = UPower.displayDevice var device = UPower.displayDevice
return PowerModel.batteryIcon(device, UPower.onBattery, upowerStates()) return Model.batteryIcon(device, UPower.onBattery, upowerStates())
} }
function modeLabel() { function modeLabel() {
var device = UPower.displayDevice var device = UPower.displayDevice
return PowerModel.modeLabel(device, UPower.onBattery, upowerStates()) return Model.modeLabel(device, UPower.onBattery, upowerStates())
} }
function profileIcon(name) { function profileIcon(name) {
return PowerModel.profileIcon(name) return Model.profileIcon(name)
} }
readonly property bool fullyCharged: { readonly property bool fullyCharged: {
@@ -59,7 +59,7 @@ Panel {
} }
readonly property bool chargeThresholdActive: { readonly property bool chargeThresholdActive: {
var device = UPower.displayDevice var device = UPower.displayDevice
return PowerModel.chargeThresholdActive(device, UPower.onBattery, upowerStates()) return Model.chargeThresholdActive(device, UPower.onBattery, upowerStates())
} }
readonly property bool batteryFull: fullyCharged || (!UPower.onBattery && batteryFraction >= 1) readonly property bool batteryFull: fullyCharged || (!UPower.onBattery && batteryFraction >= 1)
readonly property bool batteryFlowIdle: batteryFull || chargeThresholdActive readonly property bool batteryFlowIdle: batteryFull || chargeThresholdActive
@@ -67,7 +67,7 @@ Panel {
// 0..1 charge level, used by the visual progress bar. // 0..1 charge level, used by the visual progress bar.
readonly property real batteryFraction: { readonly property real batteryFraction: {
var d = UPower.displayDevice var d = UPower.displayDevice
return PowerModel.batteryFraction(d) return Model.batteryFraction(d)
} }
readonly property bool batteryLow: UPower.onBattery && batteryFraction > 0 && batteryFraction <= 0.2 readonly property bool batteryLow: UPower.onBattery && batteryFraction > 0 && batteryFraction <= 0.2
@@ -131,7 +131,7 @@ Panel {
} }
function updateKeyValue(raw, targetName) { function updateKeyValue(raw, targetName) {
var next = PowerModel.parseKeyValue(raw) var next = Model.parseKeyValue(raw)
// Keep last known good data if a refresh briefly returns nothing happens // Keep last known good data if a refresh briefly returns nothing happens
// around AC plug/unplug events. Avoids the section collapsing mid-transition. // around AC plug/unplug events. Avoids the section collapsing mid-transition.
if (Object.keys(next).length === 0) return if (Object.keys(next).length === 0) return
@@ -140,7 +140,7 @@ Panel {
} }
function updateProfiles(raw) { function updateProfiles(raw) {
var parsed = PowerModel.parseProfiles(raw, profileIndex) var parsed = Model.parseProfiles(raw, profileIndex)
// Same guard as battery: preserve the last known profile list across // Same guard as battery: preserve the last known profile list across
// transient empty payloads so the buttons don't blink out. // transient empty payloads so the buttons don't blink out.
if (parsed.profiles.length === 0) return if (parsed.profiles.length === 0) return
+1 -1
View File
@@ -9,7 +9,7 @@
"bar-widget" "bar-widget"
], ],
"entryPoints": { "entryPoints": {
"barWidget": "BarWidget.qml" "barWidget": "Panel.qml"
}, },
"barWidget": { "barWidget": {
"displayName": "Power", "displayName": "Power",
+14 -14
View File
@@ -3,7 +3,7 @@ import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Commons import qs.Commons
import qs.Ui import qs.Ui
import "WeatherModel.js" as WeatherModel import "Model.js" as Model
Panel { Panel {
id: root id: root
@@ -32,7 +32,7 @@ Panel {
property string klass: "" property string klass: ""
function updateWeather(raw) { function updateWeather(raw) {
var data = WeatherModel.parseWeatherStatus(raw) var data = Model.parseWeatherStatus(raw)
label = data.label label = data.label
klass = data.klass klass = data.klass
} }
@@ -83,54 +83,54 @@ Panel {
} }
function buildForecastDays() { function buildForecastDays() {
return WeatherModel.buildForecastDays(report, dailyForecastReport, Qt.formatDate(new Date(), "yyyy-MM-dd")) return Model.buildForecastDays(report, dailyForecastReport, Qt.formatDate(new Date(), "yyyy-MM-dd"))
} }
function openMeteoForecastDays() { function openMeteoForecastDays() {
return WeatherModel.openMeteoForecastDays(dailyForecastReport, Qt.formatDate(new Date(), "yyyy-MM-dd")) return Model.openMeteoForecastDays(dailyForecastReport, Qt.formatDate(new Date(), "yyyy-MM-dd"))
} }
function wttrNextForecastDays() { function wttrNextForecastDays() {
return WeatherModel.wttrNextForecastDays(report, Qt.formatDate(new Date(), "yyyy-MM-dd")) return Model.wttrNextForecastDays(report, Qt.formatDate(new Date(), "yyyy-MM-dd"))
} }
function isFutureForecastDate(dateString) { function isFutureForecastDate(dateString) {
return WeatherModel.isFutureForecastDate(dateString, Qt.formatDate(new Date(), "yyyy-MM-dd")) return Model.isFutureForecastDate(dateString, Qt.formatDate(new Date(), "yyyy-MM-dd"))
} }
function roundedTemp(value) { function roundedTemp(value) {
return WeatherModel.roundedTemp(value) return Model.roundedTemp(value)
} }
function celsiusToFahrenheit(value) { function celsiusToFahrenheit(value) {
return WeatherModel.celsiusToFahrenheit(value) return Model.celsiusToFahrenheit(value)
} }
function formatTemp(value) { function formatTemp(value) {
return WeatherModel.formatTemp(value, useImperial) return Model.formatTemp(value, useImperial)
} }
function dayName(dateString) { function dayName(dateString) {
return WeatherModel.dayName(dateString, function(date) { return Qt.formatDate(date, "dddd") }) return Model.dayName(dateString, function(date) { return Qt.formatDate(date, "dddd") })
} }
// Bare degree value (no unit letter), used in the forecast row. // Bare degree value (no unit letter), used in the forecast row.
function bareTempForDay(day, kind) { function bareTempForDay(day, kind) {
return WeatherModel.bareTempForDay(day, kind, useImperial) return Model.bareTempForDay(day, kind, useImperial)
} }
// Representative icon for a forecast day: the hourly entry nearest noon. // Representative icon for a forecast day: the hourly entry nearest noon.
function dayIcon(day) { function dayIcon(day) {
return WeatherModel.dayIcon(day) return Model.dayIcon(day)
} }
function iconForOpenMeteoCode(code) { function iconForOpenMeteoCode(code) {
return WeatherModel.iconForOpenMeteoCode(code) return Model.iconForOpenMeteoCode(code)
} }
// Mirrors omarchy-weather-icon's wttr.in code → nerd-font glyph mapping. // Mirrors omarchy-weather-icon's wttr.in code → nerd-font glyph mapping.
function iconForCode(code, night) { function iconForCode(code, night) {
return WeatherModel.iconForCode(code, night) return Model.iconForCode(code, night)
} }
Process { Process {
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS' run_node_test <<'JS'
const audio = requireFromRoot('shell/plugins/audio/AudioModel.js') const audio = requireFromRoot('shell/plugins/audio/Model.js')
assert(audio.isPlaybackStream({ isStream: true, isSink: true }), 'audio detects sink-backed playback streams') assert(audio.isPlaybackStream({ isStream: true, isSink: true }), 'audio detects sink-backed playback streams')
assert(audio.isPlaybackStream({ isStream: true, type: 'Stream/Output/Audio' }), 'audio detects typed playback streams') assert(audio.isPlaybackStream({ isStream: true, type: 'Stream/Output/Audio' }), 'audio detects typed playback streams')
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS' run_node_test <<'JS'
const bluetooth = requireFromRoot('shell/plugins/bluetooth/BluetoothModel.js') const bluetooth = requireFromRoot('shell/plugins/bluetooth/Model.js')
assert(bluetooth.isUuidLike('0000110b-0000-1000-8000-00805f9b34fb'), 'bluetooth detects UUID-like names') assert(bluetooth.isUuidLike('0000110b-0000-1000-8000-00805f9b34fb'), 'bluetooth detects UUID-like names')
assert(bluetooth.isAddressLike('AA:BB:CC:DD:EE:FF'), 'bluetooth detects address-like names') assert(bluetooth.isAddressLike('AA:BB:CC:DD:EE:FF'), 'bluetooth detects address-like names')
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS' run_node_test <<'JS'
const monitor = requireFromRoot('shell/plugins/monitor/MonitorModel.js') const monitor = requireFromRoot('shell/plugins/monitor/Model.js')
assertEqual(monitor.clampBrightness(0), 1, 'monitor clamps minimum brightness') assertEqual(monitor.clampBrightness(0), 1, 'monitor clamps minimum brightness')
assertEqual(monitor.clampBrightness(101), 100, 'monitor clamps maximum brightness') assertEqual(monitor.clampBrightness(101), 100, 'monitor clamps maximum brightness')
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS' run_node_test <<'JS'
const network = requireFromRoot('shell/plugins/network/NetworkModel.js') const network = requireFromRoot('shell/plugins/network/Model.js')
assertDeepEqual( assertDeepEqual(
network.parseNetworkStatus('wifi\tCafe WiFi\t78\t5200\n'), network.parseNetworkStatus('wifi\tCafe WiFi\t78\t5200\n'),
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS' run_node_test <<'JS'
const power = requireFromRoot('shell/plugins/power/PowerModel.js') const power = requireFromRoot('shell/plugins/power/Model.js')
const states = { Charging: 1, Discharging: 2, FullyCharged: 3, PendingCharge: 4 } const states = { Charging: 1, Discharging: 2, FullyCharged: 3, PendingCharge: 4 }
assertEqual(power.selectProfileIndex(0, 1, ['balanced', 'performance']), 1, 'power advances profile selection') assertEqual(power.selectProfileIndex(0, 1, ['balanced', 'performance']), 1, 'power advances profile selection')
+1 -1
View File
@@ -5,7 +5,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS' run_node_test <<'JS'
const weather = requireFromRoot('shell/plugins/weather/WeatherModel.js') const weather = requireFromRoot('shell/plugins/weather/Model.js')
assertDeepEqual( assertDeepEqual(
weather.parseWeatherStatus('{"text":"☀","class":"sunny"}'), weather.parseWeatherStatus('{"text":"☀","class":"sunny"}'),