Conversation
The dev branch was introduced in @enu3379's #4, but it was behind main. This PR syncs dev with main so future work can branch from dev. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * PDF 뷰어에 **저장** 기능이 추가되고, 단축키로도 저장할 수 있습니다. * 로컬 `file://` PDF 자동 열기 지원이 확대되었으며, 권한 상태에 따라 안내 화면이 표시됩니다. * 파일을 찾을 수 없을 때 전용 오류 화면과 재선택 동작이 추가되었습니다. * **Bug Fixes** * 새 탭/일반 페이지/PDF 이동 처리와 자동 열기 흐름이 더 안정적으로 개선되었습니다. * 로컬 PDF 접근이 꺼진 경우에도 적절한 안내로 전환됩니다. * **Documentation** * 피규어 UX, 로컬 PDF, QA 절차 관련 문서를 업데이트했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: enu3379 <enu3379@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: EUNWOO LEE <enu3379@snu.ac.kr>
## Summary Margin 뷰어가 `PDFDocumentProxy`를 확보하는 즉시 figure extract를 시작하도록 변경했습니다. 기존에는 그림·표 탭을 클릭해야 스캔이 시작됐습니다. 변경 후 대기 시간이 줄어듭니다. ## Changes - URL/로컬 파일 로드 직후 `FiguresTab.setDocument(doc)`를 호출해 figure extraction을 즉시 시작 - 문서 전환 중 이전 스캔 결과가 새 문서 UI에 섞이지 않도록 scan generation guard 추가 - “탭을 열면 스캔” 문구를 “PDF를 열면 자동 스캔” 흐름에 맞게 UI/문서에서 갱신 ## Test plan - [x] `npm.cmd run typecheck` - [x] `npm.cmd test` - [x] `npm.cmd run build` - [x] 수동 QA: PDF 로드 후 목차 탭에 머문 상태에서 그림·표 탭 선택 시 이미 진행 중이거나 완료된 결과 표시 확인 ## Checklist - [x] PR 제목이 Conventional Commits 형식 - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted (에이전트가 작성/보조한 PR이면 체크) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **새 기능** * PDF를 열면 그림·표를 자동으로 스캔해 미리보기와 캡션 정보를 더 빠르게 확인할 수 있습니다. * 문서 로딩 중에도 그림·표 탭의 진행 상태가 더 명확하게 표시됩니다. * **버그 수정** * 문서를 바꿔도 이전 문서의 스캔 결과가 남아 보이던 문제가 개선되었습니다. * 재로딩 시 오래된 스캔 진행 상태나 결과가 잘못 반영되지 않도록 안정성이 향상되었습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary 협업 규칙을 레포에 명문화하는 PR — 사람과 AI 에이전트가 같은 규칙 문서를 읽도록 구성했습니다. **이 PR의 리뷰/머지가 곧 팀 규칙 합의입니다.** @onetwothr1 내용 확인 부탁드려요. 수정 의견은 코멘트로! ## Changes - **CONTRIBUTING.md** — 규칙의 단일 출처: 브랜치 전략(main/dev/feature/fix/chore/hotfix), 2단 머지(작업→dev는 squash, dev→main은 merge commit + 태그), PR 제목 = Conventional Commits, 릴리스·핫픽스·복구 절차 - **AGENTS.md** — 코딩 에이전트(Claude Code, Codex 등)용 운영 요약: 빌드/테스트 명령, 디렉토리 구조, 워크플로우 최소 규칙. **CLAUDE.md**는 이 파일을 import만 함 - **PR 템플릿** — Summary / Changes / Test plan / 체크리스트(컨벤션·테스트·AI-assisted) - **이슈 폼** — 기능 제안(수용 기준 필수 — 이슈만 보고 착수 가능하게), 버그 리포트(재현 절차·환경 필수) - **CODEOWNERS** — 모든 PR에 상대방 리뷰 자동 요청 (알림용, 머지 차단 아님) - **ci.yml** — push 트리거에 `dev` 추가: squash 머지 후 dev에서도 한 번 더 검증 ## Test plan - 문서/템플릿 변경이라 기존 CI(typecheck·test·build, macOS+Windows)로 회귀 확인 - 머지 후: 이슈 생성 시 폼 2종 노출, 다음 PR부터 템플릿·CODEOWNERS 리뷰 요청 동작 확인 ## Checklist - [x] PR 제목이 Conventional Commits 형식 - [x] AI-assisted (Claude Code가 작성) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **문서** * 기여 가이드와 협업 규칙이 정리되어, 브랜치 운영·PR 작성·릴리스 절차를 더 쉽게 확인할 수 있습니다. * 프로젝트 실행, 테스트, 빌드, 디렉터리 구조에 대한 안내가 추가되었습니다. * **새 기능** * 버그 신고 및 기능 제안용 이슈 양식이 추가되어, 필요한 정보를 빠르게 제출할 수 있습니다. * PR 작성용 템플릿이 추가되어 검토와 제출 흐름이 표준화되었습니다. * **기타** * CI가 추가 브랜치에서도 실행되도록 범위가 확장되었습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: enu3379 <enu3379@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Preserve main and dev ancestry without changing the dev file tree.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Collaborator
## Summary Vendors the upstream engine v2.3.0 → **v2.5.0** (byte-identical copy, verified diff 0), covering both pending upstream requests: - **#14 — v2.5.0 `[BREAKING]`**: dedup relaxed from global `num` to **(num, page)** instances. Figures with numbers reused across pages (per-chapter/appendix restarts) are preserved; same-page duplicates still resolved by score. Result ordering is deterministic (page asc → natural num). Matches the `fig{num}-p{page}` ID scheme already used by `toFigureEntries` and the contract test in #18. - **#12 (engine part) — v2.5.0**: `opts.signal` (AbortSignal) cooperative cancellation — checked at entry, after document parse, per page in both passes, and before returning, so an abort never resolves with a stale complete result. - **v2.4.0 `[필드 추가]`**: `result.suspectedMissing: string[]` — integer figure numbers 1..max that were not detected (consumers may ignore). ## Changes - `src/core/fig-extract.js` — vendored v2.5.0 (byte-identical) - `src/core/fig-engine.ts` — `EngineResult.suspectedMissing`, `ExtractOptions.signal`, (num, page) identity + canvas lifetime comments - `docs/fig-extract-integration.md` — 주의사항 synced: (num, page) key, suspectedMissing, cancellation, canvas memory guidance, label-above/no-caption limitations - test fixtures updated for the new required field ## Notes - Upstream repo access for @enu3379 is granted; engine changes were validated there against a 37-paper judged corpus (regression gate: 0 severity regressions, success metric improved). - Remaining #12 items are viewer-side (render LRU, object URL revocation, wiring abort on document swap) — this PR provides the engine hooks. - Unblocks #18 (vendoring blocker satisfied). ## Test plan - [x] `npm run typecheck` - [x] `npm test` — 6 files, 25 tests - [x] `npm run build` - [x] Manual smoke test in the figures tab with a duplicate-number paper (per-page cards render as `fig1-p2` / `fig1-p18` style entries) ## Checklist - [x] PR title follows Conventional Commits - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **새로운 기능** - 그림 식별이 페이지와 번호 조합을 기준으로 개선되어, 여러 페이지의 동일 번호 그림을 정확히 구분합니다. - 누락이 의심되는 그림 번호를 결과에서 확인할 수 있습니다. - 추출 작업을 `AbortSignal`로 취소할 수 있습니다. - 결과 정렬 순서가 페이지와 그림 번호 기준으로 일관되게 적용됩니다. - **문서** - 최신 엔진 저장소와 식별 규칙, 취소 동작, 메모리 관리 및 알려진 탐지 제한사항을 문서에 반영했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Summary Tracks the fix for reused figure numbers across pages and locks the PDFViewer integration contract while the upstream fig-extract change is pending. The current commit only verifies that PDFViewer preserves separate engine results such as `fig1-p2` and `fig1-p18`. This draft must not be merged until a corrected upstream engine version is vendored byte-identically. Closes #14 ## Root cause The vendored fig-extract v2.3.0 deduplicates candidates with `best[f.num]`. A document that restarts numbering by chapter or appendix can therefore drop later figures with the same number. ## Upstream change requested @onetwothr1 Could you either grant `enu3379` access to `onetwothr1/PDFViewer-Figure-Extract`, or publish a new engine version containing the following change? - Deduplicate by page and figure number together - Keep score-based selection for duplicate candidates on the same page - Sort results deterministically by page and natural figure number - Add a regression fixture with the same figure number on multiple pages - Release the updated `fig-extract.js` with a version bump ## Changes currently included - Add an integration contract test proving that PDFViewer creates distinct page-aware IDs for reused figure numbers ## Merge blocker - [x] Upstream source access or updated engine release is available - [x] Updated `fig-extract.js` is vendored byte-identically - [x] Engine version and integration documentation are synchronized - [x] Upstream regression fixture covers reused numbers - [x] Full validation and sample PDF smoke test pass ## Test plan - [x] `npm run typecheck` - [x] `npm test` — 6 files, 26 tests - [x] `npm run build` - [x] `git diff --check` ## Checklist - [x] PR title follows Conventional Commits - [x] AI-assisted Co-authored-by: enu3379 <enu3379@users.noreply.github.com>
## Summary figure-preview-test 엔진 **v2.5.1**을 byte-identical 벤더링. PDFViewer#12 메모리 대응의 **엔진 측** 작업을 land. ## Changes - `src/core/fig-extract.js` — v2.5.1 byte-identical 복사 (엔진 repo에서만 수정, 여기선 복사) - `figure.canvas`(페이지 전체 렌더 보관) 폐지 → `figure.cropCanvas`(그림 영역만 즉시 크롭). figure마다 ~9.4MB scale-2.2 페이지 캔버스를 물던 상주 구조 제거 → 스캔 중 페이지 캔버스 동시 상주 **최대 1장** - 페이지 스캔 상한 기본값(60) 제거 — 미지정 시 전체 페이지 스캔, `opts.maxPages`는 선택적 상한으로 유지 - `opts.signal` abort 시 진행 중 페이지 렌더도 `RenderTask.cancel()`로 즉시 중단 (페이지 경계 대기 제거) - `src/core/fig-engine.ts` — `EngineFigure.canvas`→`cropCanvas`, `maxPages`·`signal` 주석 동기화 - `docs/fig-extract-integration.md` — 크롭 캔버스 수명·취소 가이드 갱신 (호스트가 문서 교체 시 signal abort 필수임을 명시) - `test/*` — mock figure의 `canvas`→`cropCanvas` ## #12 acceptance criteria - [x] Full-page canvases are not retained for every figure-bearing page after previews are produced — `cropCanvas`로 대체, 페이지 캔버스 미보관 - [x] Engine changes are made upstream first, then vendored byte-identically with a version bump — 엔진 repo v2.5.1 후 byte-identical 벤더링 - [x] `npm run typecheck` · `npm test`(25) · `npm run build` 통과 - [ ] Peak retained page render surfaces bounded (LRU) — 엔진 피크는 1장으로 bound. Margin `render-region.ts` LRU 3장은 후속 - [ ] Document replacement cancels the previous extraction — 엔진 메커니즘 완비(signal + in-flight 렌더 취소). **`main.ts`가 `AbortController`로 signal 전달·abort하는 wiring은 후속** (#17 머지 후) - [ ] Preview Blob/Object URLs revoked — Margin 측 (현행 dataURL 캐시 `setDocument` clear로 충족 검토) - [ ] Long figure-heavy PDF memory QA documented — 후속 ## Test plan - [x] `npm run typecheck` - [x] `npm test` (6 files, 25 tests) - [x] `npm run build` - [ ] 런타임 스캔 검증 — 벤더 엔진은 탐지·bbox 로직 무변경(엔진 repo에서 patch로 판정, 물리 diff 0 기대). Margin 통합 런타임은 후속 wiring PR에서 ## Notes 후속 Margin wiring(`main.ts` signal 전달, `render-region.ts` LRU, 메모리 QA)은 **#17(figure-ux) 머지 후** 별도 PR. 이 PR은 엔진 vendoring + 타입/문서 동기화만. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **개선 사항** * 문서 전체 페이지를 기본으로 스캔하도록 `maxPages` 동작을 개선했습니다. * 작업 취소 시 진행 중인 페이지 렌더링이 즉시 중단됩니다. * 그림별 크롭 캔버스를 사용해 미리보기 생성과 메모리 관리 효율을 높였습니다. * 중복 그림을 더 일찍 제거하고, 불필요한 페이지 전체 캔버스 보관을 줄였습니다. * **문서** * 취소 처리와 캔버스 수명 관리 규칙을 최신 동작에 맞게 업데이트했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Summary PDFViewer-Figure-Extract에서 검증된 엔진 **v2.6.0**을 byte-identical하게 벤더링합니다. 새 버전은 기존 Figure 계열 동작과 출력 계약을 유지하면서 Extended Data, Supplementary, PLOS 선행 S 캡션을 감지해 각각 `ED.N` / `S.N`으로 방출합니다. 이슈 없는 정기 엔진 벤더링입니다. ## Changes - `src/core/fig-extract.js`를 upstream v2.6.0으로 byte-identical 교체 - `Extended Data Fig/Figure N` → `ED.N` - `Supplementary Fig/Figure N`, `Suppl. Fig N`, `S<N> Fig/Figure` → `S.N` - 기존 `Figure N`, `Figure A.1`, 로마 숫자 감지와 구분자 방어 유지 - 엔진 계약 태그: `[계약 무변경]` — 타입 및 통합 문서 변경 없음 ## Test plan - [x] upstream/vendored SHA-256 동일 - [x] `npm.cmd run typecheck` - [x] `npm.cmd test` — 6 files, 26 tests - [x] `npm.cmd run build` - [x] Chrome 스모크 — Romera-Paredes PDF에서 ED.1~3 스캔·프리뷰 정상 ## Checklist - [x] PR 제목이 Conventional Commits 형식 - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **새 기능** * 기존 Figure/Fig. 형식 외에 Extended Data(ED.N) 및 Supplementary(S.N) 캡션 번호를 인식합니다. * 다양한 공백 및 구분자 형식의 캡션 처리가 향상되었습니다. * **개선 사항** * 버전이 2.6.0으로 업데이트되었습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary 검증된 figure 추출 엔진 v2.7.0을 byte-identical vendoring합니다. 기존에는 Figure caption이 figure 하단에 있는 경우만 탐지했으나, 이제 상단에 있는 경우도 탐지합니다. 출력 계약과 TypeScript 타입은 변경되지 않습니다. ## Changes - `src/core/fig-extract.js`를 v2.7.0으로 갱신 - 통합 문서에 label-above 지원 및 side-caption 잔여 한계 반영 ## Test plan - `npm run typecheck` - `npm test` — 26 tests passed - `npm run build` - 원본/벤더 파일 SHA-256 일치 확인 - Margin 그림·표 탭 스캔 및 프리뷰 수동 스모크 확인 ## Checklist - [x] PR 제목이 Conventional Commits 형식 (`chore: bump fig-extract to v2.7.0`) - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **개선 사항** * 그림 추출 기능이 v2.7.0으로 업데이트되었습니다. * 그림 위에 짧게 표시되는 `Figure N` 라벨이 있는 레이아웃의 인식이 개선되었습니다. * 일반 그림 캡션뿐 아니라 Extended Data 및 보충 자료 캡션 인식이 강화되었습니다. * 그림 위치와 캡션 간 관계를 더 정확히 분석해 잘못된 그림 선택을 줄였습니다. * **문서** * 라벨이 없는 그림과 좌·우 배치형 캡션의 지원 범위 및 제한 사항을 명확히 안내했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary fig-extract 엔진 v2.8.0을 PDFViewer에 byte-identical하게 반영합니다. 공개 추출 계약은 유지하면서 캡션이 figure의 위·아래·왼쪽·오른쪽에 놓인 레이아웃 탐지 범위를 확장합니다. Closes: N/A — 이 버전 동기화와 정확히 대응하는 PDFViewer 이슈가 없습니다. Upstream: onetwothr1/PDFViewer-Figure-Extract@e5f9865 ## Changes - `src/core/fig-extract.js`를 upstream v2.8.0 엔진으로 교체 - hard-caption stitching과 공통 `up/down/left/right` 후보 점수·선택 로직 반영 - side caption용 column-profile scan 및 보수적 경계/stopper 처리 반영 - `docs/fig-extract-integration.md`에 v2.8 지원 범위와 잔여 제한 업데이트 - exported API, 결과 필드, 좌표계, `(num, page)` 식별자 및 manifest 스키마 변경 없음 ## Impact 상단·좌측·우측 캡션 레이아웃의 figure 탐지 범위가 넓어집니다. captionless figure, 약한 side 정렬, cross-page figure는 계속 보수적으로 제한됩니다. PDFViewer wrapper나 UI 계약 변경은 없습니다. ## Test plan - [x] Vendored engine SHA256 일치: `CDB14E2F4ED075BDE0BBB1B2DD657D9DE5D658CAD242EB2B27280AEBEA659BB0` - [x] `npm run typecheck` - [x] `npm run build` - [x] Margin PDF의 Figures 탭 수동 smoke test: 정상 ## Checklist - [x] PR 제목은 Conventional Commits 형식 - [x] 관련 자동 검증 통과 - [x] AI-assisted
엔진 repo([figure-preview-test](https://github.com/onetwothr1/PDFViewer-Figure-Extract))의 **v2.9.0~v2.9.3**을 벤더링합니다. ## 계약 영향: 없음 네 릴리스 모두 `[계약 무변경]`입니다. 출력 필드·좌표계·`(num, page)` 식별 키·공개 export가 전부 불변이라 **`fig-engine.ts`·`fig-extract.d.ts`는 수정하지 않았습니다.** Margin 쪽 코드 변경도 없습니다. `src/core/fig-extract.js`는 엔진 repo 원본과 byte-identical입니다 (md5 `c4a809a9b98190243001aa85452988e6`). ## 엔진 변경 요약 체인지로그 정본은 엔진 repo의 `fig-extract.js` 헤더입니다. | 버전 | 내용 | |---|---| | v2.9.0 | **서브패널 라벨 stopper 면제** — figure 내부 `(a) …` 서브캡션이 본문으로 오인돼 후보가 전멸하고 figure가 통째로 사라지던 문제 | | v2.9.1 | **soft 캡션 앵커 + 문서 수준 게이트** — 번호 뒤 구분자가 없는 표기(RSC·Springer `Fig. 1 본문…`, Wiley 자간 분리 `F I G U R E 1 본문…`) 지원 | | v2.9.2 | **임베디드 캡션 앵커** — 나란한 figure의 캡션이 8pt 미만 간격으로 한 줄에 병합된 경우 분해 | | v2.9.3 | **줄끝 종결형 가드에서 접두어 길이 제외** — `Extended Data Fig. 1` 미인식 수정 | ## 사용자 체감 지금까지 **감지 0개**로 나오던 논문들이 잡힙니다. 특히 RSC·Springer·Wiley 계열에서 문서 전체가 통째로 미탐지되던 케이스가 해소됐습니다 (예: 8개 figure 논문이 0개 → 8개). ## 엔진 repo 검증 - 게이트: 네 릴리스 전부 **critical 회귀 0 · 신규 0** - gate `sCritical` 0.7781 → **0.8503**, 미탐지 59 → **31건** - 코퍼스 116편 전수 스냅샷 diff에서 각 릴리스마다 변경은 표적 논문으로 한정됨(나머지 111~115편 물리 무변경) - 앵커 13 → 20편 승격 ## 알려진 한계 (문서에 추가) 번호 글리프에 ToUnicode 매핑이 없는 PDF(Wiley 일부)는 원리상 미탐지입니다. 번호가 화면에는 정상으로 보이는데 텍스트 레이어에 문자가 없어서, **사용자에겐 "번호가 멀쩡히 보이는데 안 잡힌다"로 보입니다.** 문의가 오면 수동 크롭 안내가 맞습니다. `docs/fig-extract-integration.md`에 적어뒀습니다. ## 검증 - [x] byte-identical diff 0건 - [x] `npm run typecheck` 통과 - [x] `npm run build` 통과 - [x] 스모크 — 그림·표 탭 스캔·프리뷰 정상 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **개선 사항** * 다양한 캡션 형식과 접두어를 더 안정적으로 인식합니다. * 한 줄에 결합된 여러 캡션과 서브패널 캡션을 올바르게 분리해 추출합니다. * 번호 구분자가 없는 캡션도 문서 구조에 따라 인식할 수 있습니다. * 캡션 주변 콘텐츠가 잘못 제외되거나 중단되는 문제를 줄였습니다. * **문서** * 캡션 및 번호 인식의 제한 사항과 동작 조건을 최신화했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Vendor `fig-extract` **v2.9.3 → v2.10.2** into `src/core/fig-extract.js`. - **byte-identical** with the engine repo (md5 `8e984591…`); algorithm changes live only in figure-preview-test. - All three releases are `[계약 무변경]` — no `fig-engine.ts` / `fig-extract.d.ts` changes required. Output fields, coordinate system, and `(num,page)` identity unchanged; bboxes are just tighter. - **v2.10.0** — table-caption boundary (recognized as a crop boundary only; no table emission). - **v2.10.1** — side-by-side column separation (same-baseline siblings). - **v2.10.2** — offset side-by-side (baseline-mismatched siblings) via detected-figure region overlap. - `docs/fig-extract-integration.md` §주의사항: added a region-boundary refinement note. - ✅ `typecheck` + `build` pass · ✅ [사람] Margin smoke (figure/table tab scan + preview) OK. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **개선 사항** * 엔진이 v2.10.2로 업데이트되었습니다. * 표 캡션을 보다 정확히 인식하여 표 내용이 figure 영역에 포함되는 문제를 개선했습니다. * 나란히 배치된 figure가 서로의 영역을 잘못 포함하던 문제를 수정해 각 figure를 캡션 기준으로 분리합니다. * figure 및 table의 경계가 더 정확하고 타이트하게 산출됩니다. * 기존 출력 필드, 좌표계 및 식별자 형식은 변경되지 않습니다. * **문서** * 엔진 업데이트 및 경계 산정 개선 사항을 통합 문서에 반영했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
엔진 `fig-extract.js`를 v2.10.2 → **v2.11.0**으로 벤더링. **[계약 무변경]**. ## 엔진 변경 (캡션 문법 확장 M1·M2) 보충/부록 캡션의 inline 표기를 새로 탐지해 점형 canonical(`S.N`·`A.N`)로 방출: - 문자접두 inline 번호 `Fig. S1.`·`Figure S1:`·`Figure A1.` (번호 클래스 `[A-DS]\.?\d+` 공유) - `Supplemental`/`Supporting`/`Supp.` 접두 추가 - M2 괄호 한정구 `FIG. 3 (color online).`·`Figure 1 (저자명).` (hard 구분자 + 대문자/color online 가드) - 문자접두 대문자 case-sensitive — 복수형 `Figures 2.0` 오탐 차단, OCR 로마 유령 부수 해소 - soft 문서 게이트 hard 카운트에서 보충/부록 계열 제외 엔진 게이트: gate sCritical 0.801 → 0.9106 (+10.96pp), critical 118 → 53 (해소 49·신규 1·회귀 0), 앵커 무악화. ## 벤더링 검증 - `src/core/fig-extract.js`: **byte-identical** (md5 `05753502…`, diff 0) - `docs/fig-extract-integration.md`: §주의사항에 v2.11.0 항목 추가 - `fig-engine.ts`·`fig-extract.d.ts` **무변경** — 계약 무변경(출력 필드·좌표계·(num,page)·manifest 스키마 불변, num에 점형이 더 자주 등장할 뿐) - ✅ typecheck 통과 · ✅ build 통과 · ✅ [사람] 스모크 정상 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **새 기능** * 보충·부록 캡션 표기를 확대 지원합니다. * `S.1`, `A.1` 형식으로 캡션 번호를 일관되게 표시합니다. * `color online` 등 조건부 괄호 표기가 포함된 캡션을 인식합니다. * 표 캡션에도 동일한 번호 인식 규칙을 적용합니다. * **문서** * 지원되는 캡션 문법과 번호 출력 형식을 안내합니다. * 중첩 라벨이 있는 문서에서 동일한 figure가 중복 표시될 수 있다는 제한 사항을 추가했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
엔진 `fig-extract.js`를 v2.11.0 → **v2.12.0**으로 벤더링. **[계약 무변경]**. **Draft** — [사람] Margin 스모크 대기. ## 엔진 변경 (hugePenalty 전면 figure 오발 완화) Nature Extended Data류 **전면(full-page) figure**(up 영역이 페이지의 65~78%)가 과대 패널티(`hugePenalty -8`)에 눌려 healthy(≥8) 문턱 아래로 떨어지며 작은 side 크롭에 지던 clip을 해소: - `up && bodyStops=0 && (farClosed ‖ !raster)`일 때 hugePenalty 면제 - `!farClosed && raster`(칼럼폭 over-grab)는 벌점 유지 → 회귀 방지 엔진 게이트: gate sCritical 0.9013 → 0.9133, critical 66 → 58 (해소 8·신규 0·회귀 0), 앵커 무악화. ## 벤더링 검증 - `src/core/fig-extract.js`: **byte-identical** (md5 `32d13b96…`, diff 0) - `docs/fig-extract-integration.md`: §주의사항에 v2.12.0 항목 추가 - `fig-engine.ts`·`fig-extract.d.ts` **무변경** — 계약 무변경(출력 필드·좌표계·(num,page)·manifest 스키마 불변, 전면 figure bbox가 truth에 더 가까워질 뿐) - ✅ typecheck 통과 · ✅ build 통과 ## 남은 것 - [ ] [사람] Margin 스모크 테스트 → 정상 확인 후 Ready for review 전환·머지 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **개선 사항** * 전면 figure의 크롭 영역 정확도가 향상되었습니다. * 특정 전면 figure가 잘못 감지되는 문제가 완화되었습니다. * 엔진 버전이 2.12.0으로 업데이트되었습니다. * **문서** * 변경된 크롭 동작과 호환성 정보를 통합 문서에 반영했습니다. * 기존 출력 필드, 좌표계, 페이지 정보 및 매니페스트 형식은 변경되지 않았습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
엔진 `fig-extract` v2.12.0 → **v2.13.1** 벤더링 (byte-identical 복사, md5 `eecff2eaac53486b7f6902a545b2f249`). ## 엔진 변경 (계약 무변경) - **v2.13.0** — hugePenalty 전면 figure 오발 근본 해결. v2.12.0 프록시 `(farClosed‖!raster)`의 사각(신규 Nature 2026 전폭 clip은 `!farClosed && raster`라 미면제)을 **수평 잉크 커버리지**(up 영역 잉크 90% 질량 최소폭 ÷ 영역 폭 ≥ 0.54, 프록시에 OR 보강)로 해소. clip 11건 해소. - **v2.13.1** — CodeRabbit 지적 수용. coverage가 후보 자기폭 기준이라 좁고 긴 후보(heightRatio>0.82 단독 hugeCond)의 면제 사각을 `widthRatio ≥ 0.60` 폭 바닥으로 차단. 전수 diff v2.13.0↔v2.13.1 = 0(순수 하드닝). ## 엔진 repo 게이트 (통과) - gate sCritical **0.9077 → 0.9222**, criticalPapers 36→32, criticalFlow resolved 11·regressed 0, 앵커 8건 무변화, 전수 diff REGRESS 0. ## 소비자 영향 `[계약 무변경]` — extract() 시그니처·출력 필드 불변. `fig-engine.ts`·`fig-extract.d.ts`·integration.md 갱신 불요. - typecheck ✅ (exit 0) · build ✅ (exit 0) - [사람] Margin 스모크 **통과** (그림·표 탭 Nature ED 전폭 figure 정상) Ready for merge.
soft 캡션 문서 게이트 강건화 — 혼합-관습 문서서 hard 앵커 1개가 같은 문서 soft 캡션을 전량 몰살하던 문제 해결. ## 엔진 변경 (figure-preview-test v2.13.1 → v2.14.0) - **Part1**: 게이트 hard 카운트를 sep-증거 `hardBody`로 교정 = (비-family sep 앵커) + (반복 동형 bare, 같은 form ≥2). 고립 bare 맨-라벨(캡션 라벨이 컬럼 wrap된 잔재)은 제외. - **Part2**: `hardBody` 3-체제 라우터 — 0 전량승격 / 1 애매역(잠정승격 → up-점수 floor 8.5 검증 → 강등 → detectPage 재실행 fixpoint) / ≥2 전량기각. ## 엔진 repo 게이트 (통과) gate 스코프(3편 promoted 편입, 104편): **G0** pending 0 · **G1** regressions 0 · **G2** resolved 8 / regressed 0 · sCritical 0.9218. 전수 diff v2.13.1 ↔ v2.14.0: **ADDED 15**(전부 critical/not_detected 복구: Hao 8 · s10854 4 · s11269 3) · **REMOVED 0** · **MOVED 0**. 검증: 3 subagent + 4-critic 적대검증 패널 + 적대 코드리뷰(BUG-minor 1 수정, 코퍼스 산출 Δ0 재검증). ## 소비자 영향 `[계약 무변경]` — `extract()` 시그니처·출력 필드·좌표계·(num,page) 키·manifest 스키마 불변. `fig-engine.ts` · `fig-extract.d.ts` · `integration.md` 갱신 불요. - ✅ typecheck (exit 0) - ✅ build (exit 0) - ⏳ [사람] Margin 스모크 대기 (그림·표 탭 샘플 PDF 스캔·프리뷰 확인) byte-identical copy from figure-preview-test `fig-extract.js` (diff 0). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **버그 수정** * 캡션 인식 및 문서 게이트 처리의 정확도를 개선했습니다. * 애매한 캡션 후보를 추가 검증해 오인식 가능성을 줄였습니다. * 최종 추출 결과에서 불필요한 내부 정보가 표시되지 않도록 정리했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
엔진 repo가 v2.19.1에서 `figure.cropCanvas` 필드와 `cropCanvas()` 접근자를 **제거**했다
(`[BREAKING]`). 이 PR은 그 계약 변경을 타입·테스트·문서에 반영한다.
**엔진 벤더링은 하지 않음** — `src/core/fig-extract.js`는 v2.14.0 그대로고 추후 벤더링은 별도
PR.
## 왜 지금 타입만 고치는가
`cropCanvas`를 호출하는 코드가 이 repo에 **하나도 없다**. `tab-figures.ts`는
`cropDataURL`만 쓰고, 애초에 `Pick<FigExtractApi, 'extract' | 'cropDataURL'>`로
API를 좁혀 놨다. 나머지 등장 위치는 전부 타입 선언과 테스트 더미(`{} as HTMLCanvasElement`)였다.
운영 원칙을 문서에 명시했다: **선언 타입은 벤더링 런타임이 실제로 제공하는 것의 부분집합으로 유지한다 — 제거는 지금, 추가는
벤더링과 함께.** 근거는 두 방향의 실패 방식이 다르기 때문이다.
- **좁히기는 컴파일 타임에 요란하게 실패한다** — 누가 그 필드를 쓰고 있었다면 이 PR에서 타입 에러가 난다.
- **넓히기는 런타임에 조용히 실패한다** — 컴파일도 배포도 되고 아무 일도 안 일어난다.
그래서 v2.15.0~v2.19.1이 *추가한* `captionPage`·`onDiagnostic`·`cropImages`는
타입에 넣지 않고 「다음 벤더링 할 일」로 남겼다.
## 변경
- `src/core/fig-engine.ts` — `EngineFigure.cropCanvas` 필드 ·
`FigExtractApi.cropCanvas` 메서드 제거
- **`VENDORED_ENGINE_VERSION` 상수 + 검사 테스트 신설** — 벤더링하면 테스트가 깨져 할 일 목록으로
돌려보낸다. 버전 스큐를 날짜 박힌 문단이 아니라 테스트가 지키게 하려는 것
- `docs/fig-extract-integration.md` — §크롭 이미지 수명 재작성, 버전 스큐 절 신설, 「다음
벤더링 할 일」 5항목, 「벤더링과 무관한 선재 결함」 분리
- 테스트 더미 5곳 정리. `toFigureEntries`의 shape 단언은 `surplusEngineField`로
**보강**했다 — 더미 제거로 rest-spread 리팩터를 못 잡게 되는 걸 막는다
## 알아 둘 것 — 지금 이 확장에도 같은 결함이 있다
엔진 백로그 B7의 원인은 **Chrome이 메모리 압력에서 캔버스 백킹 스토어를 예외 없이 회수**하는 것이다. 회수된 캔버스는
그리기가 전부 무성과로 끝나고 읽으면 전면 투명이다.
벤더링본 v2.14.0은 figure마다 `cropCanvas`를 만들어 **문서 스캔이 끝날 때까지 들고
있고**(`tab-figures.ts`가 `cropDataURL`을 렌더 시점에야 부른다), `FigRenderError`도
없다. 즉 같은 상황에서 **오류도 재시도 버튼도 없이 프리뷰 카드가 백지로 뜬다.** 엔진 전수 배치에서는 한 논문 크롭
23장이 통째로 백지 PNG가 됐고 그게 출하본에까지 들어갔다.
문서에 이 사실과, 벤더링 전까지 유효한 주의 두 가지를 남겼다.
- v2.14.0의 `cropDataURL`은 `f.cropCanvas`를 읽는 **순수 접근자**다. 엔진이 준 figure
객체를 그대로 넘겨야 한다 — 선언된 필드만으로 재구성하거나 `structuredClone`/JSON 왕복을 거치면 타입은
통과하고 런타임에서 죽는다. M3의 storage 저장 작업이 정확히 이 함정을 부른다.
- `tab-figures.ts`가 `signal`을 넘기지 않아 문서 교체 시 이전 스캔이 계속 돈다. 스캔 두 개가 동시에
돌면 크롭 세트가 두 벌 상주해 **이 결함의 발생 확률을 호스트가 스스로 올린다.** 엔진 버전과 무관한 선재 결함이라 벤더링을
기다릴 이유가 없다.
## 검증
`npm run typecheck:win` exit 0 · `vitest run` **27/27** · `npm run
build:win` exit 0 · `dist/` 무변화
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **변경 사항**
- 벤더링 엔진 버전과 타입 정의 간 차이, 크롭 처리 방식 및 관련 제한 사항을 문서화했습니다.
- `cropCanvas` 공개 API를 제거하고 크롭 결과는 데이터 URL 또는 Blob 방식으로 제공합니다.
- 잘못된 렌더링 결과를 감지하면 `FigRenderError`로 처리되는 동작을 명확히 했습니다.
- 캡션·페이지 레이아웃 지원 범위와 향후 보완 항목을 문서에 추가했습니다.
- **테스트**
- 엔진의 추가 필드가 결과 데이터에 노출되지 않는 동작을 검증하도록 테스트를 강화했습니다.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
## Summary `FiguresTab.setDocument()`은 `#scanGeneration`을 올려 이전 스캔의 **결과만** 버렸고, `#scan()`이 엔진에 `opts.signal`을 넘기지 않아 **작업은 끝까지 돌았다.** 문서를 빠르게 갈아타면 스캔이 중첩돼 크롭 세트가 두 벌 상주한다 — 통합 규약([docs/fig-extract-integration.md](docs/fig-extract-integration.md) §취소)이 명시적으로 요구하는 호스트 측 배선이 빠져 있었다. 엔진은 v2.5.0부터 `opts.signal`을 지원하고 v2.5.1부터 진행 중 렌더도 `RenderTask.cancel()`로 끊는다. `ExtractOptions.signal`도 이미 선언돼 있었다. **호스트만 배선하면 되는 문제였다.** Closes #34 ## Changes - `setDocument()`가 이전 `AbortController`를 abort한다. **abort는 여기에만 둔다** — 문서 교체만이 진행 중인 스캔을 무효화하는 사건이고, 재시도(`ensureScanned`)는 종료 상태 `'error'`에서만 진입하므로 그때는 취소할 스캔이 없다. - `ensureScanned()`가 스캔마다 새 컨트롤러를 만들고 `#scan()`이 `signal`을 전달한다. - **취소를 정상 흐름으로 처리한다.** 기존 `catch`는 generation 불일치로 에러 UI가 가려지는 구조였는데, 그에 의존하지 않고 `signal.aborted`를 명시적으로 본다. 안 그러면 문서를 바꿀 때마다 "figure 스캔에 실패했어요"가 번쩍인다. 엔진이 던지는 이름(`AbortError`·`RenderingCancelledException`…)에 기대지 않는다. - **성공/실패 경로를 대칭으로** 만들었다. generation을 올리지 않는 abort 지점(`dispose`·패널 닫기 등)이 나중에 생겨도, 취소된 스캔의 결과가 후임 문서 탭에 그려지지 않는다. - 조기 반환을 `try` 안으로 옮겨 `#scanAbort` 정리가 항상 `finally`를 거치게 했다 (현재 도달 불가하지만 새 불변식을 가드 하나에만 의존시키지 않는다). ## Test plan - `npm run typecheck` 통과 · `vitest run` **29/29** (기존 27 + 신규 2) · `npm run build` 통과 - **신규 테스트가 동어반복이 아님을 확인**: `src/viewer/panel/tab-figures.ts`만 stash하고 돌려 두 테스트가 정확히 실패하는 것을 봤다. - 첫 테스트는 엔진과 같은 형태(`new DOMException('figure 추출이 취소됨', 'AbortError')`)로 거절하며, ① `signal.aborted`가 실제로 true가 되는지 ② 취소가 에러 UI·`console.error`를 남기지 않는지 ③ 새 문서 스캔이 정상 렌더되는지를 본다. - 수동 QA 완료 1) Margin 뷰어에서 figure 개수가 많은 논문 A를 엽니다. 2) 사이드 패널의 그림·표 탭을 엽니다. "figure 스캔 중…" 또는 진행 메시지가 보여야 합니다. 3) 스캔이 끝나기 전에(카드가 아직 안 뜬 상태) 같은 뷰어 창에 논문 B를 드래그&드롭. **기대 결과** - [x] "figure 스캔에 실패했어요" + [다시 시도]가 뜨지 않는다 ← 이게 핵심 확인점입니다. 취소를 에러로 처리하면 여기서 번쩍입니다. - [x] 논문 B의 figure 카드가 정상 렌더된다. - [x] DevTools 콘솔에 figure 스캔 실패 로그가 남지 않는다. - [x] 논문 A의 카드가 뒤늦게 섞여 들어오지 않는다. ## 알아 둘 것 - **취소는 협조적이라 중첩이 0이 되는 건 아니다.** 엔진은 페이지 경계 `checkAborted()`와 진행 중 렌더에서만 멈추므로, 문서 교체 후에도 나가는 스캔이 약 1페이지 분량을 더 들고 있을 수 있다. "스캔 두 개가 끝까지" 대비 이득이 목적이다. - 리뷰 중 **더 큰 압력원**을 발견해 별건으로 올렸다 (#35): `PdfHost.#setDocument`가 이전 `PDFDocumentProxy`를 `destroy()`하지 않아 세션 내내 상주한다. 문서를 열수록 단조 증가하므로 이 PR보다 영향이 크다. ## Checklist - [x] PR 제목이 Conventional Commits 형식 (`feat: …`, `fix: …` — squash 커밋 메시지가 됨) - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted (에이전트가 작성/보조한 PR이면 체크) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
## Summary `PdfHost.#setDocument()`가 `this.#doc = doc`으로 **덮어쓰기만** 해서, `PDFDocumentProxy.destroy()`가 이 repo 어디에서도 불리지 않았다(`grep -rn destroy src/` → 0건). pdf.js는 `destroy()` 전까지 워커 측 자원과 페이지 프록시를 붙들고 있어 GC 대상이 되지 않는다 — **한 세션에서 문서를 N번 열면 N개가 그대로 상주한다.** 벤더링본 v2.14.0은 크롭을 **살아 있는 캔버스**로 유지하고 `FigRenderError` 감지도 없으므로, 이 누수는 Chrome이 캔버스 백킹 스토어를 회수하는 조건(엔진 백로그 B7)에 직접 기여한다. 그 증상은 **오류도 재시도 버튼도 없이 프리뷰 카드가 백지로 뜨는 것**이다. #34가 스캔 중첩을 줄였고, 이건 단조 증가하는 쪽이라 영향이 더 크다. Closes #35 ## Changes - **`#setDocument`** — 이전 문서를 잡아 두고, `linkService.setDocument(new)`·`viewer.setDocument(new)` **이후에** `destroy()`. pdf.js의 `PDFViewer.setDocument`는 나가는 문서를 동기로 정리(render/textLayer cancel → `_resetView`)하므로 순서가 반대면 방금 파괴된 proxy를 만진다. - **`#releaseDocument`** — 정리 실패는 `console.warn`으로 삼켜 새 문서 로드를 막지 않는다. - **`#awaitLoad`** (신규) — 로드 실패 시 `loadingTask.destroy()`. pdf.js는 문서마다 전용 `PDFWorker`를 띄우는데 실패 시 promise만 reject하고 task·worker를 회수하지 않는다. 이 확장은 파일 없음·권한 거부 UX가 일급이라 **실패 N번이 워커 스레드 N개**로 쌓였다. 같은 누수 계열이라 함께 처리했다. - **`getOutlineItems`** — `getOutline()` 거절을 삼키고 문서 교체를 감지해 빈 목록을 반환. **이게 없으면 이 PR이 회귀를 만든다**: 원격 PDF의 outline이 아직 안 받은 range를 기다리는 중에 다른 PDF를 열면 `destroy()`가 그 조회를 거절시키고, 그 거절이 **이전 로드의 `catch`**로 흘러가 방금 정상 로드된 문서 위에 오류 화면을 띄운다(+ 그림·표 탭이 `#doc = null`로 죽는다). - **`#resolveDestPage`** — `doc`을 명시로 받는다. 교체 후 옛 dest를 새 문서에 풀지 않는다. - **`downloadCurrentPdf`·`jumpToOutline`** — `destroy()`가 새로 노출시키는 거절을 삼킨다(이전에는 옛 문서로 resolve했다). - **`workerPort` 금지 주석** — 설정하면 pdf.js가 `PDFWorker.fromPort`로 **공유 싱글턴 워커**를 넘기고, 그러면 한 문서 destroy가 다른 문서들의 워커까지 종료시킨다. 문서당 워커 1개가 이 코드의 전제다. ## Test plan - `npm run typecheck` 통과 · `vitest run` **35/35** (7 파일) · `npm run build` 통과 - **`test/pdf-host.test.ts` 신규** — 이 repo의 첫 `vi.mock` 사용이다. 기존 6개 테스트는 pdf.js를 피한 순수 로직이었지만 이 결함은 pdf.js 생명주기 자체라 대역이 필요했다. 검증: 첫 로드 미파괴 / 두 번째 로드에서 **이전 것만 1회·현재 것은 미파괴** / **전환 이후 순서 고정** / destroy 거절이 로드를 깨지 않음 / 로드 실패 시 loadingTask 정리 / 같은 인스턴스 재설정 시 미파괴. - **동어반복 아님을 확인**: `src/viewer/pdf-host.ts`만 되돌리면 6개 중 **4개가 실패**한다(나머지 2개는 회귀 가드라 통과가 맞다). - 수동 QA는 안 했다 — 아래 절차를 리뷰어에게 요청한다. ### 수동 확인 같은 뷰어 창에 PDF를 **드래그&드롭**하는 것이 문서 교체의 사실상 유일한 경로다(파일 선택 대화상자는 오류 화면 버튼에만 걸려 있고, `?file=` 파라미터는 페이지 로드 시 1회뿐이다). - [x] 1. **정상 교체** — 논문 A를 열고, 그림·표 탭이 카드를 그린 뒤 논문 B를 드롭. B가 정상 렌더되고 콘솔에 경고·오류가 없어야 한다. - [x] 2. **스캔 중 교체** — A의 그림·표 스캔이 **끝나기 전에** B를 드롭. `"figure 스캔에 실패했어요"`가 뜨지 않고, B의 카드가 정상 렌더돼야 한다. - [x] 3. **회귀 확인 (이 PR의 핵심 위험)** — A를 열어 목차가 있는 것을 확인하고, **A의 목차가 로드되는 도중** B를 드롭. **오류 화면이 뜨지 않아야** 하고 B의 그림·표 탭이 살아 있어야 한다. - [x] 4. **누수 확인** — DevTools → Memory에서 스냅샷을 찍고, 문서를 5~6번 교체한 뒤 다시 찍는다. 이전에는 `PDFDocumentProxy`/워커가 교체 횟수만큼 쌓였고, 이제는 그렇지 않아야 한다. Task Manager(Shift+Esc)에서 워커 프로세스 수를 봐도 된다. - [x] 5. **실패 로드** — 존재하지 않는 로컬 파일을 열어 오류 화면을 띄운 뒤 정상 PDF를 연다. 정상 동작해야 하고, 실패를 여러 번 반복해도 워커가 쌓이지 않아야 한다. ## 남긴 것 - **stale-load 문제 → [#37](#37 신규. `main.ts`에 로드 epoch가 없어 겹친 로드에서 늦게 끝난 쪽이 새 문서 상태를 덮는다. 이 PR은 가장 날카로운 트리거(`getOutlineItems`)만 함수 내부에서 막았다 — 근본 해결은 `#scanGeneration`과 같은 모양의 epoch이고, 선재 결함이라 분리했다. - **엔진 쪽 미해결**: 1차 패스의 `await page.getTextContent()` 안에서 문서가 destroy되면 그 promise가 **영원히 settle되지 않는다**(pdf.js worker가 `task.terminated`면 sink를 error 처리하지 않고, `PDFPageProxy._destroy()`는 텍스트 스트림을 추적하지 않는다). 그 스캔 frame이 pinned돼 페이지 텍스트 메타데이터가 상주한다 — 크롭 캔버스는 아니고, 근본 해결은 엔진이 `getTextContent`를 abort signal과 race시키는 것이라 Margin에서 막을 수 없다. 문서에 적어 뒀다. ## Checklist - [x] PR 제목이 Conventional Commits 형식 (`feat: …`, `fix: …` — squash 커밋 메시지가 됨) - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted (에이전트가 작성/보조한 PR이면 체크) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **버그 수정** * PDF 문서 교체 중 다운로드·목차 이동에서 발생하던 오류와 예외를 안정적으로 처리했습니다. * 중복/대체 로드가 일어날 때 오래된 로드 결과가 적용되거나 잔여 메타데이터가 남는 문제를 방지했습니다. * 로드 실패 시 로딩 작업 정리 및 화면 전환 중단 없이 동작하도록 개선했습니다. * **문서** * 문서 결함 원인/해결 및 워커 사용 시 주의사항을 보강했습니다. * **테스트** * 문서 교체·목차 동작 흐름을 검증하는 테스트를 추가했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
## Summary 엔진 [PDFViewer-Figure-Extract](https://github.com/onetwothr1/PDFViewer-Figure-Extract) **v2.19.4**를 벤더링한다 (엔진 SHA `af44f5a49c6dec19344e9893dee4963b60de76ab`). 직전 벤더링본은 v2.14.0이었고, 그 사이 **소비자 계약이 두 가지 바뀌었다** — cross-page figure의 `captionPage` 방출과 `FigRenderError`. ## Changes **cross-page figure (엔진 v2.19.0)** — 캡션이 다음 장 상단에 있는 그림을 이제 **실제 그림 페이지**에 방출한다. `figure.page`는 그림 페이지이고, 캡션이 다른 페이지일 때만 `figure.captionPage`(optional)가 실린다. same-page figure에는 필드 자체가 없다. - `captionBoxPt`는 **캡션 페이지 좌표계**다 — 그림 페이지 높이로 `toPdfRect`하면 조용히 틀린다. 타입·문서에 명시했다. - `FigureSeed.captionPage`를 필수로 두고 `toFigureEntries`가 `?? page`로 정규화한다. `FigureEntry`는 넓히지 않았다 (`captionAnchor.page`가 목적지 필드). - 새 경계 함수 `toFigureEntry()`가 필드를 **명시 나열**해 seed 전용 필드가 `FigureEntry`(→ `chrome.storage`)로 새지 않게 한다. 스프레드는 TS strict에서도 초과 속성 검사를 받지 않아 타입이 막아주지 못한다. **FigRenderError (엔진 v2.19.1)** — Chrome이 메모리 압력에서 캔버스 백킹 스토어를 회수하면 엔진이 즉시 실패시킨다(이전에는 백지 크롭이 조용히 저장됐다). `tab-figures.ts`가 엔진과 **동일한 duck-typing**(`error.name`)으로 판별해 메모리 안내 문구를 띄운다 — realm을 넘으면 `instanceof`만 깨지고 이름은 남는다. -⚠️ **부분 결과가 없다**: 앞 20페이지를 다 잡았어도 21페이지에서 죽으면 목록이 0건이다. 실패 모드가 "일부 백지"에서 "전부 없음"으로 옮겨간다. 재시도는 백오프 없는 즉시 전량 재스캔이라, 문구가 "다른 탭을 닫고"를 먼저 안내하는 것이 유일한 완화다. **크롭 접근자** — `cropCanvas`가 export에서 사라졌고 `cropDataURL`·`cropBlob`은 내부 `cropPng_`를 읽는 접근자다. "엔진 figure 객체를 그대로 넘겨라 / `structuredClone`·JSON 왕복 금지"는 필드 이름만 바뀐 채 그대로 유효하다. **`ExtractOptions`** — `cropImages`·`onDiagnostic` 추가. `onDiagnostic` record에는 캡션 줄 원문이 앵커당 최대 180자 실리므로 로컬 디버깅 외 사용 금지를 주석에 명시했다. **문서** — 버전 핀이 지키는 범위가 "상수 ↔ 엔진 파일" 한 쌍뿐이고 선언 타입이 어느 버전 기준인지는 기계가 검사할 수 없다는 점을 명시. 갱신 절차에 엔진 repo clean 확인 + SHA 기재 추가. `figure-ux.md`·`implementation-plan.md`의 캡션 검색 페이지 기준, `pdf-host.ts`의 낡은 크롭 주석, 엔진 v2.16.0에서 해소된 v2.11.0 캐비앳도 정정. ## Test plan - `npm run typecheck` 통과 - `npm test` **48 passed** (직전 41 → +7) - `npm run build` 통과 - **실 PDF 스모크 3편** — 벤더링본을 헤드리스로 로드해 확인 | 논문 | 기대 | 결과 | |---|---|---| | PLOS pbio-3002205 | `7@p15`, captionPage 16 | PASS (17건 중 captionPage 보유 1건) | | npj Microgravity Sanesi-2023 | `10@p12`, captionPage 13 | PASS (10건 중 1건) | | Barak & Krakauer-2021 | `1@p2`, captionPage 3 | PASS (4건 중 1건) | 세 편 모두 크롭(`cropDataURL`)이 PNG data URL로 생성되는 것까지 확인했다. **새 테스트가 죽이는 회귀** (뮤테이션으로 검증): 1. 좌표 변환에 캡션 페이지 높이를 쓰는 것 — 페이지별로 다른 높이를 주는 mock + 호출 인자 단언 2. 카드가 캡션 페이지를 가리키는 것 — `dataset.page`·aria-label·`p.N` 칩 세 곳 3. `FigRenderError` 판별을 `instanceof`로 좁히는 것 4. `toFigureEntry`가 seed를 스프레드해 `captionPage`를 흘리는 것 ## Checklist - [x] PR 제목이 Conventional Commits 형식 - [x] `npm run typecheck` · `npm test` 통과 - [x] AI-assisted (에이전트가 작성/보조한 PR이면 체크) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **새로운 기능** * 그림과 캡션이 다른 페이지에 있는 경우 캡션 앵커 및 영속화를 더 안정적으로 처리합니다. * 진단 기록 및 이미지 크롭 비용을 줄이는 옵션을 지원합니다. * 메모리 부담으로 렌더 실패 시 재시도 안내가 더 명확해졌습니다. * **버그 수정** * 캡션 앵커 누락/잘못된 라벨 판정, 캡션 클릭 히트 감지 오류를 개선했습니다. * 페이지별 크기 기반 좌표 변환 정확도를 높였습니다. * **문서** * 캡션 처리 원칙, 오류/재시도 동작, 엔진 갱신 절차를 최신 내용으로 보강했습니다. * **테스트** * 캡션 페이지·실패 모드·좌표 변환 관련 검증을 확장했습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
dev에서 검증된 변경을main으로 승격하기 위한 첫 릴리스 후보입니다.main과dev의 분기 이력을 먼저 합쳤고, PR #2 리뷰에서 저장소 내부만으로 개선 가능한 재시도·접근성·통합 테스트 보강을 포함합니다.현재는 fig-extract 원본 저장소에서 수정·재벤더링해야 하는 #12, #14가 남아 있으므로 draft로 유지합니다.
Changes
main/dev이력을 merge commit으로 정합화 (chore: reconcile main and dev history #11)Release blockers
package.json/manifest 현재0.1.0, 기존 태그·Release 없음)Test plan
npm run typechecknpm test— 6 files, 25 testsnpm run builddevpush CI (macOS + Windows)Checklist
npm run typecheck·npm test통과