fix(nosignal): hardware-test feedback round 2 — weather, dashboard, tray
From the 2026-06-15 on-box hardware test (all 6 prior fixes confirmed; these are the new findings, baked into the fork source rather than shipped as pacman-hook patches, per the Phase-2 "we own the shell" approach): - F-W1 (Weather.qml): fetch at startup. Stock only called reload() on a config CHANGE, so a saved location blanked after reboot and an empty/auto location never populated on a fresh boot. Add Component.onCompleted: reload() and switch the hourly Timer to reload() (re-detects auto/IP too). - F-W3 (Weather.qml): 7-day forecast showed "undefined°" in °F mode — only maxTempC/minTempC were stored. Store rounded maxTempF/minTempF (and round °C). - F-W2 (Weather.qml): empty/auto-IP location never populated — the in-shell ipinfo.io fetch didn't set loc (provider-specific; the city path works via the same Requests.get). Repair attempt: defensive JSON parse + error callback + fallback to geojs.io. Compiled Requests module not inspectable, so this is best-effort; a saved city still works regardless. - F-D1a (modules/dashboard/*.qml): square the 25 card radii bound to the compiled Tokens.rounding.* Material defaults (which ignore shell.json rounding.scale=0) so the dashboard matches the square redesign. - F-D1b (Glyphs.qml): remap the weather glyphs to the Material-weather family (every codepoint verified present in JetBrainsMono Nerd Font Mono); the old Font-Awesome codepoints were absent (rendered "?") and thunderstorm / snowing_heavy were unmapped. - F-T1 (NsBar.qml): gate the system-tray pill on SystemTray.items count so it only shows when an app registers a tray icon (was opening an empty panel). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f21e5b5eaf
commit
53c5833de0
16 changed files with 85 additions and 42 deletions
|
|
@ -75,7 +75,7 @@ Item {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Tokens.padding.large
|
||||
|
||||
radius: Tokens.rounding.large
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: "transparent"
|
||||
|
||||
Flickable {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ GridLayout {
|
|||
Layout.preferredWidth: Tokens.sizes.dashboard.userWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: Tokens.rounding.extraLarge
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
User {
|
||||
id: user
|
||||
|
|
@ -37,7 +37,7 @@ GridLayout {
|
|||
Layout.preferredWidth: Tokens.sizes.dashboard.weatherWidth
|
||||
Layout.preferredHeight: weather.implicitHeight
|
||||
|
||||
radius: Tokens.rounding.extraLarge * 1.5
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
SmallWeather {
|
||||
id: weather
|
||||
|
|
@ -49,7 +49,7 @@ GridLayout {
|
|||
Layout.preferredWidth: dateTime.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: Tokens.rounding.large
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
DateTime {
|
||||
id: dateTime
|
||||
|
|
@ -63,7 +63,7 @@ GridLayout {
|
|||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: calendar.implicitHeight
|
||||
|
||||
radius: Tokens.rounding.extraLarge
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
Calendar {
|
||||
id: calendar
|
||||
|
|
@ -78,7 +78,7 @@ GridLayout {
|
|||
Layout.preferredWidth: resources.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: Tokens.rounding.large
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
|
|
@ -92,7 +92,7 @@ GridLayout {
|
|||
Layout.preferredWidth: media.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: Tokens.rounding.extraLarge * 2
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
Media {
|
||||
id: media
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Item {
|
|||
implicitHeight: parent.implicitHeight * 2
|
||||
|
||||
color: Colours.palette.m3primary
|
||||
radius: Tokens.rounding.full
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
|
|
@ -148,7 +148,7 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitHeight: parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2
|
||||
|
||||
radius: Tokens.rounding.medium
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurface
|
||||
onClicked: root.dashState.currentTab = tab.TabBar.index
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ Item {
|
|||
Layout.fillWidth: true
|
||||
implicitHeight: bigInfoRow.implicitHeight + Tokens.padding.small
|
||||
|
||||
radius: Tokens.rounding.extraLarge * 2
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
RowLayout {
|
||||
|
|
@ -155,7 +155,7 @@ Item {
|
|||
Layout.fillWidth: true
|
||||
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
|
||||
|
||||
radius: Tokens.rounding.large
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
ColumnLayout {
|
||||
|
|
@ -209,7 +209,7 @@ Item {
|
|||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 60
|
||||
radius: Tokens.rounding.medium
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
Row {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ CustomMouseArea {
|
|||
|
||||
StateLayer {
|
||||
color: Colours.palette.m3primary
|
||||
radius: pressed ? Tokens.rounding.small : Tokens.rounding.large
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
disabled: {
|
||||
const now = new Date();
|
||||
return root.currMonth === now.getMonth() && root.currYear === now.getFullYear();
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ Item {
|
|||
|
||||
implicitWidth: 10
|
||||
implicitHeight: 10
|
||||
radius: Tokens.rounding.full
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ Item {
|
|||
|
||||
implicitWidth: 15
|
||||
implicitHeight: 15
|
||||
radius: Tokens.rounding.full
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
}
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ Item {
|
|||
anchors.leftMargin: -Tokens.padding.medium
|
||||
y: Tokens.padding.extraSmall
|
||||
|
||||
radius: Tokens.rounding.largeIncreased
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
implicitWidth: wmLabel.implicitWidth + Tokens.padding.medium * 2
|
||||
implicitHeight: wmLabel.implicitHeight + Tokens.padding.small * 2
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ Item {
|
|||
implicitWidth: shape.implicitSize + Tokens.padding.medium * 2
|
||||
implicitHeight: shape.implicitSize + Tokens.padding.medium * 2
|
||||
color: Colours.palette.m3primaryContainer
|
||||
radius: Tokens.rounding.full
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
LoadingIndicator {
|
||||
id: shape
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
anchors.margins: !btn.pressed && btn.containsMouse ? -Tokens.padding.extraSmall : 0
|
||||
group: blobGroup
|
||||
radius: Tokens.rounding.medium
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
Behavior on anchors.margins {
|
||||
Anim {}
|
||||
|
|
@ -50,7 +50,7 @@ Item {
|
|||
implicitHeight: parent.height
|
||||
|
||||
group: blobGroup
|
||||
radius: Tokens.rounding.medium
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
deformScale: 0.00001
|
||||
|
||||
states: State {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ StyledClippingRect {
|
|||
property real animPerc: UPower.displayDevice.percentage
|
||||
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
radius: Tokens.rounding.large
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
implicitWidth: Config.dashboard.performance.showCpu || (Config.dashboard.performance.showGpu && Gpu.type !== Gpu.None) || Config.dashboard.performance.showStorage || Config.dashboard.performance.showMemory ? Tokens.sizes.dashboard.perfBattWidth : Tokens.sizes.dashboard.perfBattWidthSingle
|
||||
implicitHeight: Tokens.sizes.dashboard.perfBattHeight
|
||||
|
|
@ -39,7 +39,7 @@ StyledClippingRect {
|
|||
implicitHeight: parent.height * root.animPerc
|
||||
|
||||
color: Colours.palette.m3secondary
|
||||
radius: Tokens.rounding.extraSmall
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
clip: true
|
||||
|
||||
Contents {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ StyledRect {
|
|||
required property real temperature
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Tokens.rounding.extraLarge
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
implicitWidth: Tokens.sizes.dashboard.perfHeroCardWidth
|
||||
implicitHeight: Math.max(tempProg.implicitHeight + detailsRow.implicitHeight + Tokens.spacing.large, usageShape.implicitHeight + usageLabel.implicitHeight) + Tokens.padding.large * 2
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ StyledRect {
|
|||
readonly property color accent: Colours.palette.m3tertiary
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Tokens.rounding.medium
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
implicitWidth: layout.implicitWidth + Tokens.padding.extraLargeIncreased * 2
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.large * 2
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ StyledRect {
|
|||
id: root
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Tokens.rounding.extraLarge
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
implicitWidth: Tokens.sizes.dashboard.perfNetworkCardWidth
|
||||
implicitHeight: Tokens.sizes.dashboard.perfNetworkCardHeight
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ StyledRect {
|
|||
readonly property real percentage: Storage.primaryDisk?.perc ?? 0
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Tokens.rounding.extraExtraLarge
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
|
||||
implicitWidth: layout.implicitWidth + layout.anchors.margins * 2
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.large * 2
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import Quickshell
|
|||
import Quickshell.Wayland
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.UPower
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.services
|
||||
import qs.utils
|
||||
import qs.components
|
||||
|
|
@ -139,10 +140,12 @@ Variants {
|
|||
}
|
||||
}
|
||||
|
||||
// System tray
|
||||
// System tray — only shown when an app actually registers a tray
|
||||
// icon, otherwise the pill just opened an empty panel (F-T1).
|
||||
IconPill {
|
||||
icon: "widgets"
|
||||
panel: "tray"
|
||||
visible: SystemTray.items.values.length > 0
|
||||
}
|
||||
|
||||
// Notifications bell (with unread dot overlaid on the icon)
|
||||
|
|
|
|||
|
|
@ -145,16 +145,21 @@ Singleton {
|
|||
"warning": root.cp(0xf071),
|
||||
"web_asset_off": root.cp(0xf2d0),
|
||||
"wifi_find": root.cp(0xf1eb),
|
||||
// weather (Icons.getWeatherIcon)
|
||||
"clear_day": root.cp(0xf185),
|
||||
"clear_night": root.cp(0xf186),
|
||||
"partly_cloudy_day": root.cp(0xf6c4),
|
||||
"cloudy": root.cp(0xf0c2),
|
||||
"cloud": root.cp(0xf0c2),
|
||||
"rainy": root.cp(0xf73d),
|
||||
"cloudy_snowing": root.cp(0xf2dc),
|
||||
"foggy": root.cp(0xf75f),
|
||||
"air": root.cp(0xf72e),
|
||||
// weather (Icons.getWeatherIcon) — Material-weather family, every
|
||||
// codepoint verified present in JetBrainsMono Nerd Font Mono. The old
|
||||
// Font-Awesome mapping had several glyphs absent from the font (rendered
|
||||
// as "?" boxes) and was missing thunderstorm/snowing_heavy (F-D1).
|
||||
"clear_day": root.cp(0xf0599),
|
||||
"clear_night": root.cp(0xf0594),
|
||||
"partly_cloudy_day": root.cp(0xf0595),
|
||||
"cloudy": root.cp(0xf0590),
|
||||
"cloud": root.cp(0xf0590),
|
||||
"rainy": root.cp(0xf0597),
|
||||
"cloudy_snowing": root.cp(0xf0598),
|
||||
"snowing_heavy": root.cp(0xf0f37),
|
||||
"thunderstorm": root.cp(0xf0593),
|
||||
"foggy": root.cp(0xf0591),
|
||||
"air": root.cp(0xf059d),
|
||||
// apps / devices (Icons.get*)
|
||||
"code": root.cp(0xf121),
|
||||
"forum": root.cp(0xf086),
|
||||
|
|
|
|||
|
|
@ -41,15 +41,43 @@ Singleton {
|
|||
fetchCoordsFromCity(configLocation);
|
||||
}
|
||||
} else if (!loc || timer.elapsed() > 900) {
|
||||
fetchLocationFromIp();
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-detect location from the public IP (used when weatherLocation is
|
||||
// empty). Stock caelestia only queried ipinfo.io with no error handling;
|
||||
// on the 2026-06-15 hardware test that call never populated `loc` (the
|
||||
// city path works via the same Requests.get, so it is provider-specific —
|
||||
// finding F-W2). Repair: wrap parsing defensively and fall back to a second
|
||||
// provider (geojs.io, which returns separate latitude/longitude fields) on
|
||||
// any error or empty response.
|
||||
function fetchLocationFromIp(): void {
|
||||
const useGeoJs = () => {
|
||||
Requests.get("https://get.geojs.io/v1/ip/geo.json", text => {
|
||||
try {
|
||||
const g = JSON.parse(text);
|
||||
if (g.latitude && g.longitude) {
|
||||
loc = g.latitude + "," + g.longitude;
|
||||
city = g.city ?? "";
|
||||
timer.restart();
|
||||
}
|
||||
} catch (e) {}
|
||||
});
|
||||
};
|
||||
|
||||
Requests.get("https://ipinfo.io/json", text => {
|
||||
try {
|
||||
const response = JSON.parse(text);
|
||||
if (response.loc) {
|
||||
loc = response.loc;
|
||||
city = response.city ?? "";
|
||||
timer.restart();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
useGeoJs();
|
||||
}, useGeoJs);
|
||||
}
|
||||
|
||||
function fetchCityFromCoords(coords: string): void {
|
||||
|
|
@ -131,8 +159,10 @@ Singleton {
|
|||
for (let i = 0; i < json.daily.time.length; i++)
|
||||
forecastList.push({
|
||||
date: json.daily.time[i].replace(/-/g, "/"),
|
||||
maxTempC: json.daily.temperature_2m_max[i],
|
||||
minTempC: json.daily.temperature_2m_min[i],
|
||||
maxTempC: Math.round(json.daily.temperature_2m_max[i]),
|
||||
minTempC: Math.round(json.daily.temperature_2m_min[i]),
|
||||
maxTempF: Math.round(toFahrenheit(json.daily.temperature_2m_max[i])),
|
||||
minTempF: Math.round(toFahrenheit(json.daily.temperature_2m_min[i])),
|
||||
weatherCode: json.daily.weather_code[i],
|
||||
icon: Icons.getWeatherIcon(json.daily.weather_code[i])
|
||||
});
|
||||
|
|
@ -210,6 +240,11 @@ Singleton {
|
|||
|
||||
onLocChanged: fetchWeatherData()
|
||||
|
||||
// Stock caelestia only called reload() on a config CHANGE, never at startup,
|
||||
// so a saved location blanked after a reboot and an empty (auto/IP) location
|
||||
// never populated on a fresh boot (finding F-W1). Fetch once on load.
|
||||
Component.onCompleted: reload()
|
||||
|
||||
Connections {
|
||||
function onWeatherLocationChanged(): void {
|
||||
root.reload();
|
||||
|
|
@ -222,7 +257,7 @@ Singleton {
|
|||
interval: 3600000 // 1 hour
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: fetchWeatherData()
|
||||
onTriggered: reload() // re-detect an auto/IP location too, not just refetch a known one
|
||||
}
|
||||
|
||||
ElapsedTimer {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue