Skip to content

Save PDF annotations by naming EmbedPDF's numeric subtype - #856

Merged
InfinityBowman merged 1 commit into
mainfrom
fix/pdf-annotation-subtype
Sep 22, 2026
Merged

InfinityBowman merged 1 commit into
mainfrom
fix/pdf-annotation-subtype

Conversation

@InfinityBowman

@InfinityBowman InfinityBowman commented Sep 22, 2026

Copy link
Copy Markdown
Owner

Fixes #855.

The bug

EmbedPDF reports an annotation's subtype as a numeric enum (PdfAnnotationSubtype.HIGHLIGHT = 9), and the viewer's ...annotation spread lands that number on the payload's type. The synced annotations.type column is a string, so the sync client's args check rejected every annotation.add and annotation.update with InvalidArgs before they left the browser. Reviewers got a "Change rejected" toast and no highlight was ever stored - all four active production projects export zero annotations rows. Broken since the sync-engine migration in ba6f6c8c.

The fix

annotationTypeName() maps the subtype to its EmbedPDF name (highlight, ink, ...) at both call sites. embedPdfData still carries the raw numeric type, so importing a stored annotation back into the viewer is untouched - which matters, because the restore path rebuilds annotations from embedPdfData alone.

Verified in the running app

Seeded a local project, uploaded a PDF, opened the appraisal, and drove the viewer through agent-browser:

  • Drew a highlight: accepted, stored as type: "highlight" with embedPdfData holding "type":9.
  • Reloaded: the highlight re-imported and rendered in place.
  • Drew a freehand ink annotation and dragged it: both annotation.add and annotation.update landed, updatedAt advanced, stored as type: "ink".
  • Zero client.sync.mutation_rejected lines across the whole run.

Unit tests cover the mapping and assert the pre-fix numeric payload is exactly what the mutator's schema rejects. pnpm typecheck, pnpm lint and the web suite (325 tests) pass.

Noticed, not fixed

Dragging the ink annotation logs a React "two children with the same key" warning from the viewer's annotation layer. It predates this change and is unrelated to persistence.

https://claude.ai/code/session_01H77BMkZ9GhK8D98TG4cpTg

Summary by CodeRabbit

  • Bug Fixes
    • Annotation types are now handled consistently when annotations are added or updated.
    • Outcome chips now expand to fit longer names, which wrap onto additional lines instead of being cut off. Remove controls retain a consistent size.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 2deda128-cd25-4c02-96e4-0162ed6a4e26

📥 Commits

Reviewing files that changed from the base of the PR and between 336762b and ec2c90d.

📒 Files selected for processing (3)
  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/lib/annotationType.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: c34a5d7e-aa49-4d82-8550-806e562025b8

📥 Commits

Reviewing files that changed from the base of the PR and between dc1117c and 336762b.

📒 Files selected for processing (4)
  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/lib/annotationType.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test-server
🧰 Additional context used
📓 Path-based instructions (6)
Use shadcn/ui for UI components (Radix-based, in `@/components/ui/`)

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

Files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
Path aliases: `@/` maps to `packages/web/src/`

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

Files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
  • packages/web/src/lib/annotationType.ts
Use lucide-react for the icon library Use TanStack Query for server state management (`useQuery`, `useMutation`) Import Zustand stores directly from `@/stores/` instead of prop-drilling shared state Avoid `useMemo` or `useCallback` - let th...

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

Files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
  • packages/web/src/lib/annotationType.ts
Use import aliases from tsconfig.json Code comments should explain why something is being done or provide context, not repeat what the code is saying Use TODO(agent) pattern for incomplete work or flagging items for future attention, with b...

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

Files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
  • packages/web/src/lib/annotationType.ts
For UI icons, use `lucide-react` library or SVGs only (never emojis)

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

Files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
  • packages/web/src/lib/annotationType.ts
NEVER use emojis anywhere - not in code, comments, documentation, plan files, commit messages, or examples.

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

Files:

  • packages/web/src/components/checklist/ChecklistYjsWrapper.tsx
  • packages/web/src/lib/__tests__/annotationType.test.ts
  • packages/web/src/components/project/setup/ProjectSetupCard.tsx
  • packages/web/src/lib/annotationType.ts
🔇 Additional comments (4)
packages/web/src/components/project/setup/ProjectSetupCard.tsx (1)

153-153: LGTM!

Also applies to: 157-159, 164-164

packages/web/src/components/checklist/ChecklistYjsWrapper.tsx (1)

14-14: LGTM!

Also applies to: 50-51, 286-286, 303-303

packages/web/src/lib/annotationType.ts (1)

1-13: LGTM!

packages/web/src/lib/__tests__/annotationType.test.ts (1)

1-48: LGTM!


📝 Walkthrough

Walkthrough

Annotation types are normalized before sync mutations. Outcome chips now wrap long names and use content-sized layouts.

Changes

Annotation Type Normalization

Layer / File(s) Summary
Normalize annotation types
packages/web/src/lib/annotationType.ts, packages/web/src/components/checklist/ChecklistYjsWrapper.tsx, packages/web/src/lib/__tests__/annotationType.test.ts
annotationTypeName converts numeric types to enum names or numeric strings, preserves string types, and returns an empty string for other inputs. The add and update callbacks pass the normalized type to sync mutators. Tests cover conversion and mutator validation.

Outcome Chip Layout

Layer / File(s) Summary
Adjust outcome chip sizing
packages/web/src/components/project/setup/ProjectSetupCard.tsx
Outcome chips use top-aligned, content-sized layouts. Outcome names wrap, and remove buttons do not shrink.

Priority: ⬆️ High

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix · Severity of issue fixed: High

Merge Risk: ⚪ Minimal · up to 33676

Annotation subtype normalization satisfies the sync mutation contract while preserving raw restoration data. No current merge-blocking risk was identified.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning For #855, annotationTypeName converts numeric subtypes before annotation.add and annotation.update. The new tests check known subtype names and mutator validation. The wrapper still serializes t… In packages/web/src/components/pdf/embedpdf/react/src/viewer.tsx, place ...annotation before the authoritative id, type, and pageIndex fields, or remove the spread. Add coverage that verifies those fields cannot be overwritten in …
Out of Scope Changes check ⚠️ Warning The ProjectSetupCard.tsx changes alter outcome-chip alignment, wrapping, and remove-button sizing. They do not support saving, syncing, or restoring PDF annotations in #855. Remove the unrelated ProjectSetupCard.tsx layout changes from this pull request, or link them to a relevant issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: converting EmbedPDF’s numeric annotation subtype to its name before saving annotations.
Full details: Linked Issues check

Explanation

For #855, annotationTypeName converts numeric subtypes before annotation.add and annotation.update. The new tests check known subtype names and mutator validation. The wrapper still serializes the original annotation into embedPdfData. However, viewer.tsx still builds both create and update payloads with id, type, and pageIndex before ...annotation. That spread can overwrite those fields, contrary to #855's explicit requirement.

Resolution

In packages/web/src/components/pdf/embedpdf/react/src/viewer.tsx, place ...annotation before the authoritative id, type, and pageIndex fields, or remove the spread. Add coverage that verifies those fields cannot be overwritten in create and update payloads.

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

EmbedPDF reports an annotation's subtype as a numeric enum (HIGHLIGHT = 9),
and the viewer's `...annotation` spread put that number on the payload's
`type`. The synced `annotations.type` column is a string, so every
`annotation.add` and `annotation.update` failed the client's args check with
InvalidArgs and never reached the Durable Object: no highlight has ever been
saved in production, and reviewers just saw a "Change rejected" toast.

Name the subtype before the mutation. `embedPdfData` keeps the numeric type,
so importing a stored annotation back into the viewer is unchanged.

Fixes #855

Claude-Session: https://claude.ai/code/session_01H77BMkZ9GhK8D98TG4cpTg
@InfinityBowman
InfinityBowman force-pushed the fix/pdf-annotation-subtype branch from 336762b to ec2c90d Compare September 22, 2026 23:20
@InfinityBowman
InfinityBowman merged commit 61a8e2b into main Sep 22, 2026
10 checks passed
@InfinityBowman
InfinityBowman deleted the fix/pdf-annotation-subtype branch September 22, 2026 23:23
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 never save: EmbedPDF's numeric subtype fails the mutator's string schema

1 participant