# CLAUDE.md ## Project Overview `datascape` is a minimal personal wiki where **the folder structure is the wiki**. No database, no CMS, no abstraction layer — every folder is a page, and `index.md` in a folder is that page's content. ## Build & Deploy ```bash # Local build (host architecture) go build . # Deploy to NAS make deploy ``` ### Editor bundle (the one build-pipeline exception) The page editor uses CodeMirror 6, vendored as a single pre-built IIFE at `assets/editor/vendor/codemirror.bundle.js` and embedded via `embed.FS`. This is the **only** deliberate exception to the "no build pipeline" rule below — it is a one-time, committed artifact, not a runtime build. `go build` / `make deploy` never touch Node and only consume the committed bundle. Regenerate the bundle **only** when upgrading the `@codemirror/*` versions: ```bash # bump versions in editor-build/package.json first, then: make editor # runs `npm ci && npm run build` in editor-build/, rewrites the vendored bundle ``` Commit the regenerated `codemirror.bundle.js` and the updated `editor-build/package-lock.json`. `editor-build/node_modules/` is gitignored. ## HTTP API Surface | Method | Path | Behaviour | |--------|------|-----------| | GET | `/{path}/` | If folder exists: render `index.md` + list contents. If not: show empty create prompt. | | GET | `/{path}/?edit` | CodeMirror 6 editor initialized with `index.md` content | | POST | `/{path}` | Write `index.md` to disk; creates the folder if it does not exist yet | Non-existent paths without a trailing slash redirect to the slash form (GET only — POSTs are not redirected because `path.Clean` strips the trailing slash from `PostURL` and the content would be lost). Do not add new endpoints without a concrete stated need. ## Code Structure When adding a new special folder type, create a new `.go` file. Do not add type-specific logic to `main.go` or `render.go`. Prefer separate, human-readable `.html` files over inlined HTML strings in Go. Embed them via `embed.FS` if needed. ## Architecture Rules - **Single binary** — no installer, no runtime dependencies, no Docker - **Go stdlib `net/http`** only — no web framework - **`goldmark`** for Markdown rendering — no other Markdown libraries - **`embed.FS`** for all assets — no external serving, no CDN - **No database** of any kind - **No indexing or caching** unless explicitly requested and justified - Keep dependencies to an absolute minimum; if stdlib can do it, use stdlib ## Frontend Rules - Vanilla JS only — no frameworks, no build pipeline (the single exception is the vendored CodeMirror editor bundle; see Build & Deploy) - Each feature gets its own JS file; global behaviour goes in `global-shortcuts.js` - Do not inline JS in templates or merge unrelated features into one file - `ALT+SHIFT` is the modifier for all keyboard shortcuts — do not introduce others - Editor toolbar buttons use `data-action` + `data-key`; adding `data-key` auto-registers the shortcut - For mutating modals (anything that POSTs and then navigates), call `closeModal()` and then `postReplace(action, body, target)` from `page/actions.js`. Do NOT use `
.submit()`. Two reasons: 1. The modal must be removed from the DOM before navigation, or the browser's bfcache snapshots it open and back-nav restores the modal. 2. `postReplace` uses `window.location.replace` so the action + result occupy a single history entry. A naive POST → 303 → GET creates two entries, and back-nav lands on a stale pre-mutation snapshot of the same page. ## CSS Follow **SMACSS** conventions (Scalable and Modular Architecture for CSS). The stylesheet is organized into five categories: - **Base** — element resets and global defaults only. Never style `header`, `textarea`, `input`, `aside`, `footer`, etc. directly for visual treatment — always via a class. - **Layout** — `.row`, `.col`, `.page-wrap`. Use these for flex layout; do not inline `display: flex` on feature classes. - **Modules** — reusable components: `.panel`, `.panel-header`, `.menu-row`, `.btn`, `.input`, `.muted`, `.truncate`, etc. New visual patterns should reuse these. Before adding a new module, check whether an existing one + a modifier already covers the case. - **State** — `.is-*` prefix only (`.is-open`, `.is-selected`, `.is-active`, `.is-disabled`, `.is-empty`). State is the only place a class describes a moment in time rather than a structural role. - **Theme** — colors, borders, spacing, and font sizes come from CSS variables defined in `:root` (`--bg`, `--secondary`, `--border`, `--border-dashed`, `--space-*`, `--font-*`). No hardcoded `1px solid #...`, no hardcoded rem spacing in component rules. Naming: flat-dash (`.panel-header`, `.btn-small`), not BEM (`.panel__header--small`). Modifiers attach as additional classes (`
`), not as new standalone classes. Anti-patterns to reject: - One-off classes that duplicate an existing module (`.save-button` when `.btn` exists, `.form-name-input` when `.input` exists). - Element selectors (`textarea { ... }`, `header { ... }`) for visual treatment — add a class instead. - Inlining `display: flex; gap: X` on a feature class instead of composing with `.row` / `.col`. - Adding a new module for a single use site — prefer a modifier on an existing module first. - Hardcoded colors, border widths, or spacing values inside component rules — pull a variable, or add one to `:root` if it's missing. ## Development Priorities When building features, apply this order: 1. Correctness on the filesystem — never corrupt or lose files 2. Mobile usability (primary editing device is Android over Wireguard VPN) 3. Simplicity of implementation, adhere to KISS 4. Performance ## Date Formatting - General UI dates (file listings, metadata): ISO `YYYY-MM-DD` - Diary headings (year/month/day) are also ISO short form: `# 2026`, `## 2026-05`, `### 2026-05-28`. No long-form rendering. - Calendar widget month names are German; the `germanMonths` map in `diary.go` keeps the labels keyed by `time.Month` since Go's `time.Format` is English-only. ## What to Avoid - Any parallel folder structure (e.g. a separate `media/` tree mirroring `pages/`) - Over-engineering auth — Basic auth is sufficient for a personal VPN tool - Heavy payloads or expensive rendering (target CPU: ARMv7 32-bit NAS) - Suggesting Docker (plain binary is preferred) ## Out of Scope (do not implement unless explicitly asked) - Full-text search - Browser-based file upload - Version history / git integration - Multi-user support - Tagging or metadata beyond `index.md` content