_ _ _
| |__ | |__ | | ___ __ _ _ __ __ _
| '_ \| '_ \| |/ _ \ / _` |_____| '_ \ / _` |
| | | | |_) | | (_) | (_| |_____| | | | (_| |
|_| |_|_.__/|_|\___/ \__, | |_| |_|\__, |
|___/ |___/
A knowledge-graph blog that turns an Obsidian vault of
Markdown notes into a statically-generated site with an interactive,
canvas-rendered graph of notes and their [[wikilinks]].
Built with Next.js (App Router, static export) and deployed to Cloudflare Pages.
Requires Node.js 20+ and npm.
git clone https://github.com/halit/hblog-ng.git
cd hblog-ng
npm install
npm run devWith no vault configured, npm run dev renders the bundled
example-vault/ so you can see what the project looks like
immediately. Point it at your own content by setting VAULT_PATH (see
Content below).
The graph and search indexes are generated at build time from the vault. The dev
server re-runs the data pipeline when vault content changes; if something looks
stale, run npm run prepare-data.
[[wikilinks]],[[Page#Section|Label]], and![[embeds]]resolve to real links and graph edges, with a hover preview card for each target note.- Obsidian callouts (
> [!WARNING],> [!INFO]) with themed icons. - KaTeX math (
$inline$and$$block$$), Mermaid diagrams, and charts (```chartblocks rendered with Recharts: bar, line, area, pie). - Syntax highlighting (Prism, One Dark), task lists, and footnotes.
- BibTeX citations: add a
references.bibto the vault and cite with[ref:key]. References render as numbered footnotes. - Embedded media: asciinema casts (
[asciinema:id]), YouTube, Vimeo, or direct video ([video:url]), file attachments ([file:path]), image galleries, and click-to-zoom images. #hashtagsbecome keyword pages, and```queryblocks list notes filtered by type, status, or keyword.
- Knowledge graph rendered on canvas with a D3 force simulation (not SVG),
colored by topic spectrum (offense
#ff0055, defense#00e5ff). - Drag, zoom and pan, in-graph search, node filtering, and shortest-path finding.
- Client-side fuzzy search (MiniSearch) and a command palette with keyboard
shortcuts (search
Ctrl+F, graphCtrl+G, paletteCtrl+K). - Per-note table of contents, backlinks, related notes, and suggestions.
- Static export with no server runtime. The site deploys as plain files.
- Raster images are converted to WebP (Sharp, resized to fit 1920×1920) and served with long-lived cache headers.
- Heavy libraries (graph, search, PGP, Mermaid) load on demand.
- Per-note Open Graph images (1200×630 PNG) generated at build time, plus
Twitter
summary_large_imagecards. - RSS 2.0 (
/feed.xml), Atom (/feed.atom), and JSON Feed (/feed.json). sitemap.xml,robots.txt, a PWA web manifest, and JSON-LD structured data (Article, ScholarlyArticle, SoftwareApplication, breadcrumbs).- Security and caching headers in
public/_headers.
- Posts can be PGP-signed and verified in the browser, so readers can check a
note against your public key without leaving the page. Sign with
npm run sign-posts; verification uses OpenPGP.js.
The committed .env file holds the site's defaults: title, description,
URL, author, and social handles. These NEXT_PUBLIC_* values are public by
design and are inlined into the static build.
To run your own instance, edit .env directly, or override individual
values with a git-ignored .env.local, which takes precedence and is never
committed. Next.js loads both automatically for dev and build.
Keep private material such as PGP private keys and passphrases out of .env.
Put it in .env.local or pass it through the environment at build time.
Content is not committed to this repo; it lives in a separate Obsidian vault of Markdown notes. The build resolves the vault in this order:
- The
VAULT_PATHenvironment variable, if set. - A
vault/directory in the repo root, if present (e.g. a bind-mounted vault; see.devcontainer/devcontainer.json, which mounts a host folder tovault/). - The bundled
example-vault/, a small demo vault that ships with the repo so a fresh clone renders out of the box.
To use your own content, point VAULT_PATH at your vault:
VAULT_PATH=/path/to/your/vault npm run devThe example-vault/ showcases the rendering engine: wikilinks,
callouts, math, Mermaid diagrams, charts, citations, terminal/asciinema casts, and
image galleries. Browse it to see the supported Markdown features.
npm run buildbuild runs prepare-data and then next build (static export to out/). The
data pipeline runs in order:
parse-vault: scans the vault, resolves[[wikilinks]]and related notes, builds the search index, and writesdata/vault.json(plus a trimmedpublic/vault.jsonfor the client).parse-bibtex: converts the vault'sreferences.bibtodata/references.json.optimize-images: converts images to WebP with Sharp intopublic/images/(resized to fit 1920×1920).generate-og-images: renders a 1200×630 Open Graph card per note.copy-static-assets: copies remaining static files (casts, fonts, PGP keys) intopublic/.
Generated data/ and public/* artifacts are git-ignored and rebuilt on every build.
Configuration lives in wrangler.toml
(pages_build_output_dir = "out").
Deploy from your machine (run npx wrangler login once first):
npm run deployOr connect the Git repo in the Cloudflare dashboard with build command
npm run build, output directory out, and your NEXT_PUBLIC_* variables set
in the project's environment settings. Security and caching headers are served
from public/_headers.
| Command | Description |
|---|---|
npm run lint |
ESLint |
npm run format |
Prettier |
npm run test |
Vitest |
npm run analyze |
Bundle-size visualization |
npm run sign-posts |
PGP-sign vault posts |
app/ Next.js routes (App Router)
components/ React components (graph, markdown, modals, pages)
config/ Environment, theme, and graph configuration
hooks/ Client-side React hooks
lib/ Data loading, routing, markdown, feeds, metadata
scripts/ Build-time data pipeline
utils/ Shared helpers
example-vault/ Bundled demo content (fallback vault)
Your own Markdown content lives outside the repo (set VAULT_PATH); see
Content.