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>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# 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 (768px–1023px):** 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):**
|
||||
```python
|
||||
# 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:**
|
||||
```js
|
||||
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.
|
||||
Reference in New Issue
Block a user