diff --git a/AGENTS.md b/AGENTS.md index 43a8cfe..c219677 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,9 +32,13 @@ The entire API surface should stay minimal: | Method | Path | Behaviour | |--------|------|-----------| -| GET | `/{path}` | If folder: render `index.md` + list contents. If file: serve raw. | -| GET | `/{path}?edit` | Mobile-friendly editor with `index.md` content in a textarea | -| POST | `/{path}` | Write updated `index.md` to disk | +| 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. @@ -59,6 +63,78 @@ modifiers for new shortcuts. `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 diff --git a/README.md b/README.md index 0633d5c..93f584f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,39 @@ GOOS=linux GOARCH=arm go build -o datascape . |--------|-----| | Browse | Navigate folders at `/` | | Read | Any folder with `index.md` renders it as HTML | -| Edit | Append `?edit` to any folder URL, or click **Edit** | -| Save | POST from the edit form writes `index.md` to disk | +| Edit | Append `?edit` to any folder URL, or click **[EDIT]** (Alt+Shift+E) | +| Save | POST from the edit form writes `index.md` to disk; folder is created if needed | +| New page | Click **[NEW]** (Alt+Shift+N), enter a name — opens the new page in edit mode | | Files | Drop PDFs, images, etc. next to `index.md` — they appear in the listing | + +Navigating to a URL that does not exist shows an empty page with a **[CREATE]** prompt. + +## Special Folder Types + +A folder can opt into special rendering by adding a `.page-settings` file: + +``` +type = diary +``` + +### Diary + +Designed for a chronological photo diary. Expected structure: + +``` +FolderName/ + .page-settings ← type = diary + YYYY/ + YYYY-MM-DD Desc.jpg ← photos named with date prefix + MM/ + DD/ + index.md ← diary entry for that day +``` + +| View | What renders | +|------|-------------| +| Year (`YYYY/`) | Section per month with link and photo count | +| Month (`MM/`) | Section per day with entry content and photo grid | +| Day (`DD/`) | Entry content and photo grid | + +Days with photos but no `index.md` still appear in the month view and can be created by clicking their heading link.