From e24ea9e71d0a8a8601ddf161def67ac6256a8678 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Mon, 28 Sep 2026 16:29:24 +0700 Subject: [PATCH] web: open the stage on the neutral stock, not the CLASSIC NEGIPES preset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The boot recipe was DEFAULT_RECIPES[0], so every file that landed — NEF or JPEG — came in with exposure +2 (+0.5 EV), contrast +1, saturation -1, 6300K / tint +2, highlight -2, clarity +2, grain +4 and the classic-neg filter already on. On APACHAI-20160215-357.NEF that read as pink skin and blown highlights, and it made a correct decode look like a decode bug. Develop itself was fine: 179.0/168.2/171.2 against the embedded preview's 178.2/167.1/170.7. Boot is now BASE_RECIPE — no filter, all knobs default, the same look COMPARE splits against. A session left by the old boot holds that preset verbatim, which was never the visitor's pick, so it is dropped; anything the visitor actually touched moves the stored object off the exact fingerprint and is kept. Render bytes, same 1600x1068 view of the JPEG (R/G/B means): source file 170.7 / 159.1 / 163.1 before 194.7 / 178.0 / 177.2 after 169.5 / 158.5 / 162.0 NEF render stage: 202.4 / 186.3 / 185.4 -> 177.9 / 167.3 / 169.9 Output is byte-identical to the old neutral render (md5 a3932746...), while a clicked chip (classic-neg-default) and a tweaked session both still restore their own bytes. --- docker/frontend/src/App.tsx | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docker/frontend/src/App.tsx b/docker/frontend/src/App.tsx index 9182cf5..2a6dd49 100644 --- a/docker/frontend/src/App.tsx +++ b/docker/frontend/src/App.tsx @@ -292,12 +292,23 @@ export function Workspace() { const [comparing, setComparing] = useState(false); const [compareUrl, setCompareUrl] = useState(null); - const [recipe, setRecipe] = useState(() => ({ - ...DEFAULT_RECIPES[0], - id: 'web-current', - name: DEFAULT_RECIPES[0]?.name ?? 'PROVIA', - ...session.recipe, - })); + const [recipe, setRecipe] = useState(() => { + // The stage opens on the photo itself — the neutral stock, every knob at its + // default and no film filter, the same recipe COMPARE splits against — so a + // file that is dropped in is shown as the file. The bundled CLASSIC NEGIPES + // preset used to sit here as the boot look, and it baked exposure +2 (+0.5 + // EV) plus a 6300K / tint +2 white balance onto every photo the moment it + // landed: that is what blew the highlights and turned skin pink on a fresh + // NEF. A look is now there only because a chip was clicked. + const boot = DEFAULT_RECIPES[0]; + // A session written by that old boot still holds the preset verbatim, which + // was never the visitor's choice; it is dropped instead of restored. Any + // edit to a knob moved the stored object off this exact fingerprint. + const stale = !!boot && session.recipe?.id === 'web-current' && + session.recipe.baseFilter === boot.baseFilter && + JSON.stringify(session.recipe.adjustments) === JSON.stringify(boot.adjustments); + return { ...BASE_RECIPE, id: 'web-current', ...(stale ? null : session.recipe) }; + }); const [simId, setSimId] = useState(() => session.simId ?? null); // HOVER PREVIEW: the look of the sim or recipe the pointer is over, laid on the // photo for as long as it stays there. It never becomes `recipe`, so SAVE,