@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/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  import { defineTheme } from "@nexpress/theme";
3
3
 
4
4
  // src/header.tsx
5
- import { NavMenu } from "@nexpress/next";
5
+ import { NavMenu, getCachedSite } from "@nexpress/next";
6
+ import { SearchKeyboardShortcut } from "./components/search-keyboard-shortcut.js";
6
7
 
7
8
  // src/settings-helpers.ts
8
9
  import { getCachedThemeSettings } from "@nexpress/next";
@@ -31,42 +32,83 @@ async function resolveDocsSettings() {
31
32
 
32
33
  // src/header.tsx
33
34
  import { jsx, jsxs } from "react/jsx-runtime";
35
+ var FALLBACK_SITE_NAME = "NexPress";
34
36
  async function DocsHeader() {
35
- const settings = await resolveDocsSettings();
37
+ const [settings, site] = await Promise.all([
38
+ resolveDocsSettings(),
39
+ getCachedSite()
40
+ ]);
41
+ const siteName = site?.name?.trim() || FALLBACK_SITE_NAME;
36
42
  return /* @__PURE__ */ jsx("header", { className: "np-docs-header", children: /* @__PURE__ */ jsxs("div", { className: "np-docs-header-inner", children: [
37
43
  /* @__PURE__ */ jsxs("a", { href: "/", className: "np-docs-brand", children: [
38
- /* @__PURE__ */ jsx("span", { className: "np-docs-brand-name", children: "Docs" }),
44
+ /* @__PURE__ */ jsx("span", { className: "np-docs-brand-mark", "aria-hidden": "true" }),
45
+ /* @__PURE__ */ jsx("span", { className: "np-docs-brand-name", children: siteName }),
39
46
  /* @__PURE__ */ jsx("span", { className: "np-docs-brand-version", children: settings.version })
40
47
  ] }),
41
- /* @__PURE__ */ jsx(
48
+ /* @__PURE__ */ jsxs(
42
49
  "form",
43
50
  {
44
51
  action: "/docs/search",
45
52
  method: "get",
46
53
  className: "np-docs-search-form",
47
54
  role: "search",
48
- children: /* @__PURE__ */ jsx(
49
- "input",
50
- {
51
- type: "search",
52
- name: "q",
53
- placeholder: settings.searchPlaceholder,
54
- className: "np-docs-search-input",
55
- "aria-label": "Search the docs"
56
- }
57
- )
55
+ children: [
56
+ /* @__PURE__ */ jsxs(
57
+ "svg",
58
+ {
59
+ width: "14",
60
+ height: "14",
61
+ viewBox: "0 0 24 24",
62
+ fill: "none",
63
+ stroke: "currentColor",
64
+ strokeWidth: "2",
65
+ "aria-hidden": "true",
66
+ children: [
67
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
68
+ /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
69
+ ]
70
+ }
71
+ ),
72
+ /* @__PURE__ */ jsx("label", { className: "sr-only", htmlFor: "np-docs-search-input", children: "Search the docs" }),
73
+ /* @__PURE__ */ jsx(
74
+ "input",
75
+ {
76
+ id: "np-docs-search-input",
77
+ type: "search",
78
+ name: "q",
79
+ placeholder: settings.searchPlaceholder,
80
+ className: "np-docs-search-input"
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsx("kbd", { className: "np-docs-search-kbd", children: "\u2318K" })
84
+ ]
58
85
  }
59
86
  ),
87
+ /* @__PURE__ */ jsx(SearchKeyboardShortcut, { targetId: "np-docs-search-input" }),
60
88
  /* @__PURE__ */ jsxs("nav", { className: "np-docs-nav", "aria-label": "Primary", children: [
61
89
  /* @__PURE__ */ jsx(NavMenu, { location: "primary", className: "np-docs-primary-nav" }),
62
- settings.githubRepo ? /* @__PURE__ */ jsx(
90
+ settings.githubRepo ? /* @__PURE__ */ jsxs(
63
91
  "a",
64
92
  {
65
93
  href: settings.githubRepo,
66
- className: "np-docs-github-link",
94
+ className: "np-docs-github",
67
95
  target: "_blank",
68
96
  rel: "noreferrer",
69
- children: "GitHub"
97
+ "aria-label": "GitHub repository",
98
+ children: [
99
+ /* @__PURE__ */ jsx(
100
+ "svg",
101
+ {
102
+ width: "14",
103
+ height: "14",
104
+ viewBox: "0 0 24 24",
105
+ fill: "currentColor",
106
+ "aria-hidden": "true",
107
+ children: /* @__PURE__ */ jsx("path", { d: "M12 .5a12 12 0 0 0-3.8 23.39c.6.11.82-.26.82-.58v-2c-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.1-.75.08-.74.08-.74 1.21.09 1.85 1.24 1.85 1.24 1.07 1.84 2.81 1.31 3.5 1 .11-.78.42-1.31.76-1.61-2.66-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.12-.3-.54-1.52.12-3.17 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.65.25 2.87.12 3.17.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.62-5.49 5.92.43.37.81 1.1.81 2.22v3.29c0 .32.22.7.83.58A12 12 0 0 0 12 .5Z" })
108
+ }
109
+ ),
110
+ "GitHub"
111
+ ]
70
112
  }
71
113
  ) : null
72
114
  ] })
@@ -219,49 +261,176 @@ import { findDocuments as findDocuments2 } from "@nexpress/core";
219
261
  import { notFound } from "next/navigation";
220
262
 
221
263
  // src/templates/doc-page.tsx
264
+ import * as React from "react";
222
265
  import { findDocuments } from "@nexpress/core";
223
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
266
+ import { extractHeadingToc, renderRichText } from "@nexpress/editor/server";
267
+
268
+ // src/toc-scrollspy-bridge.ts
269
+ import { TocScrollspy } from "./components/toc-scrollspy.js";
270
+
271
+ // src/templates/doc-page.tsx
272
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
224
273
  async function DocPageTemplate({
225
274
  doc: rawDoc
226
275
  }) {
227
276
  const doc = rawDoc;
228
277
  const settings = await resolveDocsSettings();
278
+ const breadcrumbs = await loadBreadcrumbs(doc);
229
279
  const navInfo = await loadPrevNext(doc);
230
- return /* @__PURE__ */ jsxs5("article", { className: "np-docs-page", children: [
231
- /* @__PURE__ */ jsx5("header", { children: /* @__PURE__ */ jsx5("h1", { children: doc.title }) }),
232
- /* @__PURE__ */ jsx5("div", { className: "np-docs-body", children: /* @__PURE__ */ jsx5("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: typeof doc.body === "string" ? doc.body : "Doc body unavailable." }) }),
233
- settings.githubRepo ? /* @__PURE__ */ jsx5("p", { style: { marginTop: "2rem" }, children: /* @__PURE__ */ jsx5(
234
- "a",
235
- {
236
- href: `${settings.githubRepo}/edit/main/docs/${doc.slug}.md`,
237
- target: "_blank",
238
- rel: "noreferrer",
239
- style: {
240
- fontSize: "0.875rem",
241
- color: "var(--np-color-muted-foreground)"
242
- },
243
- children: "\u270F\uFE0F Edit on GitHub"
244
- }
245
- ) }) : null,
246
- /* @__PURE__ */ jsxs5("nav", { className: "np-docs-prev-next", "aria-label": "Pagination", children: [
247
- navInfo.prev ? /* @__PURE__ */ jsxs5("a", { href: `/docs/${navInfo.prev.slug}`, children: [
248
- /* @__PURE__ */ jsx5("span", { className: "np-docs-prev-next-label", children: "\u2190 Previous" }),
249
- navInfo.prev.title
250
- ] }) : /* @__PURE__ */ jsx5("span", {}),
251
- navInfo.next ? /* @__PURE__ */ jsxs5(
252
- "a",
280
+ const updatedLabel = formatUpdated(doc.updatedAt ?? doc.publishedAt);
281
+ const readingLabel = readingMinutesLabel(doc.readingTime);
282
+ const editHref = settings.githubRepo ? `${settings.githubRepo}/edit/main/docs/${doc.slug}.md` : null;
283
+ const toc = extractHeadingToc(doc.body);
284
+ const reportIssueHref = settings.githubRepo ? `${settings.githubRepo}/issues/new` : null;
285
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
286
+ /* @__PURE__ */ jsxs5("article", { className: "np-docs-page", children: [
287
+ /* @__PURE__ */ jsx5("nav", { className: "np-docs-breadcrumbs", "aria-label": "Breadcrumb", children: breadcrumbs.map((crumb, index) => {
288
+ const isLast = index === breadcrumbs.length - 1;
289
+ return /* @__PURE__ */ jsxs5(React.Fragment, { children: [
290
+ index > 0 ? /* @__PURE__ */ jsx5("span", { className: "np-docs-breadcrumbs-sep", "aria-hidden": "true", children: "/" }) : null,
291
+ isLast || !crumb.slug ? /* @__PURE__ */ jsx5("span", { children: crumb.title }) : /* @__PURE__ */ jsx5("a", { href: `/docs/${crumb.slug}`, children: crumb.title })
292
+ ] }, `crumb-${index.toString()}-${crumb.slug ?? "root"}`);
293
+ }) }),
294
+ /* @__PURE__ */ jsx5("h1", { children: doc.title }),
295
+ doc.lede ? /* @__PURE__ */ jsx5("p", { className: "np-docs-page-lede", children: doc.lede }) : null,
296
+ doc.stableSince || readingLabel || updatedLabel || editHref ? /* @__PURE__ */ jsxs5("div", { className: "np-docs-page-meta", children: [
297
+ doc.stableSince ? /* @__PURE__ */ jsxs5("span", { className: "np-docs-page-meta-pill status", children: [
298
+ "Stable since ",
299
+ doc.stableSince
300
+ ] }) : null,
301
+ readingLabel ? /* @__PURE__ */ jsx5("span", { className: "np-docs-page-meta-pill", children: readingLabel }) : null,
302
+ updatedLabel ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
303
+ /* @__PURE__ */ jsx5("span", { className: "np-docs-page-meta-sep", "aria-hidden": "true", children: "\xB7" }),
304
+ /* @__PURE__ */ jsxs5("span", { children: [
305
+ "Updated ",
306
+ updatedLabel
307
+ ] })
308
+ ] }) : null,
309
+ editHref ? /* @__PURE__ */ jsx5("a", { href: editHref, target: "_blank", rel: "noreferrer", children: "Edit this page \u2192" }) : null
310
+ ] }) : null,
311
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-page-body", children: doc.body ? (
312
+ // Core types `NpRichTextContent` as the opaque
313
+ // `Record<string, unknown>`; the editor's renderer
314
+ // refines it to `{ root: ... }`. Structural cast at
315
+ // the boundary — both sides go through the same
316
+ // Lexical serializer.
317
+ renderRichText(doc.body)
318
+ ) : /* @__PURE__ */ jsx5("p", { style: { color: "var(--np-color-muted-foreground)" }, children: "No body content yet." }) }),
319
+ /* @__PURE__ */ jsxs5("div", { className: "np-docs-feedback", children: [
320
+ /* @__PURE__ */ jsxs5("div", { children: [
321
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-feedback-title", children: "Was this page helpful?" }),
322
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-feedback-helper", children: "Operators wire the feedback endpoint via a plugin or a custom client island \u2014 the form is intentionally inert in v0.1." })
323
+ ] }),
324
+ /* @__PURE__ */ jsxs5("div", { className: "np-docs-feedback-buttons", children: [
325
+ /* @__PURE__ */ jsx5("button", { type: "button", children: "Yes" }),
326
+ /* @__PURE__ */ jsx5("button", { type: "button", children: "Could be better" })
327
+ ] })
328
+ ] }),
329
+ /* @__PURE__ */ jsxs5("nav", { className: "np-docs-prev-next", "aria-label": "Pagination", children: [
330
+ navInfo.prev ? /* @__PURE__ */ jsxs5(
331
+ "a",
332
+ {
333
+ href: `/docs/${navInfo.prev.slug}`,
334
+ className: "np-docs-prev-next-prev",
335
+ children: [
336
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-prev-next-dir", children: "\u2190 Previous" }),
337
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-prev-next-title", children: navInfo.prev.title })
338
+ ]
339
+ }
340
+ ) : /* @__PURE__ */ jsx5("span", {}),
341
+ navInfo.next ? /* @__PURE__ */ jsxs5(
342
+ "a",
343
+ {
344
+ href: `/docs/${navInfo.next.slug}`,
345
+ className: "np-docs-prev-next-next",
346
+ children: [
347
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-prev-next-dir", children: "Next \u2192" }),
348
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-prev-next-title", children: navInfo.next.title })
349
+ ]
350
+ }
351
+ ) : /* @__PURE__ */ jsx5("span", {})
352
+ ] })
353
+ ] }),
354
+ toc.length > 0 ? /* @__PURE__ */ jsxs5("aside", { className: "np-docs-toc", "aria-label": "On this page", children: [
355
+ /* @__PURE__ */ jsx5("p", { className: "np-docs-toc-eyebrow", children: "On this page" }),
356
+ /* @__PURE__ */ jsx5("ul", { children: toc.map((entry) => /* @__PURE__ */ jsx5(
357
+ "li",
253
358
  {
254
- href: `/docs/${navInfo.next.slug}`,
255
- style: { textAlign: "right" },
256
- children: [
257
- /* @__PURE__ */ jsx5("span", { className: "np-docs-prev-next-label", children: "Next \u2192" }),
258
- navInfo.next.title
259
- ]
260
- }
261
- ) : /* @__PURE__ */ jsx5("span", {})
262
- ] })
359
+ style: entry.level === 3 ? { marginLeft: "0.85rem" } : void 0,
360
+ children: /* @__PURE__ */ jsx5("a", { href: `#${entry.id}`, children: entry.text })
361
+ },
362
+ `toc-${entry.id}`
363
+ )) }),
364
+ /* @__PURE__ */ jsx5(TocScrollspy, { ids: toc.map((entry) => entry.id) }),
365
+ editHref || reportIssueHref ? /* @__PURE__ */ jsxs5("div", { className: "np-docs-toc-secondary", children: [
366
+ editHref ? /* @__PURE__ */ jsxs5("a", { href: editHref, target: "_blank", rel: "noreferrer", children: [
367
+ /* @__PURE__ */ jsxs5(
368
+ "svg",
369
+ {
370
+ width: "12",
371
+ height: "12",
372
+ viewBox: "0 0 24 24",
373
+ fill: "none",
374
+ stroke: "currentColor",
375
+ strokeWidth: "2",
376
+ "aria-hidden": "true",
377
+ children: [
378
+ /* @__PURE__ */ jsx5("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
379
+ /* @__PURE__ */ jsx5("path", { d: "m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z" })
380
+ ]
381
+ }
382
+ ),
383
+ "Edit on GitHub"
384
+ ] }) : null,
385
+ reportIssueHref ? /* @__PURE__ */ jsxs5("a", { href: reportIssueHref, target: "_blank", rel: "noreferrer", children: [
386
+ /* @__PURE__ */ jsxs5(
387
+ "svg",
388
+ {
389
+ width: "12",
390
+ height: "12",
391
+ viewBox: "0 0 24 24",
392
+ fill: "none",
393
+ stroke: "currentColor",
394
+ strokeWidth: "2",
395
+ "aria-hidden": "true",
396
+ children: [
397
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10" }),
398
+ /* @__PURE__ */ jsx5("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
399
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
400
+ ]
401
+ }
402
+ ),
403
+ "Report an issue"
404
+ ] }) : null
405
+ ] }) : null
406
+ ] }) : null
263
407
  ] });
264
408
  }
409
+ async function loadBreadcrumbs(current) {
410
+ const root = { slug: null, title: "Docs" };
411
+ if (!current.parent) {
412
+ return [root, { slug: null, title: current.title }];
413
+ }
414
+ const result = await findDocuments("docs", {
415
+ where: { status: "published" },
416
+ sort: "order",
417
+ limit: 500
418
+ });
419
+ const byId = /* @__PURE__ */ new Map();
420
+ for (const r of result.docs) {
421
+ if (r.id) byId.set(r.id, r);
422
+ }
423
+ const chain = [];
424
+ let cursor = current.parent;
425
+ let safety = 6;
426
+ while (cursor && safety-- > 0) {
427
+ const node = byId.get(cursor);
428
+ if (!node) break;
429
+ chain.unshift({ slug: node.slug, title: node.title });
430
+ cursor = node.parent ?? null;
431
+ }
432
+ return [root, ...chain, { slug: null, title: current.title }];
433
+ }
265
434
  async function loadPrevNext(current) {
266
435
  const result = await findDocuments("docs", {
267
436
  where: { status: "published" },
@@ -276,6 +445,25 @@ async function loadPrevNext(current) {
276
445
  next: idx < docs.length - 1 ? docs[idx + 1] ?? null : null
277
446
  };
278
447
  }
448
+ function formatUpdated(value) {
449
+ if (!value) return null;
450
+ try {
451
+ const d = typeof value === "string" ? new Date(value) : value;
452
+ if (Number.isNaN(d.getTime())) return null;
453
+ return d.toLocaleDateString(void 0, {
454
+ year: "numeric",
455
+ month: "short",
456
+ day: "numeric"
457
+ });
458
+ } catch {
459
+ return null;
460
+ }
461
+ }
462
+ function readingMinutesLabel(value) {
463
+ if (!value && value !== 0) return null;
464
+ if (typeof value === "number") return `${value.toString()} min read`;
465
+ return value;
466
+ }
279
467
 
280
468
  // src/routes/doc-detail.tsx
281
469
  import { jsx as jsx6 } from "react/jsx-runtime";
@@ -395,16 +583,52 @@ import { findDocuments as findDocuments3 } from "@nexpress/core";
395
583
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
396
584
  async function DocsSidebar() {
397
585
  const settings = await resolveDocsSettings();
586
+ const currentSlug = await currentPathSlug();
398
587
  const result = await findDocuments3("docs", {
399
588
  where: { status: "published" },
400
589
  sort: "order",
401
590
  limit: 500
402
591
  });
403
592
  const tree = buildTree(result.docs);
404
- return /* @__PURE__ */ jsxs7("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: [
405
- /* @__PURE__ */ jsx9("h2", { children: settings.sidebarHeading }),
406
- /* @__PURE__ */ jsx9(NavTree, { nodes: tree })
407
- ] });
593
+ if (tree.length === 0) {
594
+ return /* @__PURE__ */ jsx9("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: /* @__PURE__ */ jsxs7("div", { className: "np-docs-sidebar-group", children: [
595
+ /* @__PURE__ */ jsxs7("h2", { className: "np-docs-sidebar-eyebrow", children: [
596
+ /* @__PURE__ */ jsx9("span", { className: "np-docs-sidebar-eyebrow-dot", "aria-hidden": "true" }),
597
+ settings.sidebarHeading
598
+ ] }),
599
+ /* @__PURE__ */ jsx9(
600
+ "p",
601
+ {
602
+ style: {
603
+ padding: "0.34rem 0.6rem",
604
+ fontSize: "0.875rem",
605
+ color: "var(--np-color-muted-foreground)",
606
+ margin: 0
607
+ },
608
+ children: "No docs yet."
609
+ }
610
+ )
611
+ ] }) });
612
+ }
613
+ return /* @__PURE__ */ jsx9("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: tree.map((group) => /* @__PURE__ */ jsxs7("div", { className: "np-docs-sidebar-group", children: [
614
+ /* @__PURE__ */ jsxs7("h2", { className: "np-docs-sidebar-eyebrow", children: [
615
+ /* @__PURE__ */ jsx9("span", { className: "np-docs-sidebar-eyebrow-dot", "aria-hidden": "true" }),
616
+ group.title
617
+ ] }),
618
+ group.children.length > 0 ? /* @__PURE__ */ jsx9(NavTree, { nodes: group.children, currentSlug }) : /* @__PURE__ */ jsx9("ul", { children: /* @__PURE__ */ jsx9(SidebarLink, { node: group, currentSlug }) })
619
+ ] }, group.id)) });
620
+ }
621
+ async function currentPathSlug() {
622
+ try {
623
+ const { headers } = await import("next/headers");
624
+ const list = await headers();
625
+ const pathname = list.get("x-np-pathname");
626
+ if (!pathname) return null;
627
+ const m = /^\/docs\/(.+?)\/?$/.exec(pathname);
628
+ return m ? m[1] ?? null : null;
629
+ } catch {
630
+ return null;
631
+ }
408
632
  }
409
633
  function buildTree(rawDocs) {
410
634
  const docs = rawDocs;
@@ -418,6 +642,7 @@ function buildTree(rawDocs) {
418
642
  title: typeof d.title === "string" ? d.title : d.slug,
419
643
  parent: typeof d.parent === "string" ? d.parent : null,
420
644
  order: typeof d.order === "number" ? d.order : 0,
645
+ badge: typeof d.badge === "string" ? d.badge : null,
421
646
  children: []
422
647
  });
423
648
  }
@@ -436,12 +661,34 @@ function buildTree(rawDocs) {
436
661
  sortRec(roots);
437
662
  return roots;
438
663
  }
439
- function NavTree({ nodes }) {
664
+ function NavTree({
665
+ nodes,
666
+ currentSlug
667
+ }) {
440
668
  return /* @__PURE__ */ jsx9("ul", { children: nodes.map((n) => /* @__PURE__ */ jsxs7("li", { children: [
441
- /* @__PURE__ */ jsx9("a", { href: `/docs/${n.slug}`, children: n.title }),
442
- n.children.length > 0 ? /* @__PURE__ */ jsx9(NavTree, { nodes: n.children }) : null
669
+ /* @__PURE__ */ jsx9(SidebarLink, { node: n, currentSlug }),
670
+ n.children.length > 0 ? /* @__PURE__ */ jsx9(NavTree, { nodes: n.children, currentSlug }) : null
443
671
  ] }, n.id)) });
444
672
  }
673
+ function SidebarLink({
674
+ node,
675
+ currentSlug
676
+ }) {
677
+ const isCurrent = currentSlug !== null && currentSlug === node.slug;
678
+ const badgeClass = node.badge ? `np-docs-sidebar-badge ${node.badge.toLowerCase()}` : null;
679
+ return /* @__PURE__ */ jsxs7(
680
+ "a",
681
+ {
682
+ href: `/docs/${node.slug}`,
683
+ "data-current": isCurrent ? "true" : void 0,
684
+ "aria-current": isCurrent ? "page" : void 0,
685
+ children: [
686
+ node.title,
687
+ badgeClass ? /* @__PURE__ */ jsx9("span", { className: badgeClass, children: node.badge.toUpperCase() }) : null
688
+ ]
689
+ }
690
+ );
691
+ }
445
692
 
446
693
  // src/styles.ts
447
694
  var docsCss = `
@@ -451,242 +698,830 @@ var docsCss = `
451
698
  min-height: 100vh;
452
699
  background: var(--np-color-background);
453
700
  color: var(--np-color-foreground);
454
- font-family: var(--np-font-body, system-ui, sans-serif);
701
+ font-family: var(--np-font-body, "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
702
+ line-height: 1.6;
703
+ -webkit-font-smoothing: antialiased;
704
+ }
705
+ .np-docs-shell a { color: inherit; }
706
+ .np-docs-shell code,
707
+ .np-docs-shell pre,
708
+ .np-docs-shell kbd {
709
+ font-family: var(--np-font-mono, "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace);
455
710
  }
456
711
 
712
+ /* ============================================================
713
+ * Header \u2014 sticky bar with brand + version pill, \u2318K search in
714
+ * the center, primary nav + GitHub link on the right. Grid
715
+ * keeps everything anchored regardless of viewport width.
716
+ * ============================================================ */
457
717
  .np-docs-header {
458
718
  position: sticky;
459
719
  top: 0;
460
- z-index: 50;
461
- background: var(--np-color-background);
720
+ z-index: 30;
721
+ background: color-mix(in oklab, var(--np-color-background) 80%, transparent);
722
+ backdrop-filter: saturate(140%) blur(14px);
723
+ -webkit-backdrop-filter: saturate(140%) blur(14px);
462
724
  border-bottom: 1px solid var(--np-color-border);
463
- backdrop-filter: blur(8px);
464
725
  }
465
-
466
726
  .np-docs-header-inner {
467
- max-width: 1200px;
727
+ max-width: 1380px;
468
728
  margin: 0 auto;
469
- padding: 0.75rem 1.5rem;
729
+ padding: 0.7rem 1.5rem;
470
730
  display: grid;
471
- grid-template-columns: auto 1fr auto;
731
+ grid-template-columns: minmax(220px, 1fr) minmax(0, 2fr) auto;
472
732
  gap: 1.5rem;
473
733
  align-items: center;
474
734
  }
475
-
476
735
  .np-docs-brand {
477
- display: flex;
478
- align-items: baseline;
479
- gap: 0.5rem;
480
- font-weight: 600;
736
+ display: inline-flex;
737
+ align-items: center;
738
+ gap: 0.55rem;
739
+ font-weight: 700;
740
+ font-size: 1.0625rem;
741
+ letter-spacing: -0.02em;
481
742
  text-decoration: none;
482
- color: var(--np-color-foreground);
483
743
  }
484
-
744
+ .np-docs-brand-mark {
745
+ width: 1.55rem;
746
+ height: 1.55rem;
747
+ border-radius: 6px;
748
+ background: linear-gradient(135deg, var(--np-color-primary, #2563eb), #0ea5e9);
749
+ position: relative;
750
+ flex: none;
751
+ }
752
+ .np-docs-brand-mark::after {
753
+ content: "";
754
+ position: absolute;
755
+ inset: 5px;
756
+ border-radius: 2px;
757
+ background: var(--np-color-background, #fff);
758
+ opacity: 0.95;
759
+ clip-path: polygon(0 0, 100% 0, 100% 100%, 60% 100%, 0 35%);
760
+ }
761
+ .np-docs-brand-name { font-weight: 700; }
485
762
  .np-docs-brand-version {
486
- font-size: 0.75rem;
487
- font-family: ui-monospace, "SF Mono", Menlo, monospace;
488
- color: var(--np-color-muted-foreground);
489
- background: var(--np-color-muted);
490
- padding: 0.125rem 0.375rem;
491
- border-radius: 0.25rem;
763
+ font-family: var(--np-font-mono);
764
+ font-size: 0.72rem;
765
+ font-weight: 500;
766
+ color: var(--np-color-primary);
767
+ background: color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card));
768
+ padding: 0.15rem 0.45rem;
769
+ border-radius: 5px;
492
770
  }
493
771
 
494
772
  .np-docs-search-form {
495
- flex: 1;
773
+ max-width: 520px;
774
+ width: 100%;
775
+ position: relative;
776
+ justify-self: center;
777
+ }
778
+ .np-docs-search-form svg {
779
+ position: absolute;
780
+ top: 50%;
781
+ left: 0.85rem;
782
+ transform: translateY(-50%);
783
+ color: var(--np-color-muted-foreground);
496
784
  }
497
-
498
785
  .np-docs-search-input {
499
786
  width: 100%;
500
- padding: 0.4rem 0.75rem;
501
- border: 1px solid var(--np-color-border);
502
- border-radius: 0.375rem;
503
- background: var(--np-color-card);
504
- color: var(--np-color-foreground);
787
+ padding: 0.55rem 0.85rem 0.55rem 2.4rem;
788
+ font: inherit;
505
789
  font-size: 0.875rem;
790
+ color: var(--np-color-foreground);
791
+ background: var(--np-color-card);
792
+ border: 1px solid var(--np-color-border);
793
+ border-radius: 9px;
794
+ }
795
+ .np-docs-search-input::placeholder {
796
+ color: var(--np-color-muted-foreground);
506
797
  }
507
-
508
798
  .np-docs-search-input:focus {
509
- outline: 2px solid var(--np-color-primary);
510
- outline-offset: -2px;
511
- border-color: transparent;
799
+ outline: none;
800
+ border-color: var(--np-color-primary);
801
+ box-shadow: 0 0 0 3px color-mix(in oklab, var(--np-color-primary) 22%, transparent);
802
+ }
803
+ .np-docs-search-kbd {
804
+ position: absolute;
805
+ right: 0.6rem;
806
+ top: 50%;
807
+ transform: translateY(-50%);
808
+ font-size: 0.7rem;
809
+ padding: 0.1rem 0.4rem;
810
+ color: var(--np-color-muted-foreground);
811
+ border: 1px solid var(--np-color-border);
812
+ border-radius: 4px;
512
813
  }
513
814
 
514
815
  .np-docs-nav {
515
816
  display: flex;
516
817
  align-items: center;
517
- gap: 1rem;
818
+ gap: 1.25rem;
518
819
  }
519
-
520
820
  .np-docs-primary-nav {
521
821
  display: flex;
522
822
  list-style: none;
823
+ gap: 1.25rem;
523
824
  margin: 0;
524
825
  padding: 0;
525
- gap: 1rem;
526
826
  }
527
-
528
827
  .np-docs-primary-nav a {
529
828
  color: var(--np-color-muted-foreground);
530
- text-decoration: none;
531
829
  font-size: 0.875rem;
830
+ font-weight: 500;
831
+ text-decoration: none;
532
832
  }
533
-
534
- .np-docs-primary-nav a:hover {
833
+ .np-docs-primary-nav a:hover,
834
+ .np-docs-primary-nav a[aria-current="page"] {
535
835
  color: var(--np-color-foreground);
536
836
  }
537
-
837
+ .np-docs-github,
538
838
  .np-docs-github-link {
539
- font-size: 0.875rem;
839
+ display: inline-flex;
840
+ align-items: center;
841
+ gap: 0.45rem;
842
+ padding: 0.4rem 0.7rem;
843
+ font-size: 0.8125rem;
540
844
  color: var(--np-color-muted-foreground);
845
+ background: var(--np-color-muted);
846
+ border: 1px solid var(--np-color-border);
847
+ border-radius: 7px;
541
848
  text-decoration: none;
542
849
  }
850
+ .np-docs-github:hover,
851
+ .np-docs-github-link:hover {
852
+ color: var(--np-color-foreground);
853
+ }
543
854
 
544
- .np-docs-grid {
545
- flex: 1;
546
- display: grid;
547
- grid-template-columns: 240px minmax(0, 1fr);
548
- max-width: 1200px;
855
+ @media (max-width: 800px) {
856
+ .np-docs-header-inner {
857
+ grid-template-columns: auto 1fr auto;
858
+ gap: 0.75rem;
859
+ }
860
+ .np-docs-search-form { display: none; }
861
+ .np-docs-primary-nav { display: none; }
862
+ }
863
+
864
+ /* ============================================================
865
+ * 3-column layout: sidebar + article + on-page TOC.
866
+ * ============================================================ */
867
+ .np-docs-grid,
868
+ .np-docs-body {
869
+ max-width: 1380px;
549
870
  margin: 0 auto;
550
871
  width: 100%;
551
- gap: 2.5rem;
552
- padding: 2rem 1.5rem;
872
+ display: grid;
873
+ grid-template-columns: 260px minmax(0, 1fr) 220px;
874
+ gap: 3rem;
875
+ padding: 2.25rem 1.5rem 4rem;
553
876
  }
554
-
555
- @media (max-width: 768px) {
556
- .np-docs-grid {
557
- grid-template-columns: 1fr;
877
+ @media (max-width: 1100px) {
878
+ .np-docs-grid,
879
+ .np-docs-body {
880
+ grid-template-columns: 240px minmax(0, 1fr);
558
881
  }
559
- .np-docs-sidebar {
560
- display: none;
882
+ .np-docs-toc { display: none; }
883
+ }
884
+ @media (max-width: 800px) {
885
+ .np-docs-grid,
886
+ .np-docs-body {
887
+ grid-template-columns: 1fr;
561
888
  }
889
+ .np-docs-sidebar { display: none; }
562
890
  }
563
891
 
892
+ /* ============================================================
893
+ * Sidebar \u2014 grouped link list with bullet eyebrow + badges.
894
+ * ============================================================ */
564
895
  .np-docs-sidebar {
565
896
  position: sticky;
566
- top: 4rem;
897
+ top: 4.25rem;
567
898
  align-self: start;
568
899
  max-height: calc(100vh - 5rem);
569
900
  overflow-y: auto;
901
+ padding-right: 0.5rem;
570
902
  }
571
-
572
- .np-docs-sidebar h2 {
573
- font-size: 0.75rem;
903
+ .np-docs-sidebar-group { margin-bottom: 1.5rem; }
904
+ .np-docs-sidebar-eyebrow {
905
+ display: flex;
906
+ align-items: center;
907
+ gap: 0.4rem;
908
+ font-family: var(--np-font-mono);
909
+ font-size: 0.7rem;
574
910
  text-transform: uppercase;
575
911
  letter-spacing: 0.08em;
576
912
  color: var(--np-color-muted-foreground);
577
- margin: 0 0 0.75rem;
913
+ margin: 0 0 0.65rem;
914
+ font-weight: 600;
915
+ }
916
+ .np-docs-sidebar-eyebrow-dot {
917
+ width: 0.4rem;
918
+ height: 0.4rem;
919
+ border-radius: 50%;
920
+ background: var(--np-color-primary);
578
921
  }
579
-
580
922
  .np-docs-sidebar ul {
581
923
  list-style: none;
582
924
  padding: 0;
583
925
  margin: 0;
584
926
  }
585
-
586
- .np-docs-sidebar li {
587
- margin: 0.125rem 0;
588
- }
589
-
927
+ .np-docs-sidebar li { margin: 0.05rem 0; }
590
928
  .np-docs-sidebar a {
591
929
  display: block;
592
- padding: 0.25rem 0.5rem;
593
- border-radius: 0.25rem;
930
+ padding: 0.34rem 0.6rem;
931
+ font-size: 0.875rem;
594
932
  color: var(--np-color-muted-foreground);
595
933
  text-decoration: none;
596
- font-size: 0.875rem;
934
+ border-radius: 6px;
935
+ line-height: 1.35;
597
936
  }
598
-
599
937
  .np-docs-sidebar a:hover {
600
938
  background: var(--np-color-muted);
601
939
  color: var(--np-color-foreground);
602
940
  }
603
-
604
- .np-docs-sidebar a[data-current="true"] {
605
- background: color-mix(in oklch, var(--np-color-primary) 12%, transparent);
941
+ .np-docs-sidebar a[data-current="true"],
942
+ .np-docs-sidebar a[aria-current="page"] {
606
943
  color: var(--np-color-primary);
944
+ background: color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card));
607
945
  font-weight: 500;
608
946
  }
609
-
610
947
  .np-docs-sidebar ul ul {
611
- margin-left: 0.75rem;
948
+ margin-left: 0.5rem;
949
+ padding-left: 0.85rem;
612
950
  border-left: 1px solid var(--np-color-border);
613
- padding-left: 0.5rem;
951
+ }
952
+ .np-docs-sidebar-badge {
953
+ display: inline-block;
954
+ font-family: var(--np-font-mono);
955
+ font-size: 0.62rem;
956
+ padding: 0.02rem 0.34rem;
957
+ margin-left: 0.4rem;
958
+ vertical-align: 1px;
959
+ border-radius: 4px;
960
+ background: var(--np-color-muted);
961
+ color: var(--np-color-muted-foreground);
962
+ font-weight: 500;
963
+ }
964
+ .np-docs-sidebar-badge.new { background: #dcfce7; color: #166534; }
965
+ .np-docs-sidebar-badge.beta { background: #fef3c7; color: #92400e; }
966
+ .np-docs-sidebar-badge.api {
967
+ background: color-mix(in oklab, var(--np-color-primary) 16%, var(--np-color-card));
968
+ color: var(--np-color-primary);
614
969
  }
615
970
 
971
+ /* ============================================================
972
+ * Doc page \u2014 article column. h1 + lede + meta row + sections
973
+ * with hovered anchor link icon.
974
+ * ============================================================ */
616
975
  .np-docs-page {
617
- max-width: 720px;
976
+ max-width: 760px;
977
+ min-width: 0;
978
+ }
979
+ .np-docs-breadcrumbs {
980
+ display: flex;
981
+ align-items: center;
982
+ gap: 0.4rem;
983
+ font-size: 0.8125rem;
984
+ color: var(--np-color-muted-foreground);
985
+ margin-bottom: 1rem;
618
986
  }
987
+ .np-docs-breadcrumbs a {
988
+ color: inherit;
989
+ text-decoration: none;
990
+ }
991
+ .np-docs-breadcrumbs a:hover { color: var(--np-color-foreground); }
992
+ .np-docs-breadcrumbs-sep { opacity: 0.5; }
619
993
 
620
994
  .np-docs-page h1 {
621
- font-size: 2rem;
995
+ font-size: clamp(2rem, 3.6vw, 2.5rem);
996
+ font-weight: 700;
997
+ letter-spacing: -0.03em;
998
+ line-height: 1.1;
622
999
  margin: 0 0 0.5rem;
1000
+ text-wrap: balance;
1001
+ }
1002
+ .np-docs-page-lede {
1003
+ font-size: 1.125rem;
1004
+ color: var(--np-color-muted-foreground);
1005
+ line-height: 1.55;
1006
+ margin: 0 0 2rem;
1007
+ max-width: 38rem;
1008
+ text-wrap: pretty;
1009
+ }
1010
+ .np-docs-page-meta {
1011
+ display: flex;
1012
+ flex-wrap: wrap;
1013
+ gap: 0.5rem;
1014
+ align-items: center;
1015
+ font-size: 0.8125rem;
1016
+ color: var(--np-color-muted-foreground);
1017
+ padding: 0.85rem 0;
1018
+ margin-bottom: 2rem;
1019
+ border-top: 1px solid var(--np-color-border);
1020
+ border-bottom: 1px solid var(--np-color-border);
1021
+ }
1022
+ .np-docs-page-meta-pill {
1023
+ display: inline-flex;
1024
+ align-items: center;
1025
+ gap: 0.35rem;
1026
+ padding: 0.15rem 0.55rem;
1027
+ font-family: var(--np-font-mono);
1028
+ font-size: 0.72rem;
1029
+ border: 1px solid var(--np-color-border);
1030
+ border-radius: 999px;
1031
+ background: var(--np-color-card);
1032
+ }
1033
+ .np-docs-page-meta-pill.status {
1034
+ color: #047857;
1035
+ border-color: #bbf7d0;
1036
+ background: #f0fdf4;
1037
+ }
1038
+ .np-docs-page-meta-pill.status::before {
1039
+ content: "";
1040
+ width: 0.4rem;
1041
+ height: 0.4rem;
1042
+ border-radius: 50%;
1043
+ background: #047857;
1044
+ }
1045
+ .np-docs-page-meta-sep { opacity: 0.4; }
1046
+ .np-docs-page-meta a {
1047
+ color: var(--np-color-primary);
1048
+ text-decoration: none;
1049
+ margin-left: auto;
1050
+ }
1051
+ .np-docs-page-meta a:hover { text-decoration: underline; }
1052
+
1053
+ .np-docs-page h2 {
1054
+ font-size: 1.5rem;
1055
+ font-weight: 600;
1056
+ letter-spacing: -0.02em;
1057
+ line-height: 1.25;
1058
+ margin: 3rem 0 0.85rem;
1059
+ scroll-margin-top: 5rem;
1060
+ position: relative;
1061
+ }
1062
+ .np-docs-page h2:first-of-type { margin-top: 2.5rem; }
1063
+ .np-docs-page h3 {
1064
+ font-size: 1.1rem;
1065
+ font-weight: 600;
1066
+ letter-spacing: -0.01em;
1067
+ margin: 2.25rem 0 0.7rem;
1068
+ scroll-margin-top: 5rem;
1069
+ position: relative;
1070
+ }
1071
+ .np-docs-page p { margin: 0 0 1rem; }
1072
+ .np-docs-page p code,
1073
+ .np-docs-page li code {
1074
+ font-size: 0.875em;
1075
+ padding: 0.1em 0.35em;
1076
+ background: var(--np-color-muted);
1077
+ border: 1px solid var(--np-color-border);
1078
+ border-radius: 4px;
1079
+ }
1080
+ .np-docs-page strong { font-weight: 600; }
1081
+ .np-docs-page ul,
1082
+ .np-docs-page ol {
1083
+ margin: 0 0 1rem;
1084
+ padding-left: 1.4rem;
1085
+ }
1086
+ .np-docs-page li { margin: 0.35rem 0; }
1087
+ .np-docs-page a:not(.np-docs-prev-next a):not(.np-docs-anchor) {
1088
+ color: var(--np-color-primary);
1089
+ text-decoration: underline;
1090
+ text-underline-offset: 3px;
1091
+ text-decoration-thickness: 1px;
1092
+ text-decoration-color: color-mix(in oklab, var(--np-color-primary) 45%, transparent);
1093
+ }
1094
+ .np-docs-page a:not(.np-docs-prev-next a):not(.np-docs-anchor):hover {
1095
+ text-decoration-color: currentColor;
1096
+ }
1097
+
1098
+ /* Anchor icon \u2014 visible only on heading hover. */
1099
+ .np-docs-anchor {
1100
+ position: absolute;
1101
+ left: -1.3rem;
1102
+ top: 50%;
1103
+ transform: translateY(-50%);
1104
+ color: var(--np-color-muted-foreground);
1105
+ opacity: 0;
1106
+ text-decoration: none !important;
1107
+ font-weight: 400;
1108
+ }
1109
+ .np-docs-page h2:hover .np-docs-anchor,
1110
+ .np-docs-page h3:hover .np-docs-anchor { opacity: 1; }
1111
+
1112
+ /* ============================================================
1113
+ * Callouts \u2014 info (default) / note (indigo) / warn (amber) /
1114
+ * danger (red). 3px left rule carries the variant color.
1115
+ * ============================================================ */
1116
+ .np-docs-callout {
1117
+ display: grid;
1118
+ grid-template-columns: auto 1fr;
1119
+ gap: 0.85rem;
1120
+ padding: 1rem 1.15rem;
1121
+ border: 1px solid var(--np-color-border);
1122
+ border-left: 3px solid var(--np-color-primary);
1123
+ border-radius: 8px;
1124
+ background: var(--np-color-card);
1125
+ margin: 1.25rem 0;
1126
+ font-size: 0.95rem;
1127
+ line-height: 1.55;
1128
+ }
1129
+ .np-docs-callout > svg,
1130
+ .np-docs-callout-icon {
1131
+ width: 1.25rem;
1132
+ height: 1.25rem;
1133
+ flex-shrink: 0;
1134
+ color: var(--np-color-primary);
1135
+ margin-top: 0.1rem;
1136
+ }
1137
+ .np-docs-callout p { margin: 0; }
1138
+ .np-docs-callout-title {
1139
+ font-weight: 600;
1140
+ margin-bottom: 0.15rem;
1141
+ color: var(--np-color-foreground);
1142
+ }
1143
+ .np-docs-callout--warn {
1144
+ border-left-color: #b45309;
1145
+ background: #fffbeb;
1146
+ border-color: #fde68a;
1147
+ }
1148
+ .np-docs-callout--warn .np-docs-callout-icon,
1149
+ .np-docs-callout--warn > svg { color: #b45309; }
1150
+ .np-docs-callout--note {
1151
+ border-left-color: #6366f1;
1152
+ background: #eef2ff;
1153
+ border-color: #c7d2fe;
1154
+ }
1155
+ .np-docs-callout--note .np-docs-callout-icon,
1156
+ .np-docs-callout--note > svg { color: #4338ca; }
1157
+ .np-docs-callout--danger {
1158
+ border-left-color: #b91c1c;
1159
+ background: #fef2f2;
1160
+ border-color: #fecaca;
1161
+ }
1162
+ .np-docs-callout--danger .np-docs-callout-icon,
1163
+ .np-docs-callout--danger > svg { color: #b91c1c; }
1164
+
1165
+ /* ============================================================
1166
+ * Code blocks \u2014 dark surface with a file-named header and a
1167
+ * copy button. Syntax tokens (.tk-*) cover the common slots
1168
+ * (keyword / string / function / number / type / punctuation /
1169
+ * comment) using a muted neutral-paired palette so the block
1170
+ * reads at the same contrast as the page chrome.
1171
+ * ============================================================ */
1172
+ .np-docs-code {
1173
+ margin: 1.25rem 0;
1174
+ border-radius: 10px;
1175
+ background: #0b1220;
1176
+ color: #e6edf6;
1177
+ overflow: hidden;
1178
+ border: 1px solid #1e2939;
1179
+ }
1180
+ .np-docs-code-head {
1181
+ display: flex;
1182
+ align-items: center;
1183
+ justify-content: space-between;
1184
+ padding: 0.55rem 0.85rem;
1185
+ background: #0f1a2b;
1186
+ border-bottom: 1px solid #1e293b;
1187
+ }
1188
+ .np-docs-code-file {
1189
+ display: inline-flex;
1190
+ align-items: center;
1191
+ gap: 0.5rem;
1192
+ font-family: var(--np-font-mono);
1193
+ font-size: 0.78rem;
1194
+ color: #94a3b8;
1195
+ }
1196
+ .np-docs-code-file svg { color: #64748b; }
1197
+ .np-docs-code-copy {
1198
+ display: inline-flex;
1199
+ align-items: center;
1200
+ gap: 0.35rem;
1201
+ padding: 0.25rem 0.55rem;
1202
+ font-size: 0.72rem;
1203
+ font-family: var(--np-font-mono);
1204
+ color: #94a3b8;
1205
+ background: transparent;
1206
+ border: 1px solid #1e293b;
1207
+ border-radius: 5px;
1208
+ cursor: pointer;
1209
+ }
1210
+ .np-docs-code-copy:hover {
1211
+ color: #e2e8f0;
1212
+ border-color: #334155;
1213
+ }
1214
+ .np-docs-code pre {
1215
+ margin: 0;
1216
+ padding: 1rem 1.1rem;
1217
+ font-size: 0.825rem;
1218
+ line-height: 1.65;
1219
+ overflow-x: auto;
1220
+ }
1221
+ .np-docs-code pre code {
1222
+ display: block;
1223
+ font-family: inherit;
1224
+ background: transparent;
1225
+ border: 0;
1226
+ padding: 0;
1227
+ color: inherit;
1228
+ }
1229
+ .tk-c { color: #64748b; font-style: italic; }
1230
+ .tk-k { color: #c084fc; }
1231
+ .tk-s { color: #86efac; }
1232
+ .tk-f { color: #93c5fd; }
1233
+ .tk-t { color: #fcd34d; }
1234
+ .tk-n { color: #f9a8d4; }
1235
+ .tk-p { color: #e2e8f0; }
1236
+
1237
+ /* Inline shell snippet \u2014 for terse \`pnpm dev\` style commands.
1238
+ * Named \`cmdline\` (not \`shell\`) so it doesn't collide with the
1239
+ * route shell container at \`.np-docs-shell\`. */
1240
+ .np-docs-cmdline {
1241
+ display: grid;
1242
+ grid-template-columns: auto 1fr auto;
1243
+ gap: 0.7rem;
1244
+ align-items: center;
1245
+ padding: 0.75rem 1rem;
1246
+ margin: 1.25rem 0;
1247
+ background: #0b1220;
1248
+ color: #e6edf6;
1249
+ border-radius: 9px;
1250
+ font-family: var(--np-font-mono);
1251
+ font-size: 0.875rem;
1252
+ }
1253
+ .np-docs-cmdline-prompt { color: #34d399; }
1254
+ .np-docs-cmdline-cmd { color: #e2e8f0; }
1255
+ .np-docs-cmdline-copy {
1256
+ padding: 0.2rem 0.55rem;
1257
+ font-size: 0.7rem;
1258
+ color: #94a3b8;
1259
+ background: transparent;
1260
+ border: 1px solid #1e293b;
1261
+ border-radius: 5px;
1262
+ cursor: pointer;
1263
+ }
1264
+ .np-docs-cmdline-copy:hover { color: #e2e8f0; border-color: #334155; }
1265
+
1266
+ /* ============================================================
1267
+ * Numbered steps \u2014 counter on a soft pill before each step.
1268
+ * ============================================================ */
1269
+ .np-docs-steps {
1270
+ counter-reset: step;
1271
+ list-style: none;
1272
+ padding: 0;
1273
+ margin: 1.5rem 0;
1274
+ display: grid;
1275
+ gap: 1rem;
1276
+ }
1277
+ .np-docs-steps > li {
1278
+ counter-increment: step;
1279
+ display: grid;
1280
+ grid-template-columns: 2.1rem 1fr;
1281
+ gap: 0.85rem;
1282
+ align-items: start;
1283
+ }
1284
+ .np-docs-steps > li::before {
1285
+ content: counter(step);
1286
+ width: 1.85rem;
1287
+ height: 1.85rem;
1288
+ display: inline-flex;
1289
+ align-items: center;
1290
+ justify-content: center;
1291
+ font-family: var(--np-font-mono);
1292
+ font-size: 0.85rem;
1293
+ font-weight: 600;
1294
+ color: var(--np-color-primary);
1295
+ background: color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card));
1296
+ border-radius: 50%;
1297
+ }
1298
+ .np-docs-step-title {
1299
+ font-weight: 600;
1300
+ margin: 0.25rem 0 0.25rem;
1301
+ }
1302
+ .np-docs-step-body {
1303
+ margin: 0;
1304
+ color: var(--np-color-muted-foreground);
1305
+ }
1306
+
1307
+ /* ============================================================
1308
+ * API / reference tables \u2014 uppercase mono headers.
1309
+ * ============================================================ */
1310
+ .np-docs-table {
1311
+ width: 100%;
1312
+ border-collapse: collapse;
1313
+ font-size: 0.875rem;
1314
+ margin: 1.25rem 0;
1315
+ }
1316
+ .np-docs-table thead { background: var(--np-color-muted); }
1317
+ .np-docs-table th,
1318
+ .np-docs-table td {
1319
+ text-align: left;
1320
+ padding: 0.7rem 0.85rem;
1321
+ border-bottom: 1px solid var(--np-color-border);
1322
+ vertical-align: top;
1323
+ }
1324
+ .np-docs-table th {
1325
+ font-family: var(--np-font-mono);
1326
+ font-size: 0.72rem;
1327
+ text-transform: uppercase;
1328
+ letter-spacing: 0.06em;
1329
+ color: var(--np-color-muted-foreground);
1330
+ font-weight: 600;
1331
+ }
1332
+ .np-docs-table td:first-child code {
1333
+ color: var(--np-color-foreground);
1334
+ font-weight: 500;
1335
+ }
1336
+ .np-docs-table-required {
1337
+ display: inline-block;
1338
+ font-family: var(--np-font-mono);
1339
+ font-size: 0.65rem;
1340
+ padding: 0.05rem 0.35rem;
1341
+ margin-left: 0.4rem;
1342
+ background: #fef3c7;
1343
+ color: #92400e;
1344
+ border-radius: 4px;
1345
+ vertical-align: 1px;
623
1346
  }
624
1347
 
1348
+ /* ============================================================
1349
+ * Prev / next \u2014 symmetric pair at the foot of every doc page.
1350
+ * Hover lifts the bordered card and tints the border primary.
1351
+ * ============================================================ */
625
1352
  .np-docs-prev-next {
626
1353
  display: grid;
627
1354
  grid-template-columns: 1fr 1fr;
628
1355
  gap: 1rem;
629
- margin-top: 3rem;
630
- padding-top: 1.5rem;
1356
+ margin: 3.5rem 0 1rem;
1357
+ padding-top: 2rem;
631
1358
  border-top: 1px solid var(--np-color-border);
632
1359
  }
633
-
634
1360
  .np-docs-prev-next a {
635
1361
  display: block;
636
- padding: 0.75rem 1rem;
1362
+ padding: 1rem 1.15rem;
1363
+ background: var(--np-color-card);
637
1364
  border: 1px solid var(--np-color-border);
638
- border-radius: 0.5rem;
639
- color: var(--np-color-foreground);
1365
+ border-radius: 10px;
640
1366
  text-decoration: none;
1367
+ transition: border-color 0.15s ease, transform 0.2s ease;
641
1368
  }
642
-
643
1369
  .np-docs-prev-next a:hover {
644
1370
  border-color: var(--np-color-primary);
1371
+ transform: translateY(-1px);
645
1372
  }
646
-
1373
+ .np-docs-prev-next-dir,
647
1374
  .np-docs-prev-next-label {
648
- display: block;
649
- font-size: 0.75rem;
1375
+ font-family: var(--np-font-mono);
1376
+ font-size: 0.72rem;
650
1377
  color: var(--np-color-muted-foreground);
1378
+ letter-spacing: 0.05em;
651
1379
  margin-bottom: 0.25rem;
652
1380
  }
1381
+ .np-docs-prev-next-title {
1382
+ font-weight: 600;
1383
+ font-size: 0.95rem;
1384
+ }
1385
+ .np-docs-prev-next a.np-docs-prev-next-next,
1386
+ .np-docs-prev-next a:last-child { text-align: right; }
653
1387
 
654
- /* M.* member surface \u2014 narrow auth-form column under the
655
- masthead, no sidebar (the docs sidebar is hierarchical
656
- doc nav, useless on auth forms). */
657
- .np-docs-members {
1388
+ /* ============================================================
1389
+ * Feedback row \u2014 Yes / Could be better buttons under each page.
1390
+ * ============================================================ */
1391
+ .np-docs-feedback {
1392
+ margin-top: 3rem;
1393
+ padding: 1.25rem;
1394
+ background: var(--np-color-muted);
1395
+ border: 1px solid var(--np-color-border);
1396
+ border-radius: 10px;
658
1397
  display: flex;
659
- justify-content: center;
660
- min-height: 60vh;
661
- padding: 3rem 1.5rem;
1398
+ gap: 1rem;
1399
+ align-items: center;
1400
+ justify-content: space-between;
1401
+ flex-wrap: wrap;
662
1402
  }
663
- .np-docs-members-column {
664
- width: 100%;
665
- max-width: 440px;
1403
+ .np-docs-feedback-title { font-weight: 600; font-size: 0.95rem; }
1404
+ .np-docs-feedback-helper {
1405
+ font-size: 0.825rem;
1406
+ color: var(--np-color-muted-foreground);
1407
+ margin-top: 0.15rem;
1408
+ }
1409
+ .np-docs-feedback-buttons {
1410
+ display: flex;
1411
+ gap: 0.5rem;
1412
+ }
1413
+ .np-docs-feedback-buttons button {
1414
+ padding: 0.4rem 0.85rem;
1415
+ font: inherit;
1416
+ font-size: 0.825rem;
1417
+ background: var(--np-color-card);
1418
+ border: 1px solid var(--np-color-border);
1419
+ border-radius: 7px;
1420
+ cursor: pointer;
1421
+ }
1422
+ .np-docs-feedback-buttons button:hover {
1423
+ border-color: var(--np-color-primary);
1424
+ color: var(--np-color-primary);
666
1425
  }
667
1426
 
668
- /* Member form token overrides \u2014 docs aesthetic: slightly
669
- rounded corners, neutral palette, monospace label accent. */
670
- .np-docs .np-members-form {
671
- --np-member-form-input-bg: var(--np-color-background);
672
- --np-member-form-input-border: var(--np-color-border);
673
- --np-member-form-input-border-focus: var(--np-color-primary);
674
- --np-member-form-input-radius: 0.375rem;
675
- --np-member-form-button-radius: 0.375rem;
1427
+ /* ============================================================
1428
+ * On-page TOC \u2014 right rail, sticky, current section gets a
1429
+ * primary border + soft gradient.
1430
+ * ============================================================ */
1431
+ .np-docs-toc {
1432
+ position: sticky;
1433
+ top: 4.25rem;
1434
+ align-self: start;
1435
+ max-height: calc(100vh - 5rem);
1436
+ overflow-y: auto;
1437
+ font-size: 0.825rem;
676
1438
  }
677
- .np-docs .np-members-form .np-form-label {
678
- font-family: var(--np-font-mono, ui-monospace, monospace);
679
- font-size: 0.8125rem;
1439
+ .np-docs-toc-eyebrow {
1440
+ font-family: var(--np-font-mono);
1441
+ font-size: 0.7rem;
1442
+ text-transform: uppercase;
1443
+ letter-spacing: 0.08em;
1444
+ color: var(--np-color-muted-foreground);
1445
+ margin: 0 0 0.75rem;
1446
+ font-weight: 600;
1447
+ }
1448
+ .np-docs-toc ul {
1449
+ list-style: none;
1450
+ padding: 0;
1451
+ margin: 0;
1452
+ }
1453
+ .np-docs-toc li { margin: 0.05rem 0; }
1454
+ .np-docs-toc a {
1455
+ display: block;
1456
+ padding: 0.3rem 0.5rem;
1457
+ color: var(--np-color-muted-foreground);
1458
+ text-decoration: none;
1459
+ border-left: 2px solid transparent;
1460
+ margin-left: -2px;
1461
+ line-height: 1.4;
1462
+ }
1463
+ .np-docs-toc a:hover { color: var(--np-color-foreground); }
1464
+ .np-docs-toc a[data-current="true"],
1465
+ .np-docs-toc a[aria-current="location"],
1466
+ .np-docs-toc a[aria-current="true"] {
1467
+ color: var(--np-color-primary);
1468
+ border-left-color: var(--np-color-primary);
1469
+ background: linear-gradient(
1470
+ to right,
1471
+ color-mix(in oklab, var(--np-color-primary) 14%, var(--np-color-card)),
1472
+ transparent 80%
1473
+ );
1474
+ }
1475
+ .np-docs-toc ul ul { margin-left: 0.85rem; }
1476
+ .np-docs-toc-secondary {
1477
+ margin-top: 1.5rem;
1478
+ padding-top: 1rem;
1479
+ border-top: 1px solid var(--np-color-border);
1480
+ }
1481
+ .np-docs-toc-secondary a {
1482
+ display: inline-flex;
1483
+ align-items: center;
1484
+ gap: 0.35rem;
1485
+ padding: 0.2rem 0;
1486
+ border-left: 0;
1487
+ margin: 0;
1488
+ }
1489
+ .np-docs-toc-secondary a:hover { background: transparent; }
1490
+
1491
+ /* Empty / not-found surfaces \u2014 used by routes/not-found and
1492
+ * the docs collection's empty state. */
1493
+ .np-docs-empty {
1494
+ padding: 4rem 1.5rem;
1495
+ text-align: center;
1496
+ color: var(--np-color-muted-foreground);
1497
+ }
1498
+ .np-docs-empty h1 {
1499
+ font-size: 1.5rem;
1500
+ margin: 0 0 0.5rem;
1501
+ color: var(--np-color-foreground);
680
1502
  }
681
1503
  `;
682
1504
 
683
1505
  // src/index.ts
1506
+ var SEED_NAV = {
1507
+ header: [
1508
+ { id: "nav-docs-docs", label: "Docs", type: "link", url: "/docs" },
1509
+ { id: "nav-docs-reference", label: "Reference", type: "link", url: "/docs/reference" },
1510
+ { id: "nav-docs-blog", label: "Blog", type: "link", url: "/blog" }
1511
+ ],
1512
+ footer: [
1513
+ { id: "nav-docs-footer-docs", label: "Documentation", type: "link", url: "/docs" },
1514
+ { id: "nav-docs-footer-reference", label: "Reference", type: "link", url: "/docs/reference" },
1515
+ { id: "nav-docs-footer-changelog", label: "Changelog", type: "link", url: "/changelog" },
1516
+ { id: "nav-docs-footer-github", label: "GitHub", type: "link", url: "https://github.com" }
1517
+ ]
1518
+ };
684
1519
  var docsTheme = defineTheme({
685
1520
  manifest: {
686
1521
  id: "docs",
687
1522
  name: "Docs",
688
- version: "0.1.0",
689
- description: "Documentation theme \u2014 hierarchical sidebar, prev/next nav, search masthead. Pairs with a `docs` collection that has parent/order fields.",
1523
+ version: "0.2.0",
1524
+ description: "Documentation theme \u2014 three-column layout with hierarchical sidebar, breadcrumbs + lede + meta pills on the article column, on-this-page TOC on the right rail. Blue accent on a near-white surface; pairs with a `docs` collection.",
690
1525
  author: { name: "NexPress" },
691
1526
  nexpress: { minVersion: "0.1.0" },
692
1527
  requires: {
@@ -695,13 +1530,20 @@ var docsTheme = defineTheme({
695
1530
  createIfAbsent: true,
696
1531
  fields: {
697
1532
  title: { type: "text", required: true },
1533
+ // Short opening paragraph rendered as a lede under the
1534
+ // h1. Optional — the article still renders without it.
1535
+ lede: { type: "textarea", hard: false },
698
1536
  body: { type: "richText" },
699
1537
  parent: {
700
1538
  type: "relationship",
701
1539
  relationTo: "docs",
702
1540
  hard: false
703
1541
  },
704
- order: { type: "number" }
1542
+ order: { type: "number" },
1543
+ // Meta-pill slots — all optional, all advisory hints
1544
+ // the doc-page template surfaces in the strap row.
1545
+ stableSince: { type: "text", hard: false },
1546
+ badge: { type: "text", hard: false }
705
1547
  }
706
1548
  }
707
1549
  }
@@ -716,28 +1558,35 @@ var docsTheme = defineTheme({
716
1558
  },
717
1559
  css: docsCss,
718
1560
  tokens: {
719
- // Docs lean cool/neutral with a sharp accent — distinct
720
- // from magazine's warm cream so a side-by-side preview
721
- // makes the swap obvious.
722
1561
  colors: {
723
- primary: "oklch(0.55 0.18 260)",
724
- primaryForeground: "oklch(0.985 0.005 260)",
725
- background: "oklch(0.99 0.005 260)",
726
- foreground: "oklch(0.18 0.025 260)",
727
- muted: "oklch(0.95 0.012 260)",
728
- mutedForeground: "oklch(0.5 0.025 260)",
729
- border: "oklch(0.9 0.012 260)",
730
- card: "oklch(0.985 0.008 260)",
731
- cardForeground: "oklch(0.18 0.025 260)",
732
- accent: "oklch(0.92 0.05 260)",
733
- accentForeground: "oklch(0.18 0.025 260)"
1562
+ primary: "#2563eb",
1563
+ primaryForeground: "#ffffff",
1564
+ background: "#fbfcfe",
1565
+ foreground: "#0c1320",
1566
+ muted: "#f1f4f9",
1567
+ mutedForeground: "#5b6478",
1568
+ border: "#e2e7ef",
1569
+ card: "#ffffff"
1570
+ },
1571
+ typography: {
1572
+ fontHeading: '"Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1573
+ fontBody: '"Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1574
+ fontMono: '"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
1575
+ },
1576
+ shape: {
1577
+ radiusSm: "5px",
1578
+ radiusMd: "9px",
1579
+ radiusLg: "10px"
734
1580
  }
735
1581
  },
1582
+ seedContent: {
1583
+ navigation: SEED_NAV
1584
+ },
736
1585
  templates: {
737
1586
  docs: {
738
1587
  default: {
739
1588
  label: "Doc page",
740
- description: "Hierarchical sidebar + body + prev/next nav. Optional 'Edit on GitHub' link when settings.githubRepo is set.",
1589
+ description: "Three-column reference layout \u2014 breadcrumbs + lede + meta + Lexical body + feedback + prev/next, with the docs sidebar slotted on the left and the on-page TOC on the right.",
741
1590
  component: DocPageTemplate
742
1591
  }
743
1592
  }