@krizaka/ui 3.7.1 → 3.9.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/README.md +14 -12
  2. package/dist/action-bar.js +2 -1
  3. package/dist/action-bar.js.map +1 -1
  4. package/dist/app-shell.d.ts +81 -3
  5. package/dist/app-shell.js +163 -35
  6. package/dist/app-shell.js.map +1 -1
  7. package/dist/audio-player.js +1 -1
  8. package/dist/badge.d.ts +36 -39
  9. package/dist/badge.js +11 -2
  10. package/dist/badge.js.map +1 -1
  11. package/dist/blocks.css +56 -4
  12. package/dist/button-BmCrLCq7.d.ts +114 -0
  13. package/dist/button.d.ts +48 -48
  14. package/dist/button.js +2 -1
  15. package/dist/card.d.ts +111 -111
  16. package/dist/card.js +10 -2
  17. package/dist/card.js.map +1 -1
  18. package/dist/chip.d.ts +24 -24
  19. package/dist/chip.js +10 -2
  20. package/dist/chip.js.map +1 -1
  21. package/dist/{chunk-UOMP6VOZ.js → chunk-276PTQMQ.js} +10 -4
  22. package/dist/chunk-276PTQMQ.js.map +1 -0
  23. package/dist/{chunk-Z6FUPB3Q.js → chunk-2NB27LM7.js} +11 -3
  24. package/dist/chunk-2NB27LM7.js.map +1 -0
  25. package/dist/chunk-2VNYHG4E.js +9 -0
  26. package/dist/chunk-2VNYHG4E.js.map +1 -0
  27. package/dist/{chunk-ND3L5VHT.js → chunk-5Z7FWCV2.js} +2 -2
  28. package/dist/{chunk-YH4LMWRU.js → chunk-7UCPGXKN.js} +11 -3
  29. package/dist/chunk-7UCPGXKN.js.map +1 -0
  30. package/dist/{chunk-AZPKXP3O.js → chunk-AFY35UCB.js} +6 -1
  31. package/dist/{chunk-AZPKXP3O.js.map → chunk-AFY35UCB.js.map} +1 -1
  32. package/dist/{chunk-LCZXS6AK.js → chunk-CKSHLSDH.js} +5 -5
  33. package/dist/chunk-CKSHLSDH.js.map +1 -0
  34. package/dist/chunk-D3T54RSL.js +11 -0
  35. package/dist/chunk-D3T54RSL.js.map +1 -0
  36. package/dist/{chunk-ZKVQGXLD.js → chunk-FKBHWS4N.js} +2 -2
  37. package/dist/{chunk-BRKMQ6JV.js → chunk-FZBWEDQI.js} +1 -23
  38. package/dist/chunk-FZBWEDQI.js.map +1 -0
  39. package/dist/{chunk-HCNOOPZE.js → chunk-KVDB4GXV.js} +2 -2
  40. package/dist/{chunk-RUPNPMP3.js → chunk-OWC2LZD2.js} +11 -3
  41. package/dist/chunk-OWC2LZD2.js.map +1 -0
  42. package/dist/{chunk-FDCMBWUU.js → chunk-REPTKUJ6.js} +4 -4
  43. package/dist/chunk-REPTKUJ6.js.map +1 -0
  44. package/dist/{chunk-JZPVUJIX.js → chunk-UB2BI3AW.js} +1 -1
  45. package/dist/{chunk-JZPVUJIX.js.map → chunk-UB2BI3AW.js.map} +1 -1
  46. package/dist/code-block.d.ts +32 -10
  47. package/dist/code-block.js +51 -43
  48. package/dist/code-block.js.map +1 -1
  49. package/dist/combobox.d.ts +3 -3
  50. package/dist/combobox.js +4 -3
  51. package/dist/command.js +3 -2
  52. package/dist/command.js.map +1 -1
  53. package/dist/component-page.js.map +1 -1
  54. package/dist/confirm-button.d.ts +1 -1
  55. package/dist/confirm-button.js +2 -1
  56. package/dist/confirm-button.js.map +1 -1
  57. package/dist/continue-prompt.js +11 -3
  58. package/dist/continue-prompt.js.map +1 -1
  59. package/dist/data-table.js +7 -6
  60. package/dist/data-table.js.map +1 -1
  61. package/dist/date-picker.js +1 -1
  62. package/dist/dialog.js +3 -2
  63. package/dist/dialog.js.map +1 -1
  64. package/dist/file-upload.d.ts +12 -1
  65. package/dist/file-upload.js +73 -89
  66. package/dist/file-upload.js.map +1 -1
  67. package/dist/filter-bar.js +3 -2
  68. package/dist/{geometry-CbOU4rtL.d.ts → geometry-B2E8oFhg.d.ts} +1 -1
  69. package/dist/image-cropper.js +1 -1
  70. package/dist/index.d.ts +7 -5
  71. package/dist/index.js +26 -24
  72. package/dist/index.js.map +1 -1
  73. package/dist/list.js +1 -1
  74. package/dist/map.js +1 -1
  75. package/dist/map.js.map +1 -1
  76. package/dist/markdown.d.ts +38 -0
  77. package/dist/markdown.js +150 -0
  78. package/dist/markdown.js.map +1 -0
  79. package/dist/mascot-quote.d.ts +1 -1
  80. package/dist/mascot-quote.js.map +1 -1
  81. package/dist/mascot-state.d.ts +1 -1
  82. package/dist/mascot-state.js +1 -1
  83. package/dist/mascots.d.ts +2 -2
  84. package/dist/mascots.js +1 -1
  85. package/dist/match-dialog.js +2 -1
  86. package/dist/match-dialog.js.map +1 -1
  87. package/dist/media-capture.js +1 -1
  88. package/dist/native/map.js +1 -1
  89. package/dist/native/map.js.map +1 -1
  90. package/dist/native/markdown.d.ts +24 -0
  91. package/dist/native/markdown.js +200 -0
  92. package/dist/native/markdown.js.map +1 -0
  93. package/dist/native.d.ts +10 -14
  94. package/dist/native.js +383 -119
  95. package/dist/native.js.map +1 -1
  96. package/dist/recipes.d.ts +143 -0
  97. package/dist/recipes.js +180 -0
  98. package/dist/recipes.js.map +1 -0
  99. package/dist/search-field.js +2 -2
  100. package/dist/status-page.d.ts +1 -1
  101. package/dist/status-page.js.map +1 -1
  102. package/dist/swipe-deck.js +1 -1
  103. package/dist/tag-input.js +4 -3
  104. package/dist/tag-input.js.map +1 -1
  105. package/dist/tag-selector.js +2 -1
  106. package/dist/tag-selector.js.map +1 -1
  107. package/dist/theme.d.ts +1 -1
  108. package/dist/theme.js +2 -1
  109. package/dist/theme.js.map +1 -1
  110. package/dist/tree-view.js +1 -1
  111. package/dist/vertical-video.js +2 -1
  112. package/dist/vertical-video.js.map +1 -1
  113. package/dist/video-player.js +1 -1
  114. package/dist/video-trimmer.js +1 -1
  115. package/dist/virtual.js +2 -2
  116. package/package.json +49 -18
  117. package/registry/achievement.json +1 -1
  118. package/registry/action-bar.json +1 -1
  119. package/registry/activity-feed.json +1 -1
  120. package/registry/alert.json +1 -1
  121. package/registry/app-shell.json +135 -12
  122. package/registry/argument-card.json +1 -1
  123. package/registry/audio-player.json +2 -2
  124. package/registry/auth-layout.json +1 -1
  125. package/registry/avatar.json +1 -1
  126. package/registry/badge.json +9 -3
  127. package/registry/button.json +7 -2
  128. package/registry/card-grid.json +1 -1
  129. package/registry/card.json +8 -3
  130. package/registry/catalogue-page.json +1 -1
  131. package/registry/changelog-page.json +1 -1
  132. package/registry/chat.json +1 -1
  133. package/registry/checkbox.json +1 -1
  134. package/registry/chip-link.json +1 -1
  135. package/registry/chip.json +9 -3
  136. package/registry/choice-grid.json +1 -1
  137. package/registry/code-block.json +65 -21
  138. package/registry/combobox.json +4 -3
  139. package/registry/command.json +1 -1
  140. package/registry/component-page.json +4 -4
  141. package/registry/confirm-button.json +1 -1
  142. package/registry/contact-page.json +1 -1
  143. package/registry/continue-prompt.json +1 -1
  144. package/registry/countdown.json +1 -1
  145. package/registry/cta-panel.json +1 -1
  146. package/registry/dashboard-layout.json +1 -1
  147. package/registry/data-table.json +2 -2
  148. package/registry/date-picker.json +3 -3
  149. package/registry/decision-list.json +1 -1
  150. package/registry/description-list.json +1 -1
  151. package/registry/detail-layout.json +1 -1
  152. package/registry/dialog.json +1 -1
  153. package/registry/docs-article-page.json +1 -1
  154. package/registry/docs-hub-page.json +1 -1
  155. package/registry/docs-layout.json +1 -1
  156. package/registry/dropdown-menu.json +1 -1
  157. package/registry/empty-state.json +1 -1
  158. package/registry/error-page.json +1 -1
  159. package/registry/examples/app-shell/console.d.ts +4 -0
  160. package/registry/examples/app-shell/console.js +93 -0
  161. package/registry/examples/app-shell/console.tsx +103 -0
  162. package/registry/examples/badge/native/dot.dark.png +0 -0
  163. package/registry/examples/badge/native/dot.light.png +0 -0
  164. package/registry/examples/badge/native/scrim.dark.png +0 -0
  165. package/registry/examples/badge/native/scrim.light.png +0 -0
  166. package/registry/examples/badge/native/tones.dark.png +0 -0
  167. package/registry/examples/badge/native/tones.light.png +0 -0
  168. package/registry/examples/card/native/default.dark.png +0 -0
  169. package/registry/examples/card/native/default.light.png +0 -0
  170. package/registry/examples/card/native/elevated.light.png +0 -0
  171. package/registry/examples/chip/native/multiple.dark.png +0 -0
  172. package/registry/examples/chip/native/multiple.light.png +0 -0
  173. package/registry/examples/chip/native/removable.dark.png +0 -0
  174. package/registry/examples/chip/native/removable.light.png +0 -0
  175. package/registry/examples/chip/native/single.dark.png +0 -0
  176. package/registry/examples/chip/native/single.light.png +0 -0
  177. package/registry/examples/chip/native/toggle.dark.png +0 -0
  178. package/registry/examples/chip/native/toggle.light.png +0 -0
  179. package/registry/examples/code-block/annotated.d.ts +4 -0
  180. package/registry/examples/code-block/annotated.js +31 -0
  181. package/registry/examples/code-block/annotated.tsx +26 -0
  182. package/registry/examples/component-page/button.tsx +1 -1
  183. package/registry/examples/form/native/amount.dark.png +0 -0
  184. package/registry/examples/form/native/amount.light.png +0 -0
  185. package/registry/examples/landing-page/krizaka.js +1 -1
  186. package/registry/examples/landing-page/krizaka.tsx +1 -1
  187. package/registry/examples/markdown/ai-stream.d.ts +4 -0
  188. package/registry/examples/markdown/ai-stream.js +31 -0
  189. package/registry/examples/markdown/ai-stream.tsx +32 -0
  190. package/registry/examples/markdown/basic.d.ts +4 -0
  191. package/registry/examples/markdown/basic.js +26 -0
  192. package/registry/examples/markdown/basic.tsx +22 -0
  193. package/registry/examples/markdown/native/ai-stream.d.ts +4 -0
  194. package/registry/examples/markdown/native/ai-stream.dark.png +0 -0
  195. package/registry/examples/markdown/native/ai-stream.js +17 -0
  196. package/registry/examples/markdown/native/ai-stream.light.png +0 -0
  197. package/registry/examples/markdown/native/ai-stream.tsx +17 -0
  198. package/registry/examples/markdown/native/basic.d.ts +4 -0
  199. package/registry/examples/markdown/native/basic.dark.png +0 -0
  200. package/registry/examples/markdown/native/basic.js +28 -0
  201. package/registry/examples/markdown/native/basic.light.png +0 -0
  202. package/registry/examples/markdown/native/basic.tsx +27 -0
  203. package/registry/examples/mascots/flock.tsx +1 -1
  204. package/registry/examples/profile-card/match.js +1 -1
  205. package/registry/examples/profile-card/match.tsx +1 -1
  206. package/registry/examples/recipes/tone-chip.d.ts +4 -0
  207. package/registry/examples/recipes/tone-chip.js +46 -0
  208. package/registry/examples/recipes/tone-chip.tsx +32 -0
  209. package/registry/examples/screen/native/profile.dark.png +0 -0
  210. package/registry/examples/screen/native/profile.light.png +0 -0
  211. package/registry/examples/status-page/building.js +1 -1
  212. package/registry/examples/status-page/building.tsx +1 -1
  213. package/registry/examples/tag-selector/native/interests.dark.png +0 -0
  214. package/registry/examples/tag-selector/native/interests.light.png +0 -0
  215. package/registry/field.json +1 -1
  216. package/registry/file-upload.json +8 -3
  217. package/registry/filter-bar.json +1 -1
  218. package/registry/form.json +2 -2
  219. package/registry/gallery-page.json +1 -1
  220. package/registry/glow-card.json +1 -1
  221. package/registry/goal-gauge.json +1 -1
  222. package/registry/how-it-works-page.json +1 -1
  223. package/registry/image-cropper.json +2 -2
  224. package/registry/index.json +77 -17
  225. package/registry/iso.json +1 -1
  226. package/registry/kanban.json +1 -1
  227. package/registry/kbd.json +1 -1
  228. package/registry/landing-page.json +3 -3
  229. package/registry/landscape.json +1 -1
  230. package/registry/legal-page.json +1 -1
  231. package/registry/link-card.json +1 -1
  232. package/registry/list.json +2 -2
  233. package/registry/loader.json +4 -4
  234. package/registry/longform-page.json +1 -1
  235. package/registry/map.json +3 -3
  236. package/registry/markdown.json +266 -0
  237. package/registry/marketing-layout.json +1 -1
  238. package/registry/marquee.json +1 -1
  239. package/registry/mascot-quote.json +1 -1
  240. package/registry/mascot-state.json +1 -1
  241. package/registry/mascots.json +5 -5
  242. package/registry/match-dialog.json +1 -1
  243. package/registry/media-capture.json +2 -2
  244. package/registry/meter.json +1 -1
  245. package/registry/nav-bar.json +1 -1
  246. package/registry/number-ticker.json +1 -1
  247. package/registry/page-header.json +1 -1
  248. package/registry/page-hero.json +1 -1
  249. package/registry/page.json +1 -1
  250. package/registry/pagination.json +1 -1
  251. package/registry/photo-grid.json +1 -1
  252. package/registry/pointer-light.json +1 -1
  253. package/registry/popover.json +1 -1
  254. package/registry/pricing-page.json +1 -1
  255. package/registry/product-page.json +3 -3
  256. package/registry/profile-card.json +2 -2
  257. package/registry/progress.json +1 -1
  258. package/registry/promise-card.json +1 -1
  259. package/registry/radio-group.json +1 -1
  260. package/registry/recipes.json +99 -0
  261. package/registry/scroll-stage.json +1 -1
  262. package/registry/search-field.json +4 -3
  263. package/registry/section-backdrop.json +1 -1
  264. package/registry/separator.json +1 -1
  265. package/registry/settings-layout.json +1 -1
  266. package/registry/skeleton.json +1 -1
  267. package/registry/skip-link.json +1 -1
  268. package/registry/slider.json +1 -1
  269. package/registry/slot.json +1 -1
  270. package/registry/spinner.json +1 -1
  271. package/registry/split-view.json +1 -1
  272. package/registry/stage-banner.json +1 -1
  273. package/registry/stat.json +1 -1
  274. package/registry/status-page.json +7 -7
  275. package/registry/step-progress.json +1 -1
  276. package/registry/stepper.json +1 -1
  277. package/registry/swipe-deck.json +2 -2
  278. package/registry/switch.json +1 -1
  279. package/registry/tabs.json +1 -1
  280. package/registry/tag-input.json +2 -2
  281. package/registry/tag-selector.json +1 -1
  282. package/registry/terminal-replay.json +1 -1
  283. package/registry/theme.json +1 -1
  284. package/registry/toast.json +1 -1
  285. package/registry/toc.json +1 -1
  286. package/registry/tooltip.json +1 -1
  287. package/registry/tree-view.json +2 -2
  288. package/registry/verified-badge.json +1 -1
  289. package/registry/vertical-video.json +2 -2
  290. package/registry/video-player.json +2 -2
  291. package/registry/video-trimmer.json +2 -2
  292. package/registry/virtual.json +2 -2
  293. package/dist/button-BMAO7dKF.d.ts +0 -114
  294. package/dist/chunk-BRKMQ6JV.js.map +0 -1
  295. package/dist/chunk-FDCMBWUU.js.map +0 -1
  296. package/dist/chunk-LCZXS6AK.js.map +0 -1
  297. package/dist/chunk-RUPNPMP3.js.map +0 -1
  298. package/dist/chunk-UOMP6VOZ.js.map +0 -1
  299. package/dist/chunk-YH4LMWRU.js.map +0 -1
  300. package/dist/chunk-Z6FUPB3Q.js.map +0 -1
  301. /package/dist/{chunk-ND3L5VHT.js.map → chunk-5Z7FWCV2.js.map} +0 -0
  302. /package/dist/{chunk-ZKVQGXLD.js.map → chunk-FKBHWS4N.js.map} +0 -0
  303. /package/dist/{chunk-HCNOOPZE.js.map → chunk-KVDB4GXV.js.map} +0 -0
@@ -0,0 +1,266 @@
1
+ {
2
+ "name": "markdown",
3
+ "type": "primitive",
4
+ "title": "Markdown",
5
+ "summary": "A Markdown text on the tokens — headings, lists and tasks, quotes, tables, links, images, footnotes, fenced code in `CodeBlock` — parsed by TanStack Markdown, safe by default (raw HTML is never rendered), with a streaming mode for a text that arrives in pieces: an AI answer. React Native draws the same AST in `Text` and `View`.",
6
+ "description": "A Markdown text on the tokens — headings, lists and tasks, quotes, tables, links, images, footnotes, fenced code in `CodeBlock` — parsed by TanStack Markdown, safe by default (raw HTML is never rendered), with a streaming mode for a text that arrives in pieces: an AI answer. React Native draws the same AST in `Text` and `View`.",
7
+ "status": "beta",
8
+ "deprecated": null,
9
+ "category": "data-display",
10
+ "platforms": "both",
11
+ "whenToUse": [
12
+ "An AI answer, a message, a release note, a description written by a person or a model.",
13
+ "A text that arrives in pieces (`streaming`): the unfinished end is closed, so no `**` or half a link ever shows.",
14
+ "Content that comes from a user or a model, which must never run HTML."
15
+ ],
16
+ "whenNotToUse": [
17
+ {
18
+ "when": "For the documentation of a product: its own MDX pipeline (Fumadocs on krizaka.com) renders it, with components."
19
+ },
20
+ {
21
+ "when": "For one sample of code on a page.",
22
+ "use": "code-block"
23
+ },
24
+ {
25
+ "when": "For a few words with a bold part: write the JSX."
26
+ }
27
+ ],
28
+ "bestPractices": [
29
+ "Pass the words of the code blocks' copy button with `codeLabels` (translated); without them a block has no button.",
30
+ "Give your router's link to `linkAs`: the links that stay on the site go through it, the others open as plain links with `rel=\"noopener noreferrer\"`.",
31
+ "Set `streaming` while the text is arriving and drop it when it is complete: the caret goes away and the text is read as written.",
32
+ "Write the source in the user's language; the component adds no words of its own.",
33
+ "On a phone, give `onLinkPress` to open a link in your app's browser; the default opens http(s) links with the system."
34
+ ],
35
+ "accessibility": {
36
+ "keyboard": [
37
+ {
38
+ "keys": "Tab",
39
+ "action": "Moves through the links and the code blocks (which scroll sideways with the arrow keys)."
40
+ }
41
+ ],
42
+ "notes": [
43
+ "The structure is real elements: headings in order, lists, a table with column headers, `figure` and `figcaption` for a titled code block.",
44
+ "A task list is disabled checkboxes inside their labels, so each item reads as done or not done.",
45
+ "While `streaming`, the root carries `aria-busy`; the caret is decorative (a CSS pseudo-element) and still under reduced motion.",
46
+ "Raw HTML in the source is shown as text, and links or images with an executable address (`javascript:`, `data:`) are dropped."
47
+ ]
48
+ },
49
+ "related": [
50
+ "code-block",
51
+ "page",
52
+ "txt"
53
+ ],
54
+ "web": {
55
+ "entry": "@krizaka/ui/markdown",
56
+ "import": "import { Markdown, parseSource, healStreaming } from \"@krizaka/ui/markdown\";",
57
+ "props": [
58
+ {
59
+ "component": "Markdown",
60
+ "description": "A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes.",
61
+ "props": [
62
+ {
63
+ "name": "children",
64
+ "type": "string",
65
+ "default": null,
66
+ "description": "The Markdown source.",
67
+ "required": true
68
+ },
69
+ {
70
+ "name": "codeLabels",
71
+ "type": "{ copy: string; copied: string; }",
72
+ "default": null,
73
+ "description": "The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button.",
74
+ "required": false
75
+ },
76
+ {
77
+ "name": "headingIds",
78
+ "type": "boolean",
79
+ "default": null,
80
+ "description": "Gives every heading an `id`, for `#section` links.",
81
+ "required": false
82
+ },
83
+ {
84
+ "name": "linkAs",
85
+ "type": "ElementType<DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, keyof IntrinsicElements>",
86
+ "default": null,
87
+ "description": "Your router's link (Next's `Link`) for the links that stay on the site (`/docs`, `#section`); the others are plain `a`.",
88
+ "required": false
89
+ },
90
+ {
91
+ "name": "streaming",
92
+ "type": "boolean",
93
+ "default": null,
94
+ "description": "The text is still arriving: the unfinished end is closed and a caret marks the end.",
95
+ "required": false
96
+ }
97
+ ]
98
+ }
99
+ ],
100
+ "examples": [
101
+ {
102
+ "name": "basic",
103
+ "title": "A document",
104
+ "description": "A heading, a list, a table and a titled code block.",
105
+ "path": "examples/markdown/basic.tsx",
106
+ "module": "@krizaka/ui/registry/examples/markdown/basic",
107
+ "code": "import { Markdown } from \"@krizaka/ui/markdown\";\n\nconst text = `## What changed in 3.9\n\nThe queue now **retries** a failed upload with a backoff, and \\`pause\\` keeps its place.\n\n- Pause, resume and retry per file\n- A 429 waits for its \\`Retry-After\\`\n\n| Entry | Size |\n| :--- | ---: |\n| \\`@krizaka/pacer\\` | 2 kB |\n\n\\`\\`\\`ts title=\"uploads.ts\"\nconst queue = createQueue({ retries: 3 });\n\\`\\`\\`\n`;\n\n// A Markdown text on the tokens: headings, lists, a table, a titled code block. Raw HTML is never rendered.\nexport default function MarkdownBasic() {\n return <Markdown className=\"max-w-xl\" codeLabels={{ copy: \"Copy\", copied: \"Copied\" }}>{text}</Markdown>;\n}\n"
108
+ },
109
+ {
110
+ "name": "ai-stream",
111
+ "title": "An AI answer arriving",
112
+ "description": "`streaming`: the end of the text is closed at every cut, with a caret; the button plays it from the start.",
113
+ "path": "examples/markdown/ai-stream.tsx",
114
+ "module": "@krizaka/ui/registry/examples/markdown/ai-stream",
115
+ "code": "\"use client\";\n\nimport { Button } from \"@krizaka/ui/button\";\nimport { Markdown } from \"@krizaka/ui/markdown\";\nimport * as React from \"react\";\n\nconst answer = `Use **a queue**, not a loop: \\`createQueue\\` runs two files at once, retries a failure after a pause and lets you pause one file.\n\n1. Add each file with its \\`id\\`.\n2. Pause it with \\`queue.pause(id)\\`.\n3. Read the progress from the snapshot.\n\nSee [the pacer docs](https://www.krizaka.com/docs/ui).`;\n\n// A text that arrives in pieces: `streaming` closes the unfinished end (no stray `**`) and shows a caret. It rests\n// mid-answer; the button plays it from the first letter.\nexport default function MarkdownAiStream() {\n const [count, setCount] = React.useState(130);\n const [started, setStarted] = React.useState(false);\n const playing = started && count < answer.length;\n React.useEffect(() => {\n if (!playing) return;\n const timer = setTimeout(() => setCount((n) => n + 6), 40);\n return () => clearTimeout(timer);\n }, [count, playing]);\n return (\n <div className=\"grid max-w-xl gap-4\">\n <Markdown streaming={count < answer.length}>{answer.slice(0, count)}</Markdown>\n <Button size=\"sm\" variant=\"secondary\" onClick={() => (setCount(0), setStarted(true))} className=\"justify-self-start\">Play it</Button>\n </div>\n );\n}\n"
116
+ }
117
+ ]
118
+ },
119
+ "native": {
120
+ "entry": "@krizaka/ui/native/markdown",
121
+ "import": "import { Markdown } from \"@krizaka/ui/native/markdown\";",
122
+ "differences": [
123
+ "Drawn in `Text` and `View` from the same AST: no `linkAs`; `onLinkPress` receives the address (http(s) links open with the system by default).",
124
+ "Code blocks scroll sideways and are coloured by the same grammars, from the theme's text roles; there is no copy button.",
125
+ "`headingIds` and `codeLabels` do not exist: there is nothing to link to on a phone, and no copy button."
126
+ ],
127
+ "props": [
128
+ {
129
+ "component": "Markdown",
130
+ "description": "A Markdown text in Text and View: headings, paragraphs, lists (and tasks), quotes, tables, coloured code, links, pictures.",
131
+ "props": [
132
+ {
133
+ "name": "children",
134
+ "type": "string",
135
+ "default": null,
136
+ "description": "The Markdown source.",
137
+ "required": true
138
+ },
139
+ {
140
+ "name": "onLinkPress",
141
+ "type": "((href: string) => void)",
142
+ "default": null,
143
+ "description": "Called with the address of a link that is pressed. Default: opens `http(s)`, `mailto:` and `tel:` links with the system.",
144
+ "required": false
145
+ },
146
+ {
147
+ "name": "streaming",
148
+ "type": "boolean",
149
+ "default": null,
150
+ "description": "The text is still arriving (an AI answer): the unfinished end is closed, so no `**` or half a link shows.",
151
+ "required": false
152
+ },
153
+ {
154
+ "name": "style",
155
+ "type": "StyleProp<ViewStyle>",
156
+ "default": null,
157
+ "description": "Styles merged on the container.",
158
+ "required": false
159
+ }
160
+ ]
161
+ }
162
+ ],
163
+ "examples": [
164
+ {
165
+ "name": "basic",
166
+ "title": "A document",
167
+ "description": "A heading, a list, a table and a code block.",
168
+ "path": "examples/markdown/native/basic.tsx",
169
+ "module": "@krizaka/ui/registry/examples/markdown/native/basic",
170
+ "code": "import { Markdown } from \"@krizaka/ui/native/markdown\";\nimport { View } from \"react-native\";\n\nconst text = `## What changed in 3.9\n\nThe queue now **retries** a failed upload with a backoff, and \\`pause\\` keeps its place.\n\n- Pause, resume and retry per file\n- A 429 waits for its \\`Retry-After\\`\n\n| Entry | Size |\n| :--- | ---: |\n| \\`@krizaka/pacer\\` | 2 kB |\n\n\\`\\`\\`ts title=\"uploads.ts\"\nconst queue = createQueue({ retries: 3 });\n\\`\\`\\`\n`;\n\n// The same text as the web example, in Text and View; a link calls `onLinkPress` with its address.\nexport default function NativeMarkdownBasic() {\n return (\n <View style={{ width: 340 }}>\n <Markdown onLinkPress={() => {}}>{text}</Markdown>\n </View>\n );\n}\n",
171
+ "screenshots": {
172
+ "dark": "examples/markdown/native/basic.dark.png",
173
+ "light": "examples/markdown/native/basic.light.png"
174
+ }
175
+ },
176
+ {
177
+ "name": "ai-stream",
178
+ "title": "An AI answer arriving",
179
+ "description": "`streaming` with the answer cut mid-sentence: nothing half-written shows.",
180
+ "path": "examples/markdown/native/ai-stream.tsx",
181
+ "module": "@krizaka/ui/registry/examples/markdown/native/ai-stream",
182
+ "code": "import { Markdown } from \"@krizaka/ui/native/markdown\";\nimport { View } from \"react-native\";\n\n// An AI answer cut mid-sentence: `streaming` closes what is open, so no `**` or half a link shows on the phone.\nconst arriving = `Use **a queue**, not a loop: \\`createQueue\\` runs two files at once, retries a failure and lets you pause one.\n\n1. Add each file with its \\`id\\`.\n2. Pause it with \\`queue.pause(id)\\`.\n3. Read the progress from the **snap`;\n\nexport default function NativeMarkdownAiStream() {\n return (\n <View style={{ width: 340 }}>\n <Markdown streaming>{arriving}</Markdown>\n </View>\n );\n}\n",
183
+ "screenshots": {
184
+ "dark": "examples/markdown/native/ai-stream.dark.png",
185
+ "light": "examples/markdown/native/ai-stream.light.png"
186
+ }
187
+ }
188
+ ]
189
+ },
190
+ "files": [
191
+ {
192
+ "path": "markdown/index.ts",
193
+ "content": "/** @krizaka/ui/markdown — `Markdown`: a Markdown text on the tokens (TanStack Markdown), safe by default, with a streaming mode for an AI answer. */\nexport { Markdown, type MarkdownLinkProps, type MarkdownProps } from \"./markdown\";\nexport { type BlockNode, healStreaming, type InlineNode, type ParseOptions,parseSource } from \"./parse\";\n"
194
+ },
195
+ {
196
+ "path": "markdown/markdown.tsx",
197
+ "content": "// Server-safe (no hook, no context): a Markdown text rendered on the tokens, from TanStack Markdown's AST. Raw HTML is\n// never enabled (it is shown as text), executable URLs are dropped, links to another site carry `rel=\"noopener\n// noreferrer\"`, and every fenced block is a `CodeBlock` (the same grammars, the same colours). `streaming` is for a\n// text that arrives in pieces — an AI answer: the unfinished end is closed and a caret marks where it goes on.\nimport { CodeBlock } from \"@krizaka/ui/code-block\";\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { type BlockNode, type InlineNode, parseSource, textOf } from \"./parse\";\n\nexport type MarkdownLinkProps = React.ComponentProps<\"a\">;\n\nexport type MarkdownProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The Markdown source. */\n children: string;\n /** The text is still arriving: the unfinished end is closed and a caret marks the end. */\n streaming?: boolean;\n /** Gives every heading an `id`, for `#section` links. */\n headingIds?: boolean;\n /** The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button. */\n codeLabels?: { copy: string; copied: string };\n /** Your router's link (Next's `Link`) for the links that stay on the site (`/docs`, `#section`); the others are plain `a`. */\n linkAs?: React.ElementType<MarkdownLinkProps>;\n};\n\ntype Context = Pick<MarkdownProps, \"codeLabels\" | \"linkAs\">;\n\nconst external = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nfunction Inlines({ nodes, ctx }: { nodes: readonly InlineNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Inline key={i} node={node} ctx={ctx} />);\n}\n\nfunction Inline({ node, ctx }: { node: InlineNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"text\":\n return node.value;\n case \"inlineCode\":\n return <code>{node.value}</code>;\n case \"strong\":\n return (\n <strong>\n <Inlines nodes={node.children} ctx={ctx} />\n </strong>\n );\n case \"emphasis\":\n return (\n <em>\n <Inlines nodes={node.children} ctx={ctx} />\n </em>\n );\n case \"strike\":\n return (\n <del>\n <Inlines nodes={node.children} ctx={ctx} />\n </del>\n );\n case \"break\":\n return <br />;\n case \"link\": {\n const Link = external(node.href) ? \"a\" : (ctx.linkAs ?? \"a\");\n const props = external(node.href) && !node.href.startsWith(\"mailto:\") && !node.href.startsWith(\"tel:\") ? { rel: \"noopener noreferrer\" } : {};\n return (\n <Link href={node.href} title={node.title} {...props}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Link>\n );\n }\n case \"image\":\n return <img src={node.src} alt={node.alt} title={node.title} loading=\"lazy\" />;\n case \"footnoteReference\":\n return (\n <sup>\n <a id={`kz-fnref-${node.id}`} href={`#kz-fn-${node.id}`} data-footnote-ref=\"\">\n {node.number}\n </a>\n </sup>\n );\n // Raw HTML is never enabled by the parser: it arrives as text. A node of this kind is shown as text too.\n case \"inlineHtml\":\n return node.value;\n default:\n return null;\n }\n}\n\nfunction Blocks({ nodes, ctx }: { nodes: readonly BlockNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Block key={i} node={node} ctx={ctx} />);\n}\n\nfunction Block({ node, ctx }: { node: BlockNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"heading\": {\n const Tag = `h${node.depth}` as const;\n return (\n <Tag id={node.id}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Tag>\n );\n }\n case \"paragraph\":\n return (\n <p>\n <Inlines nodes={node.children} ctx={ctx} />\n </p>\n );\n case \"code\":\n return (\n <CodeBlock\n code={node.value}\n language={node.lang ?? \"text\"}\n file={node.title ?? node.file}\n copyLabel={ctx.codeLabels?.copy}\n copiedLabel={ctx.codeLabels?.copied}\n annotations={node.highlightLines?.map((line) => ({ lines: line, kind: \"highlight\" as const }))}\n />\n );\n case \"list\": {\n const Tag = node.ordered ? \"ol\" : \"ul\";\n const tasks = node.items.some((item) => item.checked !== undefined);\n return (\n <Tag start={node.ordered && node.start !== undefined && node.start !== 1 ? node.start : undefined} data-tasks={tasks ? \"\" : undefined}>\n {node.items.map((item, i) => (\n <li key={i}>\n {item.checked !== undefined ? (\n <label>\n <input type=\"checkbox\" checked={item.checked} disabled readOnly />{\" \"}\n <ListItem blocks={item.children} ctx={ctx} />\n </label>\n ) : (\n <ListItem blocks={item.children} ctx={ctx} />\n )}\n </li>\n ))}\n </Tag>\n );\n }\n case \"blockquote\":\n return (\n <blockquote>\n <Blocks nodes={node.children} ctx={ctx} />\n </blockquote>\n );\n case \"table\":\n return (\n <div className=\"kz-md-table\">\n <table>\n <thead>\n <tr>\n {node.header.map((cell, i) => (\n <th key={i} scope=\"col\" style={{ textAlign: node.align[i] ?? undefined }}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {node.rows.map((row, r) => (\n <tr key={r}>\n {row.map((cell, i) => (\n <td key={i} style={{ textAlign: node.align[i] ?? undefined }}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"thematicBreak\":\n return <hr />;\n case \"footnotes\":\n return (\n <section data-footnotes=\"\">\n <ol>\n {node.items.map((item) => (\n <li key={item.id} id={`kz-fn-${item.id}`}>\n <Blocks nodes={item.children} ctx={ctx} />\n </li>\n ))}\n </ol>\n </section>\n );\n // Raw HTML (never enabled) arrives as a paragraph of text; extensions are not enabled either. Shown as text.\n case \"html\":\n return <p>{node.value}</p>;\n default:\n return null;\n }\n}\n\n/** A list item: a lone paragraph is inline (a tight list), anything else keeps its blocks. */\nfunction ListItem({ blocks, ctx }: { blocks: readonly BlockNode[]; ctx: Context }) {\n return blocks.map((block, i) => (block.type === \"paragraph\" && blocks.length === 1 ? <Inlines key={i} nodes={block.children} ctx={ctx} /> : <Block key={i} node={block} ctx={ctx} />));\n}\n\n/** A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes. */\nexport function Markdown({ children, streaming, headingIds, codeLabels, linkAs, className, ...props }: MarkdownProps) {\n const blocks = parseSource(children, { streaming, headingIds });\n return (\n <div className={cn(\"kz-md\", className)} data-streaming={streaming ? \"\" : undefined} aria-busy={streaming || undefined} {...props}>\n <Blocks nodes={blocks} ctx={{ codeLabels, linkAs }} />\n </div>\n );\n}\n\nexport { textOf };\n"
198
+ },
199
+ {
200
+ "path": "markdown/meta.ts",
201
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Markdown\",\n summary:\n \"A Markdown text on the tokens — headings, lists and tasks, quotes, tables, links, images, footnotes, fenced code in `CodeBlock` — parsed by TanStack Markdown, safe by default (raw HTML is never rendered), with a streaming mode for a text that arrives in pieces: an AI answer. React Native draws the same AST in `Text` and `View`.\",\n status: \"beta\",\n category: \"data-display\",\n platforms: \"both\",\n whenToUse: [\n \"An AI answer, a message, a release note, a description written by a person or a model.\",\n \"A text that arrives in pieces (`streaming`): the unfinished end is closed, so no `**` or half a link ever shows.\",\n \"Content that comes from a user or a model, which must never run HTML.\",\n ],\n whenNotToUse: [\n { when: \"For the documentation of a product: its own MDX pipeline (Fumadocs on krizaka.com) renders it, with components.\" },\n { when: \"For one sample of code on a page.\", use: \"code-block\" },\n { when: \"For a few words with a bold part: write the JSX.\" },\n ],\n bestPractices: [\n \"Pass the words of the code blocks' copy button with `codeLabels` (translated); without them a block has no button.\",\n \"Give your router's link to `linkAs`: the links that stay on the site go through it, the others open as plain links with `rel=\\\"noopener noreferrer\\\"`.\",\n \"Set `streaming` while the text is arriving and drop it when it is complete: the caret goes away and the text is read as written.\",\n \"Write the source in the user's language; the component adds no words of its own.\",\n \"On a phone, give `onLinkPress` to open a link in your app's browser; the default opens http(s) links with the system.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Tab\", action: \"Moves through the links and the code blocks (which scroll sideways with the arrow keys).\" },\n ],\n notes: [\n \"The structure is real elements: headings in order, lists, a table with column headers, `figure` and `figcaption` for a titled code block.\",\n \"A task list is disabled checkboxes inside their labels, so each item reads as done or not done.\",\n \"While `streaming`, the root carries `aria-busy`; the caret is decorative (a CSS pseudo-element) and still under reduced motion.\",\n \"Raw HTML in the source is shown as text, and links or images with an executable address (`javascript:`, `data:`) are dropped.\",\n ],\n },\n related: [\"code-block\", \"page\", \"txt\"],\n web: {\n imports: [\"Markdown\", \"parseSource\", \"healStreaming\"],\n examples: [\n { name: \"basic\", title: \"A document\", description: \"A heading, a list, a table and a titled code block.\" },\n { name: \"ai-stream\", title: \"An AI answer arriving\", description: \"`streaming`: the end of the text is closed at every cut, with a caret; the button plays it from the start.\" },\n ],\n },\n native: {\n entry: \"@krizaka/ui/native/markdown\",\n imports: [\"Markdown\"],\n differences: [\n \"Drawn in `Text` and `View` from the same AST: no `linkAs`; `onLinkPress` receives the address (http(s) links open with the system by default).\",\n \"Code blocks scroll sideways and are coloured by the same grammars, from the theme's text roles; there is no copy button.\",\n \"`headingIds` and `codeLabels` do not exist: there is nothing to link to on a phone, and no copy button.\",\n ],\n examples: [\n { name: \"basic\", title: \"A document\", description: \"A heading, a list, a table and a code block.\" },\n { name: \"ai-stream\", title: \"An AI answer arriving\", description: \"`streaming` with the answer cut mid-sentence: nothing half-written shows.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
202
+ },
203
+ {
204
+ "path": "markdown/parse.ts",
205
+ "content": "// The shared half of Markdown, web and React Native: the source becomes TanStack Markdown's serializable AST\n// (`@tanstack/markdown/parser`) — raw HTML is never enabled (it stays text), executable URLs are dropped (`javascript:`,\n// `data:`), and a text that is still arriving is healed so the half-written end reads like the finished one.\nimport type { BlockNode } from \"@tanstack/markdown\";\nimport { streamingMarkdownExtension } from \"@tanstack/markdown/extensions/streaming\";\nimport { parseMarkdown } from \"@tanstack/markdown/parser\";\n\nexport type { BlockNode, InlineNode, ListItemNode, TableNode } from \"@tanstack/markdown\";\n\nexport type ParseOptions = {\n /** The text is still arriving (an AI answer): the unfinished end is closed, so it never shows `**` or a half link. */\n streaming?: boolean;\n /** Gives every heading an `id` (`#section` links, a table of contents). */\n headingIds?: boolean;\n};\n\nconst FENCE = /^ {0,3}(```|~~~)/gm;\n\n/** A link or image cut at its end: `[label`, `[label]`, `[label](http…` become `label`, an image is dropped. Linear: one scan of the last `[`. */\nfunction healLink(text: string): string {\n const open = text.lastIndexOf(\"[\");\n if (open < 0) return text;\n const rest = text.slice(open + 1);\n if (rest.includes(\"\\n\")) return text;\n const image = open > 0 && text[open - 1] === \"!\";\n const close = rest.indexOf(\"]\");\n const after = close < 0 ? \"\" : rest.slice(close + 1);\n const cut = close < 0 || after === \"\" || (after.startsWith(\"(\") && !after.includes(\")\"));\n if (!cut) return text;\n const head = text.slice(0, image ? open - 1 : open);\n return image ? head : head + (close < 0 ? rest : rest.slice(0, close));\n}\n\n/**\n * Closes what a text cut in the middle leaves open: an emphasis (`**`, `~~`) or a code span, a link or image whose\n * address has not arrived. Inside an open code fence nothing is healed — it is shown as code as it comes.\n */\nexport function healStreaming(source: string): string {\n if (((source.match(FENCE) ?? []).length & 1) === 1) return source;\n let out = healLink(source);\n const cut = out.lastIndexOf(\"\\n\\n\");\n const block = cut < 0 ? out : out.slice(cut + 2);\n const open = (marker: string) => (block.split(marker).length - 1) % 2 === 1;\n // A marker just opened, with nothing after it yet, is dropped rather than shown as an empty emphasis.\n const close = (marker: string) => (out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`);\n if (open(\"`\")) return close(\"`\");\n for (const marker of [\"**\", \"~~\"]) if (open(marker)) out = close(marker);\n return out;\n}\n\n/** The blocks of a Markdown source. */\nexport function parseSource(source: string, { streaming = false, headingIds = false }: ParseOptions = {}): BlockNode[] {\n return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...(streaming && { extensions: [streamingMarkdownExtension()] }) }).children;\n}\n\n/** The text of inline nodes (a heading's label, an image's alt). */\nexport function textOf(nodes: readonly { type: string; value?: string; children?: readonly unknown[] }[]): string {\n return nodes\n .map((node) => (node.children ? textOf(node.children as typeof nodes) : (node.value ?? \"\")))\n .join(\"\");\n}\n"
206
+ },
207
+ {
208
+ "path": "meta.ts",
209
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"markdown\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
210
+ }
211
+ ],
212
+ "dependencies": [
213
+ "@tanstack/markdown@1.0.0"
214
+ ],
215
+ "registryDependencies": [
216
+ "cn",
217
+ "code-block"
218
+ ],
219
+ "demo": {
220
+ "path": "examples/markdown/basic.tsx",
221
+ "content": "import { Markdown } from \"@krizaka/ui/markdown\";\n\nconst text = `## What changed in 3.9\n\nThe queue now **retries** a failed upload with a backoff, and \\`pause\\` keeps its place.\n\n- Pause, resume and retry per file\n- A 429 waits for its \\`Retry-After\\`\n\n| Entry | Size |\n| :--- | ---: |\n| \\`@krizaka/pacer\\` | 2 kB |\n\n\\`\\`\\`ts title=\"uploads.ts\"\nconst queue = createQueue({ retries: 3 });\n\\`\\`\\`\n`;\n\n// A Markdown text on the tokens: headings, lists, a table, a titled code block. Raw HTML is never rendered.\nexport default function MarkdownBasic() {\n return <Markdown className=\"max-w-xl\" codeLabels={{ copy: \"Copy\", copied: \"Copied\" }}>{text}</Markdown>;\n}\n"
222
+ },
223
+ "props": [
224
+ {
225
+ "component": "Markdown",
226
+ "description": "A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes.",
227
+ "props": [
228
+ {
229
+ "name": "children",
230
+ "type": "string",
231
+ "default": null,
232
+ "description": "The Markdown source.",
233
+ "required": true
234
+ },
235
+ {
236
+ "name": "codeLabels",
237
+ "type": "{ copy: string; copied: string; }",
238
+ "default": null,
239
+ "description": "The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button.",
240
+ "required": false
241
+ },
242
+ {
243
+ "name": "headingIds",
244
+ "type": "boolean",
245
+ "default": null,
246
+ "description": "Gives every heading an `id`, for `#section` links.",
247
+ "required": false
248
+ },
249
+ {
250
+ "name": "linkAs",
251
+ "type": "ElementType<DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, keyof IntrinsicElements>",
252
+ "default": null,
253
+ "description": "Your router's link (Next's `Link`) for the links that stay on the site (`/docs`, `#section`); the others are plain `a`.",
254
+ "required": false
255
+ },
256
+ {
257
+ "name": "streaming",
258
+ "type": "boolean",
259
+ "default": null,
260
+ "description": "The text is still arriving: the unfinished end is closed and a caret marks the end.",
261
+ "required": false
262
+ }
263
+ ]
264
+ }
265
+ ]
266
+ }
@@ -136,7 +136,7 @@
136
136
  },
137
137
  {
138
138
  "path": "meta.ts",
139
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
139
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"markdown\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
140
140
  }
141
141
  ],
142
142
  "dependencies": [
@@ -148,7 +148,7 @@
148
148
  },
149
149
  {
150
150
  "path": "meta.ts",
151
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
151
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"markdown\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
152
152
  }
153
153
  ],
154
154
  "dependencies": [],
@@ -127,7 +127,7 @@
127
127
  },
128
128
  {
129
129
  "path": "meta.ts",
130
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
130
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"markdown\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
131
131
  }
132
132
  ],
133
133
  "dependencies": [],
@@ -845,7 +845,7 @@
845
845
  },
846
846
  {
847
847
  "path": "meta.ts",
848
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
848
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"markdown\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
849
849
  }
850
850
  ],
851
851
  "dependencies": [