From 019d496fabfdf7cb63038ef859684497187b41ed Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 15:31:59 +0000 Subject: [PATCH] feat: warn beside Generate when PTD is on with SLA or other enhancements A chip names what else is switched on, since PTD was trained without them; the PTD toggle's hint says the same. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_0164nckPWA2L2ugVqiDcXzc3 --- app/ui/features/generate/warn.js | 17 +++++++++++++++-- app/ui/features/generate/warn.test.js | 16 ++++++++++++++++ modules/sampling/ptd/__init__.py | 3 ++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/ui/features/generate/warn.js b/app/ui/features/generate/warn.js index 155bbbb0..bba438de 100644 --- a/app/ui/features/generate/warn.js +++ b/app/ui/features/generate/warn.js @@ -29,7 +29,11 @@ export default { const rough = c.button.sm({ label: "Camera targets rougher", tone: "ghost", title: "Shot camera is on, but spaCy is not: camera moves aim with simple word rules and pick worse targets. Switch on Automatic camera and focus in Settings ▸ Modules.", onClick: () => app.openSettings?.("modules") }).node; rough.hidden = true; - host.append(chip, plain, keyless, twice, slow, rough); + // PTD runs its own four to eight hops and was trained without these: SLA, and any other enhancement that is on. + const ptd = c.button.sm({ label: "⚠ PTD with other features", tone: "ghost", title: "", + onClick: () => app.openSettings?.("engine") }).node; + ptd.hidden = true; + host.append(chip, plain, keyless, twice, slow, rough, ptd); Promise.resolve().then(() => app.api?.torchBuild?.()).then((r) => { if (r && r.slow_int8) { slow.title = r.slow_int8; slow.hidden = false; } }).catch(() => {}); // unknown build: the loader still says it in the log when an int8 model loads @@ -44,6 +48,14 @@ export default { plain.hidden = !ps.allOff?.(); keyless.hidden = !(taste && ps.activeModules().includes(taste) && ps.useful(taste) && !String((ps.currentValues().taste || {}).key || "").trim()); }; + const drawPtd = () => { + const ps = app.pipeline, values = ps.currentValues(), mod = ps.modulesById?.().ptd; + if (!mod || ps.allOff?.() || !ps.activeModules().includes(mod) || (values.ptd || {}).enabled !== true) { ptd.hidden = true; return; } + const others = ps.activeModules().filter((m) => m.id !== "ptd" && (values[m.id] || {}).enabled === true && ps.useful(m)).map((m) => m.title || m.id); + if ((ps.slots() || []).some((s) => (s.inputs || {}).sla === true)) others.unshift("SLA attention"); + ptd.hidden = !others.length; + ptd.title = `PTD was trained without these, and they can spoil its pictures: ${others.join(", ")}. Switch them off if the result looks wrong.`; + }; let asking = 0; const drawRough = () => { const ps = app.pipeline, cam = ps.modulesById?.().conditioning_shot_camera, ask = ++asking; @@ -53,9 +65,10 @@ export default { }).catch(() => {}); // no add-on list: say nothing }; drawPlain(); + drawPtd(); drawRough(); draw(app.pipeline.slots()); drawTwice(app.pipeline.slots()); - return app.pipeline.subscribe ? app.pipeline.subscribe(() => { draw(app.pipeline.slots()); drawTwice(app.pipeline.slots()); drawPlain(); drawRough(); }) : undefined; + return app.pipeline.subscribe ? app.pipeline.subscribe(() => { draw(app.pipeline.slots()); drawTwice(app.pipeline.slots()); drawPlain(); drawPtd(); drawRough(); }) : undefined; }, }; diff --git a/app/ui/features/generate/warn.test.js b/app/ui/features/generate/warn.test.js index 12594aaf..3f196e05 100644 --- a/app/ui/features/generate/warn.test.js +++ b/app/ui/features/generate/warn.test.js @@ -84,3 +84,19 @@ test("the rougher-camera chip shows only while shot camera is active and the cam active = []; listener(); await tick(); assert.equal(rough(host).hidden, true, "shot camera not in use: nothing to say"); }); + +test("the PTD chip names what else is on, and only while PTD itself is on", () => { + const ptd = { id: "ptd", title: "PTD few-step" }, sharp = { id: "sharpness", title: "Sharpness" }; + let values = { ptd: { enabled: true }, sharpness: { enabled: true } }, slots = [{ id: "m", node: "L", inputs: { sla: true } }], listener; + const pipeline = { slots: () => slots, allOff: () => false, modulesById: () => ({ ptd }), activeModules: () => [ptd, sharp], + useful: () => true, currentValues: () => values, subscribe: (fn) => { listener = fn; return () => {}; } }; + const host = document.createElement("div"); + warn.setup({ host, app: { pipeline } }); + const chipOf = () => [...host.querySelectorAll("button")].find((b) => /PTD with other/.test(b.textContent)); + assert.equal(chipOf().hidden, false); + assert.match(chipOf().title, /SLA attention, Sharpness/); + values = { ptd: { enabled: true }, sharpness: { enabled: false } }; slots = []; listener(); + assert.equal(chipOf().hidden, true, "nothing else on"); + values = { ptd: { enabled: false }, sharpness: { enabled: true } }; listener(); + assert.equal(chipOf().hidden, true, "PTD off: no clash to name"); +}); diff --git a/modules/sampling/ptd/__init__.py b/modules/sampling/ptd/__init__.py index f0476432..e2149f6c 100644 --- a/modules/sampling/ptd/__init__.py +++ b/modules/sampling/ptd/__init__.py @@ -54,7 +54,8 @@ SETTINGS = { "type": "bool", "default": False, "label": "Few-step PTD", "hint": "Renders in the adapter file's own 4 to 8 steps with no CFG. The sampler, steps and scheduler " - "are replaced; PTD cannot start from a picture or a mask.", + "are replaced; PTD cannot start from a picture or a mask. Use the base the file was trained on, " + "and switch off SLA, turbo LoRAs and other enhancements, which it was not trained with.", }, "adapter": { "type": "enum", "default": _FOUND[0] if _FOUND else "",