@nanisoft/prism-llms 0.4.0 → 0.5.1

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.
Files changed (175) hide show
  1. package/README.md +88 -0
  2. package/THIRD-PARTY-NOTICES.md +48 -0
  3. package/dist/data.json +4037 -386
  4. package/dist/demo-graph.d.ts +21 -12
  5. package/dist/demo-graph.js +37 -21
  6. package/dist/extractor.d.ts +24 -17
  7. package/dist/extractor.js +299 -156
  8. package/dist/index.d.ts +12 -7
  9. package/dist/index.js +9 -7
  10. package/dist/llms-full.txt +8248 -2013
  11. package/dist/llms.txt +98 -93
  12. package/dist/markdown.d.ts +69 -23
  13. package/dist/markdown.js +131 -72
  14. package/dist/md/blocks/app-shell-01.md +135 -0
  15. package/dist/md/blocks/auth-form-01.md +135 -0
  16. package/dist/md/blocks/cta-01.md +88 -0
  17. package/dist/md/blocks/data-table-01.md +196 -0
  18. package/dist/md/blocks/feature-grid-01.md +92 -0
  19. package/dist/md/blocks/hero-01.md +90 -0
  20. package/dist/md/blocks/instrument-panel-01.md +109 -0
  21. package/dist/md/blocks/logo-strip-01.md +97 -0
  22. package/dist/md/blocks/note-grid-01.md +98 -0
  23. package/dist/md/blocks/page-header-01.md +97 -0
  24. package/dist/md/blocks/pricing-01.md +91 -0
  25. package/dist/md/blocks/process-rail-01.md +106 -0
  26. package/dist/md/blocks/product-grid-01.md +107 -0
  27. package/dist/md/blocks/settings-panel-01.md +146 -0
  28. package/dist/md/blocks/site-footer.md +103 -11
  29. package/dist/md/blocks/site-header.md +107 -11
  30. package/dist/md/blocks/stack-grid-01.md +108 -0
  31. package/dist/md/blocks/stats-01.md +89 -0
  32. package/dist/md/blocks/status-ledger-01.md +121 -0
  33. package/dist/md/changelogs/prism-llms.md +244 -0
  34. package/dist/md/changelogs/prism-mcp-server.md +95 -0
  35. package/dist/md/changelogs/prism-tokens.md +126 -0
  36. package/dist/md/changelogs/prism-ui.md +467 -0
  37. package/dist/md/components/accordion.md +108 -0
  38. package/dist/md/components/alert.md +90 -7
  39. package/dist/md/components/avatar.md +89 -7
  40. package/dist/md/components/badge.md +74 -7
  41. package/dist/md/components/breadcrumb.md +84 -7
  42. package/dist/md/components/button.md +90 -7
  43. package/dist/md/components/card.md +90 -7
  44. package/dist/md/components/checkbox.md +101 -7
  45. package/dist/md/components/cta-link.md +98 -0
  46. package/dist/md/components/diagram.md +124 -0
  47. package/dist/md/components/dialog.md +128 -0
  48. package/dist/md/components/dropdown-menu.md +125 -0
  49. package/dist/md/components/fact-list.md +85 -0
  50. package/dist/md/components/field.md +116 -0
  51. package/dist/md/components/input.md +86 -7
  52. package/dist/md/components/kbd.md +77 -0
  53. package/dist/md/components/pagination.md +95 -7
  54. package/dist/md/components/popover.md +95 -7
  55. package/dist/md/components/product-mark.md +114 -0
  56. package/dist/md/components/product-switcher.md +88 -0
  57. package/dist/md/components/progress.md +88 -7
  58. package/dist/md/components/prose.md +91 -0
  59. package/dist/md/components/radio-group.md +111 -0
  60. package/dist/md/components/section.md +91 -0
  61. package/dist/md/components/select.md +143 -7
  62. package/dist/md/components/separator.md +77 -0
  63. package/dist/md/components/skeleton.md +71 -7
  64. package/dist/md/components/slider.md +90 -7
  65. package/dist/md/components/switch.md +104 -7
  66. package/dist/md/components/table.md +104 -7
  67. package/dist/md/components/tabs.md +105 -7
  68. package/dist/md/components/textarea.md +101 -0
  69. package/dist/md/components/tooltip.md +95 -7
  70. package/dist/md/components/typography.md +82 -7
  71. package/dist/md/content/about-content.md +36 -0
  72. package/dist/md/content/audience.md +43 -0
  73. package/dist/md/content/glossary.md +69 -0
  74. package/dist/md/content/product-vocabulary.md +53 -0
  75. package/dist/md/content/voice.md +52 -0
  76. package/dist/md/content/writing-mechanics.md +58 -0
  77. package/dist/md/foundation/colors.md +10 -0
  78. package/dist/md/foundation/iconography.md +14 -0
  79. package/dist/md/foundation/motion.md +10 -0
  80. package/dist/md/foundation/radii.md +9 -0
  81. package/dist/md/foundation/shadows.md +9 -0
  82. package/dist/md/foundation/spacings.md +8 -0
  83. package/dist/md/foundation/typography.md +9 -0
  84. package/dist/md/foundation/variables.md +9 -0
  85. package/dist/md/overview/architecture.md +76 -0
  86. package/dist/md/overview/brand.md +41 -0
  87. package/dist/md/overview/composition.md +77 -0
  88. package/dist/md/overview/quickstart.md +97 -0
  89. package/dist/md/overview/theming.md +89 -0
  90. package/dist/md/overview/using-llms.md +83 -0
  91. package/dist/md/pages/auth-page.md +149 -0
  92. package/dist/md/pages/blog-post-page.md +136 -0
  93. package/dist/md/pages/dashboard-page.md +241 -0
  94. package/dist/md/pages/docs-shell.md +210 -15
  95. package/dist/md/pages/marketing-page.md +199 -0
  96. package/dist/md/pages/not-found-page.md +105 -0
  97. package/dist/md/pages/settings-page.md +206 -0
  98. package/dist/prism-skill.md +50 -0
  99. package/dist/store.d.ts +203 -0
  100. package/dist/store.js +317 -0
  101. package/package.json +29 -26
  102. package/dist/demo-graph.d.ts.map +0 -1
  103. package/dist/demo-graph.js.map +0 -1
  104. package/dist/extractor.d.ts.map +0 -1
  105. package/dist/extractor.js.map +0 -1
  106. package/dist/index.d.ts.map +0 -1
  107. package/dist/index.js.map +0 -1
  108. package/dist/markdown.d.ts.map +0 -1
  109. package/dist/markdown.js.map +0 -1
  110. package/dist/md/blocks/component-demo.md +0 -19
  111. package/dist/md/blocks/page-header.md +0 -19
  112. package/dist/md/components/affix.md +0 -14
  113. package/dist/md/components/anchor.md +0 -14
  114. package/dist/md/components/auto-complete.md +0 -14
  115. package/dist/md/components/back-top.md +0 -14
  116. package/dist/md/components/border-beam.md +0 -14
  117. package/dist/md/components/calendar.md +0 -14
  118. package/dist/md/components/carousel.md +0 -14
  119. package/dist/md/components/cascader.md +0 -14
  120. package/dist/md/components/col.md +0 -14
  121. package/dist/md/components/collapse.md +0 -14
  122. package/dist/md/components/color-picker.md +0 -14
  123. package/dist/md/components/date-picker.md +0 -14
  124. package/dist/md/components/descriptions.md +0 -14
  125. package/dist/md/components/display-title.md +0 -20
  126. package/dist/md/components/divider.md +0 -14
  127. package/dist/md/components/drawer.md +0 -14
  128. package/dist/md/components/dropdown.md +0 -14
  129. package/dist/md/components/empty.md +0 -14
  130. package/dist/md/components/flex.md +0 -14
  131. package/dist/md/components/float-button.md +0 -14
  132. package/dist/md/components/form.md +0 -14
  133. package/dist/md/components/grid.md +0 -14
  134. package/dist/md/components/image.md +0 -14
  135. package/dist/md/components/input-number.md +0 -14
  136. package/dist/md/components/layout.md +0 -14
  137. package/dist/md/components/list.md +0 -14
  138. package/dist/md/components/listy.md +0 -14
  139. package/dist/md/components/masonry.md +0 -14
  140. package/dist/md/components/mentions.md +0 -14
  141. package/dist/md/components/menu.md +0 -14
  142. package/dist/md/components/modal.md +0 -14
  143. package/dist/md/components/popconfirm.md +0 -14
  144. package/dist/md/components/qrcode.md +0 -14
  145. package/dist/md/components/radio.md +0 -14
  146. package/dist/md/components/rate.md +0 -14
  147. package/dist/md/components/result.md +0 -14
  148. package/dist/md/components/row.md +0 -14
  149. package/dist/md/components/segmented.md +0 -14
  150. package/dist/md/components/space.md +0 -14
  151. package/dist/md/components/spin.md +0 -14
  152. package/dist/md/components/splitter.md +0 -14
  153. package/dist/md/components/statistic.md +0 -14
  154. package/dist/md/components/steps.md +0 -14
  155. package/dist/md/components/tag.md +0 -14
  156. package/dist/md/components/time-picker.md +0 -14
  157. package/dist/md/components/timeline.md +0 -14
  158. package/dist/md/components/tour.md +0 -14
  159. package/dist/md/components/transfer.md +0 -14
  160. package/dist/md/components/tree-select.md +0 -14
  161. package/dist/md/components/tree.md +0 -14
  162. package/dist/md/components/upload.md +0 -14
  163. package/dist/md/components/watermark.md +0 -14
  164. package/dist/md/docs/brand.md +0 -41
  165. package/dist/md/pages/blog-layout.md +0 -19
  166. package/dist/md/theme/blue-dark.md +0 -127
  167. package/dist/md/theme/blue-light.md +0 -129
  168. package/dist/md/theme/green-dark.md +0 -127
  169. package/dist/md/theme/green-light.md +0 -129
  170. package/dist/md/theme/lavender-dark.md +0 -127
  171. package/dist/md/theme/lavender-light.md +0 -129
  172. package/dist/md/theme/peach-dark.md +0 -127
  173. package/dist/md/theme/peach-light.md +0 -129
  174. package/dist/md/theme/rose-dark.md +0 -127
  175. package/dist/md/theme/rose-light.md +0 -129
@@ -0,0 +1,241 @@
1
+ # DashboardPage
2
+
3
+ A full dashboard screen composing the app shell, a page header, a KPI row and a data table.
4
+
5
+ ```tsx
6
+ import { DashboardPage } from '@nanisoft/prism-ui/pages/dashboard-page'
7
+ ```
8
+
9
+ ## Overview
10
+
11
+ DashboardPage is a complete application screen. It composes the shell, a page
12
+ header, a row of key figures and a data table, and it owns the sequence and the
13
+ spacing only. The frame comes from the shell, the screen's name from the header,
14
+ the summary from the stats and the working set from the table.
15
+
16
+ The page composes these blocks:
17
+
18
+ | Region | Block | Role |
19
+ | --- | --- | --- |
20
+ | Frame | AppShell01 | the navigation rail, the top bar and the mobile navigation sheet |
21
+ | Header | PageHeader01 | the trail, the page title and the page actions |
22
+ | Summary | Stats01 | the key figures above the table |
23
+ | Working set | DataTable01 | the toolbar, filters, selection and pagination |
24
+
25
+ Navigation, rows, columns, labels and every callback are props. The page fetches
26
+ nothing and imports no router, so the consumer owns the links and the data. The
27
+ table's search, page, filters and selection are controlled by the consumer, so
28
+ the same page can be driven by a server render or a client cache.
29
+
30
+ ## Guidelines
31
+
32
+ ### When to use
33
+
34
+ Use DashboardPage for the landing screen of an application, where a reader checks
35
+ the state of a workspace and acts on a collection. It works when the navigation
36
+ is stable across screens and the table is the reason the reader came.
37
+
38
+ ### When not to use
39
+
40
+ Do not use it for a single-purpose screen with no collection. A dashboard with an
41
+ empty table is a header and a stat row around nothing; compose the header and the
42
+ stats directly instead.
43
+
44
+ Do not use it for a read-only report. The table's toolbar promises search,
45
+ filters and selection; a screen that does not offer them should not render the
46
+ toolbar.
47
+
48
+ ### Best practices
49
+
50
+ - Pass the same navigation node to both the rail and the sheet. AppShell01 renders
51
+ it twice, so keep ids in the navigation unique or avoid ids there.
52
+ - Keep the page's own controls in the header, not the table toolbar. The header
53
+ action belongs to the screen; the toolbar belongs to the rows.
54
+ - Own the table state at the call site. Pass `page`, `searchValue`, `filterValues`
55
+ and `selectedIds`, and reset the page to one when the search or a filter
56
+ changes, so the view and the data never disagree.
57
+ - Give every row a stable `getRowId`. An index-based id loses the selection when
58
+ the page changes.
59
+ - Pass every string the table needs in `labels`. The page supplies none of them.
60
+
61
+ ### Content guidelines
62
+
63
+ - The title names the screen: "Overview", not "View overview".
64
+ - A stat label is a noun phrase in sentence case, and the figure keeps one unit.
65
+ - A table column header is a short noun and a row action is a verb phrase.
66
+
67
+ **The dashboard page, with the shell, a header, a KPI row and a driven table.**
68
+
69
+ ```tsx
70
+ 'use client'
71
+
72
+ import { useState } from 'react'
73
+
74
+ import { DashboardPage } from '@nanisoft/prism-ui/pages/dashboard-page'
75
+
76
+ const LINKS = [
77
+ { href: '#dashboard', label: 'Dashboard', current: true },
78
+ { href: '#projects', label: 'Projects', current: false },
79
+ { href: '#team', label: 'Team', current: false },
80
+ ]
81
+
82
+ const ACCOUNTS = [
83
+ { id: 'northwind', name: 'Northwind', plan: 'Team', status: 'Active', seats: '24' },
84
+ { id: 'contoso', name: 'Contoso', plan: 'Business', status: 'Active', seats: '86' },
85
+ { id: 'fabrikam', name: 'Fabrikam', plan: 'Team', status: 'Paused', seats: '12' },
86
+ { id: 'adventure', name: 'Adventure Works', plan: 'Starter', status: 'Active', seats: '4' },
87
+ { id: 'litware', name: 'Litware', plan: 'Business', status: 'Active', seats: '140' },
88
+ { id: 'proseware', name: 'Proseware', plan: 'Starter', status: 'Paused', seats: '7' },
89
+ ]
90
+
91
+ const PAGE_SIZE = 3
92
+
93
+ /** The dashboard page, with the shell, a header, a KPI row and a driven table. */
94
+ export default function DashboardPageDemo() {
95
+ const [query, setQuery] = useState('')
96
+ const [filters, setFilters] = useState<Record<string, string | undefined>>({})
97
+ const [selected, setSelected] = useState<string[]>([])
98
+ const [page, setPage] = useState(1)
99
+
100
+ const filtered = ACCOUNTS.filter((row) => {
101
+ const matchesQuery = row.name.toLowerCase().includes(query.toLowerCase())
102
+ const matchesStatus = !filters.status || row.status === filters.status
103
+ return matchesQuery && matchesStatus
104
+ })
105
+ const pageCount = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE))
106
+ const pageRows = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)
107
+
108
+ return (
109
+ <DashboardPage
110
+ shell={{
111
+ navigationLabel: 'Primary',
112
+ brand: <span className="text-sm font-semibold">Northwind</span>,
113
+ breadcrumbs: [{ label: 'Dashboard', href: '#dashboard' }, { label: 'Overview' }],
114
+ actions: (
115
+ <span className="bg-muted text-muted-foreground rounded-full px-3 py-1 text-xs font-medium">
116
+ AR
117
+ </span>
118
+ ),
119
+ navigation: (
120
+ <ul className="flex flex-col gap-1 text-sm">
121
+ {LINKS.map((link) => (
122
+ <li key={link.href}>
123
+ <a
124
+ href={link.href}
125
+ className={
126
+ link.current
127
+ ? 'bg-accent text-accent-foreground block rounded-md px-3 py-2 font-medium'
128
+ : 'text-muted-foreground hover:text-foreground block rounded-md px-3 py-2'
129
+ }
130
+ >
131
+ {link.label}
132
+ </a>
133
+ </li>
134
+ ))}
135
+ </ul>
136
+ ),
137
+ }}
138
+ header={{
139
+ breadcrumbs: [{ label: 'Dashboard', href: '#dashboard' }, { label: 'Overview' }],
140
+ title: 'Overview',
141
+ description: 'Accounts, usage and the people on this workspace.',
142
+ actions: [{ label: 'New account' }],
143
+ }}
144
+ stats={{
145
+ eyebrow: 'This month',
146
+ title: 'Delivery',
147
+ headingLevel: 'h2',
148
+ stats: [
149
+ { label: 'Active accounts', value: '1,284', delta: 12, hint: 'vs last month' },
150
+ { label: 'On track', value: '47.2%', delta: -3, hint: 'vs last month' },
151
+ { label: 'Open issues', value: '138' },
152
+ ],
153
+ }}
154
+ table={{
155
+ title: 'Accounts',
156
+ description: 'Every workspace you can manage.',
157
+ columns: [
158
+ { id: 'name', header: 'Account', label: 'Account', cell: (row) => String(row.name) },
159
+ { id: 'plan', header: 'Plan', label: 'Plan', cell: (row) => String(row.plan) },
160
+ { id: 'status', header: 'Status', label: 'Status', cell: (row) => String(row.status) },
161
+ {
162
+ id: 'seats',
163
+ header: 'Seats',
164
+ label: 'Seats',
165
+ className: 'text-right',
166
+ cell: (row) => String(row.seats),
167
+ },
168
+ ],
169
+ rows: pageRows,
170
+ getRowId: (row) => String(row.id),
171
+ selectable: true,
172
+ selectedIds: selected,
173
+ onSelectedIdsChange: setSelected,
174
+ searchValue: query,
175
+ onSearchChange: (value) => {
176
+ setQuery(value)
177
+ setPage(1)
178
+ },
179
+ filters: [
180
+ {
181
+ id: 'status',
182
+ label: 'Status',
183
+ options: [
184
+ { label: 'Active', value: 'Active' },
185
+ { label: 'Paused', value: 'Paused' },
186
+ ],
187
+ },
188
+ ],
189
+ filterValues: filters,
190
+ onFilterChange: (id, value) => {
191
+ setFilters((previous) => ({ ...previous, [id]: value }))
192
+ setPage(1)
193
+ },
194
+ page,
195
+ pageCount,
196
+ onPageChange: setPage,
197
+ rowActions: () => [
198
+ { label: 'View details' },
199
+ { label: 'Rename' },
200
+ { label: 'Delete', variant: 'destructive' },
201
+ ],
202
+ labels: {
203
+ search: 'Search accounts',
204
+ filters: 'Filters',
205
+ reset: 'Reset filters',
206
+ columns: 'Columns',
207
+ viewColumns: 'View',
208
+ selectAll: 'Select every account on this page',
209
+ selectRow: 'Select this account',
210
+ rowActions: 'Account actions',
211
+ previous: 'Previous',
212
+ next: 'Next',
213
+ page: (value) => `Go to page ${value}`,
214
+ selectedCount: (count) => `${count} selected`,
215
+ empty: 'No accounts match the current search and filters.',
216
+ },
217
+ }}
218
+ />
219
+ )
220
+ }
221
+ ```
222
+
223
+ ## Props
224
+
225
+ **`shell`** `Omit<ComponentProps<typeof AppShell01>, 'children'>` · required · default: — — The frame: the navigation rail, the top bar and the mobile navigation sheet. Navigation, actions and the rail slots are passed as nodes, so the page never imports a router.
226
+ **`header`** `ComponentProps<typeof PageHeader01>` · required · default: — — The page title band.
227
+ **`stats`** `ComponentProps<typeof Stats01>` · required · default: — — The key-figure row above the table.
228
+ **`table`** `ComponentProps<typeof DataTable01>` · required · default: — — The main table, with its rows, columns, labels and callbacks.
229
+
230
+ ## Composition
231
+
232
+ **`exports`** `DashboardPage` — the public runtime names this Page promises
233
+ **`source`** `packages/ui/src/pages/dashboard-page/index.tsx` — the file this Page is defined in
234
+ **`dependencies`** `AppShell01, DataTable01, PageHeader01, Stats01` — the public Prism exports this item composes
235
+
236
+ ## Blocks
237
+
238
+ - [AppShell01](https://prism.nanisoft.com/blocks/app-shell-01.md)
239
+ - [DataTable01](https://prism.nanisoft.com/blocks/data-table-01.md)
240
+ - [PageHeader01](https://prism.nanisoft.com/blocks/page-header-01.md)
241
+ - [Stats01](https://prism.nanisoft.com/blocks/stats-01.md)
@@ -1,23 +1,218 @@
1
1
  # DocsShell
2
2
 
3
- Prism page — full documentation pending.
3
+ A documentation screen: a navigation rail whose shape arrives as data, the document at the measure, a contents rail, and a pager derived from the navigation rather than passed.
4
4
 
5
- ```ts
6
- import { DocsShell } from '@nanisoft/prism-ui/pages';
5
+ ```tsx
6
+ import { DocsShell } from '@nanisoft/prism-ui/pages/docs-shell'
7
7
  ```
8
8
 
9
- `DocsShell` is a Prism page. Full documentation is pending — the prism MCP answers API questions in the meantime (`get_item_props`, `get_item_source`).
9
+ ## Overview
10
+
11
+ DocsShell is a documentation screen: a navigation rail, the document, a contents
12
+ rail, and the pager to the neighbouring pages.
13
+
14
+ **It is the Page the other three NaniSoft sites import, and the name is theirs.**
15
+ All three already read `import { DocsShell, type DocsNavEntry } from
16
+ '@nanisoft/prism-ui/pages'` in their own document template, and all three map
17
+ their page tree into that shape in their own `lib/to-prism-tree`. A published
18
+ name cannot be cheaply changed and there is no redirect lane for item routes, so
19
+ the name follows those three import lines rather than this file's shape. It was
20
+ `docs-shell` in the retired line too, where it was an empty `div` and a class
21
+ name each site then had to style from scratch.
22
+
23
+ **The shape arrives as data.** The three sites publish full documentation sets
24
+ and none of them agrees on the shape:
25
+
26
+ - Nexus files six sections of five pages, each named for a documentation genre,
27
+ each with an index.
28
+ - Atlas files four sections, one of which holds nine pages under a single
29
+ heading, and not one of them has an index.
30
+ - AlphaLens files six sections separated by rules, named for subject matter
31
+ rather than for a genre, with a status inside three of the titles.
32
+
33
+ The tree is a prop and the Page renders it, so none of the three had to be told
34
+ which of the others it looks like.
35
+
36
+ ## Guidelines
37
+
38
+ ### When to use
39
+
40
+ Use DocsShell as the body of the route that renders one documentation page,
41
+ handed the page tree, the document's own outline, and the address being rendered.
42
+
43
+ Use it for every page of a documentation set. A documentation site whose pages
44
+ each carry their own rail is a site whose rail drifts.
45
+
46
+ ### When not to use
47
+
48
+ Do not use it for a single-page site. It is a whole screen around a tree, and a
49
+ site with one page has no tree.
50
+
51
+ Do not use it for a blog. A post is a dated argument about one thing, and
52
+ `BlogPostPage` is that screen.
53
+
54
+ Do not hand it a `nav` you built by parsing a folder tree at render time. Map
55
+ the tree once, in your own adapter, and pass the result: a Page that walked a
56
+ filesystem would be a Page that fetches, and a Page imports no router and no data
57
+ client.
58
+
59
+ ### Best practices
60
+
61
+ - **A section is a label, not a control.** It carries no status, no badge, no
62
+ count, no collapse and no sort, and that is the whole of its interface. AlphaLens
63
+ files its documentation as a pipeline, where "Unified data contract (approved)"
64
+ is a stage of the work and not a topic a reader may visit in any order, and
65
+ every structural affordance says the opposite. A status in the title is part of
66
+ the title and stays there as words: the Page reads no parentheses, matches no
67
+ vocabulary, and renders no pill beside a heading that has one.
68
+ - **Omit `href` on a group that has no index.** That absence is what makes the
69
+ heading a label rather than a route, and it is the same thing all three sites
70
+ already spell as `url: node.index?.url ?? ''`, which two of them then restyle
71
+ back into a label in their own stylesheets. Naming the case in the type makes
72
+ those rules dead rather than load-bearing, and the type is the only thing that
73
+ had to change.
74
+ - **Do not pass a previous and a next.** The pager is derived from `nav` and
75
+ `currentHref`, which removes one derivation from every consumer. It is derived
76
+ rather than passed for a second reason: a caller who passes neighbours can pass
77
+ a neighbour that is not in the tree, and this is the one arrangement where a
78
+ reader would notice.
79
+ - **Give the rail and the contents their own orders, and let both stand.** `nav`
80
+ is in the order the rail shows and `toc` is in the order the contents rail
81
+ shows. Nothing is sorted, deduplicated or alphabetised. A site that files its
82
+ rail as a pipeline and its contents as a reference has both in one render.
83
+ - Pass `toc` whole or not at all. A partial outline is a contents list that lies
84
+ about the page.
85
+ - Omit `title` when your document owns its own heading. The Page renders no empty
86
+ `h1` to fill the gap, so a document with no title keeps exactly the outline its
87
+ own content declares.
88
+ - Name the three regions. `navLabel`, `tocLabel` and `pagerLabel` are required
89
+ because they are words a reader hears, and a Page that ships no copy ships no
90
+ reader-facing copy either. A site that files its pages as a pipeline and its
91
+ contents as a reference names the three regions three different ways.
92
+
93
+ ### Content guidelines
94
+
95
+ - Put a section's stage in its title, as AlphaLens does, and let it stay a
96
+ sentence. The Page will not pull it out into a badge for you, and a badge is a
97
+ state the reader can act on, which a stage of a pipeline is not.
98
+ - Keep a rule's title short or leave it empty. A rule with words is a label a
99
+ reader reads twice: once in the rule and once on the section beneath it.
100
+ - Keep a section index page's title the same as its section's title, so a reader
101
+ who arrives at the index and a reader who is already inside the section are
102
+ reading the same words.
103
+
104
+ **A documentation screen: the rail, the document, the contents and the pager.**
105
+
106
+ ```tsx
107
+ import { DocsShell, type DocsNavEntry } from '@nanisoft/prism-ui/pages/docs-shell'
108
+
109
+ /**
110
+ * The shape the three consumer sites actually publish, taken from AlphaLens
111
+ * because it is the one that exercises every part of the interface at once: a
112
+ * status inside a section title, a rule between sections, and a section whose
113
+ * own index is a page a reader lands on.
114
+ */
115
+ const NAV: DocsNavEntry[] = [
116
+ { type: 'page', title: 'AlphaLens', href: '#alphalens' },
117
+ { type: 'divider', title: 'Data platform (live)' },
118
+ {
119
+ type: 'group',
120
+ title: 'Data platform (live)',
121
+ href: '#data-platform',
122
+ items: [
123
+ { type: 'page', title: 'Capture', href: '#capture' },
124
+ { type: 'page', title: 'Storage', href: '#storage' },
125
+ { type: 'page', title: 'Operations', href: '#platform-operations' },
126
+ ],
127
+ },
128
+ { type: 'divider', title: 'Unified data contract (approved)' },
129
+ {
130
+ type: 'group',
131
+ title: 'Unified data contract (approved)',
132
+ href: '#data-contract',
133
+ items: [
134
+ { type: 'page', title: 'Data feed view', href: '#feed-view' },
135
+ { type: 'page', title: 'Timestamps and symbols', href: '#timestamps' },
136
+ ],
137
+ },
138
+ { type: 'divider', title: 'Research pipeline (designed)' },
139
+ {
140
+ // No `href`: this group is a label, because its folder holds no index page.
141
+ type: 'group',
142
+ title: 'Research pipeline (designed)',
143
+ items: [
144
+ { type: 'page', title: 'Anatomy', href: '#anatomy' },
145
+ { type: 'page', title: 'NSE portability', href: '#nse-portability' },
146
+ ],
147
+ },
148
+ ]
149
+
150
+ /** The headings inside this page. Your own pipeline knows them; Prism does not. */
151
+ const TOC: DocsNavEntry[] = [
152
+ { type: 'page', title: 'The capture path', href: '#the-capture-path' },
153
+ { type: 'page', title: 'What the store holds', href: '#what-the-store-holds' },
154
+ { type: 'page', title: 'Where this is heading', href: '#where-this-is-heading' },
155
+ ]
156
+
157
+ /** A documentation screen: the rail, the document, the contents and the pager. */
158
+ export default function DocsShellDemo() {
159
+ return (
160
+ <DocsShell
161
+ title="Storage"
162
+ description="Where a full chain of bars lands, and how it is partitioned."
163
+ nav={NAV}
164
+ toc={TOC}
165
+ currentHref="#storage"
166
+ navLabel="AlphaLens documentation"
167
+ tocLabel="On this page"
168
+ pagerLabel="AlphaLens pages"
169
+ pagerLabels={{ previous: 'Previous', next: 'Next' }}
170
+ >
171
+ <p>
172
+ The chain is written in columnar blocks, one per session per symbol, and
173
+ nothing is rewritten to correct it. What lands is what the exchange sent.
174
+ </p>
175
+ <h2 id="the-capture-path">The capture path</h2>
176
+ <p>
177
+ A capture is a session and a symbol, never a whole day. A day is a
178
+ partition over sessions, which is what makes a backtest cheap to repeat
179
+ and expensive to store twice.
180
+ </p>
181
+ <h2 id="what-the-store-holds">What the store holds</h2>
182
+ <ul>
183
+ <li>Every strike, call and put, at the exchange clock.</li>
184
+ <li>Open interest and the implied volatility of each strike.</li>
185
+ <li>The index future and the spot it settled against.</li>
186
+ </ul>
187
+ <h2 id="where-this-is-heading">Where this is heading</h2>
188
+ <p>
189
+ The same path, ported to .NET, is designed rather than built. The stage it
190
+ is at is in its title, because a stage of a pipeline is not a topic a
191
+ reader may visit in any order.
192
+ </p>
193
+ </DocsShell>
194
+ )
195
+ }
196
+ ```
10
197
 
11
198
  ## Props
12
199
 
13
- | Prop | Type | Default | Description |
14
- | --- | --- | --- | --- |
15
- | `children` | ReactNode | — | The article body (the app renders fumadocs MDX content into it). |
16
- | `title` (optional) | string | — | Page title; omitted for index-style pages that own their heading. |
17
- | `description` (optional) | string | — | — |
18
- | `nav` (optional) | DocsNavEntry[] | — | Sidebar navigation tree. |
19
- | `toc` (optional) | DocsNavEntry[] | — | Table-of-contents entries for the current page. |
20
- | `neighbours` (optional) | { previous?: { title: string; url: string; }; next?: { title: string; url: string; }; } | — | Prev/next neighbours at the boundary. |
21
- | `header` (optional) | ReactNode | — | Slot overrides for chrome — the app's header/footer. |
22
- | `footer` (optional) | ReactNode | — | — |
23
- | `className` (optional) | string | — | — |
200
+ **`title`** `string` · optional · default: — — The page's title, and the document's `h1`. It is optional because the three consumer sites do not agree about whether a documentation page has one: two render the frontmatter title as the page heading, and one lets the document's own Markdown own it. When it is absent the Page renders no heading at all rather than an empty one, so the document keeps exactly the outline its own content declares.
201
+ **`description`** `ReactNode` · optional · default: — — One or two sentences under the title: what this part of the documentation covers. A `ReactNode` because a site that files a page for a pipeline states the pipeline's stage as markup rather than as a sentence.
202
+ **`nav`** `readonly DocsNavEntry[]` · optional · default: — — The documentation tree, in the order a reader should meet it. The whole shape arrives here, so the Page holds no opinion about how many sections a site has, how deep they nest, or what a section is called.
203
+ **`toc`** `readonly DocsNavEntry[]` · optional · default: — — The headings inside this page, in document order, for the contents rail. It is the consumer's document that knows its own outline, because Prism does not parse Markdown and a Page that guessed at it would guess wrong on every document rather than on some. Pass the whole outline or none: a partial one is a contents list that lies about the page.
204
+ **`currentHref`** `string` · optional · default: — — The address of the page being rendered. The rail marks it with `aria-current="page"` and the pager derives its two neighbours from it, so the same navigation renders a different pager for every page without the caller deriving anything.
205
+ **`navLabel`** `string` · required · default: — — The accessible name of the rail. Required whenever `nav` is passed, because it is a word a reader hears and a Page that ships no copy ships no reader-facing copy either.
206
+ **`tocLabel`** `string` · required · default: — — The accessible name of the contents rail. Required whenever `toc` is passed, for the same reason `navLabel` is.
207
+ **`pagerLabel`** `string` · required · default: — — The accessible name of the pager. Required whenever there is a neighbour to reach, for the same reason `navLabel` is.
208
+ **`pagerLabels`** `DocsPagerLabels` · required · default: — — The two words the pager renders above the neighbouring pages' titles. Required whenever either neighbour exists, for the same reason `navLabel` is.
209
+ **`children`** `ReactNode` · required · default: — — The document's own body. Any flow content: paragraphs, headings, lists, code, images, tables. `Prose` holds it to the reading measure and to the block rhythm, and it is passed `fullWidth` so the document fills the column the frame gives it rather than sitting in a second measure inside one.
210
+ **`header`** `ReactNode` · optional · default: — — A slot above the frame, inside the container: a breadcrumb trail, a version picker, a search field. A slot rather than a prop because each of those is a Block, and a Page that took one per prop would be a Page whose interface lists the whole catalogue.
211
+ **`footer`** `ReactNode` · optional · default: — — A slot below the frame, inside the container: a feedback prompt, a link back to the section index, a support address.
212
+ **`className`** `string` · optional · default: — — Layout only, exactly as on every Component. Changing a Prism-owned visual property from here is prohibited.
213
+
214
+ ## Composition
215
+
216
+ **`exports`** `DocsShell` — the public runtime names this Page promises
217
+ **`source`** `packages/ui/src/pages/docs-shell/index.tsx` — the file this Page is defined in
218
+ **`dependencies`** `Prose` — the public Prism exports this item composes