Skip to content

fix: broken initial selection of text on first page#333

Merged
chmurson merged 2 commits intomainfrom
fix-initial-pdf-selection
Oct 28, 2025
Merged

fix: broken initial selection of text on first page#333
chmurson merged 2 commits intomainfrom
fix-initial-pdf-selection

Conversation

@chmurson
Copy link
Collaborator

@chmurson chmurson commented Oct 28, 2025

What

Why?
It was obscure regression that appeard after "Selected text" was moved inside of Notes tab. It used to be above component.

Summary by CodeRabbit

  • Refactor
    • Centralized PDF text-layer rendering tracking into the provider for consistent state propagation.
    • Viewer selection now reads rendering status from the provider, improving selection readiness and stability.
    • Introduced a provider-managed text-layer tracker and removed duplicated local tracking to simplify behavior and reduce inconsistencies.

@netlify
Copy link

netlify bot commented Oct 28, 2025

Deploy Preview for graypaper-reader ready!

Name Link
🔨 Latest commit 361864f
🔍 Latest deploy log https://app.netlify.com/projects/graypaper-reader/deploys/6901333b1026ab0008efe6e3
😎 Deploy Preview https://deploy-preview-333--graypaper-reader.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Oct 28, 2025

Walkthrough

Adds a new hook useTextLayerRendered to track pdfjs "textlayerrendered" events, exposes its textLayerRenderedRef via IPdfContext in PdfProvider, removes the prior useTextLayer hook, and updates consumers (e.g., SelectionRenderer) to read the ref from context.

Changes

Cohort / File(s) Summary
PdfProvider integration
src/components/PdfProvider/PdfProvider.tsx, src/components/PdfProvider/hooks/useTextLayerRendered.ts
New useTextLayerRendered hook subscribes to pdfjs "textlayerrendered" events, maintains textLayerRenderedRef: RefObject<number[]>, and is initialized/returned in PdfProvider. IPdfContext updated: pageOffsets changed to RefObject<DOMRect[]> and textLayerRenderedRef added; ref included in memoized context value.
PdfViewer consumer updates
src/components/PdfViewer/SelectionRenderer/SelectionRenderer.tsx
Removed local useTextLayer usage; now reads textLayerRenderedRef from PdfContext and uses textLayerRenderedRef.current.includes(pageNumber) for readiness checks and effect dependencies.
Hook removal
src/components/PdfViewer/utils.ts
Removed the prior exported useTextLayer(eventBus) hook that tracked rendered pages and subscribed to "textlayerrendered".

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant PDFJS as pdfjs-dist
  participant Hook as useTextLayerRendered
  participant Provider as PdfProvider
  participant Context as PdfContext
  participant Selection as SelectionRenderer

  Note over PDFJS,Hook: text layer rendered events
  PDFJS->>Hook: "textlayerrendered"(pageNumber)
  Hook->>Hook: add unique pageNumber to textLayerRenderedRef.current
  Hook->>Provider: provide textLayerRenderedRef
  Provider->>Context: include textLayerRenderedRef in context value
  Selection->>Context: read textLayerRenderedRef
  Selection->>Selection: check textLayerRenderedRef.current.includes(pageNumber)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review useTextLayerRendered event subscription, deduplication, and cleanup.
  • Confirm IPdfContext type change (pageOffsetsRefObject<DOMRect[]>) doesn't break other consumers.
  • Verify consumers access .current correctly and effect dependency arrays are appropriate.
  • Check PdfProvider memoization includes textLayerRenderedRef so updates propagate.

Poem

🐇 I heard the render chime and hopped to the scene,
Collected page numbers, kept the list clean,
I handed the ref to the provider’s nest,
Now renderers peek in and work at their best—
A tiny rabbit cheering the shared-ref dream.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "fix: broken initial selection of text on first page" directly aligns with the stated objectives of fixing a bug where initial text selection on the first page was not appearing in the Notes tab. The changes support this fix by refactoring text layer tracking: moving the useTextLayer hook from PdfViewer/utils.ts into a new useTextLayerRendered hook within PdfProvider, exposing textLayerRenderedRef through the PdfContext, and updating SelectionRenderer to consume this context-based reference. This architectural change ensures the text layer rendered status is consistently available across the component hierarchy, which addresses the regression that occurred when the "Selected text" component was relocated inside the Notes tab. The title is specific, concise, and clearly communicates the primary objective without being misleading or overly technical.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-initial-pdf-selection

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 28, 2025

Visual Regression Test Report ✅ Passed

Github run id: 18889516699

🔗 Artifacts: Download

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/PdfProvider/PdfProvider.tsx (1)

231-245: Include textLayerRenderedVersion in context value and memo deps.

The ref identity won’t change on events; the version ensures subscribers can update.

       pageOffsets,
       downloadPdfWithTheme,
-      textLayerRenderedRef,
+      textLayerRenderedRef,
+      textLayerRenderedVersion,
     }),
-    [theme, viewer, visiblePages, services, scale, downloadPdfWithTheme, textLayerRenderedRef],
+    [theme, viewer, visiblePages, services, scale, downloadPdfWithTheme, textLayerRenderedRef, textLayerRenderedVersion],
🧹 Nitpick comments (3)
src/components/PdfViewer/SelectionRenderer/SelectionRenderer.tsx (1)

33-40: Retry timer: use functional setState and align delay with comment.

Avoid stale closure on retryScrolling and fix the “in a second” comment vs 100ms delay mismatch.

-      // we don't have an offset yet, so we will retry in a second or so.
+      // we don't have an offset yet, so retry shortly.
       setTimeout(() => {
-        setRetryScrolling(retryScrolling + 1);
-      }, 100);
+        setRetryScrolling((n) => n + 1);
+      }, 200);
src/components/PdfProvider/hooks/useTextLayerRendered.ts (2)

4-4: Naming consistency: file vs hook export.

Exported name useTextLayer doesn’t match file useTextLayerRendered.ts. Consider renaming to useTextLayerRendered for clarity and searchability.


13-15: Avoid noisy logs in production paths.

Remove console.log or gate behind NODE_ENV !== "production".

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67d8889 and 900392e.

📒 Files selected for processing (4)
  • src/components/PdfProvider/PdfProvider.tsx (4 hunks)
  • src/components/PdfProvider/hooks/useTextLayerRendered.ts (1 hunks)
  • src/components/PdfViewer/SelectionRenderer/SelectionRenderer.tsx (3 hunks)
  • src/components/PdfViewer/utils.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • src/components/PdfViewer/utils.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/PdfProvider/hooks/useTextLayerRendered.ts
  • src/components/PdfViewer/SelectionRenderer/SelectionRenderer.tsx
  • src/components/PdfProvider/PdfProvider.tsx
🧬 Code graph analysis (2)
src/components/PdfViewer/SelectionRenderer/SelectionRenderer.tsx (1)
src/components/PdfProvider/PdfProvider.tsx (2)
  • PdfContext (13-13)
  • IPdfContext (27-37)
src/components/PdfProvider/PdfProvider.tsx (1)
src/components/PdfProvider/hooks/useTextLayerRendered.ts (1)
  • useTextLayer (4-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (2)
src/components/PdfProvider/PdfProvider.tsx (1)

34-35: No fixes required; null-safety is already properly handled.

The review comment raised concerns about null-safety when accessing pageOffsets.current[page]. However, verification shows this is already correctly implemented:

  • SelectionRenderer guards with if (!pageOffset) checks before using the value
  • NoteRenderer passes the value to HighlightNote, which declares pageOffset as an optional prop (pageOffset?: DOMRect) and explicitly checks if (!pageOffset || ...) before rendering

The type definition (RefObject<DOMRect[]>) is correct, and all consumers properly handle the undefined case. Optional chaining is unnecessary here; the current pattern of defensive checks is appropriate.

src/components/PdfViewer/SelectionRenderer/SelectionRenderer.tsx (1)

110-114: Incorrect review comment - type already prevents null current.

The type RefObject<DOMRect[]> guarantees that pageOffsets.current is always a DOMRect[] array, never null. It's initialized as useRef([]) and cannot have a null current property. The concern about guarding against null current is unfounded.

Array elements may be undefined until populated, but this is already handled by existing checks (line 33 guards the element value, line 110 checks both pageNumber and validates the result, and line 42 passes to an optional prop). No changes needed.

Likely an incorrect or invalid review comment.

@chmurson chmurson force-pushed the fix-initial-pdf-selection branch from 900392e to 6f72f15 Compare October 28, 2025 20:38
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/components/PdfProvider/hooks/useTextLayerRendered.ts (1)

7-23: Reset the ref on EventBus change/clear to avoid stale pages across documents.

If a new PDF loads (or EventBus becomes undefined), textLayerRenderedRef.current retains old page numbers. Consumers checking includes(pageNumber) may get false positives for the new doc (e.g., page 1). Clear the ref when eventBus changes; this preserves your ref‑only design.

Apply this minimal diff:

   useEffect(() => {
-    if (!eventBus) {
-      return;
-    }
+    // New document or cleared EventBus → reset tracked pages
+    textLayerRenderedRef.current = [];
+    if (!eventBus) return;

     const handleTextLayerRendered = (e: { pageNumber: number }) => {
       if (!textLayerRenderedRef.current.includes(e.pageNumber)) {
         textLayerRenderedRef.current.push(e.pageNumber);
       }
     };

     eventBus.on("textlayerrendered", handleTextLayerRendered);

     return () => {
       eventBus.off("textlayerrendered", handleTextLayerRendered);
     };
   }, [eventBus]);

Optional: If your viewer reuses the same EventBus across documents, also reset on a doc lifecycle event (e.g., pagesinit/pagesloaded) within this effect.

🧹 Nitpick comments (1)
src/components/PdfProvider/hooks/useTextLayerRendered.ts (1)

12-16: Optional: streamline dedup with a Set one‑liner.

Current includes + push works; a Set keeps entries unique more idiomatically. Keeps ref‑only approach intact.

-    const handleTextLayerRendered = (e: { pageNumber: number }) => {
-      if (!textLayerRenderedRef.current.includes(e.pageNumber)) {
-        textLayerRenderedRef.current.push(e.pageNumber);
-      }
-    };
+    const handleTextLayerRendered = (e: { pageNumber: number }) => {
+      // Avoid duplicates in O(1) using Set, keep outward type as number[]
+      textLayerRenderedRef.current = Array.from(
+        new Set([...textLayerRenderedRef.current, e.pageNumber]),
+      );
+    };

Based on learnings.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f72f15 and 361864f.

📒 Files selected for processing (1)
  • src/components/PdfProvider/hooks/useTextLayerRendered.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/PdfProvider/hooks/useTextLayerRendered.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: chmurson
PR: FluffyLabs/graypaper-reader#333
File: src/components/PdfProvider/PdfProvider.tsx:34-37
Timestamp: 2025-10-28T20:41:06.862Z
Learning: In the graypaper-reader codebase, for text layer render tracking in PdfProvider, use RefObject<number[]> approach (textLayerRenderedRef) without adding reactive signals or version numbers. Consumers should check the ref imperatively when needed rather than reacting to changes.
📚 Learning: 2025-10-28T20:41:06.862Z
Learnt from: chmurson
PR: FluffyLabs/graypaper-reader#333
File: src/components/PdfProvider/PdfProvider.tsx:34-37
Timestamp: 2025-10-28T20:41:06.862Z
Learning: In the graypaper-reader codebase, for text layer render tracking in PdfProvider, use RefObject<number[]> approach (textLayerRenderedRef) without adding reactive signals or version numbers. Consumers should check the ref imperatively when needed rather than reacting to changes.

Applied to files:

  • src/components/PdfProvider/hooks/useTextLayerRendered.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (1)
src/components/PdfProvider/hooks/useTextLayerRendered.ts (1)

1-6: Design choice (ref‑only) acknowledged.

Using a ref without re‑render ticks matches the project’s intent; consumers can poll textLayerRenderedRef.current as needed. No further changes required here.

Based on learnings.

@chmurson chmurson merged commit 6502258 into main Oct 28, 2025
9 checks passed
@chmurson chmurson deleted the fix-initial-pdf-selection branch October 28, 2025 21:29
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