@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.
- package/dist/components/search-keyboard-shortcut.d.ts +10 -0
- package/dist/components/search-keyboard-shortcut.js +28 -0
- package/dist/components/search-keyboard-shortcut.js.map +1 -0
- package/dist/components/toc-scrollspy.d.ts +22 -0
- package/dist/components/toc-scrollspy.js +77 -0
- package/dist/components/toc-scrollspy.js.map +1 -0
- package/dist/index.d.ts +81 -46
- package/dist/index.js +1032 -183
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
|
@@ -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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
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
|
-
*
|
|
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
|
|
107
|
-
* residue.
|
|
108
|
-
*
|
|
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
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
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
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
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
|
|