The Problem
Users of Obsidian need to annotate PDFs without leaving the markdown workflow. The built‑in viewer lacks native highlight linking, and existing plugins either replace the viewer or store annotations in opaque JSON, causing loss of data when the plugin fails or Obsidian updates.
What This Does
obsidian-pdf-plus (PDF++) augments the native PDF viewer with markdown‑based highlights and a Vim‑style command layer. Core logic lives under src/lib/ (highlights, commands, workspace helpers) while UI glue sits in src/modals/, src/patchers/, and src/vim/. For example, src/lib/highlights/index.ts parses link‑backed selections, and src/lib/highlights/write-file/index.ts writes the resulting annotation to a side‑car markdown file. Optional Vim bindings are implemented in src/vim/vim.ts and related modules.
How It Is Wired
Execution begins when Obsidian loads the plugin and calls the exported onload in src/main.ts.
onloadregisters the plugin (addSetting,registerCommands) and creates thePDFBacklinkVisualizer(src/backlink-visualizer.ts).- User interaction (e.g., clicking a PDF link) triggers
registerPDFEventinsrc/lib/index.ts, which routes togetPage(called from 32 places) and then toaddToggleSettingvia the UI builder (display). - Highlight creation follows the call chain:
display → addToggleSetting → setAnnotationContents(entry point insrc/lib/highlights/write-file/index.ts:95), which writes a markdown file using the ObsidianFileSystemAdapter.mkdircall (filesystem touch). - Vim commands flow through the cycle
src/vim/vim.ts → src/vim/mode.ts → src/vim/scope.ts, all of which are part of an import cycle (11 members). The most connected hub issrc/vim/vim.ts(7 incoming, 8 outgoing imports, instability 0.53). - UI patches (
src/patchers/pdf-internals.ts,src/patchers/pdf-view.ts) hook into Obsidian’s private PDF APIs; they are invoked from theonloadregistration and from theregisterPDFEventpath.
The internal call graph shows a few high‑impact functions: register (35 callers), getPage (32 callers), and addSetting (27 callers). Changes to these functions propagate widely across the codebase. The most massive files—src/patchers/pdf-internals.ts (≈900 lines), src/settings.ts, and src/lib/commands.ts—contain dense branching and deep nesting, making them high‑risk change targets.
How To Use It
# Clone the forked repo
git clone https://github.com/moses-y/obsidian-pdf-plus.git
cd obsidian-pdf-plus
# Install dependencies (pnpm is the declared manager)
pnpm install
# Build the plugin for Obsidian
pnpm run build # script defined in package.json
Copy the generated main.js and manifest.json into your Obsidian vault’s /.obsidian/plugins/pdf-plus/ folder, then enable the plugin in Obsidian’s Community Plugins UI. Settings are exposed via the plugin’s settings pane (src/settings.ts). Vim keybindings can be toggled in the same pane and are implemented in src/vim/.
Real‑World Use
A researcher keeps a literature vault. When they highlight a sentence in a PDF, PDF++ creates a markdown block in annotations/<pdf‑name>.md. The block contains the highlight text, page number, and a link back to the PDF location. Later, a literature review note can [[annotations/<pdf‑name>#highlight‑id]] to embed the exact excerpt, preserving the context even if the PDF file moves.
Code Health & Issues
- High – Pin GitHub Actions –
.github/workflows/*usespnpm/action-setup@v4; replace with a commit SHA to prevent supply‑chain hijack. - High – No test suite – 83 source files, zero test files; add unit/integration tests for public entry points (
src/main.ts,src/lib/highlights/*). - Medium – Least‑privilege GITHUB_TOKEN –
release.ymllacks explicit permissions; declarecontents: read(and narrow per job). - Medium – Enable Dependabot/Renovate – No auto‑update config; add
.github/dependabot.yml. - Medium – Dependency‑vulnerability scan – CI lacks a scan step; integrate
dependency-review-actionorosv-scanner. - Medium – Checkout persist‑credentials –
actions/checkoutkeeps the token; setpersist-credentials: false. - Low – Job timeouts – Workflows omit
timeout-minutes; add sensible limits (e.g., 20 min).
Static analysis also flagged 11 import cycles, deep nesting (max indentation 6), and four oversized files (>800 LOC). These patterns increase cognitive load and risk regression when modifying core behavior.
The Bottom Line
PDF++ delivers a functional, markdown‑native PDF annotation layer for Obsidian and adds optional Vim ergonomics, but the codebase suffers from high complexity, missing tests, and several CI hygiene gaps. It is suitable for teams comfortable working in a tightly coupled TypeScript plugin and willing to invest in test coverage and refactoring; otherwise, the maintenance burden may outweigh the annotation benefits.