@eqtylab/docs 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -12
- package/dist/{chunk-VJN3HXKM.js → chunk-LNQV7TVH.js} +18 -6
- package/dist/chunk-LNQV7TVH.js.map +1 -0
- package/dist/chunk-OM7745ZQ.js +77 -0
- package/dist/chunk-OM7745ZQ.js.map +1 -0
- package/dist/{chunk-IWKZQ4CW.js → chunk-VNQZJMDO.js} +4 -2
- package/dist/chunk-VNQZJMDO.js.map +1 -0
- package/dist/{chunk-K7PJRTK3.js → chunk-X4LT7ASQ.js} +31 -4
- package/dist/chunk-X4LT7ASQ.js.map +1 -0
- package/dist/config.d.ts +10 -1
- package/dist/config.js +1 -1
- package/dist/index.js +297 -28
- package/dist/index.js.map +1 -1
- package/dist/internal/extract-versions.d.ts +33 -0
- package/dist/internal/rehype-base-url.d.ts +9 -2
- package/dist/internal/rehype-base-url.js +1 -1
- package/dist/internal/remark-archive-document.d.ts +6 -0
- package/dist/loaders.d.ts +5 -5
- package/dist/loaders.js +18 -7
- package/dist/loaders.js.map +1 -1
- package/dist/nav.d.ts +4 -0
- package/dist/nav.js +21 -7
- package/dist/nav.js.map +1 -1
- package/dist/paths.d.ts +25 -3
- package/dist/paths.js +1 -1
- package/dist/runtime/chrome/GlobalSearch.tsx +7 -2
- package/dist/runtime/chrome/Header.astro +9 -6
- package/dist/runtime/chrome/NavDrawer.astro +44 -1
- package/dist/runtime/chrome/NavTree.astro +1 -11
- package/dist/runtime/chrome/Prose.astro +18 -4
- package/dist/runtime/chrome/VersionBanner.astro +19 -0
- package/dist/runtime/chrome/VersionSwitcher.tsx +105 -0
- package/dist/runtime/components/CodeFence.astro +1 -1
- package/dist/runtime/layouts/DocsPage.astro +36 -4
- package/dist/runtime/layouts/DocsShell.astro +14 -3
- package/dist/runtime/layouts/RedirectPage.astro +19 -0
- package/dist/runtime/lib/nav-data.ts +44 -15
- package/dist/runtime/lib/versions-ui.ts +12 -0
- package/dist/runtime/routes/docs-md.ts +50 -11
- package/dist/runtime/routes/docs.astro +182 -73
- package/dist/runtime/routes/llms-txt.ts +2 -1
- package/dist/runtime/routes/not-found.astro +27 -0
- package/dist/runtime/styles/prose.css +9 -2
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +1 -1
- package/dist/versions.d.ts +37 -0
- package/package.json +2 -2
- package/dist/chunk-GSSSQTEV.js +0 -51
- package/dist/chunk-GSSSQTEV.js.map +0 -1
- package/dist/chunk-IWKZQ4CW.js.map +0 -1
- package/dist/chunk-K7PJRTK3.js.map +0 -1
- package/dist/chunk-VJN3HXKM.js.map +0 -1
package/dist/nav.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nav.ts"],"names":[],"mappings":"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","file":"nav.js","sourcesContent":["/**\n * Pure nav-tree construction. Keep it free of fs and `astro:*` imports so it\n * runs in Node and unit tests; prev/next and breadcrumbs derive from this tree.\n */\nimport { docsHref, isActive, isAncestor, type PathContext } from './paths.ts';\nimport type { DocsBadge, DocsNavEntry, NavNode, TocNode } from './types.ts';\n\nexport type SortMode = 'alpha' | 'filename' | 'manual';\n\nexport interface GroupConfig {\n label?: string;\n icon?: string;\n order: string[];\n sort?: SortMode;\n collapsed?: boolean;\n hidden: boolean;\n badge?: DocsBadge;\n links: Array<{\n label: string;\n href: string;\n external?: boolean;\n icon?: string;\n badge?: DocsBadge;\n attrs: Record<string, string | number | boolean>;\n }>;\n}\n\nexport interface BuildNavOptions {\n entries: DocsNavEntry[];\n /** Keyed by directory relative to the content root; '' is the root group. */\n groups: Map<string, GroupConfig>;\n /** `Astro.url.pathname`. */\n currentPath: string;\n paths: PathContext;\n defaultCollapsed: boolean;\n defaultSort: SortMode;\n /** Appended after folder-derived nodes. Plugin-contributed sections land here. */\n extra?: NavNode[];\n onWarn?: (message: string) => void;\n}\n\n/** \"getting-started\" -> \"Getting Started\" */\nexport function titleCase(slug: string): string {\n return slug\n .split(/[-_]/)\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n\n/** Normalise an `order` token so 'foo', 'foo.mdx' and 'foo/' all mean the same child. */\nfunction normalizeOrderToken(token: string): string {\n return token\n .trim()\n .replace(/\\/+$/, '')\n .replace(/\\.(md|mdx)$/i, '');\n}\n\nfunction byLocale(a: string, b: string): number {\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });\n}\n\ninterface Classified {\n entry: DocsNavEntry;\n dir: string;\n name: string;\n isIndex: boolean;\n}\n\n/**\n * `isIndex` must come from `filePath`: Astro strips `/index` from ids, so\n * `guides/index.mdx` and `guides.mdx` both arrive as \"guides\".\n */\nfunction classify(entry: DocsNavEntry): Classified {\n const id = entry.id;\n const segments = id.split('/').filter(Boolean);\n const fileBase = entry.filePath\n ? (entry.filePath.split('/').pop() ?? '').replace(/\\.(md|mdx)$/i, '')\n : undefined;\n const isIndex = fileBase === 'index' || id === 'index';\n\n if (isIndex) {\n const dir = id === 'index' ? '' : segments.join('/');\n return { entry, dir, name: segments.at(-1) ?? '', isIndex: true };\n }\n return {\n entry,\n dir: segments.slice(0, -1).join('/'),\n name: segments.at(-1) ?? '',\n isIndex: false,\n };\n}\n\nfunction ancestorsOf(dir: string): string[] {\n if (!dir) return [];\n const parts = dir.split('/');\n const out: string[] = [];\n for (let i = 1; i <= parts.length; i++) out.push(parts.slice(0, i).join('/'));\n return out;\n}\n\nexport function buildNavTree(options: BuildNavOptions): NavNode[] {\n const {\n entries,\n groups,\n currentPath,\n paths,\n defaultCollapsed,\n defaultSort,\n extra = [],\n onWarn,\n } = options;\n\n const visible = entries.filter((e) => !e.draft);\n const classified = visible.map(classify);\n const indexOf = new Map<string, Classified>();\n const filesIn = new Map<string, Classified[]>();\n\n for (const c of classified) {\n if (c.isIndex) {\n indexOf.set(c.dir, c);\n } else {\n const list = filesIn.get(c.dir);\n if (list) list.push(c);\n else filesIn.set(c.dir, [c]);\n }\n }\n\n const dirs = new Set<string>(['']);\n for (const key of [...filesIn.keys(), ...indexOf.keys(), ...groups.keys()]) {\n dirs.add(key);\n for (const ancestor of ancestorsOf(key)) dirs.add(ancestor);\n }\n dirs.delete('~root'); // groups-loader sentinel, never a real directory\n\n const childDirsOf = new Map<string, string[]>();\n for (const dir of dirs) {\n if (!dir) continue;\n const parent = dir.split('/').slice(0, -1).join('/');\n const list = childDirsOf.get(parent);\n if (list) list.push(dir);\n else childDirsOf.set(parent, [dir]);\n }\n\n const groupFor = (dir: string): GroupConfig | undefined =>\n groups.get(dir === '' ? '~root' : dir) ?? groups.get(dir);\n\n const building = new Set<string>();\n\n function pageNode(c: Classified): NavNode {\n const href = docsHref(c.entry.id, paths);\n return {\n kind: 'page',\n name: c.name,\n label: c.entry.label,\n href,\n icon: c.entry.icon,\n badge: c.entry.badge,\n current: isActive(currentPath, href),\n inPath: isActive(currentPath, href),\n };\n }\n\n function groupNode(dir: string): NavNode | null {\n if (building.has(dir)) return null;\n building.add(dir);\n try {\n const cfg = groupFor(dir);\n const index = indexOf.get(dir);\n const sortMode = cfg?.sort ?? defaultSort;\n\n const pages = (filesIn.get(dir) ?? []).filter((c) => !c.entry.hidden).map(pageNode);\n const subgroups = (childDirsOf.get(dir) ?? [])\n .map(groupNode)\n .filter((n): n is NavNode => n !== null);\n\n const children = orderChildren([...pages, ...subgroups], cfg, sortMode, dir, onWarn);\n\n for (const link of cfg?.links ?? []) {\n children.push({\n kind: 'link',\n name: link.label,\n label: link.label,\n href: link.href,\n icon: link.icon,\n badge: link.badge,\n external: link.external,\n attrs: link.attrs,\n });\n }\n\n if (children.length === 0 && !index) return null;\n if (cfg?.hidden) return null;\n\n const href = index ? docsHref(index.entry.id, paths) : undefined;\n const label = cfg?.label ?? index?.entry.label ?? titleCase(dir.split('/').pop() ?? '');\n\n const inPath =\n (href ? isAncestor(currentPath, href) : false) || children.some((c) => c.inPath);\n\n return {\n kind: 'group',\n name: dir.split('/').pop() ?? '',\n label,\n href,\n icon: cfg?.icon ?? index?.entry.icon,\n badge: cfg?.badge ?? index?.entry.badge,\n current: href ? isActive(currentPath, href) : false,\n inPath,\n // Containing the current page wins over `collapsed: true`.\n open: inPath || !(cfg?.collapsed ?? defaultCollapsed),\n children,\n };\n } finally {\n building.delete(dir);\n }\n }\n\n const root = groupNode('');\n const rootChildren = root?.children ?? [];\n return [...rootChildren, ...extra];\n}\n\nfunction orderChildren(\n children: NavNode[],\n cfg: GroupConfig | undefined,\n sortMode: SortMode,\n dir: string,\n onWarn?: (message: string) => void\n): NavNode[] {\n const byName = new Map<string, NavNode>();\n for (const child of children) {\n if (!byName.has(child.name)) byName.set(child.name, child);\n }\n\n const taken = new Set<NavNode>();\n const ordered: NavNode[] = [];\n\n for (const token of cfg?.order ?? []) {\n const key = normalizeOrderToken(token);\n const hit = byName.get(key);\n if (!hit) {\n onWarn?.(\n `_group.yaml in \"${dir || '<root>'}\": order lists \"${token}\", but no child is named \"${key}\". ` +\n `Available: ${[...byName.keys()].join(', ') || '(none)'}`\n );\n continue;\n }\n if (!taken.has(hit)) {\n taken.add(hit);\n ordered.push(hit);\n }\n }\n\n let rest = children.filter((c) => !taken.has(c));\n\n if (sortMode === 'manual') {\n rest = [];\n } else if (sortMode === 'filename') {\n rest.sort((a, b) => byLocale(a.name, b.name));\n } else {\n rest.sort((a, b) => byLocale(a.label, b.label) || byLocale(a.name, b.name));\n }\n\n return [...ordered, ...rest];\n}\n\n/** Depth-first list of internal, linkable nodes. Drives prev/next. */\nexport function flattenNav(tree: NavNode[]): NavNode[] {\n const out: NavNode[] = [];\n const walk = (nodes: NavNode[]) => {\n for (const node of nodes) {\n if (node.href && node.kind !== 'link') out.push(node);\n if (node.children?.length) walk(node.children);\n }\n };\n walk(tree);\n return out;\n}\n\nexport function prevNextFor(\n tree: NavNode[],\n currentPath: string\n): { prev?: NavNode; next?: NavNode } {\n const flat = flattenNav(tree);\n const i = flat.findIndex((n) => n.href && isActive(currentPath, n.href));\n if (i === -1) return {};\n return { prev: flat[i - 1], next: flat[i + 1] };\n}\n\n/** Ancestor chain from the root down to the current page. */\nexport function breadcrumbsFor(tree: NavNode[], currentPath: string): NavNode[] {\n const trail: NavNode[] = [];\n const walk = (nodes: NavNode[]): boolean => {\n for (const node of nodes) {\n const hit = node.href ? isActive(currentPath, node.href) : false;\n if (hit) {\n trail.push(node);\n return true;\n }\n if (node.children?.length) {\n trail.push(node);\n if (walk(node.children)) return true;\n trail.pop();\n }\n }\n return false;\n };\n walk(tree);\n return trail;\n}\n\n/** Nest a flat heading list into a tree, clamped to [minLevel, maxLevel]. */\nexport function buildTocTree(\n headings: Array<{ depth: number; slug: string; text: string }>,\n { minLevel = 2, maxLevel = 3 }: { minLevel?: number; maxLevel?: number } = {}\n): TocNode[] {\n const roots: TocNode[] = [];\n const stack: TocNode[] = [];\n\n for (const h of headings) {\n if (h.depth < minLevel || h.depth > maxLevel) continue;\n const node: TocNode = { depth: h.depth, slug: h.slug, text: h.text, children: [] };\n while (stack.length && (stack.at(-1) as TocNode).depth >= node.depth) stack.pop();\n const parent = stack.at(-1);\n if (parent) parent.children.push(node);\n else roots.push(node);\n stack.push(node);\n }\n return roots;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/nav.ts"],"names":[],"mappings":"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","file":"nav.js","sourcesContent":["/**\n * Pure nav-tree construction. Keep it free of fs and `astro:*` imports so it\n * runs in Node and unit tests; prev/next and breadcrumbs derive from this tree.\n */\nimport { docsHref, isActive, isAncestor, type PathContext } from './paths.ts';\nimport type { DocsBadge, DocsNavEntry, NavNode, TocNode } from './types.ts';\n\nexport type SortMode = 'alpha' | 'filename' | 'manual';\n\nexport interface GroupConfig {\n label?: string;\n icon?: string;\n /** Label for the row linking to this folder's own index page. */\n indexLabel?: string;\n order: string[];\n sort?: SortMode;\n collapsed?: boolean;\n hidden: boolean;\n badge?: DocsBadge;\n links: Array<{\n label: string;\n href: string;\n external?: boolean;\n icon?: string;\n badge?: DocsBadge;\n attrs: Record<string, string | number | boolean>;\n }>;\n}\n\nexport interface BuildNavOptions {\n entries: DocsNavEntry[];\n /** Keyed by directory relative to the content root; '' is the root group. */\n groups: Map<string, GroupConfig>;\n /** `Astro.url.pathname`. */\n currentPath: string;\n paths: PathContext;\n defaultCollapsed: boolean;\n defaultSort: SortMode;\n /** Label for the row linking to a section's own index page. Defaults to 'Overview'. */\n defaultIndexLabel?: string;\n /** Appended after folder-derived nodes. Plugin-contributed sections land here. */\n extra?: NavNode[];\n onWarn?: (message: string) => void;\n}\n\n/** \"getting-started\" -> \"Getting Started\" */\nexport function titleCase(slug: string): string {\n return slug\n .split(/[-_]/)\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n\n/** Normalise an `order` token so 'foo', 'foo.mdx' and 'foo/' all mean the same child. */\nfunction normalizeOrderToken(token: string): string {\n return token\n .trim()\n .replace(/\\/+$/, '')\n .replace(/\\.(md|mdx)$/i, '');\n}\n\nfunction byLocale(a: string, b: string): number {\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });\n}\n\ninterface Classified {\n entry: DocsNavEntry;\n dir: string;\n name: string;\n isIndex: boolean;\n}\n\n/**\n * `isIndex` must come from `filePath`: Astro strips `/index` from ids, so\n * `guides/index.mdx` and `guides.mdx` both arrive as \"guides\".\n */\nfunction classify(entry: DocsNavEntry): Classified {\n const id = entry.id;\n const segments = id.split('/').filter(Boolean);\n const fileBase = entry.filePath\n ? (entry.filePath.split('/').pop() ?? '').replace(/\\.(md|mdx)$/i, '')\n : undefined;\n const isIndex = fileBase === 'index' || id === 'index';\n\n if (isIndex) {\n const dir = id === 'index' ? '' : segments.join('/');\n return { entry, dir, name: segments.at(-1) ?? '', isIndex: true };\n }\n return {\n entry,\n dir: segments.slice(0, -1).join('/'),\n name: segments.at(-1) ?? '',\n isIndex: false,\n };\n}\n\nfunction ancestorsOf(dir: string): string[] {\n if (!dir) return [];\n const parts = dir.split('/');\n const out: string[] = [];\n for (let i = 1; i <= parts.length; i++) out.push(parts.slice(0, i).join('/'));\n return out;\n}\n\n/** A section's own page leads it, whether that is an `index.mdx` or a page named `overview`. */\nconst INDEX_LABEL = 'Overview';\n\nfunction isOverview(node: NavNode): boolean {\n return (\n node.kind === 'page' &&\n (node.name.toLowerCase() === INDEX_LABEL.toLowerCase() ||\n node.label.trim().toLowerCase() === INDEX_LABEL.toLowerCase())\n );\n}\n\nexport function buildNavTree(options: BuildNavOptions): NavNode[] {\n const {\n entries,\n groups,\n currentPath,\n paths,\n defaultCollapsed,\n defaultSort,\n // Never let this be blank: an older consumer's stored config predates the option, and\n // a nameless row is worse than a wrong one.\n defaultIndexLabel = INDEX_LABEL,\n extra = [],\n onWarn,\n } = options;\n\n const visible = entries.filter((e) => !e.draft);\n const classified = visible.map(classify);\n const indexOf = new Map<string, Classified>();\n const filesIn = new Map<string, Classified[]>();\n\n for (const c of classified) {\n if (c.isIndex) {\n indexOf.set(c.dir, c);\n } else {\n const list = filesIn.get(c.dir);\n if (list) list.push(c);\n else filesIn.set(c.dir, [c]);\n }\n }\n\n const dirs = new Set<string>(['']);\n for (const key of [...filesIn.keys(), ...indexOf.keys(), ...groups.keys()]) {\n dirs.add(key);\n for (const ancestor of ancestorsOf(key)) dirs.add(ancestor);\n }\n dirs.delete('~root'); // groups-loader sentinel, never a real directory\n\n const childDirsOf = new Map<string, string[]>();\n for (const dir of dirs) {\n if (!dir) continue;\n const parent = dir.split('/').slice(0, -1).join('/');\n const list = childDirsOf.get(parent);\n if (list) list.push(dir);\n else childDirsOf.set(parent, [dir]);\n }\n\n const groupFor = (dir: string): GroupConfig | undefined =>\n groups.get(dir === '' ? '~root' : dir) ?? groups.get(dir);\n\n const building = new Set<string>();\n\n function pageNode(c: Classified): NavNode {\n const href = docsHref(c.entry.id, paths);\n return {\n kind: 'page',\n name: c.name,\n label: c.entry.label,\n href,\n icon: c.entry.icon,\n badge: c.entry.badge,\n current: isActive(currentPath, href),\n inPath: isActive(currentPath, href),\n };\n }\n\n function groupNode(dir: string): NavNode | null {\n if (building.has(dir)) return null;\n building.add(dir);\n try {\n const cfg = groupFor(dir);\n const index = indexOf.get(dir);\n const sortMode = cfg?.sort ?? defaultSort;\n\n const pages = (filesIn.get(dir) ?? []).filter((c) => !c.entry.hidden).map(pageNode);\n const subgroups = (childDirsOf.get(dir) ?? [])\n .map(groupNode)\n .filter((n): n is NavNode => n !== null);\n\n const children = orderChildren([...pages, ...subgroups], cfg, sortMode, dir, onWarn);\n\n for (const link of cfg?.links ?? []) {\n children.push({\n kind: 'link',\n name: link.label,\n label: link.label,\n href: link.href,\n icon: link.icon,\n badge: link.badge,\n external: link.external,\n attrs: link.attrs,\n });\n }\n\n /*\n The folder's own index page gets a row of its own, first in the group.\n\n A group header only expands - it is never a link. Making it one where an index\n happens to exist teaches readers that headers are clickable, which is wrong for\n every section that has no index, and the page is easy to miss either way.\n */\n // `dir` is empty for the content root, whose index is the site's landing page - it\n // belongs in the header, not as a sidebar row.\n if (index && dir) {\n children.unshift({\n ...pageNode(index),\n name: cfg?.indexLabel ?? defaultIndexLabel,\n label: cfg?.indexLabel ?? defaultIndexLabel,\n });\n }\n\n if (children.length === 0) return null;\n if (cfg?.hidden) return null;\n\n const indexHref = index ? docsHref(index.entry.id, paths) : undefined;\n const label = cfg?.label ?? index?.entry.label ?? titleCase(dir.split('/').pop() ?? '');\n\n const inPath =\n (indexHref ? isAncestor(currentPath, indexHref) : false) || children.some((c) => c.inPath);\n\n return {\n kind: 'group',\n name: dir.split('/').pop() ?? '',\n label,\n icon: cfg?.icon ?? index?.entry.icon,\n badge: cfg?.badge ?? index?.entry.badge,\n inPath,\n // Containing the current page wins over `collapsed: true`.\n open: inPath || !(cfg?.collapsed ?? defaultCollapsed),\n children,\n };\n } finally {\n building.delete(dir);\n }\n }\n\n const root = groupNode('');\n const rootChildren = root?.children ?? [];\n return [...rootChildren, ...extra];\n}\n\nfunction orderChildren(\n children: NavNode[],\n cfg: GroupConfig | undefined,\n sortMode: SortMode,\n dir: string,\n onWarn?: (message: string) => void\n): NavNode[] {\n const byName = new Map<string, NavNode>();\n for (const child of children) {\n if (!byName.has(child.name)) byName.set(child.name, child);\n }\n\n const taken = new Set<NavNode>();\n const ordered: NavNode[] = [];\n\n for (const token of cfg?.order ?? []) {\n const key = normalizeOrderToken(token);\n const hit = byName.get(key);\n if (!hit) {\n onWarn?.(\n `_group.yaml in \"${dir || '<root>'}\": order lists \"${token}\", but no child is named \"${key}\". ` +\n `Available: ${[...byName.keys()].join(', ') || '(none)'}`\n );\n continue;\n }\n if (!taken.has(hit)) {\n taken.add(hit);\n ordered.push(hit);\n }\n }\n\n let rest = children.filter((c) => !taken.has(c));\n\n if (sortMode === 'manual') {\n rest = [];\n } else if (sortMode === 'filename') {\n rest.sort((a, b) => byLocale(a.name, b.name));\n } else {\n rest.sort((a, b) => byLocale(a.label, b.label) || byLocale(a.name, b.name));\n }\n\n const all = [...ordered, ...rest];\n\n // A section's overview leads it, however the author got it there - an `index.mdx`, a page\n // named `overview`, or one titled \"Overview\". Alphabetical order would bury it mid-list.\n const overview = all.filter(isOverview);\n return overview.length ? [...overview, ...all.filter((c) => !isOverview(c))] : all;\n}\n\n/** Depth-first list of internal, linkable nodes. Drives prev/next. */\nexport function flattenNav(tree: NavNode[]): NavNode[] {\n const out: NavNode[] = [];\n const walk = (nodes: NavNode[]) => {\n for (const node of nodes) {\n if (node.href && node.kind !== 'link') out.push(node);\n if (node.children?.length) walk(node.children);\n }\n };\n walk(tree);\n return out;\n}\n\nexport function prevNextFor(\n tree: NavNode[],\n currentPath: string\n): { prev?: NavNode; next?: NavNode } {\n const flat = flattenNav(tree);\n const i = flat.findIndex((n) => n.href && isActive(currentPath, n.href));\n if (i === -1) return {};\n return { prev: flat[i - 1], next: flat[i + 1] };\n}\n\n/** Ancestor chain from the root down to the current page. */\nexport function breadcrumbsFor(tree: NavNode[], currentPath: string): NavNode[] {\n const trail: NavNode[] = [];\n const walk = (nodes: NavNode[]): boolean => {\n for (const node of nodes) {\n const hit = node.href ? isActive(currentPath, node.href) : false;\n if (hit) {\n trail.push(node);\n return true;\n }\n if (node.children?.length) {\n trail.push(node);\n if (walk(node.children)) return true;\n trail.pop();\n }\n }\n return false;\n };\n walk(tree);\n return trail;\n}\n\n/** Nest a flat heading list into a tree, clamped to [minLevel, maxLevel]. */\nexport function buildTocTree(\n headings: Array<{ depth: number; slug: string; text: string }>,\n { minLevel = 2, maxLevel = 3 }: { minLevel?: number; maxLevel?: number } = {}\n): TocNode[] {\n const roots: TocNode[] = [];\n const stack: TocNode[] = [];\n\n for (const h of headings) {\n if (h.depth < minLevel || h.depth > maxLevel) continue;\n const node: TocNode = { depth: h.depth, slug: h.slug, text: h.text, children: [] };\n while (stack.length && (stack.at(-1) as TocNode).depth >= node.depth) stack.pop();\n const parent = stack.at(-1);\n if (parent) parent.children.push(node);\n else roots.push(node);\n stack.push(node);\n }\n return roots;\n}\n"]}
|
package/dist/paths.d.ts
CHANGED
|
@@ -13,16 +13,38 @@ export interface PathContext {
|
|
|
13
13
|
base: string;
|
|
14
14
|
/** Mounted-at prefix for docs pages, e.g. "docs". Usually empty. */
|
|
15
15
|
pathPrefix?: string;
|
|
16
|
-
/** Version segment for
|
|
16
|
+
/** Version segment for an older copy, e.g. "v3.9". Empty for latest. */
|
|
17
17
|
versionPrefix?: string;
|
|
18
18
|
}
|
|
19
19
|
/** Prefix a site-absolute path with `base`. For assets: favicons, logos, og images. */
|
|
20
20
|
export declare function withBase(path: string, ctx: PathContext): string;
|
|
21
|
-
/** The public URL for a docs collection entry id. */
|
|
21
|
+
/** The public URL for a docs collection entry id. The version rides in the route's slug, so it follows the prefix. */
|
|
22
22
|
export declare function docsHref(id: string, ctx: PathContext): string;
|
|
23
|
-
/** Remove `base` (and version
|
|
23
|
+
/** Remove `base` (and path/version prefixes) from a pathname, yielding a docs-relative path. */
|
|
24
24
|
export declare function stripBase(pathname: string, ctx: PathContext): string;
|
|
25
25
|
/** Whether `target` is the page currently being viewed. Base-aware, slash-insensitive. */
|
|
26
26
|
export declare function isActive(currentPathname: string, target: string): boolean;
|
|
27
27
|
/** Whether `target` is the current page or an ancestor of it. Drives group expansion. */
|
|
28
28
|
export declare function isAncestor(currentPathname: string, target: string): boolean;
|
|
29
|
+
interface VersionFallback {
|
|
30
|
+
base: string;
|
|
31
|
+
pathPrefix?: string;
|
|
32
|
+
/** Ids of versions that have a real copy built. */
|
|
33
|
+
copyIds: string[];
|
|
34
|
+
redirects: Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
to: string | null;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Where to send a reader whose URL named a release with no page of its own.
|
|
41
|
+
*
|
|
42
|
+
* Static hosting emits a file per URL, and stubs are emitted for bare release URLs only, so
|
|
43
|
+
* `/v3.2.4/` resolves while `/v3.2.4/components/button/` reaches the not-found page. Per-page
|
|
44
|
+
* stubs would be roughly 3,000 files that grow with every release; this recovers the same
|
|
45
|
+
* destination from one page, and unlike the bare stub it keeps the page the reader asked for.
|
|
46
|
+
*
|
|
47
|
+
* Returns null when the path names no version, which is an ordinary 404.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveVersionedPath(pathname: string, opts: VersionFallback): string | null;
|
|
50
|
+
export {};
|
package/dist/paths.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { docsHref, ensureTrailingSlash, idToPath, isActive, isAncestor, isExternalHref, joinPath, normalizePath, stripBase, withBase } from './chunk-
|
|
1
|
+
export { docsHref, ensureTrailingSlash, idToPath, isActive, isAncestor, isExternalHref, joinPath, normalizePath, resolveVersionedPath, stripBase, withBase } from './chunk-LNQV7TVH.js';
|
|
2
2
|
//# sourceMappingURL=paths.js.map
|
|
3
3
|
//# sourceMappingURL=paths.js.map
|
|
@@ -62,7 +62,10 @@ interface PagefindRawResult {
|
|
|
62
62
|
interface PagefindApi {
|
|
63
63
|
options: (opts: Record<string, unknown>) => Promise<void>;
|
|
64
64
|
init: () => Promise<void>;
|
|
65
|
-
debouncedSearch: (
|
|
65
|
+
debouncedSearch: (
|
|
66
|
+
query: string,
|
|
67
|
+
options?: { filters?: Record<string, string> }
|
|
68
|
+
) => Promise<{ results: PagefindRawResult[] } | null>;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
interface Props {
|
|
@@ -214,7 +217,9 @@ export default function GlobalSearch({ suggested = [] }: Props) {
|
|
|
214
217
|
setStatus('loading');
|
|
215
218
|
try {
|
|
216
219
|
const api = await loadPagefind();
|
|
217
|
-
|
|
220
|
+
// The shell stamps the page's version on <html>; results stay inside it.
|
|
221
|
+
const version = document.documentElement.dataset.eqDocsVersion ?? 'latest';
|
|
222
|
+
const search = await api.debouncedSearch(value, { filters: { version } });
|
|
218
223
|
// Null means a newer keystroke superseded this call.
|
|
219
224
|
if (search === null || id !== requestId.current) return;
|
|
220
225
|
const docs = await Promise.all(search.results.slice(0, MAX_RESULTS).map((r) => r.data()));
|
|
@@ -5,15 +5,19 @@ import { Icon } from '@eqtylab/equality';
|
|
|
5
5
|
import GlobalSearch from './GlobalSearch.tsx';
|
|
6
6
|
import LinkIcon from './LinkIcon.astro';
|
|
7
7
|
import ThemeToggle from './ThemeToggle.tsx';
|
|
8
|
+
import VersionSwitcher from './VersionSwitcher.tsx';
|
|
9
|
+
import type { SwitcherData } from '../lib/versions-ui.ts';
|
|
8
10
|
|
|
9
11
|
interface Props {
|
|
10
12
|
/** First page of each top-level section. Shown before a reader has any history. */
|
|
11
13
|
suggested?: Array<{ label: string; href: string }>;
|
|
12
14
|
/** False when the page forwards a consumer-provided `search` slot. */
|
|
13
15
|
showSearch?: boolean;
|
|
16
|
+
/** Absent when versioning is dormant or off. */
|
|
17
|
+
switcher?: SwitcherData;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
const { showSearch = true, suggested = [] } = Astro.props;
|
|
20
|
+
const { showSearch = true, suggested = [], switcher } = Astro.props;
|
|
17
21
|
|
|
18
22
|
const paths = { base: import.meta.env.BASE_URL, pathPrefix: CONFIG.pathPrefix };
|
|
19
23
|
const homeHref = withBase(CONFIG.pathPrefix ? `/${CONFIG.pathPrefix}/` : '/', {
|
|
@@ -32,7 +36,7 @@ const navLink = 'eq-nav-control flex';
|
|
|
32
36
|
/* Below sm the search becomes an icon in the control cluster. `order` moves it
|
|
33
37
|
among these three children, so it only works while all three stay siblings. */
|
|
34
38
|
}
|
|
35
|
-
<div class="flex h-full items-center justify-between gap-
|
|
39
|
+
<div class="flex h-full items-center justify-between gap-6 px-4 max-sm:gap-2">
|
|
36
40
|
{/* min-h-10: the mark alone leaves this link under the 24px minimum tap target. */}
|
|
37
41
|
<a
|
|
38
42
|
class="text-text-primary flex min-h-10 shrink-0 items-center gap-3 no-underline"
|
|
@@ -51,9 +55,7 @@ const navLink = 'eq-nav-control flex';
|
|
|
51
55
|
alt={CONFIG.logo.alt}
|
|
52
56
|
height="24"
|
|
53
57
|
/>
|
|
54
|
-
) :
|
|
55
|
-
<span class="whitespace-nowrap text-xl font-bold">{CONFIG.title}</span>
|
|
56
|
-
)
|
|
58
|
+
) : null
|
|
57
59
|
}
|
|
58
60
|
<span class="eq-display-gradient whitespace-nowrap text-xl font-bold forced-colors:text-inherit">
|
|
59
61
|
{CONFIG.title}
|
|
@@ -61,7 +63,7 @@ const navLink = 'eq-nav-control flex';
|
|
|
61
63
|
</a>
|
|
62
64
|
|
|
63
65
|
{/* min-w-0: without it the search pushes the theme control off a narrow header. */}
|
|
64
|
-
<div class="flex min-w-0 max-w-[
|
|
66
|
+
<div class="flex min-w-0 max-w-[22rem] flex-1 justify-center max-sm:order-2 max-sm:ml-auto max-sm:max-w-none max-sm:flex-none xl:max-w-[28rem]">
|
|
65
67
|
<slot name="search" />
|
|
66
68
|
{
|
|
67
69
|
/* client:only: with no JS there is no search, and client:load would leave a dead control. */
|
|
@@ -71,6 +73,7 @@ const navLink = 'eq-nav-control flex';
|
|
|
71
73
|
|
|
72
74
|
<div class="flex shrink-0 items-center gap-2 max-sm:order-3">
|
|
73
75
|
<slot name="versions" />
|
|
76
|
+
{switcher && <VersionSwitcher client:load data={switcher} className="eq-nav-control" />}
|
|
74
77
|
<div class="hidden items-center gap-1 lg:flex">
|
|
75
78
|
{CONFIG.header.links.map((link) => (
|
|
76
79
|
<a
|
|
@@ -10,13 +10,23 @@ import CONFIG from 'virtual:eqty-docs/config';
|
|
|
10
10
|
import type { NavNode } from '@eqtylab/docs/types';
|
|
11
11
|
import { Icon } from '@eqtylab/equality';
|
|
12
12
|
|
|
13
|
+
import type { SwitcherData } from '../lib/versions-ui.ts';
|
|
13
14
|
import LinkIcon from './LinkIcon.astro';
|
|
14
15
|
import NavTree from './NavTree.astro';
|
|
15
16
|
|
|
16
17
|
interface Props {
|
|
17
18
|
nodes: NavNode[];
|
|
19
|
+
/** Absent when versioning is dormant or off. */
|
|
20
|
+
switcher?: SwitcherData;
|
|
18
21
|
}
|
|
19
|
-
const { nodes } = Astro.props;
|
|
22
|
+
const { nodes, switcher } = Astro.props;
|
|
23
|
+
|
|
24
|
+
// data-current carries the selected state, so the rule cannot be a plain utility.
|
|
25
|
+
const versionLink =
|
|
26
|
+
'text-text-secondary hover:text-text-primary mb-0.5 flex items-center rounded-md px-2 py-1 text-sm no-underline data-[current]:text-text-primary data-[current]:font-medium';
|
|
27
|
+
|
|
28
|
+
const versions = switcher ? [switcher.latest, ...switcher.groups.flatMap((g) => g.items)] : [];
|
|
29
|
+
const currentVersion = versions.find((v) => v.current) ?? switcher?.latest;
|
|
20
30
|
---
|
|
21
31
|
|
|
22
32
|
{
|
|
@@ -37,6 +47,39 @@ const { nodes } = Astro.props;
|
|
|
37
47
|
data-pagefind-ignore
|
|
38
48
|
>
|
|
39
49
|
<nav class="block" aria-label="Documentation">
|
|
50
|
+
{
|
|
51
|
+
/* Below lg the header bar has no room for the switcher, so the versions live here. */
|
|
52
|
+
}
|
|
53
|
+
{
|
|
54
|
+
/* Closed by default. Twenty-six of these at `minor`, and more at `patch`: left open they
|
|
55
|
+
push the navigation the reader actually came for off the screen. `<details>` because
|
|
56
|
+
the sidebar's groups already are, so this is the same gesture twice rather than a
|
|
57
|
+
third one, and it needs no script inside a popover that deliberately has none. */
|
|
58
|
+
}
|
|
59
|
+
{
|
|
60
|
+
switcher && currentVersion && (
|
|
61
|
+
<details class="border-border-raised mb-2 block border-b pb-2" data-eq-versions>
|
|
62
|
+
<summary class="text-text-secondary hover:text-text-primary focus-ring flex cursor-pointer select-none items-center justify-between rounded-md px-2 py-1.5 text-sm no-underline [&::-webkit-details-marker]:hidden">
|
|
63
|
+
<span class="flex items-center gap-2">
|
|
64
|
+
<span class="text-xs font-medium uppercase">Version</span>
|
|
65
|
+
<span class="text-text-primary font-medium">{currentVersion.label}</span>
|
|
66
|
+
</span>
|
|
67
|
+
<span class="shrink-0 transition-transform duration-200 [details[open]>summary_&]:rotate-90">
|
|
68
|
+
<Icon icon="ChevronRight" size="xs" />
|
|
69
|
+
</span>
|
|
70
|
+
</summary>
|
|
71
|
+
{/* Capped rather than unbounded: the drawer scrolls too, and two nested scrolls
|
|
72
|
+
with no limit means the list can still swallow the whole panel. */}
|
|
73
|
+
<div class="mt-1 max-h-[40vh] overflow-y-auto">
|
|
74
|
+
{versions.map((item) => (
|
|
75
|
+
<a class={versionLink} href={item.href} data-current={item.current ? '' : undefined}>
|
|
76
|
+
{item.label}
|
|
77
|
+
</a>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
</details>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
40
83
|
{
|
|
41
84
|
CONFIG.header.links.map((link) => (
|
|
42
85
|
<a
|
|
@@ -47,17 +47,7 @@ const current =
|
|
|
47
47
|
>
|
|
48
48
|
<span class="flex min-w-0 flex-1 items-center gap-2 truncate">
|
|
49
49
|
{node.icon && <Icon icon={node.icon} size="sm" />}
|
|
50
|
-
{node.
|
|
51
|
-
<a
|
|
52
|
-
class="text-inherit no-underline"
|
|
53
|
-
href={node.href}
|
|
54
|
-
aria-current={node.current ? 'page' : undefined}
|
|
55
|
-
>
|
|
56
|
-
{node.label}
|
|
57
|
-
</a>
|
|
58
|
-
) : (
|
|
59
|
-
<span>{node.label}</span>
|
|
60
|
-
)}
|
|
50
|
+
<span>{node.label}</span>
|
|
61
51
|
{node.badge && (
|
|
62
52
|
<Badge variant={node.badge.variant} size="sm" className="shrink-0">
|
|
63
53
|
{node.badge.text}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { rewriteHtmlBase } from '@eqtylab/docs/internal/rehype-base-url';
|
|
4
4
|
import { Icon } from '@eqtylab/equality';
|
|
5
5
|
|
|
6
|
+
import CONFIG from 'virtual:eqty-docs/config';
|
|
7
|
+
|
|
6
8
|
import AlertBridge from '../components/AlertBridge.astro';
|
|
7
9
|
|
|
8
10
|
interface Props {
|
|
@@ -19,17 +21,28 @@ interface Props {
|
|
|
19
21
|
trail?: Array<{ label: string; href?: string }>;
|
|
20
22
|
/** Stands in for `description` in the search index when a page has none. Not rendered. */
|
|
21
23
|
fallbackSummary?: string;
|
|
24
|
+
/** Version id of an older copy; latest passes nothing. */
|
|
25
|
+
version?: string;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
const {
|
|
25
|
-
|
|
28
|
+
const {
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
deprecated,
|
|
32
|
+
markdownHref,
|
|
33
|
+
source,
|
|
34
|
+
crumbs,
|
|
35
|
+
trail,
|
|
36
|
+
fallbackSummary,
|
|
37
|
+
version,
|
|
38
|
+
} = Astro.props;
|
|
26
39
|
|
|
27
40
|
const deprecation =
|
|
28
41
|
deprecated === true ? {} : deprecated && typeof deprecated === 'object' ? deprecated : null;
|
|
29
42
|
|
|
30
43
|
// Authored frontmatter HTML bypasses rehype entirely, so base-prefix it here.
|
|
31
44
|
const deprecationMessage = deprecation?.message
|
|
32
|
-
? rewriteHtmlBase(deprecation.message, import.meta.env.BASE_URL)
|
|
45
|
+
? rewriteHtmlBase(deprecation.message, import.meta.env.BASE_URL, CONFIG.pathPrefix, version)
|
|
33
46
|
: undefined;
|
|
34
47
|
|
|
35
48
|
// Hand-rolled: every Button variant is coloured or filled, and this row is neither.
|
|
@@ -41,6 +54,7 @@ const action =
|
|
|
41
54
|
class="eq-prose"
|
|
42
55
|
data-eq-prose
|
|
43
56
|
data-pagefind-body
|
|
57
|
+
data-pagefind-filter={`version:${version ?? 'latest'}`}
|
|
44
58
|
data-pagefind-meta={crumbs ? `crumbs:${crumbs}` : undefined}
|
|
45
59
|
>
|
|
46
60
|
{
|
|
@@ -89,7 +103,7 @@ const action =
|
|
|
89
103
|
title sits entirely in the gradient's flat start. */
|
|
90
104
|
}
|
|
91
105
|
<h1
|
|
92
|
-
class="
|
|
106
|
+
class="w-fit text-4xl font-bold text-text-primary"
|
|
93
107
|
data-pagefind-meta="title"
|
|
94
108
|
>
|
|
95
109
|
{title}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
/** Every page of an older copy opens with this. `data-pagefind-ignore`: the words "latest" and the version must not match every old page in search. */
|
|
3
|
+
import { Alert } from '@eqtylab/equality';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
version: string;
|
|
7
|
+
latestLabel: string;
|
|
8
|
+
latestHref: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { version, latestLabel, latestHref } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<div class="mb-8" data-eq-chrome data-pagefind-ignore role="status">
|
|
15
|
+
<Alert variant="warning" title={`You are reading the ${version} docs.`}>
|
|
16
|
+
The latest is <a class="text-text-primary underline" href={latestHref}>{latestLabel}</a>.
|
|
17
|
+
Interactive examples are not shown in archived pages.
|
|
18
|
+
</Alert>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DropdownMenu,
|
|
3
|
+
DropdownMenuContent,
|
|
4
|
+
DropdownMenuEmpty,
|
|
5
|
+
DropdownMenuLabel,
|
|
6
|
+
DropdownMenuRadioGroup,
|
|
7
|
+
DropdownMenuRadioItem,
|
|
8
|
+
DropdownMenuSearch,
|
|
9
|
+
DropdownMenuSeparator,
|
|
10
|
+
DropdownMenuSub,
|
|
11
|
+
DropdownMenuSubContent,
|
|
12
|
+
DropdownMenuSubTrigger,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
Icon,
|
|
15
|
+
} from '@eqtylab/equality';
|
|
16
|
+
|
|
17
|
+
import type { SwitcherData } from '../lib/versions-ui.ts';
|
|
18
|
+
|
|
19
|
+
/* The trigger lays itself out. It also carries a chrome class from the header, and when that
|
|
20
|
+
class stopped supplying `flex` the icon, label and chevron stacked into a column and overflowed
|
|
21
|
+
the button. A control should not depend on its caller to sit on one line. */
|
|
22
|
+
const TRIGGER = 'inline-flex items-center gap-1.5 whitespace-nowrap';
|
|
23
|
+
|
|
24
|
+
/* Radix caps both surfaces at the height available below the trigger and Equality sets no
|
|
25
|
+
overflow, so a list taller than that cap spills instead of scrolling. Searching is when that
|
|
26
|
+
bites: every match is lifted out of its submenu into the root, which then holds all
|
|
27
|
+
twenty-seven. A `max-h` of our own is not the fix and does not even apply, measured: Equality's
|
|
28
|
+
own cap is unlayered CSS and beats a utility whatever the value. */
|
|
29
|
+
const SURFACE = 'overflow-y-auto';
|
|
30
|
+
|
|
31
|
+
interface Props {
|
|
32
|
+
data: SwitcherData;
|
|
33
|
+
/** Header's `.navLink`, so the trigger sits beside the links unaltered. */
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Picks a version. `modal={false}`: a modal Radix menu closes in the same tap that opens it on
|
|
39
|
+
* iOS Safari, the bug the theme menu had. A radio item does not follow an href on its own,
|
|
40
|
+
* hence the navigation in `onValueChange`.
|
|
41
|
+
*
|
|
42
|
+
* A radio group cannot cross a portal, so each menu surface carries its own: the top level for
|
|
43
|
+
* latest, and one inside every major's submenu. Only the group holding the current version shows
|
|
44
|
+
* a checked item, which is what the reader wants.
|
|
45
|
+
*/
|
|
46
|
+
export default function VersionSwitcher({ data, className }: Props) {
|
|
47
|
+
const items = [data.latest, ...data.groups.flatMap((g) => g.items)];
|
|
48
|
+
const current = items.find((i) => i.current) ?? data.latest;
|
|
49
|
+
const go = (href: string) => {
|
|
50
|
+
if (href !== current.href) window.location.assign(href);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
/* The bar has no room below lg: logo, search, theme and the drawer invoker already fill
|
|
55
|
+
390px. Hidden on a wrapper, not on the trigger: the trigger also carries Header's chrome
|
|
56
|
+
class, and at equal specificity the bundle order decides, so `hidden` there is a coin flip. */
|
|
57
|
+
<div className="max-lg:hidden">
|
|
58
|
+
<DropdownMenu modal={false}>
|
|
59
|
+
<DropdownMenuTrigger asChild>
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
className={[className, TRIGGER].filter(Boolean).join(' ')}
|
|
63
|
+
aria-label={`Version: ${current.label}. Choose a version`}
|
|
64
|
+
>
|
|
65
|
+
<Icon icon="Tag" size="sm" />
|
|
66
|
+
<span className="text-sm">{current.label}</span>
|
|
67
|
+
<Icon icon="ChevronDown" size="sm" />
|
|
68
|
+
</button>
|
|
69
|
+
</DropdownMenuTrigger>
|
|
70
|
+
<DropdownMenuContent align="end" className={SURFACE}>
|
|
71
|
+
{/* Nesting holds the browse case to four rows; this holds the jump case, which nesting
|
|
72
|
+
makes worse by burying a version one hover deep. Typing lifts every match out of its
|
|
73
|
+
submenu into this list, each carrying its major as a breadcrumb. */}
|
|
74
|
+
<DropdownMenuSearch alwaysVisible placeholder="Search versions..." />
|
|
75
|
+
<DropdownMenuRadioGroup value={current.href} onValueChange={go}>
|
|
76
|
+
{/* A heading, not a suffix on the name: the trigger shows the version alone, and
|
|
77
|
+
"v4.1 (latest)" would then disagree with it and read twice in the banner. */}
|
|
78
|
+
<DropdownMenuLabel>Latest</DropdownMenuLabel>
|
|
79
|
+
<DropdownMenuRadioItem value={data.latest.href}>
|
|
80
|
+
{data.latest.label}
|
|
81
|
+
</DropdownMenuRadioItem>
|
|
82
|
+
</DropdownMenuRadioGroup>
|
|
83
|
+
<DropdownMenuSeparator />
|
|
84
|
+
{data.groups.map((group) => (
|
|
85
|
+
<DropdownMenuSub key={group.label}>
|
|
86
|
+
<DropdownMenuSubTrigger>
|
|
87
|
+
<span>{group.label}</span>
|
|
88
|
+
</DropdownMenuSubTrigger>
|
|
89
|
+
<DropdownMenuSubContent className={SURFACE}>
|
|
90
|
+
<DropdownMenuRadioGroup value={current.href} onValueChange={go}>
|
|
91
|
+
{group.items.map((item) => (
|
|
92
|
+
<DropdownMenuRadioItem key={item.href} value={item.href}>
|
|
93
|
+
{item.label}
|
|
94
|
+
</DropdownMenuRadioItem>
|
|
95
|
+
))}
|
|
96
|
+
</DropdownMenuRadioGroup>
|
|
97
|
+
</DropdownMenuSubContent>
|
|
98
|
+
</DropdownMenuSub>
|
|
99
|
+
))}
|
|
100
|
+
<DropdownMenuEmpty>No matching version</DropdownMenuEmpty>
|
|
101
|
+
</DropdownMenuContent>
|
|
102
|
+
</DropdownMenu>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -26,7 +26,7 @@ const language = lang.toLowerCase();
|
|
|
26
26
|
loses to `.code-block` and the sample silently goes back to scrolling at 16rem.
|
|
27
27
|
*/}
|
|
28
28
|
<eq-highlight
|
|
29
|
-
class="mb-4 block [&_[class*=code-block]]:max-h-none!"
|
|
29
|
+
class="mb-4 block font-mono [&_[class*=code-block]]:max-h-none!"
|
|
30
30
|
data-eq-chrome
|
|
31
31
|
data-eq-copy={code}
|
|
32
32
|
>
|
|
@@ -7,6 +7,8 @@ import Header from '../chrome/Header.astro';
|
|
|
7
7
|
import NavDrawer from '../chrome/NavDrawer.astro';
|
|
8
8
|
import Sidebar from '../chrome/Sidebar.astro';
|
|
9
9
|
import TableOfContents from '../chrome/TableOfContents.astro';
|
|
10
|
+
import VersionBanner from '../chrome/VersionBanner.astro';
|
|
11
|
+
import type { SwitcherData } from '../lib/versions-ui.ts';
|
|
10
12
|
|
|
11
13
|
interface Props {
|
|
12
14
|
title: string;
|
|
@@ -15,9 +17,28 @@ interface Props {
|
|
|
15
17
|
toc: TocNode[];
|
|
16
18
|
showToc?: boolean;
|
|
17
19
|
splash?: boolean;
|
|
20
|
+
version?: string;
|
|
21
|
+
switcher?: SwitcherData;
|
|
22
|
+
noIndex?: boolean;
|
|
23
|
+
canonical?: string;
|
|
24
|
+
latestHref?: string;
|
|
25
|
+
latestLabel?: string;
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
const {
|
|
28
|
+
const {
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
nav,
|
|
32
|
+
toc,
|
|
33
|
+
showToc = true,
|
|
34
|
+
splash = false,
|
|
35
|
+
version,
|
|
36
|
+
switcher,
|
|
37
|
+
noIndex,
|
|
38
|
+
canonical,
|
|
39
|
+
latestHref,
|
|
40
|
+
latestLabel,
|
|
41
|
+
} = Astro.props;
|
|
21
42
|
|
|
22
43
|
// A section's own index page if it has one, else its first child. Never the sidebar repeated.
|
|
23
44
|
const suggested = nav
|
|
@@ -26,14 +47,20 @@ const suggested = nav
|
|
|
26
47
|
.slice(0, 4);
|
|
27
48
|
---
|
|
28
49
|
|
|
29
|
-
<DocsShell
|
|
50
|
+
<DocsShell
|
|
51
|
+
title={title}
|
|
52
|
+
description={description}
|
|
53
|
+
noIndex={noIndex}
|
|
54
|
+
canonical={canonical}
|
|
55
|
+
version={version}
|
|
56
|
+
>
|
|
30
57
|
{/* A prop, not slot fallback: Astro registers a named slot at compile time, so a forwarded
|
|
31
58
|
slot always counts as filled and the fallback never runs. */}
|
|
32
|
-
<Header showSearch={!Astro.slots.has('search')} suggested={suggested}>
|
|
59
|
+
<Header showSearch={!Astro.slots.has('search')} suggested={suggested} switcher={switcher}>
|
|
33
60
|
<slot name="search" slot="search" />
|
|
34
61
|
<slot name="versions" slot="versions" />
|
|
35
62
|
</Header>
|
|
36
|
-
<NavDrawer nodes={nav} />
|
|
63
|
+
<NavDrawer nodes={nav} switcher={switcher} />
|
|
37
64
|
<div class="flex items-start">
|
|
38
65
|
{!splash && <Sidebar nodes={nav} />}
|
|
39
66
|
{/* 128px of runway, so a page stops deliberately rather than at its last line. */}
|
|
@@ -42,6 +69,11 @@ const suggested = nav
|
|
|
42
69
|
data-splash={splash ? '' : undefined}
|
|
43
70
|
>
|
|
44
71
|
<div class="min-w-0 flex-1 [&>.eq-prose]:max-w-[var(--eq-docs-content-max)]">
|
|
72
|
+
{
|
|
73
|
+
version && latestHref && latestLabel && (
|
|
74
|
+
<VersionBanner version={version} latestLabel={latestLabel} latestHref={latestHref} />
|
|
75
|
+
)
|
|
76
|
+
}
|
|
45
77
|
<slot />
|
|
46
78
|
</div>
|
|
47
79
|
{!splash && showToc && toc.length > 0 && <TableOfContents items={toc} />}
|
|
@@ -12,9 +12,20 @@ interface Props {
|
|
|
12
12
|
noIndex?: boolean;
|
|
13
13
|
/** Canonical URL, when it differs from the current page. */
|
|
14
14
|
canonical?: string;
|
|
15
|
+
/** Version id of an older copy. Read by the search filter. */
|
|
16
|
+
version?: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
const { title, description, noIndex, canonical } = Astro.props;
|
|
19
|
+
const { title, description, noIndex, canonical, version } = Astro.props;
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* The `font-sans` class on <body> is load-bearing. Equality declares --font-sans in
|
|
23
|
+
* `@theme inline`, so Tailwind emits it only where something references it, and
|
|
24
|
+
* Equality's preflight sets `font-family: var(--font-sans)` on html. A consumer that
|
|
25
|
+
* never happens to use the utility drops the variable, the preflight resolves to
|
|
26
|
+
* nothing, and every page silently renders in the browser's default serif. Referencing
|
|
27
|
+
* it here is what keeps the design system's typeface applied for every consumer.
|
|
28
|
+
*/
|
|
18
29
|
|
|
19
30
|
const paths = {
|
|
20
31
|
base: import.meta.env.BASE_URL,
|
|
@@ -74,7 +85,7 @@ const themeBootstrap = `
|
|
|
74
85
|
---
|
|
75
86
|
|
|
76
87
|
<!doctype html>
|
|
77
|
-
<html lang="en">
|
|
88
|
+
<html lang="en" data-eq-docs-version={version ?? 'latest'}>
|
|
78
89
|
<head>
|
|
79
90
|
<meta charset="utf-8" />
|
|
80
91
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
@@ -86,7 +97,7 @@ const themeBootstrap = `
|
|
|
86
97
|
<script is:inline set:html={themeBootstrap} />
|
|
87
98
|
<slot name="head" />
|
|
88
99
|
</head>
|
|
89
|
-
<body>
|
|
100
|
+
<body class="font-sans">
|
|
90
101
|
<slot />
|
|
91
102
|
</body>
|
|
92
103
|
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
/** A one-line page for a release URL that has no copy of its own. Static hosting has no server redirects. */
|
|
3
|
+
import DocsShell from './DocsShell.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
to: string;
|
|
7
|
+
version: string | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { to, version } = Astro.props;
|
|
11
|
+
const label = version ?? 'the latest docs';
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<DocsShell title="Redirecting" noIndex canonical={to} version={version ?? undefined}>
|
|
15
|
+
<meta slot="head" http-equiv="refresh" content={`0; url=${to}`} />
|
|
16
|
+
<main class="text-text-secondary mx-auto max-w-prose px-6 py-24 text-sm">
|
|
17
|
+
<p>This release's docs live at <a href={to}>{label}</a>.</p>
|
|
18
|
+
</main>
|
|
19
|
+
</DocsShell>
|