diff --git a/TODO.md b/TODO.md index 081b08f..44a72a8 100644 --- a/TODO.md +++ b/TODO.md @@ -32,12 +32,132 @@ Dispatched from core/go orchestration. Pick up tasks in order. - [x] **Multi-word bonus** -- All query words present in topic adds +2.0. - [x] **Tests for all new features** -- Levenshtein, min3, extractPhrases, fuzzy search, phrase search, tag boost, multi-word bonus, scoring constants, phrase highlighting, section phrase matching. -## Phase 2: core.help Integration +## Phase 2: HTTP Server & Rendering -- [ ] Feed CLI docs into help catalog (parse `core` subcommand help text) -- [ ] Serve catalog via HTTP for the `core.help` domain -- [ ] Add Markdown rendering for topic bodies in HTTP responses -- [ ] Generate static site from catalog for BunnyCDN deployment +### 2.1 Markdown Rendering (`render.go`) + +- [ ] **Add `github.com/yuin/goldmark` dependency** — CommonMark-compliant Markdown renderer. Run `go get github.com/yuin/goldmark`. +- [ ] **Create `render.go`** — Markdown-to-HTML conversion: + - `func RenderMarkdown(content string) (string, error)` — converts Markdown to HTML using goldmark. Configure with: + - `html.WithUnsafe()` — allow raw HTML in source (needed for embedded code examples) + - `extension.GFM` — GitHub Flavoured Markdown (tables, strikethrough, autolinks) + - `extension.Typographer` — smart quotes and dashes + - Returns HTML fragment (no ``/`
` wrapper — the server templates handle that) +- [ ] **Create `render_test.go`** — Tests: + - (a) heading hierarchy (H1-H6) produces correct ``
+ - (c) inline code backticks produce ``
+ - (d) lists (ordered + unordered)
+ - (e) links and images
+ - (f) tables (GFM extension)
+ - (g) empty input returns empty string
+ - (h) special characters are properly escaped
+
+### 2.2 HTTP Server (`server.go`)
+
+- [ ] **Create `server.go`** — HTTP server for the help catalog:
+ - `type Server struct { catalog *Catalog; addr string; mux *http.ServeMux }` — holds catalog reference and HTTP mux
+ - `func NewServer(catalog *Catalog, addr string) *Server` — constructor. Creates mux with all routes registered.
+ - `func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)` — delegates to mux (implements `http.Handler`)
+ - `func (s *Server) ListenAndServe() error` — starts listening
+
+ **Routes:**
+ - `GET /` — Index page: rendered HTML listing all topics grouped by tags, sorted by Order then Title
+ - `GET /topics/{id}` — Topic page: rendered Markdown content + table of contents from Sections + related topics sidebar
+ - `GET /search?q={query}` — Search results page: rendered HTML with highlighted snippets, scores
+ - `GET /api/topics` — JSON array of all topics (without rendered HTML — raw content field)
+ - `GET /api/topics/{id}` — JSON single topic
+ - `GET /api/search?q={query}` — JSON array of SearchResult
+
+ **Response format:**
+ - HTML routes: render using Go `html/template` with embedded templates (see 2.3)
+ - JSON routes: `application/json` with `json.NewEncoder`
+ - All routes: set `Content-Type`, `X-Content-Type-Options: nosniff`
+ - Search routes: return 400 if query is empty
+
+- [ ] **Create `server_test.go`** — Tests using `httptest.NewServer`:
+ - (a) `GET /` returns 200 with HTML containing topic titles
+ - (b) `GET /topics/getting-started` returns 200 with rendered content
+ - (c) `GET /topics/nonexistent` returns 404
+ - (d) `GET /search?q=install` returns 200 with results
+ - (e) `GET /search` (no query) returns 400
+ - (f) `GET /api/topics` returns JSON array
+ - (g) `GET /api/topics/getting-started` returns JSON object
+ - (h) `GET /api/topics/nonexistent` returns 404
+ - (i) `GET /api/search?q=config` returns JSON results
+ - (j) Content-Type headers correct for HTML vs JSON routes
+
+### 2.3 HTML Templates (`templates.go`)
+
+- [ ] **Create `templates.go`** — Embedded HTML templates using `embed.FS`:
+ - Use `html/template` with a `//go:embed templates/*.html` directive
+ - Create `templates/` directory with:
+ - `base.html` — shared layout: ``, dark theme CSS (reuse go-session's colour palette: `--bg: #0d1117; --fg: #c9d1d9; --accent: #58a6ff`), nav bar with search input, footer
+ - `index.html` — topic listing: cards grouped by first tag, topic count, search bar
+ - `topic.html` — single topic: rendered Markdown body, table of contents (from Sections), related topics, prev/next navigation
+ - `search.html` — search results: query echo, result count, ranked list with snippets, "no results" state with fuzzy suggestions
+ - `404.html` — not found page with search suggestion
+ - Template functions: `renderMarkdown` (calls RenderMarkdown), `truncate`, `pluralise`
+ - All templates use the dark theme. Monospace font. Clean, minimal.
+
+- [ ] **Create `templates_test.go`** — Tests:
+ - (a) all templates parse without error
+ - (b) index template renders topic titles
+ - (c) topic template renders Markdown content
+ - (d) search template renders results with snippets
+ - (e) 404 template contains "not found"
+
+### 2.4 Static Site Generator (`generate.go`)
+
+- [ ] **Create `generate.go`** — Generate a static site from the catalog:
+ - `func Generate(catalog *Catalog, outputDir string) error` — writes static HTML files:
+ - `{outputDir}/index.html` — index page (rendered from index template)
+ - `{outputDir}/topics/{id}.html` — one file per topic (rendered from topic template)
+ - `{outputDir}/search.html` — search page with client-side JS search
+ - `{outputDir}/search-index.json` — JSON search index for client-side search:
+ ```json
+ [{"id":"getting-started","title":"Getting Started","tags":["intro"],"content":"...truncated..."}]
+ ```
+ - `{outputDir}/404.html` — not found page
+ - Client-side search JS: inline `