diff --git a/app/ui/composer/tests/rule_kit_uses_the_scale.test.js b/app/ui/composer/tests/rule_kit_uses_the_scale.test.js index 26d34cb3..480b5072 100644 --- a/app/ui/composer/tests/rule_kit_uses_the_scale.test.js +++ b/app/ui/composer/tests/rule_kit_uses_the_scale.test.js @@ -18,7 +18,7 @@ test("element CSS has no literal pixel sizes", () => { const css = readFileSync(f, "utf8").replace(/\/\*[\s\S]*?\*\//g, ""); for (const decl of css.split(/[;{}]/)) { if (/^\s*(--|@media|@container)/.test(decl)) continue; // a token definition is where a number belongs; a breakpoint cannot use one - found += [...decl.matchAll(/(? ![0, 1].includes(Math.abs(Number(m[1])))).length; // 0 and a 1px hairline are exempt; 1.5px and .5px... are not // 1px lines and 0 are borders and resets + found += [...decl.matchAll(/(? ![0, 1].includes(Math.abs(Number(m[1])))).length; // 0 and a 1px hairline are exempt; 1.5px and .5px... are not // 1px lines and 0 are borders and resets } } assert.equal(found, 0, `${found} literal px values in element CSS: use a --sp-* step or a component size from tokens.css`); diff --git a/app/ui/features/generate/hub.test.js b/app/ui/features/generate/hub.test.js index eea47809..0db65dba 100644 --- a/app/ui/features/generate/hub.test.js +++ b/app/ui/features/generate/hub.test.js @@ -286,10 +286,10 @@ test("the next shot waits while an upscale model runs", async () => { let queued = 0, busyNow = false, asked = 0; const { app } = rig("idle", { generate: async () => { queued += 1; return true; } }); app.has = new Set(); - app.api.upscaleBusy = async () => { asked += 1; if (asked === 2) busyNow = true; return { busy: asked === 2 }; }; // free at the click, busy once before the first shot + app.api.upscaleBusy = async () => { asked += 1; if (asked === 3) busyNow = true; return { busy: asked === 3 }; }; // free at the click and before the first shot, busy only right before queueing await app.runner.units(["a"], 1); assert.equal(queued, 1); - assert.ok(busyNow && asked >= 3, "the shot asked again and went on when the server said free"); + assert.ok(busyNow && asked >= 4, "the shot asked again right before queueing and went on when the server said free"); }); test("the wait for an upscale model is said once per Generate, not once per shot", async () => { diff --git a/app/ui/features/generate/index.js b/app/ui/features/generate/index.js index 27f103b3..422e5aea 100644 --- a/app/ui/features/generate/index.js +++ b/app/ui/features/generate/index.js @@ -131,10 +131,11 @@ export default { for (const x of placed || []) app.api.carryLog(pid, { scene_id: root.id, ...x }).catch(() => {}); // the Log records where a carried picture went; no toast if (unusedPicture) tell(`The scene's picture (${names.get(unusedPicture) || unusedPicture}) is not used: this pipeline starts from a video, not a picture.`); if (unwired) tell(unwiredText(unwired, left)); - // The page's own guard does not see another tab: ComfyUI's queue does. Asked per shot: between this run's shots it is empty. - if (app.api.projectQueued && (await app.api.projectQueued(pid))) { tell("This project already has a run in ComfyUI's queue (another tab or window?). Wait for it, or Stop it there."); break; } await waitForUpscale(); // asked again right before queueing: the shot's inputs took time to build if (stopped) break; + if (!p.project || p.project.id !== pid) { tell("Stopped: another project was opened."); break; } // the wait can take minutes + // The page's own guard does not see another tab: ComfyUI's queue does. Asked per shot: between this run's shots it is empty. + if (app.api.projectQueued && (await app.api.projectQueued(pid))) { tell("This project already has a run in ComfyUI's queue (another tab or window?). Wait for it, or Stop it there."); break; } const done = g.waitForTerminal(); // listening before the run starts, so a fast one is not missed said = false; current = snap.scenes.findIndex((s) => s.id === root.id) + 1; diff --git a/app/ui/features/upscale/index.js b/app/ui/features/upscale/index.js index c124f36d..f2037e40 100644 --- a/app/ui/features/upscale/index.js +++ b/app/ui/features/upscale/index.js @@ -41,6 +41,7 @@ export default { const stillThere = (pid, key) => p.project && p.project.id === pid && Object.values(p.project.scene_renders || {}).some((r) => r && r.media && keyOf(r.media) === key); async function run(media, pressed = false, project = null) { + let again = false; // a retry is on its way: the wait count stays if (!p.project) return; if (!media || wentThrough(media, "upscale")) return; // never upscale twice, even after an interpolation in between const key = keyOf(media), pid = project || p.project.id; @@ -64,7 +65,7 @@ export default { if (err.stopped) c.toast.info({ text: "Upscale stopped. The render stays as it was." }); else if (!pressed && /^ComfyUI is generating/.test(err.message || "") && (waited.get(key) || 0) < RETRIES) { // Always: not lost, tried again when the generation is over if (!told) { told = true; c.toast.info({ text: "Upscale waits for the generation to finish." }); } - waited.set(key, (waited.get(key) || 0) + 1); + waited.set(key, (waited.get(key) || 0) + 1); again = true; setTimeout(() => { // only for the same project, the same render and while Always is still on if (stillThere(pid, key) && p.pref("upscaleMode", "never") === "always") run(media, false, pid); else { waited.delete(key); if (!waited.size) told = false; } @@ -72,7 +73,7 @@ export default { } else { waited.delete(key); if (!waited.size) told = false; gaveUp(media, "upscale"); c.toast.warn({ text: `Upscale failed: ${err.message}` }); } } - finally { busy.delete(key); heavy.delete(key); if (!heavy.size) app.has.delete("upscale.model"); draw(); } + finally { if (!again) { waited.delete(key); if (!waited.size) told = false; } busy.delete(key); heavy.delete(key); if (!heavy.size) app.has.delete("upscale.model"); draw(); } } function draw() { diff --git a/app/ui/features/upscale/index.test.js b/app/ui/features/upscale/index.test.js index a69150e4..7867c213 100644 --- a/app/ui/features/upscale/index.test.js +++ b/app/ui/features/upscale/index.test.js @@ -64,7 +64,7 @@ test("Always tries a render again after a running generation, for as long as it }); test("Always does not try again when the render was replaced, the mode was switched off or another project is open", async () => { - for (const change of [(d) => { d.scene_renders = { a: d.scene_renders.a }; }, (d) => { d.editor_settings.upscaleMode = "never"; }]) { + for (const change of [(d) => { d.scene_renders = { a: d.scene_renders.a }; }, (d) => { d.editor_settings.upscaleMode = "never"; }, (d) => { d.id = "Q"; }]) { mock.timers.enable({ apis: ["setTimeout"] }); try { const { doc, render, calls } = always(BUSY);