中文用户请阅读中文版:README_zh.md
Add page-margin annotation cards to Obsidian's built-in PDF reader: select text, highlight or underline it, and a draggable card with your note appears in the page margin, connected to the mark with a dashed line. Annotations are stored as Markdown notes in your vault (one managed note per PDF), so they sync, search, and reference like any other note.
- Desktop only (
isDesktopOnly: true)
- Minimum Obsidian version: 1.12.0
- Current version: 0.1.2
Features
Annotating
- Highlight / underline selected text via the toolbar colors, commands, or right-click menu.
- Margin cards: every annotation gets a card in the left or right margin of its page, showing the quoted source text and your note.
- Connector: a dashed SVG thread links the card to the highlighted text; hovering a card highlights the connector.
- Multiple colors: configurable color set (default yellow/blue/green/red, up to 6), one color per annotation.
Cards
- Hover to select: hovering a card selects it (tinted background + colored border).
- Free drag: drag a card by its grip handle anywhere in the page neighborhood (margins or over the page); the position is persisted per annotation.
- Auto layout: undragged cards push down in reading order; dragged cards are pinned and others route around them.
- Double-click the grip resets to auto layout.
- Click the highlight in the text to flash and locate its card.
Card / popover display
- Card: persistent margin card (default).
- Popover: mark-only; hovering shows the same card near the text with a configurable grace period; click inside to pin, Esc or click outside to close.
- Auto-downgrade: cards that don't fit in a narrow margin fall back to popover automatically.
- Switch a single annotation between forms (card footer button), or convert all annotations in the current PDF from the toolbar.
Editing
- Click card content to edit; save with the button or
Cmd/Ctrl+Enter, cancel with Esc.
- "Highlight and annotate" / "Underline and annotate" open the edit box immediately (configurable).
- Card footer (on hover): switch color, highlight/underline, card/popover, copy portable annotation, delete.
Persistence & sync
- Annotations are Markdown: each PDF has one managed note
<PDF name>.annotations.md in <PDF folder>/Reader Margins/ by default (configurable; . = same folder as the PDF). The note's frontmatter (reader-margins / reader-margins-pdf) marks ownership; each annotation is one blockquote.
- Survives sessions and syncs with your vault: annotations are plain notes, visible to Obsidian search, graph, and sync; renaming a PDF updates links automatically.
- Optimistic concurrency: when two windows edit the same annotation, the later writer gets a conflict notice and your draft is preserved.
- Save status (saving/failed) is shown in the toolbar; deleting an annotation is undoable.
Annotation index
Open the index view from the toolbar or ribbon: search all annotations by PDF, quote, note, or source note; click to jump to the exact selection in the PDF.
Commands
- Highlight selected text (active color)
- Highlight and annotate selected text
- Underline selected text (active color)
- Underline and annotate selected text
- Save annotation edit box (default
Mod+Enter)
- Open Reader Margins annotations
Installation
Install from the Obsidian community plugin directory: Settings → Community plugins → Browse → search for "Reader Margins", then click Install.
Alternatively, build from source:
git clone <repo-url>
cd obsidian-reader-margins
npm install
npm run build # outputs main.js (+ manifest.json, styles.css)
Copy these files into your vault's plugin folder:
<vault>/.obsidian/plugins/reader-margins/
├── main.js
├── manifest.json
└── styles.css
Then enable it in Obsidian: Settings → Community plugins → disable Restricted mode → enable "Reader Margins".
Development
npm run verify # all tests + build + diff checks (run before committing)
npm run dev # esbuild watch
npm test # run unit / host-contract tests (vitest)
npm run test:watch # watch mode
Reload with Cmd/Ctrl+P → "Reload app without saving" after rebuilding.
Usage
- Open a PDF in Obsidian.
- Select text.
- Annotate: pick a color and use the highlight/underline buttons in the PDF toolbar, the right-click menu, or the commands palette.
- The annotation card appears in the margin (or as a popover, per settings); hover to drag, edit, recolor, or delete.
- Open the index view from the toolbar to browse/search all annotations.
A dismissible hint pill appears when you first open a PDF without annotations.
Settings
Settings → Reader Margins:
- Language: auto (follows Obsidian) / 中文 / English.
- Annotation folder: per-PDF folder for annotation notes (default
Reader Margins; . = same folder as the PDF; .. allowed, e.g. ../../test for a shared vault-level folder).
- Auto-open edit box: open the edit box immediately for "annotate" actions.
- Default display mode: card or popover for new annotations.
- Popover grace (ms): how long the popover stays after the pointer leaves the mark (default 180).
- Default color for selection actions.
- Annotation colors: add/rename/change colors (validated
#RRGGBB, up to 6; default colors can't be deleted).
- Reset to defaults (with confirmation).
Design
Visual system: Atomic Minimalism — flat, shadowless, 1px borders, tonal layering, 4px/8px radii, Hanken Grotesk. Color is restrained; annotation colors are used for state and identity only, not decoration.
Architecture
Layered and testable: pure logic is isolated from Obsidian/PDF.js host access. Annotations persist in vault Markdown notes; data.json stores settings only (schema v2).
src/
├── main.ts # plugin entry: settings, commands, ribbon, rename handling
├── domain/ # pure domain logic (no DOM/host dependencies)
├── markdown/ # Markdown storage (durable annotation authority)
├── store/ # settings schema/store, persistence coordinator, repository
├── host/ # defensive Obsidian/PDF.js private access (fails closed)
├── session/ # per-view lifecycle: rendering, drag, popover, editing, context menu
├── render/ # idempotent DOM projection: marks, cards, connectors, layout
├── toolbar/ # PDF toolbar (colors, highlight/underline, index, convert, status)
├── view/ # annotation index view
├── settings/ # settings tab
├── i18n/ # en/zh strings
└── diagnostics/ # diagnostics for DevTools
Key design points:
- Annotations are Markdown: each annotation is a blockquote in a managed note; multi-window conflicts are detected via block uniqueness and revision numbers; deletes are undoable through tombstones.
- Pure logic is testable: domain, layout, rendering, and codecs are pure functions tested with vitest/jsdom; Obsidian private access is confined to
host/.
- Stable coordinates: card positions are stored page-local and unscaled (
page-css-v2), stable across zoom and scroll.
- Resolvable anchors: annotations store a text locator plus quote; reopening or reflow resolves them against the live text layer; unresolved annotations aren't drawn.
- Reconciled rendering: DOM updates are batched via requestAnimationFrame.
Tests live in src/tests/ (unit + host-contract).
Limitations
- Desktop only.
- Card drag is limited to the page neighborhood (no overlap into other pages; horizontal range spans margin and page).
- Neighbors don't re-layout live while dragging (layout runs on drop).
- Rotated PDFs (non-0°) aren't supported yet.
- Depends on the built-in PDF reader's internal DOM; major Obsidian updates may require adapting
host/.
- Anchors are text-layer based (locator + quote); scanned/image-only PDFs without a text layer can't be precisely located.
License
MIT. Free to copy, modify, distribute, and use commercially; keep the copyright notice and this license notice.