Files
hermes-wiki-static/docs/ARCHITECTURE.md
T
agentandClaude 677a8dde4f Initial commit: hermes-wiki-viewer v0.2.0
Fork of DanielCheer/obsidian-web-viewer (MIT, 2026-04) with Lukas' first
additions for the Hermes Wiki workflow.

Additive customization layer (vault-custom.js) — keeps upstream-merge
trivial via a one-line script tag in vault.html.

Features added:
1. Current-page highlighting in 3D Graph — current node gold+glow,
   connected nodes light-blue, others dim to 20% opacity
2. Click-to-navigate on graph — Three.js raycaster triggers file load
3. Responsive layout — graph collapses below 1024px, tree below 768px
4. URL-based deep-linking via ?file=<path> query param
5. Server-side ?file= support in /api/vault/file/ endpoint

Modified files:
- server.py: +7 lines (Lukas-add: ?file= query param parsing)
- vault.html: +1 line (script tag for vault-custom.js)

New files:
- vault-custom.js: 11KB, all customizations in one place under HermesCustom namespace
- README.md: fork intro, quick start, customization guide
- CHANGELOG.md: Lukas-additions tracking
- docs/ARCHITECTURE.md: design rationale
- docs/CUSTOMIZATIONS.md: feature spec
- .gitignore: standard

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-16 15:17:16 +00:00

4.7 KiB

Architecture

Why this fork exists

DanielCheer/obsidian-web-viewer is a great single-file Obsidian-vault viewer (server.py + vault.html, ~10 KB + 14 KB, Python stdlib only). But for Lukas' Hermes Wiki workflow — 245 Markdown files across 18 top-level folders, frequent cross-referencing via WikiLinks, mobile reading — three specific UX gaps needed fixing:

  1. 3D Graph shows all nodes uniformly — no indication of which node the user is currently reading or which nodes are connected to it
  2. Layout breaks on small screens — the 3-panel grid (280px 1fr 260px) is too narrow on 13" laptops and unusable on phones
  3. Click-to-navigate from graph — Three.js canvas accepts clicks but doesn't trigger navigation

Design principles

1. Additive layer, not fork-and-modify

We modify exactly one line in vault.html (a <script src="vault-custom.js"></script> tag before </body>). Everything else — including the customizations themselves — lives in vault-custom.js.

Why:

  • Upstream git pull conflicts are trivial to resolve (the one line is recognizable)
  • Customizations are clearly separated from upstream code
  • Reviewing customizations is reading one file, not diffing the whole repo
  • Easier to upstream-merge back if Lukas' features prove generally useful

2. HermesCustom namespace

All custom code lives under window.HermesCustom. Methods are namespaced and self-documenting:

window.HermesCustom = window.HermesCustom || {};
HermesCustom.highlightCurrentNode = function() { ... };
HermesCustom.setupGraphClickHandler = function() { ... };
HermesCustom.responsiveLayout = function() { ... };

This way the namespace is visible in DevTools and a future maintainer can see at a glance what's custom vs. upstream.

3. Server.py stays mostly upstream

We add one enhancement to server.py: a ?file=<path> query parameter that returns the file directly (used by HermesCustom.highlightCurrentNode to know which file is open). This is a 5-line change that doesn't conflict with any upstream logic.

If upstream adds a similar feature, we drop our addition.

File map

hermes-wiki-viewer/
├── server.py                # obv-fork + 5 lines (file-query-param)
├── vault.html               # obv-fork + 1 line (script tag for custom.js)
├── vault-custom.js          # ALL Lukas-specific code
├── requirements.txt         # PyYAML only (same as upstream)
├── README.md                # this repo's entry point
├── LICENSE                  # MIT (inherited)
├── CHANGELOG.md             # release notes for Lukas-additions
├── docs/
│   ├── ARCHITECTURE.md      # you are here
│   └── CUSTOMIZATIONS.md    # spec for each Lukas-feature
└── .gitignore               # standard

Why we keep vault-custom.js separate from vault.html

Embedding customizations into vault.html would mean:

  • Every upstream update requires re-applying the entire diff
  • Reviewing customizations requires diffing two HTML files
  • Conflicts when upstream renames a function we override

A separate file means:

  • The customizations are a stable, reviewable unit
  • Upstream updates touch vault.html only, customizations are unchanged
  • The one-line patch in vault.html is a clear "anchor point" that any reviewer can understand

Why Python stdlib only (no Flask/FastAPI)

Upstream uses http.server from stdlib. We keep this. Adding a framework would:

  • Inflate dependencies
  • Require version-pinning for reproducibility
  • Make the tool harder to deploy (anywhere with Python 3.8+ works)

The 5-line addition to server.py is plain stdlib BaseHTTPRequestHandler style.

Tradeoffs we accepted

  • No build step. Customizations are vanilla JS. No bundler, no TypeScript. Means no static type-checking, but means anyone can read and modify the code without toolchain setup.
  • No tests in CI. We rely on manual testing for now. CI tests would add complexity that doesn't match the project's "simple tool" character. (Future: a small Playwright test for the graph highlighting.)
  • No auto-update from upstream. When obv updates, Lukas has to git pull upstream && git merge. The one-line vault.html patch needs re-application if upstream modified it. This is acceptable for a personal side-project.

Roadmap

See CHANGELOG.md for planned additions. Current priorities:

  1. Current-page highlighting in 3D Graph
  2. Click-to-navigate on graph
  3. Responsive layout (1024px and 768px breakpoints)
  4. Backlinks panel (per-page incoming WikiLinks)
  5. Keyboard shortcuts (j/k for next/prev file, [/] for nav, g/G for graph focus)
  6. Light theme variant (currently Catppuccin-dark only)