91 lines
5.4 KiB
Markdown
91 lines
5.4 KiB
Markdown
# Annotator & Reader Fixes Reference
|
|
|
|
## Annotation System Architecture
|
|
|
|
### Key Components
|
|
- `Annotator.tsx` - Annotation lifecycle, popup display, style/color management
|
|
- `AnnotationRangeEditor.tsx` - Drag handles for adjusting selection range
|
|
- `MagnifierLoupe.tsx` - Magnifying glass during handle drag (mobile only)
|
|
- `useTextSelector.ts` - Text selection detection and processing
|
|
- `useAnnotationEditor.ts` - Editing existing annotations
|
|
- `useInstantAnnotation.ts` - Creating new annotations on selection
|
|
|
|
### Highlight Rendering
|
|
- Highlights rendered by foliate-js `Overlayer` (SVG overlayer in paginator shadow DOM, not iframe)
|
|
- Each view in multiview paginator has its own `Overlayer` instance with unique clipPath ID
|
|
- `Overlayer.add()` stores range + draw function; `redraw()` recalculates positions from stored ranges
|
|
- Colors stored as color names mapped to custom hex via `globalReadSettings.customHighlightColors`
|
|
- Sidebar uses `color-mix()` CSS function with custom colors, not Tailwind utility classes (#3273)
|
|
- Rounded highlight style supported via `vertical` option passed to overlayer (#3208)
|
|
|
|
### Multiview Overlayer Pitfalls
|
|
- **Duplicate SVG IDs**: Each overlayer creates `<clipPath>` for loupe hole — IDs MUST be unique per instance or `url(#id)` resolves to wrong element, clipping everything
|
|
- **docLoadHandler scope**: `FoliateViewer.tsx` re-adds annotations on `load` event — MUST filter by `detail.index` (loaded section), not re-add ALL annotations (overwrites drag edits)
|
|
- **MagnifierLoupe lifecycle**: Don't destroy/recreate loupe on every drag tick — `hideLoupe()` should only run on unmount, `showLoupe()` fast path updates position only
|
|
- **Stale closures in useTextSelector**: `getProgress()` must be called inside callbacks, not captured at hook top-level (useFoliateEvents deps are `[view]` only)
|
|
|
|
## Fix History
|
|
|
|
| Issue | Problem | Root Cause | Fix |
|
|
|-------|---------|------------|-----|
|
|
| #3286 | Selection stuck on first annotation | `initializedRef` guard blocked re-computation | Remove guard, consolidate style/color effects |
|
|
| #3273 | Custom colors not in sidebar | Hardcoded Tailwind classes | Use inline `style` with `color-mix()` |
|
|
| #3234 | Letter-by-letter selection on mobile | No word boundary snapping | Add `snapRangeToWords()` using `Intl.Segmenter` |
|
|
| #3208 | Hard rectangular highlights | No border radius support | Pass `vertical` option, update foliate-js |
|
|
| #3002 | Can't see text under finger | No magnification UI | New `MagnifierLoupe` component using `view.renderer.showLoupe()` |
|
|
| #3082 | No page numbers on annotations | `pageNumber` field missing | Add `pageNumber` to BookNote type, compute on create |
|
|
| #3225 | Android tools unresponsive | Premature `makeSelection()` call | Remove premature re-selection in Android path |
|
|
|
|
## Common Annotation Bugs
|
|
|
|
### Selection Issues
|
|
- **Word snapping**: Uses `Intl.Segmenter` with `granularity: 'word'` to snap selection to word boundaries
|
|
- **Android re-selection**: Don't call `makeSelection(sel, index, true)` immediately on pointer-up; let the popup flow complete
|
|
- **Range editor handles**: Remove `initializedRef` guards that prevent re-computation when switching annotations
|
|
|
|
### Color/Style Issues
|
|
- **Custom colors in sidebar**: Use inline `style={{ backgroundColor: 'color-mix(...)' }}` not Tailwind classes
|
|
- **Style synchronization**: Consolidate `selectedStyle` and `selectedColor` into one `useEffect`
|
|
- **Switching annotations**: Must call `setShowAnnotPopup(false)` and `setEditingAnnotation(null)` before setting up new annotation
|
|
|
|
## Reader/Content Fixes
|
|
|
|
### Progress Display
|
|
- Use physical `view.renderer.page` and `view.renderer.pages` for page counts (#3213, #3200)
|
|
- Last page shows 100% by fixing boundary condition (#3383)
|
|
- FB2 subsections need special handling for progress (#3136)
|
|
|
|
### Translation View (#3078)
|
|
- Problem: Page jumps back during full-text translation
|
|
- Root cause: DOM mutations from sequential translation insertions cause paginator relayout
|
|
- Fix: Batch DOM updates with 50ms timer, use bounded concurrent queue (max 5), show loading overlay
|
|
|
|
### TOC Navigation (#3124)
|
|
- Problem: Expanding TOC chapter scrolls back to current chapter
|
|
- Fix: Only scroll-into-view on navigation, not on expand/collapse
|
|
|
|
## Accessibility (a11y) Fixes
|
|
|
|
### Screen Reader (TalkBack) Support
|
|
- **Page indicator updates** (#2276): Add focus handlers on `<p>` elements that call `view.goTo(cfi)` to update position
|
|
- **Navigation buttons** (#3036): Always show prev/next buttons when screen reader active; `PageNavigationButtons.tsx`
|
|
- **Dropdown menus** (#3035): Use `DropdownContext` with overlay dismiss instead of blur-based closing
|
|
|
|
### Dropdown Architecture for a11y
|
|
- `DropdownContext` (`src/context/DropdownContext.tsx`) manages which dropdown is open globally
|
|
- Uses `useId()` for unique identification
|
|
- One dropdown open at a time
|
|
- `<Overlay>` for dismissal (tap/click outside) instead of `onBlur`
|
|
- `<details>` element with `open={isOpen}` for semantic structure
|
|
- No auto-focus-first-item (conflicts with TalkBack)
|
|
|
|
## E-ink Readability
|
|
- Use `not-eink:` Tailwind variant for colors and opacity (#3258)
|
|
- Don't use `text-primary` (blue) or low opacity on e-ink
|
|
- Highlights use foreground color in dark mode e-ink (#3299)
|
|
|
|
## Key Utility Functions
|
|
- `snapRangeToWords()` in `src/utils/sel.ts` - Word boundary snapping
|
|
- `handleAccessibilityEvents()` in `src/utils/a11y.ts` - Screen reader focus handling
|
|
- `color-mix()` CSS function for custom highlight colors with opacity
|