diff --git a/.hermes/plans/hierarchical-folders.md b/.hermes/plans/hierarchical-folders.md new file mode 100644 index 0000000..eb5506e --- /dev/null +++ b/.hermes/plans/hierarchical-folders.md @@ -0,0 +1,151 @@ +# Phase 14 — Hierarchical Folder Structure + +> User: "gk ada bedanya, maksud saya inginnya itu bisa yg bertingkat seperti github yg memiliki folder dalam folder" +> Context: after the full dynamic-custom-fields overhaul (Phase 13), the user +> wants document URLs/content organized in **nested folders** like GitHub — +> `writeups/ctf/defcon-quals-2024/pwn-100/...` with subfolders under subfolders, +> not just one level deep. + +## Problem + +The current URL scheme is `/
/` where `slug` can contain `/` +(e.g. `writeups/ctf/defcon-quals-2024/pwn-100-ret2win-alignment` → +`/writeups/ctf/defcon-quals-2024/pwn-100-ret2win-alignment`). This works for +**files** but there are no **folder-level index pages** — navigating to +`/writeups/ctf/defcon-quals-2024/` returns 404 because Next.js catch-all +`[section]/[...slug]/page.tsx` requires at least one slug segment beyond the +section, and the sidebar only shows flat doc titles (no folder tree). + +GitHub's model: `github.com/org/repo/tree/main/path/to/folder/file` — every +folder has an index page (`/path/to/folder/`) listing its contents. + +## Solution + +### 1. Folder Index Pages + +**Create `apps/web/app/[section]/[...slug]/folder.tsx`** (or a parallel route). +Actually — cleaner approach per Next.js App Router: the catch-all +`[section]/[...slug]/page.tsx` handles both. Add logic: if the slug resolves to +an actual markdown file → doc page (existing behavior). If the slug resolves to +a **directory** (folder of docs) → render a folder index listing all docs whose +`path` starts with that prefix. + +**Mechanism:** +- Call `listDocuments()` to get all docs. +- The incoming URL path is `{section}/{...slug}`. +- Build the "folder prefix" = `${section}/${slug.join("/")}/` (with trailing `/`, + or just `${section}/${slug.join("/")}` if no slug segments). +- Filter docs whose `doc.path` starts with that prefix. +- If exactly one doc matches AND its path === prefix (trimmed .md) → it's a + doc page (existing). If zero or multiple match and they all start with the + prefix → it's a folder index. +- Edge: a folder with exactly one doc whose path matches exactly — still a doc + page. A folder is when there are docs at `prefix/sub/...`. + +**Better heuristic:** A slug path is a "folder" if there exist docs whose `path` +is `prefix/deep/...` (i.e., the slug is a parent of other doc paths, not a +leaf itself). A slug is a "leaf doc" if `path === prefix + ".md"`. + +### 2. Sidebar Tree + +**Update `Sidebar.tsx`:** +- `listDocuments()` already returns all docs with their full `slug` and `path`. +- Build a **tree** from the flat list: split each slug by `/`, create nested + folder nodes. +- Render nested `