@nexpress/theme-docs 0.2.2 → 0.3.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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Wires Cmd+K / Ctrl+K to focus the docs search input. The
3
+ * header renders a `<kbd>⌘K</kbd>` affordance next to the
4
+ * pill; this component is the thing that actually honors it.
5
+ */
6
+ declare function SearchKeyboardShortcut({ targetId }: {
7
+ targetId: string;
8
+ }): null;
9
+
10
+ export { SearchKeyboardShortcut };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/components/search-keyboard-shortcut.tsx
5
+ import { useEffect } from "react";
6
+ function SearchKeyboardShortcut({ targetId }) {
7
+ useEffect(() => {
8
+ function onKeydown(event) {
9
+ if (event.key !== "k" && event.key !== "K") return;
10
+ const cmdOrCtrl = event.metaKey || event.ctrlKey;
11
+ if (!cmdOrCtrl) return;
12
+ const el = document.getElementById(targetId);
13
+ if (!(el instanceof HTMLInputElement)) return;
14
+ event.preventDefault();
15
+ el.focus();
16
+ el.select();
17
+ }
18
+ document.addEventListener("keydown", onKeydown);
19
+ return () => {
20
+ document.removeEventListener("keydown", onKeydown);
21
+ };
22
+ }, [targetId]);
23
+ return null;
24
+ }
25
+ export {
26
+ SearchKeyboardShortcut
27
+ };
28
+ //# sourceMappingURL=search-keyboard-shortcut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/search-keyboard-shortcut.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\n\n/**\n * Wires Cmd+K / Ctrl+K to focus the docs search input. The\n * header renders a `<kbd>⌘K</kbd>` affordance next to the\n * pill; this component is the thing that actually honors it.\n */\nexport function SearchKeyboardShortcut({ targetId }: { targetId: string }) {\n useEffect(() => {\n function onKeydown(event: KeyboardEvent) {\n if (event.key !== \"k\" && event.key !== \"K\") return;\n const cmdOrCtrl = event.metaKey || event.ctrlKey;\n if (!cmdOrCtrl) return;\n const el = document.getElementById(targetId);\n if (!(el instanceof HTMLInputElement)) return;\n event.preventDefault();\n el.focus();\n el.select();\n }\n document.addEventListener(\"keydown\", onKeydown);\n return () => {\n document.removeEventListener(\"keydown\", onKeydown);\n };\n }, [targetId]);\n return null;\n}\n"],"mappings":";;;;AAEA,SAAS,iBAAiB;AAOnB,SAAS,uBAAuB,EAAE,SAAS,GAAyB;AACzE,YAAU,MAAM;AACd,aAAS,UAAU,OAAsB;AACvC,UAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAK;AAC5C,YAAM,YAAY,MAAM,WAAW,MAAM;AACzC,UAAI,CAAC,UAAW;AAChB,YAAM,KAAK,SAAS,eAAe,QAAQ;AAC3C,UAAI,EAAE,cAAc,kBAAmB;AACvC,YAAM,eAAe;AACrB,SAAG,MAAM;AACT,SAAG,OAAO;AAAA,IACZ;AACA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,SAAS;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACb,SAAO;AACT;","names":[]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * TOC scrollspy.
3
+ *
4
+ * Watches every h2/h3 inside the doc body and stamps the
5
+ * `aria-current` attribute on the matching TOC anchor as the
6
+ * user scrolls. Theme CSS targets `.np-docs-toc li > a[aria-current="true"]`
7
+ * to render the active marker.
8
+ *
9
+ * - Uses `IntersectionObserver` with a top-biased root margin
10
+ * so headings activate when they reach the top third of the
11
+ * viewport (not at center, which would feel laggy on long
12
+ * sections).
13
+ * - When multiple sections are visible, the one nearest the
14
+ * top of the viewport wins.
15
+ * - Mounted invisibly. The TOC HTML is server-rendered; this
16
+ * component only adjusts attributes on it post-mount.
17
+ */
18
+ declare function TocScrollspy({ ids }: {
19
+ ids: ReadonlyArray<string>;
20
+ }): null;
21
+
22
+ export { TocScrollspy };
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/components/toc-scrollspy.tsx
5
+ import { useEffect } from "react";
6
+ function TocScrollspy({ ids }) {
7
+ useEffect(() => {
8
+ if (ids.length === 0) return;
9
+ const headings = ids.map((id) => document.getElementById(id)).filter((el) => el !== null);
10
+ if (headings.length === 0) return;
11
+ const anchorsById = /* @__PURE__ */ new Map();
12
+ for (const id of ids) {
13
+ const a = document.querySelector(
14
+ `.np-docs-toc a[href="#${cssEscape(id)}"]`
15
+ );
16
+ if (a) anchorsById.set(id, a);
17
+ }
18
+ if (anchorsById.size === 0) return;
19
+ let current = null;
20
+ function setCurrent(id) {
21
+ if (id === current) return;
22
+ if (current !== null) {
23
+ const prev = anchorsById.get(current);
24
+ prev?.removeAttribute("aria-current");
25
+ }
26
+ if (id !== null) {
27
+ const next = anchorsById.get(id);
28
+ next?.setAttribute("aria-current", "true");
29
+ }
30
+ current = id;
31
+ }
32
+ const visible = /* @__PURE__ */ new Map();
33
+ const observer = new IntersectionObserver(
34
+ (entries) => {
35
+ for (const entry of entries) {
36
+ const id = entry.target.id;
37
+ if (entry.isIntersecting) {
38
+ visible.set(id, entry.boundingClientRect.top);
39
+ } else {
40
+ visible.delete(id);
41
+ }
42
+ }
43
+ if (visible.size === 0) return;
44
+ let best = null;
45
+ for (const [id, top] of visible) {
46
+ if (best === null || top < best.top) best = { id, top };
47
+ }
48
+ if (best) setCurrent(best.id);
49
+ },
50
+ {
51
+ // Activate when the heading enters the top third of the
52
+ // viewport, deactivate when it leaves the top.
53
+ rootMargin: "0px 0px -66% 0px",
54
+ threshold: [0, 1]
55
+ }
56
+ );
57
+ for (const heading of headings) observer.observe(heading);
58
+ return () => {
59
+ observer.disconnect();
60
+ if (current !== null) {
61
+ const prev = anchorsById.get(current);
62
+ prev?.removeAttribute("aria-current");
63
+ }
64
+ };
65
+ }, [ids]);
66
+ return null;
67
+ }
68
+ function cssEscape(value) {
69
+ if (typeof window !== "undefined" && typeof window.CSS?.escape === "function") {
70
+ return window.CSS.escape(value);
71
+ }
72
+ return value;
73
+ }
74
+ export {
75
+ TocScrollspy
76
+ };
77
+ //# sourceMappingURL=toc-scrollspy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/toc-scrollspy.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\n\n/**\n * TOC scrollspy.\n *\n * Watches every h2/h3 inside the doc body and stamps the\n * `aria-current` attribute on the matching TOC anchor as the\n * user scrolls. Theme CSS targets `.np-docs-toc li > a[aria-current=\"true\"]`\n * to render the active marker.\n *\n * - Uses `IntersectionObserver` with a top-biased root margin\n * so headings activate when they reach the top third of the\n * viewport (not at center, which would feel laggy on long\n * sections).\n * - When multiple sections are visible, the one nearest the\n * top of the viewport wins.\n * - Mounted invisibly. The TOC HTML is server-rendered; this\n * component only adjusts attributes on it post-mount.\n */\nexport function TocScrollspy({ ids }: { ids: ReadonlyArray<string> }) {\n useEffect(() => {\n if (ids.length === 0) return;\n const headings = ids\n .map((id) => document.getElementById(id))\n .filter((el): el is HTMLElement => el !== null);\n if (headings.length === 0) return;\n\n const anchorsById = new Map<string, HTMLAnchorElement>();\n for (const id of ids) {\n const a = document.querySelector<HTMLAnchorElement>(\n `.np-docs-toc a[href=\"#${cssEscape(id)}\"]`,\n );\n if (a) anchorsById.set(id, a);\n }\n if (anchorsById.size === 0) return;\n\n // Track which heading is \"active\" by keeping the visible-most\n // entry's id around between observer ticks. Setting `current`\n // is idempotent — only re-stamping changed attrs avoids\n // re-triggering CSS transitions on every scroll frame.\n let current: string | null = null;\n function setCurrent(id: string | null) {\n if (id === current) return;\n if (current !== null) {\n const prev = anchorsById.get(current);\n prev?.removeAttribute(\"aria-current\");\n }\n if (id !== null) {\n const next = anchorsById.get(id);\n next?.setAttribute(\"aria-current\", \"true\");\n }\n current = id;\n }\n\n const visible = new Map<string, number>();\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const id = entry.target.id;\n if (entry.isIntersecting) {\n visible.set(id, entry.boundingClientRect.top);\n } else {\n visible.delete(id);\n }\n }\n if (visible.size === 0) return;\n // Pick the id with the smallest top — that's the heading\n // furthest into the viewport from above.\n let best: { id: string; top: number } | null = null;\n for (const [id, top] of visible) {\n if (best === null || top < best.top) best = { id, top };\n }\n if (best) setCurrent(best.id);\n },\n {\n // Activate when the heading enters the top third of the\n // viewport, deactivate when it leaves the top.\n rootMargin: \"0px 0px -66% 0px\",\n threshold: [0, 1],\n },\n );\n\n for (const heading of headings) observer.observe(heading);\n return () => {\n observer.disconnect();\n if (current !== null) {\n const prev = anchorsById.get(current);\n prev?.removeAttribute(\"aria-current\");\n }\n };\n }, [ids]);\n return null;\n}\n\n/**\n * Minimal CSS.escape fallback — the heading slugify already\n * produces URL-safe ids (letters/digits/hyphen) so the only\n * thing we need to guard against is the rare hyphen-leading\n * id, which CSS attribute selectors handle natively. Just\n * passing the raw value works for all slugs the editor's\n * `slugifyHeading` emits. Kept as a named helper so a future\n * scope change (e.g. accepting operator-authored ids) only\n * has to upgrade this one spot.\n */\nfunction cssEscape(value: string): string {\n if (typeof window !== \"undefined\" && typeof window.CSS?.escape === \"function\") {\n return window.CSS.escape(value);\n }\n return value;\n}\n"],"mappings":";;;;AAEA,SAAS,iBAAiB;AAmBnB,SAAS,aAAa,EAAE,IAAI,GAAmC;AACpE,YAAU,MAAM;AACd,QAAI,IAAI,WAAW,EAAG;AACtB,UAAM,WAAW,IACd,IAAI,CAAC,OAAO,SAAS,eAAe,EAAE,CAAC,EACvC,OAAO,CAAC,OAA0B,OAAO,IAAI;AAChD,QAAI,SAAS,WAAW,EAAG;AAE3B,UAAM,cAAc,oBAAI,IAA+B;AACvD,eAAW,MAAM,KAAK;AACpB,YAAM,IAAI,SAAS;AAAA,QACjB,yBAAyB,UAAU,EAAE,CAAC;AAAA,MACxC;AACA,UAAI,EAAG,aAAY,IAAI,IAAI,CAAC;AAAA,IAC9B;AACA,QAAI,YAAY,SAAS,EAAG;AAM5B,QAAI,UAAyB;AAC7B,aAAS,WAAW,IAAmB;AACrC,UAAI,OAAO,QAAS;AACpB,UAAI,YAAY,MAAM;AACpB,cAAM,OAAO,YAAY,IAAI,OAAO;AACpC,cAAM,gBAAgB,cAAc;AAAA,MACtC;AACA,UAAI,OAAO,MAAM;AACf,cAAM,OAAO,YAAY,IAAI,EAAE;AAC/B,cAAM,aAAa,gBAAgB,MAAM;AAAA,MAC3C;AACA,gBAAU;AAAA,IACZ;AAEA,UAAM,UAAU,oBAAI,IAAoB;AACxC,UAAM,WAAW,IAAI;AAAA,MACnB,CAAC,YAAY;AACX,mBAAW,SAAS,SAAS;AAC3B,gBAAM,KAAK,MAAM,OAAO;AACxB,cAAI,MAAM,gBAAgB;AACxB,oBAAQ,IAAI,IAAI,MAAM,mBAAmB,GAAG;AAAA,UAC9C,OAAO;AACL,oBAAQ,OAAO,EAAE;AAAA,UACnB;AAAA,QACF;AACA,YAAI,QAAQ,SAAS,EAAG;AAGxB,YAAI,OAA2C;AAC/C,mBAAW,CAAC,IAAI,GAAG,KAAK,SAAS;AAC/B,cAAI,SAAS,QAAQ,MAAM,KAAK,IAAK,QAAO,EAAE,IAAI,IAAI;AAAA,QACxD;AACA,YAAI,KAAM,YAAW,KAAK,EAAE;AAAA,MAC9B;AAAA,MACA;AAAA;AAAA;AAAA,QAGE,YAAY;AAAA,QACZ,WAAW,CAAC,GAAG,CAAC;AAAA,MAClB;AAAA,IACF;AAEA,eAAW,WAAW,SAAU,UAAS,QAAQ,OAAO;AACxD,WAAO,MAAM;AACX,eAAS,WAAW;AACpB,UAAI,YAAY,MAAM;AACpB,cAAM,OAAO,YAAY,IAAI,OAAO;AACpC,cAAM,gBAAgB,cAAc;AAAA,MACtC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AACR,SAAO;AACT;AAYA,SAAS,UAAU,OAAuB;AACxC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,KAAK,WAAW,YAAY;AAC7E,WAAO,OAAO,IAAI,OAAO,KAAK;AAAA,EAChC;AACA,SAAO;AACT;","names":[]}
package/dist/index.d.ts CHANGED
@@ -6,19 +6,18 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  import { z } from 'zod';
7
7
 
8
8
  /**
9
- * Phase F.9-B docs theme masthead.
10
- *
11
- * Brand strap: site title (left), search input (center), nav
12
- * + version label (right). Reads settings via
13
- * `resolveDocsSettings()` so the version label and search
14
- * placeholder match the operator's admin choices.
15
- *
16
- * The search input is a plain GET form to `/docs/search`
17
- * F.2's theme route handles the query (#609: the universal
18
- * `/search` is the framework's own page, so the docs theme
19
- * scopes its search to `/docs/search` to avoid being shadowed
20
- * by the host's `app/(site)/search/page.tsx`). No client-side
21
- * JS for the input itself; works without hydration.
9
+ * Docs theme masthead. Brand strap (mark + wordmark + version
10
+ * pill) on the left, ⌘K search form centered, primary nav +
11
+ * GitHub repo link on the right.
12
+ *
13
+ * Search is a plain GET form to `/docs/search` — the theme's
14
+ * own route handles the query so the host's `(site)/search`
15
+ * page doesn't shadow it (#609). The ⌘K affordance is purely
16
+ * visual hint copy in a `<kbd>`; wiring it to a global hotkey
17
+ * is a separate client island sites can add on top.
18
+ *
19
+ * The GitHub link reads `settings.githubRepo`. When the admin
20
+ * setting is unset, the link is hidden.
22
21
  */
23
22
  declare function DocsHeader(): Promise<React.ReactElement>;
24
23
 
@@ -87,42 +86,63 @@ declare function DocsSearch({ searchParams, }: NpRouteRenderProps): Promise<Reac
87
86
  declare function DocsShell({ children }: NpThemeShellProps): React.ReactElement;
88
87
 
89
88
  /**
90
- * Phase F.9-B hierarchical sidebar.
91
- *
92
- * Walks the `docs` collection (declared as required in the
93
- * manifest), builds a parent/order tree, renders nested nav.
94
- * The current doc is highlighted via `data-current="true"`.
95
- *
96
- * The function reads `currentSlug` from a query param (when
97
- * called as a route component) or from doc context. For F.9-B
98
- * we keep it simple: the slot component fetches the request's
99
- * URL via `headers()` and matches on `pathname.startsWith`.
89
+ * Hierarchical sidebar for the docs theme.
90
+ *
91
+ * Top-level docs (those without a `parent`) become **group
92
+ * eyebrows** rendered with a bullet dot indicator; each
93
+ * group's children are the linkable items under the eyebrow.
94
+ * Deeper levels render as nested lists with a hairline left
95
+ * rule.
96
+ *
97
+ * The current doc is highlighted via `data-current="true"`
98
+ * resolved from the request's pathname. Wired through
99
+ * `next/headers` (`x-np-pathname`) so the highlight survives
100
+ * server rendering with no client-side hydration.
100
101
  */
101
102
  declare function DocsSidebar(): Promise<React.ReactElement>;
102
103
 
103
104
  /**
104
- * Phase F.9-Bdocs theme CSS.
105
+ * `@nexpress/theme-docs`CSS layout.
106
+ *
107
+ * Three-column reference-docs layout: sticky search-first header,
108
+ * hierarchical sidebar (groups with bullet eyebrows + nested
109
+ * links + status badges), centered article column, on-this-page
110
+ * TOC on the right. Sidebar + TOC collapse out below the
111
+ * tablet / phone breakpoints respectively.
105
112
  *
106
- * Scoped under `.np-docs-*` so theme swaps don't leave
107
- * residue. Uses `var(--np-color-*)` tokens so admin
108
- * settings tokens still apply on top.
113
+ * Scoped under `.np-docs-*` so a theme swap to another v0.2
114
+ * theme doesn't leave residue. All colors resolve through the
115
+ * `--np-color-*` tokens so admin overrides on top still apply.
116
+ *
117
+ * The terminal-style shell command snippet uses `.np-docs-cmdline`
118
+ * (not `.np-docs-shell`) because `.np-docs-shell` is already
119
+ * claimed by the route shell's root container.
109
120
  */
110
- declare const docsCss = "\n.np-docs-shell {\n display: flex;\n flex-direction: column;\n min-height: 100vh;\n background: var(--np-color-background);\n color: var(--np-color-foreground);\n font-family: var(--np-font-body, system-ui, sans-serif);\n}\n\n.np-docs-header {\n position: sticky;\n top: 0;\n z-index: 50;\n background: var(--np-color-background);\n border-bottom: 1px solid var(--np-color-border);\n backdrop-filter: blur(8px);\n}\n\n.np-docs-header-inner {\n max-width: 1200px;\n margin: 0 auto;\n padding: 0.75rem 1.5rem;\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 1.5rem;\n align-items: center;\n}\n\n.np-docs-brand {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n font-weight: 600;\n text-decoration: none;\n color: var(--np-color-foreground);\n}\n\n.np-docs-brand-version {\n font-size: 0.75rem;\n font-family: ui-monospace, \"SF Mono\", Menlo, monospace;\n color: var(--np-color-muted-foreground);\n background: var(--np-color-muted);\n padding: 0.125rem 0.375rem;\n border-radius: 0.25rem;\n}\n\n.np-docs-search-form {\n flex: 1;\n}\n\n.np-docs-search-input {\n width: 100%;\n padding: 0.4rem 0.75rem;\n border: 1px solid var(--np-color-border);\n border-radius: 0.375rem;\n background: var(--np-color-card);\n color: var(--np-color-foreground);\n font-size: 0.875rem;\n}\n\n.np-docs-search-input:focus {\n outline: 2px solid var(--np-color-primary);\n outline-offset: -2px;\n border-color: transparent;\n}\n\n.np-docs-nav {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.np-docs-primary-nav {\n display: flex;\n list-style: none;\n margin: 0;\n padding: 0;\n gap: 1rem;\n}\n\n.np-docs-primary-nav a {\n color: var(--np-color-muted-foreground);\n text-decoration: none;\n font-size: 0.875rem;\n}\n\n.np-docs-primary-nav a:hover {\n color: var(--np-color-foreground);\n}\n\n.np-docs-github-link {\n font-size: 0.875rem;\n color: var(--np-color-muted-foreground);\n text-decoration: none;\n}\n\n.np-docs-grid {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n max-width: 1200px;\n margin: 0 auto;\n width: 100%;\n gap: 2.5rem;\n padding: 2rem 1.5rem;\n}\n\n@media (max-width: 768px) {\n .np-docs-grid {\n grid-template-columns: 1fr;\n }\n .np-docs-sidebar {\n display: none;\n }\n}\n\n.np-docs-sidebar {\n position: sticky;\n top: 4rem;\n align-self: start;\n max-height: calc(100vh - 5rem);\n overflow-y: auto;\n}\n\n.np-docs-sidebar h2 {\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--np-color-muted-foreground);\n margin: 0 0 0.75rem;\n}\n\n.np-docs-sidebar ul {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.np-docs-sidebar li {\n margin: 0.125rem 0;\n}\n\n.np-docs-sidebar a {\n display: block;\n padding: 0.25rem 0.5rem;\n border-radius: 0.25rem;\n color: var(--np-color-muted-foreground);\n text-decoration: none;\n font-size: 0.875rem;\n}\n\n.np-docs-sidebar a:hover {\n background: var(--np-color-muted);\n color: var(--np-color-foreground);\n}\n\n.np-docs-sidebar a[data-current=\"true\"] {\n background: color-mix(in oklch, var(--np-color-primary) 12%, transparent);\n color: var(--np-color-primary);\n font-weight: 500;\n}\n\n.np-docs-sidebar ul ul {\n margin-left: 0.75rem;\n border-left: 1px solid var(--np-color-border);\n padding-left: 0.5rem;\n}\n\n.np-docs-page {\n max-width: 720px;\n}\n\n.np-docs-page h1 {\n font-size: 2rem;\n margin: 0 0 0.5rem;\n}\n\n.np-docs-prev-next {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n margin-top: 3rem;\n padding-top: 1.5rem;\n border-top: 1px solid var(--np-color-border);\n}\n\n.np-docs-prev-next a {\n display: block;\n padding: 0.75rem 1rem;\n border: 1px solid var(--np-color-border);\n border-radius: 0.5rem;\n color: var(--np-color-foreground);\n text-decoration: none;\n}\n\n.np-docs-prev-next a:hover {\n border-color: var(--np-color-primary);\n}\n\n.np-docs-prev-next-label {\n display: block;\n font-size: 0.75rem;\n color: var(--np-color-muted-foreground);\n margin-bottom: 0.25rem;\n}\n\n/* M.* member surface \u2014 narrow auth-form column under the\n masthead, no sidebar (the docs sidebar is hierarchical\n doc nav, useless on auth forms). */\n.np-docs-members {\n display: flex;\n justify-content: center;\n min-height: 60vh;\n padding: 3rem 1.5rem;\n}\n.np-docs-members-column {\n width: 100%;\n max-width: 440px;\n}\n\n/* Member form token overrides \u2014 docs aesthetic: slightly\n rounded corners, neutral palette, monospace label accent. */\n.np-docs .np-members-form {\n --np-member-form-input-bg: var(--np-color-background);\n --np-member-form-input-border: var(--np-color-border);\n --np-member-form-input-border-focus: var(--np-color-primary);\n --np-member-form-input-radius: 0.375rem;\n --np-member-form-button-radius: 0.375rem;\n}\n.np-docs .np-members-form .np-form-label {\n font-family: var(--np-font-mono, ui-monospace, monospace);\n font-size: 0.8125rem;\n}\n";
121
+ declare const docsCss = "\n.np-docs-shell {\n display: flex;\n flex-direction: column;\n min-height: 100vh;\n background: var(--np-color-background);\n color: var(--np-color-foreground);\n font-family: var(--np-font-body, \"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif);\n line-height: 1.6;\n -webkit-font-smoothing: antialiased;\n}\n.np-docs-shell a { color: inherit; }\n.np-docs-shell code,\n.np-docs-shell pre,\n.np-docs-shell kbd {\n font-family: var(--np-font-mono, \"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, monospace);\n}\n\n/* ============================================================\n * Header \u2014 sticky bar with brand + version pill, \u2318K search in\n * the center, primary nav + GitHub link on the right. Grid\n * keeps everything anchored regardless of viewport width.\n * ============================================================ */\n.np-docs-header {\n position: sticky;\n top: 0;\n z-index: 30;\n background: color-mix(in oklab, var(--np-color-background) 80%, transparent);\n backdrop-filter: saturate(140%) blur(14px);\n -webkit-backdrop-filter: saturate(140%) blur(14px);\n border-bottom: 1px solid var(--np-color-border);\n}\n.np-docs-header-inner {\n max-width: 1380px;\n margin: 0 auto;\n padding: 0.7rem 1.5rem;\n display: grid;\n grid-template-columns: minmax(220px, 1fr) minmax(0, 2fr) auto;\n gap: 1.5rem;\n align-items: center;\n}\n.np-docs-brand {\n display: inline-flex;\n align-items: center;\n gap: 0.55rem;\n font-weight: 700;\n font-size: 1.0625rem;\n letter-spacing: -0.02em;\n text-decoration: none;\n}\n.np-docs-brand-mark {\n width: 1.55rem;\n height: 1.55rem;\n border-radius: 6px;\n background: linear-gradient(135deg, var(--np-color-primary, #2563eb), #0ea5e9);\n position: relative;\n flex: none;\n}\n.np-docs-brand-mark::after {\n content: \"\";\n position: absolute;\n inset: 5px;\n border-radius: 2px;\n background: var(--np-color-background, #fff);\n opacity: 0.95;\n clip-path: polygon(0 0, 100% 0, 100% 100%, 60% 100%, 0 35%);\n}\n.np-docs-brand-name { font-weight: 700; }\n.np-docs-brand-version {\n font-family: var(--np-font-mono);\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--np-color-primary);\n background: color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card));\n padding: 0.15rem 0.45rem;\n border-radius: 5px;\n}\n\n.np-docs-search-form {\n max-width: 520px;\n width: 100%;\n position: relative;\n justify-self: center;\n}\n.np-docs-search-form svg {\n position: absolute;\n top: 50%;\n left: 0.85rem;\n transform: translateY(-50%);\n color: var(--np-color-muted-foreground);\n}\n.np-docs-search-input {\n width: 100%;\n padding: 0.55rem 0.85rem 0.55rem 2.4rem;\n font: inherit;\n font-size: 0.875rem;\n color: var(--np-color-foreground);\n background: var(--np-color-card);\n border: 1px solid var(--np-color-border);\n border-radius: 9px;\n}\n.np-docs-search-input::placeholder {\n color: var(--np-color-muted-foreground);\n}\n.np-docs-search-input:focus {\n outline: none;\n border-color: var(--np-color-primary);\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--np-color-primary) 22%, transparent);\n}\n.np-docs-search-kbd {\n position: absolute;\n right: 0.6rem;\n top: 50%;\n transform: translateY(-50%);\n font-size: 0.7rem;\n padding: 0.1rem 0.4rem;\n color: var(--np-color-muted-foreground);\n border: 1px solid var(--np-color-border);\n border-radius: 4px;\n}\n\n.np-docs-nav {\n display: flex;\n align-items: center;\n gap: 1.25rem;\n}\n.np-docs-primary-nav {\n display: flex;\n list-style: none;\n gap: 1.25rem;\n margin: 0;\n padding: 0;\n}\n.np-docs-primary-nav a {\n color: var(--np-color-muted-foreground);\n font-size: 0.875rem;\n font-weight: 500;\n text-decoration: none;\n}\n.np-docs-primary-nav a:hover,\n.np-docs-primary-nav a[aria-current=\"page\"] {\n color: var(--np-color-foreground);\n}\n.np-docs-github,\n.np-docs-github-link {\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n padding: 0.4rem 0.7rem;\n font-size: 0.8125rem;\n color: var(--np-color-muted-foreground);\n background: var(--np-color-muted);\n border: 1px solid var(--np-color-border);\n border-radius: 7px;\n text-decoration: none;\n}\n.np-docs-github:hover,\n.np-docs-github-link:hover {\n color: var(--np-color-foreground);\n}\n\n@media (max-width: 800px) {\n .np-docs-header-inner {\n grid-template-columns: auto 1fr auto;\n gap: 0.75rem;\n }\n .np-docs-search-form { display: none; }\n .np-docs-primary-nav { display: none; }\n}\n\n/* ============================================================\n * 3-column layout: sidebar + article + on-page TOC.\n * ============================================================ */\n.np-docs-grid,\n.np-docs-body {\n max-width: 1380px;\n margin: 0 auto;\n width: 100%;\n display: grid;\n grid-template-columns: 260px minmax(0, 1fr) 220px;\n gap: 3rem;\n padding: 2.25rem 1.5rem 4rem;\n}\n@media (max-width: 1100px) {\n .np-docs-grid,\n .np-docs-body {\n grid-template-columns: 240px minmax(0, 1fr);\n }\n .np-docs-toc { display: none; }\n}\n@media (max-width: 800px) {\n .np-docs-grid,\n .np-docs-body {\n grid-template-columns: 1fr;\n }\n .np-docs-sidebar { display: none; }\n}\n\n/* ============================================================\n * Sidebar \u2014 grouped link list with bullet eyebrow + badges.\n * ============================================================ */\n.np-docs-sidebar {\n position: sticky;\n top: 4.25rem;\n align-self: start;\n max-height: calc(100vh - 5rem);\n overflow-y: auto;\n padding-right: 0.5rem;\n}\n.np-docs-sidebar-group { margin-bottom: 1.5rem; }\n.np-docs-sidebar-eyebrow {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n font-family: var(--np-font-mono);\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--np-color-muted-foreground);\n margin: 0 0 0.65rem;\n font-weight: 600;\n}\n.np-docs-sidebar-eyebrow-dot {\n width: 0.4rem;\n height: 0.4rem;\n border-radius: 50%;\n background: var(--np-color-primary);\n}\n.np-docs-sidebar ul {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.np-docs-sidebar li { margin: 0.05rem 0; }\n.np-docs-sidebar a {\n display: block;\n padding: 0.34rem 0.6rem;\n font-size: 0.875rem;\n color: var(--np-color-muted-foreground);\n text-decoration: none;\n border-radius: 6px;\n line-height: 1.35;\n}\n.np-docs-sidebar a:hover {\n background: var(--np-color-muted);\n color: var(--np-color-foreground);\n}\n.np-docs-sidebar a[data-current=\"true\"],\n.np-docs-sidebar a[aria-current=\"page\"] {\n color: var(--np-color-primary);\n background: color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card));\n font-weight: 500;\n}\n.np-docs-sidebar ul ul {\n margin-left: 0.5rem;\n padding-left: 0.85rem;\n border-left: 1px solid var(--np-color-border);\n}\n.np-docs-sidebar-badge {\n display: inline-block;\n font-family: var(--np-font-mono);\n font-size: 0.62rem;\n padding: 0.02rem 0.34rem;\n margin-left: 0.4rem;\n vertical-align: 1px;\n border-radius: 4px;\n background: var(--np-color-muted);\n color: var(--np-color-muted-foreground);\n font-weight: 500;\n}\n.np-docs-sidebar-badge.new { background: #dcfce7; color: #166534; }\n.np-docs-sidebar-badge.beta { background: #fef3c7; color: #92400e; }\n.np-docs-sidebar-badge.api {\n background: color-mix(in oklab, var(--np-color-primary) 16%, var(--np-color-card));\n color: var(--np-color-primary);\n}\n\n/* ============================================================\n * Doc page \u2014 article column. h1 + lede + meta row + sections\n * with hovered anchor link icon.\n * ============================================================ */\n.np-docs-page {\n max-width: 760px;\n min-width: 0;\n}\n.np-docs-breadcrumbs {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.8125rem;\n color: var(--np-color-muted-foreground);\n margin-bottom: 1rem;\n}\n.np-docs-breadcrumbs a {\n color: inherit;\n text-decoration: none;\n}\n.np-docs-breadcrumbs a:hover { color: var(--np-color-foreground); }\n.np-docs-breadcrumbs-sep { opacity: 0.5; }\n\n.np-docs-page h1 {\n font-size: clamp(2rem, 3.6vw, 2.5rem);\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n margin: 0 0 0.5rem;\n text-wrap: balance;\n}\n.np-docs-page-lede {\n font-size: 1.125rem;\n color: var(--np-color-muted-foreground);\n line-height: 1.55;\n margin: 0 0 2rem;\n max-width: 38rem;\n text-wrap: pretty;\n}\n.np-docs-page-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n align-items: center;\n font-size: 0.8125rem;\n color: var(--np-color-muted-foreground);\n padding: 0.85rem 0;\n margin-bottom: 2rem;\n border-top: 1px solid var(--np-color-border);\n border-bottom: 1px solid var(--np-color-border);\n}\n.np-docs-page-meta-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.15rem 0.55rem;\n font-family: var(--np-font-mono);\n font-size: 0.72rem;\n border: 1px solid var(--np-color-border);\n border-radius: 999px;\n background: var(--np-color-card);\n}\n.np-docs-page-meta-pill.status {\n color: #047857;\n border-color: #bbf7d0;\n background: #f0fdf4;\n}\n.np-docs-page-meta-pill.status::before {\n content: \"\";\n width: 0.4rem;\n height: 0.4rem;\n border-radius: 50%;\n background: #047857;\n}\n.np-docs-page-meta-sep { opacity: 0.4; }\n.np-docs-page-meta a {\n color: var(--np-color-primary);\n text-decoration: none;\n margin-left: auto;\n}\n.np-docs-page-meta a:hover { text-decoration: underline; }\n\n.np-docs-page h2 {\n font-size: 1.5rem;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.25;\n margin: 3rem 0 0.85rem;\n scroll-margin-top: 5rem;\n position: relative;\n}\n.np-docs-page h2:first-of-type { margin-top: 2.5rem; }\n.np-docs-page h3 {\n font-size: 1.1rem;\n font-weight: 600;\n letter-spacing: -0.01em;\n margin: 2.25rem 0 0.7rem;\n scroll-margin-top: 5rem;\n position: relative;\n}\n.np-docs-page p { margin: 0 0 1rem; }\n.np-docs-page p code,\n.np-docs-page li code {\n font-size: 0.875em;\n padding: 0.1em 0.35em;\n background: var(--np-color-muted);\n border: 1px solid var(--np-color-border);\n border-radius: 4px;\n}\n.np-docs-page strong { font-weight: 600; }\n.np-docs-page ul,\n.np-docs-page ol {\n margin: 0 0 1rem;\n padding-left: 1.4rem;\n}\n.np-docs-page li { margin: 0.35rem 0; }\n.np-docs-page a:not(.np-docs-prev-next a):not(.np-docs-anchor) {\n color: var(--np-color-primary);\n text-decoration: underline;\n text-underline-offset: 3px;\n text-decoration-thickness: 1px;\n text-decoration-color: color-mix(in oklab, var(--np-color-primary) 45%, transparent);\n}\n.np-docs-page a:not(.np-docs-prev-next a):not(.np-docs-anchor):hover {\n text-decoration-color: currentColor;\n}\n\n/* Anchor icon \u2014 visible only on heading hover. */\n.np-docs-anchor {\n position: absolute;\n left: -1.3rem;\n top: 50%;\n transform: translateY(-50%);\n color: var(--np-color-muted-foreground);\n opacity: 0;\n text-decoration: none !important;\n font-weight: 400;\n}\n.np-docs-page h2:hover .np-docs-anchor,\n.np-docs-page h3:hover .np-docs-anchor { opacity: 1; }\n\n/* ============================================================\n * Callouts \u2014 info (default) / note (indigo) / warn (amber) /\n * danger (red). 3px left rule carries the variant color.\n * ============================================================ */\n.np-docs-callout {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.85rem;\n padding: 1rem 1.15rem;\n border: 1px solid var(--np-color-border);\n border-left: 3px solid var(--np-color-primary);\n border-radius: 8px;\n background: var(--np-color-card);\n margin: 1.25rem 0;\n font-size: 0.95rem;\n line-height: 1.55;\n}\n.np-docs-callout > svg,\n.np-docs-callout-icon {\n width: 1.25rem;\n height: 1.25rem;\n flex-shrink: 0;\n color: var(--np-color-primary);\n margin-top: 0.1rem;\n}\n.np-docs-callout p { margin: 0; }\n.np-docs-callout-title {\n font-weight: 600;\n margin-bottom: 0.15rem;\n color: var(--np-color-foreground);\n}\n.np-docs-callout--warn {\n border-left-color: #b45309;\n background: #fffbeb;\n border-color: #fde68a;\n}\n.np-docs-callout--warn .np-docs-callout-icon,\n.np-docs-callout--warn > svg { color: #b45309; }\n.np-docs-callout--note {\n border-left-color: #6366f1;\n background: #eef2ff;\n border-color: #c7d2fe;\n}\n.np-docs-callout--note .np-docs-callout-icon,\n.np-docs-callout--note > svg { color: #4338ca; }\n.np-docs-callout--danger {\n border-left-color: #b91c1c;\n background: #fef2f2;\n border-color: #fecaca;\n}\n.np-docs-callout--danger .np-docs-callout-icon,\n.np-docs-callout--danger > svg { color: #b91c1c; }\n\n/* ============================================================\n * Code blocks \u2014 dark surface with a file-named header and a\n * copy button. Syntax tokens (.tk-*) cover the common slots\n * (keyword / string / function / number / type / punctuation /\n * comment) using a muted neutral-paired palette so the block\n * reads at the same contrast as the page chrome.\n * ============================================================ */\n.np-docs-code {\n margin: 1.25rem 0;\n border-radius: 10px;\n background: #0b1220;\n color: #e6edf6;\n overflow: hidden;\n border: 1px solid #1e2939;\n}\n.np-docs-code-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.55rem 0.85rem;\n background: #0f1a2b;\n border-bottom: 1px solid #1e293b;\n}\n.np-docs-code-file {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n font-family: var(--np-font-mono);\n font-size: 0.78rem;\n color: #94a3b8;\n}\n.np-docs-code-file svg { color: #64748b; }\n.np-docs-code-copy {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.25rem 0.55rem;\n font-size: 0.72rem;\n font-family: var(--np-font-mono);\n color: #94a3b8;\n background: transparent;\n border: 1px solid #1e293b;\n border-radius: 5px;\n cursor: pointer;\n}\n.np-docs-code-copy:hover {\n color: #e2e8f0;\n border-color: #334155;\n}\n.np-docs-code pre {\n margin: 0;\n padding: 1rem 1.1rem;\n font-size: 0.825rem;\n line-height: 1.65;\n overflow-x: auto;\n}\n.np-docs-code pre code {\n display: block;\n font-family: inherit;\n background: transparent;\n border: 0;\n padding: 0;\n color: inherit;\n}\n.tk-c { color: #64748b; font-style: italic; }\n.tk-k { color: #c084fc; }\n.tk-s { color: #86efac; }\n.tk-f { color: #93c5fd; }\n.tk-t { color: #fcd34d; }\n.tk-n { color: #f9a8d4; }\n.tk-p { color: #e2e8f0; }\n\n/* Inline shell snippet \u2014 for terse `pnpm dev` style commands.\n * Named `cmdline` (not `shell`) so it doesn't collide with the\n * route shell container at `.np-docs-shell`. */\n.np-docs-cmdline {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 0.7rem;\n align-items: center;\n padding: 0.75rem 1rem;\n margin: 1.25rem 0;\n background: #0b1220;\n color: #e6edf6;\n border-radius: 9px;\n font-family: var(--np-font-mono);\n font-size: 0.875rem;\n}\n.np-docs-cmdline-prompt { color: #34d399; }\n.np-docs-cmdline-cmd { color: #e2e8f0; }\n.np-docs-cmdline-copy {\n padding: 0.2rem 0.55rem;\n font-size: 0.7rem;\n color: #94a3b8;\n background: transparent;\n border: 1px solid #1e293b;\n border-radius: 5px;\n cursor: pointer;\n}\n.np-docs-cmdline-copy:hover { color: #e2e8f0; border-color: #334155; }\n\n/* ============================================================\n * Numbered steps \u2014 counter on a soft pill before each step.\n * ============================================================ */\n.np-docs-steps {\n counter-reset: step;\n list-style: none;\n padding: 0;\n margin: 1.5rem 0;\n display: grid;\n gap: 1rem;\n}\n.np-docs-steps > li {\n counter-increment: step;\n display: grid;\n grid-template-columns: 2.1rem 1fr;\n gap: 0.85rem;\n align-items: start;\n}\n.np-docs-steps > li::before {\n content: counter(step);\n width: 1.85rem;\n height: 1.85rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--np-font-mono);\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--np-color-primary);\n background: color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card));\n border-radius: 50%;\n}\n.np-docs-step-title {\n font-weight: 600;\n margin: 0.25rem 0 0.25rem;\n}\n.np-docs-step-body {\n margin: 0;\n color: var(--np-color-muted-foreground);\n}\n\n/* ============================================================\n * API / reference tables \u2014 uppercase mono headers.\n * ============================================================ */\n.np-docs-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.875rem;\n margin: 1.25rem 0;\n}\n.np-docs-table thead { background: var(--np-color-muted); }\n.np-docs-table th,\n.np-docs-table td {\n text-align: left;\n padding: 0.7rem 0.85rem;\n border-bottom: 1px solid var(--np-color-border);\n vertical-align: top;\n}\n.np-docs-table th {\n font-family: var(--np-font-mono);\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--np-color-muted-foreground);\n font-weight: 600;\n}\n.np-docs-table td:first-child code {\n color: var(--np-color-foreground);\n font-weight: 500;\n}\n.np-docs-table-required {\n display: inline-block;\n font-family: var(--np-font-mono);\n font-size: 0.65rem;\n padding: 0.05rem 0.35rem;\n margin-left: 0.4rem;\n background: #fef3c7;\n color: #92400e;\n border-radius: 4px;\n vertical-align: 1px;\n}\n\n/* ============================================================\n * Prev / next \u2014 symmetric pair at the foot of every doc page.\n * Hover lifts the bordered card and tints the border primary.\n * ============================================================ */\n.np-docs-prev-next {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1rem;\n margin: 3.5rem 0 1rem;\n padding-top: 2rem;\n border-top: 1px solid var(--np-color-border);\n}\n.np-docs-prev-next a {\n display: block;\n padding: 1rem 1.15rem;\n background: var(--np-color-card);\n border: 1px solid var(--np-color-border);\n border-radius: 10px;\n text-decoration: none;\n transition: border-color 0.15s ease, transform 0.2s ease;\n}\n.np-docs-prev-next a:hover {\n border-color: var(--np-color-primary);\n transform: translateY(-1px);\n}\n.np-docs-prev-next-dir,\n.np-docs-prev-next-label {\n font-family: var(--np-font-mono);\n font-size: 0.72rem;\n color: var(--np-color-muted-foreground);\n letter-spacing: 0.05em;\n margin-bottom: 0.25rem;\n}\n.np-docs-prev-next-title {\n font-weight: 600;\n font-size: 0.95rem;\n}\n.np-docs-prev-next a.np-docs-prev-next-next,\n.np-docs-prev-next a:last-child { text-align: right; }\n\n/* ============================================================\n * Feedback row \u2014 Yes / Could be better buttons under each page.\n * ============================================================ */\n.np-docs-feedback {\n margin-top: 3rem;\n padding: 1.25rem;\n background: var(--np-color-muted);\n border: 1px solid var(--np-color-border);\n border-radius: 10px;\n display: flex;\n gap: 1rem;\n align-items: center;\n justify-content: space-between;\n flex-wrap: wrap;\n}\n.np-docs-feedback-title { font-weight: 600; font-size: 0.95rem; }\n.np-docs-feedback-helper {\n font-size: 0.825rem;\n color: var(--np-color-muted-foreground);\n margin-top: 0.15rem;\n}\n.np-docs-feedback-buttons {\n display: flex;\n gap: 0.5rem;\n}\n.np-docs-feedback-buttons button {\n padding: 0.4rem 0.85rem;\n font: inherit;\n font-size: 0.825rem;\n background: var(--np-color-card);\n border: 1px solid var(--np-color-border);\n border-radius: 7px;\n cursor: pointer;\n}\n.np-docs-feedback-buttons button:hover {\n border-color: var(--np-color-primary);\n color: var(--np-color-primary);\n}\n\n/* ============================================================\n * On-page TOC \u2014 right rail, sticky, current section gets a\n * primary border + soft gradient.\n * ============================================================ */\n.np-docs-toc {\n position: sticky;\n top: 4.25rem;\n align-self: start;\n max-height: calc(100vh - 5rem);\n overflow-y: auto;\n font-size: 0.825rem;\n}\n.np-docs-toc-eyebrow {\n font-family: var(--np-font-mono);\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--np-color-muted-foreground);\n margin: 0 0 0.75rem;\n font-weight: 600;\n}\n.np-docs-toc ul {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.np-docs-toc li { margin: 0.05rem 0; }\n.np-docs-toc a {\n display: block;\n padding: 0.3rem 0.5rem;\n color: var(--np-color-muted-foreground);\n text-decoration: none;\n border-left: 2px solid transparent;\n margin-left: -2px;\n line-height: 1.4;\n}\n.np-docs-toc a:hover { color: var(--np-color-foreground); }\n.np-docs-toc a[data-current=\"true\"],\n.np-docs-toc a[aria-current=\"location\"],\n.np-docs-toc a[aria-current=\"true\"] {\n color: var(--np-color-primary);\n border-left-color: var(--np-color-primary);\n background: linear-gradient(\n to right,\n color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card)),\n transparent 80%\n );\n}\n.np-docs-toc ul ul { margin-left: 0.85rem; }\n.np-docs-toc-secondary {\n margin-top: 1.5rem;\n padding-top: 1rem;\n border-top: 1px solid var(--np-color-border);\n}\n.np-docs-toc-secondary a {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.2rem 0;\n border-left: 0;\n margin: 0;\n}\n.np-docs-toc-secondary a:hover { background: transparent; }\n\n/* Empty / not-found surfaces \u2014 used by routes/not-found and\n * the docs collection's empty state. */\n.np-docs-empty {\n padding: 4rem 1.5rem;\n text-align: center;\n color: var(--np-color-muted-foreground);\n}\n.np-docs-empty h1 {\n font-size: 1.5rem;\n margin: 0 0 0.5rem;\n color: var(--np-color-foreground);\n}\n";
111
122
 
112
123
  /**
113
- * Phase F.9-Bdoc page template.
114
- *
115
- * Renders the doc's title + body, plus a prev/next bar at the
116
- * bottom that walks the same parent-ordered hierarchy the
117
- * sidebar uses. Optional "Edit on GitHub" link is added when
118
- * the operator set `settings.githubRepo`.
119
- *
120
- * Body rendering: this template assumes `doc.body` is a
121
- * Lexical / blocks payload. For a real F.9-B we'd thread
122
- * through `renderBlocks(doc.body, { ctx: blockCtx })` for the
123
- * actual content; the placeholder JSON keeps the contract
124
- * shape-correct without binding to a specific body shape that
125
- * may differ across operator setups.
124
+ * Doc page template three-zone article: header strap
125
+ * (breadcrumbs + h1 + lede + meta pills), Lexical-rendered body,
126
+ * footer (feedback widget + prev/next pair).
127
+ *
128
+ * Breadcrumbs walk the parent chain so a nested doc shows
129
+ * `Docs / Plugins / Author quickstart` without the operator
130
+ * configuring it explicitly. Falls back to a single "Docs" entry
131
+ * for root-level pages.
132
+ *
133
+ * Meta pills render only when their data is present:
134
+ *
135
+ * - `stableSince` (e.g. `"0.1"`) green pill `"Stable since 0.1"`.
136
+ * - `readingTime` (number or string) → `"X min read"` pill.
137
+ * - `updatedAt` → date string after a · separator.
138
+ * - `settings.githubRepo` set → `"Edit this page →"` link to GH.
139
+ *
140
+ * Feedback row is static HTML (Yes / Could be better buttons)
141
+ * without a wired endpoint — operators that want a real
142
+ * feedback API drop in their own client island.
143
+ *
144
+ * Prev/next walks the same ordered list the sidebar uses; the
145
+ * doc immediately before / after `current` in render-order wins.
126
146
  */
127
147
  declare function DocPageTemplate({ doc: rawDoc, }: NpTemplateRenderProps): Promise<React.ReactElement>;
128
148
 
@@ -145,11 +165,26 @@ type DocsSettings = z.infer<typeof docsSettingsSchema>;
145
165
  /**
146
166
  * `@nexpress/theme-docs` — documentation theme for NexPress.
147
167
  *
148
- * Stresses F.2 (search route + sidebar slot consuming
149
- * hierarchy) and F.3 (settings: version, githubRepo,
150
- * sidebarHeading, TOC toggle). Different contract axis from
151
- * F.9-A's magazine sidebar-driven layout, hierarchical doc
152
- * collection, prev/next navigation.
168
+ * Three-column reference-docs layout: sticky search-first header
169
+ * (brand mark + version pill + ⌘K search + primary nav + GitHub
170
+ * link), hierarchical sidebar with bullet-eyebrow groups + nested
171
+ * links + status badges, centered article column with breadcrumbs
172
+ * + lede + meta pills + Lexical body, on-this-page TOC on the
173
+ * right. Sidebar collapses out at the tablet breakpoint; TOC
174
+ * collapses out below 1100px.
175
+ *
176
+ * Pairs with a `docs` collection (auto-created via
177
+ * `requires.collections.docs.createIfAbsent` on the bundled-themes
178
+ * prebake path). Operator-declared fields with the same names win
179
+ * on collision so a site that ships its own `docs` schema is
180
+ * never overwritten.
181
+ *
182
+ * `seedContent.navigation` ships the primary header / footer
183
+ * links; the `docs` collection rows are operator-authored.
184
+ * Seeding actual doc rows requires the seedContent contract to
185
+ * grow a `documents?: Record<slug, ...>` slot, which is queued as
186
+ * a follow-up — without it, themes that target non-page / non-post
187
+ * collections (docs, projects, products) can only seed nav.
153
188
  */
154
189
  declare const docsTheme: _nexpress_theme.NpTheme;
155
190