Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/docs-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/**'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ current-docs-fingerprint.json
deployed-docs-fingerprint.json
lib/external-docs-content.json
lib/lastUpdated.json
lib/release-catalog.json
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
3 changes: 2 additions & 1 deletion components/CardLink.js
Original file line number Diff line number Diff line change
@@ -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 (
<Link href={href} className="card-link">
{icon && (
Expand All @@ -10,6 +10,7 @@ export function CardLink({ title, href, icon, children }) {
)}
<span className="card-link-text">
<span className="card-link-title">{title}</span>
{badge && <span className="release-badge">{badge}</span>}
{children && <span className="card-link-desc">{children}</span>}
</span>
</Link>
Expand Down
35 changes: 35 additions & 0 deletions components/DocsSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Link from 'next/link';

const icons = {
guide: <path d="M12 6.5C9.5 4.5 6 4 3 5v14c3-1 6.5-.5 9 1.5C14.5 18.5 18 18 21 19V5c-3-1-6.5-.5-9 1.5Zm0 0v14" />,
integration: <path d="m8 6-6 6 6 6m8-12 6 6-6 6m-3-15-2 18" />,
reference: <><rect x="5" y="3" width="15" height="18" rx="2" /><path d="M9 3v18M3 7h4m-4 5h4m-4 5h4m5-10h5m-5 4h5" /></>,
changes: <><path d="M3 11a9 9 0 1 1 2.6 7.4M3 4v7h7" /><path d="M12 7v5l3 2" /></>,
};

/** 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 (
<section className="docs-section" aria-labelledby={headingId}>
<div className="docs-section-intro">
<span className="docs-section-icon" aria-hidden="true">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round">
{icons[icon]}
</svg>
</span>
<h2 id={headingId}>
<Link href={href} className="docs-section-title">
{title}
<svg className="docs-section-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M4 12h16m-6-6 6 6-6 6" />
</svg>
</Link>
</h2>
<p>{description}</p>
</div>
<div className="docs-section-links">{children}</div>
</section>
);
}
105 changes: 70 additions & 35 deletions components/Layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 }) => (
<Link
key={section}
href={children[0].path}
className={`header-nav-link${currentSection === section ? ' header-nav-link-active' : ''}`}
aria-current={currentSection === section ? 'location' : undefined}
>
{section}
</Link>
));
}

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;
Expand All @@ -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,
Expand All @@ -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') {
Expand Down Expand Up @@ -91,7 +110,7 @@ export default function Layout({ children, frontmatter }) {
<link rel="canonical" href={canonicalUrl} />

{/* Open Graph */}
<meta property="og:type" content="article" />
<meta property="og:type" content={isLanding ? 'website' : 'article'} />
<meta property="og:title" content={pageTitle} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonicalUrl} />
Expand Down Expand Up @@ -120,23 +139,25 @@ export default function Layout({ children, frontmatter }) {

<AnnouncementBanner />

<header className="layout-header">
<header className={`layout-header${isLanding ? ' layout-header-landing' : ''}`}>
<a href="#main-content" className="skip-to-content">Skip to content</a>
<div className="layout-header-inner">
<button
className="hamburger-btn"
onClick={() => setSidebarOpen(!sidebarOpen)}
aria-label="Toggle navigation"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M3 5h14M3 10h14M3 15h14"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</button>
{!isLanding && (
<button
className="hamburger-btn"
onClick={() => setSidebarOpen(!sidebarOpen)}
aria-label="Toggle navigation"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M3 5h14M3 10h14M3 15h14"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</button>
)}
<Link href="/" className="layout-logo">
<img
src="/images/certified_wordmark_black.svg"
Expand All @@ -162,8 +183,7 @@ export default function Layout({ children, frontmatter }) {
</Link>
<span className="header-divider" aria-hidden="true" />
<nav className="header-nav" aria-label="Main navigation">
<Link href="/getting-started/quickstart" className="header-nav-link">Docs</Link>
<Link href="/tools/scaffold" className="header-nav-link">Tools</Link>
<SectionLinks currentSection={currentSection} />
</nav>
<div style={{ flex: 1 }} />
<button
Expand Down Expand Up @@ -202,22 +222,35 @@ export default function Layout({ children, frontmatter }) {
</div>
</header>

<div className="layout-container">
<Sidebar
isOpen={sidebarOpen}
onClose={() => setSidebarOpen(false)}
collapsed={sidebarCollapsed}
onToggleCollapse={toggleCollapsed}
/>
{isLanding && (
<nav className="landing-section-nav" aria-label="Documentation sections">
<SectionLinks />
</nav>
)}

<div className={`layout-container${isLanding ? ' layout-container-landing' : ''}`}>
{!isLanding && (
<Sidebar
isOpen={sidebarOpen}
onClose={() => setSidebarOpen(false)}
collapsed={sidebarCollapsed}
onToggleCollapse={toggleCollapsed}
/>
)}

<main className="layout-content" id="main-content">
<Breadcrumbs />
{frontmatter && <CopyRawButton />}
<LastUpdated />
<main className={`layout-content${isLanding ? ' docs-landing' : ''}`} id="main-content">
{!isLanding && (
<>
<Breadcrumbs />
{frontmatter && <CopyRawButton />}
<LastUpdated />
</>
)}
{isLanding && <p className="docs-landing-eyebrow">Documentation</p>}
<article>{children}</article>


{(prev || next) && (
{!isLanding && (prev || next) && (
<nav className="pagination" aria-label="Page navigation">
<div className="pagination-prev">
{prev && (
Expand All @@ -239,9 +272,11 @@ export default function Layout({ children, frontmatter }) {
)}
</main>

<aside className="layout-toc">
<TableOfContents />
</aside>
{!isLanding && (
<aside className="layout-toc">
<TableOfContents />
</aside>
)}
</div>
<SearchDialog isOpen={searchOpen} onClose={() => setSearchOpen(false)} />
</>
Expand Down
8 changes: 4 additions & 4 deletions components/SearchDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand Down
25 changes: 23 additions & 2 deletions components/Sidebar.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -37,6 +37,7 @@ function NavItem({ item, currentPath, depth = 0 }) {
style={{ paddingLeft: `${16 + depth * 16}px` }}
>
{item.title}
{item.badge && <span className="sidebar-release-badge">{item.badge}</span>}
</Link>
) : (
<span
Expand Down Expand Up @@ -112,6 +113,8 @@ function NavSection({ item, currentPath }) {
export function Sidebar({ isOpen, onClose, collapsed, onToggleCollapse }) {
const router = useRouter();
const currentPath = router.asPath.split('#')[0].split('?')[0];
const currentSection = getNavigationSection(currentPath);
const visibleNavigation = currentSection ? [currentSection] : navigation;

const onCloseRef = useRef(onClose);
useEffect(() => {
Expand Down Expand Up @@ -169,8 +172,26 @@ export function Sidebar({ isOpen, onClose, collapsed, onToggleCollapse }) {

{/* Nav content — hidden when collapsed */}
<div className="sidebar-content">
<div className="sidebar-section-switcher">
<span className="sidebar-section-switcher-title">Sections</span>
{navigation.map((section) => {
const overview = section.children.find((child) => child.path);
if (!overview) return null;

const active = section.section === currentSection?.section;
return (
<Link
key={section.section}
href={overview.path}
className={`sidebar-section-switcher-link${active ? ' sidebar-section-switcher-link-active' : ''}`}
>
{section.section}
</Link>
);
})}
</div>
<ul className="sidebar-nav">
{navigation.map((item, i) => (
{visibleNavigation.map((item, i) => (
<NavSection
key={item.section || item.path || i}
item={item}
Expand Down
22 changes: 22 additions & 0 deletions docs-sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,25 @@ sources:
repo: hypercerts-org/hypercerts-lexicon
ref: main
path: CHANGELOG.md
trackRelease: true

- id: cgs-changelog
title: Certified Group Service changelog
repo: hypercerts-org/certified-group-service
ref: main
path: CHANGELOG.md
trackRelease: true

- id: relay-changelog
title: Hypercerts Relay changelog
repo: hypercerts-org/hypercerts-relay
ref: main
path: CHANGELOG.md
trackRelease: true

- id: feed-changelog
title: Hypercerts Feed Service changelog
repo: hypercerts-org/hypercerts-feed-service
ref: main
path: CHANGELOG.md
trackRelease: true
Loading