@nexpress/theme-docs 0.3.1 → 0.3.3

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,13 +999,26 @@ 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" }),
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)) });
1002
+ return /* @__PURE__ */ jsx10("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: tree.map((group) => {
1003
+ const isLeaf = group.children.length === 0;
1004
+ const isLeafCurrent = isLeaf && currentSlug === group.slug;
1005
+ return /* @__PURE__ */ jsxs9("div", { className: "np-docs-sidebar-group", children: [
1006
+ /* @__PURE__ */ jsxs9("h2", { className: "np-docs-sidebar-eyebrow", children: [
1007
+ /* @__PURE__ */ jsx10("span", { className: "np-docs-sidebar-eyebrow-dot", "aria-hidden": "true" }),
1008
+ isLeaf ? /* @__PURE__ */ jsx10(
1009
+ "a",
1010
+ {
1011
+ className: "np-docs-sidebar-eyebrow-link",
1012
+ href: `/docs/${group.slug}`,
1013
+ "data-current": isLeafCurrent ? "true" : void 0,
1014
+ "aria-current": isLeafCurrent ? "page" : void 0,
1015
+ children: group.title
1016
+ }
1017
+ ) : group.title
1018
+ ] }),
1019
+ isLeaf ? null : /* @__PURE__ */ jsx10(NavTree, { nodes: group.children, currentSlug })
1020
+ ] }, group.id);
1021
+ }) });
620
1022
  }
621
1023
  async function currentPathSlug() {
622
1024
  try {
@@ -632,11 +1034,10 @@ async function currentPathSlug() {
632
1034
  }
633
1035
  function buildTree(rawDocs) {
634
1036
  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, {
1037
+ return buildDocTree(docs, (d) => {
1038
+ if (typeof d.id !== "string") return null;
1039
+ if (typeof d.slug !== "string") return null;
1040
+ return {
640
1041
  id: d.id,
641
1042
  slug: d.slug,
642
1043
  title: typeof d.title === "string" ? d.title : d.slug,
@@ -644,30 +1045,16 @@ function buildTree(rawDocs) {
644
1045
  order: typeof d.order === "number" ? d.order : 0,
645
1046
  badge: typeof d.badge === "string" ? d.badge : null,
646
1047
  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;
1048
+ };
1049
+ });
663
1050
  }
664
1051
  function NavTree({
665
1052
  nodes,
666
1053
  currentSlug
667
1054
  }) {
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
1055
+ return /* @__PURE__ */ jsx10("ul", { children: nodes.map((n) => /* @__PURE__ */ jsxs9("li", { children: [
1056
+ /* @__PURE__ */ jsx10(SidebarLink, { node: n, currentSlug }),
1057
+ n.children.length > 0 ? /* @__PURE__ */ jsx10(NavTree, { nodes: n.children, currentSlug }) : null
671
1058
  ] }, n.id)) });
672
1059
  }
673
1060
  function SidebarLink({
@@ -676,7 +1063,7 @@ function SidebarLink({
676
1063
  }) {
677
1064
  const isCurrent = currentSlug !== null && currentSlug === node.slug;
678
1065
  const badgeClass = node.badge ? `np-docs-sidebar-badge ${node.badge.toLowerCase()}` : null;
679
- return /* @__PURE__ */ jsxs7(
1066
+ return /* @__PURE__ */ jsxs9(
680
1067
  "a",
681
1068
  {
682
1069
  href: `/docs/${node.slug}`,
@@ -684,7 +1071,7 @@ function SidebarLink({
684
1071
  "aria-current": isCurrent ? "page" : void 0,
685
1072
  children: [
686
1073
  node.title,
687
- badgeClass ? /* @__PURE__ */ jsx9("span", { className: badgeClass, children: node.badge.toUpperCase() }) : null
1074
+ badgeClass ? /* @__PURE__ */ jsx10("span", { className: badgeClass, children: node.badge.toUpperCase() }) : null
688
1075
  ]
689
1076
  }
690
1077
  );
@@ -889,6 +1276,29 @@ var docsCss = `
889
1276
  .np-docs-sidebar { display: none; }
890
1277
  }
891
1278
 
1279
+ /* Non-docs routes (home / about / pricing / contact / member pages):
1280
+ * collapse the 3-col grid to a single wide column and hide the
1281
+ * doc-only chrome (sidebar + TOC) so a generic pages doc has full
1282
+ * canvas width instead of being squeezed into the 800-ish px
1283
+ * article column reserved for the /docs reading lane. */
1284
+ .np-docs-shell[data-layout="page"] .np-docs-grid {
1285
+ grid-template-columns: minmax(0, 1fr);
1286
+ }
1287
+ .np-docs-shell[data-layout="page"] .np-docs-sidebar,
1288
+ .np-docs-shell[data-layout="page"] .np-docs-toc {
1289
+ display: none;
1290
+ }
1291
+ /* Framework's globals.css caps .np-page at 48rem (~768px) so a
1292
+ * regular pages doc rendered through the catch-all's fallback
1293
+ * wrapper stays squeezed even after the grid collapse above.
1294
+ * Lift the cap inside the page-layout so block-level content
1295
+ * (hero, features, stats) can stretch to the docs container. */
1296
+ .np-docs-shell[data-layout="page"] .np-page {
1297
+ max-width: none;
1298
+ margin: 0;
1299
+ padding: 0;
1300
+ }
1301
+
892
1302
  /* ============================================================
893
1303
  * Sidebar \u2014 grouped link list with bullet eyebrow + badges.
894
1304
  * ============================================================ */
@@ -919,6 +1329,24 @@ var docsCss = `
919
1329
  border-radius: 50%;
920
1330
  background: var(--np-color-primary);
921
1331
  }
1332
+ /* Leaf top-level doc rendered as a clickable eyebrow \u2014 inherits
1333
+ * the eyebrow typography (mono / uppercase / 0.7rem) so it sits
1334
+ * in the same visual row as sibling group eyebrows; primary
1335
+ * accent on current, foreground on hover. */
1336
+ .np-docs-sidebar-eyebrow-link {
1337
+ color: inherit;
1338
+ text-decoration: none;
1339
+ font: inherit;
1340
+ letter-spacing: inherit;
1341
+ text-transform: inherit;
1342
+ }
1343
+ .np-docs-sidebar-eyebrow-link:hover {
1344
+ color: var(--np-color-foreground);
1345
+ }
1346
+ .np-docs-sidebar-eyebrow-link[data-current="true"],
1347
+ .np-docs-sidebar-eyebrow-link[aria-current="page"] {
1348
+ color: var(--np-color-primary);
1349
+ }
922
1350
  .np-docs-sidebar ul {
923
1351
  list-style: none;
924
1352
  padding: 0;
@@ -1031,16 +1459,16 @@ var docsCss = `
1031
1459
  background: var(--np-color-card);
1032
1460
  }
1033
1461
  .np-docs-page-meta-pill.status {
1034
- color: #047857;
1462
+ color: var(--np-color-success, #047857);
1035
1463
  border-color: #bbf7d0;
1036
- background: #f0fdf4;
1464
+ background: var(--np-color-success-soft, #f0fdf4);
1037
1465
  }
1038
1466
  .np-docs-page-meta-pill.status::before {
1039
1467
  content: "";
1040
1468
  width: 0.4rem;
1041
1469
  height: 0.4rem;
1042
1470
  border-radius: 50%;
1043
- background: #047857;
1471
+ background: var(--np-color-success, #047857);
1044
1472
  }
1045
1473
  .np-docs-page-meta-sep { opacity: 0.4; }
1046
1474
  .np-docs-page-meta a {
@@ -1141,12 +1569,12 @@ var docsCss = `
1141
1569
  color: var(--np-color-foreground);
1142
1570
  }
1143
1571
  .np-docs-callout--warn {
1144
- border-left-color: #b45309;
1145
- background: #fffbeb;
1572
+ border-left-color: var(--np-color-warning, #b45309);
1573
+ background: var(--np-color-warning-soft, #fffbeb);
1146
1574
  border-color: #fde68a;
1147
1575
  }
1148
1576
  .np-docs-callout--warn .np-docs-callout-icon,
1149
- .np-docs-callout--warn > svg { color: #b45309; }
1577
+ .np-docs-callout--warn > svg { color: var(--np-color-warning, #b45309); }
1150
1578
  .np-docs-callout--note {
1151
1579
  border-left-color: #6366f1;
1152
1580
  background: #eef2ff;
@@ -1155,12 +1583,12 @@ var docsCss = `
1155
1583
  .np-docs-callout--note .np-docs-callout-icon,
1156
1584
  .np-docs-callout--note > svg { color: #4338ca; }
1157
1585
  .np-docs-callout--danger {
1158
- border-left-color: #b91c1c;
1159
- background: #fef2f2;
1586
+ border-left-color: var(--np-color-danger, #b91c1c);
1587
+ background: var(--np-color-danger-soft, #fef2f2);
1160
1588
  border-color: #fecaca;
1161
1589
  }
1162
1590
  .np-docs-callout--danger .np-docs-callout-icon,
1163
- .np-docs-callout--danger > svg { color: #b91c1c; }
1591
+ .np-docs-callout--danger > svg { color: var(--np-color-danger, #b91c1c); }
1164
1592
 
1165
1593
  /* ============================================================
1166
1594
  * Code blocks \u2014 dark surface with a file-named header and a
@@ -1172,17 +1600,17 @@ var docsCss = `
1172
1600
  .np-docs-code {
1173
1601
  margin: 1.25rem 0;
1174
1602
  border-radius: 10px;
1175
- background: #0b1220;
1176
- color: #e6edf6;
1603
+ background: var(--np-color-code-bg, #0b1220);
1604
+ color: var(--np-color-code-fg, #e6edf6);
1177
1605
  overflow: hidden;
1178
- border: 1px solid #1e2939;
1606
+ border: 1px solid var(--np-color-code-head, #1e2939);
1179
1607
  }
1180
1608
  .np-docs-code-head {
1181
1609
  display: flex;
1182
1610
  align-items: center;
1183
1611
  justify-content: space-between;
1184
1612
  padding: 0.55rem 0.85rem;
1185
- background: #0f1a2b;
1613
+ background: var(--np-color-code-border, #0f1a2b);
1186
1614
  border-bottom: 1px solid #1e293b;
1187
1615
  }
1188
1616
  .np-docs-code-file {
@@ -1244,8 +1672,8 @@ var docsCss = `
1244
1672
  align-items: center;
1245
1673
  padding: 0.75rem 1rem;
1246
1674
  margin: 1.25rem 0;
1247
- background: #0b1220;
1248
- color: #e6edf6;
1675
+ background: var(--np-color-code-bg, #0b1220);
1676
+ color: var(--np-color-code-fg, #e6edf6);
1249
1677
  border-radius: 9px;
1250
1678
  font-family: var(--np-font-mono);
1251
1679
  font-size: 0.875rem;
@@ -1384,6 +1812,11 @@ var docsCss = `
1384
1812
  }
1385
1813
  .np-docs-prev-next a.np-docs-prev-next-next,
1386
1814
  .np-docs-prev-next a:last-child { text-align: right; }
1815
+ .np-docs-prev-next[data-single="prev"],
1816
+ .np-docs-prev-next[data-single="next"] { grid-template-columns: 1fr; }
1817
+ .np-docs-prev-next[data-single="prev"] a.np-docs-prev-next-prev,
1818
+ .np-docs-prev-next[data-single="next"] a.np-docs-prev-next-next { width: 100%; }
1819
+ .np-docs-prev-next[data-single="prev"] a.np-docs-prev-next-prev { text-align: left; }
1387
1820
 
1388
1821
  /* ============================================================
1389
1822
  * Feedback row \u2014 Yes / Could be better buttons under each page.
@@ -1473,6 +1906,7 @@ var docsCss = `
1473
1906
  );
1474
1907
  }
1475
1908
  .np-docs-toc ul ul { margin-left: 0.85rem; }
1909
+ .np-docs-toc-l3 { margin-left: 0.85rem; }
1476
1910
  .np-docs-toc-secondary {
1477
1911
  margin-top: 1.5rem;
1478
1912
  padding-top: 1rem;
@@ -1500,8 +1934,453 @@ var docsCss = `
1500
1934
  margin: 0 0 0.5rem;
1501
1935
  color: var(--np-color-foreground);
1502
1936
  }
1937
+
1938
+ /* ============================================================
1939
+ * Search route \u2014 wraps DocsSearch's output. Eyebrow + result
1940
+ * cards reuse the docs chrome (mono small caps, hairline rules,
1941
+ * bordered card with hover lift).
1942
+ * ============================================================ */
1943
+ .np-docs-search {
1944
+ max-width: 800px;
1945
+ margin: 0 auto;
1946
+ padding-top: 2.25rem;
1947
+ }
1948
+ .np-docs-search-heading {
1949
+ font-family: var(--np-font-mono);
1950
+ font-size: 0.72rem;
1951
+ text-transform: uppercase;
1952
+ letter-spacing: 0.08em;
1953
+ color: var(--np-color-muted-foreground);
1954
+ font-weight: 600;
1955
+ margin: 0 0 0.5rem;
1956
+ }
1957
+ .np-docs-search h1 {
1958
+ font-size: 1.75rem;
1959
+ font-weight: 700;
1960
+ letter-spacing: -0.02em;
1961
+ margin: 0 0 1.5rem;
1962
+ text-wrap: balance;
1963
+ }
1964
+ .np-docs-search-empty {
1965
+ color: var(--np-color-muted-foreground);
1966
+ padding: 1.5rem 0;
1967
+ font-size: 0.95rem;
1968
+ }
1969
+ .np-docs-search-results {
1970
+ list-style: none;
1971
+ padding: 0;
1972
+ margin: 1.5rem 0 0;
1973
+ display: grid;
1974
+ gap: 1rem;
1975
+ }
1976
+ .np-docs-search-result {
1977
+ padding: 1rem 1.15rem;
1978
+ border: 1px solid var(--np-color-border);
1979
+ border-radius: 10px;
1980
+ background: var(--np-color-card);
1981
+ transition: border-color 0.15s ease, transform 0.2s ease;
1982
+ }
1983
+ .np-docs-search-result:hover {
1984
+ border-color: var(--np-color-primary);
1985
+ transform: translateY(-1px);
1986
+ }
1987
+ .np-docs-search-result-eyebrow {
1988
+ font-family: var(--np-font-mono);
1989
+ font-size: 0.68rem;
1990
+ text-transform: uppercase;
1991
+ letter-spacing: 0.08em;
1992
+ color: var(--np-color-muted-foreground);
1993
+ margin: 0 0 0.35rem;
1994
+ }
1995
+ .np-docs-search-result h2 {
1996
+ font-size: 1.05rem;
1997
+ font-weight: 600;
1998
+ margin: 0 0 0.4rem;
1999
+ }
2000
+ .np-docs-search-result h2 a {
2001
+ color: var(--np-color-foreground);
2002
+ text-decoration: none;
2003
+ }
2004
+ .np-docs-search-result h2 a:hover { color: var(--np-color-primary); }
2005
+ .np-docs-search-result-excerpt {
2006
+ margin: 0;
2007
+ font-size: 0.875rem;
2008
+ color: var(--np-color-muted-foreground);
2009
+ line-height: 1.55;
2010
+ }
2011
+
2012
+ /* ============================================================
2013
+ * Front-page landing \u2014 eyebrow + display heading + lede +
2014
+ * primary CTA + 2x2 group cards + recently-updated row.
2015
+ * Renders inside the single-column page layout
2016
+ * (data-layout="page" on the shell collapses the 3-col grid).
2017
+ * ============================================================ */
2018
+ .np-docs-front {
2019
+ max-width: 880px;
2020
+ margin: 0 auto;
2021
+ padding: 2.5rem 0 4rem;
2022
+ display: grid;
2023
+ gap: 3rem;
2024
+ }
2025
+ .np-docs-front-hero {
2026
+ display: grid;
2027
+ gap: 1rem;
2028
+ }
2029
+ .np-docs-front-eyebrow {
2030
+ display: inline-flex;
2031
+ align-items: center;
2032
+ gap: 0.5rem;
2033
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2034
+ font-size: 0.72rem;
2035
+ letter-spacing: 0.16em;
2036
+ text-transform: uppercase;
2037
+ color: var(--np-color-primary);
2038
+ background: var(--np-color-primary-soft, color-mix(in oklab, var(--np-color-primary) 10%, transparent));
2039
+ padding: 0.32rem 0.6rem;
2040
+ border-radius: 999px;
2041
+ align-self: start;
2042
+ justify-self: start;
2043
+ }
2044
+ .np-docs-front-eyebrow-dot {
2045
+ width: 6px;
2046
+ height: 6px;
2047
+ border-radius: 999px;
2048
+ background: var(--np-color-success, currentColor);
2049
+ display: inline-block;
2050
+ }
2051
+ .np-docs-front h1 {
2052
+ font-size: clamp(2.4rem, 4.2vw, 3rem);
2053
+ font-weight: 700;
2054
+ letter-spacing: -0.03em;
2055
+ line-height: 1.05;
2056
+ margin: 0;
2057
+ text-wrap: balance;
2058
+ }
2059
+ .np-docs-front-lede {
2060
+ font-size: 1.125rem;
2061
+ line-height: 1.55;
2062
+ color: var(--np-color-muted-foreground);
2063
+ max-width: 60ch;
2064
+ margin: 0;
2065
+ }
2066
+ .np-docs-front-cta {
2067
+ display: flex;
2068
+ flex-wrap: wrap;
2069
+ gap: 0.65rem;
2070
+ margin-top: 0.5rem;
2071
+ }
2072
+ .np-docs-front-cta-primary {
2073
+ display: inline-flex;
2074
+ align-items: center;
2075
+ gap: 0.4rem;
2076
+ font-size: 0.92rem;
2077
+ font-weight: 500;
2078
+ padding: 0.55rem 1.05rem;
2079
+ border-radius: 999px;
2080
+ background: var(--np-color-foreground);
2081
+ color: var(--np-color-background);
2082
+ text-decoration: none;
2083
+ }
2084
+ .np-docs-front-cta-primary:hover {
2085
+ background: color-mix(in oklab, var(--np-color-foreground) 85%, transparent);
2086
+ }
2087
+ .np-docs-front-cta-secondary {
2088
+ display: inline-flex;
2089
+ align-items: center;
2090
+ font-size: 0.92rem;
2091
+ padding: 0.55rem 1.05rem;
2092
+ border-radius: 999px;
2093
+ color: var(--np-color-foreground);
2094
+ text-decoration: none;
2095
+ border: 1px solid var(--np-color-border);
2096
+ }
2097
+ .np-docs-front-cta-secondary:hover {
2098
+ background: var(--np-color-muted);
2099
+ }
2100
+
2101
+ .np-docs-front-groups {
2102
+ display: grid;
2103
+ grid-template-columns: repeat(2, 1fr);
2104
+ gap: 1rem;
2105
+ }
2106
+ @media (max-width: 720px) {
2107
+ .np-docs-front-groups { grid-template-columns: 1fr; }
2108
+ }
2109
+ .np-docs-front-group {
2110
+ display: grid;
2111
+ gap: 0.5rem;
2112
+ padding: 1.4rem 1.4rem 1.6rem;
2113
+ border-radius: var(--np-radius-lg, 10px);
2114
+ border: 1px solid var(--np-color-border);
2115
+ background: var(--np-color-card);
2116
+ text-decoration: none;
2117
+ color: inherit;
2118
+ transition: border-color 120ms ease, transform 120ms ease;
2119
+ }
2120
+ .np-docs-front-group:hover {
2121
+ border-color: var(--np-color-primary);
2122
+ transform: translateY(-1px);
2123
+ }
2124
+ .np-docs-front-group-title {
2125
+ display: flex;
2126
+ align-items: center;
2127
+ justify-content: space-between;
2128
+ font-size: 1.05rem;
2129
+ font-weight: 600;
2130
+ margin: 0;
2131
+ }
2132
+ .np-docs-front-group-count {
2133
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2134
+ font-size: 0.7rem;
2135
+ letter-spacing: 0.04em;
2136
+ color: var(--np-color-muted-foreground);
2137
+ font-weight: 400;
2138
+ }
2139
+ .np-docs-front-group-lede {
2140
+ margin: 0;
2141
+ font-size: 0.9rem;
2142
+ line-height: 1.5;
2143
+ color: var(--np-color-muted-foreground);
2144
+ }
2145
+ .np-docs-front-group-children {
2146
+ list-style: none;
2147
+ margin: 0.4rem 0 0;
2148
+ padding: 0;
2149
+ display: grid;
2150
+ gap: 0.25rem;
2151
+ }
2152
+ .np-docs-front-group-children li {
2153
+ font-size: 0.86rem;
2154
+ color: var(--np-color-muted-foreground);
2155
+ display: inline-flex;
2156
+ align-items: center;
2157
+ gap: 0.4rem;
2158
+ }
2159
+
2160
+ .np-docs-front-recent {
2161
+ display: grid;
2162
+ gap: 0.75rem;
2163
+ }
2164
+ .np-docs-front-recent-eyebrow {
2165
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2166
+ font-size: 0.7rem;
2167
+ letter-spacing: 0.18em;
2168
+ text-transform: uppercase;
2169
+ color: var(--np-color-muted-foreground);
2170
+ margin: 0;
2171
+ }
2172
+ .np-docs-front-recent-list {
2173
+ list-style: none;
2174
+ margin: 0;
2175
+ padding: 0;
2176
+ display: grid;
2177
+ gap: 0.5rem;
2178
+ }
2179
+ .np-docs-front-recent-list a {
2180
+ display: flex;
2181
+ align-items: baseline;
2182
+ justify-content: space-between;
2183
+ gap: 1rem;
2184
+ padding: 0.5rem 0;
2185
+ border-bottom: 1px solid var(--np-color-border);
2186
+ text-decoration: none;
2187
+ color: inherit;
2188
+ }
2189
+ .np-docs-front-recent-list li:last-child a {
2190
+ border-bottom: 0;
2191
+ }
2192
+ .np-docs-front-recent-list a:hover {
2193
+ color: var(--np-color-primary);
2194
+ }
2195
+ .np-docs-front-recent-title {
2196
+ font-size: 0.95rem;
2197
+ }
2198
+ .np-docs-front-recent-time {
2199
+ font-family: var(--np-font-mono, ui-monospace, monospace);
2200
+ font-size: 0.75rem;
2201
+ color: var(--np-color-muted-foreground);
2202
+ flex-shrink: 0;
2203
+ }
2204
+
2205
+ /* \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
2206
+ * Members shell (DocsMembersShell \u2014 /members/* routes)
2207
+ *
2208
+ * Drops the docs sidebar \u2014 hierarchical navigation is useless
2209
+ * on auth forms. Reuses DocsHeader directly. Body becomes a
2210
+ * narrow centered column for the form / status content.
2211
+ * \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 */
2212
+ .np-docs-members {
2213
+ padding: 4rem 1.5rem;
2214
+ min-height: 60vh;
2215
+ }
2216
+ .np-docs-members-column {
2217
+ max-width: 32rem;
2218
+ margin: 0 auto;
2219
+ display: flex;
2220
+ flex-direction: column;
2221
+ gap: 1.5rem;
2222
+ }
1503
2223
  `;
1504
2224
 
2225
+ // src/templates/page-front.tsx
2226
+ import { findDocuments as findDocuments4 } from "@nexpress/core";
2227
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2228
+ async function PageFrontTemplate(_props) {
2229
+ const settings = await resolveDocsSettings();
2230
+ const result = await findDocuments4("posts", {
2231
+ where: { status: "published", kind: "doc" },
2232
+ sort: "order",
2233
+ limit: 200
2234
+ });
2235
+ const tree = buildTree2(result.docs);
2236
+ const recent = recentlyUpdated(result.docs, 4);
2237
+ const pluginsRoot = tree.find((node) => node.slug === "plugins");
2238
+ const quickstartChild = pluginsRoot?.children.find(
2239
+ (c) => c.slug === "author-quickstart"
2240
+ );
2241
+ const quickstartTarget = quickstartChild ?? tree[0]?.children[0] ?? tree[0] ?? null;
2242
+ return /* @__PURE__ */ jsxs10("article", { className: "np-docs-front", children: [
2243
+ /* @__PURE__ */ jsxs10("header", { className: "np-docs-front-hero", children: [
2244
+ /* @__PURE__ */ jsxs10("span", { className: "np-docs-front-eyebrow", children: [
2245
+ /* @__PURE__ */ jsx11("span", { className: "np-docs-front-eyebrow-dot", "aria-hidden": "true" }),
2246
+ settings.version,
2247
+ /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", children: " \xB7 Stable" })
2248
+ ] }),
2249
+ /* @__PURE__ */ jsx11("h1", { children: "Documentation" }),
2250
+ /* @__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." }),
2251
+ quickstartTarget ? /* @__PURE__ */ jsxs10("div", { className: "np-docs-front-cta", children: [
2252
+ /* @__PURE__ */ jsxs10("a", { className: "np-docs-front-cta-primary", href: `/docs/${quickstartTarget.slug}`, children: [
2253
+ "Open ",
2254
+ quickstartTarget.title,
2255
+ " \u2192"
2256
+ ] }),
2257
+ settings.githubRepo ? /* @__PURE__ */ jsx11(
2258
+ "a",
2259
+ {
2260
+ className: "np-docs-front-cta-secondary",
2261
+ href: settings.githubRepo,
2262
+ target: "_blank",
2263
+ rel: "noreferrer",
2264
+ children: "Browse the repository"
2265
+ }
2266
+ ) : null
2267
+ ] }) : null
2268
+ ] }),
2269
+ tree.length > 0 ? /* @__PURE__ */ jsx11("section", { className: "np-docs-front-groups", "aria-label": "Documentation sections", children: tree.map((group) => /* @__PURE__ */ jsxs10(
2270
+ "a",
2271
+ {
2272
+ className: "np-docs-front-group",
2273
+ href: `/docs/${group.slug}`,
2274
+ children: [
2275
+ /* @__PURE__ */ jsxs10("h2", { className: "np-docs-front-group-title", children: [
2276
+ group.title,
2277
+ /* @__PURE__ */ jsxs10("span", { className: "np-docs-front-group-count", children: [
2278
+ group.children.length.toString(),
2279
+ " page",
2280
+ group.children.length === 1 ? "" : "s"
2281
+ ] })
2282
+ ] }),
2283
+ group.lede ? /* @__PURE__ */ jsx11("p", { className: "np-docs-front-group-lede", children: group.lede }) : null,
2284
+ 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: [
2285
+ child.title,
2286
+ child.badge ? /* @__PURE__ */ jsx11(
2287
+ "span",
2288
+ {
2289
+ className: `np-docs-sidebar-badge ${child.badge.toLowerCase()}`,
2290
+ children: child.badge.toUpperCase()
2291
+ }
2292
+ ) : null
2293
+ ] }, child.id)) }) : null
2294
+ ]
2295
+ },
2296
+ group.id
2297
+ )) }) : null,
2298
+ recent.length > 0 ? /* @__PURE__ */ jsxs10(
2299
+ "section",
2300
+ {
2301
+ className: "np-docs-front-recent",
2302
+ "aria-label": "Recently updated docs",
2303
+ children: [
2304
+ /* @__PURE__ */ jsx11("h2", { className: "np-docs-front-recent-eyebrow", children: "Recently updated" }),
2305
+ /* @__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: [
2306
+ /* @__PURE__ */ jsx11("span", { className: "np-docs-front-recent-title", children: node.title }),
2307
+ node.updatedAt ? /* @__PURE__ */ jsx11(
2308
+ "time",
2309
+ {
2310
+ className: "np-docs-front-recent-time",
2311
+ dateTime: node.updatedAt,
2312
+ children: formatRelative(node.updatedAt)
2313
+ }
2314
+ ) : null
2315
+ ] }) }, node.id)) })
2316
+ ]
2317
+ }
2318
+ ) : null
2319
+ ] });
2320
+ }
2321
+ function buildTree2(rows) {
2322
+ return buildDocTree(rows, (r) => {
2323
+ if (typeof r.id !== "string") return null;
2324
+ if (typeof r.slug !== "string") return null;
2325
+ return {
2326
+ id: r.id,
2327
+ slug: r.slug,
2328
+ title: typeof r.title === "string" ? r.title : r.slug,
2329
+ lede: typeof r.lede === "string" ? r.lede : null,
2330
+ parent: typeof r.parent === "string" ? r.parent : null,
2331
+ order: typeof r.order === "number" ? r.order : 0,
2332
+ badge: typeof r.badge === "string" ? r.badge : null,
2333
+ publishedAt: typeof r.publishedAt === "string" ? r.publishedAt : null,
2334
+ updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : null,
2335
+ children: []
2336
+ };
2337
+ });
2338
+ }
2339
+ function recentlyUpdated(rows, limit) {
2340
+ const all = [];
2341
+ for (const r of rows) {
2342
+ if (typeof r.id !== "string" || typeof r.slug !== "string") continue;
2343
+ if (typeof r.parent !== "string") continue;
2344
+ all.push({
2345
+ id: r.id,
2346
+ slug: r.slug,
2347
+ title: typeof r.title === "string" ? r.title : r.slug,
2348
+ lede: null,
2349
+ parent: typeof r.parent === "string" ? r.parent : null,
2350
+ order: 0,
2351
+ badge: typeof r.badge === "string" ? r.badge : null,
2352
+ publishedAt: typeof r.publishedAt === "string" ? r.publishedAt : null,
2353
+ updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : null,
2354
+ children: []
2355
+ });
2356
+ }
2357
+ return all.filter((n) => n.updatedAt !== null).sort((a, b) => {
2358
+ const at = a.updatedAt ?? "";
2359
+ const bt = b.updatedAt ?? "";
2360
+ return bt.localeCompare(at);
2361
+ }).slice(0, limit);
2362
+ }
2363
+ function formatRelative(iso) {
2364
+ try {
2365
+ const then = new Date(iso).getTime();
2366
+ const now = Date.now();
2367
+ const ms = Math.max(now - then, 0);
2368
+ const minute = 60 * 1e3;
2369
+ const hour = 60 * minute;
2370
+ const day = 24 * hour;
2371
+ if (ms < hour) return `${Math.max(1, Math.round(ms / minute)).toString()} min ago`;
2372
+ if (ms < day) return `${Math.round(ms / hour).toString()} h ago`;
2373
+ if (ms < 7 * day) return `${Math.round(ms / day).toString()} d ago`;
2374
+ return new Date(iso).toLocaleDateString(void 0, {
2375
+ month: "short",
2376
+ day: "numeric",
2377
+ year: "numeric"
2378
+ });
2379
+ } catch {
2380
+ return "";
2381
+ }
2382
+ }
2383
+
1505
2384
  // src/index.ts
1506
2385
  var SEED_NAV = {
1507
2386
  header: [
@@ -1516,6 +2395,282 @@ var SEED_NAV = {
1516
2395
  { id: "nav-docs-footer-github", label: "GitHub", type: "link", url: "https://github.com" }
1517
2396
  ]
1518
2397
  };
2398
+ function paragraph(text) {
2399
+ return {
2400
+ type: "paragraph",
2401
+ version: 1,
2402
+ direction: null,
2403
+ format: "",
2404
+ indent: 0,
2405
+ children: [{
2406
+ type: "text",
2407
+ version: 1,
2408
+ detail: 0,
2409
+ format: 0,
2410
+ mode: "normal",
2411
+ style: "",
2412
+ text
2413
+ }]
2414
+ };
2415
+ }
2416
+ function heading(tag, text) {
2417
+ return {
2418
+ type: "heading",
2419
+ tag,
2420
+ version: 1,
2421
+ direction: null,
2422
+ format: "",
2423
+ indent: 0,
2424
+ children: [{
2425
+ type: "text",
2426
+ version: 1,
2427
+ detail: 0,
2428
+ format: 0,
2429
+ mode: "normal",
2430
+ style: "",
2431
+ text
2432
+ }]
2433
+ };
2434
+ }
2435
+ function codeBlock(text, language) {
2436
+ return {
2437
+ type: "code",
2438
+ language: language ?? null,
2439
+ version: 1,
2440
+ direction: null,
2441
+ format: "",
2442
+ indent: 0,
2443
+ children: [{
2444
+ type: "text",
2445
+ version: 1,
2446
+ detail: 0,
2447
+ format: 0,
2448
+ mode: "normal",
2449
+ style: "",
2450
+ text
2451
+ }]
2452
+ };
2453
+ }
2454
+ function listItem(text) {
2455
+ return {
2456
+ type: "listitem",
2457
+ value: 1,
2458
+ version: 1,
2459
+ direction: null,
2460
+ format: "",
2461
+ indent: 0,
2462
+ children: [{
2463
+ type: "text",
2464
+ version: 1,
2465
+ detail: 0,
2466
+ format: 0,
2467
+ mode: "normal",
2468
+ style: "",
2469
+ text
2470
+ }]
2471
+ };
2472
+ }
2473
+ function bulletList(items) {
2474
+ return {
2475
+ type: "list",
2476
+ listType: "bullet",
2477
+ start: 1,
2478
+ tag: "ul",
2479
+ version: 1,
2480
+ direction: null,
2481
+ format: "",
2482
+ indent: 0,
2483
+ children: items.map((t) => listItem(t))
2484
+ };
2485
+ }
2486
+ function lexicalDoc(blocks) {
2487
+ return {
2488
+ root: {
2489
+ type: "root",
2490
+ version: 1,
2491
+ direction: null,
2492
+ format: "",
2493
+ indent: 0,
2494
+ children: blocks
2495
+ }
2496
+ };
2497
+ }
2498
+ var DOCS_NOW = "2026-05-02T12:00:00.000Z";
2499
+ function stubDoc(opts) {
2500
+ return {
2501
+ title: opts.title,
2502
+ excerpt: opts.lede ?? `${opts.title} reference page.`,
2503
+ content: lexicalDoc([
2504
+ 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.`)
2505
+ ]),
2506
+ publishedAt: DOCS_NOW,
2507
+ kind: "doc",
2508
+ ...opts.parentSlug ? { parentSlug: opts.parentSlug } : {},
2509
+ ...typeof opts.order === "number" ? { order: opts.order } : {},
2510
+ data: {
2511
+ ...opts.badge ? { badge: opts.badge } : {},
2512
+ ...opts.lede ? { lede: opts.lede } : {},
2513
+ ...opts.stableSince ? { stableSince: opts.stableSince } : {}
2514
+ }
2515
+ };
2516
+ }
2517
+ var QUICKSTART_BODY = lexicalDoc([
2518
+ paragraph(
2519
+ "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."
2520
+ ),
2521
+ paragraph(
2522
+ "Prerequisites: a running NexPress site (see Install & bootstrap) and Node 20+. The plugin lives inside your app \u2014 no separate workspace required to start."
2523
+ ),
2524
+ heading("h2", "Scaffold the plugin"),
2525
+ paragraph(
2526
+ "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."
2527
+ ),
2528
+ codeBlock("pnpm nexpress plugin:new hello-world", "bash"),
2529
+ paragraph(
2530
+ "The generated module is ~20 lines, ready to run. Open it in your editor before continuing."
2531
+ ),
2532
+ codeBlock(
2533
+ [
2534
+ 'import { definePlugin } from "@nexpress/plugin-sdk";',
2535
+ "",
2536
+ "export default definePlugin({",
2537
+ " manifest: {",
2538
+ ' id: "hello-world",',
2539
+ ' name: "Hello, world",',
2540
+ ' version: "0.1.0",',
2541
+ ' nexpress: { minVersion: "0.1.0" },',
2542
+ " },",
2543
+ " hooks: {",
2544
+ " onDocumentPublished: async ({ doc, collection }) => {",
2545
+ ' if (collection !== "posts") return;',
2546
+ " console.log(`Published: ${doc.title}`);",
2547
+ " },",
2548
+ " },",
2549
+ "});"
2550
+ ].join("\n"),
2551
+ "typescript"
2552
+ ),
2553
+ heading("h2", "Register it with your site"),
2554
+ paragraph(
2555
+ "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."
2556
+ ),
2557
+ codeBlock(
2558
+ [
2559
+ 'import { defineConfig } from "@nexpress/core";',
2560
+ 'import helloWorld from "./plugins/hello-world";',
2561
+ "",
2562
+ "export default defineConfig({",
2563
+ " plugins: [helloWorld()],",
2564
+ "});"
2565
+ ].join("\n"),
2566
+ "typescript"
2567
+ ),
2568
+ paragraph(
2569
+ "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."
2570
+ ),
2571
+ heading("h2", "Lifecycle hooks at a glance"),
2572
+ paragraph(
2573
+ "Hooks are typed callbacks NexPress invokes at well-known points. Each receives a context object scoped to that event. The most commonly used hooks:"
2574
+ ),
2575
+ bulletList([
2576
+ "onBoot \u2014 after config load, before request handling. Context: { config, env }.",
2577
+ "onDocumentPublished (async) \u2014 a document's status transitions to published. Context: { doc, collection, by }.",
2578
+ "onDocumentUnpublished \u2014 status leaves published. Context: { doc, collection, by }.",
2579
+ "onRequest \u2014 every request, after routing, before render. Context: { req, route, user }.",
2580
+ "onSchedule \u2014 cron-like; declare cadence in the manifest. Context: { now, schedule }."
2581
+ ]),
2582
+ paragraph(
2583
+ "The full list \u2014 including admin-surface and search hooks \u2014 lives in the lifecycle hooks reference."
2584
+ ),
2585
+ heading("h2", "Run and verify"),
2586
+ bulletList([
2587
+ "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.",
2588
+ "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.",
2589
+ "Check the dev server log. You should see Published: <title> in the terminal. That's it \u2014 the plugin is live."
2590
+ ]),
2591
+ paragraph(
2592
+ "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."
2593
+ ),
2594
+ heading("h2", "Next steps"),
2595
+ paragraph(
2596
+ "You have a plugin that runs. Two natural directions from here:"
2597
+ ),
2598
+ bulletList([
2599
+ "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.",
2600
+ "Add collections. Plugins can declare their own collections, which the admin surfaces alongside the operator's. See the Plugin manifest reference."
2601
+ ])
2602
+ ]);
2603
+ var SEED_PAGES = [
2604
+ {
2605
+ title: "Documentation",
2606
+ slug: "/",
2607
+ seoDescription: "Install NexPress, learn the core concepts, write plugins, and look up the API.",
2608
+ blocks: [],
2609
+ template: "front"
2610
+ }
2611
+ ];
2612
+ var SEED_DOCS = [
2613
+ stubDoc({
2614
+ title: "Get started",
2615
+ order: 0,
2616
+ lede: "Install NexPress, scaffold a site, and ship a first deploy."
2617
+ }),
2618
+ stubDoc({
2619
+ title: "Introduction",
2620
+ parentSlug: "get-started",
2621
+ order: 0,
2622
+ lede: "What NexPress is, what it isn't, and who it's for.",
2623
+ stableSince: "Stable since 0.1"
2624
+ }),
2625
+ stubDoc({ title: "Install & bootstrap", parentSlug: "get-started", order: 1, stableSince: "Stable since 0.1" }),
2626
+ stubDoc({ title: "Project structure", parentSlug: "get-started", order: 2 }),
2627
+ stubDoc({ title: "Configuration", parentSlug: "get-started", order: 3 }),
2628
+ stubDoc({ title: "Deployment", parentSlug: "get-started", order: 4 }),
2629
+ stubDoc({
2630
+ title: "Core concepts",
2631
+ order: 1,
2632
+ lede: "The model behind collections, themes, plugins, and blocks."
2633
+ }),
2634
+ stubDoc({ title: "Collections", parentSlug: "core-concepts", order: 0, stableSince: "Stable since 0.1" }),
2635
+ stubDoc({ title: "Pages & routing", parentSlug: "core-concepts", order: 1 }),
2636
+ stubDoc({ title: "Themes", parentSlug: "core-concepts", order: 2 }),
2637
+ stubDoc({ title: "Blocks", parentSlug: "core-concepts", order: 3 }),
2638
+ stubDoc({ title: "Hooks & access", parentSlug: "core-concepts", order: 4 }),
2639
+ stubDoc({ title: "Internationalization", parentSlug: "core-concepts", order: 5 }),
2640
+ stubDoc({
2641
+ title: "Plugins",
2642
+ order: 2,
2643
+ lede: "Extend NexPress with hooks, routes, blocks, and scheduled jobs."
2644
+ }),
2645
+ stubDoc({ title: "Plugin overview", parentSlug: "plugins", order: 0 }),
2646
+ {
2647
+ title: "Author quickstart",
2648
+ 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.',
2649
+ content: QUICKSTART_BODY,
2650
+ publishedAt: DOCS_NOW,
2651
+ kind: "doc",
2652
+ parentSlug: "plugins",
2653
+ order: 1,
2654
+ data: {
2655
+ badge: "NEW",
2656
+ 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.',
2657
+ stableSince: "Stable since 0.1"
2658
+ }
2659
+ },
2660
+ stubDoc({ title: "Manifest reference", parentSlug: "plugins", order: 2 }),
2661
+ stubDoc({ title: "Lifecycle hooks", parentSlug: "plugins", order: 3 }),
2662
+ stubDoc({ title: "Publishing", parentSlug: "plugins", order: 4 }),
2663
+ stubDoc({
2664
+ title: "Reference",
2665
+ order: 3,
2666
+ lede: "API surface \u2014 CLI, define* helpers, server functions."
2667
+ }),
2668
+ stubDoc({ title: "CLI", parentSlug: "reference", order: 0 }),
2669
+ stubDoc({ title: "defineCollection", parentSlug: "reference", order: 1, badge: "API" }),
2670
+ stubDoc({ title: "defineTheme", parentSlug: "reference", order: 2 }),
2671
+ stubDoc({ title: "definePlugin", parentSlug: "reference", order: 3 }),
2672
+ stubDoc({ title: "Server functions", parentSlug: "reference", order: 4, badge: "BETA" })
2673
+ ];
1519
2674
  var docsTheme = defineTheme({
1520
2675
  manifest: {
1521
2676
  id: "docs",
@@ -1605,6 +2760,10 @@ var docsTheme = defineTheme({
1605
2760
  colors: {
1606
2761
  primary: "#2563eb",
1607
2762
  primaryForeground: "#ffffff",
2763
+ // 10% tint of primary — equivalent to the color-mix
2764
+ // fallback in styles.ts, but materialised so the
2765
+ // generated theme CSS picks it up explicitly.
2766
+ primarySoft: "rgba(37, 99, 235, 0.1)",
1608
2767
  background: "#fbfcfe",
1609
2768
  foreground: "#0c1320",
1610
2769
  muted: "#f1f4f9",
@@ -1624,9 +2783,18 @@ var docsTheme = defineTheme({
1624
2783
  }
1625
2784
  },
1626
2785
  seedContent: {
1627
- navigation: SEED_NAV
2786
+ navigation: SEED_NAV,
2787
+ pages: SEED_PAGES,
2788
+ posts: SEED_DOCS
1628
2789
  },
1629
2790
  templates: {
2791
+ pages: {
2792
+ front: {
2793
+ label: "Front page",
2794
+ 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.',
2795
+ component: PageFrontTemplate
2796
+ }
2797
+ },
1630
2798
  // Universal-content-model #748 — docs are posts with
1631
2799
  // `kind: "doc"`. The template key matches the kind value so
1632
2800
  // the per-kind template lookup picks this up automatically.
@@ -1665,7 +2833,7 @@ var docsTheme = defineTheme({
1665
2833
  { pattern: "/docs/:slug", component: DocsDetailRoute }
1666
2834
  ],
1667
2835
  navLocations: {
1668
- primary: {
2836
+ header: {
1669
2837
  label: "Primary header nav",
1670
2838
  description: "Inline links beside the masthead search box.",
1671
2839
  maxItems: 5
@@ -1692,10 +2860,12 @@ var docsTheme = defineTheme({
1692
2860
  members: {
1693
2861
  shell: DocsMembersShell,
1694
2862
  notFound: DocsMembersNotFound
1695
- }
2863
+ },
2864
+ blocks: docsBlocks
1696
2865
  }
1697
2866
  });
1698
2867
  export {
2868
+ CopyButton,
1699
2869
  DocPageTemplate,
1700
2870
  DocsHeader,
1701
2871
  DocsMembersNotFound,
@@ -1704,6 +2874,7 @@ export {
1704
2874
  DocsSearch,
1705
2875
  DocsShell,
1706
2876
  DocsSidebar,
2877
+ docsBlocks,
1707
2878
  docsCss,
1708
2879
  docsSettingsSchema,
1709
2880
  docsTheme