@nanisoft/prism-ui 0.10.2 → 0.13.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.
Files changed (334) hide show
  1. package/LICENSE +21 -21
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/blocks/app-shell-01/app-shell.js +4 -4
  4. package/dist/blocks/app-shell-01/index.d.ts +2 -2
  5. package/dist/blocks/app-shell-01/index.js +1 -1
  6. package/dist/blocks/auth-form-01/auth-form.d.ts +11 -1
  7. package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
  8. package/dist/blocks/auth-form-01/auth-form.js +11 -8
  9. package/dist/blocks/auth-form-01/index.d.ts +2 -2
  10. package/dist/blocks/auth-form-01/index.js +1 -1
  11. package/dist/blocks/cta-01/cta.d.ts +26 -21
  12. package/dist/blocks/cta-01/cta.d.ts.map +1 -1
  13. package/dist/blocks/cta-01/cta.js +2 -2
  14. package/dist/blocks/cta-01/index.d.ts +2 -2
  15. package/dist/blocks/cta-01/index.js +1 -1
  16. package/dist/blocks/data-table-01/data-table.js +7 -7
  17. package/dist/blocks/data-table-01/index.d.ts +2 -2
  18. package/dist/blocks/data-table-01/index.js +1 -1
  19. package/dist/blocks/empty-state-01/empty-state.d.ts +143 -0
  20. package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -0
  21. package/dist/blocks/empty-state-01/empty-state.js +104 -0
  22. package/dist/blocks/empty-state-01/index.d.ts +3 -0
  23. package/dist/blocks/empty-state-01/index.d.ts.map +1 -0
  24. package/dist/blocks/empty-state-01/index.js +1 -0
  25. package/dist/blocks/feature-grid-01/feature-grid.d.ts +28 -1
  26. package/dist/blocks/feature-grid-01/feature-grid.d.ts.map +1 -1
  27. package/dist/blocks/feature-grid-01/feature-grid.js +34 -3
  28. package/dist/blocks/feature-grid-01/index.d.ts +2 -2
  29. package/dist/blocks/feature-grid-01/index.js +1 -1
  30. package/dist/blocks/hero-01/hero.d.ts +35 -15
  31. package/dist/blocks/hero-01/hero.d.ts.map +1 -1
  32. package/dist/blocks/hero-01/hero.js +14 -9
  33. package/dist/blocks/hero-01/index.d.ts +2 -1
  34. package/dist/blocks/hero-01/index.d.ts.map +1 -1
  35. package/dist/blocks/hero-01/index.js +1 -1
  36. package/dist/blocks/index.d.ts +20 -20
  37. package/dist/blocks/index.js +19 -19
  38. package/dist/blocks/instrument-panel-01/index.d.ts +2 -2
  39. package/dist/blocks/instrument-panel-01/index.js +1 -1
  40. package/dist/blocks/instrument-panel-01/instrument-panel.js +1 -1
  41. package/dist/blocks/logo-strip-01/index.d.ts +2 -2
  42. package/dist/blocks/logo-strip-01/index.js +1 -1
  43. package/dist/blocks/logo-strip-01/logo-strip.d.ts +1 -1
  44. package/dist/blocks/logo-strip-01/logo-strip.js +2 -2
  45. package/dist/blocks/note-grid-01/index.d.ts +2 -2
  46. package/dist/blocks/note-grid-01/index.js +1 -1
  47. package/dist/blocks/note-grid-01/note-grid.d.ts +1 -1
  48. package/dist/blocks/note-grid-01/note-grid.js +1 -1
  49. package/dist/blocks/page-header-01/index.d.ts +2 -2
  50. package/dist/blocks/page-header-01/index.js +1 -1
  51. package/dist/blocks/page-header-01/page-header.d.ts +1 -1
  52. package/dist/blocks/page-header-01/page-header.js +4 -4
  53. package/dist/blocks/pricing-01/index.d.ts +1 -1
  54. package/dist/blocks/pricing-01/index.js +1 -1
  55. package/dist/blocks/pricing-01/pricing.d.ts +8 -1
  56. package/dist/blocks/pricing-01/pricing.d.ts.map +1 -1
  57. package/dist/blocks/pricing-01/pricing.js +15 -5
  58. package/dist/blocks/process-flow-01/index.d.ts +3 -0
  59. package/dist/blocks/process-flow-01/index.d.ts.map +1 -0
  60. package/dist/blocks/process-flow-01/index.js +1 -0
  61. package/dist/blocks/process-flow-01/process-flow.d.ts +118 -0
  62. package/dist/blocks/process-flow-01/process-flow.d.ts.map +1 -0
  63. package/dist/blocks/process-flow-01/process-flow.js +71 -0
  64. package/dist/blocks/process-rail-01/index.d.ts +2 -2
  65. package/dist/blocks/process-rail-01/index.js +1 -1
  66. package/dist/blocks/process-rail-01/process-rail.d.ts +1 -1
  67. package/dist/blocks/process-rail-01/process-rail.js +2 -2
  68. package/dist/blocks/product-grid-01/index.d.ts +2 -2
  69. package/dist/blocks/product-grid-01/index.js +1 -1
  70. package/dist/blocks/product-grid-01/product-grid.d.ts +37 -5
  71. package/dist/blocks/product-grid-01/product-grid.d.ts.map +1 -1
  72. package/dist/blocks/product-grid-01/product-grid.js +20 -6
  73. package/dist/blocks/run-console-01/index.d.ts +3 -0
  74. package/dist/blocks/run-console-01/index.d.ts.map +1 -0
  75. package/dist/blocks/run-console-01/index.js +1 -0
  76. package/dist/blocks/run-console-01/run-console.d.ts +111 -0
  77. package/dist/blocks/run-console-01/run-console.d.ts.map +1 -0
  78. package/dist/blocks/run-console-01/run-console.js +48 -0
  79. package/dist/blocks/settings-panel-01/index.d.ts +2 -2
  80. package/dist/blocks/settings-panel-01/index.js +1 -1
  81. package/dist/blocks/settings-panel-01/settings-panel.d.ts +11 -1
  82. package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
  83. package/dist/blocks/settings-panel-01/settings-panel.js +15 -10
  84. package/dist/blocks/site-footer/index.d.ts +2 -2
  85. package/dist/blocks/site-footer/index.js +1 -1
  86. package/dist/blocks/site-footer/site-footer.d.ts +1 -1
  87. package/dist/blocks/site-footer/site-footer.js +2 -2
  88. package/dist/blocks/site-header/index.d.ts +2 -2
  89. package/dist/blocks/site-header/index.js +1 -1
  90. package/dist/blocks/site-header/site-header.d.ts +2 -2
  91. package/dist/blocks/site-header/site-header.js +3 -3
  92. package/dist/blocks/site-navbar/index.d.ts +3 -0
  93. package/dist/blocks/site-navbar/index.d.ts.map +1 -0
  94. package/dist/blocks/site-navbar/index.js +1 -0
  95. package/dist/blocks/site-navbar/mobile-nav.d.ts +48 -0
  96. package/dist/blocks/site-navbar/mobile-nav.d.ts.map +1 -0
  97. package/dist/blocks/site-navbar/mobile-nav.js +35 -0
  98. package/dist/blocks/site-navbar/mode-toggle.d.ts +39 -0
  99. package/dist/blocks/site-navbar/mode-toggle.d.ts.map +1 -0
  100. package/dist/blocks/site-navbar/mode-toggle.js +39 -0
  101. package/dist/blocks/site-navbar/navbar-controls.d.ts +79 -0
  102. package/dist/blocks/site-navbar/navbar-controls.d.ts.map +1 -0
  103. package/dist/blocks/site-navbar/navbar-controls.js +40 -0
  104. package/dist/blocks/site-navbar/search-entry.d.ts +29 -0
  105. package/dist/blocks/site-navbar/search-entry.d.ts.map +1 -0
  106. package/dist/blocks/site-navbar/search-entry.js +52 -0
  107. package/dist/blocks/site-navbar/site-navbar.d.ts +212 -0
  108. package/dist/blocks/site-navbar/site-navbar.d.ts.map +1 -0
  109. package/dist/blocks/site-navbar/site-navbar.js +61 -0
  110. package/dist/blocks/site-navbar/sites-menu.d.ts +40 -0
  111. package/dist/blocks/site-navbar/sites-menu.d.ts.map +1 -0
  112. package/dist/blocks/site-navbar/sites-menu.js +48 -0
  113. package/dist/blocks/site-navbar/theme-menu.d.ts +47 -0
  114. package/dist/blocks/site-navbar/theme-menu.d.ts.map +1 -0
  115. package/dist/blocks/site-navbar/theme-menu.js +55 -0
  116. package/dist/blocks/stack-grid-01/index.d.ts +2 -2
  117. package/dist/blocks/stack-grid-01/index.js +1 -1
  118. package/dist/blocks/stack-grid-01/stack-grid.d.ts +42 -11
  119. package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -1
  120. package/dist/blocks/stack-grid-01/stack-grid.js +15 -7
  121. package/dist/blocks/stats-01/index.d.ts +9 -1
  122. package/dist/blocks/stats-01/index.d.ts.map +1 -1
  123. package/dist/blocks/stats-01/index.js +8 -1
  124. package/dist/blocks/stats-01/stats.d.ts +1 -1
  125. package/dist/blocks/stats-01/stats.js +2 -2
  126. package/dist/blocks/status-ledger-01/index.d.ts +2 -2
  127. package/dist/blocks/status-ledger-01/index.js +1 -1
  128. package/dist/blocks/status-ledger-01/status-ledger.d.ts +1 -1
  129. package/dist/blocks/status-ledger-01/status-ledger.js +2 -2
  130. package/dist/catalog.d.ts +1 -1
  131. package/dist/catalog.d.ts.map +1 -1
  132. package/dist/catalog.js +473 -1
  133. package/dist/components/index.d.ts +104 -66
  134. package/dist/components/index.d.ts.map +1 -1
  135. package/dist/components/index.js +74 -40
  136. package/dist/components/ui/accordion.js +1 -1
  137. package/dist/components/ui/alert-dialog.d.ts +119 -0
  138. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  139. package/dist/components/ui/alert-dialog.js +82 -0
  140. package/dist/components/ui/alert.js +1 -1
  141. package/dist/components/ui/aspect-ratio.d.ts +60 -0
  142. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  143. package/dist/components/ui/aspect-ratio.js +61 -0
  144. package/dist/components/ui/avatar.js +1 -1
  145. package/dist/components/ui/badge.js +1 -1
  146. package/dist/components/ui/breadcrumb.js +1 -1
  147. package/dist/components/ui/button-group.d.ts +68 -0
  148. package/dist/components/ui/button-group.d.ts.map +1 -0
  149. package/dist/components/ui/button-group.js +54 -0
  150. package/dist/components/ui/button.js +1 -1
  151. package/dist/components/ui/calendar.d.ts +158 -0
  152. package/dist/components/ui/calendar.d.ts.map +1 -0
  153. package/dist/components/ui/calendar.js +216 -0
  154. package/dist/components/ui/card.d.ts +18 -6
  155. package/dist/components/ui/card.d.ts.map +1 -1
  156. package/dist/components/ui/card.js +23 -8
  157. package/dist/components/ui/carousel.d.ts +118 -0
  158. package/dist/components/ui/carousel.d.ts.map +1 -0
  159. package/dist/components/ui/carousel.js +152 -0
  160. package/dist/components/ui/chart-frame.d.ts +216 -0
  161. package/dist/components/ui/chart-frame.d.ts.map +1 -0
  162. package/dist/components/ui/chart-frame.js +286 -0
  163. package/dist/components/ui/checkbox.js +1 -1
  164. package/dist/components/ui/collapsible.d.ts +97 -0
  165. package/dist/components/ui/collapsible.d.ts.map +1 -0
  166. package/dist/components/ui/collapsible.js +54 -0
  167. package/dist/components/ui/combobox.d.ts +141 -0
  168. package/dist/components/ui/combobox.d.ts.map +1 -0
  169. package/dist/components/ui/combobox.js +243 -0
  170. package/dist/components/ui/command-palette.d.ts +106 -0
  171. package/dist/components/ui/command-palette.d.ts.map +1 -0
  172. package/dist/components/ui/command-palette.js +176 -0
  173. package/dist/components/ui/command.d.ts +82 -0
  174. package/dist/components/ui/command.d.ts.map +1 -0
  175. package/dist/components/ui/command.js +37 -0
  176. package/dist/components/ui/context-menu.d.ts +151 -0
  177. package/dist/components/ui/context-menu.d.ts.map +1 -0
  178. package/dist/components/ui/context-menu.js +97 -0
  179. package/dist/components/ui/cta-link.js +1 -1
  180. package/dist/components/ui/date-picker.d.ts +111 -0
  181. package/dist/components/ui/date-picker.d.ts.map +1 -0
  182. package/dist/components/ui/date-picker.js +74 -0
  183. package/dist/components/ui/diagram.d.ts +84 -15
  184. package/dist/components/ui/diagram.d.ts.map +1 -1
  185. package/dist/components/ui/diagram.js +77 -7
  186. package/dist/components/ui/dialog.js +1 -1
  187. package/dist/components/ui/diff.d.ts +105 -0
  188. package/dist/components/ui/diff.d.ts.map +1 -0
  189. package/dist/components/ui/diff.js +113 -0
  190. package/dist/components/ui/dropdown-menu.d.ts +18 -0
  191. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  192. package/dist/components/ui/dropdown-menu.js +1 -1
  193. package/dist/components/ui/fact-list.js +1 -1
  194. package/dist/components/ui/field.js +1 -1
  195. package/dist/components/ui/form.d.ts +151 -0
  196. package/dist/components/ui/form.d.ts.map +1 -0
  197. package/dist/components/ui/form.js +73 -0
  198. package/dist/components/ui/hover-card.d.ts +105 -0
  199. package/dist/components/ui/hover-card.d.ts.map +1 -0
  200. package/dist/components/ui/hover-card.js +49 -0
  201. package/dist/components/ui/input-group.d.ts +99 -0
  202. package/dist/components/ui/input-group.d.ts.map +1 -0
  203. package/dist/components/ui/input-group.js +83 -0
  204. package/dist/components/ui/input.js +1 -1
  205. package/dist/components/ui/item.d.ts +113 -0
  206. package/dist/components/ui/item.d.ts.map +1 -0
  207. package/dist/components/ui/item.js +61 -0
  208. package/dist/components/ui/kbd.js +1 -1
  209. package/dist/components/ui/label.d.ts +58 -0
  210. package/dist/components/ui/label.d.ts.map +1 -0
  211. package/dist/components/ui/label.js +29 -0
  212. package/dist/components/ui/live-region.d.ts +9 -0
  213. package/dist/components/ui/live-region.d.ts.map +1 -1
  214. package/dist/components/ui/live-region.js +10 -2
  215. package/dist/components/ui/mark.js +1 -1
  216. package/dist/components/ui/menubar.d.ts +197 -0
  217. package/dist/components/ui/menubar.d.ts.map +1 -0
  218. package/dist/components/ui/menubar.js +109 -0
  219. package/dist/components/ui/meter.d.ts +121 -0
  220. package/dist/components/ui/meter.d.ts.map +1 -0
  221. package/dist/components/ui/meter.js +82 -0
  222. package/dist/components/ui/native-select.d.ts +72 -0
  223. package/dist/components/ui/native-select.d.ts.map +1 -0
  224. package/dist/components/ui/native-select.js +55 -0
  225. package/dist/components/ui/navigation-menu.d.ts +186 -0
  226. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  227. package/dist/components/ui/navigation-menu.js +94 -0
  228. package/dist/components/ui/number-field.d.ts +111 -0
  229. package/dist/components/ui/number-field.d.ts.map +1 -0
  230. package/dist/components/ui/number-field.js +84 -0
  231. package/dist/components/ui/one-time-code.d.ts +127 -0
  232. package/dist/components/ui/one-time-code.d.ts.map +1 -0
  233. package/dist/components/ui/one-time-code.js +57 -0
  234. package/dist/components/ui/pagination.js +1 -1
  235. package/dist/components/ui/popover.js +1 -1
  236. package/dist/components/ui/product-mark.d.ts +1 -1
  237. package/dist/components/ui/product-mark.js +1 -1
  238. package/dist/components/ui/product-switcher.d.ts +1 -1
  239. package/dist/components/ui/product-switcher.js +2 -2
  240. package/dist/components/ui/progress.js +1 -1
  241. package/dist/components/ui/prose.js +1 -1
  242. package/dist/components/ui/pulse-graph.js +1 -1
  243. package/dist/components/ui/pulse-series.js +1 -1
  244. package/dist/components/ui/radio-group.js +1 -1
  245. package/dist/components/ui/resizable.d.ts +129 -0
  246. package/dist/components/ui/resizable.d.ts.map +1 -0
  247. package/dist/components/ui/resizable.js +205 -0
  248. package/dist/components/ui/scroll-area.d.ts +97 -0
  249. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  250. package/dist/components/ui/scroll-area.js +68 -0
  251. package/dist/components/ui/search-dialog.d.ts +139 -0
  252. package/dist/components/ui/search-dialog.d.ts.map +1 -0
  253. package/dist/components/ui/search-dialog.js +178 -0
  254. package/dist/components/ui/section.d.ts +26 -0
  255. package/dist/components/ui/section.d.ts.map +1 -1
  256. package/dist/components/ui/section.js +32 -1
  257. package/dist/components/ui/select.js +1 -1
  258. package/dist/components/ui/separator.js +1 -1
  259. package/dist/components/ui/sheet.d.ts +103 -0
  260. package/dist/components/ui/sheet.d.ts.map +1 -0
  261. package/dist/components/ui/sheet.js +58 -0
  262. package/dist/components/ui/sidebar.d.ts +228 -0
  263. package/dist/components/ui/sidebar.d.ts.map +1 -0
  264. package/dist/components/ui/sidebar.js +172 -0
  265. package/dist/components/ui/signal-field.js +1 -1
  266. package/dist/components/ui/skeleton.js +1 -1
  267. package/dist/components/ui/slider.js +1 -1
  268. package/dist/components/ui/spinner.d.ts +79 -0
  269. package/dist/components/ui/spinner.d.ts.map +1 -0
  270. package/dist/components/ui/spinner.js +79 -0
  271. package/dist/components/ui/switch.js +1 -1
  272. package/dist/components/ui/table-sort.d.ts +97 -0
  273. package/dist/components/ui/table-sort.d.ts.map +1 -0
  274. package/dist/components/ui/table-sort.js +102 -0
  275. package/dist/components/ui/table.js +1 -1
  276. package/dist/components/ui/tabs.js +1 -1
  277. package/dist/components/ui/textarea.js +1 -1
  278. package/dist/components/ui/timeline.d.ts +106 -0
  279. package/dist/components/ui/timeline.d.ts.map +1 -0
  280. package/dist/components/ui/timeline.js +111 -0
  281. package/dist/components/ui/toast.d.ts +219 -0
  282. package/dist/components/ui/toast.d.ts.map +1 -0
  283. package/dist/components/ui/toast.js +270 -0
  284. package/dist/components/ui/toggle-group.d.ts +118 -0
  285. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  286. package/dist/components/ui/toggle-group.js +194 -0
  287. package/dist/components/ui/toggle.d.ts +73 -0
  288. package/dist/components/ui/toggle.d.ts.map +1 -0
  289. package/dist/components/ui/toggle.js +44 -0
  290. package/dist/components/ui/tooltip.js +1 -1
  291. package/dist/components/ui/tree.js +1 -1
  292. package/dist/components/ui/typography.js +1 -1
  293. package/dist/fonts/Inter-OFL.txt +93 -93
  294. package/dist/index.d.ts +72 -70
  295. package/dist/index.d.ts.map +1 -1
  296. package/dist/index.js +61 -60
  297. package/dist/lib/rank.d.ts +69 -0
  298. package/dist/lib/rank.d.ts.map +1 -0
  299. package/dist/lib/rank.js +77 -0
  300. package/dist/live/index.d.ts +22 -0
  301. package/dist/live/index.d.ts.map +1 -0
  302. package/dist/live/index.js +20 -0
  303. package/dist/live/run-stream-01/index.d.ts +3 -0
  304. package/dist/live/run-stream-01/index.d.ts.map +1 -0
  305. package/dist/live/run-stream-01/index.js +1 -0
  306. package/dist/live/run-stream-01/run-stream.d.ts +125 -0
  307. package/dist/live/run-stream-01/run-stream.d.ts.map +1 -0
  308. package/dist/live/run-stream-01/run-stream.js +111 -0
  309. package/dist/pages/auth-page/index.d.ts +15 -2
  310. package/dist/pages/auth-page/index.d.ts.map +1 -1
  311. package/dist/pages/auth-page/index.js +8 -4
  312. package/dist/pages/blog-post-page/index.js +5 -5
  313. package/dist/pages/dashboard-page/index.d.ts +4 -4
  314. package/dist/pages/dashboard-page/index.js +4 -4
  315. package/dist/pages/docs-shell/docs-shell.d.ts +37 -4
  316. package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
  317. package/dist/pages/docs-shell/docs-shell.js +107 -16
  318. package/dist/pages/docs-shell/index.d.ts +2 -2
  319. package/dist/pages/docs-shell/index.js +1 -1
  320. package/dist/pages/index.d.ts +14 -14
  321. package/dist/pages/index.js +7 -7
  322. package/dist/pages/marketing-page/index.d.ts +6 -6
  323. package/dist/pages/marketing-page/index.js +5 -5
  324. package/dist/pages/not-found-page/index.js +4 -4
  325. package/dist/pages/settings-page/index.d.ts +3 -3
  326. package/dist/pages/settings-page/index.js +4 -4
  327. package/dist/provider/index.d.ts +4 -4
  328. package/dist/provider/index.js +2 -2
  329. package/dist/provider/provider.d.ts +1 -1
  330. package/dist/provider/provider.js +1 -1
  331. package/dist/provider/theme-script.d.ts +1 -1
  332. package/dist/provider/theme-script.js +1 -1
  333. package/dist/styles.css +689 -0
  334. package/package.json +9 -1
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Who produced an event, as a closed set.
3
+ *
4
+ * The four are the ones an agent run actually has, and they are closed because the
5
+ * treatment is not the caller's: a role is a slot for a colour, a mark and an
6
+ * announcement, and a free-text role would be a string with no rendering behind it.
7
+ * The **words** are still the caller's, in `message`. Prism owns which role looks
8
+ * like what, and ships none of the labels.
9
+ */
10
+ export type RunEventRole = 'agent' | 'tool' | 'system' | 'error';
11
+ /** One event, as the surface receives it. */
12
+ export type RunEvent = {
13
+ /** Stable across re-renders, so an event is never re-announced because its index moved. */
14
+ id: string;
15
+ role: RunEventRole;
16
+ /** Epoch milliseconds, used for order and for the elapsed column. */
17
+ at: number;
18
+ /** What happened, in the consumer's words. */
19
+ message: string;
20
+ /** Optional second line: a tool's arguments, a path, a code. */
21
+ detail?: string;
22
+ };
23
+ /** Where a run has got to. The tiers are Prism's; the words beside them are the caller's. */
24
+ export type RunStatus = 'queued' | 'running' | 'waiting' | 'done' | 'failed';
25
+ /**
26
+ * How the surface receives events.
27
+ *
28
+ * The consumer owns the socket, the transport and the persistence, so this is a
29
+ * function it supplies rather than a connection Prism opens. It is called once on
30
+ * mount and must return an unsubscribe, which is what makes a WebSocket, an
31
+ * `EventSource`, a polling timer or a test's own array all the same shape to the
32
+ * surface. Nothing here knows which one it got.
33
+ */
34
+ export type RunSubscribe = (emit: (event: RunEvent) => void) => () => void;
35
+ export type RunStream01Props = {
36
+ /**
37
+ * Called once to begin receiving, and again whenever `resubscribeKey` changes.
38
+ * Must return an unsubscribe; the surface calls it on unmount and before
39
+ * re-subscribing, so a consumer that returns nothing leaks its connection.
40
+ */
41
+ subscribe: RunSubscribe;
42
+ /**
43
+ * Which run this surface is showing, and the reset lever.
44
+ *
45
+ * Changing it **clears the log and reseeds it from `initial`**, as well as
46
+ * tearing the subscription down and opening a new one. A run is identified by
47
+ * whatever the consumer already uses to identify it, so a second run in the same
48
+ * surface is a new subscription rather than two consumers of one, and it is a new
49
+ * log rather than the old run's events with the new run's appended, which is the
50
+ * reading a reader would not distinguish from one run being longer than it was.
51
+ */
52
+ resubscribeKey?: string | number;
53
+ /**
54
+ * The events already known when this run is opened, so a surface opened mid-run
55
+ * is not blank. Oldest first.
56
+ *
57
+ * Read when the run is opened, and again whenever `resubscribeKey` changes. It is
58
+ * **not** read on every render: a prop that looked like the current event list
59
+ * and quietly seeded only once would be a trap, and a prop that re-seeded on
60
+ * every render would discard the stream. This seeds, and the stream takes over.
61
+ */
62
+ initial?: readonly RunEvent[];
63
+ /** Where the run has got to. */
64
+ status?: RunStatus;
65
+ /** The word beside the status. Required when a status is passed. */
66
+ statusLabel?: string;
67
+ /** Optional label above the surface's heading. See the No-Default-Eyebrow Rule. */
68
+ eyebrow?: string;
69
+ /** The surface's heading. Omit it for one composed under its own. */
70
+ title?: string;
71
+ /** One or two sentences under the heading. */
72
+ description?: string;
73
+ /** What the surface says when it holds no events yet. */
74
+ emptyLabel?: string;
75
+ /**
76
+ * The accessible name of the scrolling region.
77
+ *
78
+ * Required, because `ScrollArea` requires it and the reason is the right one: a
79
+ * scroll region with no name is announced as a group of focusable nothing, and a
80
+ * keyboard reader who tabs into it cannot tell what is in it or what is past the
81
+ * edge. The name is the consumer's word, as every other word here is.
82
+ */
83
+ eventsLabel: string;
84
+ /** Announced to assistive technology as events arrive. */
85
+ liveLabel?: string;
86
+ /** How many events to keep. Older events fall off the top. */
87
+ limit?: number;
88
+ className?: string;
89
+ };
90
+ /**
91
+ * The event log of a run, receiving events as they arrive.
92
+ *
93
+ * **This is the first surface in the package that is a client Component**, and that
94
+ * is the whole of what the fourth Kind is for. A Block takes its content as props,
95
+ * so it is rendered once and re-rendered when the consumer's framework decides; a
96
+ * `live` surface's content changes **without a navigation event**, which no amount
97
+ * of prop-passing expresses. So this owns a subscription and the accumulation
98
+ * behind it.
99
+ *
100
+ * **Prism owns the surface, the consumer owns the transport.** `subscribe` is a
101
+ * function the consumer supplies and a function it tears down. There is no socket
102
+ * in this file, no endpoint, no retry policy and no persistence, and there is no
103
+ * provider to mount: a consumer that already has a connection passes it in, and one
104
+ * that does not has not acquired a client runtime by importing this.
105
+ *
106
+ * **The events are kept sorted by `at`, not by arrival.** A consumer's transport
107
+ * decides when a message is delivered, and a reconnect can deliver an older event
108
+ * after a newer one. Sorting on arrival would put a run's own history out of order
109
+ * in exactly the case where a reader is most likely to be watching. Ties break on
110
+ * `id`, so the order is total and two runs of the same suite render the same log.
111
+ *
112
+ * **New events are announced once, politely, and the log itself is not a live
113
+ * region.** Announcing a growing list would make a screen reader read the whole run
114
+ * again on every arrival, which is the failure a live region on a list guarantees.
115
+ * `LiveRegion` carries the newest event's message alone, and the log is an ordinary
116
+ * scrollable list a reader can move through at their own pace.
117
+ *
118
+ * `limit` bounds what is held, because an agent run can produce thousands of events
119
+ * and a surface that grows without bound is a surface that eventually stops
120
+ * repainting. Older events fall off the top, which is the direction a reader wants:
121
+ * the run's beginning is the part they can scroll back to in another surface.
122
+ */
123
+ export declare function RunStream01({ subscribe, resubscribeKey, initial, status, statusLabel, eyebrow, title, description, emptyLabel, eventsLabel, liveLabel, limit, className, }: RunStream01Props): import("react").JSX.Element;
124
+ export default RunStream01;
125
+ //# sourceMappingURL=run-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/live/run-stream-01/run-stream.tsx"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEhE,6CAA6C;AAC7C,MAAM,MAAM,QAAQ,GAAG;IACrB,2FAA2F;IAC3F,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,YAAY,CAAA;IAClB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAA;IACV,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,6FAA6F;AAC7F,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE5E;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;AAE1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;OAIG;IACH,SAAS,EAAE,YAAY,CAAA;IACvB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC7B,gCAAgC;IAChC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,cAAc,EACd,OAAO,EACP,MAAM,EACN,WAAW,EACX,OAAO,EACP,KAAK,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,SAAS,EACT,KAAW,EACX,SAAS,GACV,EAAE,gBAAgB,+BA8HlB;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,111 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { LiveRegion } from '../../components/ui/live-region.js';
5
+ import { ScrollArea } from '../../components/ui/scroll-area.js';
6
+ import { cn } from '../../lib/utils.js';
7
+ /** The ink each role is stated in, and the tick beside it. */
8
+ const ROLE_INK = {
9
+ agent: 'text-foreground',
10
+ tool: 'text-foreground',
11
+ system: 'text-muted-foreground',
12
+ error: 'text-destructive',
13
+ };
14
+ /** The tick each role draws, so the four are told apart without relying on colour. */
15
+ const ROLE_MARK = {
16
+ agent: '●',
17
+ tool: 'â–¸',
18
+ system: '·',
19
+ error: '×',
20
+ };
21
+ const STATUS_INK = {
22
+ queued: 'text-muted-foreground',
23
+ running: 'text-foreground',
24
+ waiting: 'text-warning',
25
+ done: 'text-success',
26
+ failed: 'text-destructive',
27
+ };
28
+ /** The one source of truth for order, so a late event with an earlier `at` sorts there. */
29
+ const byTimeThenId = (a, b) => a.at - b.at || a.id.localeCompare(b.id);
30
+ /**
31
+ * The event log of a run, receiving events as they arrive.
32
+ *
33
+ * **This is the first surface in the package that is a client Component**, and that
34
+ * is the whole of what the fourth Kind is for. A Block takes its content as props,
35
+ * so it is rendered once and re-rendered when the consumer's framework decides; a
36
+ * `live` surface's content changes **without a navigation event**, which no amount
37
+ * of prop-passing expresses. So this owns a subscription and the accumulation
38
+ * behind it.
39
+ *
40
+ * **Prism owns the surface, the consumer owns the transport.** `subscribe` is a
41
+ * function the consumer supplies and a function it tears down. There is no socket
42
+ * in this file, no endpoint, no retry policy and no persistence, and there is no
43
+ * provider to mount: a consumer that already has a connection passes it in, and one
44
+ * that does not has not acquired a client runtime by importing this.
45
+ *
46
+ * **The events are kept sorted by `at`, not by arrival.** A consumer's transport
47
+ * decides when a message is delivered, and a reconnect can deliver an older event
48
+ * after a newer one. Sorting on arrival would put a run's own history out of order
49
+ * in exactly the case where a reader is most likely to be watching. Ties break on
50
+ * `id`, so the order is total and two runs of the same suite render the same log.
51
+ *
52
+ * **New events are announced once, politely, and the log itself is not a live
53
+ * region.** Announcing a growing list would make a screen reader read the whole run
54
+ * again on every arrival, which is the failure a live region on a list guarantees.
55
+ * `LiveRegion` carries the newest event's message alone, and the log is an ordinary
56
+ * scrollable list a reader can move through at their own pace.
57
+ *
58
+ * `limit` bounds what is held, because an agent run can produce thousands of events
59
+ * and a surface that grows without bound is a surface that eventually stops
60
+ * repainting. Older events fall off the top, which is the direction a reader wants:
61
+ * the run's beginning is the part they can scroll back to in another surface.
62
+ */
63
+ export function RunStream01({ subscribe, resubscribeKey, initial, status, statusLabel, eyebrow, title, description, emptyLabel, eventsLabel, liveLabel, limit = 500, className, }) {
64
+ /**
65
+ * The run's log, held against the key that produced it.
66
+ *
67
+ * Adjusting state during render when the key changes is React's documented
68
+ * pattern for "a prop changed, start over", and it is the right one here: the
69
+ * alternative is an effect, and an effect that clears the log runs *after* a
70
+ * render, so the surface would paint one frame holding the previous run's events
71
+ * under the new run's heading.
72
+ */
73
+ const [newest, setNewest] = useState(undefined);
74
+ const [run, setRun] = useState(() => ({
75
+ key: resubscribeKey,
76
+ events: [...(initial ?? [])].sort(byTimeThenId),
77
+ }));
78
+ if (run.key !== resubscribeKey) {
79
+ setRun({ key: resubscribeKey, events: [...(initial ?? [])].sort(byTimeThenId) });
80
+ // The announcement is cleared with the log it announced, so a run switch does
81
+ // not leave the previous run's last event in the live region saying so.
82
+ setNewest(undefined);
83
+ }
84
+ const events = run.events;
85
+ const setEvents = (next) => {
86
+ setRun((current) => ({ ...current, events: next(current.events) }));
87
+ };
88
+ // A ref rather than state, so the effect does not re-subscribe because an event
89
+ // arrived. An effect that depended on the value it writes re-runs itself, and the
90
+ // surface would open a new subscription per event.
91
+ const emit = useRef(() => { });
92
+ useEffect(() => {
93
+ emit.current = (event) => {
94
+ setEvents((current) => {
95
+ const next = [...current, event].sort(byTimeThenId);
96
+ return next.length > limit ? next.slice(next.length - limit) : next;
97
+ });
98
+ setNewest(event.message);
99
+ };
100
+ const stop = subscribe(emit.current);
101
+ return () => {
102
+ // Calling an unsubscribe the consumer did not return would throw during
103
+ // unmount, so a missing one is stepped over and the leak is the consumer's to
104
+ // fix rather than a crash in someone else's teardown.
105
+ if (typeof stop === 'function')
106
+ stop();
107
+ };
108
+ }, [subscribe, resubscribeKey, limit]);
109
+ return (_jsxs("section", { "data-slot": "run-stream", className: cn('border-border flex flex-col gap-4 rounded-lg border p-4', className), children: [LiveRegion({ politeness: 'polite', label: liveLabel, children: newest }), title !== undefined || eyebrow !== undefined || description !== undefined ? (_jsxs("header", { className: "flex flex-col gap-1", children: [eyebrow ? (_jsx("span", { className: "text-muted-foreground text-xs font-medium tracking-wide uppercase", children: eyebrow })) : null, title ? _jsx("h2", { className: "text-lg font-semibold tracking-tight", children: title }) : null, description ? (_jsx("p", { className: "text-muted-foreground text-pretty text-sm", children: description })) : null] })) : null, status !== undefined ? (_jsx("p", { "data-slot": "run-status", className: cn('font-mono text-xs', STATUS_INK[status]), children: statusLabel })) : null, events.length === 0 ? (_jsx("p", { "data-slot": "run-stream-empty", className: "text-muted-foreground text-sm", children: emptyLabel })) : (_jsx(ScrollArea, { label: eventsLabel, className: "max-h-80", children: _jsx("ol", { "data-slot": "run-events", className: "flex flex-col gap-2", children: events.map((event) => (_jsxs("li", { "data-slot": "run-event", "data-role": event.role, className: "grid grid-cols-[auto_auto_1fr] items-baseline gap-x-3 gap-y-0.5", children: [_jsx("span", { "aria-hidden": "true", className: cn('font-mono text-xs', ROLE_INK[event.role]), children: ROLE_MARK[event.role] }), _jsx("time", { dateTime: new Date(event.at).toISOString(), className: "text-muted-foreground font-mono text-xs tabular-nums", children: new Date(event.at).toISOString().slice(11, 19) }), _jsxs("span", { className: cn('text-pretty text-sm', ROLE_INK[event.role]), children: [event.message, event.detail ? (_jsx("span", { className: "text-muted-foreground block font-mono text-xs", children: event.detail })) : null] })] }, event.id))) }) }))] }));
110
+ }
111
+ export default RunStream01;
@@ -1,5 +1,6 @@
1
1
  import type { ComponentProps, ReactNode } from 'react';
2
- import { AuthForm01 } from '../../blocks/auth-form-01';
2
+ import { AuthForm01 } from '../../blocks/auth-form-01/index.js';
3
+ import type { HeadingLevel } from '../../components/ui/section.js';
3
4
  export type AuthPageAside = {
4
5
  /** The supporting card's title. */
5
6
  title: string;
@@ -17,6 +18,18 @@ export type AuthPageProps = {
17
18
  aside?: AuthPageAside;
18
19
  /** A node under the form, such as a legal line or a link to another screen. */
19
20
  footer?: ReactNode;
21
+ /**
22
+ * Heading level for the two region titles, the form's and the aside's. See
23
+ * `HeadingLevel`.
24
+ *
25
+ * The page draws no heading of its own, so each region names itself and the two
26
+ * are siblings in the outline. They therefore take one level between them
27
+ * rather than each choosing one: two regions that announced themselves at
28
+ * different depths would read as one inside the other, and nothing in the layout
29
+ * says so. Defaults to `h2`, because a page composes under a document that has
30
+ * its own `h1`.
31
+ */
32
+ headingLevel?: HeadingLevel;
20
33
  };
21
34
  /**
22
35
  * A complete authentication screen.
@@ -31,6 +44,6 @@ export type AuthPageProps = {
31
44
  * split from the large breakpoint up when one is. That keeps the docs preview
32
45
  * and a narrow screen on the same, readable path.
33
46
  */
34
- export declare function AuthPage({ form, aside, footer }: AuthPageProps): import("react").JSX.Element;
47
+ export declare function AuthPage({ form, aside, footer, headingLevel }: AuthPageProps): import("react").JSX.Element;
35
48
  export default AuthPage;
36
49
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/auth-page/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAStD,MAAM,MAAM,aAAa,GAAG;IAC1B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,2BAA2B;IAC3B,IAAI,EAAE,cAAc,CAAC,OAAO,UAAU,CAAC,CAAA;IACvC;;OAEG;IACH,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,aAAa,+BA+B9D;AAED,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/auth-page/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAQtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG;IAC1B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,2BAA2B;IAC3B,IAAI,EAAE,cAAc,CAAC,OAAO,UAAU,CAAC,CAAA;IACvC;;OAEG;IACH,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;CAC5B,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,YAAmB,EAAE,EAAE,aAAa,+BAqCnF;AAED,eAAe,QAAQ,CAAA"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { AuthForm01 } from '../../blocks/auth-form-01';
4
- import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '../../components/ui/card';
3
+ import { AuthForm01 } from '../../blocks/auth-form-01/index.js';
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '../../components/ui/card.js';
5
5
  /**
6
6
  * A complete authentication screen.
7
7
  *
@@ -15,9 +15,13 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '../.
15
15
  * split from the large breakpoint up when one is. That keeps the docs preview
16
16
  * and a narrow screen on the same, readable path.
17
17
  */
18
- export function AuthPage({ form, aside, footer }) {
18
+ export function AuthPage({ form, aside, footer, headingLevel = 'h2' }) {
19
+ // The form and the aside are sibling regions, so they announce themselves at the
20
+ // same depth: at different depths one would read as inside the other, and
21
+ // nothing in the layout says so.
22
+ const Title = headingLevel;
19
23
  return (_jsx("div", { className: "bg-background text-foreground flex min-h-svh w-full items-center justify-center px-6 py-16", children: _jsxs("div", { className: aside
20
24
  ? 'grid w-full max-w-page items-center gap-10 lg:grid-cols-2'
21
- : 'w-full max-w-measure-narrow', children: [aside ? (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: aside.title }), aside.description ? (_jsx(CardDescription, { children: aside.description })) : null] }), aside.children ? _jsx(CardContent, { children: aside.children }) : null] })) : null, _jsxs("div", { className: "mx-auto w-full max-w-measure-narrow", children: [_jsx(AuthForm01, { ...form }), footer ? (_jsx("div", { className: "text-muted-foreground mt-4 text-center text-sm", children: footer })) : null] })] }) }));
25
+ : 'w-full max-w-measure-narrow', children: [aside ? (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: _jsx(Title, { children: aside.title }) }), aside.description ? (_jsx(CardDescription, { children: aside.description })) : null] }), aside.children ? _jsx(CardContent, { children: aside.children }) : null] })) : null, _jsxs("div", { className: "mx-auto w-full max-w-measure-narrow", children: [_jsx(AuthForm01, { ...form, headingLevel: headingLevel }), footer ? (_jsx("div", { className: "text-muted-foreground mt-4 text-center text-sm", children: footer })) : null] })] }) }));
22
26
  }
23
27
  export default AuthPage;
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Badge } from '../../components/ui/badge';
3
- import { CtaLink } from '../../components/ui/cta-link';
4
- import { Heading, Text } from '../../components/ui/typography';
5
- import { Prose } from '../../components/ui/prose';
6
- import { Section } from '../../components/ui/section';
2
+ import { Badge } from '../../components/ui/badge.js';
3
+ import { CtaLink } from '../../components/ui/cta-link.js';
4
+ import { Heading, Text } from '../../components/ui/typography.js';
5
+ import { Prose } from '../../components/ui/prose.js';
6
+ import { Section } from '../../components/ui/section.js';
7
7
  /**
8
8
  * A complete blog post screen: the title, the standfirst, the byline, the body at
9
9
  * the measure, and the trail to the neighbouring posts.
@@ -1,8 +1,8 @@
1
1
  import type { ComponentProps } from 'react';
2
- import { AppShell01 } from '../../blocks/app-shell-01';
3
- import { DataTable01 } from '../../blocks/data-table-01';
4
- import { PageHeader01 } from '../../blocks/page-header-01';
5
- import { Stats01 } from '../../blocks/stats-01';
2
+ import { AppShell01 } from '../../blocks/app-shell-01/index.js';
3
+ import { DataTable01 } from '../../blocks/data-table-01/index.js';
4
+ import { PageHeader01 } from '../../blocks/page-header-01/index.js';
5
+ import { Stats01 } from '../../blocks/stats-01/index.js';
6
6
  export type DashboardPageProps = {
7
7
  /**
8
8
  * The frame: the navigation rail, the top bar and the mobile navigation
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { AppShell01 } from '../../blocks/app-shell-01';
4
- import { DataTable01 } from '../../blocks/data-table-01';
5
- import { PageHeader01 } from '../../blocks/page-header-01';
6
- import { Stats01 } from '../../blocks/stats-01';
3
+ import { AppShell01 } from '../../blocks/app-shell-01/index.js';
4
+ import { DataTable01 } from '../../blocks/data-table-01/index.js';
5
+ import { PageHeader01 } from '../../blocks/page-header-01/index.js';
6
+ import { Stats01 } from '../../blocks/stats-01/index.js';
7
7
  /**
8
8
  * A complete dashboard screen.
9
9
  *
@@ -5,12 +5,27 @@ import type { ReactNode } from 'react';
5
5
  * `href` is required. A page in the navigation that goes nowhere is an entry a
6
6
  * reader can focus and not follow, and the Page renders every page entry as a
7
7
  * native anchor so a reader can see where a destination is before taking it.
8
+ *
9
+ * **Required is not the same as enforced, and the difference is stated here
10
+ * rather than left to be discovered.** `href: string` admits `''`, because the
11
+ * empty string is a legal `string` and the only type that refuses it is a branded
12
+ * one, which would put an `as` in every consumer's adapter for three sites this
13
+ * Page is already published to. So the type admits the value and `readTree`
14
+ * refuses it, at the boundary, by throwing and naming the entry. The promise this
15
+ * block used to make was true only of the group arm; it is now true of what the
16
+ * Page renders, and it says which of the two is doing the work.
17
+ *
18
+ * `title` may be the empty string, and an entry with no words is not a page: it
19
+ * renders as a label on the same rule that makes a group with no index a label,
20
+ * and it is not a page the pager can reach either. A link with no accessible name
21
+ * is announced as "link" and nothing else, which is the one thing a link in a
22
+ * rail must never be.
8
23
  */
9
24
  export type DocsNavPage = {
10
25
  type: 'page';
11
- /** The words the reader meets. */
26
+ /** The words the reader meets. An empty string makes this a label, not a link. */
12
27
  title: string;
13
- /** The address the page is read at. */
28
+ /** The address the page is read at. An empty string is refused, by name. */
14
29
  href: string;
15
30
  };
16
31
  /**
@@ -37,6 +52,13 @@ export type DocsNavGroup = {
37
52
  /**
38
53
  * The group's own index page, where it has one. Omit it for a group that is
39
54
  * only a heading over its pages.
55
+ *
56
+ * An empty or blank string is the same answer as omitting it, and deliberately
57
+ * so: all three consumer adapters write `url: node.index?.url ?? ''` for a
58
+ * folder holding no `index.mdx`, so the empty string is how they have always
59
+ * spelled "this group is not a route", and reading it as the absence is what
60
+ * keeps those adapters correct without a change in any of them. A page has no
61
+ * such reading: there, an empty address is a fault, and `readTree` says so.
40
62
  */
41
63
  href?: string;
42
64
  /** The pages under this group, in the order a reader should meet them. */
@@ -209,8 +231,19 @@ export type DocsShellProps = {
209
231
  * consumer sites all write `url: node.index?.url ?? ''` in their own adapter
210
232
  * today, which is the same fact spelled as an empty string, and two of the three
211
233
  * then carry a stylesheet rule that styles the resulting anchor-with-no-href
212
- * back into a label. Naming the case in the type makes those two rules dead
213
- * rather than load-bearing, and the type is the only place that had to change.
234
+ * back into a label. So the Page reads an empty or blank `href` as the absence on
235
+ * this arm, and the two stylesheet rules are dead rather than load-bearing. It
236
+ * takes one function rather than only a type, because an optional field the
237
+ * caller spells as `''` is not an optional field the type can see.
238
+ *
239
+ * **An entry with no words is a label too, and a page with no address is
240
+ * refused.** Both halves of "what makes this a destination" are answered in one
241
+ * function, `destinationOf`, and both the rail and the pager ask it. An entry
242
+ * that is a label on the rail is not a page the pager reaches either, so one
243
+ * malformed row cannot become two surfaces' worth of broken link. A page whose
244
+ * address is empty or blank has no such reading: there is nothing to render in
245
+ * place of the link, so the Page throws, naming the entry, before any of the tree
246
+ * renders.
214
247
  *
215
248
  * **The pager is derived from the navigation rather than passed.** The rail
216
249
  * already knows every page and the order a reader meets them in, and deriving the
@@ -1 +1 @@
1
- {"version":3,"file":"docs-shell.d.ts","sourceRoot":"","sources":["../../../src/pages/docs-shell/docs-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAC/B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAA;AAEtE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,EAAE,eAAe,CAAA;IAC5B;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,GAAG,EACH,GAAG,EACH,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE,cAAc,+BAmFhB;AA4QD,eAAe,SAAS,CAAA"}
1
+ {"version":3,"file":"docs-shell.d.ts","sourceRoot":"","sources":["../../../src/pages/docs-shell/docs-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAC/B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAA;AAEtE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,EAAE,eAAe,CAAA;IAC5B;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,GAAG,EACH,GAAG,EACH,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE,cAAc,+BAsFhB;AAwWD,eAAe,SAAS,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Prose } from '../../components/ui/prose';
3
- import { Heading, Text } from '../../components/ui/typography';
4
- import { cn } from '../../lib/utils';
2
+ import { Prose } from '../../components/ui/prose.js';
3
+ import { Heading, Text } from '../../components/ui/typography.js';
4
+ import { cn } from '../../lib/utils.js';
5
5
  /**
6
6
  * A documentation screen: a navigation rail, the document, a contents rail and
7
7
  * the pager to the neighbouring pages.
@@ -31,8 +31,19 @@ import { cn } from '../../lib/utils';
31
31
  * consumer sites all write `url: node.index?.url ?? ''` in their own adapter
32
32
  * today, which is the same fact spelled as an empty string, and two of the three
33
33
  * then carry a stylesheet rule that styles the resulting anchor-with-no-href
34
- * back into a label. Naming the case in the type makes those two rules dead
35
- * rather than load-bearing, and the type is the only place that had to change.
34
+ * back into a label. So the Page reads an empty or blank `href` as the absence on
35
+ * this arm, and the two stylesheet rules are dead rather than load-bearing. It
36
+ * takes one function rather than only a type, because an optional field the
37
+ * caller spells as `''` is not an optional field the type can see.
38
+ *
39
+ * **An entry with no words is a label too, and a page with no address is
40
+ * refused.** Both halves of "what makes this a destination" are answered in one
41
+ * function, `destinationOf`, and both the rail and the pager ask it. An entry
42
+ * that is a label on the rail is not a page the pager reaches either, so one
43
+ * malformed row cannot become two surfaces' worth of broken link. A page whose
44
+ * address is empty or blank has no such reading: there is nothing to render in
45
+ * place of the link, so the Page throws, naming the entry, before any of the tree
46
+ * renders.
36
47
  *
37
48
  * **The pager is derived from the navigation rather than passed.** The rail
38
49
  * already knows every page and the order a reader meets them in, and deriving the
@@ -68,6 +79,8 @@ import { cn } from '../../lib/utils';
68
79
  * and hands it the document their pipeline produced.
69
80
  */
70
81
  export function DocsShell({ title, description, nav, toc, currentHref, navLabel, tocLabel, pagerLabel, pagerLabels, header, footer, className, children, }) {
82
+ readTree(nav, 'nav');
83
+ readTree(toc, 'toc');
71
84
  const rail = nav && nav.length > 0 ? nav : undefined;
72
85
  const contents = toc && toc.length > 0 ? toc : undefined;
73
86
  const neighbours = currentHref === undefined ? undefined : deriveNeighbours(nav ?? [], currentHref);
@@ -79,6 +92,68 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
79
92
  // them.
80
93
  contents ? 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)] xl:grid-cols-[15rem_minmax(0,1fr)_13rem]' : 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)]'), children: [rail ? (_jsx("aside", { "data-slot": "docs-rail", className: "hidden lg:col-start-1 lg:row-start-1 lg:block", children: _jsx("nav", { "aria-label": navLabel, className: "lg:sticky lg:top-20 lg:max-h-[calc(100svh-7rem)] lg:overflow-y-auto lg:pe-4", children: _jsx(NavList, { entries: rail, currentHref: currentHref, depth: 0 }) }) })) : null, _jsxs("article", { "data-slot": "docs-article", className: "flex min-w-0 flex-col gap-8", children: [title ? (_jsxs("header", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h1", size: "3xl", children: title }), description ? (_jsx(Text, { size: "lg", tone: "muted", className: "max-w-measure", children: description })) : null] })) : description ? (_jsx(Text, { size: "lg", tone: "muted", className: "max-w-measure", children: description })) : null, _jsx(Prose, { fullWidth: true, children: children }), neighbours ? (_jsx(Pager, { previous: neighbours.previous, next: neighbours.next, label: pagerLabel, labels: pagerLabels })) : null] }), contents ? (_jsx("aside", { "data-slot": "docs-contents", className: "hidden lg:col-start-3 lg:row-start-1 lg:block", children: _jsx("nav", { "aria-label": tocLabel, className: "lg:sticky lg:top-20 lg:max-h-[calc(100svh-7rem)] lg:overflow-y-auto", children: _jsx(NavList, { entries: contents, currentHref: currentHref, depth: 0 }) }) })) : null] }), footer] }));
81
94
  }
95
+ /* ------------------------------------------------------------------ *
96
+ * The tree
97
+ * ------------------------------------------------------------------ */
98
+ /**
99
+ * The address an entry has, or `undefined` when it has none.
100
+ *
101
+ * Blank counts as none, which is not pedantry. All three consumer adapters write
102
+ * `url: node.index?.url ?? ''` for a folder holding no index, so the empty string
103
+ * is how they spell "this group is not a route", and a guard that read only
104
+ * `=== undefined` let that same spelling through to the renderer on the arm where
105
+ * it means something else entirely.
106
+ */
107
+ function addressOf(href) {
108
+ return href !== undefined && href.trim() !== '' ? href : undefined;
109
+ }
110
+ /**
111
+ * The address an entry is reachable at, or `undefined` when it is not a
112
+ * destination at all.
113
+ *
114
+ * An entry is a destination only when it has an address AND words, and both
115
+ * halves live here so that one rule answers for both surfaces that read a tree.
116
+ * The rail asks it to decide between an anchor and a label; the pager asks it to
117
+ * decide between a neighbour and nothing. Two places deciding that separately is
118
+ * how one malformed row becomes a nameless anchor in the rail and a nameless
119
+ * `Next` link under it.
120
+ */
121
+ function destinationOf(entry) {
122
+ return entry.title === '' ? undefined : addressOf(entry.href);
123
+ }
124
+ /**
125
+ * Reads a tree before any of it renders, and refuses the one shape it cannot
126
+ * render honestly.
127
+ *
128
+ * A page entry whose address is empty or blank is an entry whose whole content is
129
+ * a route the consumer does not have. There is no honest rendering of it: a label
130
+ * in its place would hide a page the tree is missing rather than report it, and an
131
+ * anchor would be a control a reader can focus and cannot operate. So the Page
132
+ * throws, naming the entry, which is the opposite of copy and reaches a developer
133
+ * rather than a reader.
134
+ *
135
+ * Two things are deliberate here. The pass is in the component and not in a
136
+ * renderer, so it runs once for both trees and cannot be reached only on the path
137
+ * that happens to draw the bad row. And a GROUP is not refused: no address is a
138
+ * documented state for a group and the empty string is how all three consumers
139
+ * already spell it, so it renders as a label. Only the arm where an address is
140
+ * the entire entry is a fault.
141
+ */
142
+ function readTree(entries, source) {
143
+ for (const entry of entries ?? []) {
144
+ if (entry.type === 'divider')
145
+ continue;
146
+ if (entry.type === 'group') {
147
+ readTree(entry.items, source);
148
+ continue;
149
+ }
150
+ if (addressOf(entry.href) === undefined) {
151
+ throw new Error(`docs-shell: a page entry in the ${source} has no address, and an anchor with no address is a ` +
152
+ 'control a reader can focus and cannot follow. Give the entry the address its page is read at, or ' +
153
+ `remove it from the tree. Its title is ${JSON.stringify(entry.title)}.`);
154
+ }
155
+ }
156
+ }
82
157
  /* ------------------------------------------------------------------ *
83
158
  * The rail
84
159
  * ------------------------------------------------------------------ */
@@ -111,12 +186,17 @@ function NavNode({ entry, currentHref, depth, }) {
111
186
  // above the hairline rather than a word a reader has to interpret.
112
187
  return (_jsx("li", { "data-slot": "docs-nav-divider", className: "border-border mt-2 border-t pt-2 first:mt-0 first:border-t-0 first:pt-0", children: entry.title ? (_jsx("span", { className: "text-muted-foreground text-xs font-medium tracking-wide uppercase", children: entry.title })) : null }));
113
188
  }
189
+ const href = destinationOf(entry);
114
190
  if (entry.type === 'group') {
115
- return (_jsx("li", { "data-slot": "docs-nav-group", children: _jsxs("div", { className: "flex flex-col gap-1", children: [entry.href === undefined ? (_jsx("span", { "data-slot": "docs-nav-label", className: cn('rounded-sm text-sm font-semibold tracking-tight', containsHref(entry, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })) : (_jsx("a", { "data-slot": "docs-nav-heading", href: entry.href, "aria-current": currentHref === entry.href ? 'page' : undefined, className: cn('hover:text-foreground rounded-sm text-sm font-semibold tracking-tight transition-colors duration-fast ease-out', under(entry.href, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })), entry.items.length > 0 ? (_jsx(NavList, { entries: entry.items, currentHref: currentHref, depth: depth + 1 })) : null] }) }));
191
+ return (_jsx("li", { "data-slot": "docs-nav-group", children: _jsxs("div", { className: "flex flex-col gap-1", children: [href === undefined ? (_jsx("span", { "data-slot": "docs-nav-label", className: cn('rounded-sm text-sm font-semibold tracking-tight', containsHref(entry, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })) : (_jsx("a", { "data-slot": "docs-nav-heading", href: href, "aria-current": currentHref === href ? 'page' : undefined, className: cn('hover:text-foreground rounded-sm text-sm font-semibold tracking-tight transition-colors duration-fast ease-out', under(href, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })), entry.items.length > 0 ? (_jsx(NavList, { entries: entry.items, currentHref: currentHref, depth: depth + 1 })) : null] }) }));
116
192
  }
117
- return (_jsx("li", { "data-slot": "docs-nav-page", children: _jsx("a", { "data-slot": "docs-nav-link", href: entry.href, "aria-current": currentHref === entry.href ? 'page' : undefined, className: cn('-ms-px block border-l-2 py-1 ps-3 text-sm transition-colors duration-fast ease-out', currentHref === entry.href
193
+ // The page arm. `href` is never empty here: `readTree` refused that above and
194
+ // named the entry, so what is left to decide is the words. A nameless page is
195
+ // the same rule as a group with no index, rendered as a label, because a link
196
+ // with no accessible name is announced as "link" and nothing else.
197
+ return (_jsx("li", { "data-slot": "docs-nav-page", children: href === undefined ? (_jsx("span", { "data-slot": "docs-nav-label", className: "-ms-px block border-l-2 border-transparent py-1 ps-3 text-sm text-muted-foreground", children: entry.title })) : (_jsx("a", { "data-slot": "docs-nav-link", href: href, "aria-current": currentHref === href ? 'page' : undefined, className: cn('-ms-px block border-l-2 py-1 ps-3 text-sm transition-colors duration-fast ease-out', currentHref === href
118
198
  ? 'text-foreground border-primary font-medium'
119
- : 'text-muted-foreground hover:text-foreground border-transparent'), children: entry.title }) }));
199
+ : 'text-muted-foreground hover:text-foreground border-transparent'), children: entry.title })) }));
120
200
  }
121
201
  /**
122
202
  * Every page the tree holds, in the order a reader meets them.
@@ -126,18 +206,23 @@ function NavNode({ entry, currentHref, depth, }) {
126
206
  * contributes its children and nothing else, which is the second thing that
127
207
  * makes an absent `href` a label rather than a route: there is no page there for
128
208
  * the pager to reach either. A divider contributes nothing at all.
209
+ *
210
+ * The row a reader can click and the row the pager can reach are decided by the
211
+ * SAME function, `destinationOf`. A nameless entry is a label on the rail and is
212
+ * dropped here, so one malformed row cannot be published twice: a nameless anchor
213
+ * in the rail, and a `Next` link under it carrying the text of whichever entry
214
+ * happened to follow, is what deriving without asking produced. The two surfaces
215
+ * do not each get their own opinion about a row that is not a page.
129
216
  */
130
217
  function flatten(entries, into = []) {
131
218
  for (const entry of entries) {
132
- if (entry.type === 'page') {
133
- into.push({ title: entry.title, href: entry.href });
219
+ if (entry.type === 'divider')
134
220
  continue;
135
- }
136
- if (entry.type === 'group') {
137
- if (entry.href !== undefined)
138
- into.push({ title: entry.title, href: entry.href });
221
+ const href = destinationOf(entry);
222
+ if (href !== undefined)
223
+ into.push({ title: entry.title, href });
224
+ if (entry.type === 'group')
139
225
  flatten(entry.items, into);
140
- }
141
226
  }
142
227
  return into;
143
228
  }
@@ -182,9 +267,15 @@ function under(href, currentHref) {
182
267
  * A group with a route answers for that route and everything under it. A group
183
268
  * without one has no route to answer with, so it asks its children, which is the
184
269
  * only way a label can still show a reader that they are inside it.
270
+ *
271
+ * The group's own address is read through `addressOf`, like every other read of
272
+ * it. A group whose `href` is the empty string is a label, and a blank string
273
+ * handed to `under` as though it were a route matches every absolute address on
274
+ * the site, which is every label-only section claiming to be the current one at
275
+ * once.
185
276
  */
186
277
  function containsHref(group, currentHref) {
187
- if (under(group.href, currentHref))
278
+ if (under(addressOf(group.href), currentHref))
188
279
  return true;
189
280
  return group.items.some((entry) => {
190
281
  if (entry.type === 'page')
@@ -1,3 +1,3 @@
1
- export { DocsShell, default } from './docs-shell';
2
- export type { DocsShellProps, DocsNavEntry, DocsNavPage, DocsNavGroup, DocsNavDivider, DocsPagerLabels, } from './docs-shell';
1
+ export { DocsShell, default } from './docs-shell.js';
2
+ export type { DocsShellProps, DocsNavEntry, DocsNavPage, DocsNavGroup, DocsNavDivider, DocsPagerLabels, } from './docs-shell.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- export { DocsShell, default } from './docs-shell';
1
+ export { DocsShell, default } from './docs-shell.js';