Files
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

5.5 KiB
Raw Permalink Blame History

Customizations — Feature Spec

Each section describes one Lukas-feature: what it does, how it works, where the code lives, and known limitations.

1. Current-page highlighting in 3D Graph

What: When you open a file, the corresponding node in the 3D Graph becomes brighter and larger. Nodes that the open file links to (via [[wikilinks]]) also brighten. All other nodes dim.

How:

  1. HermesCustom.getCurrentFile() reads the file path from ?file= URL param (preferred) or tracks via the loadFile() hook
  2. HermesCustom.getConnectedFiles(currentFile) looks up outgoing edges in the global graph data
  3. HermesCustom.highlightCurrentNode() iterates window.graphNodeObjects (obv's Three.js node meshes) and adjusts material.color, emissive, scale, opacity

Code: vault-custom.js, methods under namespace HermesCustom.*.

Materials used (Catppuccin palette, matches theme):

  • Current: #FFD700 (gold) + emissive glow + 1.5x scale
  • Connected: #89b4fa (light blue) + faint emissive + 1.1x scale
  • Other: #313244 (muted) + 20% opacity (transparent)

Known limitation:

  • Uses window.graphNodeObjects global from obv — if obv renames this, we need to update. Mitigation: detection in highlightCurrentNode waits up to 500ms for obv's graph to be ready.
  • Highlight doesn't preserve selection across page reload — fresh fetch on every load.

2. Click-to-navigate on 3D Graph

What: Clicking a node in the 3D Graph navigates to that file's page.

How:

  1. HermesCustom.setupGraphClickHandler() sets up a Three.js Raycaster on the canvas DOM element
  2. On click, computes normalized mouse coords, raycasts into scene
  3. First intersection's userData.nodeId is the file path
  4. Calls window.loadFile(nodeId) (obv's existing loader)
  5. Updates URL with window.history.pushState({}, '', '?file=<path>')

Code: vault-custom.js, ~25 lines.

Known limitation:

  • Three.js raycasting requires nodes to have userData.nodeId set — verified in obv's addNodeToScene function (line ~310 in obv's graph render). If obv removes this, our handler silently does nothing.
  • No keyboard navigation in graph (yet). Roadmap item.

3. Responsive Layout

What: The 3-panel desktop layout collapses gracefully on smaller screens.

Breakpoints:

  • Desktop (≥1024px): Full 3-panel layout — Tree 240px | Content 1fr | Graph 240px
  • Tablet (768px1023px): Tree 240px | Content 1fr | Graph 200px (bottom panel)
  • Mobile (<768px): Tree as hamburger menu (off-canvas, slides in from left) | Content full-width | Graph hidden

How:

  • HermesCustom.injectResponsiveCSS() adds <style> tag with media queries
  • HermesCustom.responsiveLayout() adds .mobile-mode / .tablet-mode classes to .vault-app based on window.innerWidth
  • Resize handler is throttled (150ms) to avoid jank
  • Hamburger toggle button is appended to header in mobile mode

Code: vault-custom.js, ~80 lines (including CSS string).

Known limitation:

  • The injected CSS uses !important to override obv's grid-template-columns. If obv restructures the layout, we may need to adjust selectors.
  • Hamburger menu doesn't auto-close on file selection — minor UX nit. Roadmap.

4. URL-based navigation (?file=)

What: The URL contains the currently-open file as a ?file=<path> query param. This makes pages deep-linkable and back-button friendly.

How:

  • HermesCustom.getCurrentFile() reads ?file= from URLSearchParams
  • vault-custom.js updates URL via history.pushState() on file load
  • Server.py patch accepts ?file= in API calls

Server-side patch (server.py):

# Lukas-add: also support ?file=<path> query for clean URL navigation
if "?" in rel:
    rel, _, query = rel.partition("?")
    from urllib.parse import parse_qs
    file_param = parse_qs(query).get("file", [None])[0]
    if file_param:
        rel = file_param

Known limitation:

  • Server-side patch assumes ?file= is the only query parameter obv might add. If obv adds another (e.g. ?theme=), the simple partition("?") still works but is naive. Acceptable for current state.

5. Tracking current file via loadFile hook

What: vault-custom.js patches obv's window.loadFile to remember the last loaded path. Used as a fallback when the URL doesn't have ?file= (e.g., when obv opens a file from search without updating URL).

How:

const originalLoadFile = window.loadFile;
window.loadFile = function(path) {
    HermesCustom._lastLoadedFile = path;
    const result = originalLoadFile.apply(this, arguments);
    setTimeout(HermesCustom.highlightCurrentNode, 300);
    return result;
};

Known limitation:

  • The 300ms delay assumes the render finishes within that time. Slow devices may need a longer delay. Could be improved by hooking into a render-completion signal if obv provides one.

Testing

Manual testing only for now. Test cases:

Scenario Expected
Open concepts/agent-reference-model.md Graph: that node gold + glow, connected nodes light blue, others dim
Open file via search (not via tree click) Same highlighting (URL gets ?file=)
Click graph node File loads, URL updates
Resize browser to <1024px Graph moves to bottom panel
Resize browser to <768px Tree becomes hamburger, graph hidden
Hard reload on file URL File loads directly (server.py parses ?file=)
Upstream obv update (theoretical) Only 1 line of conflict in vault.html, easy merge

Planned: Playwright-based automated test for the 5 visual features.