fix(B05·B06): 조작 놓은 직후 ↶ 바로 켜짐 · 기다림 중 ↶ = 먼저 찍고 되돌림 (PLAN 477-3 · 요구 433)

- 찍기 기다림(400 ms) 들어가면 한 틱 뒤 단추 맞춤 — 세션이 지금 걸음과 다를 때만 켜짐(바뀐 것 없는 누름은 그대로 꺼짐)
- ↶ ↷ 누르면 기다리던 찍기 먼저(누름 중이어도) — 방금 조작이 한 걸음으로 되돌아감
- 기다림 시간 · 서버 다시 셈 묶음은 그대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 07:14:34 +09:00
co-authored by Claude Opus 5.5
parent 10fba8983c
commit 78188cf7a3
+27 -8
View File
@@ -114,10 +114,18 @@ const changed = (): void => hooks.forEach((entry) => entry.change?.());
function settle(): void { function settle(): void {
clearTimeout(settleTimer); clearTimeout(settleTimer);
settling = true; settling = true;
settleTimer = setTimeout(() => { settleTimer = setTimeout(flushSettle, SETTLE_MS);
settling = false; // ↶ 는 기다리지 않고 켬(477-3) — 같은 손길의 칸 쓰기가 끝난 한 틱 뒤 단추만 맞춤
active?.record(); setTimeout(changed, 0);
}, SETTLE_MS); }
/** 기다리던 찍기를 지금 — 기다림이 끝났거나 그 사이 ↶ ↷ 를 누름(477-3 · 한 걸음 그대로). */
function flushSettle(): void {
if (!settling) return;
clearTimeout(settleTimer);
settling = false;
active?.recordNow();
changed();
} }
function watchPointer(): void { function watchPointer(): void {
@@ -167,6 +175,8 @@ function restoreSnapshot(projectId: string, snapshot: HistorySnapshot): void {
export interface ProfileHistory { export interface ProfileHistory {
/** 조작이 끝난 뒤 현재 상태를 이력에 쌓는다(직전과 같으면 무시). */ /** 조작이 끝난 뒤 현재 상태를 이력에 쌓는다(직전과 같으면 무시). */
record(): void; record(): void;
/** 누름이 남아 있어도 지금 찍음 — ↶ 단추 누름 중 기다리던 찍기(477-3). */
recordNow(): void;
/** 연속 조작(길게 누르기) 동안 기록을 미룬다 — 20칸 이동이 되돌리기 20번이 되는 것을 막는다. */ /** 연속 조작(길게 누르기) 동안 기록을 미룬다 — 20칸 이동이 되돌리기 20번이 되는 것을 막는다. */
pause(): void; pause(): void;
/** 연속 조작이 끝났음을 알린다 — 그 동안의 변화를 **한 덩어리로** 한 번만 쌓는다. */ /** 연속 조작이 끝났음을 알린다 — 그 동안의 변화를 **한 덩어리로** 한 번만 쌓는다. */
@@ -224,6 +234,10 @@ export function createProfileHistory(
return true; return true;
} }
/** 찍기를 기다리는 동안 세션이 지금 걸음과 다름 — ↶ 를 바로 켜는 몫(477-3). */
const pending = (): boolean =>
settling && !restoring && !sameSnapshot(takeSnapshot(projectId, stack[cursor]), stack[cursor]);
const history: ProfileHistory = { const history: ProfileHistory = {
record() { record() {
if (restoring || paused) return; if (restoring || paused) return;
@@ -231,6 +245,10 @@ export function createProfileHistory(
releaseWaiters.add(history.record); releaseWaiters.add(history.record);
return; return;
} }
history.recordNow();
},
recordNow() {
if (restoring || paused) return;
sync(); sync();
restAt = gestures; restAt = gestures;
const snapshot = takeSnapshot(projectId, stack[cursor]); const snapshot = takeSnapshot(projectId, stack[cursor]);
@@ -253,10 +271,11 @@ export function createProfileHistory(
paused = false; paused = false;
history.record(); history.record();
}, },
undo: () => apply(cursor - 1), // 기다리던 찍기 먼저 — 바로 누른 ↶ = 방금 조작 되돌림 · 묶음 한 걸음(477-3)
redo: () => apply(cursor + 1), undo: () => (flushSettle(), apply(cursor - 1)),
canUndo: () => cursor > 0, redo: () => (flushSettle(), apply(cursor + 1)),
canRedo: () => cursor < stack.length - 1, canUndo: () => cursor > 0 || pending(),
canRedo: () => cursor < stack.length - 1 && !pending(),
}; };
rebaseActive = () => { rebaseActive = () => {
if (restoring || paused || pointerHeld || settling || gestures !== restAt) return; if (restoring || paused || pointerHeld || settling || gestures !== restAt) return;