From 7454ea1218065c2169db392fac3706b4a256a02f Mon Sep 17 00:00:00 2001 From: locphamtran Date: Thu, 10 Sep 2026 22:18:43 +0700 Subject: [PATCH] feat(panel): one RESET on every tab, back to the app's defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RESET was look-relative: it only put the sliders back on the values the active recipe ships with, so the look itself (a saved recipe, a film sim) survived the tap. It now restores the state a clean start leaves — the PROVIA startup look, every parameter neutral, frame off, the custom and GPS watermarks back to their defaults — and it sits on every tab, pinned outside the scrolling chip row so it can never scroll out of reach. Mode and aspect ratio stay put: they frame the shot being worked on, not the look. --- App.tsx | 30 ++++++++++-- src/components/AdjustmentPanel.tsx | 79 +++++++++++------------------- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/App.tsx b/App.tsx index dfd166f..9e7fd87 100644 --- a/App.tsx +++ b/App.tsx @@ -499,6 +499,32 @@ export default function App() { setGpsWm({ ...DEFAULT_GPS_WM }); }; + // RESET (panel chip, any tab): the state a default start would leave — the + // startup look, every parameter back to neutral, frame off and both + // watermarks back to their defaults. Same pieces as resetEditsToRecipe, but + // the look goes back to the app's own default rather than the recipe's. + const handleResetAll = () => { + const provia = FILM_SIMS.find((f) => f.id === 'sim-provia'); + if (provia) { + applyRecipe(filmSimToRecipe(provia)); + } else { + setSelectedRecipe(null); + setAdjustments({ ...DEFAULT_ADJUSTMENTS }); + setSelectedFrame('none'); + } + setCustomWm({ ...DEFAULT_CUSTOM_WM }); + setCustomWmApplied({ ...DEFAULT_CUSTOM_WM }); + setGpsWm({ ...DEFAULT_GPS_WM }); + // The rest of the look's state, each back to the value a clean start ships + // with: the GPS stamp off, both watermark rotations upright, the library + // photo and its wall frame back to portrait. ponytail: mode + aspect stay + // put — they frame the shot you are working on, not the look. + setUseGeotag(false); + setWmRotation(0); + setPhotoRotation(0); + setWallLandscape(false); + }; + const handleUpdateAdjustments = (updates: Partial) => { // Storm guard, generic across every param: same-value writes (slider // micro-jitter / repeated pointer-move events) still build a fresh @@ -1249,9 +1275,7 @@ export default function App() { recipes={recipes} currentRecipeId={selectedRecipe?.id || ''} adjustments={adjustments} - lookDefaults={ - (selectedRecipe && pristineRecipe(selectedRecipe)?.adjustments) || DEFAULT_ADJUSTMENTS - } + onResetAll={handleResetAll} selectedFrame={selectedFrame} cropRatio={cropRatio} onUpdateCropRatio={setCropRatio} diff --git a/src/components/AdjustmentPanel.tsx b/src/components/AdjustmentPanel.tsx index d3554dc..bbd8322 100644 --- a/src/components/AdjustmentPanel.tsx +++ b/src/components/AdjustmentPanel.tsx @@ -36,9 +36,9 @@ interface AdjustmentPanelProps { recipes: Recipe[]; currentRecipeId: string; adjustments: ColorAdjustments; - // The values the active look ships with — a saved recipe's own set, a film - // sim's canonical set, or the neutral defaults. RESET puts them back. - lookDefaults: ColorAdjustments; + // RESET: the app's own defaults, from any tab — the startup look, every + // parameter, the frame and the watermarks. + onResetAll: () => void; selectedFrame: FrameId; // FRAME tab CROP. Only meaningful on the library's plain frames (the camera // composes through its ratio band, polaroid/wall through their own window), @@ -166,7 +166,7 @@ export default function AdjustmentPanel({ recipes, currentRecipeId, adjustments, - lookDefaults, + onResetAll, selectedFrame, cropRatio, onUpdateCropRatio, @@ -250,17 +250,6 @@ export default function AdjustmentPanel({ const a = adjustments; - // RESET (parameter tabs): every number of the active look back on the value - // it ships with. Only the parameters count as "changed" — the groups below - // are part of the same look, so they light it too. - const lookDirty = - [...PARAM_DEFS.iq, ...PARAM_DEFS.wb, ...PARAM_DEFS.filters].some( - (d) => d.get(a) !== d.get(lookDefaults) - ) || - (a.colorChrome || 'none') !== (lookDefaults.colorChrome || 'none') || - (a.colorChromeBlue || 'none') !== (lookDefaults.colorChromeBlue || 'none') || - String(a.dynamicRange ?? 'auto') !== String(lookDefaults.dynamicRange ?? 'auto'); - const renderChip = (c: ChipDef) => { const tone = c.active ? 'border-amber-500/70 bg-amber-500/15' @@ -317,19 +306,16 @@ export default function AdjustmentPanel({ }) ); + // Every tab carries the same RESET. It is pinned OUTSIDE the scroll row on + // purpose: the rows keep their scroll offset across tabs, so a chip inside + // one can sit off-screen just when it is wanted. const resetChip = (): ChipDef => ({ - key: 'reset-params', + key: 'reset-all', label: 'RESET', active: false, - onPress: () => onUpdateAdjustments({ ...lookDefaults }), + onPress: onResetAll, }); - // Pinned OUTSIDE the scroll row: the rows keep their scroll offset across - // tabs, so a RESET sitting inside one can be scrolled off just when it is - // wanted — after a tweak halfway along the strip. - const resetChipFixed = () => - lookDirty ? {renderChip(resetChip())} : null; - const choiceChips = ( keyPrefix: string, options: { v: string; d: string }[], @@ -549,28 +535,22 @@ export default function AdjustmentPanel({ // opens its AUTO/DR100/DR200/DR400 strip above (same as the chrome // groups on WB). return ( - - {resetChipFixed()} - - {paramChips(paramDefs.iq)} - {renderChip(groupChip('dr'))} - - + + {paramChips(paramDefs.iq)} + {renderChip(groupChip('dr'))} + ); case 'wb': return ( - - {resetChipFixed()} + {choiceChips( @@ -595,17 +575,13 @@ export default function AdjustmentPanel({ case 'filters': return ( - - {resetChipFixed()} - - {paramChips(paramDefs.filters)} - - + + {paramChips(paramDefs.filters)} + ); case 'frame': @@ -798,7 +774,10 @@ export default function AdjustmentPanel({ /> )} - {chipsRow} + + {renderChip(resetChip())} + {chipsRow} +