aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/sbbuilder
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-21 10:39:57 +0200
committerDanilo M. <danix@danix.xyz>2026-10-21 10:39:57 +0200
commit10ba628793bac9aa26b3ca5a9484b09d1b46e8b6 (patch)
tree0642fc66956f3e87c3e3a35a819a0be63bc2e250 /desktop/modules/sbbuilder
parente3cbb769f63813a2a2e90aa9607dcc7359a18903 (diff)
downloadquickshell-10ba628793bac9aa26b3ca5a9484b09d1b46e8b6.tar.gz
quickshell-10ba628793bac9aa26b3ca5a9484b09d1b46e8b6.zip
sbbuilder: list the default manifests, and keep them on the first edit
The helper replaces its default [manifests] table with the file's, so adding one manifest to a file without the table dropped neovim's, and the page showed no rows for the default that was in effect. With no table in the file the page now lists the helper's defaults, and the first add, remove or URL edit writes them all before its own change. Toml.js gains sections(), to tell a missing table from an empty one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/sbbuilder')
-rw-r--r--desktop/modules/sbbuilder/README.md6
-rw-r--r--desktop/modules/sbbuilder/SbbVmModule.qml1
-rw-r--r--desktop/modules/sbbuilder/Store.qml26
-rw-r--r--desktop/modules/sbbuilder/Toml.js3
-rw-r--r--desktop/modules/sbbuilder/test_toml.js7
5 files changed, 37 insertions, 6 deletions
diff --git a/desktop/modules/sbbuilder/README.md b/desktop/modules/sbbuilder/README.md
index 73bfe51..ddb5b75 100644
--- a/desktop/modules/sbbuilder/README.md
+++ b/desktop/modules/sbbuilder/README.md
@@ -19,7 +19,11 @@ Both follow the settings window's draft rules (`desktop/README.md`). A field
is typed after its default: lists are words split on spaces (a list entry
holding a space is a `ponytail:` ceiling: edit the file), numbers are
range-checked. An emptied manifest URL holds Save back rather than unsetting
-the key, which would remove the row being typed in. The desktop defaults are
+the key, which would remove the row being typed in. The helper takes a
+`[manifests]` table whole, in place of its default one, so a file without
+one lists the helper's default rows, and the first add, remove or edit
+writes them all first: adding one row once silently dropped the default
+neovim manifest. The desktop defaults are
copied from sb-builder's `config.py`, a `ponytail:` ceiling in `Sbb.qml`;
the VM ones come from the helper.
diff --git a/desktop/modules/sbbuilder/SbbVmModule.qml b/desktop/modules/sbbuilder/SbbVmModule.qml
index 45c7216..97e7983 100644
--- a/desktop/modules/sbbuilder/SbbVmModule.qml
+++ b/desktop/modules/sbbuilder/SbbVmModule.qml
@@ -114,6 +114,7 @@ Module {
width: col.width
wrapMode: Text.Wrap
text: "A package's upstream deps list; {VERSION} is the new version. A hint update moves the package's extra downloads to the versions listed there."
+ + (col.st.manifestsDefault ? " These are the helper's defaults, not in the file: the first change writes them all, then adds or removes its own." : "")
color: Theme.subtext
font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
}
diff --git a/desktop/modules/sbbuilder/Store.qml b/desktop/modules/sbbuilder/Store.qml
index c2afa5d..7036f36 100644
--- a/desktop/modules/sbbuilder/Store.qml
+++ b/desktop/modules/sbbuilder/Store.qml
@@ -32,20 +32,35 @@ QtObject {
// Assigned only when the set changes, as Qtm does: a Repeater bound to a
// fresh array per keystroke rebuilds the row being typed in.
property var manifests: []
+ // The helper takes a [manifests] table whole, in place of its default
+ // one, so with none in the file the default rows are the ones in effect
+ // and are listed; the first edit writes them all before its own.
+ readonly property bool manifestsDefault: !T.sections(st.draft).includes("manifests")
onDraftChanged: {
- const k = T.entries(st.draft, "manifests").map(e => e.key);
+ const k = st.manifestsDefault ? Object.keys(st.fallback("manifests") ?? {})
+ : T.entries(st.draft, "manifests").map(e => e.key);
if (JSON.stringify(k) !== JSON.stringify(st.manifests)) st.manifests = k;
}
+ function seedManifests(section) {
+ if (section !== "manifests" || !st.manifestsDefault) return;
+ const d = st.fallback("manifests") ?? {};
+ for (const k in d) st.draft = T.set(st.draft, "manifests", k, d[k]);
+ }
- function fallback(section, key) { return (st.defaults[section] || {})[key]; }
+ function fallback(section, key) { return key === undefined ? st.defaults[section] : (st.defaults[section] || {})[key]; }
function show(v) { return v === undefined ? undefined : Array.isArray(v) ? v.join(" ") : String(v); }
function placeholder(section, key) { return st.show(st.fallback(section, key)) ?? ""; }
- function get(section, key) { return st.show(T.get(st.draft, section, key)); }
+ function get(section, key) {
+ const v = T.get(st.draft, section, key);
+ // A listed default manifest shows its URL, so editing starts from it.
+ return st.show(v === undefined && section === "manifests" && st.manifestsDefault ? st.fallback(section, key) : v);
+ }
// The effective value, typed: the file's, else the default.
function value(section, key) { return T.get(st.draft, section, key) ?? st.fallback(section, key); }
function entries(section) { return T.entries(st.draft, section); }
function put(section, key, v) {
+ st.seedManifests(section);
try { st.draft = T.set(st.draft, section, key, v); } catch (e) {
st.error = e.message;
st.markInvalid(section + "/" + key, true); // hold Save back
@@ -63,7 +78,10 @@ QtObject {
: typeof d === "number" ? Number(t)
: typeof d === "boolean" ? t === "true" : t);
}
- function remove(section, key) { st.draft = T.remove(st.draft, section, key); }
+ function remove(section, key) {
+ st.seedManifests(section);
+ st.draft = T.remove(st.draft, section, key);
+ }
function markInvalid(id, bad) {
if (!!st.invalid[id] === bad) return;
diff --git a/desktop/modules/sbbuilder/Toml.js b/desktop/modules/sbbuilder/Toml.js
index 46b9914..dddb6f7 100644
--- a/desktop/modules/sbbuilder/Toml.js
+++ b/desktop/modules/sbbuilder/Toml.js
@@ -92,6 +92,9 @@ function ranges(lines) {
return out;
}
+// The [section] names present, in file order; "" is not one.
+function sections(text) { return ranges(text.split("\n")).slice(1).map(r => r.name); }
+
// [{key, value}] for the active keys of a section, in file order.
function entries(text, section) {
const lines = text.split("\n");
diff --git a/desktop/modules/sbbuilder/test_toml.js b/desktop/modules/sbbuilder/test_toml.js
index 8378519..c86741d 100644
--- a/desktop/modules/sbbuilder/test_toml.js
+++ b/desktop/modules/sbbuilder/test_toml.js
@@ -18,7 +18,7 @@ const assert = require("assert");
const src = fs.readFileSync(__dirname + "/Toml.js", "utf8")
.replace(/^\.pragma library$/m, "");
-const T = new Function(src + "\nreturn { entries, get, set, remove };")();
+const T = new Function(src + "\nreturn { sections, entries, get, set, remove };")();
const file = [
"# helper config",
@@ -97,4 +97,9 @@ t = T.remove(file, "manifests", "gtk+3");
assert.deepStrictEqual(T.entries(t, "manifests").map(e => e.key), ["neovim"]);
assert.strictEqual(T.remove(file, "manifests", "nope"), file);
+// sections tells a missing table from an empty one, which entries cannot.
+assert.deepStrictEqual(T.sections(file), ["manifests", "refresh"]);
+assert.deepStrictEqual(T.sections("a = 1\n[manifests]\n"), ["manifests"]);
+assert.deepStrictEqual(T.sections("a = 1\n"), []);
+
console.log("test_toml: ok");