Files
Panama/config/dot/quickshell/services/DateTime.qml
T
Gabriel Brown d96863b687 Convert British spellings to American across the repo
colour -> color, behaviour -> behavior, centre -> center, favourite ->
favorite, and about twenty other pairs, applied consistently across
comments, docs, error/UI copy, and a handful of QML identifiers that
used the British spelling as their actual name: SystemSettings'
serialiseValue/serialiseTable/normaliseGradient, Displays'
normaliseModes, Wallpaper's normalisePolicy, SettingsBackup's
serialiseHomeState, DateTime's ntpSynchronised property, Clipboard's
_normalise helper, and ShortcutCapture's cancelled signal (with its
onCancelled handler in ShortcutsPage.qml). Every call site and the two
tests that assert on the literal source text (settings-ownership and
settings-backup-live contracts) were updated in lockstep.

Left untouched: config/dot/espanso/match/packages/misspell-en/ is a
vendored third-party autocorrect dictionary -- its entries are typo
corrections, not our prose, and rewriting them would fight the
package's own purpose (and any future re-sync from upstream).

The already-American `favorites` property (Home page pinned
accessories) was never actually misspelled -- only nearby comments and
error strings said "favourites" -- so no data migration was needed
there.

Claude-Session: https://claude.ai/code/session_01E6TJUAh41HaP25MVHWkhRZ
2026-08-19 08:07:55 -04:00

134 lines
4.4 KiB
QML

pragma Singleton
// System date, time, and timezone.
//
// Deliberately NOT backed by the Panama settings store. The timezone and the
// network-time setting belong to the machine, not to this desktop: they are
// shared with every other session and with services that never see Panama's
// JSON. Storing a copy would create a second answer to a question the system
// already answers, which is the exact failure this settings rewrite exists to
// remove. So this reads and writes `timedatectl` directly and holds no state of
// its own beyond what it last observed.
//
// Setting the timezone or toggling NTP needs privilege. timedatectl asks
// polkit, which shows the usual authentication dialog; on refusal the command
// fails and the error is surfaced rather than the UI pretending it worked.
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property string timezone: ""
property bool ntpEnabled: false
property bool ntpSynchronized: false
property string localTime: ""
property string universalTime: ""
property string rtcTime: ""
property string lastError: ""
property var zones: []
readonly property bool busy: statusQuery.running || zonesQuery.running || writeRun.running
// "America/New_York" -> "New York" for display, keeping the region as a
// separate field so the list can be grouped and searched sensibly.
function regionOf(zone: string): string {
const slash = zone.indexOf("/");
return slash < 0 ? zone : zone.slice(0, slash);
}
function cityOf(zone: string): string {
const slash = zone.indexOf("/");
return (slash < 0 ? zone : zone.slice(slash + 1)).replace(/_/g, " ");
}
Process {
id: statusQuery
command: ["timedatectl", "show",
"-p", "Timezone", "-p", "NTP", "-p", "NTPSynchronized",
"-p", "TimeUSec", "-p", "RTCTimeUSec"]
stdout: StdioCollector {
onStreamFinished: root.parseStatus(this.text)
}
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0)
root.lastError = "Could not read the system clock settings.";
}
}
Process {
id: zonesQuery
command: ["timedatectl", "list-timezones"]
stdout: StdioCollector {
onStreamFinished: {
root.zones = this.text.split("\n")
.map(line => line.trim())
.filter(line => line.length > 0);
}
}
}
Process {
id: writeRun
onExited: (exitCode, exitStatus) => {
// A polkit refusal and a bad value both land here. Neither should
// leave the UI showing a value the system did not take, so the
// status is re-read either way.
root.lastError = exitCode === 0
? ""
: "The system rejected that change, or authentication was canceled.";
root.refresh();
}
}
Component.onCompleted: {
root.refresh();
zonesQuery.running = true;
}
function parseStatus(text: string): void {
for (const line of text.split("\n")) {
const split = line.indexOf("=");
if (split < 0)
continue;
const key = line.slice(0, split);
const value = line.slice(split + 1);
if (key === "Timezone")
root.timezone = value;
else if (key === "NTP")
root.ntpEnabled = value === "yes";
else if (key === "NTPSynchronized")
root.ntpSynchronized = value === "yes";
}
root.lastError = "";
}
function refresh(): void {
if (!statusQuery.running)
statusQuery.running = true;
}
// Only a timezone the system itself listed is ever passed on, so no
// caller-supplied text reaches the command.
function setTimezone(zone: string): bool {
if (root.zones.indexOf(zone) < 0) {
root.lastError = "That is not a timezone this system recognizes.";
return false;
}
if (writeRun.running)
return false;
writeRun.exec(["timedatectl", "set-timezone", zone]);
return true;
}
function setNtp(enabled: bool): bool {
if (writeRun.running)
return false;
writeRun.exec(["timedatectl", "set-ntp", enabled ? "true" : "false"]);
return true;
}
}