Skip to content

user can now change pdfs in the pdf viewer#88

Merged
InfinityBowman merged 2 commits into
mainfrom
87-pdf-side-by-side-viewer-show-multiple-pdfs
Dec 18, 2025
Merged

user can now change pdfs in the pdf viewer#88
InfinityBowman merged 2 commits into
mainfrom
87-pdf-side-by-side-viewer-show-multiple-pdfs

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Dec 18, 2025

Summary by CodeRabbit

  • New Features

    • Multi-PDF selection and switching support in checklist and reconciliation views
    • Optional PDF deletion controls for local checklists
    • Improved PDF loading to maintain visibility during transitions
  • Style

    • Streamlined PDF selector interface

✏️ Tip: You can customize this high-level summary in your review settings.

@InfinityBowman InfinityBowman linked an issue Dec 18, 2025 that may be closed by this pull request
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Dec 18, 2025

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
corates 9577364 Commit Preview URL Dec 18 2025, 08:29 PM

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 18, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • packages/landing/public/bimi.svg is excluded by !**/*.svg

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR refactors the PDF handling system across checklist and reconciliation UI components to support multi-PDF selection and enhanced PDF loading behavior. Changes include new props for PDF deletion control, multi-PDF switching, and introduction of docId-based DOM recreation in usePdfJs to properly handle PDF transitions while retaining prior PDF data during loading.

Changes

Cohort / File(s) Summary
Build Configuration
.gitignore
Updated ignore pattern from docs/plans/* to docs/plans* to match docs/plans directory and any path prefix starting with docs/plans.
Checklist Component Wrappers
packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx, packages/web/src/components/checklist-ui/LocalChecklistView.jsx
Added four new props to ChecklistWithPdf (allowDelete, pdfs, selectedPdfId, onPdfSelect) for multi-PDF support and delete button control. LocalChecklistView passes allowDelete={true} to enable deletion in local checklist context.
Checklist State Management
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
Removed PDF deletion flow (handlePdfClear, deletePdf calls) and PdfSelector component. Simplified PDF state management by removing data clearing on PDF switch and adding explicit selectedPdfId tracking. Updated ChecklistWithPdf props and removed onPdfClear.
Reconciliation Components
packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx, packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
ReconciliationWithPdf now forwards pdfs, selectedPdfId, and onPdfSelect props to PdfViewer. ReconciliationWrapper introduces selectedPdfId state, memoized studyPdfs/defaultPdf/currentPdf helpers, auto-selects default PDF on load, adds handlePdfSelect handler, and modifies loading flow to skip unnecessary reloads while preserving prior PDF data.
PDF UI Components
packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx, packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx, packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
PdfSelector simplified with streamlined menu items (value/label format) and unified selection handler. PdfToolbar adds allowDelete, pdfs, selectedPdfId, onPdfSelect props; integrates PdfSelector for multi-PDF display and conditionally shows delete button. PdfViewer updates rendering from Index to For with docId-based keys for DOM recreation and forwards new props to PdfToolbar.
PDF Loading Logic
packages/web/src/components/checklist-ui/pdf/usePdfJs.js
Introduced docId state and loadedPdfName tracking for DOM recreation on PDF switch. Modified load logic to skip reload if same file already loaded, clear canvases and increment docId on new PDF load. Uses queueMicrotask for deferred rendering with captured scale. Exposes docId in return for consumers to react to PDF changes.

Sequence Diagram

sequenceDiagram
    participant User
    participant ReconciliationWrapper as ReconciliationWrapper<br/>(State Mgmt)
    participant PdfViewer as PdfViewer<br/>(Component)
    participant PdfToolbar as PdfToolbar<br/>(UI)
    participant usePdfJs as usePdfJs<br/>(Load Logic)
    participant Cache as PDF Cache

    User->>ReconciliationWrapper: Select PDF (onPdfSelect)
    activate ReconciliationWrapper
    ReconciliationWrapper->>ReconciliationWrapper: Update selectedPdfId<br/>Derive currentPdf from<br/>studyPdfs list
    ReconciliationWrapper->>PdfViewer: Pass pdfs, selectedPdfId,<br/>onPdfSelect props
    deactivate ReconciliationWrapper

    activate PdfViewer
    PdfViewer->>PdfToolbar: Forward multi-PDF props
    deactivate PdfViewer

    activate PdfToolbar
    PdfToolbar->>User: Render PdfSelector<br/>with PDF options
    activate User
    User->>PdfToolbar: Confirm selection
    deactivate User
    PdfToolbar->>PdfViewer: Trigger onPdfSelect callback
    deactivate PdfToolbar

    activate usePdfJs
    usePdfJs->>usePdfJs: Check loadedPdfName<br/>vs. new PDF name
    alt Same file already loaded
        usePdfJs->>User: Return (skip reload)
    else New PDF file
        usePdfJs->>usePdfJs: Clear canvases<br/>Increment docId
        usePdfJs->>Cache: getCachedPdf(fileName)
        activate Cache
        Cache-->>usePdfJs: pdfData (or null)
        deactivate Cache
        alt Cache hit
            usePdfJs->>usePdfJs: Load from cache
        else Cache miss
            usePdfJs->>Cache: downloadPdf(fileName)
            Cache-->>usePdfJs: pdfData
        end
        usePdfJs->>usePdfJs: Update loadedPdfName<br/>Queue deferred render<br/>with docId key
        usePdfJs->>PdfViewer: Trigger DOM recreation<br/>(docId-based keys)
        activate PdfViewer
        PdfViewer->>User: Render new PDF pages
        deactivate PdfViewer
    end
    deactivate usePdfJs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • ReconciliationWrapper: Introduces new state management (selectedPdfId, memoized helpers) and reworks PDF loading flow with multi-step fallback logic—requires careful review of selection/loading interactions
  • usePdfJs: Significant changes to load sequence and timing with docId/loadedPdfName tracking, queueMicrotask deferral, and DOM recreation logic—critical path requiring thorough validation
  • Heterogeneous scope: Mix of simple prop forwarding (ChecklistWithPdf, LocalChecklistView) and substantial refactoring (ChecklistYjsWrapper removal of deletion flow, PdfSelector simplification) creates varied reasoning requirements
  • Cross-component threading: Props propagate through multiple layers (ChecklistWithPdf → PdfViewer → PdfToolbar → PdfSelector); verify all handler chains work correctly
  • State transition edge cases: Verify that PDF switching, re-selection of same file, and data retention during transitions work as intended across all wrapper components

Possibly related PRs

  • PR #79: Modifies the same PDF UI codepaths (PdfViewer, PdfToolbar, usePdfJs, checklist wrappers) to add multi-PDF selection props and change selection/loading/deletion wiring
  • PR #57: Alters usePdfJs with loadingSourceId and ArrayBuffer cloning for safe PDF.js loads, sharing the same critical PDF loading logic file
  • PR #83: Implements multi-PDF support with related props (pdfs, selectedPdfId, onPdfSelect) and study-card PDF list modifications at the code level

Poem

🐰 With docId hops and selectors spry,
PDF pages dance and multiply!
No more deletion, just a thoughtful view—
When PDFs swap, the DOM's brand new! ✨
Multi-choice magic, smooth and clean,
The finest checklist ever seen! 📋

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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 title accurately summarizes the main functionality added: users can now select and switch between multiple PDFs in the PDF viewer, which is the primary change across all modified components.

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

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (3)
packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx (1)

16-25: Consider extracting shared PDF sorting logic.

The sorting logic here duplicates the implementation in PdfList.jsx (lines 25-35). To align with DRY principles, consider extracting this into a shared utility function.

Example extraction
// In a shared utility, e.g., packages/web/src/utils/pdfUtils.js
export function sortPdfsByTag(pdfs) {
  const tagOrder = { primary: 0, protocol: 1, secondary: 2 };
  return [...pdfs].sort((a, b) => {
    const tagA = tagOrder[a.tag] ?? 2;
    const tagB = tagOrder[b.tag] ?? 2;
    return tagA - tagB;
  });
}
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (1)

76-82: Consider guarding against stale selection.

The auto-select effect sets selectedPdfId when defaultPdf is available and no selection exists. However, if a previously selected PDF is removed from studyPdfs, the selectedPdfId signal will retain a stale ID. While currentPdf falls back to defaultPdf() in this case (line 68), the stale ID persists in state.

Consider clearing selectedPdfId when the selected PDF no longer exists in studyPdfs:

Optional improvement
  createEffect(() => {
    const pdf = defaultPdf();
-   if (pdf && !selectedPdfId()) {
+   const selected = selectedPdfId();
+   const pdfs = studyPdfs();
+   
+   // Clear stale selection if PDF no longer exists
+   if (selected && !pdfs.find(p => p.id === selected)) {
+     setSelectedPdfId(null);
+   }
+   
+   // Auto-select default if no valid selection
+   if (pdf && !selectedPdfId()) {
      setSelectedPdfId(pdf.id);
    }
  });
packages/web/src/components/checklist-ui/pdf/usePdfJs.js (1)

223-233: Consider strengthening source identification for concurrent load prevention.

Using byteLength as a source identifier is weak—two different PDFs could have identical sizes. While the risk is low (the identifier is cleared after loading completes), rapid switching between same-size PDFs could theoretically skip a load.

Consider hashing a portion of the data or using a counter/timestamp for uniqueness:

Suggested approach
+let loadCounter = 0;
+
 async function loadPdf(source) {
   if (!pdfjsLib) return;

-  // Generate a unique ID for this source to prevent duplicate loads
-  const sourceId = source.data ? source.data.byteLength : JSON.stringify(source);
+  // Generate a unique ID for this load operation
+  const sourceId = ++loadCounter;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38a48f2 and 36f9bad.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx (2 hunks)
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (4 hunks)
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx (1 hunks)
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx (2 hunks)
  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (3 hunks)
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx (1 hunks)
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx (3 hunks)
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx (6 hunks)
  • packages/web/src/components/checklist-ui/pdf/usePdfJs.js (7 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

For UI icons, use the solid-icons library or SVGs only. Do not use emojis

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/usePdfJs.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Use Zod for schema and input validation

**/*.{js,jsx,ts,tsx}: Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Keep files small, focused, and modular. If a file exceeds a high number of lines, consider refactoring by extracting sub-modules into a folder, moving complex logic into separate utility files or primitives, or splitting large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/usePdfJs.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
packages/web/src/components/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/components/**/*.{jsx,tsx}: Use responsive design principles for UI components
Use Zag.js for UI components and design system
Reuse existing Zag components from packages/web/src/components/zag/* and check the README.md list before adding new components
Components should receive at most 1–5 props for local configuration only, not shared state
Components should be lean and focused on rendering, not implement business logic. Move business logic into stores, utilities, or primitives
Never have a component act as a God component coordinating multiple large concerns

packages/web/src/components/**/*.{jsx,tsx}: Group related components in subdirectories with an index.js barrel export
Use Zag.js for UI components and design system
Zag components exist in packages/web/src/components/zag/* and should be reused. Check the README.md in that folder for a list of existing components before adding new components and when debugging
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file. Import stores directly where needed instead of passing values through multiple components
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context (when scoped to a feature)
With SolidJS, do not destructure props as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Components should be lean and focused. They should not implement business logic; move that into stores, utilities, or primitives. Never have a component act as a 'God component' coordinating multiple large concerns

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Group related components in subdirectories with an index.js barrel export

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/usePdfJs.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/**/*.{jsx,tsx}: Do NOT prop-drill application state in SolidJS. Use external stores under packages/web/src/stores/ or component-relative stores instead
Do not destructure props in SolidJS components; access props directly or wrap them in a function to maintain reactivity
Use createMemo for derived values and createStore for complex state in SolidJS

packages/web/src/**/*.{jsx,tsx}: When you need to compute a value based on props or state in SolidJS, use createMemo to ensure it updates reactively
When you have complex state or state objects, use SolidJS's createStore for better performance and reactivity
You may create reusable logic in 'primitives' (hooks) that can be shared across components. This keeps components clean and focused on rendering

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create reusable logic in primitives (hooks) that can be shared across components

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/usePdfJs.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
**/*.{js,jsx,ts,tsx,md,json,sql}

📄 CodeRabbit inference engine (.cursorrules)

Do not use emojis in code, comments, documentation, or commit messages

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/usePdfJs.js
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{jsx,tsx}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis
Use responsive design principles for UI components

Files:

  • packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx
  • packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx
  • packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
  • packages/web/src/components/checklist-ui/LocalChecklistView.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx
  • packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx
🧠 Learnings (4)
📚 Learning: 2025-12-18T05:00:29.870Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-18T05:00:29.870Z
Learning: Applies to docs/plans/** : Place plans in the docs/plans/ directory

Applied to files:

  • .gitignore
📚 Learning: 2025-12-18T17:05:47.243Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-18T17:05:47.243Z
Learning: Place plans in the docs/plans/ directory

Applied to files:

  • .gitignore
📚 Learning: 2025-12-18T05:00:29.870Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-18T05:00:29.870Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx,js,ts} : Create reusable logic in primitives (hooks) that can be shared across components

Applied to files:

  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-18T17:05:47.242Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-18T17:05:47.242Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : You may create reusable logic in 'primitives' (hooks) that can be shared across components. This keeps components clean and focused on rendering

Applied to files:

  • packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx
🧬 Code graph analysis (6)
packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx (1)
packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx (1)
  • PdfViewer (17-116)
packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx (2)
packages/web/src/components/checklist-ui/pdf/PdfList.jsx (1)
  • sortedPdfs (26-36)
packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx (1)
  • pdf (29-34)
packages/web/src/components/checklist-ui/pdf/usePdfJs.js (1)
packages/web/src/components/project-ui/ChartSection.jsx (1)
  • canvas (46-46)
packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (5)
packages/web/src/primitives/useProject/index.js (1)
  • useProject (25-207)
packages/web/src/components/checklist-ui/LocalChecklistView.jsx (1)
  • params (19-19)
packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (2)
  • params (15-15)
  • handlePdfSelect (131-135)
packages/web/src/components/checklist-ui/pdf/usePdfJs.js (1)
  • fileName (28-28)
packages/web/src/primitives/useAddStudies.js (1)
  • handlePdfSelect (170-236)
packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx (1)
packages/web/src/lib/pdfUtils.js (2)
  • pdf (47-47)
  • pdf (184-184)
packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx (1)
packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx (1)
  • PdfSelector (11-53)
⏰ 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: Workers Builds: corates
🔇 Additional comments (21)
packages/web/src/components/checklist-ui/pdf/PdfSelector.jsx (2)

27-41: LGTM!

The menu items memo correctly formats labels with tag suffixes, and the selection handler properly forwards the selected PDF ID to the parent.


45-52: LGTM!

The conditional rendering using Show with the shouldShow memo correctly displays the selector only when multiple PDFs exist.

packages/web/src/components/checklist-ui/pdf/PdfToolbar.jsx (3)

92-99: LGTM!

The PdfSelector integration is clean. The conditional rendering correctly shows the selector only when multiple PDFs exist, and props are forwarded appropriately.


101-106: LGTM!

Good logic to hide the standalone filename when the multi-PDF selector is active, avoiding duplicate display of PDF information.


108-128: LGTM!

The delete button implementation is solid:

  • Proper guards with fileName && !readOnly && allowDelete
  • Confirmation dialog with clear messaging and danger variant
  • Async handler correctly awaits user confirmation before calling onClearPdf
packages/web/src/components/checklist-ui/LocalChecklistView.jsx (1)

213-213: LGTM!

Enabling allowDelete for local checklists makes sense - users should be able to manage PDFs in their local storage.

packages/web/src/components/checklist-ui/compare/ReconciliationWithPdf.jsx (1)

200-207: LGTM!

Multi-PDF props are correctly forwarded to PdfViewer while maintaining readOnly={true} for the reconciliation context. This allows switching between PDFs without modifying them.

packages/web/src/components/checklist-ui/ChecklistWithPdf.jsx (1)

22-25: LGTM!

Clean prop forwarding with matching documentation. The component correctly acts as a pass-through for the new multi-PDF capabilities.

Also applies to: 51-54

packages/web/src/components/checklist-ui/compare/ReconciliationWrapper.jsx (4)

49-71: LGTM!

The memoized PDF selection logic is well-structured:

  • studyPdfs safely extracts the PDF array with fallback
  • defaultPdf prioritizes primary tag with fallback to first
  • currentPdf provides selection with fallback chain

84-128: LGTM!

The PDF loading effect handles the multi-PDF flow well:

  • Correctly tracks currentPdf changes
  • Prevents redundant loads with attemptedPdfFile check
  • Maintains previous PDF during transitions to avoid flash
  • Cache-first approach with cloud fallback is efficient

130-135: LGTM!

The handlePdfSelect function correctly updates selection and resets attemptedPdfFile to trigger a new load cycle.


312-314: LGTM!

Props are correctly passed to ReconciliationWithPdf, completing the multi-PDF flow from state management to UI.

packages/web/src/components/checklist-ui/pdf/PdfViewer.jsx (2)

87-110: For-based rendering with docId keys for DOM recreation looks good.

The switch from Index to For with composite keys (${docId}-${page}) correctly forces DOM recreation when the PDF changes, preventing stale canvas content. The parsing approach works since docId is an incrementing integer.

One minor observation: if future changes modify docId to include hyphens, the split('-')[1] parsing would break. Consider using lastIndexOf('-') for slightly more robust parsing, though this is not an issue currently.


47-71: Props wiring for multi-PDF support is correct.

The new props (allowDelete, pdfs, selectedPdfId, onPdfSelect) are properly passed through to PdfToolbar, enabling the multi-PDF selection UI. The component remains focused on composition rather than implementing selection logic itself, following the coding guidelines.

packages/web/src/components/checklist-ui/pdf/usePdfJs.js (4)

185-221: Render effect with queueMicrotask is well-designed.

The approach of:

  1. Tracking docId() to re-run on PDF changes
  2. Capturing currentScale before async work
  3. Using queueMicrotask to ensure DOM readiness

...correctly handles the timing issues between SolidJS reactivity and canvas rendering. The captured scale prevents stale closure issues in the async callback.


274-288: Canvas clearing and docId increment sequence is correct.

The order of operations—clearing existing canvases, incrementing docId, then setting the new document—ensures:

  1. No stale content visible during transition
  2. DOM recreation triggered via the For loop's key change
  3. New canvases are ready for the new PDF's pages

517-520: Reset of loadedPdfName on clear enables file re-selection.

Resetting loadedPdfName to null alongside userCleared = true correctly allows the same PDF file to be reloaded after an explicit clear operation.


141-158: Use content-based identification for PDF reload detection.

The filename-only check at line 143 (if (loadedPdfName() === savedName) return;) will skip reloading a PDF if the same filename is provided with different content. If a user replaces an existing PDF file with different content but keeps the same filename, the component will silently skip the reload.

The secondary sourceId check based on byteLength (lines 227-233) would catch this, but it never executes because the effect returns early. Consider using a content hash or combining filename + file size for more robust duplicate detection, or document that filenames are guaranteed to be unique per file version.

packages/web/src/components/checklist-ui/ChecklistYjsWrapper.jsx (3)

82-127: PDF loading effect with explicit dependency tracking is correct.

The pattern of calling selectedPdfId(); on line 86 to explicitly track the dependency is a proper SolidJS technique for ensuring the effect re-runs when the selection changes. The decision to keep the previous PDF visible during transitions (lines 93-94) provides a better user experience by avoiding empty state flashes.


352-365: Multi-PDF props integration with allowDelete={false} is appropriate.

The new props correctly enable multi-PDF selection while disabling deletion for Yjs-synced checklists. This is a sensible distinction from LocalChecklistView which enables deletion, as collaborative checklists may require more controlled PDF management.


25-29: Simplified useProject destructuring aligns with removed deletion flow.

The removal of removePdfFromStudy from the destructured values is consistent with the refactored PDF management approach. The deletePdf API call (line 167) is correctly retained for cleaning up orphaned uploads on failure—this is distinct from the Y.js data operation that was removed.

Comment thread .gitignore

# Temp files
/docs/plans/* No newline at end of file
/docs/plans* No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the .gitignore file around line 53
cat -n .gitignore | sed -n '45,60p'

Repository: InfinityBowman/corates

Length of output: 229


🏁 Script executed:

# Check if docs/plans directory exists
find . -type d -name "plans" 2>/dev/null | head -20

Repository: InfinityBowman/corates

Length of output: 48


🏁 Script executed:

# Check the current state of docs directory
ls -la docs/ 2>/dev/null || echo "docs directory not found"

Repository: InfinityBowman/corates

Length of output: 561


Fix: Pattern change conflicts with documented guidelines for storing plans.

The pattern /docs/plans* ignores the docs/plans/ directory itself, not just its contents. Git patterns with /* match direct contents only, while patterns without the trailing / match the directory itself and any paths matching the glob pattern. Since the learnings from .github/copilot-instructions.md and .cursorrules state "Place plans in the docs/plans/ directory," this pattern prevents that directory from being tracked.

If committed plan files should live in docs/plans/, change the pattern to /docs/plans/* to ignore only the directory contents while allowing the directory structure to be tracked. Otherwise, update the documentation to reflect that plans should not be committed to the repository.

🤖 Prompt for AI Agents
In .gitignore around line 53, the pattern "/docs/plans*" unintentionally ignores
the docs/plans/ directory itself (preventing the tracked directory and its
intended plan files), so replace it with "/docs/plans/*" to ignore only the
contents while allowing the docs/plans/ directory to be tracked; alternatively,
if plans must not be committed, update the documentation to state that and keep
or adjust the ignore pattern accordingly.

@InfinityBowman InfinityBowman merged commit f0d65e7 into main Dec 18, 2025
1 of 3 checks passed
@InfinityBowman InfinityBowman deleted the 87-pdf-side-by-side-viewer-show-multiple-pdfs branch December 18, 2025 20:27
@github-actions github-actions Bot restored the 87-pdf-side-by-side-viewer-show-multiple-pdfs branch December 18, 2025 20:27
@InfinityBowman InfinityBowman deleted the 87-pdf-side-by-side-viewer-show-multiple-pdfs branch December 19, 2025 20: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.

PDF side by side viewer show multiple pdfs

1 participant