Skip to content

Task #661: 드래그 선택 중 커서/스크롤 튐 정정 (closes #661)#718

Closed
postmelee wants to merge 6 commits into
edwardkim:develfrom
postmelee:pr-task661
Closed

Task #661: 드래그 선택 중 커서/스크롤 튐 정정 (closes #661)#718
postmelee wants to merge 6 commits into
edwardkim:develfrom
postmelee:pr-task661

Conversation

@postmelee

Copy link
Copy Markdown
Collaborator

Summary

  • rhwp-studio에서 텍스트 드래그 선택 중 커서와 스크롤 위치가 포인터와 무관하게 튀는 문제를 정정합니다. Closes rhwp-studio: 텍스트 드래그 선택 중 커서와 스크롤 위치가 튀는 현상 #661
  • 드래그 중에는 caret rect 기준 scrollCaretIntoView()를 호출하지 않고, 포인터가 편집 영역 상/하단 edge에 들어온 경우에만 별도 RAF 루프로 자동 스크롤합니다.
  • 드래그 중 마지막 포인터 좌표를 저장하고, 스크롤이 실제 발생한 경우 같은 포인터 좌표로 다시 hit-test하여 선택 focus를 이어갑니다.
  • 편집 영역 밖으로 포인터가 나가도 선택 드래그가 계속되도록 document-level mousemove를 드래그 동안만 등록하고 종료/dispose()에서 해제합니다.
  • 검증 중 별도로 발견한 표 셀 빈 영역 클릭 hit-test 문제는 #717로 분리 등록했습니다.

Root cause

  • 기존 드래그 선택 경로는 mousemove마다 hit-test 결과로 cursor focus를 이동한 뒤 caret 갱신 과정에서 caret rect 기준 자동 스크롤을 함께 수행했습니다.
  • 드래그 중 선택 focus가 다른 페이지/문단으로 순간 이동하면 스크롤 컨테이너가 포인터가 아니라 caret rect를 따라가면서, 사용자가 드래그 중인 위치와 문서 스크롤 위치가 함께 튀었습니다.
  • 이번 PR은 드래그 중 caret 갱신과 스크롤 책임을 분리합니다. caret은 선택 상태 표시만 갱신하고, 스크롤은 포인터 edge 감지만 담당합니다.

변경 사항

  • InputHandler.updateCaretDuringDrag() 추가
    • 일반 updateCaret()과 달리 scrollCaretIntoView()를 호출하지 않습니다.
    • 최신 devel에 없는 선행 #664의 CaretRenderer.updateLive()에 의존하지 않도록 기존 CaretRenderer.update()를 사용합니다.
  • 텍스트 선택 드래그 helper 추가
    • startTextSelectionDrag()
    • updateTextSelectionDragPointer()
    • updateTextSelectionDragFromPointer()
    • stopTextSelectionDrag()
  • 포인터 edge 자동 스크롤 추가
    • edge 폭: 48px
    • step: 2px ~ 20px
    • scrollTop 변화가 있을 때만 선택 focus 재계산
  • rhwp-studio/e2e/drag-selection-autoscroll.test.mjs 추가
    • 70줄 문서 생성
    • 첫 줄에서 하단 edge까지 드래그
    • 스크롤 증가, 선택 상태, highlight 표시, focus 문단 확장을 검증

Test plan

  • cargo test — 1254 passed, 0 failed, 3 ignored
  • cargo clippy -- -D warnings
  • cd rhwp-studio && npm run build
    • Vite chunk size warning만 발생, 빌드 성공
  • cd rhwp-studio && CHROME_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" npm run e2e:drag-autoscroll -- --mode=headless
    • scrollTop: 0 -> 1529
    • hasSelection: true
    • selection.end.paragraphIndex: 69
    • highlightCount: 70
  • git diff --check upstream/devel...HEAD

PR 구성 메모

관련 자료

edwardkim added a commit that referenced this pull request May 8, 2026
closes #658

4 commits 단계별 보존 (작업지시자 직접 결정):
- e0ce874 Task #658: Add selection rect diagnostics
- f3c7fc0 Task #658: Fix selection rect line boundaries
- 99b3a5a Task #658: Reduce selection drag DOM churn
- 5fa80bf Task #658: Finalize selection drag fix report

본질 정정:
- 선택 rect 시작/끝 위치 영역의 cursor hit bias 영역 추가 — 줄바꿈 경계 영역 영역의 같은 문자 오프셋 영역 영역 이전 줄 끝 vs 다음 줄 시작 영역 구분
- SelectionRenderer 영역의 highlight div 영역 재사용 + 동일 rect 반복 렌더링 skip 영역 (DOM churn 영역 정정)
- 드래그 영역 caret 업데이트 영역 가벼운 처리 영역 경로 추가

본 환경 검증:
- cherry-pick 충돌 0건 (orders/20260507.md 자동 머지)
- cargo test --release ALL PASS (1165 lib + issue_658 2/2)
- TypeScript 빌드 통과
- clippy clean

PR: #664
컨트리뷰터: @postmelee (Taegyu Lee)
후속 분리: #661 (드래그 시작 영역 영역 커서/스크롤 위치 튐 영역) → PR #718 영역
edwardkim added a commit that referenced this pull request May 8, 2026
PR #664 (Task #658): rhwp-studio 드래그 선택 하이라이트 오버플로우 수정
- merge commit: c6bf769 (4 commits 단계별 보존 no-ff merge — 작업지시자 직접 결정)
- 본질 정정: cursor hit bias (cursor_nav.rs +118/-53) + selection-renderer DOM churn 정정
- 본 환경 결정적 검증 1165 lib + issue_658 2/2 + TypeScript clean + clippy clean
- WASM 빌드 4,584,723 bytes
- 작업지시자 시각 판정 ★ 통과 (exam_social.hwp 드래그 선택 페이지 폭 안 정합)

컨트리뷰터: @postmelee (Taegyu Lee) — 다회 사이클 영역 (PR #663 close → #664 재제출)
후속 분리: #661 (드래그 시작 영역 영역 커서/스크롤 위치 튐 영역) → PR #718 영역

closes #658

산출물:
- mydocs/pr/archives/pr_664_review.md
- mydocs/pr/archives/pr_664_report.md
- mydocs/orders/20260508.md 갱신
edwardkim added a commit that referenced this pull request May 9, 2026
본질: rhwp-studio 영역 의 텍스트 드래그 선택 중 커서 / 스크롤 위치 영역
포인터 영역 무관 영역 의 튐 결함 정정.

기존 경로: mousemove 마다 hit-test → cursor focus 이동 → caret 갱신 +
scrollCaretIntoView() 호출 → 선택 focus 의 다른 페이지/문단 순간 이동 시
스크롤 컨테이너 가 caret rect 추적 → 사용자 드래그 위치 ↔ 문서 스크롤
함께 튐.

정정 (드래그 중 caret 갱신 + 스크롤 책임 분리):
1. caret: 선택 상태 표시만 갱신 (scrollCaretIntoView 부재)
2. 스크롤: 포인터 edge 감지만 담당 (RAF 루프 + edge 48 px + step 2~20 px)
3. 편집 영역 외부 영역 도 드래그 지속 (document-level mousemove 등록/해제)
4. 포인터 좌표 저장 + 스크롤 발생 시 동일 좌표로 hit-test 재계산

신규 헬퍼 (input-handler.ts +128 LOC):
- startTextSelectionDrag / stopTextSelectionDrag
- updateTextSelectionDragPointer / updateTextSelectionDragFromPointer
- hitTestFromClientPoint (clientX/Y parameter 분리)
- updateTextSelectionDragAutoScroll / runTextSelectionDragAutoScroll
- updateCaretDuringDrag (드래그 중 caret 갱신)

input-handler-mouse.ts (+6/-9):
- 3 곳 isDragging=true → startTextSelectionDrag(e) (드래그 시작 시 포인터
  좌표 보존 + document mousemove 등록)
- mousemove 분기: hit-test 직접 호출 → updateTextSelectionDragFromPointer
  래퍼 (저장된 포인터 좌표 사용)
- mouseup: isDragging=false → stopTextSelectionDrag

회귀 가드 e2e (drag-selection-autoscroll.test.mjs +86 LOC):
- 70줄 문서 + 첫 줄 → 하단 edge 드래그
- scrollTop 0 → 1529 / hasSelection=true / focus 문단 69 / highlight 70

메인테이너 통합 정정 영역 (충돌 해결):
- input-handler-mouse.ts (mousemove 분기): PR #718 영역 의
  updateTextSelectionDragFromPointer 영역 채택
- input-handler.ts (hitTestFromClientPoint): PR #718 영역 의 clientX/Y
  parameter 영역 + devel 영역 의 PR #693 (Task #685+#689) getPageAtPoint
  (그리드 모드 click 좌표 정합) 영역 통합
- input-handler.ts (updateCaretDuringDrag): PR #718 영역 의
  scrollCaretIntoView 부재 본질 + devel 영역 의 PR #664 (Task #658)
  caret.updateLive (깜박임 타이머 유지 본질) 영역 통합

검증:
- cargo test --release: lib 1173 + 통합 ALL GREEN, failed 0
- cargo clippy --release: 신규 경고 0
- npx tsc --noEmit (rhwp-studio): clean
- npm run build (rhwp-studio): PWA 정상

분리된 후속:
- Issue #717: 표 셀 빈 영역 클릭 hit-test (PR #725 별도 처리 영역)

Closes #661

Co-Authored-By: Taegyu Lee <86018802+postmelee@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 9, 2026
- mydocs/pr/archives/pr_718_review.md (검토 문서, 충돌 본질 분석 + 메인테이너 통합 정정 방향)
- mydocs/pr/archives/pr_718_report.md (처리 보고서, 3건 충돌 해결 명시)
- mydocs/plans/archives/task_m100_661{,_impl}.md
- mydocs/orders/20260509.md: PR #718 행 + 본 사이클 패턴 라인 추가

처리 결과:
- 옵션 A — 5 commits 단계별 보존 cherry-pick + 메인테이너 통합 정정 + no-ff merge (95eea5f)
- 작업지시자 시각 판정 ★ 통과 (웹 에디터, 블럭 드래그 선택 기능)
- 충돌 3건 메인테이너 통합 정정 (PR #693 getPageAtPoint + PR #664 updateLive 영역 + PR #718 본질 영역 양립)
- WASM 빌드 4,607,734 bytes
- Issue #661 close 자동 정합

분리된 후속: Issue #717 OPEN (PR #725 별도 처리 영역)

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

Copy link
Copy Markdown
Owner

@postmelee PR 머지 완료되었습니다 (devel 95eea5f9, --no-ff merge).

처리 결과

충돌 해결 안내 (메인테이너 통합 정정)

본 PR base = de1c2d00 (5/8 시점) 영역 ↔ devel HEAD = 93d6c6a7 (PR #693/#664 머지 후) 영역의 충돌 3건:

  1. input-handler-mouse.ts:1107 (mousemove 분기): PR Task #661: 드래그 선택 중 커서/스크롤 튐 정정 (closes #661) #718updateTextSelectionDragFromPointer 영역 채택 (포인터 좌표 기반 hit-test, 자동 스크롤 영역과 동기)

  2. input-handler.ts:980 (hitTestFromClientPoint): PR Task #661: 드래그 선택 중 커서/스크롤 튐 정정 (closes #661) #718clientX/Y parameter 영역 + devel 의 getPageAtPoint (PR fix: 그리드 모드 click 좌표 단일 컬럼 가정 + getPageAtY X 무시 일괄 정정 (closes #685, #689) #693 그리드 모드 click 좌표 정합) 영역 통합

    const contentX = clientX - contentRect.left;
    const contentY = clientY - contentRect.top;
    const pageIdx = this.virtualScroll.getPageAtPoint(contentX, contentY);  // PR #693 정합
  3. input-handler.ts:1648 (updateCaretDuringDrag 함수 본문): PR Task #661: 드래그 선택 중 커서/스크롤 튐 정정 (closes #661) #718scrollCaretIntoView 부재 본질 + devel 의 caret.updateLive (PR rhwp-studio: 드래그 선택 하이라이트 오버플로우 수정 #664 깜박임 타이머 유지 본질) 영역 통합

    this.caret.updateLive(rect, zoom);   // devel: PR #664 본질 보존
    // [Task #661] 드래그 중 스크롤은 caret rect 가 아니라 포인터 edge 기준 경로에서만 처리한다.

6번째 commit "Align drag caret update with devel" 는 메인테이너 통합 정정 영역에 이미 포함되어 빈 commit 영역으로 skip 처리.

검증

  • cargo test --release: lib 1173 + 통합 ALL GREEN, failed 0
  • cargo clippy --release: 신규 경고 0
  • npx tsc --noEmit (rhwp-studio): clean
  • npm run build (rhwp-studio): PWA 정상
  • WASM 빌드 4,607,734 bytes

메모리 룰 정합

잔존 후속: Issue #717 OPEN (PR #725 별도 처리 영역)

처리 보고서: mydocs/pr/archives/pr_718_report.md. 감사합니다.

@edwardkim edwardkim closed this May 9, 2026
edwardkim pushed a commit that referenced this pull request May 10, 2026
본질 진단 (소스 정밀 추적)
- scrollTop 변경 영역: viewport-manager (API), canvas-view (초기 로드),
  input-handler (drag autoscroll PR #718, scrollCaretIntoView)
- scrollCaretIntoView 호출 영역: updateCaret() 안 단일 호출 (line 1609)
- updateCaret() 호출 영역: 키보드/마우스/programmatic 30+ 곳

가설 도출
- 가설 A (단순 scrollbar): native scrollbar 영역 의 mousedown/mouseup 이
  document level 발동 안 됨 → onMouseUp chain 미발동 → 본 결함 설명 부재
- 가설 B (drag-during-scroll): 사용자 가 텍스트 클릭 (isDragging=true) +
  mouseup 안 한 채 scrollbar 까지 drag → scrollbar 위 mouseup → onMouseUp
  발동 → updateCaret → scrollCaretIntoView → caret 원본 위치 scroll back
- 가설 C: 다른 trigger 영역 (focus, scrollend 등) — Stage 2 의 정정 +
  작업지시자 시각 검증 으로 confirm

산출물
- mydocs/plans/task_m100_779.md (수행계획서)
- mydocs/plans/task_m100_779_impl.md (구현계획서)
- mydocs/working/task_m100_779_stage1.md (본 보고서)

후속 (Stage 2)
- updateCaret(skipScroll: boolean = false) 시그니처 확장
- onMouseUp 의 updateCaret(true) 변경 (가설 A/B 영역)
- 작업지시자 browser 시각 검증 으로 confirm
edwardkim pushed a commit that referenced this pull request May 10, 2026
본질
- updateCaret() 영역 의 scrollCaretIntoView() 호출 영역 이 cursor 변경 trigger 없는
  mouseup (drag-during-scroll 패턴) 영역 의 caret 원본 위치 자동 복귀 결함 발동.
- onMouseUp 영역 의 updateCaret() 호출 영역 의 본질: selection 종료 영역 의 visual
  cleanup 영역 (caret 위치 자체 변경 부재). scroll 호출 영역 의 의도 부재.

정정
- updateCaret(skipScroll: boolean = false) 시그니처 확장. 기본값 false 으로 기존 30+ 곳
  호출 영역 무영향 (opt-in skip) 영역 좁힘.
- skipScroll true 시 scrollCaretIntoView 호출 skip.
- onMouseUp 영역 (input-handler-mouse.ts:1390) 의 updateCaret() → updateCaret(true) 변경.

영역 좁힘 (회귀 부재 가드)
- 키보드 영역 (input-handler-keyboard.ts 20+ 곳): 기본값 false → 기존 동작
- programmatic cursor move (moveCursorTo, enterInlineEditing 등): 기본값 false → 기존
- onMouseDown 영역 의 cursor placement (input-handler-mouse.ts 8+ 곳): 기본값 false → 기존
- 드래그 selection autoscroll (PR #718, updateTextSelectionDragAutoScroll): 별도 path → 보존

검증
- tsc --noEmit: clean
- npm run build: 성공 (4.6 MB WASM, 707 KB index.js)
- cargo test --lib --release: 1217 passed (rust lib 무영향)

후속 (Stage 3)
- 작업지시자 시각 판정 (수동 시나리오 1~5)
- e2e 회귀 가드 (선택) — scroll-page-preserve.test.mjs
edwardkim pushed a commit that referenced this pull request May 10, 2026
Stage 3 검증
- tsc --noEmit: clean
- npm run build: 성공 (WASM 4.6 MB, index.js 707 KB)
- cargo test --lib --release: 1217 passed
- cargo clippy --release --lib: 신규 경고 0
- 작업지시자 시각 판정: ★ 통과 ("해결 완료")
  - 시나리오 1: 본 결함 해소 (scrollbar drag → release → 위치 보존)
  - 시나리오 2: cursor click 정상 (회귀 부재)
  - 시나리오 3: 키보드 navigation 정상 (회귀 부재)
  - 시나리오 4: 드래그 selection autoscroll PR #718 정상 (회귀 부재)
  - 시나리오 5: wheel scroll 정상 (회귀 부재)

Stage 4 산출물
- mydocs/working/task_m100_779_stage3.md
- mydocs/report/task_m100_779_report.md (최종)
- mydocs/orders/20260510.md (메인테이너 본인 영역 신규 등록)

권위 사례 강화
- feedback_hancom_compat_specific_over_general: opt-in skipScroll 영역 좁힘
- feedback_pr_supersede_chain: PR #718 (Task #661) 후속 영역
- feedback_visual_judgment_authority: 작업지시자 시각 판정 confirm

후속
- e2e 회귀 가드 (별도 task) — scroll-page-preserve.test.mjs
edwardkim added a commit that referenced this pull request May 10, 2026
…k-scroll 정정

@jangster77 영역 rhwp-studio editor 영역 drag-during-scroll 결함 정정.

본질: 사용자 텍스트 클릭 (caret p.1) → 마우스 보유 상태 영역 scrollbar drag → release 시 mouseup listener 가 scrollbar release catch → updateCaret → scrollCaretIntoView → caret 원본 위치 자동 scroll back.

정정 (input-handler.ts +11/-3 + input-handler-mouse.ts +7/-1, 본질 +18/-4):
- updateCaret(skipScroll: boolean = false) 시그니처 확장
- onMouseUp 영역 updateCaret(true) — opt-in skip 영역 좁힘
- 30+ 기존 호출 무영향 (키보드/programmatic/onMouseDown/PR #718 autoscroll 보존)

거버넌스 +617 (Stage 1 진단 + Stage 2 GREEN + Stage 3+4 검증/보고서 + 거버넌스 문서).

검증:
- tsc --noEmit ✅
- cargo test --release ALL GREEN
- cargo clippy --release -- -D warnings 통과
- 광범위 sweep 170/170 same
- WASM 4.68 MB 재빌드
- 작업지시자 웹 에디터 시각 판정 ✅ 통과 (5 시나리오 — 본 결함 해소 + cursor click + 키보드 + 드래그 selection autoscroll + wheel scroll 회귀 부재)

closes #779
edwardkim added a commit that referenced this pull request May 11, 2026
표 셀 내부 텍스트 드래그 선택 시 선택 하이라이트 미렌더링 결함 정정 (Issue #669).

본질: 드래그 중 hitTest 가 셀 내부 빈 영역에서 본문 레벨 위치 반환 → anchor(셀) ↔ focus(본문) 혼합 → updateSelection() 영역 영역 selectionRenderer.clear() 호출 → 선택 미렌더링.

정정 (input-handler.ts:1014 updateTextSelectionDragFromPointer 래퍼 안 +14):
- anchor 셀 내부일 때 hit 가 같은 셀 컨텍스트 (parentParaIndex/controlIndex/cellIndex 모두 일치) 가 아니면 cursor.moveTo 건너뜀
- 셀 내 선택 유지

본 환경 충돌 수동 해결: HEAD (devel) 영역 영역 PR #718 (Task #661) updateTextSelectionDragFromPointer 래퍼 사용 영역 영역 incoming 의 onMouseMove 영역 영역 직접 hit + moveTo 영역 영역 비대칭. 본 환경 영역 영역 셀 가드 영역 영역 input-handler.ts 의 래퍼 안 적용 (PR #718 정합성 보존).

검증:
- tsc --noEmit ✅
- cargo test --release ALL GREEN
- 광범위 sweep 170/170 same
- WASM 4.68 MB 재빌드
- 작업지시자 웹 에디터 시각 검증 ✅ 통과

closes #669
edwardkim added a commit that referenced this pull request May 11, 2026
PR #795 (closes #669) — 표 셀 내부 드래그 선택 시 셀 컨텍스트 이탈 방지.
충돌 수동 해결: 셀 가드 영역 input-handler.ts 의 updateTextSelectionDragFromPointer 래퍼 안 적용 (PR #718 정합성 보존).

Merge commit: 58176ed
Cherry-pick: 2efe20c
@postmelee postmelee deleted the pr-task661 branch June 7, 2026 19:34
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.

2 participants