Skip to content

202 pdf annotations#323

Merged
InfinityBowman merged 8 commits into
mainfrom
202-pdf-annotations
Jan 26, 2026
Merged

202 pdf annotations#323
InfinityBowman merged 8 commits into
mainfrom
202-pdf-annotations

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Jan 25, 2026

Summary by CodeRabbit

  • New Features

    • PDF Annotation is live: real-time, persistent annotations (highlight, underline, draw, text) with collaborative syncing and automatic inter-rater reliability.
    • Viewer and checklist components now support external annotation persistence and callbacks for add/update/delete enabling project-wide annotation flows.
  • Improvements

    • New workflow steps: independent appraisal with guided checklists and collaborative resolution with automatic scoring.
    • Enhanced landing visuals with layered animations and adjusted badge positioning.
  • Content

    • Added licensing/usage notes to tool and resource pages.

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

@InfinityBowman InfinityBowman linked an issue Jan 25, 2026 that may be closed by this pull request
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Jan 25, 2026

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 258ef54 Commit Preview URL Jan 26 2026, 05:52 AM

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 25, 2026

📝 Walkthrough

Walkthrough

Adds end-to-end PDF annotation support and syncing: viewer-side hook and sync manager, Yjs-backed per-project annotation operations, wiring into checklist components, and landing page UI/content updates removing "coming soon" labels and adding workflow copy and cards.

Changes

Cohort / File(s) Summary
Landing & Docs
packages/landing/src/components/FeatureShowcase.jsx, packages/landing/src/components/HowItWorks.jsx, packages/landing/src/components/SupportedTools.jsx, packages/landing/src/components/resources/ToolResourcePage.jsx
Removed "coming soon" labels for PDF Annotation, updated collaboration/workflow copy and steps, added licensing/IP informational card, and adjusted decorative/animation layers and FloatingBadge positions.
Checklist — UI Wiring
packages/web/src/components/checklist/ChecklistWithPdf.jsx, packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
Exposed annotation props (onAnnotationAdd, onAnnotationUpdate, onAnnotationDelete, initialAnnotations) on ChecklistWithPdf; ChecklistYjsWrapper computes per-PDF/checklist initialAnnotations and implements handlers that call project-level annotation ops.
EmbedPdf Viewer — Solid wrapper
packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
Use SolidJS untrack for non-reactive props and forward annotation callbacks and initialAnnotations as untracked props to the embedded Preact viewer, keeping pdfData/selectedPdfId reactive.
EmbedPdf Viewer — Preact viewer & hook
packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js, packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
New useAnnotationSync hook to load/subscribe/filter viewer annotation events and an AnnotationSyncManager integrated into the Preact viewer; viewer props extended with annotation callbacks and initialAnnotations for persistence/hydration.
Project-level annotation store/ops
packages/web/src/primitives/useProject/annotations.js, packages/web/src/primitives/useProject/index.js, packages/web/src/primitives/useProject/sync.js
New Yjs-backed annotation operations (add/update/delete/get/merge/bulk), wired into useProject (exposed via projectActionsStore and returned API), and sync parsing logic to hydrate study.annotations from the Y.Map structure.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Viewer as EmbedPDF Viewer (Preact)
    participant Hook as useAnnotationSync
    participant Manager as AnnotationSyncManager
    participant Checklist as ChecklistYjsWrapper
    participant Ops as AnnotationOps (Yjs)

    User->>Viewer: create/update/delete annotation
    Viewer->>Hook: emit annotation event
    Hook->>Manager: filter & translate event
    Manager->>Checklist: invoke onAnnotationAdd/update/delete
    Checklist->>Ops: call add/update/deleteAnnotation
    Ops->>Ops: persist to Yjs (Y.Map)
    Ops-->>Checklist: return status
    Checklist-->>User: reflect change in UI
Loading
sequenceDiagram
    participant Yjs as Yjs Document
    participant Sync as buildAnnotationsFromYMap
    participant Study as In-memory Study Object
    participant Consumer as useProject Consumer

    Yjs->>Sync: provide annotations Y.Map
    Sync->>Sync: iterate checklists, parse embedPdfData
    Sync->>Study: attach parsed annotations per checklist
    Study-->>Consumer: return hydrated study.annotations
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • 215 embedpdf for main viewer #228: Overlaps EmbedPdf viewer annotation integration and external callback/prop surface (onAnnotationAdd/onAnnotationUpdate/onAnnotationDelete/initialAnnotations).
  • 258 robins stability #259: Modifies ChecklistYjsWrapper and projectOps usage — related to checklist-level annotation handler wiring added here.
  • 104 refactor project state handlers #105: Related to useProject API changes and wiring of project-level operations (this PR adds annotationOps into the useProject surface).
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title '202 pdf annotations' is vague and uses non-descriptive phrasing that doesn't convey meaningful information about the changeset, making it difficult for teammates to understand the primary change from scanning history. Revise the title to be more descriptive, such as 'Add PDF annotation support with real-time collaboration' or 'Implement annotation persistence and sync for PDF reviews', which better communicates the main feature being introduced.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx (3)

137-153: Consider cleaning up tracking refs on import failure.

When annotations are added to loadedAnnotationIdsRef and annotationMapRef before importAnnotations is called, a failed import leaves these entries in place. If the user triggers a retry (e.g., by remounting or changing props), the annotations won't be re-imported because they're already marked as loaded.

Suggested fix: move tracking to after successful import
     const importItems = annotationsToLoad.map(annotation => {
       const embedData = annotation.embedPdfData;
       const annotationId = annotation.id || embedData.id;
 
-      // Mark as loaded before importing to prevent duplicates
-      loadedAnnotationIdsRef.current.add(annotationId);
-      annotationMapRef.current.set(annotationId, annotation);
-
       console.log('[AnnotationSync] Preparing import for:', annotationId, 'embedData:', embedData);
 
       return {
         annotation: {
           ...embedData,
           id: annotationId,
         },
+        // Store original annotation for tracking after success
+        _originalAnnotation: annotation,
       };
     });
 
     // Wrap in async to properly guard isApplyingRef if importAnnotations is async
     (async () => {
       try {
         console.log('[AnnotationSync] Calling importAnnotations with', importItems.length, 'items');
         await annotationScope.importAnnotations(importItems);
         console.log('[AnnotationSync] importAnnotations completed');
+        // Mark as loaded only after successful import
+        if (!cancelled) {
+          importItems.forEach(item => {
+            const annotationId = item.annotation.id;
+            loadedAnnotationIdsRef.current.add(annotationId);
+            annotationMapRef.current.set(annotationId, item._originalAnnotation);
+          });
+        }
       } catch (err) {
         console.error('[AnnotationSync] Failed to import annotations:', err);
       } finally {

255-255: Consider documenting callback stability expectations.

The subscription effect re-runs when onAnnotationAdd, onAnnotationUpdate, or onAnnotationDelete change. If parent components don't memoize these callbacks (e.g., via useCallback), the subscription will be torn down and recreated on every render. This is standard React behavior, but consider adding a JSDoc comment to ViewerPageProps noting that callbacks should be stable/memoized for optimal performance.


107-111: Consider gating verbose logging behind a debug flag.

The extensive console.log statements throughout AnnotationSyncManager are valuable for development but may produce noise in production. Consider wrapping them in a debug condition or using a structured logging utility.

Example: debug flag pattern
const DEBUG_ANNOTATION_SYNC = process.env.NODE_ENV === 'development';

// Then wrap logs:
if (DEBUG_ANNOTATION_SYNC) {
  console.log('[AnnotationSync] Load effect triggered:', { ... });
}
📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 445498e and 258ef54.

📒 Files selected for processing (2)
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/primitives/useProject/annotations.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/web/src/primitives/useProject/annotations.js
🧰 Additional context used
📓 Path-based instructions (16)
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Use Show and For components for conditional and list rendering in SolidJS

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/**

📄 CodeRabbit inference engine (.cursor/rules/organizations.mdc)

packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/*.{js,ts,jsx,tsx}: Use apiFetch for all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
Use handleFetchError for wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions like isErrorCode from error-utils to check specific error types instead of direct string comparisons

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Use createFormErrorSignals for form error handling in frontend forms to manage field-level and global error states

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Never throw string literals; always throw Error objects or return domain errors from API routes

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}

📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)

Shared or cross-feature state should use external stores located in packages/web/src/stores/

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{ts,tsx}: For UI icons, use solid-icons library or SVGs only (never emojis)
Use import aliases from jsconfig.json as defined for the project
Use Ark UI component library (@ark-ui/solid) for UI components
Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)
Do not prop-drill application state in SolidJS components; import stores directly where needed
Do not destructure props in SolidJS; access props.field directly or wrap in a function: () => props.field
Components should receive at most 1-5 props (local config only, not shared state)
Use createMemo for derived values in SolidJS
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state; import stores directly

Use import aliases from jsconfig.json as defined in the project configuration

packages/web/src/**/*.{ts,tsx}: Import stores directly and never prop-drill application state in SolidJS components
Never destructure SolidJS props - use props.field or () => props.field to preserve reactivity

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
**/*

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

Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/{web,landing}/src/**/*.{ts,tsx}

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

packages/{web,landing}/src/**/*.{ts,tsx}: For UI icons, use solid-icons library (e.g., solid-icons/bi, solid-icons/fi) or SVGs only, never emojis
Ensure browser compatibility, with particular attention to Safari
Group related components in subdirectories with barrel exports
Use Ark UI components from @ark-ui/solid for UI components
Use solid-icons icon library for all icon needs
Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure SolidJS props - access props.field directly or wrap in function: () => props.field
SolidJS components should receive at most 1-5 props (local config only, not shared state)
Use createStore for complex state objects in SolidJS
Use createMemo for derived values in SolidJS
Move business logic to stores, utilities, or primitives - not components
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state - import stores directly

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/**/*.{ts,tsx,js,jsx}

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

packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention // TODO(agent): Brief description for incomplete work, flagging items for future attention, known limitations, and documentation section references

Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `pdfPreviewStore` from `@/stores/pdfPreviewStore.js` for managing PDF preview state (openPreview, closePreview, getPreview methods)

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use PDF operations from `useProject` hook instead of bypassing through direct API calls

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2026-01-17T16:10:07.531Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.531Z
Learning: Applies to packages/**/*.{ts,tsx,js,jsx} : Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2026-01-17T00:25:35.716Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.716Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
🧬 Code graph analysis (1)
packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx (3)
packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js (4)
  • annotationCapability (41-41)
  • isApplyingRef (43-43)
  • annotationMapRef (44-44)
  • annotationScope (47-47)
packages/web/src/components/checklist/ChecklistYjsWrapper.jsx (2)
  • initialAnnotations (411-429)
  • selectedPdfId (54-54)
packages/web/src/components/pdf/embedpdf/preact/src/config/types.ts (1)
  • SidebarState (38-41)
⏰ 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 (5)
packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx (5)

38-42: LGTM!

The annotation plugin imports are correctly structured and all imported symbols are used within the file.


267-275: LGTM!

The AnnotationData type captures the essential fields for annotation persistence. Using any for embedPdfData is pragmatic given it stores third-party library data structures.


284-289: LGTM!

The new annotation persistence props are well-structured as optional additions to the viewer interface.


354-357: LGTM!

Disabling autoCommit is the correct approach for external persistence via Y.js. The inline comment clearly explains the rationale.


543-558: LGTM!

The conditional rendering logic correctly gates the AnnotationSyncManager based on the presence of annotation-related props. The component properly receives all necessary context to manage the sync lifecycle.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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.

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: 3

🤖 Fix all issues with AI agents
In `@packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx`:
- Around line 154-163: importAnnotations is being called synchronously and
isApplyingRef.current is reset immediately, causing a race with async annotation
events; wrap the call in an async function, await
annotationScope.importAnnotations(...) (or wrap its callback into a Promise if
it doesn't return one), and move isApplyingRef.current = false into a finally
block so it's only cleared after import completes; also add a local
cancelled/isMounted flag and cleanup on unmount to ignore late events (or abort)
so import won't flip the guard after the component unmounts—update the block
around annotationScope.importAnnotations, isApplyingRef, and any surrounding
effect to use async/await with try/finally and cleanup.

In `@packages/web/src/primitives/useProject/annotations.js`:
- Around line 69-79: The function addAnnotation currently documents userId as
required but silently defaults it to 'unknown' when missing; either make userId
truly required by adding it to the existing required-parameter check (alongside
studyId, pdfId, checklistId, annotationData) and return/null-warn when absent,
or update the JSDoc for addAnnotation to mark userId as optional and keep the
fallback behavior—pick one approach and make the validation and JSDoc consistent
for addAnnotation and its callers.
- Around line 287-302: Merged annotations copy embedPdfData from the source but
do not update its internal id to the newly generated newAnnotationId, causing
annotation.id !== annotation.embedPdfData.id; fix this in the merge code that
builds newAnnotationYMap by, when data.embedPdfData exists, creating/using a
copy of embedPdfData and setting its id to newAnnotationId (mirroring the
behavior in addAnnotation) before calling newAnnotationYMap.set('embedPdfData',
...), so newAnnotationId and embedPdfData.id stay in sync.
🧹 Nitpick comments (9)
packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx (2)

107-161: Remove debug logging before merging.

The extensive console.log statements throughout AnnotationSyncManager (lines 107-111, 120-121, 125, 129, 144, 156-158, 170, 174, 178, 189, 194, 208, 222, 231) are useful for development but should be removed or guarded behind a debug flag before production release.

Example cleanup for the load effect
   useEffect(() => {
-    console.log('[AnnotationSync] Load effect triggered:', {
-      hasAnnotationScope: !!annotationScope,
-      initialAnnotationsCount: initialAnnotations?.length || 0,
-      initialAnnotations,
-    });

     if (!annotationScope || !initialAnnotations?.length) {
       return;
     }

     // Filter to only annotations we haven't loaded yet
     const annotationsToLoad = initialAnnotations.filter(annotation => {
       if (!annotation.embedPdfData) {
-        console.log('[AnnotationSync] Skipping annotation without embedPdfData:', annotation);
         return false;
       }
       const annotationId = annotation.id || annotation.embedPdfData.id;
       const alreadyLoaded = loadedAnnotationIdsRef.current.has(annotationId);
-      console.log('[AnnotationSync] Annotation:', annotationId, 'alreadyLoaded:', alreadyLoaded);
       return annotationId && !alreadyLoaded;
     });
-
-    console.log('[AnnotationSync] Annotations to load:', annotationsToLoad.length);
     // ... rest of the function

252-260: Consider exporting AnnotationData type for reuse.

The AnnotationData type is declared internally but is also used in ViewerPageProps.initialAnnotations. If external callers need to construct this type, consider exporting it for better type safety across the annotation system.

packages/web/src/primitives/useProject/sync.js (1)

214-224: Consider validating required annotation fields.

If pdfId, type, or pageIndex are missing, the annotation may be unusable downstream. Consider skipping invalid annotations or logging a warning.

Optional validation
+      // Skip annotations missing required fields
+      if (!annotationData.pdfId || annotationData.pageIndex === undefined) {
+        console.warn('Skipping annotation with missing required fields:', annotationId);
+        continue;
+      }
+
       checklistAnnotations.push({
         id: annotationData.id || annotationId,
         pdfId: annotationData.pdfId,
packages/web/src/components/checklist/ChecklistYjsWrapper.jsx (1)

415-420: Remove debug logging before merging.

Multiple console.log statements in the annotation handlers and memo should be removed for production.

Lines to remove
   const initialAnnotations = createMemo(() => {
     const study = currentStudy();
     const pdfId = selectedPdfId();
     const checklistId = params.checklistId;
-    console.log('[ChecklistYjsWrapper] initialAnnotations memo:', {
-      hasStudy: !!study,
-      studyAnnotations: study?.annotations,
-      pdfId,
-      checklistId,
-    });
     if (!study?.annotations || !pdfId || !checklistId) return [];
     // ...
-    console.log('[ChecklistYjsWrapper] Filtered annotations:', filtered);
     return filtered;
   });

   const handleAnnotationAdd = annotation => {
-    console.log('[ChecklistYjsWrapper] handleAnnotationAdd called:', annotation);
     if (isReadOnly()) {
-      console.log('[ChecklistYjsWrapper] Read-only mode, skipping');
       return;
     }
     const pdfId = selectedPdfId();
     if (!pdfId) {
-      console.log('[ChecklistYjsWrapper] No pdfId, skipping');
       return;
     }
-
-    console.log('[ChecklistYjsWrapper] Calling addAnnotation with:', {
-      studyId: params.studyId,
-      pdfId,
-      checklistId: params.checklistId,
-      annotation,
-      userId: user()?.id,
-    });
     const result = addAnnotation(params.studyId, pdfId, params.checklistId, annotation, user()?.id);
-    console.log('[ChecklistYjsWrapper] addAnnotation result:', result);
   };

Also applies to: 433-436, 440-441, 444-452

packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js (2)

178-181: isLoaded will not trigger re-renders.

The isLoaded property returns loadedRef.current directly, which is a plain value at call time. Changes to the ref won't cause consuming components to re-render. If consumers need reactive loaded state, consider using useState instead:

-  const loadedRef = useRef(false);
+  const [isLoaded, setIsLoaded] = useState(false);

// Then in loadAnnotations:
-  loadedRef.current = true;
+  setIsLoaded(true);

// And in reset effect:
-  loadedRef.current = false;
+  setIsLoaded(false);

// Return:
   return {
     loadAnnotations,
-    isLoaded: loadedRef.current,
+    isLoaded,
   };

32-89: Consider consolidating with AnnotationSyncManager in viewer.tsx.

This hook and AnnotationSyncManager (viewer.tsx lines 69-244) implement very similar logic for:

  • Tracking loaded annotations via refs/maps
  • Loading initial annotations
  • Subscribing to annotation events
  • Skipping events during loading

The key difference is that AnnotationSyncManager uses importAnnotations() while this hook uses createAnnotation(). Consider whether one implementation can serve both purposes to reduce maintenance burden.

packages/web/src/primitives/useProject/annotations.js (3)

70-76: Remove debug console.log statements before merging.

These verbose debug logs will clutter production console output. Consider removing them or gating behind a debug flag.

Proposed fix
  function addAnnotation(studyId, pdfId, checklistId, annotationData, userId) {
-    console.log('[annotations.js] addAnnotation called:', {
-      studyId,
-      pdfId,
-      checklistId,
-      annotationData,
-      userId,
-    });
    if (!studyId || !pdfId || !checklistId || !annotationData) {
      console.warn('addAnnotation: missing required parameters');
      return null;
    }

    const checklistAnnotationsMap = getChecklistAnnotationsMap(studyId, checklistId, true);
-    console.log('[annotations.js] checklistAnnotationsMap:', checklistAnnotationsMap);
    if (!checklistAnnotationsMap) return null;

    // ... rest of function ...

    checklistAnnotationsMap.set(annotationId, annotationYMap);

-    console.log('[annotations.js] Annotation stored successfully:', annotationId);
    return annotationId;
  }

Also applies to: 83-83, 103-103


23-23: Unused projectId parameter.

The projectId parameter is passed but never used within the factory. If intentionally unused, prefix it with _ like _isSynced for consistency and to signal intent.

Proposed fix
-export function createAnnotationOperations(projectId, getYDoc, _isSynced) {
+export function createAnnotationOperations(_projectId, getYDoc, _isSynced) {

181-200: Consider extracting annotation parsing logic to reduce duplication.

The annotation parsing logic (toJSON conversion, pdfId filtering, embedPdfData parsing) is duplicated between getAnnotations and getAllAnnotationsForPdf. A small helper would improve maintainability.

Example helper extraction
/**
 * Parse a single annotation Y.Map into a plain object
 * `@private`
 */
function parseAnnotationYMap(annotationYMap) {
  const data = annotationYMap.toJSON ? annotationYMap.toJSON() : annotationYMap;
  let embedPdfData = {};
  try {
    embedPdfData = JSON.parse(data.embedPdfData || '{}');
  } catch (err) {
    console.warn('Failed to parse annotation embedPdfData:', data.id, err);
  }
  return { ...data, embedPdfData };
}

Then use in both getAnnotations and getAllAnnotationsForPdf:

const annotation = parseAnnotationYMap(annotationYMap);
if (pdfId && annotation.pdfId !== pdfId) continue;
annotations.push(annotation);

Also applies to: 218-236

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca5f39 and 9066c2d.

📒 Files selected for processing (12)
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/landing/src/components/HowItWorks.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/sync.js
🧰 Additional context used
📓 Path-based instructions (17)
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Use Show and For components for conditional and list rendering in SolidJS

Files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code

Files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
**/*

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

Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples

Files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/**/*.{ts,tsx,js,jsx}

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

packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention // TODO(agent): Brief description for incomplete work, flagging items for future attention, known limitations, and documentation section references

Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
  • packages/landing/src/components/SupportedTools.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/src/**

📄 CodeRabbit inference engine (.cursor/rules/organizations.mdc)

packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/*.{js,ts,jsx,tsx}: Use apiFetch for all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
Use handleFetchError for wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions like isErrorCode from error-utils to check specific error types instead of direct string comparisons

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Never throw string literals; always throw Error objects or return domain errors from API routes

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}

📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)

Shared or cross-feature state should use external stores located in packages/web/src/stores/

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Use createFormErrorSignals for form error handling in frontend forms to manage field-level and global error states

Files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
packages/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{ts,tsx}: For UI icons, use solid-icons library or SVGs only (never emojis)
Use import aliases from jsconfig.json as defined for the project
Use Ark UI component library (@ark-ui/solid) for UI components
Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)
Do not prop-drill application state in SolidJS components; import stores directly where needed
Do not destructure props in SolidJS; access props.field directly or wrap in a function: () => props.field
Components should receive at most 1-5 props (local config only, not shared state)
Use createMemo for derived values in SolidJS
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state; import stores directly

Use import aliases from jsconfig.json as defined in the project configuration

packages/web/src/**/*.{ts,tsx}: Import stores directly and never prop-drill application state in SolidJS components
Never destructure SolidJS props - use props.field or () => props.field to preserve reactivity

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/{web,landing}/src/**/*.{ts,tsx}

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

packages/{web,landing}/src/**/*.{ts,tsx}: For UI icons, use solid-icons library (e.g., solid-icons/bi, solid-icons/fi) or SVGs only, never emojis
Ensure browser compatibility, with particular attention to Safari
Group related components in subdirectories with barrel exports
Use Ark UI components from @ark-ui/solid for UI components
Use solid-icons icon library for all icon needs
Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure SolidJS props - access props.field directly or wrap in function: () => props.field
SolidJS components should receive at most 1-5 props (local config only, not shared state)
Use createStore for complex state objects in SolidJS
Use createMemo for derived values in SolidJS
Move business logic to stores, utilities, or primitives - not components
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state - import stores directly

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
🧠 Learnings (45)
📓 Common learnings
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `addPdfToStudy` operation from `useProject` hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/** : Use scoreChecklist utility from `@/AMSTAR2/checklist.js` to determine quality assessment rating: 'High' | 'Moderate' | 'Low' | 'Critically Low'

Applied to files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/ROBINS-I/** : Use scoreChecklist utility from `@/ROBINS-I/checklist.js` to determine risk of bias rating: 'Low' | 'Moderate' | 'Serious' | 'Critical'

Applied to files:

  • packages/landing/src/components/resources/ToolResourcePage.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use PDF operations from `useProject` hook instead of bypassing through direct API calls

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `pdfPreviewStore` from `@/stores/pdfPreviewStore.js` for managing PDF preview state (openPreview, closePreview, getPreview methods)

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `addPdfToStudy` operation from `useProject` hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js
  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation

Applied to files:

  • packages/landing/src/components/FeatureShowcase.jsx
  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `cachePdf` and `getCachedPdf` from `primitives/pdfCache.js` for caching PDF data in IndexedDB

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2026-01-17T16:09:36.920Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.920Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Do NOT destructure SolidJS props - access `props.field` directly or wrap in function: `() => props.field`

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2026-01-17T16:09:36.920Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.920Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Never destructure SolidJS props as it breaks reactivity

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2026-01-17T16:10:07.531Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.531Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Never destructure SolidJS props - use `props.field` or `() => props.field` to preserve reactivity

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Always clean up SolidJS effects that create subscriptions or timers using onCleanup

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2026-01-17T00:25:12.518Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.518Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for derived/computed values in SolidJS to maintain reactivity

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createMemo for computed/derived values that depend on reactive state in SolidJS

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2026-01-17T16:09:36.920Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.920Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2026-01-17T00:25:12.518Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.518Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createStore for complex objects and arrays that need granular reactivity in SolidJS

Applied to files:

  • packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing

Applied to files:

  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure

Applied to files:

  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/AMSTAR2/checklist-compare.js} : Use compareChecklists utility from checklist-compare.js for comparing reviewer checklists

Applied to files:

  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/primitives/useProject/index.js
  • packages/landing/src/components/HowItWorks.jsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/** : Use NoteEditor component from `@/components/checklist-ui/common/NoteEditor.jsx` for question note editing with max 2000 character limit

Applied to files:

  • packages/web/src/components/checklist/ChecklistWithPdf.jsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc

Applied to files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study

Applied to files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use the `useProject` hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly

Applied to files:

  • packages/web/src/primitives/useProject/index.js
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Use getChecklistStatus utility from `@/lib/checklist-domain.js` to determine current checklist status before performing operations

Applied to files:

  • packages/web/src/primitives/useProject/index.js
  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/lib/checklist-domain.js : Register and retrieve checklists using checklist-registry: getChecklistType and getChecklistComponent functions

Applied to files:

  • packages/web/src/primitives/useProject/index.js
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to packages/workers/src/durable-objects/ProjectDoc.js, packages/web/src/primitives/useProject/** : Y.Doc structure in ProjectDoc follows a specific hierarchy: meta (Map), members (Map), reviews (Map containing studies with checklists, pdfs, and reconciliation)

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
📚 Learning: 2025-12-27T03:01:35.601Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.601Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Structure ProjectDoc Y.Doc with meta (Map), members (Map), and reviews (Map) containing checklists (Map), pdfs (Array), and reconciliation (Map)

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Always use `uploadPdf` from `api/pdf-api.js` for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/primitives/useProject/sync.js
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Never manually call syncFromYDoc() - sync from Y.Doc to store is handled automatically via ydoc.on('update')

Applied to files:

  • packages/web/src/primitives/useProject/sync.js
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/lib/checklist-domain.js} : Store reconciliation progress metadata (IDs and optional currentPage) in the study's Y.Map reconciliation property

Applied to files:

  • packages/web/src/primitives/useProject/sync.js
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js : Checklist status transitions follow sequence: in_progress → completed (when all questions answered) → reconciled (after reconciliation complete)

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2026-01-17T16:09:36.920Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.920Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `solid-icons` icon library for all icon needs

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2026-01-17T00:25:12.518Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.518Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Show and For components for conditional and list rendering in SolidJS

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's For component for rendering lists instead of Array.map()

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2026-01-17T00:25:35.716Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.716Z
Learning: Applies to packages/web/src/**/*.{ts,tsx} : Use `solid-icons` library for icons (e.g., `solid-icons/bi`, `solid-icons/fi`)

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2026-01-17T16:09:36.920Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.920Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : For UI icons, use `solid-icons` library (e.g., `solid-icons/bi`, `solid-icons/fi`) or SVGs only, never emojis

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2026-01-17T16:09:36.920Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.920Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use Ark UI components from `ark-ui/solid` for UI components

Applied to files:

  • packages/landing/src/components/HowItWorks.jsx
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/lib/checklist-domain.js,packages/web/src/AMSTAR2/checklist-compare.js} : Store final answers in the reconciled checklist itself, not in reconciliation progress metadata

Applied to files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.jsx
🧬 Code graph analysis (4)
packages/web/src/components/pdf/embedpdf/EmbedPdfViewer.jsx (2)
packages/web/src/components/checklist/ChecklistYjsWrapper.jsx (2)
  • pdfData (51-51)
  • selectedPdfId (54-54)
packages/web/src/primitives/useProject/sync.js (1)
  • pdfData (141-141)
packages/web/src/primitives/useProject/index.js (1)
packages/web/src/primitives/useProject/annotations.js (1)
  • createAnnotationOperations (23-321)
packages/web/src/primitives/useProject/annotations.js (3)
packages/web/src/primitives/useProject/index.js (1)
  • getYDoc (159-159)
packages/workers/src/durable-objects/__tests__/ProjectDoc.test.js (2)
  • studiesMap (25-25)
  • studyYMap (35-35)
packages/web/src/primitives/useProject/sync.js (3)
  • annotationsMap (178-178)
  • annotationData (204-204)
  • annotations (193-193)
packages/web/src/primitives/useProject/sync.js (1)
packages/workers/src/durable-objects/__tests__/ProjectDoc.test.js (1)
  • studyYMap (35-35)
⏰ 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

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx Outdated
Comment thread packages/web/src/primitives/useProject/annotations.js
Comment thread packages/web/src/primitives/useProject/annotations.js
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: 2

🤖 Fix all issues with AI agents
In `@packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx`:
- Around line 192-207: The event handler in viewer.tsx uses event.annotation.id
(annotationId) without checking for existence, which can write undefined into
annotationMapRef.current and call callbacks with an invalid ID; update the
'create' case to guard early: verify annotation and annotationId are present (if
not, log a warning and return), only then call
annotationMapRef.current.set(annotationId, annotation) and invoke
onAnnotationAdd/onAnnotationChange/onAnnotationDelete using the validated
annotationId so no undefined keys or callback payloads are produced.

In `@packages/web/src/primitives/useProject/annotations.js`:
- Around line 296-304: The merge logic for updating embedPdfData can throw when
JSON.parse fails, which would abort the Yjs merge; wrap the parse in a try/catch
inside the block that checks data.embedPdfData (the code that currently computes
parsed and updatedEmbed) and on parse error fall back to using data.embedPdfData
as-is or skip setting embedPdfData on newAnnotationYMap (i.e., avoid calling
newAnnotationYMap.set with a malformed JSON string), and optionally record the
parse error via your existing logging mechanism; ensure this change touches the
block referencing embedPdfData, parsed, updatedEmbed, newAnnotationId and
newAnnotationYMap.set so bad JSON no longer aborts the merge.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae15e3d and 445498e.

📒 Files selected for processing (2)
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
  • packages/web/src/primitives/useProject/annotations.js
🧰 Additional context used
📓 Path-based instructions (17)
packages/web/src/**/*.{js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)

packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from @/lib/formStatePersistence.js

Files:

  • packages/web/src/primitives/useProject/annotations.js
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

packages/web/src/**/*.{js,jsx,ts,tsx}: Always use uploadPdf from @api/pdf-api.js for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
Use cachePdf and getCachedPdf from @primitives/pdfCache.js for caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
Use importFromGoogleDrive from @api/google-drive.js for importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state using saveFormState from @/lib/formStatePersistence.js before triggering OAuth redirects for Google Drive
Use addPdfToStudy operation from useProject hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
Use removePdfFromStudy operation from useProject hook to remove PDFs from a study
Use pdfPreviewStore from @/stores/pdfPreviewStore.js for managing PDF preview state (openPreview, closePreview, getPreview methods)
Use downloadPdf from @api/pdf-api.js to download PDFs from server, then cache the result using cachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations from useProject hook instead of bypassing through direct API calls

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state

{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)

**/*.{js,jsx,ts,tsx}: Use the useProject hook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/**

📄 CodeRabbit inference engine (.cursor/rules/organizations.mdc)

packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/**/*.{js,ts,jsx,tsx}: Use apiFetch for all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
Use handleFetchError for wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions like isErrorCode from error-utils to check specific error types instead of direct string comparisons

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Never throw string literals; always throw Error objects or return domain errors from API routes

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}

📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)

Shared or cross-feature state should use external stores located in packages/web/src/stores/

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
**/*

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

Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/**/*.{ts,tsx,js,jsx}

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

packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention // TODO(agent): Brief description for incomplete work, flagging items for future attention, known limitations, and documentation section references

Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)

Use PdfViewer component from @/components/checklist-ui/pdf/PdfViewer.jsx for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS components

Use Show and For components for conditional and list rendering in SolidJS

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

Use createFormErrorSignals for form error handling in frontend forms to manage field-level and global error states

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{ts,tsx}: For UI icons, use solid-icons library or SVGs only (never emojis)
Use import aliases from jsconfig.json as defined for the project
Use Ark UI component library (@ark-ui/solid) for UI components
Use solid-icons library for icons (e.g., solid-icons/bi, solid-icons/fi)
Do not prop-drill application state in SolidJS components; import stores directly where needed
Do not destructure props in SolidJS; access props.field directly or wrap in a function: () => props.field
Components should receive at most 1-5 props (local config only, not shared state)
Use createMemo for derived values in SolidJS
Frontend uses orgSlug in URLs (/orgs/:orgSlug/...) for readability
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state; import stores directly

Use import aliases from jsconfig.json as defined in the project configuration

packages/web/src/**/*.{ts,tsx}: Import stores directly and never prop-drill application state in SolidJS components
Never destructure SolidJS props - use props.field or () => props.field to preserve reactivity

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/web/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
packages/{web,landing}/src/**/*.{ts,tsx}

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

packages/{web,landing}/src/**/*.{ts,tsx}: For UI icons, use solid-icons library (e.g., solid-icons/bi, solid-icons/fi) or SVGs only, never emojis
Ensure browser compatibility, with particular attention to Safari
Group related components in subdirectories with barrel exports
Use Ark UI components from @ark-ui/solid for UI components
Use solid-icons icon library for all icon needs
Do NOT prop-drill application state in SolidJS - import stores directly where needed
Do NOT destructure SolidJS props - access props.field directly or wrap in function: () => props.field
SolidJS components should receive at most 1-5 props (local config only, not shared state)
Use createStore for complex state objects in SolidJS
Use createMemo for derived values in SolidJS
Move business logic to stores, utilities, or primitives - not components
Never destructure SolidJS props as it breaks reactivity
Never prop-drill shared state - import stores directly

Files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to packages/web/src/components/checklist-ui/compare/** : Use Y.Text objects for collaborative editing of question notes in reconciliation
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `addPdfToStudy` operation from `useProject` hook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/primitives/useProject/checklists.js : Store question notes as Y.Text objects obtained from useProject hook via getQuestionNote operation to enable collaborative editing

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use PDF operations from `useProject` hook instead of bypassing through direct API calls

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to packages/workers/src/durable-objects/ProjectDoc.js, packages/web/src/primitives/useProject/** : Y.Doc structure in ProjectDoc follows a specific hierarchy: meta (Map), members (Map), reviews (Map containing studies with checklists, pdfs, and reconciliation)

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:01:35.601Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/durable-objects.mdc:0-0
Timestamp: 2025-12-27T03:01:35.601Z
Learning: Applies to packages/workers/src/durable-objects/**/ProjectDoc.{js,ts} : Structure ProjectDoc Y.Doc with meta (Map), members (Map), and reviews (Map) containing checklists (Map), pdfs (Array), and reconciliation (Map)

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/AMSTAR2/**,packages/web/src/ROBINS-I/**,packages/web/src/lib/checklist-domain.js,packages/web/src/primitives/useProject/checklists.js : Use checklist operations from useProject hook (createChecklist, updateChecklistAnswer, getChecklistData) instead of manually updating checklist structure

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Always use `uploadPdf` from `api/pdf-api.js` for uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to packages/web/src/primitives/useProject/**, packages/web/src/stores/projectStore.js : Never manually call syncFromYDoc() - sync from Y.Doc to store is handled automatically via ydoc.on('update')

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/lib/checklist-domain.js,packages/web/src/AMSTAR2/checklist-compare.js} : Reconciliation structure should contain checklist1Id, checklist2Id, and reconciledChecklistId

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:50.087Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/yjs-sync.mdc:0-0
Timestamp: 2025-12-27T03:02:50.087Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:14.854Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/reconciliation.mdc:0-0
Timestamp: 2025-12-27T03:02:14.854Z
Learning: Applies to {packages/web/src/components/checklist-ui/compare/**,packages/web/src/lib/checklist-domain.js} : Store reconciliation progress metadata (IDs and optional currentPage) in the study's Y.Map reconciliation property

Applied to files:

  • packages/web/src/primitives/useProject/annotations.js
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{jsx,tsx} : Use `PdfViewer` component from `@/components/checklist-ui/pdf/PdfViewer.jsx` for displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `pdfPreviewStore` from `@/stores/pdfPreviewStore.js` for managing PDF preview state (openPreview, closePreview, getPreview methods)

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `cachePdf` and `getCachedPdf` from `primitives/pdfCache.js` for caching PDF data in IndexedDB

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2026-01-17T16:10:07.531Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.531Z
Learning: Applies to packages/**/*.{ts,tsx,js,jsx} : Use TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
📚 Learning: 2026-01-17T00:25:35.716Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.716Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references

Applied to files:

  • packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
🧬 Code graph analysis (1)
packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx (2)
packages/web/src/components/pdf/embedpdf/preact/src/hooks/useAnnotationSync.js (4)
  • annotationCapability (41-41)
  • isApplyingRef (43-43)
  • annotationMapRef (44-44)
  • annotationScope (47-47)
packages/web/src/components/pdf/embedpdf/preact/src/config/types.ts (1)
  • SidebarState (38-41)
⏰ 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 (3)
packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx (2)

349-352: Annotation plugin registration aligns with external persistence.

Disabling auto-commit is consistent with persisting annotations to Y.js rather than mutating the PDF file.


538-552: Good gating for AnnotationSyncManager.

Conditionally mounting only when a document is active and annotation hooks are present avoids unnecessary subscriptions.

packages/web/src/primitives/useProject/annotations.js (1)

93-97: Embed data id normalization on creation is solid.

This keeps stored embedPdfData aligned with the generated annotation ID.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/web/src/components/pdf/embedpdf/preact/src/viewer.tsx
Comment thread packages/web/src/primitives/useProject/annotations.js
@InfinityBowman InfinityBowman merged commit d5076c7 into main Jan 26, 2026
2 checks passed
@InfinityBowman InfinityBowman deleted the 202-pdf-annotations branch January 26, 2026 17:12
@coderabbitai coderabbitai Bot mentioned this pull request Mar 29, 2026
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 Annotations

2 participants