Skip to content

[refactor/MAT-901] DrawingCanvas HistoryManager 명령 객체 기반 전환#352

Draft
b0nsu wants to merge 3 commits into
refactor/mat-899-eraser-bounds-first-passfrom
refactor/mat-901-history-manager-lite
Draft

[refactor/MAT-901] DrawingCanvas HistoryManager 명령 객체 기반 전환#352
b0nsu wants to merge 3 commits into
refactor/mat-899-eraser-bounds-first-passfrom
refactor/mat-901-history-manager-lite

Conversation

@b0nsu
Copy link
Copy Markdown
Collaborator

@b0nsu b0nsu commented May 26, 2026

Summary

매 변경마다 전체 strokes/texts deepCopy 로 스냅샷하던 history 를 명령 객체(append-stroke / erase-strokes / edit-text) 기반으로 전환.

Linear

Changes

  • `HistoryEntry` discriminated union + `HistoryManager` 클래스 (push/undo/redo/canUndo/canRedo/clear/lock/unlock, 50개 cap, listener, erase transaction)
  • entry 에 paths 까지 캡처 → undo/redo 시 path 재계산 없이 즉시 swap
  • `pathsRef` 추가 (paths state 페어). 모든 페어 호출은 `applyStrokes(strokes, paths)` helper 로 통일 → length 불일치 silent bug 방지
  • `finalizeStroke`: append-stroke push
  • `eraseAtPoint`: stroke + path 페어 필터 (paths 재계산 제거)
  • `startEraser` / `finalizeEraser`: begin/commit/discard transaction
  • `confirmTextInput`: edit-text push (textsBefore/After 캡처)
  • `undo` / `redo`: entry kind 별 처리. append-stroke 는 `slice(0,-1)` 로 O(1), erase 는 캐시 swap, edit-text 는 텍스트 추가 직후 편집 모드 재진입 특수 케이스 유지
  • `recalculateMaxY` 헬퍼 추출
  • `HistoryManager.lock() / unlock()` 도입 — activeTextInput 잠금을 manager 내부로 (외부 가드 누락 위험 제거)
  • `historyManagerRef` lazy init — React Strict mode 의 double-invoke garbage alloc 방지
  • `onHistoryChange` 를 ref 로 보관 + listener 등록 effect 가 mount 1회만 실행 (dep 회전 제거)
  • activeTextInput 변경 시 `manager.lock()/unlock()` → notify 자동
  • `canUndo` / `canRedo` 를 historyManager 위임으로 단순화
  • `deepCopyStrokes` / `deepCopyTexts` / `saveToHistory` / `restoreFromHistory` / `historyRef` / `historyIndexRef` / `HistoryState` 제거

Testing

  • `pnpm typecheck && pnpm lint && pnpm build` 통과

Risk / Impact

  • props / imperative API 시그니처 변경 없음 (`undo` / `redo` / `canUndo` / `canRedo` / `onHistoryChange` 동작 동일)
  • 동작 변화 없음. 텍스트 추가 직후 undo → 편집 모드 재진입 기존 특수 케이스 유지

Dependency

MAT-898 위에 stacked (base: `refactor/mat-898-stroke-bounds`). PR #348 머지 후 base 자동 develop 으로 변경.

@linear
Copy link
Copy Markdown

linear Bot commented May 26, 2026

MAT-901

b0nsu and others added 3 commits May 26, 2026 17:11
- HistoryEntry union: append-stroke / erase-strokes / edit-text
- HistoryManager 클래스 (push / undo / redo / canUndo / canRedo / clear, 50개 cap, listener)
- 매 변경마다 deepCopyStrokes/deepCopyTexts 전체 스냅샷 제거 → 변경 단위 캡처
- paths 도 entry 에 함께 캡처해 undo/redo 시 path 재계산 없이 swap
- pathsRef 추가 (paths state 와 페어)
- finalizeStroke: append-stroke push
- eraser: beginEraseTransaction / commitEraseTransaction / discardEraseTransaction
- confirmTextInput: edit-text push (textsBefore/After 캡처)
- undo: entry kind 분기 — append-stroke 는 slice(0,-1) O(1), erase 는 캐시 swap, edit-text 는 텍스트 추가 직후 편집 모드 재진입 특수 케이스 유지
- redo: 동일 패턴
- recalculateMaxY 헬퍼로 maxY 재계산 통합
- clear / canUndo / canRedo 를 historyManager 에 위임
- deepCopyStrokes/deepCopyTexts/saveToHistory/restoreFromHistory/historyRef/historyIndexRef/HistoryState 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- HistoryManager.lock() / unlock() 도입 — activeTextInput 잠금을 manager 내부로
  · canUndo/canRedo 가 locked 자체 판단, 외부 가드 누락 위험 제거
- historyManagerRef lazy init — React Strict mode 의 double-invoke 에서 garbage 인스턴스 alloc 방지
- onHistoryChange 를 ref 로 보관 → listener 등록 effect 가 mount 1회만 실행
- activeTextInput 변경 시 manager.lock/unlock 호출 → notify 자동 → 별도 effect 제거
- applyStrokes(strokes, paths) helper — strokesRef/pathsRef + setStrokes/setPaths 페어 invariant 보장
- ref API canUndo/canRedo 를 manager 위임으로 단순화

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Y 활용

기존: N stroke × M points flatMap+map 으로 모든 좌표 순회.
변경: stroke.bounds.maxY 캐시(MAT-898) 활용, for 루프로 O(N). N×M points 누적 시 ~200× 가속.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@b0nsu b0nsu force-pushed the refactor/mat-901-history-manager-lite branch from bbbbe4f to 15b826d Compare May 26, 2026 08:13
@b0nsu b0nsu changed the base branch from refactor/mat-898-stroke-bounds to refactor/mat-899-eraser-bounds-first-pass May 26, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant