Skip to content

Commit 1e27063

Browse files
Copilotobserverw
andauthored
Extend web docs page to display all docs/ directory content (#7)
* Initial plan * Update DocsPage to display all docs from docs/ directory Co-authored-by: observerw <[email protected]> * Optimize sidebar rendering by memoizing filtered doc arrays Co-authored-by: observerw <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: observerw <[email protected]>
1 parent 24f83e3 commit 1e27063

File tree

2 files changed

+93
-51
lines changed

2 files changed

+93
-51
lines changed

web/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"private": true,
66
"scripts": {
77
"dev": "vite",
8-
"predev": "mkdir -p public/docs && cp -r ../docs/schemas public/docs/",
9-
"prebuild": "mkdir -p public/docs && cp -r ../docs/schemas public/docs/",
8+
"predev": "mkdir -p public/docs && cp -r ../docs/* public/docs/",
9+
"prebuild": "mkdir -p public/docs && cp -r ../docs/* public/docs/",
1010
"build": "vite build",
1111
"preview": "vite preview"
1212
},

web/src/pages/DocsPage.tsx

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function posixJoin(baseDir: string, hrefPath: string) {
6969
return out.join("/");
7070
}
7171

72-
const RAW_DOCS = import.meta.glob("../../../docs/schemas/**/*.md", {
72+
const RAW_DOCS = import.meta.glob("../../../docs/**/*.md", {
7373
query: "?raw",
7474
import: "default",
7575
eager: true,
@@ -79,12 +79,12 @@ function makeDocIndex(): DocEntry[] {
7979
const entries: DocEntry[] = [];
8080

8181
for (const [sourcePath, raw] of Object.entries(RAW_DOCS)) {
82-
const marker = "/docs/schemas/";
82+
const marker = "/docs/";
8383
const idx = sourcePath.lastIndexOf(marker);
8484
if (idx === -1) continue;
8585
const relativePath = sourcePath.slice(idx + marker.length);
8686
const { frontmatter, body } = parseFrontmatter(raw);
87-
const draft = relativePath.startsWith("draft/");
87+
const draft = relativePath.startsWith("schemas/draft/");
8888

8989
const frontmatterTitle = frontmatter.title?.trim();
9090
const headingTitle = extractTitleFromMarkdown(body)?.trim();
@@ -96,14 +96,20 @@ function makeDocIndex(): DocEntry[] {
9696
const title =
9797
frontmatterTitle || headingTitle || fallbackTitle || "Documentation";
9898

99-
const route = `/docs/schemas/${relativePath.replace(/\.md$/, "")}`;
99+
const route = `/docs/${relativePath.replace(/\.md$/, "")}`;
100100
entries.push({ route, relativePath, title, draft });
101101
}
102102

103103
entries.sort((a, b) => {
104-
const aIsReadme = a.relativePath.toLowerCase() === "readme.md";
105-
const bIsReadme = b.relativePath.toLowerCase() === "readme.md";
104+
const aIsReadme = a.relativePath.toLowerCase() === "schemas/readme.md";
105+
const bIsReadme = b.relativePath.toLowerCase() === "schemas/readme.md";
106106
if (aIsReadme !== bIsReadme) return aIsReadme ? -1 : 1;
107+
108+
// Group by category: root docs vs schema docs
109+
const aIsSchema = a.relativePath.startsWith("schemas/");
110+
const bIsSchema = b.relativePath.startsWith("schemas/");
111+
if (aIsSchema !== bIsSchema) return aIsSchema ? 1 : -1;
112+
107113
if (a.draft !== b.draft) return a.draft ? 1 : -1;
108114
return a.relativePath.localeCompare(b.relativePath);
109115
});
@@ -121,15 +127,8 @@ function canonicalRouteFromSplat(splat?: string) {
121127
if (cleaned === "schemas") return DEFAULT_ROUTE;
122128
if (cleaned === "schemas/README") return DEFAULT_ROUTE;
123129

124-
if (cleaned.startsWith("schemas/")) {
125-
const normalized = cleaned.replace(/\.md$/, "");
126-
return `/docs/${normalized}`;
127-
}
128-
129-
const old = cleaned.replace(/\.md$/, "");
130-
if (old.startsWith("draft_"))
131-
return `/docs/schemas/draft/${old.replace(/^draft_/, "")}`;
132-
return `/docs/schemas/${old}`;
130+
const normalized = cleaned.replace(/\.md$/, "");
131+
return `/docs/${normalized}`;
133132
}
134133

135134
function hrefToDocsRoute(href: string, currentDocRelativePath: string) {
@@ -142,10 +141,7 @@ function hrefToDocsRoute(href: string, currentDocRelativePath: string) {
142141

143142
if (normalized.startsWith("/docs/")) {
144143
const stripped = normalized.replace(/^\/docs\//, "").replace(/\.md$/, "");
145-
if (stripped.startsWith("schemas/")) return `/docs/${stripped}${hash}`;
146-
if (stripped.startsWith("draft_"))
147-
return `/docs/schemas/draft/${stripped.replace(/^draft_/, "")}${hash}`;
148-
return `/docs/schemas/${stripped}${hash}`;
144+
return `/docs/${stripped}${hash}`;
149145
}
150146

151147
const underSchemas = normalized.startsWith("schemas/");
@@ -158,7 +154,7 @@ function hrefToDocsRoute(href: string, currentDocRelativePath: string) {
158154
: posixJoin(baseDir, schemaRelative);
159155

160156
const withoutExt = resolvedRelative.replace(/\.md$/, "");
161-
return `/docs/schemas/${withoutExt}${hash}`;
157+
return `/docs/${withoutExt}${hash}`;
162158
}
163159

164160
export default function DocsPage() {
@@ -173,6 +169,15 @@ export default function DocsPage() {
173169
const currentDoc =
174170
DOC_BY_ROUTE.get(canonicalRoute) ?? DOC_BY_ROUTE.get(DEFAULT_ROUTE);
175171

172+
const generalDocs = useMemo(
173+
() => docs.filter(doc => !doc.relativePath.startsWith("schemas/")),
174+
[docs]
175+
);
176+
const schemaDocs = useMemo(
177+
() => docs.filter(doc => doc.relativePath.startsWith("schemas/")),
178+
[docs]
179+
);
180+
176181
useEffect(() => {
177182
const titleSuffix = currentDoc?.draft ? " (draft)" : "";
178183
document.title = `${
@@ -184,7 +189,7 @@ export default function DocsPage() {
184189
if (!currentDoc) return;
185190
setLoading(true);
186191
const sourcePath = Object.keys(RAW_DOCS).find((p) =>
187-
p.endsWith(`/docs/schemas/${currentDoc.relativePath}`)
192+
p.endsWith(`/docs/${currentDoc.relativePath}`)
188193
);
189194
const raw = sourcePath ? RAW_DOCS[sourcePath] ?? "" : "";
190195
const { body } = parseFrontmatter(raw);
@@ -213,35 +218,72 @@ export default function DocsPage() {
213218
Documentation
214219
</h2>
215220
<nav className="space-y-1">
216-
{docs.map((doc) => (
217-
<Link
218-
key={doc.route}
219-
to={doc.route}
220-
className={`
221-
flex items-center gap-2 px-3 py-2 rounded-sm text-sm transition-colors
222-
${
223-
currentDoc.route === doc.route
224-
? "bg-primary/10 text-primary font-medium"
225-
: "text-muted-foreground hover:text-foreground hover:bg-muted"
226-
}
227-
`}
228-
>
229-
<FileText className="h-3.5 w-3.5" />
230-
<span className="font-mono text-xs">{doc.title}</span>
231-
{doc.draft && (
232-
<span
233-
title="Draft: content may change"
234-
aria-label="Draft: content may change"
235-
className="ml-1 cursor-help rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-wider text-muted-foreground"
221+
{/* Root level docs */}
222+
{generalDocs.length > 0 && (
223+
<>
224+
<div className="px-3 py-2 font-mono text-xs font-medium text-muted-foreground uppercase tracking-wider">
225+
General
226+
</div>
227+
{generalDocs.map((doc) => (
228+
<Link
229+
key={doc.route}
230+
to={doc.route}
231+
className={`
232+
flex items-center gap-2 px-3 py-2 rounded-sm text-sm transition-colors
233+
${
234+
currentDoc.route === doc.route
235+
? "bg-primary/10 text-primary font-medium"
236+
: "text-muted-foreground hover:text-foreground hover:bg-muted"
237+
}
238+
`}
236239
>
237-
draft
238-
</span>
239-
)}
240-
{currentDoc.route === doc.route && (
241-
<ChevronRight className="h-3.5 w-3.5 ml-auto" />
242-
)}
243-
</Link>
244-
))}
240+
<FileText className="h-3.5 w-3.5" />
241+
<span className="font-mono text-xs">{doc.title}</span>
242+
{currentDoc.route === doc.route && (
243+
<ChevronRight className="h-3.5 w-3.5 ml-auto" />
244+
)}
245+
</Link>
246+
))}
247+
</>
248+
)}
249+
250+
{/* Schema docs */}
251+
{schemaDocs.length > 0 && (
252+
<>
253+
<div className="px-3 py-2 font-mono text-xs font-medium text-muted-foreground uppercase tracking-wider mt-4">
254+
Schemas
255+
</div>
256+
{schemaDocs.map((doc) => (
257+
<Link
258+
key={doc.route}
259+
to={doc.route}
260+
className={`
261+
flex items-center gap-2 px-3 py-2 rounded-sm text-sm transition-colors
262+
${
263+
currentDoc.route === doc.route
264+
? "bg-primary/10 text-primary font-medium"
265+
: "text-muted-foreground hover:text-foreground hover:bg-muted"
266+
}
267+
`}
268+
>
269+
<FileText className="h-3.5 w-3.5" />
270+
<span className="font-mono text-xs">{doc.title}</span>
271+
{doc.draft && (
272+
<span
273+
title="Draft: content may change"
274+
aria-label="Draft: content may change"
275+
className="ml-1 cursor-help rounded-sm border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-wider text-muted-foreground"
276+
>
277+
draft
278+
</span>
279+
)}
280+
{currentDoc.route === doc.route && (
281+
<ChevronRight className="h-3.5 w-3.5 ml-auto" />
282+
)}
283+
</Link>
284+
))}
285+
</>
286+
)}
245287
</nav>
246288
</Card>
247289
</aside>

0 commit comments

Comments
 (0)