feat(B05): 종단도 방향키 연속 이동·이력 한 덩어리·쉬프트 상하

- 방향키 청취를 패널 루트에서 window 로 옮김 — 그래프 클릭 뒤 포커스가 body 에
  남아 방향키가 아무 반응이 없던 결함 수정 (패널이 화면에 없으면 무시)
- 길게 누르면 편집 버튼과 같은 속도로 연속 이동 (0.5초 뒤 0.1초 간격, OS 키 반복 무시)
- 이력에 pause()/resume() 추가 — 연속 이동 전체가 되돌리기 1회로 원위치.
  구조물 이동은 서버 재계산이 뒤따르므로 키를 뗀 뒤 700ms 뒤 기록
- pause() 에서도 관 목록을 세션에 흘려 되돌리기 기준을 조작 전 값으로 고정
- 좌우 연속 이동 중에는 처음 잡은 구조물 측점·자체 누가거리 카운터 사용 —
  관 측점 id 가 누가거리 기반이라 한 번 옮기면 선택이 풀려 한 칸만 움직이던 결함 수정
- ProfileTools.nudge(delta) 노출 — [쉬프트] 모드의 상·하 방향키가 도구 ▲▼ 와
  같은 경로(onShift)로 구간 평행이동

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-04 07:11:41 +09:00
co-authored by Claude Opus 5
parent af85dea714
commit ecb3bcb9ce
4 changed files with 144 additions and 26 deletions
+15 -1
View File
@@ -75,6 +75,10 @@ function restoreSnapshot(snapshot: HistorySnapshot): void {
export interface ProfileHistory {
/** 조작이 끝난 뒤 현재 상태를 이력에 쌓는다(직전과 같으면 무시). */
record(): void;
/** 연속 조작(길게 누르기) 동안 기록을 미룬다 — 20칸 이동이 되돌리기 20번이 되는 것을 막는다. */
pause(): void;
/** 연속 조작이 끝났음을 알린다 — 그 동안의 변화를 **한 덩어리로** 한 번만 쌓는다. */
resume(): void;
undo(): boolean;
redo(): boolean;
canUndo(): boolean;
@@ -92,6 +96,8 @@ export function createProfileHistory(onRestore: () => void, limit = 50): Profile
let cursor = 0;
/** 복원 중에 들어오는 record()를 무시한다 — 복원이 새 이력을 만들면 redo가 사라진다. */
let restoring = false;
/** 연속 조작 중에는 record()를 흘려보내고 resume()에서 한 번만 쌓는다. */
let paused = false;
function apply(index: number): boolean {
if (index < 0 || index >= stack.length) return false;
@@ -108,7 +114,7 @@ export function createProfileHistory(onRestore: () => void, limit = 50): Profile
return {
record() {
if (restoring) return;
if (restoring || paused) return;
const snapshot = takeSnapshot();
if (sameSnapshot(snapshot, stack[cursor])) return;
stack.splice(cursor + 1);
@@ -116,6 +122,14 @@ export function createProfileHistory(onRestore: () => void, limit = 50): Profile
if (stack.length > limit) stack.shift();
cursor = stack.length - 1;
},
pause() {
paused = true;
},
resume() {
if (!paused) return;
paused = false;
this.record();
},
undo: () => apply(cursor - 1),
redo: () => apply(cursor + 1),
canUndo: () => cursor > 0,