Own the network: details, VPN, enterprise Wi-Fi, and a firewall that can also allow

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 16:31:52 -04:00
parent aba2d16ffa
commit b30bf40407
29 changed files with 4452 additions and 241 deletions
@@ -29,6 +29,43 @@ SettingsPage {
readonly property bool manualReady: /^(ipp|ipps|socket):\/\/\S+$/.test(root.manualUri)
&& root.manualName.trim() !== ""
// The two defaults a queue can be told to change, dressed for reading. The
// values are the helper's closed vocabulary and nothing else reaches
// lpadmin; the choices come from what the printer said it supports, so a
// queue that cannot print two-sided never grows a two-sided dropdown.
//
// Empty while get-options is still on its way, which is why the rows are
// gated on the list rather than on the current value: an empty dropdown
// above a printer is worse than no dropdown at all.
function mediaChoices(reported: var): var {
const detail = {
"Letter": "8.5 × 11 in",
"A4": "210 × 297 mm",
"Legal": "8.5 × 14 in"
};
return (reported?.choices?.media ?? []).map(choice => ({
value: String(choice),
label: String(choice),
detail: detail[String(choice)] ?? ""
}));
}
function sidesChoices(reported: var): var {
const named = {
"one-sided": { label: "Off", detail: "One page per sheet" },
"two-sided-long-edge": { label: "Long edge", detail: "Bound like a book" },
"two-sided-short-edge": { label: "Short edge", detail: "Bound like a notepad" }
};
return (reported?.choices?.sides ?? []).map(choice => {
const known = named[String(choice)];
return {
value: String(choice),
label: known ? known.label : String(choice),
detail: known ? known.detail : ""
};
});
}
Component.onCompleted: Printers.refresh()
TextRow {
@@ -80,9 +117,18 @@ SettingsPage {
}
Column {
id: printerBody
width: printerBlock.width
visible: printerBlock.open
// What this queue is set to, as CUPS reports it. Null until
// the read comes back, and a printer that reports neither
// key simply has no dropdowns -- an option offered for a
// printer that does not have it is a setting that silently
// does nothing.
readonly property var reported: Printers.optionsFor(printerBlock.name)
TextRow {
width: printerBlock.width
label: "Model"
@@ -90,6 +136,28 @@ SettingsPage {
value: String(printerBlock.modelData.makeAndModel ?? "Unknown")
}
OptionPickerRow {
width: printerBlock.width
visible: root.mediaChoices(printerBody.reported).length > 0
label: "Paper size"
detail: "What applications print onto unless they ask for something else"
enabled: !Printers.busy
options: root.mediaChoices(printerBody.reported)
current: String(printerBody.reported?.options?.media ?? "")
onPicked: value => Printers.setOption(printerBlock.name, "media", String(value))
}
OptionPickerRow {
width: printerBlock.width
visible: root.sidesChoices(printerBody.reported).length > 0
label: "Two-sided"
detail: "Long edge is the usual choice for text; short edge flips like a notepad"
enabled: !Printers.busy
options: root.sidesChoices(printerBody.reported)
current: String(printerBody.reported?.options?.sides ?? "")
onPicked: value => Printers.setOption(printerBlock.name, "sides", String(value))
}
ActionRow {
width: printerBlock.width
visible: !printerBlock.modelData.isDefault
@@ -173,22 +241,47 @@ SettingsPage {
Repeater {
model: Printers.jobs
delegate: ActionRow {
delegate: SettingRow {
id: jobRow
required property var modelData
required property int index
readonly property bool held: Printers.isHeld(jobRow.modelData)
width: parent.width
label: String(jobRow.modelData.name ?? "Untitled")
detail: String(jobRow.modelData.printer ?? "") + " · "
+ String(jobRow.modelData.state ?? "")
+ (Number(jobRow.modelData.pages ?? 0) > 0
? " · " + jobRow.modelData.pages + " pages" : "")
action: "Cancel"
enabled: !Printers.busy
controlWidth: 175
divider: jobRow.index < Printers.jobs.length - 1
onTriggered: Printers.cancel(Number(jobRow.modelData.id))
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 8
// Holding is the answer to "stop this one, I have not
// finished with it" -- cancelling was the only way out of
// the queue before, and it is not reversible.
SettingsButton {
anchors.verticalCenter: parent.verticalCenter
text: jobRow.held ? "Release" : "Hold"
enabled: !Printers.busy
onClicked: jobRow.held
? Printers.release(Number(jobRow.modelData.id))
: Printers.hold(Number(jobRow.modelData.id))
}
SettingsButton {
anchors.verticalCenter: parent.verticalCenter
text: "Cancel"
enabled: !Printers.busy
onClicked: Printers.cancel(Number(jobRow.modelData.id))
}
}
}
}
@@ -201,42 +294,26 @@ SettingsPage {
}
}
// ── Nothing set up yet ───────────────────────────────────────────────────
SettingsCard {
visible: Printers.scanned && !Printers.anyPrinters
title: "No printers yet"
subtitle: "Printers that announce themselves on your network appear here on their own."
ActionRow {
label: "Search the network"
detail: Printers.searching
? "Listening for printers that announce themselves…"
: (Printers.searched
? Printers.addable.length + " found"
: "Looks for printers over mDNS, the same way phones and laptops find them")
action: Printers.searching ? "Searching…" : "Search"
enabled: !Printers.searching
divider: false
onTriggered: Printers.search()
}
}
// ── Adding ───────────────────────────────────────────────────────────────
//
// One card, not two. "No printers yet" and "Add a printer" carried the same
// Search row with two different wordings, so a machine with no printers
// offered the identical button twice under two headings.
SettingsCard {
title: "Add a printer"
subtitle: "Driverless printers only. One that needs a manufacturer driver has to be set up with the system printer tool."
title: Printers.anyPrinters ? "Add a printer" : "No printers yet"
subtitle: Printers.anyPrinters
? "Driverless printers only. One that needs a manufacturer driver has to be set up with the system printer tool."
: "Printers on this network that speak modern IPP appear here on their own. Driverless only."
ActionRow {
visible: Printers.anyPrinters
label: "Search the network"
detail: Printers.searching
? "Listening for printers that announce themselves…"
: (Printers.searched
? Printers.addable.length + " printer"
+ (Printers.addable.length === 1 ? "" : "s") + " found that are not set up here"
: "Looks for printers over mDNS")
: "Looks for printers over mDNS, the same way phones and laptops find them")
action: Printers.searching ? "Searching…" : "Search"
enabled: !Printers.searching
onTriggered: Printers.search()