@nexpress/theme-docs 0.3.1 → 0.3.2

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
@@ -1,5 +1,365 @@
1
1
  // src/index.ts
2
- import { defineTheme } from "@nexpress/theme";
2
+ import {
3
+ defineTheme
4
+ } from "@nexpress/theme";
5
+
6
+ // src/copy-button-bridge.ts
7
+ import { CopyButton } from "./components/copy-button.js";
8
+
9
+ // src/blocks/index.tsx
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ var readString = (value, fallback) => typeof value === "string" && value.trim().length > 0 ? value : fallback;
12
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13
+ var CALLOUT_VARIANTS = ["default", "note", "warn", "danger"];
14
+ function readCalloutVariant(value) {
15
+ if (typeof value === "string") {
16
+ for (const candidate of CALLOUT_VARIANTS) {
17
+ if (candidate === value) return candidate;
18
+ }
19
+ }
20
+ return "default";
21
+ }
22
+ function CalloutIcon({ variant }) {
23
+ if (variant === "warn") {
24
+ return /* @__PURE__ */ jsxs(
25
+ "svg",
26
+ {
27
+ width: "18",
28
+ height: "18",
29
+ viewBox: "0 0 24 24",
30
+ fill: "none",
31
+ stroke: "currentColor",
32
+ strokeWidth: "2",
33
+ strokeLinecap: "round",
34
+ strokeLinejoin: "round",
35
+ "aria-hidden": "true",
36
+ children: [
37
+ /* @__PURE__ */ jsx("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" }),
38
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
39
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
40
+ ]
41
+ }
42
+ );
43
+ }
44
+ if (variant === "danger") {
45
+ return /* @__PURE__ */ jsxs(
46
+ "svg",
47
+ {
48
+ width: "18",
49
+ height: "18",
50
+ viewBox: "0 0 24 24",
51
+ fill: "none",
52
+ stroke: "currentColor",
53
+ strokeWidth: "2",
54
+ strokeLinecap: "round",
55
+ strokeLinejoin: "round",
56
+ "aria-hidden": "true",
57
+ children: [
58
+ /* @__PURE__ */ jsx("polygon", { points: "7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2" }),
59
+ /* @__PURE__ */ jsx("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
60
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
61
+ ]
62
+ }
63
+ );
64
+ }
65
+ return /* @__PURE__ */ jsxs(
66
+ "svg",
67
+ {
68
+ width: "18",
69
+ height: "18",
70
+ viewBox: "0 0 24 24",
71
+ fill: "none",
72
+ stroke: "currentColor",
73
+ strokeWidth: "2",
74
+ strokeLinecap: "round",
75
+ strokeLinejoin: "round",
76
+ "aria-hidden": "true",
77
+ children: [
78
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
79
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
80
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
81
+ ]
82
+ }
83
+ );
84
+ }
85
+ var docsCalloutBlock = {
86
+ type: "docsCallout",
87
+ label: "Callout",
88
+ description: "Highlighted note / warning / danger panel with a leading icon.",
89
+ icon: "Info",
90
+ iconKind: "lucide",
91
+ summaryFields: ["title"],
92
+ category: "Content",
93
+ source: "theme",
94
+ keywords: ["note", "warning", "danger", "admonition", "info"],
95
+ defaultProps: {
96
+ variant: "default",
97
+ title: "Heads up",
98
+ body: "Add context that an operator should not miss when scanning the page."
99
+ },
100
+ propsSchema: [
101
+ {
102
+ name: "variant",
103
+ label: "Variant",
104
+ type: "select",
105
+ defaultValue: "default",
106
+ options: [
107
+ { label: "Default", value: "default" },
108
+ { label: "Note", value: "note" },
109
+ { label: "Warning", value: "warn" },
110
+ { label: "Danger", value: "danger" }
111
+ ]
112
+ },
113
+ { name: "title", label: "Title", type: "text", defaultValue: "Heads up" },
114
+ {
115
+ name: "body",
116
+ label: "Body",
117
+ type: "textarea",
118
+ defaultValue: "Add context that an operator should not miss when scanning the page."
119
+ }
120
+ ],
121
+ render: (props) => {
122
+ const variant = readCalloutVariant(props.variant);
123
+ const title = readString(props.title, "Heads up");
124
+ const body = readString(props.body, "");
125
+ return /* @__PURE__ */ jsxs("aside", { className: `np-docs-callout np-docs-callout--${variant}`, children: [
126
+ /* @__PURE__ */ jsx(CalloutIcon, { variant }),
127
+ /* @__PURE__ */ jsxs("div", { children: [
128
+ /* @__PURE__ */ jsx("div", { className: "np-docs-callout-title", children: title }),
129
+ body ? /* @__PURE__ */ jsx("p", { children: body }) : null
130
+ ] })
131
+ ] });
132
+ }
133
+ };
134
+ var docsCodePanelBlock = {
135
+ type: "docsCodePanel",
136
+ label: "Code panel",
137
+ description: "Dark code surface with filename, language pill, and copy button.",
138
+ icon: "Code2",
139
+ iconKind: "lucide",
140
+ summaryFields: ["filename", "language"],
141
+ category: "Content",
142
+ source: "theme",
143
+ keywords: ["code", "snippet", "syntax", "block"],
144
+ defaultProps: {
145
+ filename: "example.ts",
146
+ language: "ts",
147
+ source: 'export const greeting = "hello";'
148
+ },
149
+ propsSchema: [
150
+ {
151
+ name: "filename",
152
+ label: "Filename",
153
+ type: "text",
154
+ defaultValue: "example.ts"
155
+ },
156
+ {
157
+ name: "language",
158
+ label: "Language",
159
+ type: "text",
160
+ defaultValue: "ts"
161
+ },
162
+ {
163
+ name: "source",
164
+ label: "Source",
165
+ type: "textarea",
166
+ rows: 10,
167
+ defaultValue: 'export const greeting = "hello";'
168
+ }
169
+ ],
170
+ render: (props) => {
171
+ const filename = readString(props.filename, "");
172
+ const language = readString(props.language, "");
173
+ const source = readString(props.source, "");
174
+ return /* @__PURE__ */ jsxs("div", { className: "np-docs-code", children: [
175
+ /* @__PURE__ */ jsxs("div", { className: "np-docs-code-head", children: [
176
+ /* @__PURE__ */ jsxs("span", { className: "np-docs-code-file", children: [
177
+ filename ? /* @__PURE__ */ jsx("span", { children: filename }) : null,
178
+ language ? /* @__PURE__ */ jsx("span", { className: "np-docs-brand-version", children: language }) : null
179
+ ] }),
180
+ /* @__PURE__ */ jsx(CopyButton, { text: source })
181
+ ] }),
182
+ /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: source }) })
183
+ ] });
184
+ }
185
+ };
186
+ var docsShellCommandBlock = {
187
+ type: "docsShellCommand",
188
+ label: "Shell command",
189
+ description: "Inline terminal-style command snippet with a copy button.",
190
+ icon: "Terminal",
191
+ iconKind: "lucide",
192
+ summaryFields: ["command"],
193
+ category: "Content",
194
+ source: "theme",
195
+ keywords: ["terminal", "shell", "bash", "command", "cli"],
196
+ defaultProps: {
197
+ prompt: "$",
198
+ command: "pnpm install"
199
+ },
200
+ propsSchema: [
201
+ { name: "prompt", label: "Prompt", type: "text", defaultValue: "$" },
202
+ {
203
+ name: "command",
204
+ label: "Command",
205
+ type: "text",
206
+ defaultValue: "pnpm install"
207
+ }
208
+ ],
209
+ render: (props) => {
210
+ const prompt = readString(props.prompt, "$");
211
+ const command = readString(props.command, "");
212
+ return /* @__PURE__ */ jsxs("div", { className: "np-docs-cmdline", children: [
213
+ /* @__PURE__ */ jsx("span", { className: "np-docs-cmdline-prompt", children: prompt }),
214
+ /* @__PURE__ */ jsx("code", { className: "np-docs-cmdline-cmd", children: command }),
215
+ /* @__PURE__ */ jsx(CopyButton, { text: command, className: "np-docs-cmdline-copy" })
216
+ ] });
217
+ }
218
+ };
219
+ function readSteps(value) {
220
+ if (!Array.isArray(value)) return [];
221
+ return value.filter(isRecord).map((item) => ({
222
+ title: readString(item.title, "Step"),
223
+ body: readString(item.body, "")
224
+ }));
225
+ }
226
+ var docsStepsBlock = {
227
+ type: "docsSteps",
228
+ label: "Steps",
229
+ description: "Numbered checklist of ordered steps.",
230
+ icon: "ListOrdered",
231
+ iconKind: "lucide",
232
+ category: "Content",
233
+ source: "theme",
234
+ keywords: ["steps", "checklist", "ordered", "numbered", "tutorial"],
235
+ defaultProps: {
236
+ items: [
237
+ { title: "Install", body: "Add the package to your workspace." },
238
+ { title: "Configure", body: "Wire it into the bootstrap." },
239
+ { title: "Run", body: "Boot the dev server and verify." }
240
+ ]
241
+ },
242
+ propsSchema: [
243
+ {
244
+ name: "items",
245
+ label: "Steps",
246
+ type: "array",
247
+ defaultValue: [
248
+ { title: "Install", body: "Add the package to your workspace." },
249
+ { title: "Configure", body: "Wire it into the bootstrap." },
250
+ { title: "Run", body: "Boot the dev server and verify." }
251
+ ],
252
+ itemDefault: { title: "New step", body: "" },
253
+ itemSchema: [
254
+ { name: "title", label: "Title", type: "text", defaultValue: "New step" },
255
+ { name: "body", label: "Body", type: "textarea", defaultValue: "" }
256
+ ]
257
+ }
258
+ ],
259
+ render: (props) => {
260
+ const items = readSteps(props.items);
261
+ if (items.length === 0) return /* @__PURE__ */ jsx("ol", { className: "np-docs-steps" });
262
+ return /* @__PURE__ */ jsx("ol", { className: "np-docs-steps", children: items.map((step, index) => /* @__PURE__ */ jsxs("li", { children: [
263
+ /* @__PURE__ */ jsx("div", { className: "np-docs-step-title", children: step.title }),
264
+ step.body ? /* @__PURE__ */ jsx("p", { className: "np-docs-step-body", children: step.body }) : null
265
+ ] }, `step-${index.toString()}`)) });
266
+ }
267
+ };
268
+ var docsApiTableBlock = {
269
+ type: "docsApiTable",
270
+ label: "API table",
271
+ description: "Reference table with uppercase mono headers and a required pill.",
272
+ icon: "Table",
273
+ iconKind: "lucide",
274
+ category: "Content",
275
+ source: "theme",
276
+ keywords: ["api", "reference", "table", "schema", "props"],
277
+ defaultProps: {
278
+ columns: ["Name", "Type", "Description"],
279
+ rows: [
280
+ { cells: ["slug", "string", "Unique URL fragment."], required: true },
281
+ { cells: ["title", "string", "Document title."], required: true },
282
+ { cells: ["body", "RichText", "Lexical body content."], required: false }
283
+ ]
284
+ },
285
+ propsSchema: [
286
+ {
287
+ name: "columns",
288
+ label: "Columns",
289
+ type: "array",
290
+ defaultValue: ["Name", "Type", "Description"],
291
+ itemDefault: { value: "Column" },
292
+ itemSchema: [
293
+ { name: "value", label: "Header", type: "text", defaultValue: "Column" }
294
+ ]
295
+ },
296
+ {
297
+ name: "rows",
298
+ label: "Rows",
299
+ type: "array",
300
+ defaultValue: [
301
+ { cells: ["slug", "string", "Unique URL fragment."], required: true }
302
+ ],
303
+ itemDefault: { cells: [], required: false },
304
+ itemSchema: [
305
+ {
306
+ name: "cells",
307
+ label: "Cells",
308
+ type: "array",
309
+ defaultValue: [],
310
+ itemDefault: { value: "" },
311
+ itemSchema: [
312
+ { name: "value", label: "Cell", type: "text", defaultValue: "" }
313
+ ]
314
+ },
315
+ {
316
+ name: "required",
317
+ label: "Required",
318
+ type: "boolean",
319
+ defaultValue: false
320
+ }
321
+ ]
322
+ }
323
+ ],
324
+ render: (props) => {
325
+ const rawColumns = props.columns;
326
+ const columns = Array.isArray(rawColumns) ? rawColumns.map((entry) => {
327
+ if (typeof entry === "string") return entry;
328
+ if (isRecord(entry) && typeof entry.value === "string") return entry.value;
329
+ return "";
330
+ }) : [];
331
+ const rawRows = Array.isArray(props.rows) ? props.rows : [];
332
+ const rows = rawRows.filter(isRecord).map((row) => {
333
+ const cellsRaw = row.cells;
334
+ const cells = Array.isArray(cellsRaw) ? cellsRaw.map((entry) => {
335
+ if (typeof entry === "string") return entry;
336
+ if (isRecord(entry) && typeof entry.value === "string") return entry.value;
337
+ return "";
338
+ }) : [];
339
+ return { cells, required: row.required === true };
340
+ });
341
+ if (columns.length === 0 && rows.length === 0) {
342
+ return /* @__PURE__ */ jsx("table", { className: "np-docs-table" });
343
+ }
344
+ return /* @__PURE__ */ jsxs("table", { className: "np-docs-table", children: [
345
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((col, i) => /* @__PURE__ */ jsx("th", { children: col }, `col-${i.toString()}`)) }) }),
346
+ /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsx("tr", { children: row.cells.map((cell, j) => {
347
+ const isFirst = j === 0;
348
+ return /* @__PURE__ */ jsxs("td", { children: [
349
+ isFirst ? /* @__PURE__ */ jsx("code", { children: cell }) : cell,
350
+ isFirst && row.required ? /* @__PURE__ */ jsx("span", { className: "np-docs-table-required", children: "required" }) : null
351
+ ] }, `cell-${i.toString()}-${j.toString()}`);
352
+ }) }, `row-${i.toString()}`)) })
353
+ ] });
354
+ }
355
+ };
356
+ var docsBlocks = [
357
+ docsCalloutBlock,
358
+ docsCodePanelBlock,
359
+ docsShellCommandBlock,
360
+ docsStepsBlock,
361
+ docsApiTableBlock
362
+ ];
3
363
 
4
364
  // src/header.tsx
5
365
  import { NavMenu, getCachedSite } from "@nexpress/next";
@@ -17,6 +377,15 @@ var docsSettingsSchema = z.object({
17
377
  githubRepo: z.string().url().optional().describe(
18
378
  "Optional repository URL \u2014 when set, page templates render an 'Edit on GitHub' link in the prev/next bar."
19
379
  ),
380
+ githubBranch: z.string().default("main").describe(
381
+ "Branch the 'Edit on GitHub' link points at. Default 'main' \u2014 change to 'master' / 'docs' / whatever the repo uses."
382
+ ),
383
+ githubDocsPath: z.string().default("docs").describe(
384
+ "Path under the repo where doc source files live. Joined with the doc slug to build the edit link target."
385
+ ),
386
+ githubExtension: z.string().default(".md").describe(
387
+ "File extension appended to the slug when building the edit link (e.g. '.md', '.mdx'). Include the leading dot."
388
+ ),
20
389
  sidebarHeading: z.string().default("Documentation").describe("Heading shown above the hierarchical sidebar nav."),
21
390
  showTableOfContents: z.boolean().default(true).describe("Render the in-page TOC sidebar on doc pages."),
22
391
  searchPlaceholder: z.string().default("Search the docs\u2026").describe("Placeholder text for the search input in the masthead.")
@@ -31,7 +400,7 @@ async function resolveDocsSettings() {
31
400
  }
32
401
 
33
402
  // src/header.tsx
34
- import { jsx, jsxs } from "react/jsx-runtime";
403
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
35
404
  var FALLBACK_SITE_NAME = "NexPress";
36
405
  async function DocsHeader() {
37
406
  const [settings, site] = await Promise.all([
@@ -39,13 +408,13 @@ async function DocsHeader() {
39
408
  getCachedSite()
40
409
  ]);
41
410
  const siteName = site?.name?.trim() || FALLBACK_SITE_NAME;
42
- return /* @__PURE__ */ jsx("header", { className: "np-docs-header", children: /* @__PURE__ */ jsxs("div", { className: "np-docs-header-inner", children: [
43
- /* @__PURE__ */ jsxs("a", { href: "/", className: "np-docs-brand", children: [
44
- /* @__PURE__ */ jsx("span", { className: "np-docs-brand-mark", "aria-hidden": "true" }),
45
- /* @__PURE__ */ jsx("span", { className: "np-docs-brand-name", children: siteName }),
46
- /* @__PURE__ */ jsx("span", { className: "np-docs-brand-version", children: settings.version })
411
+ return /* @__PURE__ */ jsx2("header", { className: "np-docs-header", children: /* @__PURE__ */ jsxs2("div", { className: "np-docs-header-inner", children: [
412
+ /* @__PURE__ */ jsxs2("a", { href: "/", className: "np-docs-brand", children: [
413
+ /* @__PURE__ */ jsx2("span", { className: "np-docs-brand-mark", "aria-hidden": "true" }),
414
+ /* @__PURE__ */ jsx2("span", { className: "np-docs-brand-name", children: siteName }),
415
+ /* @__PURE__ */ jsx2("span", { className: "np-docs-brand-version", children: settings.version })
47
416
  ] }),
48
- /* @__PURE__ */ jsxs(
417
+ /* @__PURE__ */ jsxs2(
49
418
  "form",
50
419
  {
51
420
  action: "/docs/search",
@@ -53,7 +422,7 @@ async function DocsHeader() {
53
422
  className: "np-docs-search-form",
54
423
  role: "search",
55
424
  children: [
56
- /* @__PURE__ */ jsxs(
425
+ /* @__PURE__ */ jsxs2(
57
426
  "svg",
58
427
  {
59
428
  width: "14",
@@ -64,13 +433,13 @@ async function DocsHeader() {
64
433
  strokeWidth: "2",
65
434
  "aria-hidden": "true",
66
435
  children: [
67
- /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
68
- /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
436
+ /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "11", r: "7" }),
437
+ /* @__PURE__ */ jsx2("path", { d: "m21 21-4.3-4.3" })
69
438
  ]
70
439
  }
71
440
  ),
72
- /* @__PURE__ */ jsx("label", { className: "sr-only", htmlFor: "np-docs-search-input", children: "Search the docs" }),
73
- /* @__PURE__ */ jsx(
441
+ /* @__PURE__ */ jsx2("label", { className: "sr-only", htmlFor: "np-docs-search-input", children: "Search the docs" }),
442
+ /* @__PURE__ */ jsx2(
74
443
  "input",
75
444
  {
76
445
  id: "np-docs-search-input",
@@ -80,14 +449,14 @@ async function DocsHeader() {
80
449
  className: "np-docs-search-input"
81
450
  }
82
451
  ),
83
- /* @__PURE__ */ jsx("kbd", { className: "np-docs-search-kbd", children: "\u2318K" })
452
+ /* @__PURE__ */ jsx2("kbd", { className: "np-docs-search-kbd", children: "\u2318K" })
84
453
  ]
85
454
  }
86
455
  ),
87
- /* @__PURE__ */ jsx(SearchKeyboardShortcut, { targetId: "np-docs-search-input" }),
88
- /* @__PURE__ */ jsxs("nav", { className: "np-docs-nav", "aria-label": "Primary", children: [
89
- /* @__PURE__ */ jsx(NavMenu, { location: "primary", className: "np-docs-primary-nav" }),
90
- settings.githubRepo ? /* @__PURE__ */ jsxs(
456
+ /* @__PURE__ */ jsx2(SearchKeyboardShortcut, { targetId: "np-docs-search-input" }),
457
+ /* @__PURE__ */ jsxs2("nav", { className: "np-docs-nav", "aria-label": "Primary", children: [
458
+ /* @__PURE__ */ jsx2(NavMenu, { location: "header", className: "np-docs-primary-nav" }),
459
+ settings.githubRepo ? /* @__PURE__ */ jsxs2(
91
460
  "a",
92
461
  {
93
462
  href: settings.githubRepo,
@@ -96,7 +465,7 @@ async function DocsHeader() {
96
465
  rel: "noreferrer",
97
466
  "aria-label": "GitHub repository",
98
467
  children: [
99
- /* @__PURE__ */ jsx(
468
+ /* @__PURE__ */ jsx2(
100
469
  "svg",
101
470
  {
102
471
  width: "14",
@@ -104,7 +473,7 @@ async function DocsHeader() {
104
473
  viewBox: "0 0 24 24",
105
474
  fill: "currentColor",
106
475
  "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" })
476
+ children: /* @__PURE__ */ jsx2("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
477
  }
109
478
  ),
110
479
  "GitHub"
@@ -116,9 +485,9 @@ async function DocsHeader() {
116
485
  }
117
486
 
118
487
  // src/members-not-found.tsx
119
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
488
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
120
489
  function DocsMembersNotFound() {
121
- return /* @__PURE__ */ jsxs2(
490
+ return /* @__PURE__ */ jsxs3(
122
491
  "div",
123
492
  {
124
493
  className: "np-docs-members-not-found",
@@ -128,7 +497,7 @@ function DocsMembersNotFound() {
128
497
  padding: "0 1.5rem"
129
498
  },
130
499
  children: [
131
- /* @__PURE__ */ jsx2(
500
+ /* @__PURE__ */ jsx3(
132
501
  "p",
133
502
  {
134
503
  style: {
@@ -142,7 +511,7 @@ function DocsMembersNotFound() {
142
511
  children: "404 \xB7 account"
143
512
  }
144
513
  ),
145
- /* @__PURE__ */ jsx2(
514
+ /* @__PURE__ */ jsx3(
146
515
  "h1",
147
516
  {
148
517
  style: {
@@ -155,7 +524,7 @@ function DocsMembersNotFound() {
155
524
  children: "That account link is no longer valid."
156
525
  }
157
526
  ),
158
- /* @__PURE__ */ jsx2(
527
+ /* @__PURE__ */ jsx3(
159
528
  "p",
160
529
  {
161
530
  style: {
@@ -167,7 +536,7 @@ function DocsMembersNotFound() {
167
536
  children: "Verification and password-reset links are single-use and expire after a short window. Open the sign-in page and request a fresh one."
168
537
  }
169
538
  ),
170
- /* @__PURE__ */ jsx2("p", { style: { margin: "1.75rem 0 0" }, children: /* @__PURE__ */ jsx2(
539
+ /* @__PURE__ */ jsx3("p", { style: { margin: "1.75rem 0 0" }, children: /* @__PURE__ */ jsx3(
171
540
  "a",
172
541
  {
173
542
  href: "/members/login",
@@ -190,18 +559,18 @@ function DocsMembersNotFound() {
190
559
  }
191
560
 
192
561
  // src/members-shell.tsx
193
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
562
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
194
563
  function DocsMembersShell({ children }) {
195
- return /* @__PURE__ */ jsxs3("div", { className: "np-docs np-docs-shell", children: [
196
- /* @__PURE__ */ jsx3(DocsHeader, {}),
197
- /* @__PURE__ */ jsx3("div", { className: "np-docs-members", children: /* @__PURE__ */ jsx3("div", { className: "np-docs-members-column", children }) })
564
+ return /* @__PURE__ */ jsxs4("div", { className: "np-docs np-docs-shell", children: [
565
+ /* @__PURE__ */ jsx4(DocsHeader, {}),
566
+ /* @__PURE__ */ jsx4("div", { className: "np-docs-members", children: /* @__PURE__ */ jsx4("div", { className: "np-docs-members-column", children }) })
198
567
  ] });
199
568
  }
200
569
 
201
570
  // src/not-found.tsx
202
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
571
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
203
572
  function DocsNotFound() {
204
- return /* @__PURE__ */ jsxs4(
573
+ return /* @__PURE__ */ jsxs5(
205
574
  "div",
206
575
  {
207
576
  className: "np-docs-not-found",
@@ -211,7 +580,7 @@ function DocsNotFound() {
211
580
  padding: "0 1.5rem"
212
581
  },
213
582
  children: [
214
- /* @__PURE__ */ jsx4(
583
+ /* @__PURE__ */ jsx5(
215
584
  "p",
216
585
  {
217
586
  style: {
@@ -224,8 +593,8 @@ function DocsNotFound() {
224
593
  children: "404 \u2014 Not found"
225
594
  }
226
595
  ),
227
- /* @__PURE__ */ jsx4("h1", { style: { margin: "0.75rem 0 0.5rem", fontSize: "1.75rem" }, children: "That page isn't in the docs." }),
228
- /* @__PURE__ */ jsx4(
596
+ /* @__PURE__ */ jsx5("h1", { style: { margin: "0.75rem 0 0.5rem", fontSize: "1.75rem" }, children: "That page isn't in the docs." }),
597
+ /* @__PURE__ */ jsx5(
229
598
  "p",
230
599
  {
231
600
  style: {
@@ -235,7 +604,7 @@ function DocsNotFound() {
235
604
  children: "It may have been renamed or merged into another section. Try the search bar in the header, or head to the homepage."
236
605
  }
237
606
  ),
238
- /* @__PURE__ */ jsx4(
607
+ /* @__PURE__ */ jsx5(
239
608
  "a",
240
609
  {
241
610
  href: "/",
@@ -269,7 +638,7 @@ import { extractHeadingToc, renderRichText } from "@nexpress/editor/server";
269
638
  import { TocScrollspy } from "./components/toc-scrollspy.js";
270
639
 
271
640
  // src/templates/doc-page.tsx
272
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
641
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
273
642
  async function DocPageTemplate({
274
643
  doc: rawDoc
275
644
  }) {
@@ -279,92 +648,103 @@ async function DocPageTemplate({
279
648
  const navInfo = await loadPrevNext(doc);
280
649
  const updatedLabel = formatUpdated(doc.updatedAt ?? doc.publishedAt);
281
650
  const readingLabel = readingMinutesLabel(doc.readingTime);
282
- const editHref = settings.githubRepo ? `${settings.githubRepo}/edit/main/docs/${doc.slug}.md` : null;
651
+ const editHref = settings.githubRepo ? `${settings.githubRepo}/edit/${settings.githubBranch}/${settings.githubDocsPath}/${doc.slug}${settings.githubExtension}` : null;
283
652
  const toc = extractHeadingToc(doc.body);
284
653
  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) => {
654
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
655
+ /* @__PURE__ */ jsxs6("article", { className: "np-docs-page", children: [
656
+ /* @__PURE__ */ jsx6("nav", { className: "np-docs-breadcrumbs", "aria-label": "Breadcrumb", children: breadcrumbs.map((crumb, index) => {
288
657
  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 })
658
+ return /* @__PURE__ */ jsxs6(React.Fragment, { children: [
659
+ index > 0 ? /* @__PURE__ */ jsx6("span", { className: "np-docs-breadcrumbs-sep", "aria-hidden": "true", children: "/" }) : null,
660
+ isLast || !crumb.slug ? /* @__PURE__ */ jsx6("span", { children: crumb.title }) : /* @__PURE__ */ jsx6("a", { href: `/docs/${crumb.slug}`, children: crumb.title })
292
661
  ] }, `crumb-${index.toString()}-${crumb.slug ?? "root"}`);
293
662
  }) }),
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: [
663
+ /* @__PURE__ */ jsx6("h1", { children: doc.title }),
664
+ doc.lede ? /* @__PURE__ */ jsx6("p", { className: "np-docs-page-lede", children: doc.lede }) : null,
665
+ doc.stableSince || readingLabel || updatedLabel || editHref ? /* @__PURE__ */ jsxs6("div", { className: "np-docs-page-meta", children: [
666
+ doc.stableSince ? /* @__PURE__ */ jsxs6("span", { className: "np-docs-page-meta-pill status", children: [
298
667
  "Stable since ",
299
668
  doc.stableSince
300
669
  ] }) : 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: [
670
+ readingLabel ? /* @__PURE__ */ jsx6("span", { className: "np-docs-page-meta-pill", children: readingLabel }) : null,
671
+ updatedLabel ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
672
+ /* @__PURE__ */ jsx6("span", { className: "np-docs-page-meta-sep", "aria-hidden": "true", children: "\xB7" }),
673
+ /* @__PURE__ */ jsxs6("span", { children: [
305
674
  "Updated ",
306
675
  updatedLabel
307
676
  ] })
308
677
  ] }) : null,
309
- editHref ? /* @__PURE__ */ jsx5("a", { href: editHref, target: "_blank", rel: "noreferrer", children: "Edit this page \u2192" }) : null
678
+ editHref ? /* @__PURE__ */ jsx6("a", { href: editHref, target: "_blank", rel: "noreferrer", children: "Edit this page \u2192" }) : null
310
679
  ] }) : null,
311
- /* @__PURE__ */ jsx5("div", { className: "np-docs-page-body", children: doc.body ? (
680
+ /* @__PURE__ */ jsx6("div", { className: "np-docs-page-body", children: doc.body ? (
312
681
  // Core types `NpRichTextContent` as the opaque
313
682
  // `Record<string, unknown>`; the editor's renderer
314
683
  // refines it to `{ root: ... }`. Structural cast at
315
684
  // the boundary — both sides go through the same
316
685
  // 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." })
686
+ renderRichText(
687
+ doc.body,
688
+ { headingAnchors: true }
689
+ )
690
+ ) : /* @__PURE__ */ jsx6("p", { style: { color: "var(--np-color-muted-foreground)" }, children: "No body content yet." }) }),
691
+ /* @__PURE__ */ jsxs6("div", { className: "np-docs-feedback", children: [
692
+ /* @__PURE__ */ jsxs6("div", { children: [
693
+ /* @__PURE__ */ jsx6("div", { className: "np-docs-feedback-title", children: "Was this page helpful?" }),
694
+ /* @__PURE__ */ jsx6("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
695
  ] }),
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" })
696
+ /* @__PURE__ */ jsxs6("div", { className: "np-docs-feedback-buttons", children: [
697
+ /* @__PURE__ */ jsx6("button", { type: "button", children: "Yes" }),
698
+ /* @__PURE__ */ jsx6("button", { type: "button", children: "Could be better" })
327
699
  ] })
328
700
  ] }),
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
- ] })
701
+ navInfo.prev || navInfo.next ? /* @__PURE__ */ jsxs6(
702
+ "nav",
703
+ {
704
+ className: "np-docs-prev-next",
705
+ "aria-label": "Pagination",
706
+ "data-single": navInfo.prev && !navInfo.next ? "prev" : navInfo.next && !navInfo.prev ? "next" : void 0,
707
+ children: [
708
+ navInfo.prev ? /* @__PURE__ */ jsxs6(
709
+ "a",
710
+ {
711
+ href: `/docs/${navInfo.prev.slug}`,
712
+ className: "np-docs-prev-next-prev",
713
+ children: [
714
+ /* @__PURE__ */ jsx6("div", { className: "np-docs-prev-next-dir", children: "\u2190 Previous" }),
715
+ /* @__PURE__ */ jsx6("div", { className: "np-docs-prev-next-title", children: navInfo.prev.title })
716
+ ]
717
+ }
718
+ ) : null,
719
+ navInfo.next ? /* @__PURE__ */ jsxs6(
720
+ "a",
721
+ {
722
+ href: `/docs/${navInfo.next.slug}`,
723
+ className: "np-docs-prev-next-next",
724
+ children: [
725
+ /* @__PURE__ */ jsx6("div", { className: "np-docs-prev-next-dir", children: "Next \u2192" }),
726
+ /* @__PURE__ */ jsx6("div", { className: "np-docs-prev-next-title", children: navInfo.next.title })
727
+ ]
728
+ }
729
+ ) : null
730
+ ]
731
+ }
732
+ ) : null
353
733
  ] }),
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(
734
+ toc.length > 0 ? /* @__PURE__ */ jsxs6("aside", { className: "np-docs-toc", "aria-label": "On this page", children: [
735
+ /* @__PURE__ */ jsx6("p", { className: "np-docs-toc-eyebrow", children: "On this page" }),
736
+ /* @__PURE__ */ jsx6("ul", { children: toc.map((entry) => /* @__PURE__ */ jsx6(
357
737
  "li",
358
738
  {
359
- style: entry.level === 3 ? { marginLeft: "0.85rem" } : void 0,
360
- children: /* @__PURE__ */ jsx5("a", { href: `#${entry.id}`, children: entry.text })
739
+ className: entry.level === 3 ? "np-docs-toc-l3" : void 0,
740
+ children: /* @__PURE__ */ jsx6("a", { href: `#${entry.id}`, children: entry.text })
361
741
  },
362
742
  `toc-${entry.id}`
363
743
  )) }),
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(
744
+ /* @__PURE__ */ jsx6(TocScrollspy, { ids: toc.map((entry) => entry.id) }),
745
+ editHref || reportIssueHref ? /* @__PURE__ */ jsxs6("div", { className: "np-docs-toc-secondary", children: [
746
+ editHref ? /* @__PURE__ */ jsxs6("a", { href: editHref, target: "_blank", rel: "noreferrer", children: [
747
+ /* @__PURE__ */ jsxs6(
368
748
  "svg",
369
749
  {
370
750
  width: "12",
@@ -375,15 +755,15 @@ async function DocPageTemplate({
375
755
  strokeWidth: "2",
376
756
  "aria-hidden": "true",
377
757
  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" })
758
+ /* @__PURE__ */ jsx6("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
759
+ /* @__PURE__ */ jsx6("path", { d: "m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z" })
380
760
  ]
381
761
  }
382
762
  ),
383
763
  "Edit on GitHub"
384
764
  ] }) : null,
385
- reportIssueHref ? /* @__PURE__ */ jsxs5("a", { href: reportIssueHref, target: "_blank", rel: "noreferrer", children: [
386
- /* @__PURE__ */ jsxs5(
765
+ reportIssueHref ? /* @__PURE__ */ jsxs6("a", { href: reportIssueHref, target: "_blank", rel: "noreferrer", children: [
766
+ /* @__PURE__ */ jsxs6(
387
767
  "svg",
388
768
  {
389
769
  width: "12",
@@ -394,9 +774,9 @@ async function DocPageTemplate({
394
774
  strokeWidth: "2",
395
775
  "aria-hidden": "true",
396
776
  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" })
777
+ /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
778
+ /* @__PURE__ */ jsx6("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
779
+ /* @__PURE__ */ jsx6("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
400
780
  ]
401
781
  }
402
782
  ),
@@ -466,7 +846,7 @@ function readingMinutesLabel(value) {
466
846
  }
467
847
 
468
848
  // src/routes/doc-detail.tsx
469
- import { jsx as jsx6 } from "react/jsx-runtime";
849
+ import { jsx as jsx7 } from "react/jsx-runtime";
470
850
  async function DocsDetailRoute({
471
851
  params,
472
852
  blockCtx
@@ -483,12 +863,12 @@ async function DocsDetailRoute({
483
863
  doc,
484
864
  blockCtx
485
865
  };
486
- return /* @__PURE__ */ jsx6(DocPageTemplate, { ...templateProps });
866
+ return /* @__PURE__ */ jsx7(DocPageTemplate, { ...templateProps });
487
867
  }
488
868
 
489
869
  // src/search.tsx
490
870
  import { getCollectionConfig, searchCollections } from "@nexpress/core";
491
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
871
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
492
872
  function resolveResultUrl(collection, doc) {
493
873
  try {
494
874
  const config = getCollectionConfig(collection);
@@ -508,62 +888,33 @@ async function DocsSearch({
508
888
  const raw = searchParams.q;
509
889
  const query = typeof raw === "string" ? raw.trim() : "";
510
890
  if (query.length === 0) {
511
- return /* @__PURE__ */ jsxs6("div", { className: "np-docs-search", children: [
512
- /* @__PURE__ */ jsx7("h1", { children: "Search" }),
513
- /* @__PURE__ */ jsx7("p", { style: { color: "var(--np-color-muted-foreground)" }, children: "Enter a query in the masthead search box to find pages." })
891
+ return /* @__PURE__ */ jsxs7("div", { className: "np-docs-search", children: [
892
+ /* @__PURE__ */ jsx8("p", { className: "np-docs-search-heading", children: "Search" }),
893
+ /* @__PURE__ */ jsx8("h1", { children: "Search the docs" }),
894
+ /* @__PURE__ */ jsx8("p", { className: "np-docs-search-empty", children: "Enter a query in the masthead search box to find pages." })
514
895
  ] });
515
896
  }
516
897
  const result = await searchCollections({ q: query, limit: 20 });
517
- return /* @__PURE__ */ jsxs6("div", { className: "np-docs-search", children: [
518
- /* @__PURE__ */ jsxs6("h1", { children: [
519
- "Search results for \u201C",
898
+ return /* @__PURE__ */ jsxs7("div", { className: "np-docs-search", children: [
899
+ /* @__PURE__ */ jsx8("p", { className: "np-docs-search-heading", children: "Search results for" }),
900
+ /* @__PURE__ */ jsxs7("h1", { children: [
901
+ "\u201C",
520
902
  query,
521
903
  "\u201D"
522
904
  ] }),
523
- result.results.length === 0 ? /* @__PURE__ */ jsx7("p", { style: { color: "var(--np-color-muted-foreground)" }, children: "No matches." }) : /* @__PURE__ */ jsx7("ul", { style: { listStyle: "none", padding: 0, margin: "1.5rem 0 0" }, children: result.results.map((item, i) => {
905
+ result.results.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "np-docs-search-empty", children: "No matches." }) : /* @__PURE__ */ jsx8("ul", { className: "np-docs-search-results", children: result.results.map((item, i) => {
524
906
  const doc = item.doc;
525
907
  const slug = typeof doc.slug === "string" ? doc.slug : null;
526
908
  const title = typeof doc.title === "string" ? doc.title : slug ?? "Untitled";
527
909
  const url = resolveResultUrl(item.collection, doc);
528
- return /* @__PURE__ */ jsxs6(
910
+ return /* @__PURE__ */ jsxs7(
529
911
  "li",
530
912
  {
531
- style: {
532
- padding: "1rem 0",
533
- borderBottom: "1px solid var(--np-color-border)"
534
- },
913
+ className: "np-docs-search-result",
535
914
  children: [
536
- /* @__PURE__ */ jsx7(
537
- "p",
538
- {
539
- style: {
540
- margin: 0,
541
- fontSize: "0.75rem",
542
- textTransform: "uppercase",
543
- letterSpacing: "0.08em",
544
- color: "var(--np-color-muted-foreground)"
545
- },
546
- children: item.collection
547
- }
548
- ),
549
- /* @__PURE__ */ jsx7("h2", { style: { margin: "0.25rem 0 0.5rem", fontSize: "1.125rem" }, children: /* @__PURE__ */ jsx7(
550
- "a",
551
- {
552
- href: url,
553
- style: { color: "inherit", textDecoration: "none" },
554
- children: title
555
- }
556
- ) }),
557
- typeof doc.excerpt === "string" ? /* @__PURE__ */ jsx7(
558
- "p",
559
- {
560
- style: {
561
- margin: 0,
562
- color: "var(--np-color-muted-foreground)"
563
- },
564
- children: doc.excerpt
565
- }
566
- ) : null
915
+ /* @__PURE__ */ jsx8("p", { className: "np-docs-search-result-eyebrow", children: item.collection }),
916
+ /* @__PURE__ */ jsx8("h2", { children: /* @__PURE__ */ jsx8("a", { href: url, children: title }) }),
917
+ typeof doc.excerpt === "string" ? /* @__PURE__ */ jsx8("p", { className: "np-docs-search-result-excerpt", children: doc.excerpt }) : null
567
918
  ]
568
919
  },
569
920
  `${item.collection}:${doc.id ?? i}`
@@ -573,14 +924,52 @@ async function DocsSearch({
573
924
  }
574
925
 
575
926
  // src/shell.tsx
576
- import { jsx as jsx8 } from "react/jsx-runtime";
577
- function DocsShell({ children }) {
578
- return /* @__PURE__ */ jsx8("div", { className: "np-docs-shell", children: /* @__PURE__ */ jsx8("div", { className: "np-docs-grid", children }) });
927
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
928
+ var TOKEN_CSS = `:root{--np-color-success:#047857;--np-color-warning:#b45309;--np-color-danger:#b91c1c;--np-color-success-soft:#ecfdf5;--np-color-warning-soft:#fffbeb;--np-color-danger-soft:#fef2f2;--np-color-code-bg:#0b1220;--np-color-code-fg:#e6edf6;--np-color-code-head:#1e2939;--np-color-code-border:#0f1a2b;}`;
929
+ async function DocsShell({
930
+ children
931
+ }) {
932
+ let layout = "docs";
933
+ try {
934
+ const { headers } = await import("next/headers");
935
+ const pathname = (await headers()).get("x-np-pathname") ?? "";
936
+ if (!pathname.startsWith("/docs")) layout = "page";
937
+ } catch {
938
+ }
939
+ return /* @__PURE__ */ jsxs8("div", { className: "np-docs-shell", "data-layout": layout, children: [
940
+ /* @__PURE__ */ jsx9("style", { dangerouslySetInnerHTML: { __html: TOKEN_CSS } }),
941
+ /* @__PURE__ */ jsx9("div", { className: "np-docs-grid", children })
942
+ ] });
579
943
  }
580
944
 
581
945
  // src/sidebar.tsx
582
946
  import { findDocuments as findDocuments3 } from "@nexpress/core";
583
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
947
+
948
+ // src/lib/doc-tree.ts
949
+ function buildDocTree(rows, toNode) {
950
+ const byId = /* @__PURE__ */ new Map();
951
+ for (const row of rows) {
952
+ const node = toNode(row);
953
+ if (node) byId.set(node.id, node);
954
+ }
955
+ const roots = [];
956
+ for (const node of byId.values()) {
957
+ if (node.parent && byId.has(node.parent)) {
958
+ byId.get(node.parent).children.push(node);
959
+ } else {
960
+ roots.push(node);
961
+ }
962
+ }
963
+ const sortLevel = (list) => {
964
+ list.sort((a, b) => a.order - b.order);
965
+ for (const n of list) sortLevel(n.children);
966
+ };
967
+ sortLevel(roots);
968
+ return roots;
969
+ }
970
+
971
+ // src/sidebar.tsx
972
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
584
973
  async function DocsSidebar() {
585
974
  const settings = await resolveDocsSettings();
586
975
  const currentSlug = await currentPathSlug();
@@ -591,12 +980,12 @@ async function DocsSidebar() {
591
980
  });
592
981
  const tree = buildTree(result.docs);
593
982
  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" }),
983
+ return /* @__PURE__ */ jsx10("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: /* @__PURE__ */ jsxs9("div", { className: "np-docs-sidebar-group", children: [
984
+ /* @__PURE__ */ jsxs9("h2", { className: "np-docs-sidebar-eyebrow", children: [
985
+ /* @__PURE__ */ jsx10("span", { className: "np-docs-sidebar-eyebrow-dot", "aria-hidden": "true" }),
597
986
  settings.sidebarHeading
598
987
  ] }),
599
- /* @__PURE__ */ jsx9(
988
+ /* @__PURE__ */ jsx10(
600
989
  "p",
601
990
  {
602
991
  style: {
@@ -610,12 +999,12 @@ async function DocsSidebar() {
610
999
  )
611
1000
  ] }) });
612
1001
  }
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" }),
1002
+ return /* @__PURE__ */ jsx10("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: tree.map((group) => /* @__PURE__ */ jsxs9("div", { className: "np-docs-sidebar-group", children: [
1003
+ /* @__PURE__ */ jsxs9("h2", { className: "np-docs-sidebar-eyebrow", children: [
1004
+ /* @__PURE__ */ jsx10("span", { className: "np-docs-sidebar-eyebrow-dot", "aria-hidden": "true" }),
616
1005
  group.title
617
1006
  ] }),
618
- group.children.length > 0 ? /* @__PURE__ */ jsx9(NavTree, { nodes: group.children, currentSlug }) : /* @__PURE__ */ jsx9("ul", { children: /* @__PURE__ */ jsx9(SidebarLink, { node: group, currentSlug }) })
1007
+ group.children.length > 0 ? /* @__PURE__ */ jsx10(NavTree, { nodes: group.children, currentSlug }) : /* @__PURE__ */ jsx10("ul", { children: /* @__PURE__ */ jsx10(SidebarLink, { node: group, currentSlug }) })
619
1008
  ] }, group.id)) });
620
1009
  }
621
1010
  async function currentPathSlug() {
@@ -632,11 +1021,10 @@ async function currentPathSlug() {
632
1021
  }
633
1022
  function buildTree(rawDocs) {
634
1023
  const docs = rawDocs;
635
- const byId = /* @__PURE__ */ new Map();
636
- for (const d of docs) {
637
- if (typeof d.id !== "string") continue;
638
- if (typeof d.slug !== "string") continue;
639
- byId.set(d.id, {
1024
+ return buildDocTree(docs, (d) => {
1025
+ if (typeof d.id !== "string") return null;
1026
+ if (typeof d.slug !== "string") return null;
1027
+ return {
640
1028
  id: d.id,
641
1029
  slug: d.slug,
642
1030
  title: typeof d.title === "string" ? d.title : d.slug,
@@ -644,30 +1032,16 @@ function buildTree(rawDocs) {
644
1032
  order: typeof d.order === "number" ? d.order : 0,
645
1033
  badge: typeof d.badge === "string" ? d.badge : null,
646
1034
  children: []
647
- });
648
- }
649
- const roots = [];
650
- for (const node of byId.values()) {
651
- if (node.parent && byId.has(node.parent)) {
652
- byId.get(node.parent).children.push(node);
653
- } else {
654
- roots.push(node);
655
- }
656
- }
657
- const sortRec = (list) => {
658
- list.sort((a, b) => a.order - b.order);
659
- for (const n of list) sortRec(n.children);
660
- };
661
- sortRec(roots);
662
- return roots;
1035
+ };
1036
+ });
663
1037
  }
664
1038
  function NavTree({
665
1039
  nodes,
666
1040
  currentSlug
667
1041
  }) {
668
- return /* @__PURE__ */ jsx9("ul", { children: nodes.map((n) => /* @__PURE__ */ jsxs7("li", { children: [
669
- /* @__PURE__ */ jsx9(SidebarLink, { node: n, currentSlug }),
670
- n.children.length > 0 ? /* @__PURE__ */ jsx9(NavTree, { nodes: n.children, currentSlug }) : null
1042
+ return /* @__PURE__ */ jsx10("ul", { children: nodes.map((n) => /* @__PURE__ */ jsxs9("li", { children: [
1043
+ /* @__PURE__ */ jsx10(SidebarLink, { node: n, currentSlug }),
1044
+ n.children.length > 0 ? /* @__PURE__ */ jsx10(NavTree, { nodes: n.children, currentSlug }) : null
671
1045
  ] }, n.id)) });
672
1046
  }
673
1047
  function SidebarLink({
@@ -676,7 +1050,7 @@ function SidebarLink({
676
1050
  }) {
677
1051
  const isCurrent = currentSlug !== null && currentSlug === node.slug;
678
1052
  const badgeClass = node.badge ? `np-docs-sidebar-badge ${node.badge.toLowerCase()}` : null;
679
- return /* @__PURE__ */ jsxs7(
1053
+ return /* @__PURE__ */ jsxs9(
680
1054
  "a",
681
1055
  {
682
1056
  href: `/docs/${node.slug}`,
@@ -684,7 +1058,7 @@ function SidebarLink({
684
1058
  "aria-current": isCurrent ? "page" : void 0,
685
1059
  children: [
686
1060
  node.title,
687
- badgeClass ? /* @__PURE__ */ jsx9("span", { className: badgeClass, children: node.badge.toUpperCase() }) : null
1061
+ badgeClass ? /* @__PURE__ */ jsx10("span", { className: badgeClass, children: node.badge.toUpperCase() }) : null
688
1062
  ]
689
1063
  }
690
1064
  );
@@ -889,6 +1263,29 @@ var docsCss = `
889
1263
  .np-docs-sidebar { display: none; }
890
1264
  }
891
1265
 
1266
+ /* Non-docs routes (home / about / pricing / contact / member pages):
1267
+ * collapse the 3-col grid to a single wide column and hide the
1268
+ * doc-only chrome (sidebar + TOC) so a generic pages doc has full
1269
+ * canvas width instead of being squeezed into the 800-ish px
1270
+ * article column reserved for the /docs reading lane. */
1271
+ .np-docs-shell[data-layout="page"] .np-docs-grid {
1272
+ grid-template-columns: minmax(0, 1fr);
1273
+ }
1274
+ .np-docs-shell[data-layout="page"] .np-docs-sidebar,
1275
+ .np-docs-shell[data-layout="page"] .np-docs-toc {
1276
+ display: none;
1277
+ }
1278
+ /* Framework's globals.css caps .np-page at 48rem (~768px) so a
1279
+ * regular pages doc rendered through the catch-all's fallback
1280
+ * wrapper stays squeezed even after the grid collapse above.
1281
+ * Lift the cap inside the page-layout so block-level content
1282
+ * (hero, features, stats) can stretch to the docs container. */
1283
+ .np-docs-shell[data-layout="page"] .np-page {
1284
+ max-width: none;
1285
+ margin: 0;
1286
+ padding: 0;
1287
+ }
1288
+
892
1289
  /* ============================================================
893
1290
  * Sidebar \u2014 grouped link list with bullet eyebrow + badges.
894
1291
  * ============================================================ */
@@ -1031,16 +1428,16 @@ var docsCss = `
1031
1428
  background: var(--np-color-card);
1032
1429
  }
1033
1430
  .np-docs-page-meta-pill.status {
1034
- color: #047857;
1431
+ color: var(--np-color-success, #047857);
1035
1432
  border-color: #bbf7d0;
1036
- background: #f0fdf4;
1433
+ background: var(--np-color-success-soft, #f0fdf4);
1037
1434
  }
1038
1435
  .np-docs-page-meta-pill.status::before {
1039
1436
  content: "";
1040
1437
  width: 0.4rem;
1041
1438
  height: 0.4rem;
1042
1439
  border-radius: 50%;
1043
- background: #047857;
1440
+ background: var(--np-color-success, #047857);
1044
1441
  }
1045
1442
  .np-docs-page-meta-sep { opacity: 0.4; }
1046
1443
  .np-docs-page-meta a {
@@ -1141,12 +1538,12 @@ var docsCss = `
1141
1538
  color: var(--np-color-foreground);
1142
1539
  }
1143
1540
  .np-docs-callout--warn {
1144
- border-left-color: #b45309;
1145
- background: #fffbeb;
1541
+ border-left-color: var(--np-color-warning, #b45309);
1542
+ background: var(--np-color-warning-soft, #fffbeb);
1146
1543
  border-color: #fde68a;
1147
1544
  }
1148
1545
  .np-docs-callout--warn .np-docs-callout-icon,
1149
- .np-docs-callout--warn > svg { color: #b45309; }
1546
+ .np-docs-callout--warn > svg { color: var(--np-color-warning, #b45309); }
1150
1547
  .np-docs-callout--note {
1151
1548
  border-left-color: #6366f1;
1152
1549
  background: #eef2ff;
@@ -1155,12 +1552,12 @@ var docsCss = `
1155
1552
  .np-docs-callout--note .np-docs-callout-icon,
1156
1553
  .np-docs-callout--note > svg { color: #4338ca; }
1157
1554
  .np-docs-callout--danger {
1158
- border-left-color: #b91c1c;
1159
- background: #fef2f2;
1555
+ border-left-color: var(--np-color-danger, #b91c1c);
1556
+ background: var(--np-color-danger-soft, #fef2f2);
1160
1557
  border-color: #fecaca;
1161
1558
  }
1162
1559
  .np-docs-callout--danger .np-docs-callout-icon,
1163
- .np-docs-callout--danger > svg { color: #b91c1c; }
1560
+ .np-docs-callout--danger > svg { color: var(--np-color-danger, #b91c1c); }
1164
1561
 
1165
1562
  /* ============================================================
1166
1563
  * Code blocks \u2014 dark surface with a file-named header and a
@@ -1172,17 +1569,17 @@ var docsCss = `
1172
1569
  .np-docs-code {
1173
1570
  margin: 1.25rem 0;
1174
1571
  border-radius: 10px;
1175
- background: #0b1220;
1176
- color: #e6edf6;
1572
+ background: var(--np-color-code-bg, #0b1220);
1573
+ color: var(--np-color-code-fg, #e6edf6);
1177
1574
  overflow: hidden;
1178
- border: 1px solid #1e2939;
1575
+ border: 1px solid var(--np-color-code-head, #1e2939);
1179
1576
  }
1180
1577
  .np-docs-code-head {
1181
1578
  display: flex;
1182
1579
  align-items: center;
1183
1580
  justify-content: space-between;
1184
1581
  padding: 0.55rem 0.85rem;
1185
- background: #0f1a2b;
1582
+ background: var(--np-color-code-border, #0f1a2b);
1186
1583
  border-bottom: 1px solid #1e293b;
1187
1584
  }
1188
1585
  .np-docs-code-file {
@@ -1244,8 +1641,8 @@ var docsCss = `
1244
1641
  align-items: center;
1245
1642
  padding: 0.75rem 1rem;
1246
1643
  margin: 1.25rem 0;
1247
- background: #0b1220;
1248
- color: #e6edf6;
1644
+ background: var(--np-color-code-bg, #0b1220);
1645
+ color: var(--np-color-code-fg, #e6edf6);
1249
1646
  border-radius: 9px;
1250
1647
  font-family: var(--np-font-mono);
1251
1648
  font-size: 0.875rem;
@@ -1384,6 +1781,11 @@ var docsCss = `
1384
1781
  }
1385
1782
  .np-docs-prev-next a.np-docs-prev-next-next,
1386
1783
  .np-docs-prev-next a:last-child { text-align: right; }
1784
+ .np-docs-prev-next[data-single="prev"],
1785
+ .np-docs-prev-next[data-single="next"] { grid-template-columns: 1fr; }
1786
+ .np-docs-prev-next[data-single="prev"] a.np-docs-prev-next-prev,
1787
+ .np-docs-prev-next[data-single="next"] a.np-docs-prev-next-next { width: 100%; }
1788
+ .np-docs-prev-next[data-single="prev"] a.np-docs-prev-next-prev { text-align: left; }
1387
1789
 
1388
1790
  /* ============================================================
1389
1791
  * Feedback row \u2014 Yes / Could be better buttons under each page.
@@ -1473,6 +1875,7 @@ var docsCss = `
1473
1875
  );
1474
1876
  }
1475
1877
  .np-docs-toc ul ul { margin-left: 0.85rem; }
1878
+ .np-docs-toc-l3 { margin-left: 0.85rem; }
1476
1879
  .np-docs-toc-secondary {
1477
1880
  margin-top: 1.5rem;
1478
1881
  padding-top: 1rem;
@@ -1500,8 +1903,453 @@ var docsCss = `
1500
1903
  margin: 0 0 0.5rem;
1501
1904
  color: var(--np-color-foreground);
1502
1905
  }
1906
+
1907
+ /* ============================================================
1908
+ * Search route \u2014 wraps DocsSearch's output. Eyebrow + result
1909
+ * cards reuse the docs chrome (mono small caps, hairline rules,
1910
+ * bordered card with hover lift).
1911
+ * ============================================================ */
1912
+ .np-docs-search {
1913
+ max-width: 800px;
1914
+ margin: 0 auto;
1915
+ padding-top: 2.25rem;
1916
+ }
1917
+ .np-docs-search-heading {
1918
+ font-family: var(--np-font-mono);
1919
+ font-size: 0.72rem;
1920
+ text-transform: uppercase;
1921
+ letter-spacing: 0.08em;
1922
+ color: var(--np-color-muted-foreground);
1923
+ font-weight: 600;
1924
+ margin: 0 0 0.5rem;
1925
+ }
1926
+ .np-docs-search h1 {
1927
+ font-size: 1.75rem;
1928
+ font-weight: 700;
1929
+ letter-spacing: -0.02em;
1930
+ margin: 0 0 1.5rem;
1931
+ text-wrap: balance;
1932
+ }
1933
+ .np-docs-search-empty {
1934
+ color: var(--np-color-muted-foreground);
1935
+ padding: 1.5rem 0;
1936
+ font-size: 0.95rem;
1937
+ }
1938
+ .np-docs-search-results {
1939
+ list-style: none;
1940
+ padding: 0;
1941
+ margin: 1.5rem 0 0;
1942
+ display: grid;
1943
+ gap: 1rem;
1944
+ }
1945
+ .np-docs-search-result {
1946
+ padding: 1rem 1.15rem;
1947
+ border: 1px solid var(--np-color-border);
1948
+ border-radius: 10px;
1949
+ background: var(--np-color-card);
1950
+ transition: border-color 0.15s ease, transform 0.2s ease;
1951
+ }
1952
+ .np-docs-search-result:hover {
1953
+ border-color: var(--np-color-primary);
1954
+ transform: translateY(-1px);
1955
+ }
1956
+ .np-docs-search-result-eyebrow {
1957
+ font-family: var(--np-font-mono);
1958
+ font-size: 0.68rem;
1959
+ text-transform: uppercase;
1960
+ letter-spacing: 0.08em;
1961
+ color: var(--np-color-muted-foreground);
1962
+ margin: 0 0 0.35rem;
1963
+ }
1964
+ .np-docs-search-result h2 {
1965
+ font-size: 1.05rem;
1966
+ font-weight: 600;
1967
+ margin: 0 0 0.4rem;
1968
+ }
1969
+ .np-docs-search-result h2 a {
1970
+ color: var(--np-color-foreground);
1971
+ text-decoration: none;
1972
+ }
1973
+ .np-docs-search-result h2 a:hover { color: var(--np-color-primary); }
1974
+ .np-docs-search-result-excerpt {
1975
+ margin: 0;
1976
+ font-size: 0.875rem;
1977
+ color: var(--np-color-muted-foreground);
1978
+ line-height: 1.55;
1979
+ }
1980
+
1981
+ /* ============================================================
1982
+ * Front-page landing \u2014 eyebrow + display heading + lede +
1983
+ * primary CTA + 2x2 group cards + recently-updated row.
1984
+ * Renders inside the single-column page layout
1985
+ * (data-layout="page" on the shell collapses the 3-col grid).
1986
+ * ============================================================ */
1987
+ .np-docs-front {
1988
+ max-width: 880px;
1989
+ margin: 0 auto;
1990
+ padding: 2.5rem 0 4rem;
1991
+ display: grid;
1992
+ gap: 3rem;
1993
+ }
1994
+ .np-docs-front-hero {
1995
+ display: grid;
1996
+ gap: 1rem;
1997
+ }
1998
+ .np-docs-front-eyebrow {
1999
+ display: inline-flex;
2000
+ align-items: center;
2001
+ gap: 0.5rem;
2002
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2003
+ font-size: 0.72rem;
2004
+ letter-spacing: 0.16em;
2005
+ text-transform: uppercase;
2006
+ color: var(--np-color-primary);
2007
+ background: var(--np-color-primary-soft, color-mix(in oklab, var(--np-color-primary) 10%, transparent));
2008
+ padding: 0.32rem 0.6rem;
2009
+ border-radius: 999px;
2010
+ align-self: start;
2011
+ justify-self: start;
2012
+ }
2013
+ .np-docs-front-eyebrow-dot {
2014
+ width: 6px;
2015
+ height: 6px;
2016
+ border-radius: 999px;
2017
+ background: var(--np-color-success, currentColor);
2018
+ display: inline-block;
2019
+ }
2020
+ .np-docs-front h1 {
2021
+ font-size: clamp(2.4rem, 4.2vw, 3rem);
2022
+ font-weight: 700;
2023
+ letter-spacing: -0.03em;
2024
+ line-height: 1.05;
2025
+ margin: 0;
2026
+ text-wrap: balance;
2027
+ }
2028
+ .np-docs-front-lede {
2029
+ font-size: 1.125rem;
2030
+ line-height: 1.55;
2031
+ color: var(--np-color-muted-foreground);
2032
+ max-width: 60ch;
2033
+ margin: 0;
2034
+ }
2035
+ .np-docs-front-cta {
2036
+ display: flex;
2037
+ flex-wrap: wrap;
2038
+ gap: 0.65rem;
2039
+ margin-top: 0.5rem;
2040
+ }
2041
+ .np-docs-front-cta-primary {
2042
+ display: inline-flex;
2043
+ align-items: center;
2044
+ gap: 0.4rem;
2045
+ font-size: 0.92rem;
2046
+ font-weight: 500;
2047
+ padding: 0.55rem 1.05rem;
2048
+ border-radius: 999px;
2049
+ background: var(--np-color-foreground);
2050
+ color: var(--np-color-background);
2051
+ text-decoration: none;
2052
+ }
2053
+ .np-docs-front-cta-primary:hover {
2054
+ background: color-mix(in oklab, var(--np-color-foreground) 85%, transparent);
2055
+ }
2056
+ .np-docs-front-cta-secondary {
2057
+ display: inline-flex;
2058
+ align-items: center;
2059
+ font-size: 0.92rem;
2060
+ padding: 0.55rem 1.05rem;
2061
+ border-radius: 999px;
2062
+ color: var(--np-color-foreground);
2063
+ text-decoration: none;
2064
+ border: 1px solid var(--np-color-border);
2065
+ }
2066
+ .np-docs-front-cta-secondary:hover {
2067
+ background: var(--np-color-muted);
2068
+ }
2069
+
2070
+ .np-docs-front-groups {
2071
+ display: grid;
2072
+ grid-template-columns: repeat(2, 1fr);
2073
+ gap: 1rem;
2074
+ }
2075
+ @media (max-width: 720px) {
2076
+ .np-docs-front-groups { grid-template-columns: 1fr; }
2077
+ }
2078
+ .np-docs-front-group {
2079
+ display: grid;
2080
+ gap: 0.5rem;
2081
+ padding: 1.4rem 1.4rem 1.6rem;
2082
+ border-radius: var(--np-radius-lg, 10px);
2083
+ border: 1px solid var(--np-color-border);
2084
+ background: var(--np-color-card);
2085
+ text-decoration: none;
2086
+ color: inherit;
2087
+ transition: border-color 120ms ease, transform 120ms ease;
2088
+ }
2089
+ .np-docs-front-group:hover {
2090
+ border-color: var(--np-color-primary);
2091
+ transform: translateY(-1px);
2092
+ }
2093
+ .np-docs-front-group-title {
2094
+ display: flex;
2095
+ align-items: center;
2096
+ justify-content: space-between;
2097
+ font-size: 1.05rem;
2098
+ font-weight: 600;
2099
+ margin: 0;
2100
+ }
2101
+ .np-docs-front-group-count {
2102
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2103
+ font-size: 0.7rem;
2104
+ letter-spacing: 0.04em;
2105
+ color: var(--np-color-muted-foreground);
2106
+ font-weight: 400;
2107
+ }
2108
+ .np-docs-front-group-lede {
2109
+ margin: 0;
2110
+ font-size: 0.9rem;
2111
+ line-height: 1.5;
2112
+ color: var(--np-color-muted-foreground);
2113
+ }
2114
+ .np-docs-front-group-children {
2115
+ list-style: none;
2116
+ margin: 0.4rem 0 0;
2117
+ padding: 0;
2118
+ display: grid;
2119
+ gap: 0.25rem;
2120
+ }
2121
+ .np-docs-front-group-children li {
2122
+ font-size: 0.86rem;
2123
+ color: var(--np-color-muted-foreground);
2124
+ display: inline-flex;
2125
+ align-items: center;
2126
+ gap: 0.4rem;
2127
+ }
2128
+
2129
+ .np-docs-front-recent {
2130
+ display: grid;
2131
+ gap: 0.75rem;
2132
+ }
2133
+ .np-docs-front-recent-eyebrow {
2134
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2135
+ font-size: 0.7rem;
2136
+ letter-spacing: 0.18em;
2137
+ text-transform: uppercase;
2138
+ color: var(--np-color-muted-foreground);
2139
+ margin: 0;
2140
+ }
2141
+ .np-docs-front-recent-list {
2142
+ list-style: none;
2143
+ margin: 0;
2144
+ padding: 0;
2145
+ display: grid;
2146
+ gap: 0.5rem;
2147
+ }
2148
+ .np-docs-front-recent-list a {
2149
+ display: flex;
2150
+ align-items: baseline;
2151
+ justify-content: space-between;
2152
+ gap: 1rem;
2153
+ padding: 0.5rem 0;
2154
+ border-bottom: 1px solid var(--np-color-border);
2155
+ text-decoration: none;
2156
+ color: inherit;
2157
+ }
2158
+ .np-docs-front-recent-list li:last-child a {
2159
+ border-bottom: 0;
2160
+ }
2161
+ .np-docs-front-recent-list a:hover {
2162
+ color: var(--np-color-primary);
2163
+ }
2164
+ .np-docs-front-recent-title {
2165
+ font-size: 0.95rem;
2166
+ }
2167
+ .np-docs-front-recent-time {
2168
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2169
+ font-size: 0.75rem;
2170
+ color: var(--np-color-muted-foreground);
2171
+ flex-shrink: 0;
2172
+ }
2173
+
2174
+ /* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2175
+ * Members shell (DocsMembersShell \u2014 /members/* routes)
2176
+ *
2177
+ * Drops the docs sidebar \u2014 hierarchical navigation is useless
2178
+ * on auth forms. Reuses DocsHeader directly. Body becomes a
2179
+ * narrow centered column for the form / status content.
2180
+ * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2181
+ .np-docs-members {
2182
+ padding: 4rem 1.5rem;
2183
+ min-height: 60vh;
2184
+ }
2185
+ .np-docs-members-column {
2186
+ max-width: 32rem;
2187
+ margin: 0 auto;
2188
+ display: flex;
2189
+ flex-direction: column;
2190
+ gap: 1.5rem;
2191
+ }
1503
2192
  `;
1504
2193
 
2194
+ // src/templates/page-front.tsx
2195
+ import { findDocuments as findDocuments4 } from "@nexpress/core";
2196
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2197
+ async function PageFrontTemplate(_props) {
2198
+ const settings = await resolveDocsSettings();
2199
+ const result = await findDocuments4("posts", {
2200
+ where: { status: "published", kind: "doc" },
2201
+ sort: "order",
2202
+ limit: 200
2203
+ });
2204
+ const tree = buildTree2(result.docs);
2205
+ const recent = recentlyUpdated(result.docs, 4);
2206
+ const pluginsRoot = tree.find((node) => node.slug === "plugins");
2207
+ const quickstartChild = pluginsRoot?.children.find(
2208
+ (c) => c.slug === "author-quickstart"
2209
+ );
2210
+ const quickstartTarget = quickstartChild ?? tree[0]?.children[0] ?? tree[0] ?? null;
2211
+ return /* @__PURE__ */ jsxs10("article", { className: "np-docs-front", children: [
2212
+ /* @__PURE__ */ jsxs10("header", { className: "np-docs-front-hero", children: [
2213
+ /* @__PURE__ */ jsxs10("span", { className: "np-docs-front-eyebrow", children: [
2214
+ /* @__PURE__ */ jsx11("span", { className: "np-docs-front-eyebrow-dot", "aria-hidden": "true" }),
2215
+ settings.version,
2216
+ /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", children: " \xB7 Stable" })
2217
+ ] }),
2218
+ /* @__PURE__ */ jsx11("h1", { children: "Documentation" }),
2219
+ /* @__PURE__ */ jsx11("p", { className: "np-docs-front-lede", children: "Everything you need to install, configure, extend, and ship a NexPress site \u2014 from a first install to the API reference. Browse by section, or jump straight into the plugin author quickstart." }),
2220
+ quickstartTarget ? /* @__PURE__ */ jsxs10("div", { className: "np-docs-front-cta", children: [
2221
+ /* @__PURE__ */ jsxs10("a", { className: "np-docs-front-cta-primary", href: `/docs/${quickstartTarget.slug}`, children: [
2222
+ "Open ",
2223
+ quickstartTarget.title,
2224
+ " \u2192"
2225
+ ] }),
2226
+ settings.githubRepo ? /* @__PURE__ */ jsx11(
2227
+ "a",
2228
+ {
2229
+ className: "np-docs-front-cta-secondary",
2230
+ href: settings.githubRepo,
2231
+ target: "_blank",
2232
+ rel: "noreferrer",
2233
+ children: "Browse the repository"
2234
+ }
2235
+ ) : null
2236
+ ] }) : null
2237
+ ] }),
2238
+ tree.length > 0 ? /* @__PURE__ */ jsx11("section", { className: "np-docs-front-groups", "aria-label": "Documentation sections", children: tree.map((group) => /* @__PURE__ */ jsxs10(
2239
+ "a",
2240
+ {
2241
+ className: "np-docs-front-group",
2242
+ href: `/docs/${group.slug}`,
2243
+ children: [
2244
+ /* @__PURE__ */ jsxs10("h2", { className: "np-docs-front-group-title", children: [
2245
+ group.title,
2246
+ /* @__PURE__ */ jsxs10("span", { className: "np-docs-front-group-count", children: [
2247
+ group.children.length.toString(),
2248
+ " page",
2249
+ group.children.length === 1 ? "" : "s"
2250
+ ] })
2251
+ ] }),
2252
+ group.lede ? /* @__PURE__ */ jsx11("p", { className: "np-docs-front-group-lede", children: group.lede }) : null,
2253
+ group.children.length > 0 ? /* @__PURE__ */ jsx11("ul", { className: "np-docs-front-group-children", children: group.children.slice(0, 4).map((child) => /* @__PURE__ */ jsxs10("li", { children: [
2254
+ child.title,
2255
+ child.badge ? /* @__PURE__ */ jsx11(
2256
+ "span",
2257
+ {
2258
+ className: `np-docs-sidebar-badge ${child.badge.toLowerCase()}`,
2259
+ children: child.badge.toUpperCase()
2260
+ }
2261
+ ) : null
2262
+ ] }, child.id)) }) : null
2263
+ ]
2264
+ },
2265
+ group.id
2266
+ )) }) : null,
2267
+ recent.length > 0 ? /* @__PURE__ */ jsxs10(
2268
+ "section",
2269
+ {
2270
+ className: "np-docs-front-recent",
2271
+ "aria-label": "Recently updated docs",
2272
+ children: [
2273
+ /* @__PURE__ */ jsx11("h2", { className: "np-docs-front-recent-eyebrow", children: "Recently updated" }),
2274
+ /* @__PURE__ */ jsx11("ul", { className: "np-docs-front-recent-list", children: recent.map((node) => /* @__PURE__ */ jsx11("li", { children: /* @__PURE__ */ jsxs10("a", { href: `/docs/${node.slug}`, children: [
2275
+ /* @__PURE__ */ jsx11("span", { className: "np-docs-front-recent-title", children: node.title }),
2276
+ node.updatedAt ? /* @__PURE__ */ jsx11(
2277
+ "time",
2278
+ {
2279
+ className: "np-docs-front-recent-time",
2280
+ dateTime: node.updatedAt,
2281
+ children: formatRelative(node.updatedAt)
2282
+ }
2283
+ ) : null
2284
+ ] }) }, node.id)) })
2285
+ ]
2286
+ }
2287
+ ) : null
2288
+ ] });
2289
+ }
2290
+ function buildTree2(rows) {
2291
+ return buildDocTree(rows, (r) => {
2292
+ if (typeof r.id !== "string") return null;
2293
+ if (typeof r.slug !== "string") return null;
2294
+ return {
2295
+ id: r.id,
2296
+ slug: r.slug,
2297
+ title: typeof r.title === "string" ? r.title : r.slug,
2298
+ lede: typeof r.lede === "string" ? r.lede : null,
2299
+ parent: typeof r.parent === "string" ? r.parent : null,
2300
+ order: typeof r.order === "number" ? r.order : 0,
2301
+ badge: typeof r.badge === "string" ? r.badge : null,
2302
+ publishedAt: typeof r.publishedAt === "string" ? r.publishedAt : null,
2303
+ updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : null,
2304
+ children: []
2305
+ };
2306
+ });
2307
+ }
2308
+ function recentlyUpdated(rows, limit) {
2309
+ const all = [];
2310
+ for (const r of rows) {
2311
+ if (typeof r.id !== "string" || typeof r.slug !== "string") continue;
2312
+ if (typeof r.parent !== "string") continue;
2313
+ all.push({
2314
+ id: r.id,
2315
+ slug: r.slug,
2316
+ title: typeof r.title === "string" ? r.title : r.slug,
2317
+ lede: null,
2318
+ parent: typeof r.parent === "string" ? r.parent : null,
2319
+ order: 0,
2320
+ badge: typeof r.badge === "string" ? r.badge : null,
2321
+ publishedAt: typeof r.publishedAt === "string" ? r.publishedAt : null,
2322
+ updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : null,
2323
+ children: []
2324
+ });
2325
+ }
2326
+ return all.filter((n) => n.updatedAt !== null).sort((a, b) => {
2327
+ const at = a.updatedAt ?? "";
2328
+ const bt = b.updatedAt ?? "";
2329
+ return bt.localeCompare(at);
2330
+ }).slice(0, limit);
2331
+ }
2332
+ function formatRelative(iso) {
2333
+ try {
2334
+ const then = new Date(iso).getTime();
2335
+ const now = Date.now();
2336
+ const ms = Math.max(now - then, 0);
2337
+ const minute = 60 * 1e3;
2338
+ const hour = 60 * minute;
2339
+ const day = 24 * hour;
2340
+ if (ms < hour) return `${Math.max(1, Math.round(ms / minute)).toString()} min ago`;
2341
+ if (ms < day) return `${Math.round(ms / hour).toString()} h ago`;
2342
+ if (ms < 7 * day) return `${Math.round(ms / day).toString()} d ago`;
2343
+ return new Date(iso).toLocaleDateString(void 0, {
2344
+ month: "short",
2345
+ day: "numeric",
2346
+ year: "numeric"
2347
+ });
2348
+ } catch {
2349
+ return "";
2350
+ }
2351
+ }
2352
+
1505
2353
  // src/index.ts
1506
2354
  var SEED_NAV = {
1507
2355
  header: [
@@ -1516,6 +2364,282 @@ var SEED_NAV = {
1516
2364
  { id: "nav-docs-footer-github", label: "GitHub", type: "link", url: "https://github.com" }
1517
2365
  ]
1518
2366
  };
2367
+ function paragraph(text) {
2368
+ return {
2369
+ type: "paragraph",
2370
+ version: 1,
2371
+ direction: null,
2372
+ format: "",
2373
+ indent: 0,
2374
+ children: [{
2375
+ type: "text",
2376
+ version: 1,
2377
+ detail: 0,
2378
+ format: 0,
2379
+ mode: "normal",
2380
+ style: "",
2381
+ text
2382
+ }]
2383
+ };
2384
+ }
2385
+ function heading(tag, text) {
2386
+ return {
2387
+ type: "heading",
2388
+ tag,
2389
+ version: 1,
2390
+ direction: null,
2391
+ format: "",
2392
+ indent: 0,
2393
+ children: [{
2394
+ type: "text",
2395
+ version: 1,
2396
+ detail: 0,
2397
+ format: 0,
2398
+ mode: "normal",
2399
+ style: "",
2400
+ text
2401
+ }]
2402
+ };
2403
+ }
2404
+ function codeBlock(text, language) {
2405
+ return {
2406
+ type: "code",
2407
+ language: language ?? null,
2408
+ version: 1,
2409
+ direction: null,
2410
+ format: "",
2411
+ indent: 0,
2412
+ children: [{
2413
+ type: "text",
2414
+ version: 1,
2415
+ detail: 0,
2416
+ format: 0,
2417
+ mode: "normal",
2418
+ style: "",
2419
+ text
2420
+ }]
2421
+ };
2422
+ }
2423
+ function listItem(text) {
2424
+ return {
2425
+ type: "listitem",
2426
+ value: 1,
2427
+ version: 1,
2428
+ direction: null,
2429
+ format: "",
2430
+ indent: 0,
2431
+ children: [{
2432
+ type: "text",
2433
+ version: 1,
2434
+ detail: 0,
2435
+ format: 0,
2436
+ mode: "normal",
2437
+ style: "",
2438
+ text
2439
+ }]
2440
+ };
2441
+ }
2442
+ function bulletList(items) {
2443
+ return {
2444
+ type: "list",
2445
+ listType: "bullet",
2446
+ start: 1,
2447
+ tag: "ul",
2448
+ version: 1,
2449
+ direction: null,
2450
+ format: "",
2451
+ indent: 0,
2452
+ children: items.map((t) => listItem(t))
2453
+ };
2454
+ }
2455
+ function lexicalDoc(blocks) {
2456
+ return {
2457
+ root: {
2458
+ type: "root",
2459
+ version: 1,
2460
+ direction: null,
2461
+ format: "",
2462
+ indent: 0,
2463
+ children: blocks
2464
+ }
2465
+ };
2466
+ }
2467
+ var DOCS_NOW = "2026-05-02T12:00:00.000Z";
2468
+ function stubDoc(opts) {
2469
+ return {
2470
+ title: opts.title,
2471
+ excerpt: opts.lede ?? `${opts.title} reference page.`,
2472
+ content: lexicalDoc([
2473
+ paragraph(`Placeholder body for ${opts.title}. Operators replace this once they're set up \u2014 every doc-kind post renders through the three-column docs template.`)
2474
+ ]),
2475
+ publishedAt: DOCS_NOW,
2476
+ kind: "doc",
2477
+ ...opts.parentSlug ? { parentSlug: opts.parentSlug } : {},
2478
+ ...typeof opts.order === "number" ? { order: opts.order } : {},
2479
+ data: {
2480
+ ...opts.badge ? { badge: opts.badge } : {},
2481
+ ...opts.lede ? { lede: opts.lede } : {},
2482
+ ...opts.stableSince ? { stableSince: opts.stableSince } : {}
2483
+ }
2484
+ };
2485
+ }
2486
+ var QUICKSTART_BODY = lexicalDoc([
2487
+ paragraph(
2488
+ "A NexPress plugin is a single function that returns a manifest. The framework loads it during boot, validates its declared shape, and wires the hooks and routes into the request pipeline. There's no plugin loader to learn \u2014 if you can write a TypeScript module, you can write a plugin."
2489
+ ),
2490
+ paragraph(
2491
+ "Prerequisites: a running NexPress site (see Install & bootstrap) and Node 20+. The plugin lives inside your app \u2014 no separate workspace required to start."
2492
+ ),
2493
+ heading("h2", "Scaffold the plugin"),
2494
+ paragraph(
2495
+ "The CLI ships a scaffold command that drops a typed plugin module into plugins/. The generated file imports definePlugin from the SDK and exports a single function."
2496
+ ),
2497
+ codeBlock("pnpm nexpress plugin:new hello-world", "bash"),
2498
+ paragraph(
2499
+ "The generated module is ~20 lines, ready to run. Open it in your editor before continuing."
2500
+ ),
2501
+ codeBlock(
2502
+ [
2503
+ 'import { definePlugin } from "@nexpress/plugin-sdk";',
2504
+ "",
2505
+ "export default definePlugin({",
2506
+ " manifest: {",
2507
+ ' id: "hello-world",',
2508
+ ' name: "Hello, world",',
2509
+ ' version: "0.1.0",',
2510
+ ' nexpress: { minVersion: "0.1.0" },',
2511
+ " },",
2512
+ " hooks: {",
2513
+ " onDocumentPublished: async ({ doc, collection }) => {",
2514
+ ' if (collection !== "posts") return;',
2515
+ " console.log(`Published: ${doc.title}`);",
2516
+ " },",
2517
+ " },",
2518
+ "});"
2519
+ ].join("\n"),
2520
+ "typescript"
2521
+ ),
2522
+ heading("h2", "Register it with your site"),
2523
+ paragraph(
2524
+ "NexPress loads plugins from nexpress.config.ts. Import your module and add it to the plugins array \u2014 order matters, hooks run in the order they're registered."
2525
+ ),
2526
+ codeBlock(
2527
+ [
2528
+ 'import { defineConfig } from "@nexpress/core";',
2529
+ 'import helloWorld from "./plugins/hello-world";',
2530
+ "",
2531
+ "export default defineConfig({",
2532
+ " plugins: [helloWorld()],",
2533
+ "});"
2534
+ ].join("\n"),
2535
+ "typescript"
2536
+ ),
2537
+ paragraph(
2538
+ "Hot reload: the dev server picks up new plugin files without a restart. Config changes do require one \u2014 that's a Next.js constraint, not ours."
2539
+ ),
2540
+ heading("h2", "Lifecycle hooks at a glance"),
2541
+ paragraph(
2542
+ "Hooks are typed callbacks NexPress invokes at well-known points. Each receives a context object scoped to that event. The most commonly used hooks:"
2543
+ ),
2544
+ bulletList([
2545
+ "onBoot \u2014 after config load, before request handling. Context: { config, env }.",
2546
+ "onDocumentPublished (async) \u2014 a document's status transitions to published. Context: { doc, collection, by }.",
2547
+ "onDocumentUnpublished \u2014 status leaves published. Context: { doc, collection, by }.",
2548
+ "onRequest \u2014 every request, after routing, before render. Context: { req, route, user }.",
2549
+ "onSchedule \u2014 cron-like; declare cadence in the manifest. Context: { now, schedule }."
2550
+ ]),
2551
+ paragraph(
2552
+ "The full list \u2014 including admin-surface and search hooks \u2014 lives in the lifecycle hooks reference."
2553
+ ),
2554
+ heading("h2", "Run and verify"),
2555
+ bulletList([
2556
+ "Start the dev server. pnpm dev from the repo root. Codegen runs alongside Next's watcher, so plugin types are picked up as you save.",
2557
+ "Publish a post from the admin. Open /admin, create a draft in the posts collection, and click Publish. The hook fires inside the same request.",
2558
+ "Check the dev server log. You should see Published: <title> in the terminal. That's it \u2014 the plugin is live."
2559
+ ]),
2560
+ paragraph(
2561
+ "Hooks block the response: onDocumentPublished runs inside the publish request. Long-running work \u2014 sending emails, regenerating sitemaps \u2014 belongs in onSchedule or a queued job. Otherwise the editor will wait on it."
2562
+ ),
2563
+ heading("h2", "Next steps"),
2564
+ paragraph(
2565
+ "You have a plugin that runs. Two natural directions from here:"
2566
+ ),
2567
+ bulletList([
2568
+ "Add a route. Declare a routes entry in the manifest to expose a public URL \u2014 for webhooks, OAuth callbacks, or a custom admin screen.",
2569
+ "Add collections. Plugins can declare their own collections, which the admin surfaces alongside the operator's. See the Plugin manifest reference."
2570
+ ])
2571
+ ]);
2572
+ var SEED_PAGES = [
2573
+ {
2574
+ title: "Documentation",
2575
+ slug: "/",
2576
+ seoDescription: "Install NexPress, learn the core concepts, write plugins, and look up the API.",
2577
+ blocks: [],
2578
+ template: "front"
2579
+ }
2580
+ ];
2581
+ var SEED_DOCS = [
2582
+ stubDoc({
2583
+ title: "Get started",
2584
+ order: 0,
2585
+ lede: "Install NexPress, scaffold a site, and ship a first deploy."
2586
+ }),
2587
+ stubDoc({
2588
+ title: "Introduction",
2589
+ parentSlug: "get-started",
2590
+ order: 0,
2591
+ lede: "What NexPress is, what it isn't, and who it's for.",
2592
+ stableSince: "Stable since 0.1"
2593
+ }),
2594
+ stubDoc({ title: "Install & bootstrap", parentSlug: "get-started", order: 1, stableSince: "Stable since 0.1" }),
2595
+ stubDoc({ title: "Project structure", parentSlug: "get-started", order: 2 }),
2596
+ stubDoc({ title: "Configuration", parentSlug: "get-started", order: 3 }),
2597
+ stubDoc({ title: "Deployment", parentSlug: "get-started", order: 4 }),
2598
+ stubDoc({
2599
+ title: "Core concepts",
2600
+ order: 1,
2601
+ lede: "The model behind collections, themes, plugins, and blocks."
2602
+ }),
2603
+ stubDoc({ title: "Collections", parentSlug: "core-concepts", order: 0, stableSince: "Stable since 0.1" }),
2604
+ stubDoc({ title: "Pages & routing", parentSlug: "core-concepts", order: 1 }),
2605
+ stubDoc({ title: "Themes", parentSlug: "core-concepts", order: 2 }),
2606
+ stubDoc({ title: "Blocks", parentSlug: "core-concepts", order: 3 }),
2607
+ stubDoc({ title: "Hooks & access", parentSlug: "core-concepts", order: 4 }),
2608
+ stubDoc({ title: "Internationalization", parentSlug: "core-concepts", order: 5 }),
2609
+ stubDoc({
2610
+ title: "Plugins",
2611
+ order: 2,
2612
+ lede: "Extend NexPress with hooks, routes, blocks, and scheduled jobs."
2613
+ }),
2614
+ stubDoc({ title: "Plugin overview", parentSlug: "plugins", order: 0 }),
2615
+ {
2616
+ title: "Author quickstart",
2617
+ excerpt: 'From "I want to add behavior to NexPress" to a running plugin in about ten minutes. Walks through the manifest, a lifecycle hook, and shipping the result to your own site.',
2618
+ content: QUICKSTART_BODY,
2619
+ publishedAt: DOCS_NOW,
2620
+ kind: "doc",
2621
+ parentSlug: "plugins",
2622
+ order: 1,
2623
+ data: {
2624
+ badge: "NEW",
2625
+ lede: 'From "I want to add behavior to NexPress" to a running plugin in about ten minutes. Walks through the manifest, a lifecycle hook, and shipping the result to your own site.',
2626
+ stableSince: "Stable since 0.1"
2627
+ }
2628
+ },
2629
+ stubDoc({ title: "Manifest reference", parentSlug: "plugins", order: 2 }),
2630
+ stubDoc({ title: "Lifecycle hooks", parentSlug: "plugins", order: 3 }),
2631
+ stubDoc({ title: "Publishing", parentSlug: "plugins", order: 4 }),
2632
+ stubDoc({
2633
+ title: "Reference",
2634
+ order: 3,
2635
+ lede: "API surface \u2014 CLI, define* helpers, server functions."
2636
+ }),
2637
+ stubDoc({ title: "CLI", parentSlug: "reference", order: 0 }),
2638
+ stubDoc({ title: "defineCollection", parentSlug: "reference", order: 1, badge: "API" }),
2639
+ stubDoc({ title: "defineTheme", parentSlug: "reference", order: 2 }),
2640
+ stubDoc({ title: "definePlugin", parentSlug: "reference", order: 3 }),
2641
+ stubDoc({ title: "Server functions", parentSlug: "reference", order: 4, badge: "BETA" })
2642
+ ];
1519
2643
  var docsTheme = defineTheme({
1520
2644
  manifest: {
1521
2645
  id: "docs",
@@ -1605,6 +2729,10 @@ var docsTheme = defineTheme({
1605
2729
  colors: {
1606
2730
  primary: "#2563eb",
1607
2731
  primaryForeground: "#ffffff",
2732
+ // 10% tint of primary — equivalent to the color-mix
2733
+ // fallback in styles.ts, but materialised so the
2734
+ // generated theme CSS picks it up explicitly.
2735
+ primarySoft: "rgba(37, 99, 235, 0.1)",
1608
2736
  background: "#fbfcfe",
1609
2737
  foreground: "#0c1320",
1610
2738
  muted: "#f1f4f9",
@@ -1624,9 +2752,18 @@ var docsTheme = defineTheme({
1624
2752
  }
1625
2753
  },
1626
2754
  seedContent: {
1627
- navigation: SEED_NAV
2755
+ navigation: SEED_NAV,
2756
+ pages: SEED_PAGES,
2757
+ posts: SEED_DOCS
1628
2758
  },
1629
2759
  templates: {
2760
+ pages: {
2761
+ front: {
2762
+ label: "Front page",
2763
+ description: 'Docs landing \u2014 hero + 2x2 group cards walking the kind="doc" tree + recently-updated row. The seeded home page (slug "/") ships with this template.',
2764
+ component: PageFrontTemplate
2765
+ }
2766
+ },
1630
2767
  // Universal-content-model #748 — docs are posts with
1631
2768
  // `kind: "doc"`. The template key matches the kind value so
1632
2769
  // the per-kind template lookup picks this up automatically.
@@ -1665,7 +2802,7 @@ var docsTheme = defineTheme({
1665
2802
  { pattern: "/docs/:slug", component: DocsDetailRoute }
1666
2803
  ],
1667
2804
  navLocations: {
1668
- primary: {
2805
+ header: {
1669
2806
  label: "Primary header nav",
1670
2807
  description: "Inline links beside the masthead search box.",
1671
2808
  maxItems: 5
@@ -1692,10 +2829,12 @@ var docsTheme = defineTheme({
1692
2829
  members: {
1693
2830
  shell: DocsMembersShell,
1694
2831
  notFound: DocsMembersNotFound
1695
- }
2832
+ },
2833
+ blocks: docsBlocks
1696
2834
  }
1697
2835
  });
1698
2836
  export {
2837
+ CopyButton,
1699
2838
  DocPageTemplate,
1700
2839
  DocsHeader,
1701
2840
  DocsMembersNotFound,
@@ -1704,6 +2843,7 @@ export {
1704
2843
  DocsSearch,
1705
2844
  DocsShell,
1706
2845
  DocsSidebar,
2846
+ docsBlocks,
1707
2847
  docsCss,
1708
2848
  docsSettingsSchema,
1709
2849
  docsTheme