feat(panel): RESET shows a star while the look differs from a clean start

This commit is contained in:
2026-09-11 09:08:37 +07:00
parent 7ed4301055
commit ba7e579986
2 changed files with 37 additions and 1 deletions
+29
View File
@@ -28,6 +28,14 @@ import { PARAM_DEFS } from './src/utils/paramDefs';
// @ts-ignore
import './global.css';
// Shallow value diff over the union of both objects' keys: undefined and a
// missing key both read as "no value", so an old session without the newer
// optional knobs is not reported as an edit.
const differs = <T extends object>(a: T, b: T) => {
const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof T>;
return [...keys].some((k) => (a[k] ?? null) !== (b[k] ?? null));
};
const SESSION_KEY = 'saved_session';
const STARTUP_MODE_KEY = 'startup_mode';
// Camera/device settings that survive restarts regardless of startup mode
@@ -523,8 +531,28 @@ export default function App() {
setWmRotation(0);
setPhotoRotation(0);
setWallLandscape(false);
setCropRatio('none');
setCropRect(DEFAULT_CROP_RECT);
};
// RESET lights up while the look differs from what handleResetAll puts back —
// every clause here has a matching setter there, or the star never goes out.
// The clean-start values: the startup sim, neutral knobs, no frame, no crop,
// both marks at their defaults, GPS stamp off, nothing rotated.
const resetDirty =
(selectedRecipe?.id ?? 'sim-provia') !== 'sim-provia' ||
differs(adjustments, DEFAULT_ADJUSTMENTS) ||
selectedFrame !== 'none' ||
differs(customWm, DEFAULT_CUSTOM_WM) ||
differs(customWmApplied, DEFAULT_CUSTOM_WM) ||
differs(gpsWm, DEFAULT_GPS_WM) ||
useGeotag ||
wmRotation !== 0 ||
photoRotation !== 0 ||
wallLandscape ||
cropRatio !== 'none' ||
differs(cropRect, DEFAULT_CROP_RECT);
const handleUpdateAdjustments = (updates: Partial<ColorAdjustments>) => {
// Storm guard, generic across every param: same-value writes (slider
// micro-jitter / repeated pointer-move events) still build a fresh
@@ -1276,6 +1304,7 @@ export default function App() {
currentRecipeId={selectedRecipe?.id || ''}
adjustments={adjustments}
onResetAll={handleResetAll}
resetDirty={resetDirty}
selectedFrame={selectedFrame}
cropRatio={cropRatio}
onUpdateCropRatio={setCropRatio}
+8 -1
View File
@@ -41,6 +41,9 @@ interface AdjustmentPanelProps {
// RESET: the app's own defaults, from any tab — the startup look, every
// parameter, the frame and the watermarks.
onResetAll: () => void;
// True while the look differs from a clean start: the RESET chip says so
// ("RESET *", amber) instead of silently doing nothing visible.
resetDirty: boolean;
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),
@@ -179,6 +182,7 @@ export default function AdjustmentPanel({
currentRecipeId,
adjustments,
onResetAll,
resetDirty,
selectedFrame,
cropRatio,
onUpdateCropRatio,
@@ -327,8 +331,11 @@ export default function AdjustmentPanel({
// one can sit off-screen just when it is wanted.
const resetChip = (): ChipDef => ({
key: 'reset-all',
label: 'RESET',
// Trailing star + amber text: there is a change to undo (the chip is
// otherwise a plain neutral RESET that puts the code back to a clean start).
label: resetDirty ? 'RESET *' : 'RESET',
active: false,
amberValue: resetDirty,
onPress: onResetAll,
});