Tier 0: render what the services already decided, honestly

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-25 00:24:30 -04:00
parent be0e55214b
commit 8b1205e4b8
38 changed files with 1474 additions and 260 deletions
@@ -48,6 +48,48 @@ SettingsPage {
property bool importOpen: false
property string importPath: ""
// The proxy dropdown and its address, page-local until they add up to a
// whole setting.
//
// A manual proxy is a host AND a port -- GNOME ignores a proxy whose host
// is empty or whose port is 0 -- so engaging "manual" before an address
// exists produces a mode that says it is proxying and is not. The dropdown
// therefore leads the applied mode: picking Manual reveals the fields, and
// the proxy is written on the edit that completes the pair, whichever of
// the two that is. Committing on the first field instead would have to
// guess at the other one; requiring the other to be stored first, which is
// what this page used to do, meant neither could ever be first.
//
// The three start as bindings and stay bound until the first edit, which is
// what carries the helper's opening read into a page that was built before
// it answered. After that they are the user's, and nothing re-seeds them:
// a reply landing mid-edit must not move the dropdown or empty the field
// somebody is typing into.
property string proxyChoice: NetworkTools.proxyMode
property string draftProxyHost: NetworkTools.proxyHost
property string draftProxyPort: NetworkTools.proxyPort
// "host", "port", "both" or "" -- which half of the pair is still missing.
readonly property string proxyMissing: {
const host = root.draftProxyHost.trim() === "";
const port = root.draftProxyPort.trim() === "";
if (host && port)
return "both";
if (host)
return "host";
if (port)
return "port";
return "";
}
// Called after either field is edited. Writes only a whole address, so a
// half-filled form leaves the proxy exactly as it was.
function commitProxyManual(): void {
if (root.proxyMissing !== "")
return;
NetworkTools.setProxyManual(root.draftProxyHost.trim(), root.draftProxyPort.trim());
}
readonly property string wiredConnection:
String(Connectivity.wiredDevice?.network?.name ?? "")
@@ -410,9 +452,18 @@ SettingsPage {
OptionPickerRow {
width: parent.width
label: "Network proxy"
detail: NetworkTools.proxyMode === "none"
? "Applications that honour the system proxy use this. Not every application does."
: "In use: " + NetworkTools.proxySummary
detail: {
if (root.proxyChoice === "manual" && NetworkTools.proxyMode !== "manual") {
if (root.proxyMissing === "both")
return "Not applied yet — fill in the host and the port below.";
if (root.proxyMissing !== "")
return "Not applied yet — the " + root.proxyMissing + " below is still empty.";
return "Not applied yet.";
}
if (NetworkTools.proxyMode === "none")
return "Applications that honour the system proxy use this. Not every application does.";
return "In use: " + NetworkTools.proxySummary;
}
enabled: !NetworkTools.busy
options: [
{
@@ -431,48 +482,60 @@ SettingsPage {
detail: "A configuration URL decides, per address"
}
]
current: NetworkTools.proxyMode
onPicked: value => NetworkTools.setProxyMode(String(value))
current: root.proxyChoice
// Off and Automatic mean something the moment they are picked;
// Manual does not, so it only opens the fields. See proxyChoice.
onPicked: value => {
root.proxyChoice = String(value);
if (root.proxyChoice === "manual")
root.commitProxyManual();
else
NetworkTools.setProxyMode(root.proxyChoice);
}
}
Column {
width: parent.width
visible: NetworkTools.proxyMode === "manual"
visible: root.proxyChoice === "manual"
// Host and port are written together, because the proxy is only
// usable as a pair -- so each field commits with whatever the other
// one currently holds, and neither writes a half-configuration.
// usable as a pair. Each field edits the page's draft; the pair is
// written once both halves are there.
TextFieldRow {
width: parent.width
label: "Proxy host"
detail: "The machine applications should go through"
detail: root.proxyMissing === "host"
? "Still empty — the proxy applies once this and the port are both set"
: "The machine applications should go through"
placeholder: "proxy.example.com"
text: NetworkTools.proxyHost
text: root.draftProxyHost
enabled: !NetworkTools.busy
onAccepted: value => {
if (value.trim() !== "" && NetworkTools.proxyPort !== "")
NetworkTools.setProxyManual(value.trim(), NetworkTools.proxyPort);
root.draftProxyHost = value.trim();
root.commitProxyManual();
}
}
TextFieldRow {
width: parent.width
label: "Port"
detail: "The port that proxy listens on"
detail: root.proxyMissing === "port"
? "Still empty — the proxy applies once this and the host are both set"
: "The port that proxy listens on"
placeholder: "8080"
text: NetworkTools.proxyPort
text: root.draftProxyPort
enabled: !NetworkTools.busy
divider: false
onAccepted: value => {
if (value.trim() !== "" && NetworkTools.proxyHost !== "")
NetworkTools.setProxyManual(NetworkTools.proxyHost, value.trim());
root.draftProxyPort = value.trim();
root.commitProxyManual();
}
}
}
TextFieldRow {
width: parent.width
visible: NetworkTools.proxyMode === "auto"
visible: root.proxyChoice === "auto"
label: "Configuration URL"
detail: "The .pac file whoever runs the network published"
placeholder: "http://example.com/proxy.pac"