Skip to content

Commit ce6c3bf

Browse files
committed
Update docs
Implemented multi-version documentation. Key results: - 217 published GitHub releases selectable - 88 deduplicated API snapshots - Version-specific installation and API pages - Search, sidebar, version selector, release history, dark mode - Responsive desktop/mobile design - Vercel-compatible Next.js build - CI documentation workflow - npm run lint and npm run build pass - Production routes and 404 behavior verified Main files: - metbit/docs/app/docs/[version]/page.tsx - metbit/docs/scripts/sync_version_docs.py - metbit/docs/content/generated/releases.json - metbit/docs/README.md
1 parent 75bd1e2 commit ce6c3bf

110 files changed

Lines changed: 92769 additions & 189 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Documentation
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "docs/**"
7+
- "metbit/**"
8+
- ".github/workflows/docs.yml"
9+
push:
10+
branches: [main]
11+
paths:
12+
- "docs/**"
13+
- "metbit/**"
14+
- ".github/workflows/docs.yml"
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-latest
22+
defaults:
23+
run:
24+
working-directory: docs
25+
steps:
26+
- name: Check out repository with tags
27+
uses: actions/checkout@v4
28+
with:
29+
fetch-depth: 0
30+
31+
- name: Set up Node.js
32+
uses: actions/setup-node@v4
33+
with:
34+
node-version: 22
35+
cache: npm
36+
cache-dependency-path: docs/package-lock.json
37+
38+
- name: Install dependencies
39+
run: npm ci
40+
41+
- name: Lint
42+
run: npm run lint
43+
44+
- name: Build
45+
run: npm run build

docs/README.md

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
# metbit documentation site
22

3-
This is the Next.js documentation app for `metbit`. It uses the App Router and MDX pages for guides and API reference content.
3+
This is the Next.js documentation app for `metbit`. It provides release-specific
4+
guides and API reference pages generated from each published GitHub release.
45

56
## Structure
67

7-
- `app/` — App Router
8-
- `page.tsx` — Landing page
9-
- `docs/` — Docs section with a sidebar layout
10-
- `overview/page.mdx`
11-
- `getting-started/page.mdx`
12-
- `api/` — API reference pages
8+
- `app/docs/[version]/` — version-aware documentation routes
9+
- `content/generated/releases.json` — published release manifest
10+
- `content/generated/snapshots/` — deduplicated API snapshots parsed from Git tags
11+
- `scripts/sync_version_docs.py` — release and API documentation generator
1312
- `globals.css` — global and docs layout styles
1413
- `next.config.js` — MDX-enabled config
1514
- `package.json` — scripts and dependencies
@@ -21,18 +20,29 @@ This is the Next.js documentation app for `metbit`. It uses the App Router and M
2120
3. Dev server: `npm run dev`
2221
4. Open: http://localhost:3000
2322

23+
## Refresh versioned documentation
24+
25+
The sync script reads GitHub Releases and the corresponding local Git tags:
26+
27+
```bash
28+
npm run docs:sync
29+
```
30+
31+
Run `git fetch --tags` first when new releases have been published. The
32+
generator stores identical parsed APIs once and maps every release to its
33+
matching snapshot, keeping the Vercel deployment compact.
34+
2435
## Authoring notes
2536

26-
- Add new docs by creating folders under `app/docs/<slug>/page.mdx`.
27-
- API pages live under `app/docs/api/<slug>/page.mdx`.
28-
- Prefer root imports in examples, for example `from metbit import pca, opls_da`.
29-
- Use subpackage imports only when documenting advanced internals, for example `from metbit.nmr.alignment import PeakAligner`.
30-
- Keep code examples runnable with current package exports.
31-
- MDX allows mixing Markdown with React components.
37+
- New releases require a GitHub Release and a matching local Git tag.
38+
- API pages are generated from source signatures and docstrings; improve the
39+
Python docstring when generated documentation is incomplete.
40+
- Historical releases may not support current Python versions or dependencies.
3241
- Icons come from `react-icons`.
3342

3443
## Maintenance checklist
3544

36-
- Update quick-start examples when public imports change in `metbit/__init__.py`.
37-
- Keep the API index aligned with public exports and important subpackage utilities.
45+
- Run `npm run docs:sync` after publishing a release.
46+
- Review the generated manifest and release count.
47+
- Check one current and one historical version.
3848
- Run `npm run build` before publishing the docs site.

docs/app/components/DocsSearch.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use client'
2+
3+
import { useMemo, useState } from 'react'
4+
import { FiSearch, FiX } from 'react-icons/fi'
5+
6+
type SearchItem = {
7+
label: string
8+
detail: string
9+
href: string
10+
}
11+
12+
export default function DocsSearch({ items }: { items: SearchItem[] }) {
13+
const [query, setQuery] = useState('')
14+
const results = useMemo(() => {
15+
const normalized = query.trim().toLowerCase()
16+
if (!normalized) return []
17+
return items
18+
.filter((item) => `${item.label} ${item.detail}`.toLowerCase().includes(normalized))
19+
.slice(0, 12)
20+
}, [items, query])
21+
22+
return (
23+
<div className="docsSearch">
24+
<FiSearch aria-hidden />
25+
<input
26+
aria-label="Search documentation"
27+
placeholder="Search documentation"
28+
value={query}
29+
onChange={(event) => setQuery(event.target.value)}
30+
/>
31+
{query ? (
32+
<button type="button" aria-label="Clear search" onClick={() => setQuery('')}>
33+
<FiX aria-hidden />
34+
</button>
35+
) : (
36+
<kbd>/</kbd>
37+
)}
38+
{query && (
39+
<div className="searchResults">
40+
{results.length ? (
41+
results.map((result) => (
42+
<a key={result.href} href={result.href} onClick={() => setQuery('')}>
43+
<strong>{result.label}</strong>
44+
<span>{result.detail}</span>
45+
</a>
46+
))
47+
) : (
48+
<p>No documentation found.</p>
49+
)}
50+
</div>
51+
)}
52+
</div>
53+
)
54+
}

docs/app/components/MetbitMark.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function MetbitMark() {
2+
return (
3+
<svg className="metbitMark" viewBox="0 0 44 30" role="img" aria-label="metbit">
4+
<path d="M2 26h40" />
5+
<path d="M5 26V20M10 26V13M15 26V4M20 26V17M25 26V9M30 26V21M35 26V15M40 26V23" />
6+
</svg>
7+
)
8+
}

docs/app/components/ThemeToggle.tsx

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
"use client"
1+
'use client'
2+
23
import { useEffect, useState } from 'react'
34
import { FiSun, FiMonitor, FiMoon } from 'react-icons/fi'
45

@@ -46,36 +47,37 @@ export default function ThemeToggle() {
4647
}
4748

4849
return (
49-
<div className="themeToggle" aria-label="Theme">
50-
<div className="seg" role="group" aria-label="Theme toggle">
51-
<button
52-
type="button"
53-
className={mode==='light' ? 'active' : ''}
54-
onClick={() => choose('light')}
55-
aria-pressed={mode==='light'}
56-
aria-label="Light theme"
57-
>
58-
<FiSun aria-hidden /> Light
59-
</button>
60-
<button
61-
type="button"
62-
className={mode==='system' ? 'active' : ''}
63-
onClick={() => choose('system')}
64-
aria-pressed={mode==='system'}
65-
aria-label="System theme"
66-
>
67-
<FiMonitor aria-hidden /> System
68-
</button>
69-
<button
70-
type="button"
71-
className={mode==='dark' ? 'active' : ''}
72-
onClick={() => choose('dark')}
73-
aria-pressed={mode==='dark'}
74-
aria-label="Dark theme"
75-
>
76-
<FiMoon aria-hidden /> Dark
77-
</button>
78-
</div>
50+
<div className="themeToggle" role="group" aria-label="Color theme">
51+
<button
52+
type="button"
53+
className={mode === 'light' ? 'active' : ''}
54+
onClick={() => choose('light')}
55+
aria-pressed={mode === 'light'}
56+
aria-label="Use light theme"
57+
title="Light theme"
58+
>
59+
<FiSun aria-hidden />
60+
</button>
61+
<button
62+
type="button"
63+
className={mode === 'system' ? 'active' : ''}
64+
onClick={() => choose('system')}
65+
aria-pressed={mode === 'system'}
66+
aria-label="Use system theme"
67+
title="System theme"
68+
>
69+
<FiMonitor aria-hidden />
70+
</button>
71+
<button
72+
type="button"
73+
className={mode === 'dark' ? 'active' : ''}
74+
onClick={() => choose('dark')}
75+
aria-pressed={mode === 'dark'}
76+
aria-label="Use dark theme"
77+
title="Dark theme"
78+
>
79+
<FiMoon aria-hidden />
80+
</button>
7981
</div>
8082
)
8183
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use client'
2+
3+
import { usePathname, useRouter } from 'next/navigation'
4+
5+
import type { Release } from '@/lib/versioned-docs'
6+
7+
export default function VersionSelector({
8+
releases,
9+
current,
10+
}: {
11+
releases: Release[]
12+
current: string
13+
}) {
14+
const pathname = usePathname()
15+
const router = useRouter()
16+
17+
function selectVersion(tag: string) {
18+
const segments = pathname.split('/')
19+
if (segments[1] === 'docs' && segments[2]) {
20+
segments[2] = encodeURIComponent(tag)
21+
const stableSuffix = segments[3]
22+
if (stableSuffix === 'getting-started' || stableSuffix === 'releases' || stableSuffix === 'api' && segments.length === 4) {
23+
router.push(segments.join('/'))
24+
} else {
25+
router.push(`/docs/${encodeURIComponent(tag)}`)
26+
}
27+
return
28+
}
29+
router.push(`/docs/${encodeURIComponent(tag)}`)
30+
}
31+
32+
return (
33+
<label className="versionControl">
34+
<span>Version</span>
35+
<select value={current} onChange={(event) => selectVersion(event.target.value)}>
36+
{releases.map((release) => (
37+
<option key={release.tag} value={release.tag}>
38+
{release.version}
39+
</option>
40+
))}
41+
</select>
42+
</label>
43+
)
44+
}

0 commit comments

Comments
 (0)