@nexpress/theme-docs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,813 @@
1
+ // src/index.ts
2
+ import { defineTheme } from "@nexpress/theme";
3
+
4
+ // src/header.tsx
5
+ import { NavMenu } from "@nexpress/next";
6
+
7
+ // src/settings-helpers.ts
8
+ import { getCachedThemeSettings } from "@nexpress/next";
9
+
10
+ // src/settings.ts
11
+ import { z } from "zod";
12
+ var docsSettingsSchema = z.object({
13
+ version: z.string().default("v1").describe(
14
+ "Currently-displayed version label, shown in the masthead. Update on each release."
15
+ ),
16
+ githubRepo: z.string().url().optional().describe(
17
+ "Optional repository URL \u2014 when set, page templates render an 'Edit on GitHub' link in the prev/next bar."
18
+ ),
19
+ sidebarHeading: z.string().default("Documentation").describe("Heading shown above the hierarchical sidebar nav."),
20
+ showTableOfContents: z.boolean().default(true).describe("Render the in-page TOC sidebar on doc pages."),
21
+ searchPlaceholder: z.string().default("Search the docs\u2026").describe("Placeholder text for the search input in the masthead.")
22
+ });
23
+
24
+ // src/settings-helpers.ts
25
+ async function resolveDocsSettings() {
26
+ const raw = await getCachedThemeSettings("docs");
27
+ const parsed = docsSettingsSchema.safeParse(raw);
28
+ if (parsed.success) return parsed.data;
29
+ return docsSettingsSchema.parse({});
30
+ }
31
+
32
+ // src/header.tsx
33
+ import { jsx, jsxs } from "react/jsx-runtime";
34
+ async function DocsHeader() {
35
+ const settings = await resolveDocsSettings();
36
+ return /* @__PURE__ */ jsx("header", { className: "np-docs-header", children: /* @__PURE__ */ jsxs("div", { className: "np-docs-header-inner", children: [
37
+ /* @__PURE__ */ jsxs("a", { href: "/", className: "np-docs-brand", children: [
38
+ /* @__PURE__ */ jsx("span", { className: "np-docs-brand-name", children: "Docs" }),
39
+ /* @__PURE__ */ jsx("span", { className: "np-docs-brand-version", children: settings.version })
40
+ ] }),
41
+ /* @__PURE__ */ jsx(
42
+ "form",
43
+ {
44
+ action: "/docs/search",
45
+ method: "get",
46
+ className: "np-docs-search-form",
47
+ role: "search",
48
+ children: /* @__PURE__ */ jsx(
49
+ "input",
50
+ {
51
+ type: "search",
52
+ name: "q",
53
+ placeholder: settings.searchPlaceholder,
54
+ className: "np-docs-search-input",
55
+ "aria-label": "Search the docs"
56
+ }
57
+ )
58
+ }
59
+ ),
60
+ /* @__PURE__ */ jsxs("nav", { className: "np-docs-nav", "aria-label": "Primary", children: [
61
+ /* @__PURE__ */ jsx(NavMenu, { location: "primary", className: "np-docs-primary-nav" }),
62
+ settings.githubRepo ? /* @__PURE__ */ jsx(
63
+ "a",
64
+ {
65
+ href: settings.githubRepo,
66
+ className: "np-docs-github-link",
67
+ target: "_blank",
68
+ rel: "noreferrer",
69
+ children: "GitHub"
70
+ }
71
+ ) : null
72
+ ] })
73
+ ] }) });
74
+ }
75
+
76
+ // src/members-not-found.tsx
77
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
78
+ function DocsMembersNotFound() {
79
+ return /* @__PURE__ */ jsxs2(
80
+ "div",
81
+ {
82
+ className: "np-docs-members-not-found",
83
+ style: {
84
+ maxWidth: 520,
85
+ margin: "5rem auto",
86
+ padding: "0 1.5rem"
87
+ },
88
+ children: [
89
+ /* @__PURE__ */ jsx2(
90
+ "p",
91
+ {
92
+ style: {
93
+ margin: 0,
94
+ fontSize: "0.75rem",
95
+ textTransform: "uppercase",
96
+ letterSpacing: "0.12em",
97
+ color: "var(--np-color-muted-foreground)",
98
+ fontFamily: "var(--np-font-mono, ui-monospace, monospace)"
99
+ },
100
+ children: "404 \xB7 account"
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx2(
104
+ "h1",
105
+ {
106
+ style: {
107
+ margin: "0.75rem 0 0",
108
+ fontSize: "1.875rem",
109
+ fontFamily: "var(--np-font-heading)",
110
+ fontWeight: 600,
111
+ lineHeight: 1.2
112
+ },
113
+ children: "That account link is no longer valid."
114
+ }
115
+ ),
116
+ /* @__PURE__ */ jsx2(
117
+ "p",
118
+ {
119
+ style: {
120
+ margin: "1.25rem 0 0",
121
+ color: "var(--np-color-muted-foreground)",
122
+ fontSize: "0.9375rem",
123
+ lineHeight: 1.6
124
+ },
125
+ 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."
126
+ }
127
+ ),
128
+ /* @__PURE__ */ jsx2("p", { style: { margin: "1.75rem 0 0" }, children: /* @__PURE__ */ jsx2(
129
+ "a",
130
+ {
131
+ href: "/members/login",
132
+ style: {
133
+ display: "inline-block",
134
+ padding: "0.5rem 1.25rem",
135
+ borderRadius: "0.375rem",
136
+ background: "var(--np-color-primary)",
137
+ color: "var(--np-color-primary-foreground)",
138
+ textDecoration: "none",
139
+ fontSize: "0.875rem",
140
+ fontWeight: 500
141
+ },
142
+ children: "Go to sign in"
143
+ }
144
+ ) })
145
+ ]
146
+ }
147
+ );
148
+ }
149
+
150
+ // src/members-shell.tsx
151
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
152
+ function DocsMembersShell({ children }) {
153
+ return /* @__PURE__ */ jsxs3("div", { className: "np-docs np-docs-shell", children: [
154
+ /* @__PURE__ */ jsx3(DocsHeader, {}),
155
+ /* @__PURE__ */ jsx3("div", { className: "np-docs-members", children: /* @__PURE__ */ jsx3("div", { className: "np-docs-members-column", children }) })
156
+ ] });
157
+ }
158
+
159
+ // src/not-found.tsx
160
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
161
+ function DocsNotFound() {
162
+ return /* @__PURE__ */ jsxs4(
163
+ "div",
164
+ {
165
+ className: "np-docs-not-found",
166
+ style: {
167
+ maxWidth: 560,
168
+ margin: "5rem auto",
169
+ padding: "0 1.5rem"
170
+ },
171
+ children: [
172
+ /* @__PURE__ */ jsx4(
173
+ "p",
174
+ {
175
+ style: {
176
+ margin: 0,
177
+ fontSize: "0.75rem",
178
+ textTransform: "uppercase",
179
+ letterSpacing: "0.08em",
180
+ color: "var(--np-color-muted-foreground)"
181
+ },
182
+ children: "404 \u2014 Not found"
183
+ }
184
+ ),
185
+ /* @__PURE__ */ jsx4("h1", { style: { margin: "0.75rem 0 0.5rem", fontSize: "1.75rem" }, children: "That page isn't in the docs." }),
186
+ /* @__PURE__ */ jsx4(
187
+ "p",
188
+ {
189
+ style: {
190
+ margin: "0.75rem 0 1.5rem",
191
+ color: "var(--np-color-muted-foreground)"
192
+ },
193
+ children: "It may have been renamed or merged into another section. Try the search bar in the header, or head to the homepage."
194
+ }
195
+ ),
196
+ /* @__PURE__ */ jsx4(
197
+ "a",
198
+ {
199
+ href: "/",
200
+ style: {
201
+ display: "inline-block",
202
+ padding: "0.4rem 1rem",
203
+ borderRadius: "0.375rem",
204
+ background: "var(--np-color-primary)",
205
+ color: "var(--np-color-primary-foreground)",
206
+ textDecoration: "none",
207
+ fontWeight: 500
208
+ },
209
+ children: "Homepage"
210
+ }
211
+ )
212
+ ]
213
+ }
214
+ );
215
+ }
216
+
217
+ // src/routes/doc-detail.tsx
218
+ import { findDocuments as findDocuments2 } from "@nexpress/core";
219
+ import { notFound } from "next/navigation";
220
+
221
+ // src/templates/doc-page.tsx
222
+ import { findDocuments } from "@nexpress/core";
223
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
224
+ async function DocPageTemplate({
225
+ doc: rawDoc
226
+ }) {
227
+ const doc = rawDoc;
228
+ const settings = await resolveDocsSettings();
229
+ const navInfo = await loadPrevNext(doc);
230
+ return /* @__PURE__ */ jsxs5("article", { className: "np-docs-page", children: [
231
+ /* @__PURE__ */ jsx5("header", { children: /* @__PURE__ */ jsx5("h1", { children: doc.title }) }),
232
+ /* @__PURE__ */ jsx5("div", { className: "np-docs-body", children: /* @__PURE__ */ jsx5("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: typeof doc.body === "string" ? doc.body : "Doc body unavailable." }) }),
233
+ settings.githubRepo ? /* @__PURE__ */ jsx5("p", { style: { marginTop: "2rem" }, children: /* @__PURE__ */ jsx5(
234
+ "a",
235
+ {
236
+ href: `${settings.githubRepo}/edit/main/docs/${doc.slug}.md`,
237
+ target: "_blank",
238
+ rel: "noreferrer",
239
+ style: {
240
+ fontSize: "0.875rem",
241
+ color: "var(--np-color-muted-foreground)"
242
+ },
243
+ children: "\u270F\uFE0F Edit on GitHub"
244
+ }
245
+ ) }) : null,
246
+ /* @__PURE__ */ jsxs5("nav", { className: "np-docs-prev-next", "aria-label": "Pagination", children: [
247
+ navInfo.prev ? /* @__PURE__ */ jsxs5("a", { href: `/docs/${navInfo.prev.slug}`, children: [
248
+ /* @__PURE__ */ jsx5("span", { className: "np-docs-prev-next-label", children: "\u2190 Previous" }),
249
+ navInfo.prev.title
250
+ ] }) : /* @__PURE__ */ jsx5("span", {}),
251
+ navInfo.next ? /* @__PURE__ */ jsxs5(
252
+ "a",
253
+ {
254
+ href: `/docs/${navInfo.next.slug}`,
255
+ style: { textAlign: "right" },
256
+ children: [
257
+ /* @__PURE__ */ jsx5("span", { className: "np-docs-prev-next-label", children: "Next \u2192" }),
258
+ navInfo.next.title
259
+ ]
260
+ }
261
+ ) : /* @__PURE__ */ jsx5("span", {})
262
+ ] })
263
+ ] });
264
+ }
265
+ async function loadPrevNext(current) {
266
+ const result = await findDocuments("docs", {
267
+ where: { status: "published" },
268
+ sort: "order",
269
+ limit: 500
270
+ });
271
+ const docs = result.docs;
272
+ const idx = docs.findIndex((d) => d.id === current.id);
273
+ if (idx < 0) return { prev: null, next: null };
274
+ return {
275
+ prev: idx > 0 ? docs[idx - 1] ?? null : null,
276
+ next: idx < docs.length - 1 ? docs[idx + 1] ?? null : null
277
+ };
278
+ }
279
+
280
+ // src/routes/doc-detail.tsx
281
+ import { jsx as jsx6 } from "react/jsx-runtime";
282
+ async function DocsDetailRoute({
283
+ params,
284
+ blockCtx
285
+ }) {
286
+ const slug = typeof params.slug === "string" ? params.slug : "";
287
+ if (!slug) notFound();
288
+ const result = await findDocuments2("docs", {
289
+ where: { slug, status: "published" },
290
+ limit: 1
291
+ });
292
+ const doc = result.docs[0];
293
+ if (!doc) notFound();
294
+ const templateProps = {
295
+ doc,
296
+ blockCtx
297
+ };
298
+ return /* @__PURE__ */ jsx6(DocPageTemplate, { ...templateProps });
299
+ }
300
+
301
+ // src/search.tsx
302
+ import { getCollectionConfig, searchCollections } from "@nexpress/core";
303
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
304
+ function resolveResultUrl(collection, doc) {
305
+ try {
306
+ const config = getCollectionConfig(collection);
307
+ const urlPath = config.seo?.urlPath;
308
+ if (typeof urlPath === "function") {
309
+ const result = urlPath(doc);
310
+ if (typeof result === "string" && result.length > 0) return result;
311
+ }
312
+ } catch {
313
+ }
314
+ const slug = typeof doc.slug === "string" ? doc.slug : "";
315
+ return slug ? `/${collection}/${slug}` : "#";
316
+ }
317
+ async function DocsSearch({
318
+ searchParams
319
+ }) {
320
+ const raw = searchParams.q;
321
+ const query = typeof raw === "string" ? raw.trim() : "";
322
+ if (query.length === 0) {
323
+ return /* @__PURE__ */ jsxs6("div", { className: "np-docs-search", children: [
324
+ /* @__PURE__ */ jsx7("h1", { children: "Search" }),
325
+ /* @__PURE__ */ jsx7("p", { style: { color: "var(--np-color-muted-foreground)" }, children: "Enter a query in the masthead search box to find pages." })
326
+ ] });
327
+ }
328
+ const result = await searchCollections({ q: query, limit: 20 });
329
+ return /* @__PURE__ */ jsxs6("div", { className: "np-docs-search", children: [
330
+ /* @__PURE__ */ jsxs6("h1", { children: [
331
+ "Search results for \u201C",
332
+ query,
333
+ "\u201D"
334
+ ] }),
335
+ 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) => {
336
+ const doc = item.doc;
337
+ const slug = typeof doc.slug === "string" ? doc.slug : null;
338
+ const title = typeof doc.title === "string" ? doc.title : slug ?? "Untitled";
339
+ const url = resolveResultUrl(item.collection, doc);
340
+ return /* @__PURE__ */ jsxs6(
341
+ "li",
342
+ {
343
+ style: {
344
+ padding: "1rem 0",
345
+ borderBottom: "1px solid var(--np-color-border)"
346
+ },
347
+ children: [
348
+ /* @__PURE__ */ jsx7(
349
+ "p",
350
+ {
351
+ style: {
352
+ margin: 0,
353
+ fontSize: "0.75rem",
354
+ textTransform: "uppercase",
355
+ letterSpacing: "0.08em",
356
+ color: "var(--np-color-muted-foreground)"
357
+ },
358
+ children: item.collection
359
+ }
360
+ ),
361
+ /* @__PURE__ */ jsx7("h2", { style: { margin: "0.25rem 0 0.5rem", fontSize: "1.125rem" }, children: /* @__PURE__ */ jsx7(
362
+ "a",
363
+ {
364
+ href: url,
365
+ style: { color: "inherit", textDecoration: "none" },
366
+ children: title
367
+ }
368
+ ) }),
369
+ typeof doc.excerpt === "string" ? /* @__PURE__ */ jsx7(
370
+ "p",
371
+ {
372
+ style: {
373
+ margin: 0,
374
+ color: "var(--np-color-muted-foreground)"
375
+ },
376
+ children: doc.excerpt
377
+ }
378
+ ) : null
379
+ ]
380
+ },
381
+ `${item.collection}:${doc.id ?? i}`
382
+ );
383
+ }) })
384
+ ] });
385
+ }
386
+
387
+ // src/shell.tsx
388
+ import { jsx as jsx8 } from "react/jsx-runtime";
389
+ function DocsShell({ children }) {
390
+ return /* @__PURE__ */ jsx8("div", { className: "np-docs-shell", children: /* @__PURE__ */ jsx8("div", { className: "np-docs-grid", children }) });
391
+ }
392
+
393
+ // src/sidebar.tsx
394
+ import { findDocuments as findDocuments3 } from "@nexpress/core";
395
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
396
+ async function DocsSidebar() {
397
+ const settings = await resolveDocsSettings();
398
+ const result = await findDocuments3("docs", {
399
+ where: { status: "published" },
400
+ sort: "order",
401
+ limit: 500
402
+ });
403
+ const tree = buildTree(result.docs);
404
+ return /* @__PURE__ */ jsxs7("aside", { className: "np-docs-sidebar", "aria-label": "Docs navigation", children: [
405
+ /* @__PURE__ */ jsx9("h2", { children: settings.sidebarHeading }),
406
+ /* @__PURE__ */ jsx9(NavTree, { nodes: tree })
407
+ ] });
408
+ }
409
+ function buildTree(rawDocs) {
410
+ const docs = rawDocs;
411
+ const byId = /* @__PURE__ */ new Map();
412
+ for (const d of docs) {
413
+ if (typeof d.id !== "string") continue;
414
+ if (typeof d.slug !== "string") continue;
415
+ byId.set(d.id, {
416
+ id: d.id,
417
+ slug: d.slug,
418
+ title: typeof d.title === "string" ? d.title : d.slug,
419
+ parent: typeof d.parent === "string" ? d.parent : null,
420
+ order: typeof d.order === "number" ? d.order : 0,
421
+ children: []
422
+ });
423
+ }
424
+ const roots = [];
425
+ for (const node of byId.values()) {
426
+ if (node.parent && byId.has(node.parent)) {
427
+ byId.get(node.parent).children.push(node);
428
+ } else {
429
+ roots.push(node);
430
+ }
431
+ }
432
+ const sortRec = (list) => {
433
+ list.sort((a, b) => a.order - b.order);
434
+ for (const n of list) sortRec(n.children);
435
+ };
436
+ sortRec(roots);
437
+ return roots;
438
+ }
439
+ function NavTree({ nodes }) {
440
+ return /* @__PURE__ */ jsx9("ul", { children: nodes.map((n) => /* @__PURE__ */ jsxs7("li", { children: [
441
+ /* @__PURE__ */ jsx9("a", { href: `/docs/${n.slug}`, children: n.title }),
442
+ n.children.length > 0 ? /* @__PURE__ */ jsx9(NavTree, { nodes: n.children }) : null
443
+ ] }, n.id)) });
444
+ }
445
+
446
+ // src/styles.ts
447
+ var docsCss = `
448
+ .np-docs-shell {
449
+ display: flex;
450
+ flex-direction: column;
451
+ min-height: 100vh;
452
+ background: var(--np-color-background);
453
+ color: var(--np-color-foreground);
454
+ font-family: var(--np-font-body, system-ui, sans-serif);
455
+ }
456
+
457
+ .np-docs-header {
458
+ position: sticky;
459
+ top: 0;
460
+ z-index: 50;
461
+ background: var(--np-color-background);
462
+ border-bottom: 1px solid var(--np-color-border);
463
+ backdrop-filter: blur(8px);
464
+ }
465
+
466
+ .np-docs-header-inner {
467
+ max-width: 1200px;
468
+ margin: 0 auto;
469
+ padding: 0.75rem 1.5rem;
470
+ display: grid;
471
+ grid-template-columns: auto 1fr auto;
472
+ gap: 1.5rem;
473
+ align-items: center;
474
+ }
475
+
476
+ .np-docs-brand {
477
+ display: flex;
478
+ align-items: baseline;
479
+ gap: 0.5rem;
480
+ font-weight: 600;
481
+ text-decoration: none;
482
+ color: var(--np-color-foreground);
483
+ }
484
+
485
+ .np-docs-brand-version {
486
+ font-size: 0.75rem;
487
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
488
+ color: var(--np-color-muted-foreground);
489
+ background: var(--np-color-muted);
490
+ padding: 0.125rem 0.375rem;
491
+ border-radius: 0.25rem;
492
+ }
493
+
494
+ .np-docs-search-form {
495
+ flex: 1;
496
+ }
497
+
498
+ .np-docs-search-input {
499
+ width: 100%;
500
+ padding: 0.4rem 0.75rem;
501
+ border: 1px solid var(--np-color-border);
502
+ border-radius: 0.375rem;
503
+ background: var(--np-color-card);
504
+ color: var(--np-color-foreground);
505
+ font-size: 0.875rem;
506
+ }
507
+
508
+ .np-docs-search-input:focus {
509
+ outline: 2px solid var(--np-color-primary);
510
+ outline-offset: -2px;
511
+ border-color: transparent;
512
+ }
513
+
514
+ .np-docs-nav {
515
+ display: flex;
516
+ align-items: center;
517
+ gap: 1rem;
518
+ }
519
+
520
+ .np-docs-primary-nav {
521
+ display: flex;
522
+ list-style: none;
523
+ margin: 0;
524
+ padding: 0;
525
+ gap: 1rem;
526
+ }
527
+
528
+ .np-docs-primary-nav a {
529
+ color: var(--np-color-muted-foreground);
530
+ text-decoration: none;
531
+ font-size: 0.875rem;
532
+ }
533
+
534
+ .np-docs-primary-nav a:hover {
535
+ color: var(--np-color-foreground);
536
+ }
537
+
538
+ .np-docs-github-link {
539
+ font-size: 0.875rem;
540
+ color: var(--np-color-muted-foreground);
541
+ text-decoration: none;
542
+ }
543
+
544
+ .np-docs-grid {
545
+ flex: 1;
546
+ display: grid;
547
+ grid-template-columns: 240px minmax(0, 1fr);
548
+ max-width: 1200px;
549
+ margin: 0 auto;
550
+ width: 100%;
551
+ gap: 2.5rem;
552
+ padding: 2rem 1.5rem;
553
+ }
554
+
555
+ @media (max-width: 768px) {
556
+ .np-docs-grid {
557
+ grid-template-columns: 1fr;
558
+ }
559
+ .np-docs-sidebar {
560
+ display: none;
561
+ }
562
+ }
563
+
564
+ .np-docs-sidebar {
565
+ position: sticky;
566
+ top: 4rem;
567
+ align-self: start;
568
+ max-height: calc(100vh - 5rem);
569
+ overflow-y: auto;
570
+ }
571
+
572
+ .np-docs-sidebar h2 {
573
+ font-size: 0.75rem;
574
+ text-transform: uppercase;
575
+ letter-spacing: 0.08em;
576
+ color: var(--np-color-muted-foreground);
577
+ margin: 0 0 0.75rem;
578
+ }
579
+
580
+ .np-docs-sidebar ul {
581
+ list-style: none;
582
+ padding: 0;
583
+ margin: 0;
584
+ }
585
+
586
+ .np-docs-sidebar li {
587
+ margin: 0.125rem 0;
588
+ }
589
+
590
+ .np-docs-sidebar a {
591
+ display: block;
592
+ padding: 0.25rem 0.5rem;
593
+ border-radius: 0.25rem;
594
+ color: var(--np-color-muted-foreground);
595
+ text-decoration: none;
596
+ font-size: 0.875rem;
597
+ }
598
+
599
+ .np-docs-sidebar a:hover {
600
+ background: var(--np-color-muted);
601
+ color: var(--np-color-foreground);
602
+ }
603
+
604
+ .np-docs-sidebar a[data-current="true"] {
605
+ background: color-mix(in oklch, var(--np-color-primary) 12%, transparent);
606
+ color: var(--np-color-primary);
607
+ font-weight: 500;
608
+ }
609
+
610
+ .np-docs-sidebar ul ul {
611
+ margin-left: 0.75rem;
612
+ border-left: 1px solid var(--np-color-border);
613
+ padding-left: 0.5rem;
614
+ }
615
+
616
+ .np-docs-page {
617
+ max-width: 720px;
618
+ }
619
+
620
+ .np-docs-page h1 {
621
+ font-size: 2rem;
622
+ margin: 0 0 0.5rem;
623
+ }
624
+
625
+ .np-docs-prev-next {
626
+ display: grid;
627
+ grid-template-columns: 1fr 1fr;
628
+ gap: 1rem;
629
+ margin-top: 3rem;
630
+ padding-top: 1.5rem;
631
+ border-top: 1px solid var(--np-color-border);
632
+ }
633
+
634
+ .np-docs-prev-next a {
635
+ display: block;
636
+ padding: 0.75rem 1rem;
637
+ border: 1px solid var(--np-color-border);
638
+ border-radius: 0.5rem;
639
+ color: var(--np-color-foreground);
640
+ text-decoration: none;
641
+ }
642
+
643
+ .np-docs-prev-next a:hover {
644
+ border-color: var(--np-color-primary);
645
+ }
646
+
647
+ .np-docs-prev-next-label {
648
+ display: block;
649
+ font-size: 0.75rem;
650
+ color: var(--np-color-muted-foreground);
651
+ margin-bottom: 0.25rem;
652
+ }
653
+
654
+ /* M.* member surface \u2014 narrow auth-form column under the
655
+ masthead, no sidebar (the docs sidebar is hierarchical
656
+ doc nav, useless on auth forms). */
657
+ .np-docs-members {
658
+ display: flex;
659
+ justify-content: center;
660
+ min-height: 60vh;
661
+ padding: 3rem 1.5rem;
662
+ }
663
+ .np-docs-members-column {
664
+ width: 100%;
665
+ max-width: 440px;
666
+ }
667
+
668
+ /* Member form token overrides \u2014 docs aesthetic: slightly
669
+ rounded corners, neutral palette, monospace label accent. */
670
+ .np-docs .np-members-form {
671
+ --np-member-form-input-bg: var(--np-color-background);
672
+ --np-member-form-input-border: var(--np-color-border);
673
+ --np-member-form-input-border-focus: var(--np-color-primary);
674
+ --np-member-form-input-radius: 0.375rem;
675
+ --np-member-form-button-radius: 0.375rem;
676
+ }
677
+ .np-docs .np-members-form .np-form-label {
678
+ font-family: var(--np-font-mono, ui-monospace, monospace);
679
+ font-size: 0.8125rem;
680
+ }
681
+ `;
682
+
683
+ // src/index.ts
684
+ var docsTheme = defineTheme({
685
+ manifest: {
686
+ id: "docs",
687
+ name: "Docs",
688
+ version: "0.1.0",
689
+ description: "Documentation theme \u2014 hierarchical sidebar, prev/next nav, search masthead. Pairs with a `docs` collection that has parent/order fields.",
690
+ author: { name: "NexPress" },
691
+ nexpress: { minVersion: "0.1.0" },
692
+ requires: {
693
+ collections: {
694
+ docs: {
695
+ createIfAbsent: true,
696
+ fields: {
697
+ title: { type: "text", required: true },
698
+ body: { type: "richText" },
699
+ parent: {
700
+ type: "relationship",
701
+ relationTo: "docs",
702
+ hard: false
703
+ },
704
+ order: { type: "number" }
705
+ }
706
+ }
707
+ }
708
+ },
709
+ settingsSchema: docsSettingsSchema
710
+ },
711
+ impl: {
712
+ shell: DocsShell,
713
+ slots: {
714
+ header: DocsHeader,
715
+ sidebar: DocsSidebar
716
+ },
717
+ css: docsCss,
718
+ tokens: {
719
+ // Docs lean cool/neutral with a sharp accent — distinct
720
+ // from magazine's warm cream so a side-by-side preview
721
+ // makes the swap obvious.
722
+ colors: {
723
+ primary: "oklch(0.55 0.18 260)",
724
+ primaryForeground: "oklch(0.985 0.005 260)",
725
+ background: "oklch(0.99 0.005 260)",
726
+ foreground: "oklch(0.18 0.025 260)",
727
+ muted: "oklch(0.95 0.012 260)",
728
+ mutedForeground: "oklch(0.5 0.025 260)",
729
+ border: "oklch(0.9 0.012 260)",
730
+ card: "oklch(0.985 0.008 260)",
731
+ cardForeground: "oklch(0.18 0.025 260)",
732
+ accent: "oklch(0.92 0.05 260)",
733
+ accentForeground: "oklch(0.18 0.025 260)"
734
+ }
735
+ },
736
+ templates: {
737
+ docs: {
738
+ default: {
739
+ label: "Doc page",
740
+ description: "Hierarchical sidebar + body + prev/next nav. Optional 'Edit on GitHub' link when settings.githubRepo is set.",
741
+ component: DocPageTemplate
742
+ }
743
+ }
744
+ },
745
+ routes: [
746
+ // F.2 — docs theme's scoped search route. Lives at
747
+ // `/docs/search` rather than `/search` (#609): the host's
748
+ // reference app has an app-explicit `/search` page route
749
+ // that takes precedence over theme routes per the locked
750
+ // dispatch order (app file > page > theme > plugin). The
751
+ // theme can't override the universal search page, so it
752
+ // scopes its own search to a `/docs/*` namespace and the
753
+ // operator gets both routes: framework `/search` + docs
754
+ // theme `/docs/search`.
755
+ //
756
+ // Order matters: search comes first so `/docs/search` is
757
+ // matched as a literal rather than `{ slug: "search" }`
758
+ // by the parametric detail route below (dispatcher is
759
+ // first-match-wins).
760
+ { pattern: "/docs/search", component: DocsSearch },
761
+ // Doc detail dispatch (#614). The sidebar + template emit
762
+ // `/docs/<slug>` links; without this route those 404 in
763
+ // the reference app — the catch-all only resolves `pages`
764
+ // rows, not arbitrary `docs` collection rows. The
765
+ // component looks up the docs row by slug and renders
766
+ // through `templates.docs.default` (DocPageTemplate).
767
+ { pattern: "/docs/:slug", component: DocsDetailRoute }
768
+ ],
769
+ navLocations: {
770
+ primary: {
771
+ label: "Primary header nav",
772
+ description: "Inline links beside the masthead search box.",
773
+ maxItems: 5
774
+ }
775
+ },
776
+ notFound: DocsNotFound,
777
+ // M.* adoption (2026-05-11). Docs gains purpose-built member
778
+ // chrome: drops the docs sidebar (hierarchical doc nav is
779
+ // useless on auth forms), keeps the masthead, narrows the
780
+ // content column. Without this, the fallback chain would
781
+ // walk back to `impl.shell` (the 3-column grid) and the
782
+ // sidebar slot would surface alongside an auth form.
783
+ // - `shell`: DocsMembersShell (header + narrow column, no
784
+ // sidebar).
785
+ // - `notFound`: DocsMembersNotFound (stale-auth-link framing
786
+ // with /members/login CTA, monospace accent matching the
787
+ // theme).
788
+ // - `error`: forward-compat type marker; the actual render
789
+ // goes through `./components/members-error`'s client
790
+ // subpath, lazy-imported by
791
+ // `apps/web/src/app/(member)/error.tsx`'s registry
792
+ // (F.7.1 delegation — Next mandates `error.tsx` is "use
793
+ // client").
794
+ members: {
795
+ shell: DocsMembersShell,
796
+ notFound: DocsMembersNotFound
797
+ }
798
+ }
799
+ });
800
+ export {
801
+ DocPageTemplate,
802
+ DocsHeader,
803
+ DocsMembersNotFound,
804
+ DocsMembersShell,
805
+ DocsNotFound,
806
+ DocsSearch,
807
+ DocsShell,
808
+ DocsSidebar,
809
+ docsCss,
810
+ docsSettingsSchema,
811
+ docsTheme
812
+ };
813
+ //# sourceMappingURL=index.js.map