# AGENTS.md — Personal Wiki (go-wiki) ## Project Philosophy This is a minimal personal wiki where the **folder structure is the wiki**. There is no database, no CMS, no abstraction layer between the user and their files. Every decision should reinforce this — if a proposed solution adds indirection between the filesystem and what the user sees, question it. ## Core Concept - Every folder *is* a page - `index.md` in a folder is that page's content - All related files (PDFs, images, CAD files, etc.) live in the same folder as `index.md` - Image links in `index.md` like `![](photo.jpg)` work because siblings are served at the same path - There are no "attachments" — files are just files in a folder ## Target Environment - Runs on a QNAP TS-431P3 NAS (Annapurna Labs AL-314, ARMv7 32-bit, `linux/arm`) - All files live on the NAS and are mounted/accessed locally by the binary - Users access via browser over Wireguard VPN from Windows, Linux, and Android - Must cross-compile cleanly: `GOARCH=arm GOOS=linux GOARM=7 go build` ## Tech Constraints - **Language:** Go - **Output:** Single static binary, no installer, no runtime dependencies - **Markdown:** `goldmark` for server-side rendering — no other markdown libraries - **Assets:** Embedded via `embed.FS` — no external asset serving or CDN - **HTTP:** stdlib `net/http` only — no web framework - **Dependencies:** Keep to an absolute minimum. If stdlib can do it, use stdlib. ## HTTP Interface The entire API surface should stay minimal: | 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 endpoints beyond these without a concrete stated need. ## UI Principles - Mobile-first — the primary editing device is Android over Wireguard - No JavaScript frameworks — vanilla JS only, and only when necessary - No build pipeline for frontend assets — what is embedded is what is served - Readable on small screens without zooming - Fast on a low-power ARM CPU — no heavy rendering, no large payloads ## Frontend Conventions **JS file scoping:** each feature gets its own file. Global app behaviour goes in `global-shortcuts.js`. Feature-specific logic gets its own file (e.g. `editor.js`). Do not inline JS in the template or consolidate unrelated features into one file. **Keyboard shortcuts:** `ALT+SHIFT` is the established modifier for all application shortcuts — it avoids collisions with browser and OS bindings. Do not use other modifiers for new shortcuts. **Editor toolbar:** buttons use `data-action` (maps to a JS action function) and `data-key` (the `ALT+SHIFT+KEY` shortcut letter). Adding a `data-key` to a button automatically registers its shortcut — no extra wiring needed. ## Code Structure The backend is split across three files: | File | Responsibility | |------|----------------| | `main.go` | Server setup, routing, `serveDir`, `handlePost`, `pageTypeHandler` interface, `readPageSettings` | | `render.go` | Shared helpers: markdown rendering, heading extraction, file listing, icons, formatting | | `diary.go` | Diary page type: all types, templates, and render functions | When adding a new special folder type, create a new `.go` file. Do not add type-specific logic to `main.go` or `render.go`. ## Special Folder Types (`pageTypeHandler`) Folders can opt into special rendering by placing a `.page-settings` file in them. Format: one `key = value` per line; `#` lines are comments. ``` # example type = diary ``` The server walks up from the requested path looking for a `.page-settings` file. When found, it determines the depth of the current path relative to that root and dispatches to the matching `pageTypeHandler`. **Interface** (defined in `main.go`): ```go type specialPage struct { Content template.HTML SuppressListing bool } type pageTypeHandler interface { handle(root, fsPath, urlPath string) *specialPage } ``` `handle` returns `nil` when the handler does not apply. `SuppressListing` hides the default file/folder table (used when the special content replaces it). **Registering a new type:** implement the interface in a new file and register via `init()`: ```go func init() { pageTypeHandlers = append(pageTypeHandlers, &myHandler{}) } ``` `serveDir` iterates `pageTypeHandlers` and uses the first non-nil result. It has no knowledge of specific types. ### Diary type (`diary.go`) Activated by `type = diary` in a `.page-settings` file. Folder structure: ``` Root/ ← .page-settings (type = diary) YYYY/ ← depth 1 — year view (month sections + photo counts) YYYY-MM-DD Description.ext ← photos live here, named with date prefix MM/ ← depth 2 — month view (day sections with content + photos) DD/ ← depth 3 — day view (index.md content + photo grid) index.md ``` Photos are associated to days by parsing the `YYYY-MM-DD` prefix from filenames in the year folder. No thumbnailing is performed — images are served at full resolution with `loading="lazy"`. The year view shows only photo counts, not grids, for performance. ## Auth - Basic auth is sufficient — this is a personal tool on a private VPN - Do not over-engineer access control ## What to Avoid - No database of any kind - No indexing or caching layer unless explicitly requested and justified - No parallel folder structures (the DokuWiki anti-pattern: `pages/` mirrored by `media/`) - No frameworks (web, ORM, DI, etc.) - No build steps for frontend assets - Do not suggest Docker unless the user asks — a plain binary is preferred ## Development Order When building new features, follow this priority order: 1. Correctness on the filesystem (never corrupt or lose user files) 2. Mobile usability 3. Simplicity of implementation 4. Performance ## Out of Scope (for now) These are explicitly deferred — do not implement or scaffold unless asked: - Full-text search - File upload via browser - Version history / git integration - Multi-user support - Tagging or metadata beyond `index.md` content