5.6 KiB
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
# Local build (host architecture)
go build .
# Deploy to NAS
make deploy
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 |
Mobile-friendly editor with index.md content in a textarea |
| 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/httponly — no web framework goldmarkfor Markdown rendering — no other Markdown librariesembed.FSfor 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
- 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+SHIFTis the modifier for all keyboard shortcuts — do not introduce others- Editor toolbar buttons use
data-action+data-key; addingdata-keyauto-registers the shortcut - For mutating modals (anything that POSTs and then navigates), call
closeModal()and thenpostReplace(action, body, target)frompage/actions.js. Do NOT use<form>.submit(). Two reasons:- The modal must be removed from the DOM before navigation, or the browser's bfcache snapshots it open and back-nav restores the modal.
postReplaceuseswindow.location.replaceso 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 inlinedisplay: flexon 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 hardcoded1px 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 (<div class="btn btn-small">), not as new standalone classes.
Anti-patterns to reject:
- One-off classes that duplicate an existing module (
.save-buttonwhen.btnexists,.form-name-inputwhen.inputexists). - Element selectors (
textarea { ... },header { ... }) for visual treatment — add a class instead. - Inlining
display: flex; gap: Xon 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
:rootif it's missing.
Development Priorities
When building features, apply this order:
- Correctness on the filesystem — never corrupt or lose files
- Mobile usability (primary editing device is Android over Wireguard VPN)
- Simplicity of implementation, adhere to KISS
- 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
germanMonthsmap indiary.gokeeps the labels keyed bytime.Monthsince Go'stime.Formatis English-only.
What to Avoid
- Any parallel folder structure (e.g. a separate
media/tree mirroringpages/) - 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.mdcontent