// DocsLayout.jsx — v0.19.0 / roadmap item #30. // // Left-side flyout nav + content area for the `/docs/*` route tree: // // /docs → redirect to /docs/user-guide // /docs/user-guide → DOCS.md (existing v0.14.0 content) // /docs/sessions → redirect to /docs/sessions/about // /docs/sessions/about → README.md from the sessions repo // /docs/sessions/:nnnn → per-session index page // /docs/sessions/:nnnn/:file → per-transcript view // // The flyout is a persistent left sidebar on desktop and a slide-out // drawer on mobile (toggled by the icon button in the docs header). // The session list is driven by the `/api/docs/sessions/manifest` // fetch: // - loading → skeleton in the nav (three placeholder rows) // - manifest 502 → error banner in the nav with "Try again" // - empty manifest → only "About" under Sessions; no NNNN rows // // Amplitude analytics (per SPEC §21): // - track('Doc Viewed', { section: '...' }) on each sub-route mount; // the sub-route component owns the fire (it knows the section). // - flyout buttons + links carry `aria-label` + `data-amp-track-name` // so autocapture rows are readable rather than ":nth-child(7)". import { useEffect, useState, useCallback } from 'react' import { Link, useNavigate, useLocation, Outlet } from 'react-router-dom' import { getSessionsManifest } from '../api.js' export default function DocsLayout({ authenticated }) { const [manifest, setManifest] = useState(null) const [manifestState, setManifestState] = useState('loading') // loading | ok | error const [drawerOpen, setDrawerOpen] = useState(false) const [reloadTick, setReloadTick] = useState(0) const navigate = useNavigate() const location = useLocation() useEffect(() => { let active = true setManifestState('loading') getSessionsManifest() .then(payload => { if (!active) return setManifest(payload || {}) setManifestState('ok') }) .catch(() => { if (!active) return setManifest({}) setManifestState('error') }) return () => { active = false } }, [reloadTick]) // Close the mobile drawer on every navigation so a click in the nav // doesn't strand the user on a drawer-open view. useEffect(() => { setDrawerOpen(false) }, [location.pathname]) const retryManifest = useCallback(() => { setReloadTick(t => t + 1) }, []) return (
Docs {!authenticated && ( Home )}
{drawerOpen && (
) } function DocsNav({ manifest, manifestState, onRetry, currentPath }) { const isActive = (path) => currentPath === path || currentPath.startsWith(path + '/') // Sort session keys ascending (newest sessions render last). The // manifest's keys are zero-padded 4-digit strings so lexicographic // order is the same as numeric. const sessionKeys = Object.keys(manifest || {}).sort() return ( ) }