Skip to content

feat: rhwpDev 디버깅 툴킷 — showAllIds / search / findNearest (closes #449)#602

Closed
oksure wants to merge 2 commits intoedwardkim:develfrom
oksure:contrib/rhwpdev-toolkit
Closed

feat: rhwpDev 디버깅 툴킷 — showAllIds / search / findNearest (closes #449)#602
oksure wants to merge 2 commits intoedwardkim:develfrom
oksure:contrib/rhwpdev-toolkit

Conversation

@oksure
Copy link
Copy Markdown
Contributor

@oksure oksure commented May 5, 2026

목적

서드파티 애플리케이션에서 rhwp 렌더링 뷰어와 연동 시, AI가 제공한 activeId 가 DOM에 없거나 찾지 못하는 매핑 실패 이슈 디버깅 지원.

구현 내용

window.rhwpDev 전역 객체 (모든 모드에서 활성):

메서드 설명
showAllIds(page?) 렌더링된 페이지에 문단 ID (s{섹션}:pi={인덱스}) 오버레이
hideAllIds() 오버레이 제거
search(text) 텍스트 → section/paragraph/charOffset/page 역추적
findNearest(id, page?) 유효하지 않은 ID에 가장 가까운 유효 ID 제안
help() 콘솔 사용법 안내

사용 예시

// 콘솔에서:
rhwpDev.showAllIds()        // 모든 페이지 ID 표시
rhwpDev.showAllIds(2)       // 3페이지만

rhwpDev.search("기구 및 조직")  // → {section:0, paragraph:18, ...}

rhwpDev.findNearest(11, 0)  // ID 11이 없을 때 → 가장 가까운 ID 제안

파일 변경

  • rhwp-studio/src/core/rhwp-dev.ts — 신규 (디버깅 툴킷 모듈)
  • rhwp-studio/src/main.tsinitRhwpDev(wasm) 호출 추가

검증

  • npx tsc --noEmit: 기존 chrome 타입 에러 외 신규 에러 없음
  • WASM getPageTextLayout / searchText API 기반 구현

참고

  • 이슈에서 제안된 3가지 기능 중 showAllIds (시각적 오버레이), search (텍스트 역추적), 스마트 에러 로깅 (findNearest) 를 모두 구현
  • getPageTextLayout 반환 형식에 따라 오버레이 정확도가 달라질 수 있음 — 피드백 후 조정 가능

이 방향이 적절한지 피드백 부탁드립니다.

서드파티 앱 연동 시 activeId 매핑 실패 디버깅을 위한 콘솔 API:
- rhwpDev.showAllIds(page?) — 렌더링 영역에 pi 오버레이 표시
- rhwpDev.hideAllIds() — 오버레이 제거
- rhwpDev.search(text) — 텍스트 → section/para/offset 역추적
- rhwpDev.findNearest(id, page?) — 유효하지 않은 ID에 가장 가까운 ID 제안
- rhwpDev.help() — 사용법 안내

window.rhwpDev 로 전역 노출 (모든 모드).
Copilot AI review requested due to automatic review settings May 5, 2026 03:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a global window.rhwpDev debugging toolkit to the rhwp-studio frontend so third-party integrators can inspect rendered paragraph IDs and troubleshoot activeId ↔ DOM mapping failures.

Changes:

  • Added a new rhwp-studio/src/core/rhwp-dev.ts module with showAllIds, hideAllIds, search, findNearest, and help.
  • Initialized the toolkit from rhwp-studio/src/main.ts using the existing WasmBridge instance.
  • Exposed the toolkit in all modes for integration/debugging workflows.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
rhwp-studio/src/main.ts Wires the new debugging toolkit into app startup.
rhwp-studio/src/core/rhwp-dev.ts Implements the global debugging helpers that consume WASM text/layout APIs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
showAllIds(pageNum?: number): void {
removeOverlay();
const container = createOverlayContainer();
const totalPages = wasm.getPageCount();
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
Comment on lines +65 to +67
const pageEls = document.querySelectorAll(`[data-page="${p}"]`);
const pageEl = pageEls[0] as HTMLElement | undefined;
if (!pageEl) continue;
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
const offsetY = pageRect.top - scrollRect.top + getScrollContainer()!.scrollTop;

for (const run of data.runs) {
const label = `s${run.section ?? '?'}:pi=${run.para ?? '?'}`;
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
Comment on lines +91 to +95
if (!result) {
console.warn(`[rhwpDev] search("${text}"): not found`);
return null;
}
const pos = result as any;
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
Comment on lines +96 to +104
const out: SearchResult = {
section: pos.section ?? 0,
paragraph: pos.paragraph ?? 0,
charOffset: pos.charOffset ?? 0,
page: pos.page ?? 0,
x: pos.x ?? 0,
y: pos.y ?? 0,
};
console.log(`[rhwpDev] search("${text}"): s${out.section}:pi=${out.paragraph} offset=${out.charOffset} page=${out.page}`);
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
},

findNearest(targetId: number, pageNum?: number): { id: number; distance: number; text: string } | null {
const totalPages = wasm.getPageCount();
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated

let nearest: { id: number; distance: number; text: string } | null = null;
for (const run of data.runs) {
const id = run.para as number;
Comment thread rhwp-studio/src/core/rhwp-dev.ts Outdated
Comment on lines +68 to +72
const pageRect = pageEl.getBoundingClientRect();
const scrollRect = getScrollContainer()!.getBoundingClientRect();
const offsetX = pageRect.left - scrollRect.left + getScrollContainer()!.scrollLeft;
const offsetY = pageRect.top - scrollRect.top + getScrollContainer()!.scrollTop;

…ayout)

- wasm.getPageCount() → wasm.pageCount (getter)
- searchText 반환: { found: boolean, sec, para, charOffset, length }
- getPageTextLayout 필드: secIdx/paraIdx/charStart (not section/para)
- showAllIds: DOM 오버레이 대신 console.table (canvas 렌더러에 data-page 속성 부재)
- findNearest: paraIdx 필드 사용
- 줌 스케일링 이슈 회피 (DOM 오버레이 제거로 해소)
@oksure
Copy link
Copy Markdown
Contributor Author

oksure commented May 5, 2026

Addressed all 8 review comments in 07139be:

  • wasm.getPageCount()wasm.pageCount (getter)
  • searchText return check: result.found instead of null check
  • getPageTextLayout fields: secIdx/paraIdx/charStart
  • DOM overlay 제거 → console.table 기반 출력 (canvas 렌더러에 data-page 속성이 없어 DOM 오버레이 불가, 줌 스케일링 이슈도 회피)
  • findNearest 도 동일하게 paraIdx 필드 사용으로 수정

Canvas 위 시각적 오버레이가 필요하면 별도 overlay canvas layer를 CanvasView에 추가하는 방향으로 후속 구현 가능합니다.

edwardkim added a commit that referenced this pull request May 5, 2026
v0.7.9 후속 patch 사이클 (5/4 ~ 5/6).

## 신규 기능

- **CLI 바이너리 릴리즈** (Issue #608/#612, @almet 의 요청)
  - 4 플랫폼 GitHub Release 자산 첨부 (Linux x86_64 / macOS x86_64+aarch64 /
    Windows x86_64) + SHA-256 체크섬
- **PNG raster backend** (PR #599, @seo-rii) — render P4 단계
  - native Skia 기반 PageLayerTree → PNG export, native-skia feature gate
  - **AI 파이프라인 + VLM 연동 도입** (메인테이너 후속 정정):
    - --vlm-target claude (1568 longest edge / 1.15 MP, Claude Vision 정합)
    - --scale / --max-dimension (자동 scale 계산)
    - export-png CLI 명령 + 매뉴얼 (한글 + 영문 dual)
    - 한글 폰트 fallback chain + char 단위 fallback (공백 두부 정정) +
      --font-path 동적 로딩

## 외부 PR cherry-pick (13 PR / 7 컨트리뷰터)

- @planet6897 / Jaeook Ryu (협업): PR #587/#589/#561/#564/#570/#575/
  #580/#584/#592/#593/#567
- @oksure (Hyunwoo Park): PR #600 (closes #513)
- @seo-rii: PR #599 (refs #536)
- @cskwork / @johndoekim / @nameofSEOKWONHONG / @jangster77 — 사이클 누적

## 메인테이너 정정

Skia 폰트 영역 5개 정정 (한글 fallback / font-path / char-fallback /
VLM 옵션 / export-png CLI).

## 인프라

- CI 빌드 안정성 (Cargo.toml [[example]] required-features)
- 광범위 페이지네이션 회귀 sweep 도구 (164 fixture / 1,614 페이지 자동)

## 후속 이슈

- #613 (VLM 프리셋 확장)
- #614 (DPI 메타데이터)
- #615 (pua_oldhangul.rs U+F53A 한컴 정합)
- #598 (rhwp-studio 각주 삭제, 외부 컨트리뷰터 공개)

## 잔여 PR (v0.7.11 후속 patch)

PR #601, #602 (@oksure) / PR #607 (@dicebattle) / PR #609 (@jangster77,
Task #604) / PR #611 (@kihyunnn).

상세: CHANGELOG.md (한글) / CHANGELOG_EN.md (영문).
edwardkim pushed a commit that referenced this pull request May 7, 2026
@edwardkim
Copy link
Copy Markdown
Owner

@oksure 님 7번째 사이클 PR 정합 — cherry-pick 머지 완료 (devel 9e97072).

처리 결과

  • 2 commits 보존 cherry-pick (398385b + 284abc7, author Hyunwoo Park 보존)
  • 결정적 검증 통과:
    • cherry-pick 충돌 0
    • cargo test --lib 1141 passed (회귀 0, rhwp-studio 영역 무관)
    • rhwp-studio 신규 TypeScript 에러 0
    • WASM 재빌드 (Docker, v0.7.10) 후 dev 서버 재시작
  • 메인테이너 시각 판정 ★ 통과 (web 환경 콘솔):
    • rhwpDev.search("사업계획"){found: true, sec: 0, para: 0, charOffset: 4, len: 4} 정상
    • rhwpDev.showAllIds(0)console.table paragraph ID 목록 정상 표시
    • [rhwpDev] Debugging toolkit loaded 로딩 메시지 정상

Copilot 자체 검토 응답 정합 (100%)

마지막 commit 284abc7 "address review" 에서 Copilot 7 코멘트 모두 반영:

  • wasm.getPageCount()wasm.pageCount (getter)
  • searchText 반환 형식 ({ found, sec, para, charOffset, length })
  • getPageTextLayout 필드 (secIdx / paraIdx / charStart)
  • ✅ DOM 오버레이 → console.table (canvas 렌더러 data-page 부재 + 줌 스케일링 회피)
  • findNearest: paraIdx 필드 사용

Issue #449 본질 영역 커버리지 (~85%)

요청 처리
showAllIds() 시각적 ID 오버레이 ⚠️ console.table 응답 (canvas 렌더러 영역 결함으로 후속 권유)
스마트 에러 로깅 (가장 가까운 ID 추천) findNearest(targetId, pageNum?)
search(text) 헬퍼 ✅ section/paragraph/charOffset/length 정확 매핑

후속 권유

정식 시각 오버레이 구현 — canvas 위 overlay div 또는 Canvas API 직접 그리기. 본 영역 후속 PR 환영합니다.

처리 보고서: mydocs/pr/archives/pr_602_report.md
검토 보고서: mydocs/pr/archives/pr_602_review.md

Issue #449 assignee 정합 (feedback_assign_issue_before_work 정합) — 메인테이너 직접 위임 영역 권위 사례.

활발한 컨트리뷰터의 7번째 PR 흡수 — 감사합니다 🙏

@edwardkim
Copy link
Copy Markdown
Owner

@oksure 님 — 본 PR cherry-pick 머지 후 메인테이너 시각 판정 게이트웨이 검증 영역에서 본질 결함 두 건이 식별되어 안내드립니다. 본 PR 자체는 close 유지하되, Issue #449 를 reopen 하여 후속 PR 영역 분리합니다.

메인테이너 판정 게이트웨이 방식 (참고용)

본 환경 PR 처리는 결정적 검증 + 권위 자료 시각 판정의 2 단계 게이트웨이로 진행합니다:

  1. 결정적 검증: cargo test, clippy, build, 광범위 sweep (회귀 0 입증)
  2. 권위 자료 시각 판정: 메인테이너 직접 등록한 권위 샘플로 시각/콘솔 출력 정합성 검증

본 PR 의 권위 게이트웨이: samples/aift.hwp (Issue #652 권위 자료, M100 v1.0.0 영역) — 본 환경 web 에디터 + 콘솔 검증.

본질 결함 두 건 (게이트웨이 검증에서 식별)

결함 #1: paragraph 식별 부정확 (본문 vs 표/글상자 내부)

검증:
```
samples/aift.hwp paragraph 0 (본 환경 `./target/release/rhwp dump samples/aift.hwp -s 0 -p 0` 결과):

  • 본문: " * 사업계획서 제출 시 상기 문구 삭제" (cc=56, text_len=23)
  • controls=4: [구역나누기, 단정의, 표 1행×1열, 쪽번호]
    └─ 표 셀[0] paragraph 0 첫 텍스트: "※ 동 사업(AI 응용제품 신속 상용화 지원사업)은 복..."
    ```

현재 동작:
```javascript
rhwpDev.search('사업계획')
// → { found: true, sec: 0, para: 0, charOffset: 4, len: 4 } ← para=0 본문 가리킴 (정합)

rhwpDev.showAllIds(0)
// → console.table:
// page=0, secIdx=0, paraIdx=0, charStart=0, x=82.4, y=86.2, text='※ ' ← '※ ' 은 표 셀 내부 paragraph 의 첫 텍스트
```

결함 본질: `getPageTextLayout` 의 `runs` 배열은 본문 + 표/글상자 내부 paragraph 를 모두 평탄화 하여 반환. `secIdx`/`paraIdx` 키만 사용 시 본문 paragraph 0 과 셀 내부 paragraph 0 가 같은 키 (`0:0`) 로 충돌 → `showAllIds` 가 본문 paragraph 첫 텍스트 (" * 사업계획서...") 를 표시하지 않고 셀 내부 첫 paragraph 의 "※ " 만 표시.

AI activeId 매핑 디버깅 본질에서 부적합: AI 가 `activeId=11` 등 본문 paragraph 를 가리킬 때 셀 내부 paragraph 와 구분 안 되면 매핑 실패 진단 자체가 오도 (Issue #449 의 "표/글상자 내부 등 DOM 깊은 곳" 영역과 정확히 충돌).

개선 영역 제안:

  • `getPageTextLayout` 의 `runs` 에 `cellContext` 또는 `isCellInner: boolean` 필드 추가 (본 환경 추가 필요)
  • `showAllIds` 에서 본문 paragraph + 셀 내부 paragraph 를 구분하여 표시 (예: `secIdx:paraIdx` vs `secIdx:paraIdx[ctrl_idx:cell_idx:cell_para]`)
  • 또는 본문 paragraph 만 우선 표시 + 셀 내부는 별도 옵션 (`showAllIds(page, { includeCellInner: true })`)

결함 #2: 다중 매치 미처리 (단일 매치만 반환)

현재 동작:
```typescript
search(text) {
const result = wasm.searchText(text, 0, 0, 0, true, false); // 첫 매치만
return result;
}
```

결함 본질: `wasm.searchText` 는 첫 매치만 반환. "사업계획" 이 본문 paragraph 0 ("사업계획서") + 표 내부 다수 위치에 등장 시 첫 위치만 표시.

AI activeId 매핑 디버깅 본질에서 부적합: 키워드 일치 후보가 여럿일 때 메인테이너/외부 개발자가 모든 후보를 보고 정확한 ID 식별 (또는 "AI 가 잘못된 후보를 골랐는지" 판정) 필요 (Issue #449 의 "AI 환각/매핑 오류 시 Fallback 용도" 영역과 직접 연관).

개선 영역 제안:

  • `search` 가 모든 매치 위치 array 반환 (`searchText` 를 loop 호출, fromSec/fromPara/fromChar 갱신하며 wrapped 시 종료)
  • 또는 본 환경 wasm-bridge 에 `searchAllText(query, caseSensitive)` API 추가 (`document_core/queries/search_query.rs:73` 의 `search_all` 노출)
  • 반환 형식: `Array<{ sec, para, charOffset, length, cellContext? }>`

본 환경 cherry-pick 결과 (참고)

본 PR 의 회귀 0 영역 + 정합 영역 (search 단일 매치 + findNearest distance 추천) 은 cherry-pick 머지 유지. 본문 vs 셀 내부 paragraph 식별 + 다중 매치 영역은 위 두 결함 영역으로 후속 PR 영역 분리.

후속 PR 권유

위 두 결함 영역 정정 + 다음 본 PR 의 자체 게이트웨이 검증 권유 (메인테이너 판정 게이트웨이 방식 정합):

  1. `samples/aift.hwp` 로드
  2. `rhwpDev.search('사업계획')` 결과의 sec/para 가 본문 paragraph 0 (" * 사업계획서 제출 시...") 인지 확인
  3. `rhwpDev.showAllIds(0)` 결과가 본문 paragraph + 셀 내부 paragraph 를 명확히 구분 하여 표시하는지 확인
  4. 다중 매치 케이스 (예: "사업" 키워드) 에서 모든 후보 위치 array 반환 여부 확인

본 환경 직접 점검 없이 컨트리뷰터 fork 에서 자체 게이트웨이 검증 후 후속 PR 등록 환영.

`feedback_per_task_pr_branch` 정합 — 후속 PR 은 별도 fork branch (`oksure:contrib/rhwpdev-toolkit-v2` 등) 권장.

게이트웨이 영역 식별 가치 본 PR 의 흡수 영역 — 감사합니다 🙏

edwardkim pushed a commit that referenced this pull request May 7, 2026
- mydocs/pr/archives/pr_602_review.md (검토 보고서)
- mydocs/pr/archives/pr_602_report.md (처리 보고서)
- mydocs/orders/20260507.md PR #602 행 추가 (다섯 번째 처리 PR)
- rhwp-studio/package-lock.json (본 환경 npm install 결과 정합)

PR 처리 본질:
- 활발한 컨트리뷰터 @oksure 7번째 PR — 2 commits 보존 cherry-pick 머지
- Docker WASM 재빌드 v0.7.10 (본 환경 v0.6.0 → v0.7.10, renderPageToCanvasFiltered 부재 결함 동시 해소)
- 메인테이너 권위 자료 (samples/aift.hwp) 게이트웨이 검증에서 본질 결함 두 건 식별:
  - 결함 #1: paragraph 식별 부정확 (본문 vs 표/글상자 내부)
  - 결함 #2: 다중 매치 미처리
- PR #602 close 유지 + Issue #449 reopen + 컨트리뷰터에게 메인테이너 판정 게이트웨이 방식 + 후속 PR 자체 검증 영역 안내

메인테이너 판정 게이트웨이 방식의 권위 사례 — 결정적 검증만으로는 본질 결함 식별 부족, 권위 자료 시각 판정에서 결함 식별 + 컨트리뷰터에게 게이트웨이 방식 + 자체 검증 패턴 안내.
edwardkim added a commit that referenced this pull request May 7, 2026
PR #684 cherry-pick 결과 영역에서 main.ts 영역의 initRhwpDev 영역 import 영역 중복 영역 발생 영역 — 본 환경 영역의 PR #602 영역 결과 (line 28 import + line 38-40 외부 호출 영역) 와 PR #684 영역의 c72e5e5 commit 영역 (initialize() 함수 안 DEV 게이트 영역 안 호출 영역) 영역 동시 영역 존재 영역 → TypeScript TS2300 영역 에러 영역.

PR #684 의 본질 영역 정합 영역으로 line 38-40 영역 제거:
- line 28 영역 import 영역 유지 (본 환경 PR #602 영역 결과)
- line 38-40 영역의 외부 호출 영역 제거 (PR #684 의 의도 영역 — initialize() 안 DEV 게이트 영역 안 호출 영역만 활성 영역)
- line 99-100 영역의 if (import.meta.env.DEV) { initRhwpDev(wasm); } 영역 유지 (PR #684 c72e5e5 영역 결과)

검증:
- npm run build TypeScript 타입 체크 + dist 빌드 통과 (index-21tD9d3W.js 691,386)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

3 participants