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.

  1. onload registers the plugin (addSetting, registerCommands) and creates the PDFBacklinkVisualizer (src/backlink-visualizer.ts).
  2. User interaction (e.g., clicking a PDF link) triggers registerPDFEvent in src/lib/index.ts, which routes to getPage (called from 32 places) and then to addToggleSetting via the UI builder (display).
  3. Highlight creation follows the call chain: display → addToggleSetting → setAnnotationContents (entry point in src/lib/highlights/write-file/index.ts:95), which writes a markdown file using the Obsidian FileSystemAdapter.mkdir call (filesystem touch).
  4. 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 is src/vim/vim.ts (7 incoming, 8 outgoing imports, instability 0.53).
  5. UI patches (src/patchers/pdf-internals.ts, src/patchers/pdf-view.ts) hook into Obsidian’s private PDF APIs; they are invoked from the onload registration and from the registerPDFEvent path.

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/* uses pnpm/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_TOKENrelease.yml lacks explicit permissions; declare contents: 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-action or osv-scanner.
  • Medium – Checkout persist‑credentialsactions/checkout keeps the token; set persist-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.