diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml index 4260c4a..9a2f8d1 100644 --- a/.github/workflows/docs-ci.yml +++ b/.github/workflows/docs-ci.yml @@ -15,6 +15,12 @@ on: - 'lib/generate-raw-pages.js' - 'lib/generate-search-index.js' - 'lib/compare-docs-fingerprint.js' + - 'lib/releases.js' + - 'lib/github-auth.js' + - 'lib/release-components.json' + - 'lib/protocol-releases.json' + - 'lib/navigation.js' + - 'styles/**' - 'markdoc/**' - 'next.config.js' - 'pages/**' diff --git a/.gitignore b/.gitignore index 06937ba..3bd32a3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ current-docs-fingerprint.json deployed-docs-fingerprint.json lib/external-docs-content.json lib/lastUpdated.json +lib/release-catalog.json diff --git a/AGENTS.md b/AGENTS.md index fc5058d..3e069a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,11 @@ # Agent Instructions +## Documentation writing + +- Regular documentation and inline code comments describe the current state: what a concept or component is, how it works, and how to use it. +- Keep historical decisions, previous approaches, and the reasons for replacing them in changelogs and blog posts. Do not add a backstory to a current-state explanation. +- Keep overview pages concise. Use names, version badges, and short descriptions rather than commentary about how the documentation was assembled. + ## Git Workflow — Branch + PR (MANDATORY) **NEVER commit directly to `main`.** All work goes through feature branches and pull requests. diff --git a/components/CardLink.js b/components/CardLink.js index c860124..08995e5 100644 --- a/components/CardLink.js +++ b/components/CardLink.js @@ -1,6 +1,6 @@ import Link from "next/link"; -export function CardLink({ title, href, icon, children }) { +export function CardLink({ title, href, icon, badge, children }) { return ( {icon && ( @@ -10,6 +10,7 @@ export function CardLink({ title, href, icon, children }) { )} {title} + {badge && {badge}} {children && {children}} diff --git a/components/DocsSection.js b/components/DocsSection.js new file mode 100644 index 0000000..2671fa2 --- /dev/null +++ b/components/DocsSection.js @@ -0,0 +1,35 @@ +import Link from 'next/link'; + +const icons = { + guide: , + integration: , + reference: <>, + changes: <>, +}; + +/** A section introduction and its curated links on the documentation landing page. */ +export function DocsSection({ title, href, icon, description, children }) { + const headingId = `docs-${icon}`; + + return ( +
+
+ +

+ + {title} + + +

+

{description}

+
+
{children}
+
+ ); +} diff --git a/components/Layout.js b/components/Layout.js index 1aab698..8c7d293 100644 --- a/components/Layout.js +++ b/components/Layout.js @@ -4,7 +4,7 @@ import Link from 'next/link'; import { useRouter } from 'next/router'; import { Sidebar } from './Sidebar'; import { TableOfContents } from './TableOfContents'; -import { getPrevNext } from '../lib/navigation'; +import { getNavigationSection, getPrevNext, navigation } from '../lib/navigation'; import { LastUpdated } from './LastUpdated'; import { Breadcrumbs } from './Breadcrumbs'; import { ThemeToggle } from './ThemeToggle'; @@ -18,12 +18,27 @@ const DEFAULT_DESCRIPTION = 'Documentation for the Hypercerts Protocol — structured, verifiable records of impact work built on AT Protocol.'; const OG_IMAGE = `${SITE_URL}/images/hypercerts_logo.png`; +function SectionLinks({ currentSection }) { + return navigation.map(({ section, children }) => ( + + {section} + + )); +} + export default function Layout({ children, frontmatter }) { const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [searchOpen, setSearchOpen] = useState(false); const router = useRouter(); const currentPath = router.asPath.split('#')[0].split('?')[0]; + const isLanding = currentPath === '/'; + const currentSection = getNavigationSection(currentPath)?.section; const { prev, next } = getPrevNext(currentPath); const title = frontmatter?.title; @@ -35,7 +50,7 @@ export default function Layout({ children, frontmatter }) { const jsonLd = { '@context': 'https://schema.org', - '@type': 'TechArticle', + '@type': isLanding ? 'WebPage' : 'TechArticle', headline: title || SITE_NAME, description, url: canonicalUrl, @@ -61,6 +76,10 @@ export default function Layout({ children, frontmatter }) { } }, []); + useEffect(() => { + setSidebarOpen(false); + }, [currentPath]); + useEffect(() => { const handler = (e) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { @@ -91,7 +110,7 @@ export default function Layout({ children, frontmatter }) { {/* Open Graph */} - + @@ -120,23 +139,25 @@ export default function Layout({ children, frontmatter }) { -
+
Skip to content
- + {!isLanding && ( + + )}
-
- setSidebarOpen(false)} - collapsed={sidebarCollapsed} - onToggleCollapse={toggleCollapsed} - /> + {isLanding && ( + + )} + +
+ {!isLanding && ( + setSidebarOpen(false)} + collapsed={sidebarCollapsed} + onToggleCollapse={toggleCollapsed} + /> + )} -
- - {frontmatter && } - +
+ {!isLanding && ( + <> + + {frontmatter && } + + + )} + {isLanding &&

Documentation

}
{children}
- {(prev || next) && ( + {!isLanding && (prev || next) && (
- + {!isLanding && ( + + )}
setSearchOpen(false)} /> diff --git a/components/SearchDialog.js b/components/SearchDialog.js index b451eff..477dcc0 100644 --- a/components/SearchDialog.js +++ b/components/SearchDialog.js @@ -23,12 +23,12 @@ function getSnippet(body, query, contextChars = 60) { // Paths for the curated quick links shown in the empty state const QUICK_LINK_PATHS = [ - '/getting-started/quickstart', + '/guide', + '/client-integration', + '/reference', + '/changes', '/core-concepts/what-is-hypercerts', '/core-concepts/hypercerts-core-data-model', - '/tools/scaffold', - '/architecture/overview', - '/reference/glossary', ]; export function SearchDialog({ isOpen, onClose }) { diff --git a/components/Sidebar.js b/components/Sidebar.js index 909cf3e..74a8ebb 100644 --- a/components/Sidebar.js +++ b/components/Sidebar.js @@ -1,7 +1,7 @@ import { useState, useEffect, useRef } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { navigation } from '../lib/navigation'; +import { getNavigationSection, navigation } from '../lib/navigation'; function isActive(path, currentPath) { return path === currentPath; @@ -37,6 +37,7 @@ function NavItem({ item, currentPath, depth = 0 }) { style={{ paddingLeft: `${16 + depth * 16}px` }} > {item.title} + {item.badge && {item.badge}} ) : ( { @@ -169,8 +172,26 @@ export function Sidebar({ isOpen, onClose, collapsed, onToggleCollapse }) { {/* Nav content — hidden when collapsed */}
+
+ Sections + {navigation.map((section) => { + const overview = section.children.find((child) => child.path); + if (!overview) return null; + + const active = section.section === currentSection?.section; + return ( + + {section.section} + + ); + })} +