Show the pre-edit values while the finger holds, and pass the FRAME tab flag

Pressing and holding on the picture with the LIGHT/WB/FX panel open now
rewinds the last edit, and the panel rewinds with it: the chip numbers and
the open slider read the same pre-edit snapshot, so before and after can be
compared by eye and not just by looking at the picture. Only the displayed
values move -- the stored adjustments, RECIPE and the export keep the real
ones, and a gap longer than PEEK_GAP_MS starts a new snapshot for the next
gesture.

Also pass frameTabActive, without which the FRAME preview zoom added in
Viewfinder.tsx could never turn on.
This commit is contained in:
2026-09-12 08:51:43 +07:00
parent 98b8021f3b
commit a662674478
+38 -3
View File
@@ -578,6 +578,30 @@ export default function App() {
cropRatio !== 'none' ||
differs(cropRect, DEFAULT_CROP_RECT);
// ---- Hold-to-compare -----------------------------------------------------
// While a LIGHT/WB/FX panel is open, pressing and holding on the picture
// shows it with the last edit undone, so the user can flick between before
// and after. The picture and the parameter display both follow the peek;
// the stored values, RECIPE and the export never move.
const PEEK_GAP_MS = 700; // a longer pause = a new gesture, so re-snapshot
const peekRef = useRef<{ before: ColorAdjustments | null; at: number }>({
before: null,
at: 0,
});
const [peekAdjustments, setPeekAdjustments] = useState<ColorAdjustments | null>(null);
const peekArmed = openTab === 'iq' || openTab === 'wb' || openTab === 'filters';
const handleComparePeek = useCallback((on: boolean) => {
setPeekAdjustments(on ? peekRef.current.before : null);
}, []);
// The values the panel and the picture SHOW. While the finger is down they
// fall back to the pre-edit snapshot, so the chip numbers and the open slider
// travel back with the picture and the before/after is readable, not just
// visible. The real values stay untouched: writes, RECIPE and the on-image
// drag baseline all keep reading `adjustments`.
const shownAdjustments = peekArmed && peekAdjustments ? peekAdjustments : adjustments;
const withPeek = (r: Recipe): Recipe =>
shownAdjustments === r.adjustments ? r : { ...r, adjustments: shownAdjustments };
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
@@ -586,6 +610,15 @@ export default function App() {
// skipped identical EV needs no re-send.
const keys = Object.keys(updates) as (keyof ColorAdjustments)[];
if (keys.every((k) => updates[k] === adjustments[k])) return;
// Remember where THIS edit started. A pause longer than PEEK_GAP_MS counts
// as a new gesture, so the snapshot is retaken; within one drag the earlier
// value survives and the peek shows the drag's own starting point.
// ponytail: a >700ms stall in the middle of one drag re-snapshots mid-way.
const peekNow = Date.now();
if (peekNow - peekRef.current.at > PEEK_GAP_MS) {
peekRef.current.before = adjustments;
}
peekRef.current.at = peekNow;
// B8: never call a setState inside another setState's updater.
const next = { ...adjustments, ...updates };
setAdjustments(next);
@@ -1300,7 +1333,7 @@ export default function App() {
ref={viewfinderRef}
mode={mode}
facing={cameraFacing}
recipe={
recipe={withPeek(
selectedRecipe || {
id: 'temp',
name: 'Custom',
@@ -1309,7 +1342,7 @@ export default function App() {
frameId: selectedFrame,
useGeotag,
}
}
)}
selectedFrame={selectedFrame}
useGeotag={useGeotag}
gpsInfo={gpsInfo}
@@ -1331,6 +1364,7 @@ export default function App() {
onExposureChange={(ev) => handleUpdateAdjustments({ exposureCompensation: ev })}
meteringAdjustments={meteringAdj}
imageAdjustTarget={imageAdjustTarget}
onComparePeek={peekArmed ? handleComparePeek : undefined}
customWm={customWm}
wmRotation={wmRotation}
gpsWm={gpsWm}
@@ -1340,6 +1374,7 @@ export default function App() {
// The mark only captures image touches while its panel is open, so a
// watermark left ON cannot block tap-to-focus or pinch/zoom.
wmEditing={openTab === 'frame' && wmPanelOpen}
frameTabActive={openTab === 'frame'}
onWmPositionChange={(x, y) => setCustomWm((p) => ({ ...p, x, y }))}
onWmEdit={() => {
// Double-tap on the mark re-opens its editor (and with it the
@@ -1381,7 +1416,7 @@ export default function App() {
activeTab={openTab}
recipes={recipes}
currentRecipeId={selectedRecipe?.id || ''}
adjustments={adjustments}
adjustments={shownAdjustments}
onResetAll={handleResetAll}
resetDirty={resetDirty}
selectedFrame={selectedFrame}