@nanisoft/prism-ui 0.5.0 → 0.6.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 (303) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/THIRD-PARTY-NOTICES.md +48 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/blocks/app-shell-01/app-shell.d.ts +38 -0
  6. package/dist/blocks/app-shell-01/app-shell.d.ts.map +1 -0
  7. package/dist/blocks/app-shell-01/app-shell.js +27 -0
  8. package/dist/blocks/app-shell-01/index.d.ts +3 -0
  9. package/dist/blocks/app-shell-01/index.d.ts.map +1 -0
  10. package/dist/blocks/app-shell-01/index.js +1 -0
  11. package/dist/blocks/auth-form-01/auth-form.d.ts +52 -0
  12. package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -0
  13. package/dist/blocks/auth-form-01/auth-form.js +23 -0
  14. package/dist/blocks/auth-form-01/index.d.ts +3 -0
  15. package/dist/blocks/auth-form-01/index.d.ts.map +1 -0
  16. package/dist/blocks/auth-form-01/index.js +1 -0
  17. package/dist/blocks/cta-01/cta.d.ts +83 -0
  18. package/dist/blocks/cta-01/cta.d.ts.map +1 -0
  19. package/dist/blocks/cta-01/cta.js +29 -0
  20. package/dist/blocks/cta-01/index.d.ts +3 -0
  21. package/dist/blocks/cta-01/index.d.ts.map +1 -0
  22. package/dist/blocks/cta-01/index.js +1 -0
  23. package/dist/blocks/data-table-01/data-table.d.ts +102 -0
  24. package/dist/blocks/data-table-01/data-table.d.ts.map +1 -0
  25. package/dist/blocks/data-table-01/data-table.js +116 -0
  26. package/dist/blocks/data-table-01/index.d.ts +3 -0
  27. package/dist/blocks/data-table-01/index.d.ts.map +1 -0
  28. package/dist/blocks/data-table-01/index.js +1 -0
  29. package/dist/blocks/feature-grid-01/feature-grid.d.ts +78 -0
  30. package/dist/blocks/feature-grid-01/feature-grid.d.ts.map +1 -0
  31. package/dist/blocks/feature-grid-01/feature-grid.js +24 -0
  32. package/dist/blocks/feature-grid-01/index.d.ts +3 -0
  33. package/dist/blocks/feature-grid-01/index.d.ts.map +1 -0
  34. package/dist/blocks/feature-grid-01/index.js +1 -0
  35. package/dist/blocks/hero-01/hero.d.ts +59 -0
  36. package/dist/blocks/hero-01/hero.d.ts.map +1 -0
  37. package/dist/blocks/hero-01/hero.js +24 -0
  38. package/dist/blocks/hero-01/index.d.ts +2 -0
  39. package/dist/blocks/hero-01/index.d.ts.map +1 -0
  40. package/dist/blocks/hero-01/index.js +1 -0
  41. package/dist/blocks/index.d.ts +28 -0
  42. package/dist/blocks/index.d.ts.map +1 -0
  43. package/dist/blocks/index.js +26 -0
  44. package/dist/blocks/instrument-panel-01/index.d.ts +3 -0
  45. package/dist/blocks/instrument-panel-01/index.d.ts.map +1 -0
  46. package/dist/blocks/instrument-panel-01/index.js +1 -0
  47. package/dist/blocks/instrument-panel-01/instrument-panel.d.ts +114 -0
  48. package/dist/blocks/instrument-panel-01/instrument-panel.d.ts.map +1 -0
  49. package/dist/blocks/instrument-panel-01/instrument-panel.js +64 -0
  50. package/dist/blocks/logo-strip-01/index.d.ts +3 -0
  51. package/dist/blocks/logo-strip-01/index.d.ts.map +1 -0
  52. package/dist/blocks/logo-strip-01/index.js +1 -0
  53. package/dist/blocks/logo-strip-01/logo-strip.d.ts +84 -0
  54. package/dist/blocks/logo-strip-01/logo-strip.d.ts.map +1 -0
  55. package/dist/blocks/logo-strip-01/logo-strip.js +40 -0
  56. package/dist/blocks/note-grid-01/index.d.ts +3 -0
  57. package/dist/blocks/note-grid-01/index.d.ts.map +1 -0
  58. package/dist/blocks/note-grid-01/index.js +1 -0
  59. package/dist/blocks/note-grid-01/note-grid.d.ts +79 -0
  60. package/dist/blocks/note-grid-01/note-grid.d.ts.map +1 -0
  61. package/dist/blocks/note-grid-01/note-grid.js +37 -0
  62. package/dist/blocks/page-header-01/index.d.ts +3 -0
  63. package/dist/blocks/page-header-01/index.d.ts.map +1 -0
  64. package/dist/blocks/page-header-01/index.js +1 -0
  65. package/dist/blocks/page-header-01/page-header.d.ts +48 -0
  66. package/dist/blocks/page-header-01/page-header.d.ts.map +1 -0
  67. package/dist/blocks/page-header-01/page-header.js +27 -0
  68. package/dist/blocks/pricing-01/index.d.ts +2 -0
  69. package/dist/blocks/pricing-01/index.d.ts.map +1 -0
  70. package/dist/blocks/pricing-01/index.js +1 -0
  71. package/dist/blocks/pricing-01/pricing.d.ts +35 -0
  72. package/dist/blocks/pricing-01/pricing.d.ts.map +1 -0
  73. package/dist/blocks/pricing-01/pricing.js +20 -0
  74. package/dist/blocks/process-rail-01/index.d.ts +3 -0
  75. package/dist/blocks/process-rail-01/index.d.ts.map +1 -0
  76. package/dist/blocks/process-rail-01/index.js +1 -0
  77. package/dist/blocks/process-rail-01/process-rail.d.ts +99 -0
  78. package/dist/blocks/process-rail-01/process-rail.d.ts.map +1 -0
  79. package/dist/blocks/process-rail-01/process-rail.js +58 -0
  80. package/dist/blocks/product-grid-01/index.d.ts +3 -0
  81. package/dist/blocks/product-grid-01/index.d.ts.map +1 -0
  82. package/dist/blocks/product-grid-01/index.js +1 -0
  83. package/dist/blocks/product-grid-01/product-grid.d.ts +114 -0
  84. package/dist/blocks/product-grid-01/product-grid.d.ts.map +1 -0
  85. package/dist/blocks/product-grid-01/product-grid.js +51 -0
  86. package/dist/blocks/settings-panel-01/index.d.ts +3 -0
  87. package/dist/blocks/settings-panel-01/index.d.ts.map +1 -0
  88. package/dist/blocks/settings-panel-01/index.js +1 -0
  89. package/dist/blocks/settings-panel-01/settings-panel.d.ts +71 -0
  90. package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -0
  91. package/dist/blocks/settings-panel-01/settings-panel.js +38 -0
  92. package/dist/blocks/site-footer/index.d.ts +3 -0
  93. package/dist/blocks/site-footer/index.d.ts.map +1 -0
  94. package/dist/blocks/site-footer/index.js +1 -0
  95. package/dist/blocks/site-footer/site-footer.d.ts +135 -0
  96. package/dist/blocks/site-footer/site-footer.d.ts.map +1 -0
  97. package/dist/blocks/site-footer/site-footer.js +41 -0
  98. package/dist/blocks/site-header/index.d.ts +3 -0
  99. package/dist/blocks/site-header/index.d.ts.map +1 -0
  100. package/dist/blocks/site-header/index.js +1 -0
  101. package/dist/blocks/site-header/site-header.d.ts +142 -0
  102. package/dist/blocks/site-header/site-header.d.ts.map +1 -0
  103. package/dist/blocks/site-header/site-header.js +46 -0
  104. package/dist/blocks/stack-grid-01/index.d.ts +3 -0
  105. package/dist/blocks/stack-grid-01/index.d.ts.map +1 -0
  106. package/dist/blocks/stack-grid-01/index.js +1 -0
  107. package/dist/blocks/stack-grid-01/stack-grid.d.ts +118 -0
  108. package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -0
  109. package/dist/blocks/stack-grid-01/stack-grid.js +49 -0
  110. package/dist/blocks/stats-01/index.d.ts +2 -0
  111. package/dist/blocks/stats-01/index.d.ts.map +1 -0
  112. package/dist/blocks/stats-01/index.js +1 -0
  113. package/dist/blocks/stats-01/stats.d.ts +28 -0
  114. package/dist/blocks/stats-01/stats.d.ts.map +1 -0
  115. package/dist/blocks/stats-01/stats.js +27 -0
  116. package/dist/blocks/status-ledger-01/index.d.ts +3 -0
  117. package/dist/blocks/status-ledger-01/index.d.ts.map +1 -0
  118. package/dist/blocks/status-ledger-01/index.js +1 -0
  119. package/dist/blocks/status-ledger-01/status-ledger.d.ts +131 -0
  120. package/dist/blocks/status-ledger-01/status-ledger.d.ts.map +1 -0
  121. package/dist/blocks/status-ledger-01/status-ledger.js +80 -0
  122. package/dist/catalog.d.ts +28 -0
  123. package/dist/catalog.d.ts.map +1 -0
  124. package/dist/catalog.js +672 -0
  125. package/dist/components/index.d.ts +64 -0
  126. package/dist/components/index.d.ts.map +1 -0
  127. package/dist/components/index.js +43 -0
  128. package/dist/components/ui/accordion.d.ts +65 -0
  129. package/dist/components/ui/accordion.d.ts.map +1 -0
  130. package/dist/components/ui/accordion.js +30 -0
  131. package/dist/components/ui/alert.d.ts +19 -0
  132. package/dist/components/ui/alert.d.ts.map +1 -0
  133. package/dist/components/ui/alert.js +31 -0
  134. package/dist/components/ui/avatar.d.ts +47 -0
  135. package/dist/components/ui/avatar.d.ts.map +1 -0
  136. package/dist/components/ui/avatar.js +30 -0
  137. package/dist/components/ui/badge.d.ts +14 -0
  138. package/dist/components/ui/badge.d.ts.map +1 -0
  139. package/dist/components/ui/badge.js +26 -0
  140. package/dist/components/ui/breadcrumb.d.ts +35 -0
  141. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  142. package/dist/components/ui/breadcrumb.js +48 -0
  143. package/dist/components/ui/button.d.ts +16 -0
  144. package/dist/components/ui/button.d.ts.map +1 -0
  145. package/dist/components/ui/button.js +63 -0
  146. package/dist/components/ui/card.d.ts +41 -0
  147. package/dist/components/ui/card.d.ts.map +1 -0
  148. package/dist/components/ui/card.js +53 -0
  149. package/dist/components/ui/checkbox.d.ts +43 -0
  150. package/dist/components/ui/checkbox.d.ts.map +1 -0
  151. package/dist/components/ui/checkbox.js +18 -0
  152. package/dist/components/ui/cta-link.d.ts +45 -0
  153. package/dist/components/ui/cta-link.d.ts.map +1 -0
  154. package/dist/components/ui/cta-link.js +81 -0
  155. package/dist/components/ui/diagram.d.ts +154 -0
  156. package/dist/components/ui/diagram.d.ts.map +1 -0
  157. package/dist/components/ui/diagram.js +150 -0
  158. package/dist/components/ui/dialog.d.ts +81 -0
  159. package/dist/components/ui/dialog.d.ts.map +1 -0
  160. package/dist/components/ui/dialog.js +60 -0
  161. package/dist/components/ui/dropdown-menu.d.ts +189 -0
  162. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  163. package/dist/components/ui/dropdown-menu.js +67 -0
  164. package/dist/components/ui/fact-list.d.ts +90 -0
  165. package/dist/components/ui/fact-list.d.ts.map +1 -0
  166. package/dist/components/ui/fact-list.js +38 -0
  167. package/dist/components/ui/field.d.ts +36 -0
  168. package/dist/components/ui/field.d.ts.map +1 -0
  169. package/dist/components/ui/field.js +46 -0
  170. package/dist/components/ui/input.d.ts +12 -0
  171. package/dist/components/ui/input.d.ts.map +1 -0
  172. package/dist/components/ui/input.js +14 -0
  173. package/dist/components/ui/kbd.d.ts +10 -0
  174. package/dist/components/ui/kbd.d.ts.map +1 -0
  175. package/dist/components/ui/kbd.js +12 -0
  176. package/dist/components/ui/pagination.d.ts +40 -0
  177. package/dist/components/ui/pagination.d.ts.map +1 -0
  178. package/dist/components/ui/pagination.js +49 -0
  179. package/dist/components/ui/popover.d.ts +56 -0
  180. package/dist/components/ui/popover.d.ts.map +1 -0
  181. package/dist/components/ui/popover.js +25 -0
  182. package/dist/components/ui/product-mark.d.ts +115 -0
  183. package/dist/components/ui/product-mark.d.ts.map +1 -0
  184. package/dist/components/ui/product-mark.js +82 -0
  185. package/dist/components/ui/product-switcher.d.ts +108 -0
  186. package/dist/components/ui/product-switcher.d.ts.map +1 -0
  187. package/dist/components/ui/product-switcher.js +47 -0
  188. package/dist/components/ui/progress.d.ts +38 -0
  189. package/dist/components/ui/progress.d.ts.map +1 -0
  190. package/dist/components/ui/progress.js +17 -0
  191. package/dist/components/ui/prose.d.ts +88 -0
  192. package/dist/components/ui/prose.d.ts.map +1 -0
  193. package/dist/components/ui/prose.js +53 -0
  194. package/dist/components/ui/radio-group.d.ts +66 -0
  195. package/dist/components/ui/radio-group.d.ts.map +1 -0
  196. package/dist/components/ui/radio-group.js +30 -0
  197. package/dist/components/ui/section.d.ts +60 -0
  198. package/dist/components/ui/section.d.ts.map +1 -0
  199. package/dist/components/ui/section.js +21 -0
  200. package/dist/components/ui/select.d.ts +124 -0
  201. package/dist/components/ui/select.d.ts.map +1 -0
  202. package/dist/components/ui/select.js +47 -0
  203. package/dist/components/ui/separator.d.ts +16 -0
  204. package/dist/components/ui/separator.d.ts.map +1 -0
  205. package/dist/components/ui/separator.js +15 -0
  206. package/dist/components/ui/skeleton.d.ts +12 -0
  207. package/dist/components/ui/skeleton.d.ts.map +1 -0
  208. package/dist/components/ui/skeleton.js +14 -0
  209. package/dist/components/ui/slider.d.ts +43 -0
  210. package/dist/components/ui/slider.d.ts.map +1 -0
  211. package/dist/components/ui/slider.js +23 -0
  212. package/dist/components/ui/switch.d.ts +42 -0
  213. package/dist/components/ui/switch.d.ts.map +1 -0
  214. package/dist/components/ui/switch.js +16 -0
  215. package/dist/components/ui/table.d.ts +27 -0
  216. package/dist/components/ui/table.d.ts.map +1 -0
  217. package/dist/components/ui/table.js +43 -0
  218. package/dist/components/ui/tabs.d.ts +66 -0
  219. package/dist/components/ui/tabs.d.ts.map +1 -0
  220. package/dist/components/ui/tabs.js +29 -0
  221. package/dist/components/ui/textarea.d.ts +11 -0
  222. package/dist/components/ui/textarea.d.ts.map +1 -0
  223. package/dist/components/ui/textarea.js +13 -0
  224. package/dist/components/ui/tooltip.d.ts +63 -0
  225. package/dist/components/ui/tooltip.d.ts.map +1 -0
  226. package/dist/components/ui/tooltip.js +25 -0
  227. package/dist/components/ui/typography.d.ts +32 -0
  228. package/dist/components/ui/typography.d.ts.map +1 -0
  229. package/dist/components/ui/typography.js +51 -0
  230. package/dist/index.d.ts +79 -0
  231. package/dist/index.d.ts.map +1 -0
  232. package/dist/index.js +68 -0
  233. package/dist/lib/utils.d.ts +3 -0
  234. package/dist/lib/utils.d.ts.map +1 -0
  235. package/dist/lib/utils.js +5 -0
  236. package/dist/pages/auth-page/index.d.ts +36 -0
  237. package/dist/pages/auth-page/index.d.ts.map +1 -0
  238. package/dist/pages/auth-page/index.js +23 -0
  239. package/dist/pages/blog-post-page/index.d.ts +174 -0
  240. package/dist/pages/blog-post-page/index.d.ts.map +1 -0
  241. package/dist/pages/blog-post-page/index.js +49 -0
  242. package/dist/pages/dashboard-page/index.d.ts +36 -0
  243. package/dist/pages/dashboard-page/index.d.ts.map +1 -0
  244. package/dist/pages/dashboard-page/index.js +24 -0
  245. package/dist/pages/index.d.ts +21 -0
  246. package/dist/pages/index.d.ts.map +1 -0
  247. package/dist/pages/index.js +14 -0
  248. package/dist/pages/marketing-page/index.d.ts +43 -0
  249. package/dist/pages/marketing-page/index.d.ts.map +1 -0
  250. package/dist/pages/marketing-page/index.js +25 -0
  251. package/dist/pages/not-found-page/index.d.ts +112 -0
  252. package/dist/pages/not-found-page/index.d.ts.map +1 -0
  253. package/dist/pages/not-found-page/index.js +46 -0
  254. package/dist/pages/settings-page/index.d.ts +47 -0
  255. package/dist/pages/settings-page/index.d.ts.map +1 -0
  256. package/dist/pages/settings-page/index.js +24 -0
  257. package/dist/provider/index.d.ts +12 -0
  258. package/dist/provider/index.d.ts.map +1 -0
  259. package/dist/provider/index.js +9 -0
  260. package/dist/provider/provider.d.ts +42 -0
  261. package/dist/provider/provider.d.ts.map +1 -0
  262. package/dist/provider/provider.js +178 -0
  263. package/dist/provider/theme-script.d.ts +55 -0
  264. package/dist/provider/theme-script.d.ts.map +1 -0
  265. package/dist/provider/theme-script.js +79 -0
  266. package/dist/styles.css +2894 -0
  267. package/dist/theming/index.d.ts +173 -0
  268. package/dist/theming/index.d.ts.map +1 -0
  269. package/dist/theming/index.js +186 -0
  270. package/package.json +89 -18
  271. package/components.json +0 -21
  272. package/public/r/badge.json +0 -26
  273. package/public/r/button.json +0 -26
  274. package/public/r/card.json +0 -25
  275. package/public/r/cta-01.json +0 -44
  276. package/public/r/feature-grid-01.json +0 -44
  277. package/public/r/hero-01.json +0 -44
  278. package/public/r/pricing-01.json +0 -46
  279. package/public/r/registry.json +0 -294
  280. package/public/r/section.json +0 -25
  281. package/public/r/stats-01.json +0 -44
  282. package/registry.json +0 -294
  283. package/src/components/ds/blocks/cta-01/block.json +0 -8
  284. package/src/components/ds/blocks/cta-01/cta.tsx +0 -62
  285. package/src/components/ds/blocks/cta-01/index.tsx +0 -1
  286. package/src/components/ds/blocks/feature-grid-01/block.json +0 -8
  287. package/src/components/ds/blocks/feature-grid-01/feature-grid.tsx +0 -69
  288. package/src/components/ds/blocks/feature-grid-01/index.tsx +0 -1
  289. package/src/components/ds/blocks/hero-01/block.json +0 -8
  290. package/src/components/ds/blocks/hero-01/hero.tsx +0 -85
  291. package/src/components/ds/blocks/hero-01/index.tsx +0 -1
  292. package/src/components/ds/blocks/pricing-01/block.json +0 -8
  293. package/src/components/ds/blocks/pricing-01/index.tsx +0 -1
  294. package/src/components/ds/blocks/pricing-01/pricing.tsx +0 -122
  295. package/src/components/ds/blocks/section.meta.json +0 -6
  296. package/src/components/ds/blocks/section.tsx +0 -86
  297. package/src/components/ds/blocks/stats-01/block.json +0 -8
  298. package/src/components/ds/blocks/stats-01/index.tsx +0 -1
  299. package/src/components/ds/blocks/stats-01/stats.tsx +0 -92
  300. package/src/components/ui/badge.tsx +0 -37
  301. package/src/components/ui/button.tsx +0 -77
  302. package/src/components/ui/card.tsx +0 -65
  303. package/src/lib/utils.ts +0 -6
@@ -0,0 +1,112 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * One way out of a page that was not found.
4
+ *
5
+ * A destination, and the words for it. Both are required, because a not-found
6
+ * page with no way out is a dead end with a message on it, and a link with no
7
+ * words is a shape a reader has to guess at.
8
+ */
9
+ export type NotFoundLink = {
10
+ /** The words of the link. */
11
+ label: string;
12
+ /** Where it goes. */
13
+ href: string;
14
+ /**
15
+ * Opens the destination in a new browsing context, which defaults the link
16
+ * relationship to `noopener noreferrer`. Almost never right here: a reader who
17
+ * followed a broken link and is sent to a new tab has to find their way back.
18
+ */
19
+ newTab?: boolean;
20
+ };
21
+ /**
22
+ * The props a NotFoundPage takes.
23
+ *
24
+ * Every string is a prop and the Page ships none. There is no "404", no "this
25
+ * page does not exist", no product name and no default set of links: a not-found
26
+ * page that hardcoded any of them would be a page that made a claim about a
27
+ * product it knows nothing about, and the one page on a site that every reader
28
+ * and no crawler will see is the worst place to hardcode a sentence.
29
+ */
30
+ export type NotFoundPageProps = {
31
+ /**
32
+ * The status code, shown as the page's own `h1`. Required, and a string rather
33
+ * than a number because it is displayed, not computed: a Page never decides
34
+ * what code it is answering.
35
+ */
36
+ code: string;
37
+ /**
38
+ * The headline under the code: the sentence that says what happened. It is the
39
+ * part a reader actually reads, and it is the caller's because every product
40
+ * says it differently - "No node here", "This page does not exist (yet)".
41
+ */
42
+ title: string;
43
+ /**
44
+ * One or two sentences under the headline, for anything a reader should know
45
+ * before they follow one of the links: that the URL may be mistyped, that the
46
+ * page is planned, that a search would find it.
47
+ */
48
+ description?: string;
49
+ /**
50
+ * The ways out, in the order a reader should see them. Put the destination
51
+ * most likely to help first; a not-found page that offers a homepage link
52
+ * before the documentation sends a reader further from what they wanted.
53
+ */
54
+ links: readonly NotFoundLink[];
55
+ /**
56
+ * The accessible name of the row of ways out. It is required, because it is a
57
+ * word a reader hears and a Page that ships no copy ships no reader-facing copy
58
+ * either. "Ways out" is the Page's own phrasing; a site that files its
59
+ * destinations differently names the same landmark differently.
60
+ */
61
+ linksLabel: string;
62
+ /**
63
+ * A slot for whatever else the site wants to say here: a search box, a list of
64
+ * recent posts, a contact address.
65
+ */
66
+ children?: ReactNode;
67
+ /**
68
+ * Layout only, exactly as on every Component. Changing a Prism-owned visual
69
+ * property from here is prohibited.
70
+ */
71
+ className?: string;
72
+ };
73
+ /**
74
+ * A complete not-found screen: the code, what happened, and the ways out.
75
+ *
76
+ * All four NaniSoft sites compose one, each in its own `not-found` route, and
77
+ * three of them are the same six lines with a different product name on it: a
78
+ * display title carrying the code, one muted sentence, and two or three links.
79
+ * The fourth wrote the same thing in its own markup. Four hand-written screens
80
+ * for one shape.
81
+ *
82
+ * It is a Page rather than a Block because it is a whole screen: it owns the
83
+ * document's `h1`, it is what a router renders when a route does not resolve, and
84
+ * it composes `Section`, `Heading`, `Text`, `Prose` and `CtaLink` into the one
85
+ * arrangement of them that answers "this is not here". A Block would be a band
86
+ * of that arrangement, and the band's job is not separable from the screen's:
87
+ * there is one correct page here and it is short.
88
+ *
89
+ * **The code is the `h1` and the sentence is not.** A not-found page whose
90
+ * `h1` is "Page not found" gives a search engine and a screen reader nothing
91
+ * distinct to index, and the code is the one string on the page that is the same
92
+ * for every broken URL. The code takes the display step and the sentence takes
93
+ * the muted one, which is the reverse of what a reader sees first and the right
94
+ * way round for anything that reads the outline.
95
+ *
96
+ * The links are `CtaLink`, so each one is a native anchor with a destination a
97
+ * browser can show, copy and open in a new tab. A not-found page is the one place
98
+ * on a site where a reader most wants to see where a link goes before following
99
+ * it, because they have just followed one that went nowhere.
100
+ *
101
+ * The page carries no `noindex` and no `robots` directive, because this is a
102
+ * component and not a route: what a not-found response is indexed as is the
103
+ * consumer's server's decision, and a Page that declared it would be a Page
104
+ * making a claim about a deployment it cannot see.
105
+ *
106
+ * It is a server Component. It fetches nothing, it holds no state and it imports
107
+ * no router, which is what lets a consumer render it from whichever not-found
108
+ * route their framework names.
109
+ */
110
+ export declare function NotFoundPage({ code, title, description, links, linksLabel, children, className, }: NotFoundPageProps): import("react").JSX.Element;
111
+ export default NotFoundPage;
112
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/not-found-page/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BA+BnB;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CtaLink } from '../../components/ui/cta-link';
3
+ import { Heading, Text } from '../../components/ui/typography';
4
+ import { Prose } from '../../components/ui/prose';
5
+ import { Section } from '../../components/ui/section';
6
+ /**
7
+ * A complete not-found screen: the code, what happened, and the ways out.
8
+ *
9
+ * All four NaniSoft sites compose one, each in its own `not-found` route, and
10
+ * three of them are the same six lines with a different product name on it: a
11
+ * display title carrying the code, one muted sentence, and two or three links.
12
+ * The fourth wrote the same thing in its own markup. Four hand-written screens
13
+ * for one shape.
14
+ *
15
+ * It is a Page rather than a Block because it is a whole screen: it owns the
16
+ * document's `h1`, it is what a router renders when a route does not resolve, and
17
+ * it composes `Section`, `Heading`, `Text`, `Prose` and `CtaLink` into the one
18
+ * arrangement of them that answers "this is not here". A Block would be a band
19
+ * of that arrangement, and the band's job is not separable from the screen's:
20
+ * there is one correct page here and it is short.
21
+ *
22
+ * **The code is the `h1` and the sentence is not.** A not-found page whose
23
+ * `h1` is "Page not found" gives a search engine and a screen reader nothing
24
+ * distinct to index, and the code is the one string on the page that is the same
25
+ * for every broken URL. The code takes the display step and the sentence takes
26
+ * the muted one, which is the reverse of what a reader sees first and the right
27
+ * way round for anything that reads the outline.
28
+ *
29
+ * The links are `CtaLink`, so each one is a native anchor with a destination a
30
+ * browser can show, copy and open in a new tab. A not-found page is the one place
31
+ * on a site where a reader most wants to see where a link goes before following
32
+ * it, because they have just followed one that went nowhere.
33
+ *
34
+ * The page carries no `noindex` and no `robots` directive, because this is a
35
+ * component and not a route: what a not-found response is indexed as is the
36
+ * consumer's server's decision, and a Page that declared it would be a Page
37
+ * making a claim about a deployment it cannot see.
38
+ *
39
+ * It is a server Component. It fetches nothing, it holds no state and it imports
40
+ * no router, which is what lets a consumer render it from whichever not-found
41
+ * route their framework names.
42
+ */
43
+ export function NotFoundPage({ code, title, description, links, linksLabel, children, className, }) {
44
+ return (_jsx(Section, { className: className, children: _jsxs("div", { className: "flex flex-col items-start gap-6", children: [_jsx(Heading, { as: "h1", size: "4xl", className: "text-muted-foreground font-mono", children: code }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h2", size: "2xl", children: title }), description ? _jsx(Text, { size: "lg", children: description }) : null] }), links.length > 0 ? (_jsx("nav", { "aria-label": linksLabel, className: "flex flex-wrap items-center gap-3", children: links.map((link, index) => (_jsx(CtaLink, { href: link.href, newTab: link.newTab, variant: index === 0 ? 'default' : 'outline', children: link.label }, link.href))) })) : null, children ? _jsx(Prose, { className: "mt-2", children: children }) : null] }) }));
45
+ }
46
+ export default NotFoundPage;
@@ -0,0 +1,47 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import { AppShell01 } from '../../blocks/app-shell-01';
3
+ import { PageHeader01 } from '../../blocks/page-header-01';
4
+ import { SettingsPanel01 } from '../../blocks/settings-panel-01';
5
+ export type SettingsPageTab = {
6
+ /** The value shared by the trigger and its panel. */
7
+ value: string;
8
+ /** The trigger label. */
9
+ label: string;
10
+ /**
11
+ * A saved settings form for this tab. Rendered through `SettingsPanel01`, so
12
+ * the tab inherits the grouped sections, the field kinds and the save footer.
13
+ */
14
+ panel?: ComponentProps<typeof SettingsPanel01>;
15
+ /** Content for a tab that is not a saved form, such as a read-only summary. */
16
+ content?: ReactNode;
17
+ };
18
+ export type SettingsPageProps = {
19
+ /** The frame: rail, top bar and mobile navigation sheet. */
20
+ shell: Omit<ComponentProps<typeof AppShell01>, 'children'>;
21
+ /** The page title band. */
22
+ header: ComponentProps<typeof PageHeader01>;
23
+ /** The tabs, in order. At least one is required. */
24
+ tabs: SettingsPageTab[];
25
+ /** The tab shown first, for uncontrolled tabs. Defaults to the first tab. */
26
+ defaultValue?: string;
27
+ /** The controlled active tab. */
28
+ value?: string;
29
+ onValueChange?: (value: string) => void;
30
+ };
31
+ /**
32
+ * A complete settings screen.
33
+ *
34
+ * It composes `AppShell01`, `PageHeader01`, `Tabs` and `SettingsPanel01`: the
35
+ * shell provides the frame, the header names the screen, and each tab is a
36
+ * saved settings form or a read-only panel. The page owns the sequence and the
37
+ * tab wiring only. Every section, field, option and label is a prop, and the
38
+ * page fetches nothing.
39
+ *
40
+ * A tab that carries a `panel` renders `SettingsPanel01`, so a settings screen
41
+ * is a set of grouped forms rather than one long page. A tab that carries
42
+ * `content` instead renders the node as-is. Both are optional, so a caller can
43
+ * mix forms and read-only views in one screen.
44
+ */
45
+ export declare function SettingsPage({ shell, header, tabs, defaultValue, value, onValueChange, }: SettingsPageProps): import("react").JSX.Element;
46
+ export default SettingsPage;
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/settings-page/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAGhE,MAAM,MAAM,eAAe,GAAG;IAC5B,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAA;IACb,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,eAAe,CAAC,CAAA;IAC9C,+EAA+E;IAC/E,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4DAA4D;IAC5D,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,UAAU,CAAC,EAAE,UAAU,CAAC,CAAA;IAC1D,2BAA2B;IAC3B,MAAM,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAA;IAC3C,oDAAoD;IACpD,IAAI,EAAE,eAAe,EAAE,CAAA;IACvB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,aAAa,GACd,EAAE,iBAAiB,+BA4BnB;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AppShell01 } from '../../blocks/app-shell-01';
4
+ import { PageHeader01 } from '../../blocks/page-header-01';
5
+ import { SettingsPanel01 } from '../../blocks/settings-panel-01';
6
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/ui/tabs';
7
+ /**
8
+ * A complete settings screen.
9
+ *
10
+ * It composes `AppShell01`, `PageHeader01`, `Tabs` and `SettingsPanel01`: the
11
+ * shell provides the frame, the header names the screen, and each tab is a
12
+ * saved settings form or a read-only panel. The page owns the sequence and the
13
+ * tab wiring only. Every section, field, option and label is a prop, and the
14
+ * page fetches nothing.
15
+ *
16
+ * A tab that carries a `panel` renders `SettingsPanel01`, so a settings screen
17
+ * is a set of grouped forms rather than one long page. A tab that carries
18
+ * `content` instead renders the node as-is. Both are optional, so a caller can
19
+ * mix forms and read-only views in one screen.
20
+ */
21
+ export function SettingsPage({ shell, header, tabs, defaultValue, value, onValueChange, }) {
22
+ return (_jsx(AppShell01, { ...shell, children: _jsxs("div", { className: "mx-auto flex w-full max-w-page flex-col gap-8", children: [_jsx(PageHeader01, { ...header, headingLevel: header.headingLevel ?? 'h1' }), _jsxs(Tabs, { defaultValue: defaultValue ?? tabs[0]?.value, value: value, onValueChange: onValueChange, children: [_jsx(TabsList, { children: tabs.map((tab) => (_jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value))) }), tabs.map((tab) => (_jsx(TabsContent, { value: tab.value, children: tab.panel ? _jsx(SettingsPanel01, { ...tab.panel }) : tab.content }, tab.value)))] })] }) }));
23
+ }
24
+ export default SettingsPage;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The Prism provider entry.
3
+ *
4
+ * `PrismProvider` and `usePrismTheme` are the optional programmatic path;
5
+ * `PrismThemeScript` is the optional pre-hydration script. The declarative
6
+ * `data-pack` and `.dark` attributes are the default and need none of this.
7
+ */
8
+ export { PrismProvider, usePrismTheme } from './provider';
9
+ export type { PrismProviderProps, PrismTheme } from './provider';
10
+ export { PrismThemeScript } from './theme-script';
11
+ export type { PrismThemeScriptProps } from './theme-script';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The Prism provider entry.
3
+ *
4
+ * `PrismProvider` and `usePrismTheme` are the optional programmatic path;
5
+ * `PrismThemeScript` is the optional pre-hydration script. The declarative
6
+ * `data-pack` and `.dark` attributes are the default and need none of this.
7
+ */
8
+ export { PrismProvider, usePrismTheme } from './provider';
9
+ export { PrismThemeScript } from './theme-script';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The optional client runtime for programmatic theme switching.
3
+ *
4
+ * The provider is strictly additive: a consumer who puts `data-pack` and
5
+ * `.dark` on `<html>` never needs it, and a consumer who wants no client
6
+ * JavaScript omits it. It carries the pack and mode through context and writes
7
+ * the same two markup attributes the declarative form uses. There is no
8
+ * override, merge, token, className or style prop: the provider's only outputs
9
+ * are the document-element attributes and context (ticket 07).
10
+ *
11
+ * It resolves through `resolveTheme`, the one rule the boot script also
12
+ * implements, and it never writes `THEME_ORIGIN_ATTRIBUTE`: the script is that
13
+ * attribute's only writer, and two writers would turn a record of the origin
14
+ * into a race.
15
+ */
16
+ import { type ReactNode } from 'react';
17
+ import { type Mode, type PackId } from '../theming';
18
+ export type PrismTheme = {
19
+ pack: PackId;
20
+ mode: Mode;
21
+ setPack: (pack: PackId) => void;
22
+ setMode: (mode: Mode) => void;
23
+ toggleMode: () => void;
24
+ };
25
+ export type PrismProviderProps = {
26
+ /** Pack used when nothing is stored and no pack attribute is present. */
27
+ defaultPack?: PackId;
28
+ /** Mode used in the same fallback. */
29
+ defaultMode?: Mode;
30
+ /** localStorage key for the persisted `{ pack, mode }` object. */
31
+ storageKey?: string;
32
+ children?: ReactNode;
33
+ };
34
+ export declare function PrismProvider({ defaultPack, defaultMode, storageKey, children, }: PrismProviderProps): import("react").JSX.Element;
35
+ /**
36
+ * Reads and mutates the active pack and mode.
37
+ *
38
+ * Throws outside a `PrismProvider`. The provider is optional, so a consumer
39
+ * that does not mount it simply does not call this hook.
40
+ */
41
+ export declare function usePrismTheme(): PrismTheme;
42
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/provider/provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AAEd,OAAO,EAML,KAAK,IAAI,EACT,KAAK,MAAM,EAEZ,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC7B,UAAU,EAAE,MAAM,IAAI,CAAA;CACvB,CAAA;AAID,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAyCD,wBAAgB,aAAa,CAAC,EAC5B,WAAuB,EACvB,WAAqB,EACrB,UAAgC,EAChC,QAAQ,GACT,EAAE,kBAAkB,+BAuHpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAM1C"}
@@ -0,0 +1,178 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * The optional client runtime for programmatic theme switching.
5
+ *
6
+ * The provider is strictly additive: a consumer who puts `data-pack` and
7
+ * `.dark` on `<html>` never needs it, and a consumer who wants no client
8
+ * JavaScript omits it. It carries the pack and mode through context and writes
9
+ * the same two markup attributes the declarative form uses. There is no
10
+ * override, merge, token, className or style prop: the provider's only outputs
11
+ * are the document-element attributes and context (ticket 07).
12
+ *
13
+ * It resolves through `resolveTheme`, the one rule the boot script also
14
+ * implements, and it never writes `THEME_ORIGIN_ATTRIBUTE`: the script is that
15
+ * attribute's only writer, and two writers would turn a record of the origin
16
+ * into a race.
17
+ */
18
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
19
+ import { DEFAULT_STORAGE_KEY, PACK_ATTRIBUTE, readLegacyMode, resolveTheme, themeAttributes, } from '../theming';
20
+ const PrismThemeContext = createContext(null);
21
+ function applyDocumentAttributes(pack, mode) {
22
+ const root = document.documentElement;
23
+ const { 'data-pack': packAttribute, className } = themeAttributes({ pack, mode });
24
+ if (packAttribute)
25
+ root.setAttribute(PACK_ATTRIBUTE, packAttribute);
26
+ else
27
+ root.removeAttribute(PACK_ATTRIBUTE);
28
+ if (className)
29
+ root.classList.add('dark');
30
+ else
31
+ root.classList.remove('dark');
32
+ }
33
+ /** One guarded read. A blocked or full store must not break the page. */
34
+ function read(key) {
35
+ try {
36
+ return localStorage.getItem(key);
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ /** One guarded write. The attributes are already applied when this fails. */
43
+ function persist(key, pack, mode) {
44
+ try {
45
+ localStorage.setItem(key, JSON.stringify({ pack, mode }));
46
+ }
47
+ catch {
48
+ // A blocked or full localStorage must not break theme switching. The
49
+ // attributes are already applied, so the visible behaviour is unchanged.
50
+ }
51
+ }
52
+ function retire(key) {
53
+ try {
54
+ localStorage.removeItem(key);
55
+ }
56
+ catch {
57
+ // Nothing to do: a key that cannot be removed is read again next load,
58
+ // recovers the same decision, and writes the same pair.
59
+ }
60
+ }
61
+ export function PrismProvider({ defaultPack = 'default', defaultMode = 'light', storageKey = DEFAULT_STORAGE_KEY, children, }) {
62
+ /*
63
+ * The resolution, and whether a decision has been made. Two pieces of state
64
+ * for two different questions, which is why the old `resolved` flag was not
65
+ * enough on its own:
66
+ *
67
+ * - `theme` is null until the store has been read. It gates APPLY, because
68
+ * applying the default over a server-rendered pack before resolution is
69
+ * the flash the resolution order exists to prevent. It carries the
70
+ * origin, so the recovery below can read it without a second lookup.
71
+ * - `decided` is a ref, not state, because it must be readable by the write
72
+ * effect without causing a render, and settable synchronously by a caller
73
+ * whose state update has not been committed yet. It gates WRITE.
74
+ *
75
+ * A ref also cannot be re-rendered into an extra effect pass, which is what
76
+ * would have made the write fire once for the flag and once for the value.
77
+ */
78
+ const [theme, setTheme] = useState(null);
79
+ const decided = useRef(false);
80
+ /*
81
+ * Resolution on mount, through the one shared rule: the stored value, then a
82
+ * key this line no longer writes, then what the server rendered, then the
83
+ * defaults. A server-rendered pack therefore survives hydration.
84
+ */
85
+ useEffect(() => {
86
+ const recovered = readLegacyMode(read);
87
+ const next = resolveTheme({
88
+ stored: read(storageKey),
89
+ legacy: recovered?.mode ?? null,
90
+ documentPack: document.documentElement.getAttribute(PACK_ATTRIBUTE),
91
+ documentDark: document.documentElement.classList.contains('dark'),
92
+ defaultPack,
93
+ defaultMode,
94
+ });
95
+ if (next.origin === 'legacy' && recovered) {
96
+ /*
97
+ * The recovery write. A decision taken from a key this line no longer
98
+ * writes is put in the key it does write, and the old key is retired in
99
+ * the same breath, which is what makes the clause self-disabling: the
100
+ * next load finds nothing to recover, so a reader is migrated once and
101
+ * the population the clause can still reach only ever shrinks.
102
+ */
103
+ persist(storageKey, next.pack, next.mode);
104
+ retire(recovered.key);
105
+ }
106
+ // A caller that decided in a layout effect before this ran keeps its value.
107
+ if (!decided.current)
108
+ setTheme(next);
109
+ }, [storageKey, defaultPack, defaultMode]);
110
+ /*
111
+ * Apply always, once there is a resolution; persist only on a decision.
112
+ *
113
+ * The first resolution from the document or the defaults writes nothing, so a
114
+ * visitor who never touched the toggle ends the load with an empty store and
115
+ * a site that changes its default reaches everyone who has not chosen.
116
+ */
117
+ useEffect(() => {
118
+ if (!theme)
119
+ return;
120
+ applyDocumentAttributes(theme.pack, theme.mode);
121
+ if (decided.current)
122
+ persist(storageKey, theme.pack, theme.mode);
123
+ }, [theme, storageKey]);
124
+ const setPack = useCallback((next) => {
125
+ decided.current = true;
126
+ setTheme((current) => ({
127
+ pack: next,
128
+ mode: current?.mode ?? defaultMode,
129
+ origin: 'stored',
130
+ }));
131
+ }, [defaultMode]);
132
+ const setMode = useCallback((next) => {
133
+ decided.current = true;
134
+ setTheme((current) => ({
135
+ pack: current?.pack ?? defaultPack,
136
+ mode: next,
137
+ origin: 'stored',
138
+ }));
139
+ }, [defaultPack]);
140
+ /*
141
+ * The pair is a closed union of two members, so the other one is the other
142
+ * member. Spelling it out costs nothing and does not hide the coupling behind
143
+ * an index into a list this file does not otherwise need.
144
+ *
145
+ * The three setters all fall back to the consumer's own default rather than
146
+ * to the neutral one, so a decision taken before the store has been read
147
+ * pairs the chosen axis with the site's default and not with a guess.
148
+ */
149
+ const toggleMode = useCallback(() => {
150
+ decided.current = true;
151
+ setTheme((current) => ({
152
+ pack: current?.pack ?? defaultPack,
153
+ mode: current?.mode === 'dark' ? 'light' : 'dark',
154
+ origin: 'stored',
155
+ }));
156
+ }, [defaultPack]);
157
+ const value = useMemo(() => ({
158
+ pack: theme?.pack ?? defaultPack,
159
+ mode: theme?.mode ?? defaultMode,
160
+ setPack,
161
+ setMode,
162
+ toggleMode,
163
+ }), [theme, defaultPack, defaultMode, setPack, setMode, toggleMode]);
164
+ return _jsx(PrismThemeContext.Provider, { value: value, children: children });
165
+ }
166
+ /**
167
+ * Reads and mutates the active pack and mode.
168
+ *
169
+ * Throws outside a `PrismProvider`. The provider is optional, so a consumer
170
+ * that does not mount it simply does not call this hook.
171
+ */
172
+ export function usePrismTheme() {
173
+ const value = useContext(PrismThemeContext);
174
+ if (!value) {
175
+ throw new Error('usePrismTheme must be used within a PrismProvider');
176
+ }
177
+ return value;
178
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The optional pre-hydration theme script.
3
+ *
4
+ * A Server Component that renders one blocking script. Placed in `<head>`, it
5
+ * applies the stored pack and mode before React hydrates, so the first paint is
6
+ * already correct. It is safe to omit; a consumer who renders the attributes on
7
+ * `<html>` needs no client runtime at all.
8
+ *
9
+ * It is the only writer of `THEME_ORIGIN_ATTRIBUTE`, and it writes that
10
+ * attribute on every path it reaches, so the origin of the active theme is
11
+ * always readable and never merely absent.
12
+ *
13
+ * The string below is a second implementation of `resolveTheme`, not a copy of
14
+ * it that has been kept in step. A function cannot be shared with a string that
15
+ * a minifier rewrites: a renamed identifier inside the script's own `try` throws
16
+ * there, the catch swallows it, and the theme silently stops applying on every
17
+ * page. So the string implements the rule and
18
+ * `scripts/check-theme-resolution.mjs` runs BOTH over one table of stored
19
+ * values, which is the only thing that can hold them together.
20
+ *
21
+ * Its price is a separate ceiling, `scripts/check-boot-budget.mjs`, because this
22
+ * string runs before paint on every page and adding it to a table of component
23
+ * bundle sizes would corrupt the unit that table exists to measure.
24
+ */
25
+ import { type Mode, type PackId } from '../theming';
26
+ /**
27
+ * The first half of the storage-migration clause: read one key this line no
28
+ * longer writes.
29
+ *
30
+ * Exported because `check-boot-budget.mjs` prices the clause rather than
31
+ * guessing at it. The emitted string contains this fragment verbatim, so its
32
+ * byte count is the clause's cost and not an estimate of it.
33
+ */
34
+ export declare const THEME_MIGRATION_READ: string;
35
+ /**
36
+ * The second half: the clause retires itself.
37
+ *
38
+ * The recovery is the only write this string performs, and it writes the
39
+ * recovered pair into the key this line does write and removes the key it came
40
+ * from, in one guarded step. That is the clause's expiry by rule rather than by
41
+ * number: it fires only while a retired key still holds a mode, it fires at
42
+ * most once per reader because its own write is what makes the second load find
43
+ * nothing, and it is retired for good by deleting the last entry of
44
+ * `LEGACY_MODE_STORAGE_KEYS`. There is no date and no version constant in it,
45
+ * and the gate reports how many keys are still live so the number is visible
46
+ * rather than remembered.
47
+ */
48
+ export declare const THEME_MIGRATION_RETIRE = "if(G===\"legacy\"){try{localStorage.setItem(K,JSON.stringify({pack:pack,mode:m}));localStorage.removeItem(LK);}catch(e){}}";
49
+ export type PrismThemeScriptProps = {
50
+ storageKey?: string;
51
+ defaultPack?: PackId;
52
+ defaultMode?: Mode;
53
+ };
54
+ export declare function PrismThemeScript({ storageKey, defaultPack, defaultMode, }: PrismThemeScriptProps): import("react").JSX.Element;
55
+ //# sourceMappingURL=theme-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-script.d.ts","sourceRoot":"","sources":["../../src/provider/theme-script.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAOL,KAAK,IAAI,EACT,KAAK,MAAM,EACZ,MAAM,YAAY,CAAA;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,QAEvB,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,+HACyF,CAAA;AAE5H,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAgC,EAChC,WAAuB,EACvB,WAAqB,GACtB,EAAE,qBAAqB,+BA+BvB"}
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * The optional pre-hydration theme script.
4
+ *
5
+ * A Server Component that renders one blocking script. Placed in `<head>`, it
6
+ * applies the stored pack and mode before React hydrates, so the first paint is
7
+ * already correct. It is safe to omit; a consumer who renders the attributes on
8
+ * `<html>` needs no client runtime at all.
9
+ *
10
+ * It is the only writer of `THEME_ORIGIN_ATTRIBUTE`, and it writes that
11
+ * attribute on every path it reaches, so the origin of the active theme is
12
+ * always readable and never merely absent.
13
+ *
14
+ * The string below is a second implementation of `resolveTheme`, not a copy of
15
+ * it that has been kept in step. A function cannot be shared with a string that
16
+ * a minifier rewrites: a renamed identifier inside the script's own `try` throws
17
+ * there, the catch swallows it, and the theme silently stops applying on every
18
+ * page. So the string implements the rule and
19
+ * `scripts/check-theme-resolution.mjs` runs BOTH over one table of stored
20
+ * values, which is the only thing that can hold them together.
21
+ *
22
+ * Its price is a separate ceiling, `scripts/check-boot-budget.mjs`, because this
23
+ * string runs before paint on every page and adding it to a table of component
24
+ * bundle sizes would corrupt the unit that table exists to measure.
25
+ */
26
+ import { DEFAULT_STORAGE_KEY, LEGACY_MODE_STORAGE_KEYS, MODES, PACKS, PACK_ATTRIBUTE, THEME_ORIGIN_ATTRIBUTE, } from '../theming';
27
+ /**
28
+ * The first half of the storage-migration clause: read one key this line no
29
+ * longer writes.
30
+ *
31
+ * Exported because `check-boot-budget.mjs` prices the clause rather than
32
+ * guessing at it. The emitted string contains this fragment verbatim, so its
33
+ * byte count is the clause's cost and not an estimate of it.
34
+ */
35
+ export const THEME_MIGRATION_READ = LEGACY_MODE_STORAGE_KEYS.map((key) => `try{var g=localStorage.getItem(${JSON.stringify(key)});if(M.indexOf(g)>-1){L=g;LK=${JSON.stringify(key)};}}catch(e){}`).join('');
36
+ /**
37
+ * The second half: the clause retires itself.
38
+ *
39
+ * The recovery is the only write this string performs, and it writes the
40
+ * recovered pair into the key this line does write and removes the key it came
41
+ * from, in one guarded step. That is the clause's expiry by rule rather than by
42
+ * number: it fires only while a retired key still holds a mode, it fires at
43
+ * most once per reader because its own write is what makes the second load find
44
+ * nothing, and it is retired for good by deleting the last entry of
45
+ * `LEGACY_MODE_STORAGE_KEYS`. There is no date and no version constant in it,
46
+ * and the gate reports how many keys are still live so the number is visible
47
+ * rather than remembered.
48
+ */
49
+ export const THEME_MIGRATION_RETIRE = 'if(G==="legacy"){try{localStorage.setItem(K,JSON.stringify({pack:pack,mode:m}));localStorage.removeItem(LK);}catch(e){}}';
50
+ export function PrismThemeScript({ storageKey = DEFAULT_STORAGE_KEY, defaultPack = 'default', defaultMode = 'light', }) {
51
+ const script = '(function(){try{' +
52
+ 'var r=document.documentElement,' +
53
+ `P=${JSON.stringify(PACKS)},` +
54
+ `M=${JSON.stringify(MODES)},` +
55
+ `K=${JSON.stringify(storageKey)},` +
56
+ `A=${JSON.stringify(PACK_ATTRIBUTE)},` +
57
+ `O=${JSON.stringify(THEME_ORIGIN_ATTRIBUTE)},` +
58
+ `DP=${JSON.stringify(defaultPack)},` +
59
+ `DM=${JSON.stringify(defaultMode)},` +
60
+ 'L=null,LK=null,S=null,T=null;' +
61
+ 'try{var t=localStorage.getItem(K);if(t){T=t;S=JSON.parse(t);}}catch(e){}' +
62
+ THEME_MIGRATION_READ +
63
+ 'var a=r.getAttribute(A),' +
64
+ 'DPK=P.indexOf(a)>-1?a:null,' +
65
+ 'DK=r.classList.contains("dark")?"dark":null,' +
66
+ 'U=S&&typeof S=="object"&&P.indexOf(S.pack)>-1&&M.indexOf(S.mode)>-1,' +
67
+ 'pack,m,G;' +
68
+ 'if(U){pack=S.pack;m=S.mode;G="stored";}' +
69
+ 'else if(T){pack=DPK||DP;m=DK||DM;G="unparsed";}' +
70
+ 'else if(L){pack=DP;m=L;G="legacy";}' +
71
+ 'else if(DPK||DK){pack=DPK||DP;m=DK||DM;G="document";}' +
72
+ 'else{pack=DP;m=DM;G="default";}' +
73
+ THEME_MIGRATION_RETIRE +
74
+ 'if(pack!=="default"){r.setAttribute(A,pack);}else{r.removeAttribute(A);}' +
75
+ 'r.classList.toggle("dark",m==="dark");' +
76
+ 'r.setAttribute(O,G);' +
77
+ '}catch(e){}})();';
78
+ return _jsx("script", { dangerouslySetInnerHTML: { __html: script } });
79
+ }