docs(docs): add new problem documentation pages and source selector component

This commit is contained in:
Prad Nukala committed 2026-10-07 12:58:59 -04:00
1 parent 598688977a
commit 5627d0ca64
9 files changed
+321 -6

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
---
/**
* The `Sidebar` slot (also the mobile drawer's nav, which defaults to it): a
* Fumadocs-style content-source selector, then the pinned links, then
* Blume's own nav tree scoped to the active source (`sources.ts`).
*
* The pinned links live here, not in `navigation.featured`: Blume renders
* featured links above this slot, so the selector could not sit over them.
*
* The selector is a `<details>` with Blume's `data-blume-dropdown` light
* dismiss (outside press, Escape, focus leaving), like its own NavSelector.
*/
import Icon from "blume/components/Icon.astro";
import { withBase, withMountedBase } from "blume/components/islands/base-path.ts";
import NavTree from "blume/components/layout/NavTree.astro";
import type { UIStrings } from "blume/core/i18n-ui.ts";
import { isUnderPath } from "blume/core/navigation.ts";
import type { NavNode } from "blume/core/types.ts";
import { SOURCES } from "../sources.ts";
interface Props {
items: NavNode[];
currentRoute: string;
strings?: UIStrings["nav"];
ids?: Map<NavNode, string>;
fragmentBase?: string;
}
const PINNED = [
{ label: "Changelog", href: "/changelog", icon: "clock" },
{ label: "Appendix", href: "/appendix", icon: "library" },
];
const { items, currentRoute, ...tree } = Astro.props;
const active =
SOURCES.filter((s) => isUnderPath(currentRoute, s.path)).sort(
(a, b) => b.path.length - a.path.length,
)[0] ?? SOURCES[0]!;
const claims = (node: NavNode, path: string) => {
const at = node.kind === "page" ? node.route : node.path;
return at !== undefined && isUnderPath(at, path);
};
// The root source keeps every node no other source claims; any other source
// shows its folder's children, as if that folder were the content root.
const scoped =
active.path === "/"
? items.filter(
(node) => !SOURCES.some((s) => s.path !== "/" && claims(node, s.path)),
)
: items.flatMap((node) =>
node.kind === "group" && node.path === active.path ? node.children : [],
);
---
<details class="group relative mb-3" data-blume-dropdown>
<summary
aria-label={`Content source: ${active.label}`}
class="flex w-full cursor-pointer list-none items-center gap-2.5 rounded-xl border border-border bg-muted/50 p-1.5 pe-2.5 text-foreground text-sm transition-colors hover:bg-muted group-open:bg-muted [&::-webkit-details-marker]:hidden"
>
<span class="flex size-7 shrink-0 items-center justify-center rounded-lg border border-border bg-background">
<Icon name={active.icon} size={15} />
</span>
<span class="min-w-0 flex-1 truncate font-medium">{active.label}</span>
<Icon class="shrink-0 text-muted-foreground" name="chevrons-up-down" size={14} />
</summary>
<div
class="absolute inset-x-0 z-50 mt-1.5 rounded-xl border border-border bg-background p-1 shadow-xl"
data-blume-dropdown-panel
>
{
SOURCES.map((source) => (
<a
aria-current={source === active ? "true" : undefined}
class="flex items-start gap-2.5 rounded-lg p-2 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground"
href={withMountedBase(source.path)}
>
<Icon class="mt-0.5 shrink-0" name={source.icon} size={16} />
<span class="min-w-0 flex-1">
<span class="block font-medium">{source.label}</span>
<span class="block text-muted-foreground text-xs">{source.description}</span>
</span>
{source === active && <Icon class="mt-0.5 shrink-0" name="check" size={14} />}
</a>
))
}
</div>
</details>
<nav aria-label="Featured" class="mb-4 border-border border-b pb-4">
<ul class="m-0 list-none p-0">
{
PINNED.map((link) => (
<li>
<a
aria-current={currentRoute === link.href ? "page" : undefined}
class="flex items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
href={withBase(link.href)}
>
<Icon class="shrink-0" name={link.icon} size={14} />
<span class="flex-1 truncate">{link.label}</span>
</a>
</li>
))
}
</ul>
</nav>
<NavTree {...tree} currentRoute={currentRoute} items={scoped} />
<script>
import { installDropdownDismiss } from "blume/components/dropdown-dismiss.ts";
installDropdownDismiss();
</script>