Skip to content

Repository files navigation

Bella Note

A local-first web app for reviewing PDFs (often exported from PowerPoint): it extracts highlighted regions and annotations, shows each page as a slide image next to editable notes, and stores metadata in SQLite by default or in Postgres (Supabase) when DATABASE_URL is set.

This repository is a monorepo (single Git repo):

Package Path Role
API apps/api/ FastAPI, PDF processing, SQLite or Postgres (data/ at repo root for files)
Web frontend/ React + Vite (npm workspace)

Shared at repo root: data/ (runtime), static/ (production UI build output), pyproject.toml (Ruff), package.json (npm workspaces).

Stack

Layer Technology
API FastAPI, Uvicorn
PDF / slides PyMuPDF, pdfplumber, pdf2image (Poppler), Pillow
DB SQLite (data/db.sqlite, WAL) or Postgres via DATABASE_URL (e.g. Supabase)
UI React 19, Vite, Ant Design, TipTap (WYSIWYG notes), dnd-kit (sidebar order)

Features

  • Import PDFs — Upload one or more PDFs; pages are rendered to PNGs under data/slides/.
  • Highlight extraction — Standard annotations, yellow vector fills (common in PowerPoint PDFs), and pixmap fallback when needed.
  • Sidebar — Drag to reorder documents; rename titles; delete documents; export selected docs to a plain-text study file.
  • Slides — Filter by “no highlights”, “starred only”; hide slides; rescan from the stored PDF copy (data/originals/).
  • Notes — Each highlight or Add note entry uses a rich editor (bold, lists, etc.); text is stored as HTML; star a note to mark it; delete per note.
  • Single-server deploynpm run build writes the SPA into static/; FastAPI serves the UI and API on one port.

Requirements

  • Python 3.11+ (3.13 works with the pinned stack)

  • Node.js 20+ (for the frontend)

  • Poppler — required by pdf2image to rasterize pages:

    brew install poppler

    On Linux, install the poppler-utils (or equivalent) package for your distribution.

Setup

  1. Clone the repository and enter the project directory.

  2. Python virtual environment (recommended at repo root)

    python3 -m venv venv
    source venv/bin/activate   # Windows: venv\Scripts\activate
    pip install -r apps/api/requirements.txt
  3. Install frontend dependencies (npm workspaces — run once from repo root)

    npm install
  4. Production UI build (writes to static/)

    npm run build
  5. Run the API (serves API + static/ SPA)

    source venv/bin/activate
    cd apps/api
    python main.py
  6. Open http://localhost:8000 in your browser.

Optional: Supabase / Postgres

With DATABASE_URL set (and empty/unset for plain SQLite), the API uses psycopg against your Postgres database. The app still stores PDF originals and slide PNGs on disk under data/; only documents / slides / highlights metadata live in Postgres.

  1. Create a Supabase project and run the SQL in supabase/migrations/20260403180000_bella_note_initial.sql (or apply migrations from the Supabase dashboard / CLI).

  2. In Supabase: Project Settings → Database → Connection string → URI. Prefer Session pooler (Session mode) if you deploy anywhere without IPv6 (e.g. Render); Direct can fail with “network is unreachable” there. Include ?sslmode=require if not already in the string.

  3. Add the URI to a .env file at the repo root (copy from .env.example), for example:

    cp .env.example .env
    # Edit .env and set DATABASE_URL=postgresql://...
  4. Start the API as usual; variables from .env load automatically:

    cd apps/api && python main.py

    Optional: put overrides in apps/api/.env (loaded after the root .env).

  5. Slide images on a host without persistent disk (e.g. Render): set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY (see .env.example). Slides are stored in the bella-note-slides Storage bucket; the UI still uses /slides/... and the API redirects to the public object URL.

API documentation

With the server running, interactive docs are at http://localhost:8000/docs (OpenAPI).

Development

Terminal 1 — API with auto-reload (working directory must be apps/api so imports resolve):

source venv/bin/activate
cd apps/api
uvicorn main:app --reload --host 127.0.0.1 --port 8000

Terminal 2 — Vite dev server (from repo root; proxies API routes to port 8000):

npm run dev

Use http://localhost:5173 during UI work.

Linting

Install Python dev tools:

source venv/bin/activate
pip install -r apps/api/requirements-dev.txt

Python — from the repository root:

ruff check .
ruff format --check .    # or `ruff format .` to apply

Frontend — from repo root:

npm run lint
npm run lint:fix

npm run build runs tsc -b in the workspace.

GitHub Actions (.github/workflows/lint.yml) runs Ruff, ESLint, and the production build on push and pull requests.

Vite, Rolldown, and CI

Vite 8 uses Rolldown, which depends on platform-specific native bindings. The lockfile reflects the machine where you last ran npm install; GitHub Actions uses Linux x64. Without an explicit Linux binding entry, npm ci on CI can succeed locally on macOS but fail in Actions with “Cannot find native binding” / missing @rolldown/binding-linux-x64-gnu.

This repo pins @rolldown/binding-linux-x64-gnu under optionalDependencies in frontend/package.json (same version as the rolldown required by Vite) so Linux CI installs that package while macOS skips it.

When upgrading Vite: align that optional dependency with the Rolldown version Vite pulls in (check npm ls rolldown or package-lock.json), run npm install at the repo root, and commit the updated lockfile. If you prefer to avoid native bindings altogether, you can instead standardize on Vite 6 (Rollup-based) and drop the Rolldown binding workaround.

Project layout

.
├── package.json           # npm workspaces (includes frontend)
├── package-lock.json      # after npm install at root
├── pyproject.toml         # Ruff (repo-wide)
├── apps/
│   └── api/               # FastAPI backend
│       ├── main.py
│       ├── database.py
│       ├── pdf_processor.py
│       ├── requirements.txt
│       └── requirements-dev.txt
├── frontend/              # React + Vite (workspace package)
│   ├── src/
│   └── package.json
├── data/                  # Runtime (gitignored except .gitkeep)
│   ├── db.sqlite
│   ├── slides/
│   └── originals/
└── static/                # Built SPA (gitignored; from npm run build)

Usage tips

  • Export — Use the export control in the sidebar to include checked documents in study_master.txt (plain text; HTML in notes is stripped for export).
  • Rescan — Only works when a stored original exists. Older uploads without a copy must be re-imported once.
  • Flattened PDFs — If highlights are baked into a flat image with no vectors or annotations, extraction may find nothing; the app will warn after upload or rescan.

Git and data

  • static/ is ignored so the repo stays free of build artifacts; CI or deploy steps should run npm run build.
  • data/ (database, slides, originals) is ignored except optional .gitkeep placeholders—do not commit personal PDFs or databases unless you intend to.

License

No license file is included in this repository; add one if you distribute the project.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages