From dc35fc4c587285ab2c3d79c7fe44a70d2c34b647 Mon Sep 17 00:00:00 2001 From: Will Knowles Date: Fri, 16 Oct 2026 23:57:04 +0100 Subject: [PATCH 1/3] Join says "Joining XX%" under its bar, and locks its settings while it runs The clip list goes inert and grey, and Join into, File name and Browse are disabled, until the join ends however it ends. A file dropped on Join mid-run isn't added. --- src/index.html | 5 ++++- src/renderer.js | 37 ++++++++++++++++++++++++++++++++----- src/styles.css | 4 ++++ test/kit.test.js | 15 ++++++++++++++- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/index.html b/src/index.html index 79fcb3e..682cbcc 100644 --- a/src/index.html +++ b/src/index.html @@ -141,7 +141,10 @@
-
+ +
+
+
diff --git a/src/renderer.js b/src/renderer.js index a577893..f05090f 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -1645,6 +1645,11 @@ let joinTrimOpen = null; // the id of the clip whose trim is open, if any */ async function addJoinPaths(inputPaths) { if (!inputPaths || inputPaths.length === 0) return; + // The list is what's being joined, so it holds still until the join ends. + if (joinRunning) { + kit.ui.toast("Wait for the join to finish before adding more files.", { type: "warning" }); + return; + } try { const result = await api.scanPaths(inputPaths, buildScanOptions()); const added = await addJoinClips(result.files); @@ -2052,7 +2057,30 @@ function renderJoinActions() { $("btn-join-run").classList.toggle("d-none", Boolean(joinRunning)); $("btn-join-cancel").classList.toggle("d-none", !joinRunning); $("btn-join-clear").disabled = Boolean(joinRunning); - joinProgress.element.classList.toggle("d-none", !joinRunning); + renderJoinLock(); + $("join-progress").classList.toggle("d-none", !joinRunning); +} + +/** + * While a join runs, everything that configures it is greyed out and locked: + * the clip list (order, trims, removing, Add More) and the side's fields. The + * list is made inert rather than each control disabled, so the rows keep their + * own disabled states (the first row's Move Up) for when it unlocks. + */ +function renderJoinLock() { + const locked = Boolean(joinRunning); + const scroll = document.querySelector("#join-body .join-scroll"); + scroll.inert = locked; + scroll.classList.toggle("is-locked", locked); + for (const id of ["join-format", "join-name", "btn-join-browse"]) $(id).disabled = locked; +} + +/** Join's progress: the bar, then "Joining 42%" under it, left-aligned. */ +function setJoinProgress(percent) { + joinProgress.set(percent); + $("join-progress-text").textContent = typeof percent === "number" + ? `Joining ${Math.round(Math.min(100, Math.max(0, percent)))}%` + : "Joining…"; } async function runJoin() { @@ -2078,7 +2106,7 @@ async function runJoin() { joinRunning = generateId(); spec.id = joinRunning; - joinProgress.set(0); + setJoinProgress(0); renderJoinActions(); // However it ends, as a conversion's. @@ -2106,8 +2134,7 @@ let joinProgress = null; function setupJoin() { joinProgress = kit.ui.progress({ label: "Joining" }); - joinProgress.element.classList.add("join-progress", "d-none"); - $("join-progress").replaceWith(joinProgress.element); + $("join-progress").prepend(joinProgress.element); // The same drop zone as Convert's, from the kit, so the two sections read // as one app: only the icon and the label change. Files dropped anywhere @@ -2367,7 +2394,7 @@ api.onJobProgress(({ jobId, percent }) => { // A join is a job too, and it is the only one with no card to land on. if (jobId === joinRunning) { // null when the length isn't known: the bar slides instead of reading "null%". - joinProgress?.set(typeof percent === "number" ? percent : null); + if (joinProgress) setJoinProgress(typeof percent === "number" ? percent : null); return; } const job = jobs.find(j => j.id === jobId); diff --git a/src/styles.css b/src/styles.css index 8c0b5e3..eb23e0e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -665,6 +665,10 @@ a { .join-plan-why { margin: 0.35rem 0 0; opacity: 0.75; } .join-progress { margin-top: 0.9rem; } +.join-progress-text { margin-top: 0.35rem; font-size: 0.8rem; text-align: left; opacity: 0.75; } + +/* While a join runs the list can't change, and looks it. */ +.join-scroll.is-locked { opacity: 0.5; transition: opacity var(--dur-state) var(--ease-state); } .join-actions { margin-top: auto; padding-top: 0.9rem; display: flex; gap: 0.5rem; justify-content: flex-end; } diff --git a/test/kit.test.js b/test/kit.test.js index 9071723..b188464 100644 --- a/test/kit.test.js +++ b/test/kit.test.js @@ -161,10 +161,23 @@ test("what Restart Now asks about is cleared however a conversion or a join ends test("progress of null is \"not known\": a card's and Join's bars slide, never sit at 0% or read null%", () => { const renderer = read("renderer.js"); assert.match(renderer, /progress\.set\(typeof job\.progress === "number" \? job\.progress : null\)/); - assert.match(renderer, /joinProgress\?\.set\(typeof percent === "number" \? percent : null\)/); + assert.match(renderer, /setJoinProgress\(typeof percent === "number" \? percent : null\)/); assert.ok(!/style\.width = `\$\{percent\}%`/.test(renderer), "Join's bar used to read \"null%\""); }); +test("Join's bar has \"Joining XX%\" under it, and its settings lock while it runs", () => { + const renderer = read("renderer.js"); + const html = read("index.html"); + assert.match(html, /id="join-progress-text"/); + assert.match(renderer, /`Joining \$\{Math\.round\(/); + assert.match(renderer, /: "Joining…";/); + // The list goes inert and grey; the side's fields are disabled. + assert.match(renderer, /scroll\.inert = locked;/); + assert.match(renderer, /\["join-format", "join-name", "btn-join-browse"\]\) \$\(id\)\.disabled = locked;/); + // A drop during a join doesn't change what's being joined. + assert.match(renderer, /if \(joinRunning\) \{\r?\n\s+kit\.ui\.toast\("Wait for the join to finish/); +}); + test("every icon is hidden from screen readers, and every icon-only button has a name", () => { const html = read("index.html"); const spans = html.match(/ left-aligned; the list was inert and greyed, the side's fields disabled, a +> click on Remove did nothing; all of it came back after the join, with the +> first row's Move Up still disabled). + +### Joining XX% + +- [ ] In Join, add two videos of different sizes (so they re-encode, which + takes long enough to watch), choose a folder, press **Join**. Under the + bar, on the left, it reads **Joining 0%** and counts up to the end. +- [ ] When the join ends, the bar and its text go away. + +### Locked while joining + +- [ ] Open one clip's **Trim**, then press **Join**. While it runs, the list + is greyed out: Trim, the slider, Start and End, Move Up and Down, Remove + and **Add More** do nothing, and Tab doesn't reach them. +- [ ] **Join into**, **File name** and **Browse** are greyed out and can't be + changed. **Clear** is disabled; **Cancel** works. +- [ ] Drop a file on Join while it runs: it isn't added, and a toast says to + wait for the join to finish. +- [ ] When it finishes, everything works again, and the first clip's Move Up + and the last clip's Move Down are still disabled. +- [ ] Do it again and press **Cancel**: everything unlocks the same way. +- [ ] Look at it in both light and dark themes. From 7e49afcebfb07bc66bbe25d3baaf666cc11e2a43 Mon Sep 17 00:00:00 2001 From: Will Knowles Date: Fri, 16 Oct 2026 23:57:05 +0100 Subject: [PATCH 3/3] chore: 2.0.0-beta.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 338ef5e..a6174fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "diamond-file-converter", - "version": "2.0.0-beta.7", + "version": "2.0.0-beta.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "diamond-file-converter", - "version": "2.0.0-beta.7", + "version": "2.0.0-beta.8", "license": "Apache-2.0", "dependencies": { "@diamonddigitaldev/electron-kit": "0.1.0-beta.8", diff --git a/package.json b/package.json index 401c22e..48a833f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diamond-file-converter", - "version": "2.0.0-beta.7", + "version": "2.0.0-beta.8", "description": "An Electron-based desktop app for converting audio, video, and image files.", "main": "src/main.js", "funding": {