diff --git a/docs/MANUAL-TESTING.md b/docs/MANUAL-TESTING.md index 6cc3d57..e701f05 100644 --- a/docs/MANUAL-TESTING.md +++ b/docs/MANUAL-TESTING.md @@ -29,7 +29,7 @@ sections — the history is useful. ``` npm install -npm test # expect 198 passing, 0 failing +npm test # expect 207 passing, 0 failing npm start ``` @@ -2876,3 +2876,43 @@ Two things Will found on beta.6: - [x] Look at all of it in both light and dark themes. - [x] Advanced Options' trim on the Convert view still drags, Shift-drags and shows its two frames as before. + +## Join says how far it is, and locks while it runs (2026-10-16) + +Two things Will found on beta.7: + +- **Join's bar had no words.** It moved, but nothing said how far. It now has + **Joining 42%** under it, left-aligned, and **Joining…** while the length + isn't known (the bar slides then). +- **Join's settings could be changed mid-join.** The clip list (order, trims, + Remove, Add More) and Join into, File name and Browse all stayed live while + a join ran, though nothing changed then would take effect. Now they're + greyed out and locked until it ends, however it ends. A file dropped on Join + during a run isn't added: a toast says to wait for the join to finish. + +> **Status: not yet run by hand.** Driven once in the running app on +> 2026-10-16 (two clips needing a re-encode: the label read "Joining 48%", +> 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. 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": { 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(/