@krizaka/ui 3.53.0 → 3.55.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 (504) hide show
  1. package/README.md +1 -0
  2. package/dist/account-menu.d.ts +6 -2
  3. package/dist/account-menu.js +8 -1
  4. package/dist/account-menu.js.map +1 -1
  5. package/dist/account-settings.js +28 -88
  6. package/dist/account-settings.js.map +1 -1
  7. package/dist/anatomy-plate.d.ts +4 -1
  8. package/dist/anatomy-plate.js +4 -0
  9. package/dist/anatomy-plate.js.map +1 -1
  10. package/dist/app-header/search.d.ts +63 -0
  11. package/dist/app-header/search.js +70 -0
  12. package/dist/app-header/search.js.map +1 -0
  13. package/dist/app-header.d.ts +88 -4
  14. package/dist/app-header.js +63 -5
  15. package/dist/app-header.js.map +1 -1
  16. package/dist/app-shell.js +31 -40
  17. package/dist/app-shell.js.map +1 -1
  18. package/dist/argument-card.js +3 -3
  19. package/dist/bird-plate.d.ts +3 -1
  20. package/dist/bird-plate.js +2 -2
  21. package/dist/bird-plate.js.map +1 -1
  22. package/dist/blocks.css +202 -27
  23. package/dist/chapter.d.ts +20 -3
  24. package/dist/chapter.js +82 -31
  25. package/dist/chapter.js.map +1 -1
  26. package/dist/chat.js +2 -2
  27. package/dist/chat.js.map +1 -1
  28. package/dist/checkbox.js +1 -1
  29. package/dist/checkbox.js.map +1 -1
  30. package/dist/chip.js +5 -98
  31. package/dist/chip.js.map +1 -1
  32. package/dist/choice-grid.js +28 -88
  33. package/dist/choice-grid.js.map +1 -1
  34. package/dist/{chunk-YGIN2YIY.js → chunk-3X3OF4TI.js} +2 -2
  35. package/dist/chunk-3X3OF4TI.js.map +1 -0
  36. package/dist/chunk-5VEVYDVC.js +11 -0
  37. package/dist/chunk-5VEVYDVC.js.map +1 -0
  38. package/dist/chunk-7U5TXA5Q.js +105 -0
  39. package/dist/chunk-7U5TXA5Q.js.map +1 -0
  40. package/dist/{chunk-KBQD6HE3.js → chunk-AEP3K4YD.js} +3 -3
  41. package/dist/{chunk-KBQD6HE3.js.map → chunk-AEP3K4YD.js.map} +1 -1
  42. package/dist/chunk-BDIU5LZB.js +18 -0
  43. package/dist/chunk-BDIU5LZB.js.map +1 -0
  44. package/dist/chunk-CSRKYZ53.js +39 -0
  45. package/dist/chunk-CSRKYZ53.js.map +1 -0
  46. package/dist/chunk-HEKDKM5Y.js +128 -0
  47. package/dist/chunk-HEKDKM5Y.js.map +1 -0
  48. package/dist/chunk-KGLRZMCY.js +16 -0
  49. package/dist/chunk-KGLRZMCY.js.map +1 -0
  50. package/dist/chunk-KID32IRJ.js +18 -0
  51. package/dist/chunk-KID32IRJ.js.map +1 -0
  52. package/dist/{chunk-ZQW7LF4Y.js → chunk-OIMXKRXF.js} +2 -2
  53. package/dist/{chunk-YHKTYFOQ.js → chunk-QWKCNSPF.js} +271 -47
  54. package/dist/chunk-QWKCNSPF.js.map +1 -0
  55. package/dist/{chunk-4LYKBSOG.js → chunk-USPOIQV5.js} +2 -2
  56. package/dist/chunk-USPOIQV5.js.map +1 -0
  57. package/dist/{chunk-7NRSPHAF.js → chunk-VB7MWE2N.js} +2 -2
  58. package/dist/{chunk-7NRSPHAF.js.map → chunk-VB7MWE2N.js.map} +1 -1
  59. package/dist/chunk-VIPSZXO4.js +103 -0
  60. package/dist/chunk-VIPSZXO4.js.map +1 -0
  61. package/dist/chunk-YCZ7N6Q6.js +385 -0
  62. package/dist/chunk-YCZ7N6Q6.js.map +1 -0
  63. package/dist/code-block.js +81 -98
  64. package/dist/code-block.js.map +1 -1
  65. package/dist/combobox.js +2 -2
  66. package/dist/component-page.d.ts +4 -1
  67. package/dist/component-page.js +5 -0
  68. package/dist/component-page.js.map +1 -1
  69. package/dist/continue-prompt.d.ts +4 -1
  70. package/dist/continue-prompt.js +4 -0
  71. package/dist/continue-prompt.js.map +1 -1
  72. package/dist/data-table.d.ts +60 -4
  73. package/dist/data-table.js +19 -7
  74. package/dist/date-picker.js +2 -2
  75. package/dist/date-picker.js.map +1 -1
  76. package/dist/detail-layout.d.ts +28 -17
  77. package/dist/detail-layout.js +73 -16
  78. package/dist/detail-layout.js.map +1 -1
  79. package/dist/doodle.d.ts +21 -0
  80. package/dist/doodle.js +39 -0
  81. package/dist/doodle.js.map +1 -0
  82. package/dist/drop-overlay.d.ts +12 -4
  83. package/dist/drop-overlay.js +16 -6
  84. package/dist/drop-overlay.js.map +1 -1
  85. package/dist/elephant.d.ts +2 -0
  86. package/dist/elephant.js +4 -0
  87. package/dist/elephant.js.map +1 -1
  88. package/dist/field.js +1 -1
  89. package/dist/field.js.map +1 -1
  90. package/dist/filter-bar.js +2 -2
  91. package/dist/force-update-screen.d.ts +5 -1
  92. package/dist/force-update-screen.js +4 -0
  93. package/dist/force-update-screen.js.map +1 -1
  94. package/dist/gallery-page.d.ts +4 -1
  95. package/dist/gallery-page.js +4 -0
  96. package/dist/gallery-page.js.map +1 -1
  97. package/dist/genui.js +5 -5
  98. package/dist/image-cropper.d.ts +10 -2
  99. package/dist/image-cropper.js +4 -0
  100. package/dist/image-cropper.js.map +1 -1
  101. package/dist/kanban.d.ts +3 -0
  102. package/dist/kanban.js +4 -0
  103. package/dist/kanban.js.map +1 -1
  104. package/dist/landscape.d.ts +4 -1
  105. package/dist/landscape.js +4 -0
  106. package/dist/landscape.js.map +1 -1
  107. package/dist/list.js +1 -1
  108. package/dist/maintenance-screen.d.ts +5 -1
  109. package/dist/maintenance-screen.js +4 -0
  110. package/dist/maintenance-screen.js.map +1 -1
  111. package/dist/map.js +6 -0
  112. package/dist/map.js.map +1 -1
  113. package/dist/markdown.d.ts +5 -1
  114. package/dist/markdown.js +12 -5
  115. package/dist/markdown.js.map +1 -1
  116. package/dist/match-dialog.d.ts +4 -1
  117. package/dist/match-dialog.js +4 -0
  118. package/dist/match-dialog.js.map +1 -1
  119. package/dist/media-capture.d.ts +6 -1
  120. package/dist/media-capture.js +4 -0
  121. package/dist/media-capture.js.map +1 -1
  122. package/dist/meter.js +5 -121
  123. package/dist/meter.js.map +1 -1
  124. package/dist/native/map.js +6 -0
  125. package/dist/native/map.js.map +1 -1
  126. package/dist/native/markdown.d.ts +9 -2
  127. package/dist/native/markdown.js +46 -7
  128. package/dist/native/markdown.js.map +1 -1
  129. package/dist/native/media-capture.d.ts +1 -0
  130. package/dist/native/media-capture.js +5 -0
  131. package/dist/native/media-capture.js.map +1 -1
  132. package/dist/native/vertical-video.d.ts +8 -2
  133. package/dist/native/vertical-video.js +6 -0
  134. package/dist/native/vertical-video.js.map +1 -1
  135. package/dist/native.d.ts +94 -4
  136. package/dist/native.js +1236 -1110
  137. package/dist/native.js.map +1 -1
  138. package/dist/nav-bar/me.d.ts +32 -0
  139. package/dist/nav-bar/me.js +23 -0
  140. package/dist/nav-bar/me.js.map +1 -0
  141. package/dist/nav-bar.d.ts +60 -2
  142. package/dist/nav-bar.js +92 -48
  143. package/dist/nav-bar.js.map +1 -1
  144. package/dist/notification-preferences.d.ts +151 -0
  145. package/dist/notification-preferences.js +77 -0
  146. package/dist/notification-preferences.js.map +1 -0
  147. package/dist/page-header.js +1 -1
  148. package/dist/page-header.js.map +1 -1
  149. package/dist/pointer-light.js +5 -0
  150. package/dist/pointer-light.js.map +1 -1
  151. package/dist/product-page.d.ts +4 -1
  152. package/dist/product-page.js +4 -0
  153. package/dist/product-page.js.map +1 -1
  154. package/dist/profile-card.d.ts +119 -1
  155. package/dist/profile-card.js +68 -21
  156. package/dist/profile-card.js.map +1 -1
  157. package/dist/promise-card.d.ts +4 -1
  158. package/dist/promise-card.js +4 -0
  159. package/dist/promise-card.js.map +1 -1
  160. package/dist/quota-meter.d.ts +113 -0
  161. package/dist/quota-meter.js +58 -0
  162. package/dist/quota-meter.js.map +1 -0
  163. package/dist/radio-group.js +6 -8
  164. package/dist/radio-group.js.map +1 -1
  165. package/dist/rail-nav.d.ts +18 -2
  166. package/dist/rail-nav.js +25 -2
  167. package/dist/rail-nav.js.map +1 -1
  168. package/dist/retro-tv.d.ts +30 -0
  169. package/dist/retro-tv.js +124 -0
  170. package/dist/retro-tv.js.map +1 -0
  171. package/dist/router.d.ts +12 -1
  172. package/dist/router.js +85 -8
  173. package/dist/router.js.map +1 -1
  174. package/dist/scroll-stage.d.ts +5 -1
  175. package/dist/scroll-stage.js +5 -0
  176. package/dist/scroll-stage.js.map +1 -1
  177. package/dist/search-field.js +1 -1
  178. package/dist/showcase.d.ts +9 -2
  179. package/dist/showcase.js +5 -0
  180. package/dist/showcase.js.map +1 -1
  181. package/dist/signature.d.ts +19 -0
  182. package/dist/signature.js +39 -0
  183. package/dist/signature.js.map +1 -0
  184. package/dist/site-footer.js +1 -1
  185. package/dist/sites.js +1 -1
  186. package/dist/sites.js.map +1 -1
  187. package/dist/slot.d.ts +1 -1
  188. package/dist/stat.js +1 -1
  189. package/dist/stat.js.map +1 -1
  190. package/dist/status-page.d.ts +4 -1
  191. package/dist/status-page.js +5 -0
  192. package/dist/status-page.js.map +1 -1
  193. package/dist/stuck.d.ts +27 -0
  194. package/dist/stuck.js +26 -0
  195. package/dist/stuck.js.map +1 -0
  196. package/dist/swipe-deck.d.ts +9 -2
  197. package/dist/swipe-deck.js +5 -0
  198. package/dist/swipe-deck.js.map +1 -1
  199. package/dist/switch.js +4 -32
  200. package/dist/switch.js.map +1 -1
  201. package/dist/tabs.d.ts +47 -8
  202. package/dist/tabs.js +62 -12
  203. package/dist/tabs.js.map +1 -1
  204. package/dist/tag-input.js +2 -2
  205. package/dist/tag-selector.js +1 -1
  206. package/dist/terminal-replay.d.ts +4 -1
  207. package/dist/terminal-replay.js +4 -0
  208. package/dist/terminal-replay.js.map +1 -1
  209. package/dist/tree-view.js +4 -0
  210. package/dist/tree-view.js.map +1 -1
  211. package/dist/update-prompt.d.ts +5 -1
  212. package/dist/update-prompt.js +4 -0
  213. package/dist/update-prompt.js.map +1 -1
  214. package/dist/video-player.js +7 -376
  215. package/dist/video-player.js.map +1 -1
  216. package/dist/video-trimmer.d.ts +6 -1
  217. package/dist/video-trimmer.js +4 -0
  218. package/dist/video-trimmer.js.map +1 -1
  219. package/package.json +143 -31
  220. package/registry/account-menu.json +9 -8
  221. package/registry/account-settings.json +2 -2
  222. package/registry/achievement.json +3 -4
  223. package/registry/action-bar.json +3 -3
  224. package/registry/action-sheet.json +1 -0
  225. package/registry/activity-feed.json +2 -1
  226. package/registry/agenda.json +1 -1
  227. package/registry/alert.json +1 -1
  228. package/registry/anatomy-plate.json +13 -5
  229. package/registry/app-header.json +470 -23
  230. package/registry/app-shell.json +2 -2
  231. package/registry/argument-card.json +1 -1
  232. package/registry/audio-player.json +1 -1
  233. package/registry/auth-layout.json +1 -1
  234. package/registry/avatar.json +3 -2
  235. package/registry/badge.json +2 -1
  236. package/registry/bird-plate.json +16 -2
  237. package/registry/button.json +7 -6
  238. package/registry/captcha.json +1 -1
  239. package/registry/card-grid.json +1 -1
  240. package/registry/card.json +7 -6
  241. package/registry/catalogue-page.json +3 -4
  242. package/registry/changelog-page.json +3 -5
  243. package/registry/chapter.json +46 -10
  244. package/registry/chat.json +3 -2
  245. package/registry/checkbox.json +4 -3
  246. package/registry/chip-link.json +2 -2
  247. package/registry/chip-row.json +2 -2
  248. package/registry/chip.json +3 -2
  249. package/registry/choice-grid.json +3 -4
  250. package/registry/code-block.json +1 -1
  251. package/registry/combobox.json +3 -2
  252. package/registry/command.json +2 -2
  253. package/registry/comparison-table.json +1 -1
  254. package/registry/component-page.json +14 -5
  255. package/registry/confirm-button.json +4 -4
  256. package/registry/contact-page.json +1 -1
  257. package/registry/continue-prompt.json +13 -5
  258. package/registry/countdown.json +4 -3
  259. package/registry/cover-hero.json +3 -4
  260. package/registry/cta-panel.json +1 -1
  261. package/registry/dashboard-layout.json +1 -1
  262. package/registry/data-table.json +63 -7
  263. package/registry/date-picker.json +5 -4
  264. package/registry/decision-list.json +1 -1
  265. package/registry/description-list.json +2 -1
  266. package/registry/detail-layout.json +30 -6
  267. package/registry/dialog.json +7 -6
  268. package/registry/docs-article-page.json +3 -4
  269. package/registry/docs-hub-page.json +1 -1
  270. package/registry/docs-layout.json +1 -1
  271. package/registry/docs-nav.json +1 -1
  272. package/registry/document-viewer.json +3 -5
  273. package/registry/doodle.json +209 -0
  274. package/registry/drop-overlay.json +30 -16
  275. package/registry/dropdown-menu.json +2 -2
  276. package/registry/elephant.json +15 -7
  277. package/registry/empty-state.json +2 -1
  278. package/registry/engine-flow.json +1 -1
  279. package/registry/error-page.json +3 -5
  280. package/registry/examples/app-header/family.d.ts +4 -0
  281. package/registry/examples/app-header/family.js +41 -0
  282. package/registry/examples/app-header/family.tsx +37 -0
  283. package/registry/examples/app-header/native/family.d.ts +4 -0
  284. package/registry/examples/app-header/native/family.dark.png +0 -0
  285. package/registry/examples/app-header/native/family.js +25 -0
  286. package/registry/examples/app-header/native/family.light.png +0 -0
  287. package/registry/examples/app-header/native/family.tsx +33 -0
  288. package/registry/examples/chapter/album.d.ts +4 -0
  289. package/registry/examples/chapter/album.js +34 -0
  290. package/registry/examples/chapter/album.tsx +30 -0
  291. package/registry/examples/chapter/in-ruled-page.d.ts +4 -0
  292. package/registry/examples/chapter/in-ruled-page.js +23 -0
  293. package/registry/examples/chapter/in-ruled-page.tsx +19 -0
  294. package/registry/examples/data-table/applicant-files.d.ts +4 -0
  295. package/registry/examples/data-table/applicant-files.js +24 -0
  296. package/registry/examples/data-table/applicant-files.tsx +23 -0
  297. package/registry/examples/data-table/checks.d.ts +4 -0
  298. package/registry/examples/data-table/checks.js +25 -0
  299. package/registry/examples/data-table/checks.tsx +24 -0
  300. package/registry/examples/detail-layout/profile.d.ts +4 -0
  301. package/registry/examples/detail-layout/profile.js +26 -0
  302. package/registry/examples/detail-layout/profile.tsx +21 -0
  303. package/registry/examples/doodle/kinds.d.ts +4 -0
  304. package/registry/examples/doodle/kinds.js +17 -0
  305. package/registry/examples/doodle/kinds.tsx +14 -0
  306. package/registry/examples/doodle/margin.d.ts +4 -0
  307. package/registry/examples/doodle/margin.js +17 -0
  308. package/registry/examples/doodle/margin.tsx +17 -0
  309. package/registry/examples/markdown/native/basic.dark.png +0 -0
  310. package/registry/examples/markdown/native/basic.light.png +0 -0
  311. package/registry/examples/markdown/native/wide-table.d.ts +4 -0
  312. package/registry/examples/markdown/native/wide-table.dark.png +0 -0
  313. package/registry/examples/markdown/native/wide-table.js +23 -0
  314. package/registry/examples/markdown/native/wide-table.light.png +0 -0
  315. package/registry/examples/markdown/native/wide-table.tsx +22 -0
  316. package/registry/examples/markdown/wide-table.d.ts +4 -0
  317. package/registry/examples/markdown/wide-table.js +28 -0
  318. package/registry/examples/markdown/wide-table.tsx +23 -0
  319. package/registry/examples/nav-bar/family-product.js +2 -4
  320. package/registry/examples/nav-bar/family-product.tsx +3 -5
  321. package/registry/examples/notification-preferences/default.d.ts +4 -0
  322. package/registry/examples/notification-preferences/default.js +29 -0
  323. package/registry/examples/notification-preferences/default.tsx +28 -0
  324. package/registry/examples/profile-card/banner.d.ts +4 -0
  325. package/registry/examples/profile-card/banner.js +28 -0
  326. package/registry/examples/profile-card/banner.tsx +24 -0
  327. package/registry/examples/profile-card/native/banner.d.ts +4 -0
  328. package/registry/examples/profile-card/native/banner.dark.png +0 -0
  329. package/registry/examples/profile-card/native/banner.js +22 -0
  330. package/registry/examples/profile-card/native/banner.light.png +0 -0
  331. package/registry/examples/profile-card/native/banner.tsx +18 -0
  332. package/registry/examples/quota-meter/default.d.ts +4 -0
  333. package/registry/examples/quota-meter/default.js +26 -0
  334. package/registry/examples/quota-meter/default.tsx +20 -0
  335. package/registry/examples/quota-meter/full.d.ts +4 -0
  336. package/registry/examples/quota-meter/full.js +25 -0
  337. package/registry/examples/quota-meter/full.tsx +18 -0
  338. package/registry/examples/rail-nav/shortcuts.d.ts +4 -0
  339. package/registry/examples/rail-nav/shortcuts.js +49 -0
  340. package/registry/examples/rail-nav/shortcuts.tsx +40 -0
  341. package/registry/examples/retro-tv/cabinet.d.ts +4 -0
  342. package/registry/examples/retro-tv/cabinet.js +12 -0
  343. package/registry/examples/retro-tv/cabinet.tsx +7 -0
  344. package/registry/examples/retro-tv/portable.d.ts +4 -0
  345. package/registry/examples/retro-tv/portable.js +12 -0
  346. package/registry/examples/retro-tv/portable.tsx +7 -0
  347. package/registry/examples/signature/both.d.ts +4 -0
  348. package/registry/examples/signature/both.js +11 -0
  349. package/registry/examples/signature/both.tsx +6 -0
  350. package/registry/examples/signature/variants.d.ts +4 -0
  351. package/registry/examples/signature/variants.js +14 -0
  352. package/registry/examples/signature/variants.tsx +11 -0
  353. package/registry/examples/stuck/card.d.ts +4 -0
  354. package/registry/examples/stuck/card.js +13 -0
  355. package/registry/examples/stuck/card.tsx +14 -0
  356. package/registry/examples/stuck/holds.d.ts +4 -0
  357. package/registry/examples/stuck/holds.js +12 -0
  358. package/registry/examples/stuck/holds.tsx +16 -0
  359. package/registry/examples/stuck/tag.d.ts +4 -0
  360. package/registry/examples/stuck/tag.js +12 -0
  361. package/registry/examples/stuck/tag.tsx +13 -0
  362. package/registry/examples/tabs/binder.d.ts +4 -0
  363. package/registry/examples/tabs/binder.js +22 -0
  364. package/registry/examples/tabs/binder.tsx +20 -0
  365. package/registry/examples/tabs/native/binder.d.ts +4 -0
  366. package/registry/examples/tabs/native/binder.dark.png +0 -0
  367. package/registry/examples/tabs/native/binder.js +26 -0
  368. package/registry/examples/tabs/native/binder.light.png +0 -0
  369. package/registry/examples/tabs/native/binder.tsx +22 -0
  370. package/registry/fab.json +1 -0
  371. package/registry/field.json +3 -2
  372. package/registry/file-upload.json +6 -11
  373. package/registry/filter-bar.json +2 -2
  374. package/registry/flock.json +3 -4
  375. package/registry/footnote.json +2 -3
  376. package/registry/force-update-screen.json +14 -5
  377. package/registry/form.json +5 -4
  378. package/registry/gallery-page.json +14 -5
  379. package/registry/genui.json +2 -2
  380. package/registry/glossary-term.json +1 -1
  381. package/registry/glossary.json +1 -1
  382. package/registry/glow-card.json +2 -2
  383. package/registry/goal-gauge.json +2 -1
  384. package/registry/header.json +1 -0
  385. package/registry/how-it-works-page.json +3 -5
  386. package/registry/image-cropper.json +13 -5
  387. package/registry/image-viewer.json +3 -4
  388. package/registry/index.json +306 -91
  389. package/registry/iso.json +1 -1
  390. package/registry/kanban.json +13 -5
  391. package/registry/kbd.json +1 -1
  392. package/registry/kpi-explained.json +2 -2
  393. package/registry/landing-page.json +3 -5
  394. package/registry/landscape.json +13 -5
  395. package/registry/language-switch.json +1 -1
  396. package/registry/legal-page.json +1 -1
  397. package/registry/link-card.json +1 -1
  398. package/registry/list.json +2 -1
  399. package/registry/loader.json +2 -1
  400. package/registry/longform-page.json +1 -1
  401. package/registry/maintenance-screen.json +14 -5
  402. package/registry/map.json +3 -2
  403. package/registry/markdown.json +86 -9
  404. package/registry/marketing-layout.json +1 -1
  405. package/registry/marquee.json +1 -1
  406. package/registry/mascot-quote.json +1 -1
  407. package/registry/mascot-state.json +12 -11
  408. package/registry/mascots.json +2 -1
  409. package/registry/match-dialog.json +14 -5
  410. package/registry/media-capture.json +14 -5
  411. package/registry/media-gallery.json +3 -5
  412. package/registry/media-grid.json +1 -1
  413. package/registry/media-picker.json +2 -3
  414. package/registry/meta-row.json +2 -1
  415. package/registry/meter.json +4 -3
  416. package/registry/nav-bar.json +100 -9
  417. package/registry/next-actions.json +1 -1
  418. package/registry/notification-preferences.json +250 -0
  419. package/registry/number-ticker.json +2 -1
  420. package/registry/onboarding.json +1 -0
  421. package/registry/opening-hours.json +1 -1
  422. package/registry/otp-input.json +1 -0
  423. package/registry/page-header.json +2 -2
  424. package/registry/page-hero.json +1 -1
  425. package/registry/page-index.json +1 -1
  426. package/registry/page.json +3 -3
  427. package/registry/pagination.json +2 -1
  428. package/registry/permission-prompt.json +2 -2
  429. package/registry/photo-grid.json +4 -4
  430. package/registry/pointer-light.json +6 -2
  431. package/registry/popover.json +2 -2
  432. package/registry/pricing-page.json +4 -6
  433. package/registry/product-page.json +14 -5
  434. package/registry/profile-card.json +232 -8
  435. package/registry/progress.json +4 -3
  436. package/registry/promise-card.json +13 -5
  437. package/registry/pull-to-refresh.json +1 -0
  438. package/registry/quota-meter.json +308 -0
  439. package/registry/radio-group.json +4 -3
  440. package/registry/rail-nav.json +48 -10
  441. package/registry/recipes.json +1 -1
  442. package/registry/retro-tv.json +478 -0
  443. package/registry/router.json +3 -3
  444. package/registry/screen.json +1 -0
  445. package/registry/scroll-stage.json +14 -5
  446. package/registry/search-field.json +3 -2
  447. package/registry/section-backdrop.json +1 -1
  448. package/registry/segmented.json +1 -0
  449. package/registry/separator.json +2 -2
  450. package/registry/service-card.json +1 -1
  451. package/registry/settings-layout.json +2 -1
  452. package/registry/showcase.json +14 -5
  453. package/registry/signature.json +185 -0
  454. package/registry/site-footer.json +3 -3
  455. package/registry/sites.json +2 -2
  456. package/registry/skeleton.json +2 -1
  457. package/registry/skip-link.json +1 -1
  458. package/registry/slider.json +5 -4
  459. package/registry/slot-picker.json +1 -1
  460. package/registry/slot.json +2 -2
  461. package/registry/specimen-tag.json +1 -1
  462. package/registry/spinner.json +4 -3
  463. package/registry/splash-handoff.json +1 -0
  464. package/registry/split-view.json +1 -1
  465. package/registry/stage-banner.json +2 -3
  466. package/registry/stat.json +2 -2
  467. package/registry/status-page.json +13 -5
  468. package/registry/step-progress.json +2 -1
  469. package/registry/step-showcase.json +3 -5
  470. package/registry/stepper.json +2 -1
  471. package/registry/sticky-action.json +1 -1
  472. package/registry/stuck.json +219 -0
  473. package/registry/swipe-deck.json +14 -5
  474. package/registry/swipeable-row.json +1 -0
  475. package/registry/switch.json +3 -2
  476. package/registry/tab-bar.json +1 -0
  477. package/registry/tabs.json +154 -22
  478. package/registry/tag-input.json +1 -1
  479. package/registry/tag-selector.json +2 -1
  480. package/registry/taped-photo.json +1 -1
  481. package/registry/terminal-replay.json +14 -5
  482. package/registry/text-style.json +5 -5
  483. package/registry/theme.json +2 -1
  484. package/registry/toast.json +2 -1
  485. package/registry/toc.json +1 -1
  486. package/registry/tooltip.json +2 -2
  487. package/registry/tree-view.json +6 -2
  488. package/registry/txt.json +1 -0
  489. package/registry/update-prompt.json +14 -5
  490. package/registry/upload-queue.json +1 -1
  491. package/registry/verified-badge.json +6 -6
  492. package/registry/vertical-video.json +8 -3
  493. package/registry/video-player.json +5 -6
  494. package/registry/video-trimmer.json +13 -5
  495. package/registry/virtual.json +2 -2
  496. package/registry/waveform.json +4 -3
  497. package/dist/chunk-4LYKBSOG.js.map +0 -1
  498. package/dist/chunk-HHFWK2HH.js +0 -163
  499. package/dist/chunk-HHFWK2HH.js.map +0 -1
  500. package/dist/chunk-R3LTSMS5.js +0 -75
  501. package/dist/chunk-R3LTSMS5.js.map +0 -1
  502. package/dist/chunk-YGIN2YIY.js.map +0 -1
  503. package/dist/chunk-YHKTYFOQ.js.map +0 -1
  504. /package/dist/{chunk-ZQW7LF4Y.js.map → chunk-OIMXKRXF.js.map} +0 -0
@@ -2,27 +2,27 @@
2
2
  "name": "app-header",
3
3
  "type": "primitive",
4
4
  "title": "App header",
5
- "summary": "The top bar that sits next to a `RailNav`: the product's name as text, optional status chips, and the actions. The mark is the rail's; on a phone, where the rail is a bottom tab bar, the header shows it too, so one mark is visible at every width.",
6
- "description": "The top bar that sits next to a `RailNav`: the product's name as text, optional status chips, and the actions. The mark is the rail's; on a phone, where the rail is a bottom tab bar, the header shows it too, so one mark is visible at every width.",
5
+ "summary": "The family's top bar beside a `RailNav`: the product's name as text, optional status chips, then the session's controls in a fixed order — search (⌘K), language, the host's actions, messages and notifications with their unread counts, the primary action, the account with its name. Every slot is the host's; an empty one draws nothing.",
6
+ "description": "The family's top bar beside a `RailNav`: the product's name as text, optional status chips, then the session's controls in a fixed order — search (⌘K), language, the host's actions, messages and notifications with their unread counts, the primary action, the account with its name. Every slot is the host's; an empty one draws nothing.",
7
7
  "status": "beta",
8
8
  "deprecated": null,
9
9
  "category": "navigation",
10
- "platforms": "web",
10
+ "platforms": "both",
11
11
  "parity": {
12
12
  "web": {
13
13
  "state": "yes"
14
14
  },
15
15
  "native": {
16
- "state": "n/a",
17
- "reason": "A native screen has its own header.",
18
- "use": "header"
16
+ "state": "yes"
19
17
  },
20
18
  "desktop": {
21
19
  "state": "yes"
22
20
  }
23
21
  },
24
22
  "whenToUse": [
25
- "Beside a `RailNav`, as the bar of every signed-in screen: name, status, theme, notifications, account."
23
+ "Beside a `RailNav`, as the bar of every signed-in screen of a Krizaka app (Orochia, Yomizaka, Akinaza): the same bar in every product.",
24
+ "With `search` (`AppHeaderSearch`) when the app has a palette (`CommandDialog`): ⌘K opens it from anywhere.",
25
+ "With `messages` and `notifications` (`AppHeaderCount`) when the app has an inbox: the unread count is drawn and said."
26
26
  ],
27
27
  "whenNotToUse": [
28
28
  {
@@ -36,35 +36,61 @@
36
36
  {
37
37
  "when": "A page's title block.",
38
38
  "use": "page-header"
39
+ },
40
+ {
41
+ "when": "A native screen's title that folds as the list scrolls.",
42
+ "use": "header"
39
43
  }
40
44
  ],
41
45
  "bestPractices": [
42
46
  "Pass the name as text and the mark as `mark`: never put a mark in the header that the rail already carries on a desktop (`mark` is hidden from `md` up for that reason).",
47
+ "Fill the slots, never `actions` with everything: the order (search · language · actions · messages · notifications · primary · account) is the family's, so every product's bar reads the same.",
48
+ "`AppHeaderSearch` comes from `@krizaka/ui/app-header/search` (a client entry: its ⌘K listener); the bar itself stays server-safe.",
49
+ "Give each count its words with their plural (`countLabel=\"3 nouvelles\"`): the badge is decorative, the name says it.",
50
+ "The language is `LanguageSwitch` (the same control as `NavBar.Language`); the account is `AccountMenu` with `showName` (the name from `sm`).",
43
51
  "Chips say a state, not a place: « Local · Sovereign », « Dev », a version.",
44
- "Keep the actions to what changes the session (theme, notifications, account menu): the sections are in the rail.",
45
52
  "Make the name a link home with `homeLink` (your router's link) or `href`."
46
53
  ],
47
54
  "accessibility": {
48
- "keyboard": [],
55
+ "keyboard": [
56
+ {
57
+ "keys": "⌘ K / Ctrl K",
58
+ "action": "Opens the palette from anywhere on the page (`AppHeaderSearch`, `shortcut`)."
59
+ },
60
+ {
61
+ "keys": "Tab",
62
+ "action": "Walks the controls in their visual order: search, language, actions, messages, notifications, primary, account."
63
+ },
64
+ {
65
+ "keys": "Enter / Space",
66
+ "action": "Opens the palette, follows a count's link or opens the host's popover."
67
+ }
68
+ ],
49
69
  "notes": [
50
- "A `header` landmark; the name is plain text (the page's `h1` is the screen's, below).",
51
- "The mark is decorative beside the name: give the home link its name through the visible text."
70
+ "A `header` landmark (`banner`); the name is plain text (the page's `h1` is the screen's, below).",
71
+ "A count is in the control's name: « Notifications, 3 new »; the digits on the icon are hidden from screen readers. A page it leads to is `aria-current=\"page\"`.",
72
+ "The search trigger is a button named by its words at every width (sr-only below `md`), with `aria-haspopup=\"dialog\"` and `aria-keyshortcuts`.",
73
+ "Reduced motion: the controls' colour transitions stop; nothing else moves.",
74
+ "At 360 px the name truncates and the controls keep their size: nothing is clipped."
52
75
  ]
53
76
  },
54
77
  "related": [
55
78
  "rail-nav",
56
79
  "app-shell",
57
80
  "nav-bar",
81
+ "command",
82
+ "language-switch",
83
+ "account-menu",
58
84
  "badge",
59
85
  "page-header"
60
86
  ],
61
87
  "web": {
62
88
  "entry": "@krizaka/ui/app-header",
63
- "import": "import { AppHeader } from \"@krizaka/ui/app-header\";",
89
+ "import": "import { AppHeader, AppHeaderCount } from \"@krizaka/ui/app-header\";",
64
90
  "props": [
65
91
  {
66
92
  "component": "AppHeader",
67
- "description": "The app's top bar: the name next to the rail, the mark on a phone, chips, actions.",
93
+ "description": "The app's top bar: the name next to the rail, the mark on a phone, chips, then search · language · actions · messages · notifications · primary · account.",
68
94
  "props": [
69
95
  {
70
96
  "name": "name",
@@ -73,11 +99,18 @@
73
99
  "description": "The product's name, as text (`Orazaka`): the only brand element on a desktop.",
74
100
  "required": true
75
101
  },
102
+ {
103
+ "name": "account",
104
+ "type": "ReactNode",
105
+ "default": null,
106
+ "description": "The account, last: `AccountMenu` with `showName` (the name from `sm`), or the visitor's Sign in.",
107
+ "required": false
108
+ },
76
109
  {
77
110
  "name": "actions",
78
111
  "type": "ReactNode",
79
112
  "default": null,
80
- "description": "On the right: theme, notifications, the account menu.",
113
+ "description": "The host's own controls, after the language (a theme toggle). Alone, it is the whole right side, as before.",
81
114
  "required": false
82
115
  },
83
116
  {
@@ -101,12 +134,109 @@
101
134
  "description": "Makes the name (and the mark) a link home: pass your router's link as `homeLink` or give a `href`.",
102
135
  "required": false
103
136
  },
137
+ {
138
+ "name": "language",
139
+ "type": "ReactNode",
140
+ "default": null,
141
+ "description": "The language: `LanguageSwitch` (`@krizaka/ui/language-switch`) — a globe and « FR », labelled.",
142
+ "required": false
143
+ },
104
144
  {
105
145
  "name": "mark",
106
146
  "type": "ReactNode",
107
147
  "default": null,
108
148
  "description": "The product's mark (`<ProductLogo id=\"orazaka\" size={22} />`): shown below `md` only, where the rail (which carries it) is a bottom bar.",
109
149
  "required": false
150
+ },
151
+ {
152
+ "name": "messages",
153
+ "type": "ReactNode",
154
+ "default": null,
155
+ "description": "Messages: an `AppHeaderCount` (an icon button or link with its unread count). Empty: hidden.",
156
+ "required": false
157
+ },
158
+ {
159
+ "name": "notifications",
160
+ "type": "ReactNode",
161
+ "default": null,
162
+ "description": "Notifications: an `AppHeaderCount` (or the trigger of the host's popover). Empty: hidden.",
163
+ "required": false
164
+ },
165
+ {
166
+ "name": "primary",
167
+ "type": "ReactNode",
168
+ "default": null,
169
+ "description": "The one primary action of the app (« New check », #588), before the account.",
170
+ "required": false
171
+ },
172
+ {
173
+ "name": "search",
174
+ "type": "ReactNode",
175
+ "default": null,
176
+ "description": "The search trigger, first of the controls: `AppHeaderSearch` (`@krizaka/ui/app-header/search`) — a field from `md`, an icon below, ⌘K.",
177
+ "required": false
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ "component": "AppHeaderCount",
183
+ "description": "An icon button or link of the top bar with an unread count, the count announced in its name.",
184
+ "props": [
185
+ {
186
+ "name": "icon",
187
+ "type": "ReactNode",
188
+ "default": null,
189
+ "description": "The icon (`<NotificationIcon />` of\n@krizaka /icons): decorative, the name is `label`.",
190
+ "required": true
191
+ },
192
+ {
193
+ "name": "label",
194
+ "type": "string",
195
+ "default": null,
196
+ "description": "What it is (« Notifications », « Messages ») — passed translated; its accessible name, with the count.",
197
+ "required": true
198
+ },
199
+ {
200
+ "name": "count",
201
+ "type": "number",
202
+ "default": null,
203
+ "description": "The unread count; 0 or absent draws no badge.",
204
+ "required": false
205
+ },
206
+ {
207
+ "name": "countLabel",
208
+ "type": "string",
209
+ "default": null,
210
+ "description": "The count in words, said after the label (« 3 new », « 3 nouvelles ») — passed translated, with its plural. Default: the number.",
211
+ "required": false
212
+ },
213
+ {
214
+ "name": "current",
215
+ "type": "boolean",
216
+ "default": null,
217
+ "description": "The page is the one it leads to (`aria-current=\"page\"`).",
218
+ "required": false
219
+ },
220
+ {
221
+ "name": "href",
222
+ "type": "string",
223
+ "default": null,
224
+ "description": "A link to the page (`/notifications`) instead of a button.",
225
+ "required": false
226
+ },
227
+ {
228
+ "name": "linkAs",
229
+ "type": "ElementType<any, keyof IntrinsicElements>",
230
+ "default": null,
231
+ "description": "Your router's link (Next's `Link`) for `href`; an `<a>` otherwise.",
232
+ "required": false
233
+ },
234
+ {
235
+ "name": "max",
236
+ "type": "number",
237
+ "default": "99",
238
+ "description": "Above it the badge shows `max+`.",
239
+ "required": false
110
240
  }
111
241
  ]
112
242
  }
@@ -115,36 +245,249 @@
115
245
  {
116
246
  "name": "basic",
117
247
  "title": "Name, chip and actions",
118
- "description": "Orazaka's bar: the name, a « Local · Sovereign » chip, and the theme and account on the right; the mark joins the name below `md`.",
248
+ "description": "Orazaka's bar: the name, a « Local · Sovereign » chip, and the account on the right; the mark joins the name below `md`.",
119
249
  "path": "examples/app-header/basic.tsx",
120
250
  "module": "@krizaka/ui/registry/examples/app-header/basic",
121
251
  "code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppHeader } from \"@krizaka/ui/app-header\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\n\n// The name is text; the mark shows below `md` only (a desktop's rail carries it). Chips say a state.\nexport default function AppHeaderBasic() {\n return (\n <AppHeader\n name=\"Orazaka\"\n mark={<ProductLogo id=\"orazaka\" size={22} animated={false} />}\n href=\"#home\"\n chips={<Badge tone=\"success\" dot>Local · Sovereign</Badge>}\n actions={<Button size=\"sm\" variant=\"secondary\">Account</Button>}\n />\n );\n}\n"
252
+ },
253
+ {
254
+ "name": "family",
255
+ "title": "The family's bar",
256
+ "description": "Yomizaka's bar: search with ⌘K and its palette, the language, messages and notifications with their counts, the account with its name.",
257
+ "path": "examples/app-header/family.tsx",
258
+ "module": "@krizaka/ui/registry/examples/app-header/family",
259
+ "code": "\"use client\";\n\nimport { ProductLogo } from \"@krizaka/ui\";\nimport { AccountMenu } from \"@krizaka/ui/account-menu\";\nimport { AppHeader, AppHeaderCount } from \"@krizaka/ui/app-header\";\nimport { AppHeaderSearch } from \"@krizaka/ui/app-header/search\";\nimport { Command, CommandDialog } from \"@krizaka/ui/command\";\nimport { languagesFor, LanguageSwitch } from \"@krizaka/ui/language-switch\";\nimport { useState } from \"react\";\n\nconst icon = (d: string) => <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden><path d={d} /></svg>;\n\n// The family's bar: search (⌘K opens the palette), language, messages, notifications with their counts, the account.\nexport default function AppHeaderFamily() {\n const [open, setOpen] = useState(false);\n return (\n <>\n <AppHeader\n name=\"Yomizaka\"\n mark={<ProductLogo id=\"yomizaka\" size={22} animated={false} />}\n href=\"#home\"\n search={<AppHeaderSearch label=\"Search files and checks\" onOpen={() => setOpen(true)} />}\n language={<LanguageSwitch label=\"Language\" current=\"en\" languages={languagesFor(\"/en/files\")} />}\n messages={<AppHeaderCount label=\"Messages\" icon={icon(\"M4 5h16v11H8l-4 4z\")} count={2} countLabel=\"2 unread\" href=\"#messages\" />}\n notifications={<AppHeaderCount label=\"Notifications\" icon={icon(\"M6 16V11a6 6 0 1 1 12 0v5l2 2H4zM10 20h4\")} count={3} countLabel=\"3 new\" />}\n account={<AccountMenu label=\"Account\" name=\"Eric Tremblay\" email=\"eric@example.com\" showName avatar={{ defaultAvatar: { brand: \"yomizaka\", seed: \"eric\" } }} signOut={{ label: \"Sign out\", onSelect: () => {} }} />}\n />\n <CommandDialog open={open} onOpenChange={setOpen} label=\"Search\">\n <Command.Input placeholder=\"Search files and checks\" />\n <Command.List label=\"Results\" emptyLabel=\"Nothing matches.\">\n <Command.Item onSelect={() => setOpen(false)}>Applicant files</Command.Item>\n <Command.Item onSelect={() => setOpen(false)}>Checks</Command.Item>\n </Command.List>\n </CommandDialog>\n </>\n );\n}\n"
260
+ }
261
+ ]
262
+ },
263
+ "native": {
264
+ "entry": "@krizaka/ui/native",
265
+ "import": "import { AppHeader, AppHeaderCount, AppHeaderSearch } from \"@krizaka/ui/native\";",
266
+ "differences": [
267
+ "`onHome` instead of `href`/`homeLink`; no `chips` (a phone has no room).",
268
+ "`AppHeaderSearch` is an icon (no ⌘K): it opens the app's search screen; `AppHeaderCount` takes `onPress`.",
269
+ "The safe area's top inset is the provider's (`insets`)."
270
+ ],
271
+ "deprecated": null,
272
+ "props": [
273
+ {
274
+ "component": "AppHeader",
275
+ "description": "The app's top bar, the web's `AppHeader` on a phone: the mark and the name, then the controls in the family's fixed order —\nsearch · language · actions · messages · notifications · primary · account — under the status bar's safe area. The\nname truncates first; the controls keep their size.",
276
+ "props": [
277
+ {
278
+ "name": "name",
279
+ "type": "string",
280
+ "default": null,
281
+ "description": "The product's name, as text (`Yomizaka`).",
282
+ "required": true
283
+ },
284
+ {
285
+ "name": "account",
286
+ "type": "ReactNode",
287
+ "default": null,
288
+ "description": "The account, last (an `Avatar` in a button, named).",
289
+ "required": false
290
+ },
291
+ {
292
+ "name": "actions",
293
+ "type": "ReactNode",
294
+ "default": null,
295
+ "description": "The host's own controls, after the language.",
296
+ "required": false
297
+ },
298
+ {
299
+ "name": "language",
300
+ "type": "ReactNode",
301
+ "default": null,
302
+ "description": "The language (« FR »): the host's control, labelled.",
303
+ "required": false
304
+ },
305
+ {
306
+ "name": "mark",
307
+ "type": "ReactNode",
308
+ "default": null,
309
+ "description": "The product's mark (`<ProductMark id=\"yomizaka\" size={24} />`), before the name.",
310
+ "required": false
311
+ },
312
+ {
313
+ "name": "messages",
314
+ "type": "ReactNode",
315
+ "default": null,
316
+ "description": "Messages: an `AppHeaderCount`. Empty: hidden.",
317
+ "required": false
318
+ },
319
+ {
320
+ "name": "notifications",
321
+ "type": "ReactNode",
322
+ "default": null,
323
+ "description": "Notifications: an `AppHeaderCount`. Empty: hidden.",
324
+ "required": false
325
+ },
326
+ {
327
+ "name": "onHome",
328
+ "type": "(() => void)",
329
+ "default": null,
330
+ "description": "Makes the name a button home; its accessible name is `name`.",
331
+ "required": false
332
+ },
333
+ {
334
+ "name": "primary",
335
+ "type": "ReactNode",
336
+ "default": null,
337
+ "description": "The one primary action, before the account.",
338
+ "required": false
339
+ },
340
+ {
341
+ "name": "search",
342
+ "type": "ReactNode",
343
+ "default": null,
344
+ "description": "The search trigger, first of the controls: `AppHeaderSearch`.",
345
+ "required": false
346
+ },
347
+ {
348
+ "name": "style",
349
+ "type": "StyleProp<ViewStyle>",
350
+ "default": null,
351
+ "description": "Styles merged last.",
352
+ "required": false
353
+ }
354
+ ]
355
+ },
356
+ {
357
+ "component": "AppHeaderCount",
358
+ "description": "An icon button of the top bar with an unread count, the count said in its name (« Notifications, 3 new »).",
359
+ "props": [
360
+ {
361
+ "name": "icon",
362
+ "type": "ReactNode",
363
+ "default": null,
364
+ "description": "The icon (an `@krizaka/icons/native` glyph): decorative.",
365
+ "required": true
366
+ },
367
+ {
368
+ "name": "label",
369
+ "type": "string",
370
+ "default": null,
371
+ "description": "What it is (« Notifications ») — passed translated; its accessible name, with the count.",
372
+ "required": true
373
+ },
374
+ {
375
+ "name": "count",
376
+ "type": "number",
377
+ "default": null,
378
+ "description": "The unread count; 0 or absent draws no badge.",
379
+ "required": false
380
+ },
381
+ {
382
+ "name": "countLabel",
383
+ "type": "string",
384
+ "default": null,
385
+ "description": "The count in words (« 3 new ») — passed translated, with its plural. Default: the number.",
386
+ "required": false
387
+ },
388
+ {
389
+ "name": "current",
390
+ "type": "boolean",
391
+ "default": null,
392
+ "description": "The screen it leads to is on view (drawn as pressed).",
393
+ "required": false
394
+ },
395
+ {
396
+ "name": "max",
397
+ "type": "number",
398
+ "default": "99",
399
+ "description": "Above it the badge shows `max+`. Default 99.",
400
+ "required": false
401
+ },
402
+ {
403
+ "name": "onPress",
404
+ "type": "(() => void)",
405
+ "default": null,
406
+ "description": "Opens the screen (Notifications, Messages).",
407
+ "required": false
408
+ }
409
+ ]
410
+ },
411
+ {
412
+ "component": "AppHeaderSearch",
413
+ "description": "The top bar's search trigger on a phone: a magnifier, named by `label`, that opens the app's search.",
414
+ "props": [
415
+ {
416
+ "name": "label",
417
+ "type": "string",
418
+ "default": null,
419
+ "description": "What it searches (« Search files and checks ») — passed translated: the button's name.",
420
+ "required": true
421
+ },
422
+ {
423
+ "name": "onOpen",
424
+ "type": "() => void",
425
+ "default": null,
426
+ "description": "Opens the app's search screen or palette.",
427
+ "required": true
428
+ }
429
+ ]
430
+ }
431
+ ],
432
+ "examples": [
433
+ {
434
+ "name": "family",
435
+ "title": "The family's bar",
436
+ "description": "Search, language, notifications with their count and the account, at 360 pt.",
437
+ "path": "examples/app-header/native/family.tsx",
438
+ "module": "@krizaka/ui/registry/examples/app-header/native/family",
439
+ "code": "import { AppHeader, AppHeaderCount, AppHeaderSearch, Avatar, ProductMark, useTheme } from \"@krizaka/ui/native\";\nimport { Pressable, Text, View } from \"react-native\";\nimport Svg, { Path } from \"react-native-svg\";\n\n// The family's bar on a phone: search, language, notifications with their count, the account.\nexport default function NativeAppHeaderFamily() {\n const { theme } = useTheme();\n const bell = (\n <Svg width={20} height={20} viewBox=\"0 0 24 24\" fill=\"none\" stroke={theme.textSecondary} strokeWidth={1.8} strokeLinecap=\"round\" aria-hidden>\n <Path d=\"M6 16V11a6 6 0 1 1 12 0v5l2 2H4zM10 20h4\" />\n </Svg>\n );\n return (\n <View style={{ width: 360 }}>\n <AppHeader\n name=\"Yomizaka\"\n mark={<ProductMark id=\"yomizaka\" size={24} animated={false} />}\n search={<AppHeaderSearch label=\"Search files and checks\" onOpen={() => undefined} />}\n language={\n <Pressable role=\"button\" aria-label=\"Language: EN (English)\" style={{ minHeight: 40, justifyContent: \"center\", paddingHorizontal: 6 }}>\n <Text style={{ color: theme.textSecondary, fontWeight: \"600\", fontSize: 12 }}>EN</Text>\n </Pressable>\n }\n notifications={<AppHeaderCount label=\"Notifications\" icon={bell} count={3} countLabel=\"3 new\" onPress={() => undefined} />}\n account={\n <Pressable role=\"button\" aria-label=\"Account, Eric Tremblay\">\n <Avatar size=\"sm\" fallback=\"E\" />\n </Pressable>\n }\n />\n </View>\n );\n}\n",
440
+ "screenshots": {
441
+ "dark": "examples/app-header/native/family.dark.png",
442
+ "light": "examples/app-header/native/family.light.png"
443
+ }
122
444
  }
123
445
  ]
124
446
  },
125
- "native": null,
126
447
  "files": [
127
448
  {
128
449
  "path": "app-header/app-header.tsx",
129
- "content": "// Server-safe: no hook, no context. The top bar that sits next to a `RailNav`: the product's NAME as text (the mark is the\n// rail's), then optional chips, then the actions. Where the rail is a bottom tab bar (a phone) it has no room for the mark,\n// so the header shows it there and only there: exactly one brand mark is visible at every width.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const appHeader = tv({\n slots: {\n root: \"sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-1 px-4 md:bg-surface-1 md:pl-3 md:pr-5\",\n // The rail's mark is 56 px tall and so is this bar: the word shares the mark's line, right after the rail.\n brand: \"flex min-w-0 items-center gap-2 font-display text-sm font-bold tracking-tight text-fg no-underline\",\n mark: \"inline-flex shrink-0 md:hidden\",\n name: \"truncate\",\n chips: \"flex shrink-0 items-center gap-2\",\n actions: \"ml-auto flex shrink-0 items-center gap-2\",\n },\n});\nconst s = appHeader();\n\nexport type AppHeaderProps = Omit<React.ComponentProps<\"header\">, \"children\"> & {\n /** The product's name, as text (`Orazaka`): the only brand element on a desktop. */\n name: string;\n /** The product's mark (`<ProductLogo id=\"orazaka\" size={22} />`): shown below `md` only, where the rail (which carries it) is a bottom bar. */\n mark?: React.ReactNode;\n /** Makes the name (and the mark) a link home: pass your router's link as `homeLink` or give a `href`. */\n href?: string;\n /** Your router's link element for the home link; it receives the name and the mark as children. */\n homeLink?: React.ReactElement<{ className?: string; children?: React.ReactNode }>;\n /** Status chips beside the name (`Badge`): « Local · Sovereign », « Dev », a version. */\n chips?: React.ReactNode;\n /** On the right: theme, notifications, the account menu. */\n actions?: React.ReactNode;\n};\n\n/** The app's top bar: the name next to the rail, the mark on a phone, chips, actions. */\nexport function AppHeader({ name, mark, href, homeLink, chips, actions, className, ...props }: AppHeaderProps) {\n const brand = (\n <>\n {mark && <span data-brand-mark className={s.mark()}>{mark}</span>}\n <span className={s.name()}>{name}</span>\n </>\n );\n return (\n <header className={s.root({ className })} {...props}>\n {homeLink ? (\n React.cloneElement(homeLink, { className: s.brand() }, brand)\n ) : href ? (\n <a href={href} className={s.brand()}>{brand}</a>\n ) : (\n <div className={s.brand()}>{brand}</div>\n )}\n {chips && <div className={s.chips()}>{chips}</div>}\n {actions && <div className={s.actions()}>{actions}</div>}\n </header>\n );\n}\n"
450
+ "content": "// Server-safe: no hook, no context. The family's top bar (#640): the product's NAME as text (the mark is the rail's), optional\n// chips, then the session's controls in a fixed order — search, language, the host's actions (a theme toggle), messages,\n// notifications, the primary action, the account. Every slot is the host's; a slot left empty draws nothing. Where the\n// rail is a bottom tab bar (a phone) it has no room for the mark, so the header shows it there and only there: exactly\n// one brand mark is visible at every width. At 360 px the name truncates first; the controls never clip.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const appHeader = tv({\n slots: {\n root: \"sticky top-0 z-30 flex h-14 w-full min-w-0 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-1 px-4 md:bg-surface-1 md:pl-3 md:pr-5\",\n // The rail's mark is 56 px tall and so is this bar: the word shares the mark's line, right after the rail.\n brand: \"flex min-w-0 items-center gap-2 font-display text-sm font-bold tracking-tight text-fg no-underline\",\n mark: \"inline-flex shrink-0 md:hidden\",\n name: \"truncate\",\n chips: \"flex shrink-0 items-center gap-2\",\n // The controls: one row at the right, in the order of the slots. They keep their size; the name gives way.\n actions: \"ml-auto flex shrink-0 items-center gap-1 sm:gap-2\",\n search: \"flex min-w-0 shrink-0\",\n },\n});\nconst s = appHeader();\n\nexport type AppHeaderProps = Omit<React.ComponentProps<\"header\">, \"children\"> & {\n /** The product's name, as text (`Orazaka`): the only brand element on a desktop. */\n name: string;\n /** The product's mark (`<ProductLogo id=\"orazaka\" size={22} />`): shown below `md` only, where the rail (which carries it) is a bottom bar. */\n mark?: React.ReactNode;\n /** Makes the name (and the mark) a link home: pass your router's link as `homeLink` or give a `href`. */\n href?: string;\n /** Your router's link element for the home link; it receives the name and the mark as children. */\n homeLink?: React.ReactElement<{ className?: string; children?: React.ReactNode }>;\n /** Status chips beside the name (`Badge`): « Local · Sovereign », « Dev », a version. */\n chips?: React.ReactNode;\n /** The search trigger, first of the controls: `AppHeaderSearch` (`@krizaka/ui/app-header/search`) — a field from `md`, an icon below, ⌘K. */\n search?: React.ReactNode;\n /** The language: `LanguageSwitch` (`@krizaka/ui/language-switch`) — a globe and « FR », labelled. */\n language?: React.ReactNode;\n /** The host's own controls, after the language (a theme toggle). Alone, it is the whole right side, as before. */\n actions?: React.ReactNode;\n /** Messages: an `AppHeaderCount` (an icon button or link with its unread count). Empty: hidden. */\n messages?: React.ReactNode;\n /** Notifications: an `AppHeaderCount` (or the trigger of the host's popover). Empty: hidden. */\n notifications?: React.ReactNode;\n /** The one primary action of the app (« New check », #588), before the account. */\n primary?: React.ReactNode;\n /** The account, last: `AccountMenu` with `showName` (the name from `sm`), or the visitor's Sign in. */\n account?: React.ReactNode;\n};\n\n/** The app's top bar: the name next to the rail, the mark on a phone, chips, then search · language · actions · messages · notifications · primary · account. */\nexport function AppHeader({ name, mark, href, homeLink, chips, search, language, actions, messages, notifications, primary, account, className, ...props }: AppHeaderProps) {\n const brand = (\n <>\n {mark && <span data-brand-mark className={s.mark()}>{mark}</span>}\n <span className={s.name()}>{name}</span>\n </>\n );\n const hasControls = [search, language, actions, messages, notifications, primary, account].some(Boolean);\n return (\n <header className={s.root({ className })} {...props}>\n {homeLink ? (\n React.cloneElement(homeLink, { className: s.brand() }, brand)\n ) : href ? (\n <a href={href} className={s.brand()}>{brand}</a>\n ) : (\n <div className={s.brand()}>{brand}</div>\n )}\n {chips && <div className={s.chips()}>{chips}</div>}\n {/* The order is the family's, not the host's: the same bar in every product. */}\n {hasControls && (\n <div className={s.actions()}>\n {search && <div data-slot=\"search\" className={s.search()}>{search}</div>}\n {language}\n {actions}\n {messages}\n {notifications}\n {primary}\n {account}\n </div>\n )}\n </header>\n );\n}\n"
451
+ },
452
+ {
453
+ "path": "app-header/count-text.ts",
454
+ "content": "// Pure, no import: shared by the web's AppHeaderCount and the native one (src/native/app-header.tsx).\n\n/** What the badge shows: the count, or `max+` above `max`; nothing at 0. */\nexport function countText(count: number | undefined, max = 99): string | null {\n if (!count || count <= 0 || !Number.isFinite(count)) return null;\n return count > max ? `${max}+` : String(Math.floor(count));\n}\n\n/** The control's accessible name: the label alone, or the label and the count's words (« Notifications, 3 new »). */\nexport function countName(label: string, count: number | undefined, countLabel?: string): string {\n if (!countText(count, Number.POSITIVE_INFINITY)) return label;\n return `${label}, ${countLabel ?? String(Math.floor(count!))}`;\n}\n"
455
+ },
456
+ {
457
+ "path": "app-header/count.tsx",
458
+ "content": "// Server-safe: no hook. An icon control of the top bar with an unread count — messages, notifications. A link (`href`, your\n// router's `linkAs`) or a button (the trigger of the host's popover, through `asChild` of Radix: its props and ref are\n// spread here). The count is drawn as a small badge and said in the name: « Notifications, 3 new ».\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { countName, countText } from \"./count-text\";\n\nexport const appHeaderCount = tv({\n slots: {\n root:\n \"relative inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-fg-secondary no-underline transition-colors \" +\n \"hover:bg-surface-2 hover:text-fg aria-expanded:bg-surface-2 aria-expanded:text-fg aria-[current=page]:bg-surface-2 aria-[current=page]:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none\",\n icon: \"inline-flex h-5 w-5 items-center justify-center [&>svg]:h-5 [&>svg]:w-5\",\n count:\n \"pointer-events-none absolute -right-0.5 -top-0.5 flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-accent px-1 \" +\n \"font-mono text-[10px] font-bold leading-none tabular-nums text-on-accent ring-2 ring-surface-1\",\n },\n});\nconst s = appHeaderCount();\n\nexport type AppHeaderCountProps = Omit<React.ComponentProps<\"button\">, \"children\"> & {\n /** What it is (« Notifications », « Messages ») — passed translated; its accessible name, with the count. */\n label: string;\n /** The icon (`<NotificationIcon />` of @krizaka/icons): decorative, the name is `label`. */\n icon: React.ReactNode;\n /** The unread count; 0 or absent draws no badge. */\n count?: number;\n /** The count in words, said after the label (« 3 new », « 3 nouvelles ») — passed translated, with its plural. Default: the number. */\n countLabel?: string;\n /**\n * Above it the badge shows `max+`.\n * @default 99\n */\n max?: number;\n /** A link to the page (`/notifications`) instead of a button. */\n href?: string;\n /** Your router's link (Next's `Link`) for `href`; an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /** The page is the one it leads to (`aria-current=\"page\"`). */\n current?: boolean;\n};\n\n/** An icon button or link of the top bar with an unread count, the count announced in its name. */\nexport function AppHeaderCount({ label, icon, count, countLabel, max = 99, href, linkAs, current, className, type, ...props }: AppHeaderCountProps) {\n const text = countText(count, max);\n const shared = {\n ...props,\n \"aria-label\": countName(label, count, countLabel),\n \"aria-current\": current ? (\"page\" as const) : undefined,\n \"data-unread\": text ? \"\" : undefined,\n className: s.root({ className }),\n };\n const children = (\n <>\n <span aria-hidden className={s.icon()}>{icon}</span>\n {text && <span aria-hidden data-count className={s.count()}>{text}</span>}\n </>\n );\n if (href) {\n const Link = linkAs ?? \"a\";\n return <Link href={href} {...shared}>{children}</Link>;\n }\n return <button type={type ?? \"button\"} {...shared}>{children}</button>;\n}\n"
130
459
  },
131
460
  {
132
461
  "path": "app-header/index.ts",
133
- "content": "/** @krizaka/ui/app-header — `AppHeader` (`name`, `mark`, `chips`, `actions`), `appHeader`: the top bar beside a `RailNav`. Server-safe. */\nexport { AppHeader, appHeader, type AppHeaderProps } from \"./app-header\";\n"
462
+ "content": "/**\n * @krizaka/ui/app-header — `AppHeader` (`name`, `mark`, `chips`, then the slots `search` · `language` · `actions` · `messages` ·\n * `notifications` · `primary` · `account`), `appHeader`, and `AppHeaderCount` (an icon control with its unread count): the\n * family's top bar beside a `RailNav`. Server-safe; the search trigger (a hook: ⌘K) is `@krizaka/ui/app-header/search`.\n */\nexport { AppHeader, appHeader, type AppHeaderProps } from \"./app-header\";\nexport { AppHeaderCount, appHeaderCount, type AppHeaderCountProps } from \"./count\";\nexport { countName, countText } from \"./count-text\";\n"
134
463
  },
135
464
  {
136
465
  "path": "app-header/meta.ts",
137
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"App header\",\n summary: \"The top bar that sits next to a `RailNav`: the product's name as text, optional status chips, and the actions. The mark is the rail's; on a phone, where the rail is a bottom tab bar, the header shows it too, so one mark is visible at every width.\",\n status: \"beta\",\n category: \"navigation\",\n platforms: \"web\",\n parity: { native: { status: \"n/a\", reason: \"A native screen has its own header.\", use: \"header\" }, desktop: \"web\" },\n whenToUse: [\n \"Beside a `RailNav`, as the bar of every signed-in screen: name, status, theme, notifications, account.\",\n ],\n whenNotToUse: [\n { when: \"The bar of a site, with its menus and its call to action.\", use: \"nav-bar\" },\n { when: \"A screen's title and its actions inside an AppShell.\", use: \"app-shell\" },\n { when: \"A page's title block.\", use: \"page-header\" },\n ],\n bestPractices: [\n \"Pass the name as text and the mark as `mark`: never put a mark in the header that the rail already carries on a desktop (`mark` is hidden from `md` up for that reason).\",\n \"Chips say a state, not a place: « Local · Sovereign », « Dev », a version.\",\n \"Keep the actions to what changes the session (theme, notifications, account menu): the sections are in the rail.\",\n \"Make the name a link home with `homeLink` (your router's link) or `href`.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"A `header` landmark; the name is plain text (the page's `h1` is the screen's, below).\",\n \"The mark is decorative beside the name: give the home link its name through the visible text.\",\n ],\n },\n related: [\"rail-nav\", \"app-shell\", \"nav-bar\", \"badge\", \"page-header\"],\n web: {\n imports: [\"AppHeader\"],\n examples: [\n { name: \"basic\", title: \"Name, chip and actions\", description: \"Orazaka's bar: the name, a « Local · Sovereign » chip, and the theme and account on the right; the mark joins the name below `md`.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
466
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"App header\",\n summary:\n \"The family's top bar beside a `RailNav`: the product's name as text, optional status chips, then the session's controls in a fixed order — search (⌘K), language, the host's actions, messages and notifications with their unread counts, the primary action, the account with its name. Every slot is the host's; an empty one draws nothing.\",\n status: \"beta\",\n category: \"navigation\",\n platforms: \"both\",\n parity: { desktop: \"web\" },\n whenToUse: [\n \"Beside a `RailNav`, as the bar of every signed-in screen of a Krizaka app (Orochia, Yomizaka, Akinaza): the same bar in every product.\",\n \"With `search` (`AppHeaderSearch`) when the app has a palette (`CommandDialog`): ⌘K opens it from anywhere.\",\n \"With `messages` and `notifications` (`AppHeaderCount`) when the app has an inbox: the unread count is drawn and said.\",\n ],\n whenNotToUse: [\n { when: \"The bar of a site, with its menus and its call to action.\", use: \"nav-bar\" },\n { when: \"A screen's title and its actions inside an AppShell.\", use: \"app-shell\" },\n { when: \"A page's title block.\", use: \"page-header\" },\n { when: \"A native screen's title that folds as the list scrolls.\", use: \"header\" },\n ],\n bestPractices: [\n \"Pass the name as text and the mark as `mark`: never put a mark in the header that the rail already carries on a desktop (`mark` is hidden from `md` up for that reason).\",\n \"Fill the slots, never `actions` with everything: the order (search · language · actions · messages · notifications · primary · account) is the family's, so every product's bar reads the same.\",\n \"`AppHeaderSearch` comes from `@krizaka/ui/app-header/search` (a client entry: its ⌘K listener); the bar itself stays server-safe.\",\n \"Give each count its words with their plural (`countLabel=\\\"3 nouvelles\\\"`): the badge is decorative, the name says it.\",\n \"The language is `LanguageSwitch` (the same control as `NavBar.Language`); the account is `AccountMenu` with `showName` (the name from `sm`).\",\n \"Chips say a state, not a place: « Local · Sovereign », « Dev », a version.\",\n \"Make the name a link home with `homeLink` (your router's link) or `href`.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"⌘ K / Ctrl K\", action: \"Opens the palette from anywhere on the page (`AppHeaderSearch`, `shortcut`).\" },\n { keys: \"Tab\", action: \"Walks the controls in their visual order: search, language, actions, messages, notifications, primary, account.\" },\n { keys: \"Enter / Space\", action: \"Opens the palette, follows a count's link or opens the host's popover.\" },\n ],\n notes: [\n \"A `header` landmark (`banner`); the name is plain text (the page's `h1` is the screen's, below).\",\n \"A count is in the control's name: « Notifications, 3 new »; the digits on the icon are hidden from screen readers. A page it leads to is `aria-current=\\\"page\\\"`.\",\n \"The search trigger is a button named by its words at every width (sr-only below `md`), with `aria-haspopup=\\\"dialog\\\"` and `aria-keyshortcuts`.\",\n \"Reduced motion: the controls' colour transitions stop; nothing else moves.\",\n \"At 360 px the name truncates and the controls keep their size: nothing is clipped.\",\n ],\n },\n related: [\"rail-nav\", \"app-shell\", \"nav-bar\", \"command\", \"language-switch\", \"account-menu\", \"badge\", \"page-header\"],\n web: {\n imports: [\"AppHeader\", \"AppHeaderCount\"],\n examples: [\n { name: \"basic\", title: \"Name, chip and actions\", description: \"Orazaka's bar: the name, a « Local · Sovereign » chip, and the account on the right; the mark joins the name below `md`.\" },\n { name: \"family\", title: \"The family's bar\", description: \"Yomizaka's bar: search with ⌘K and its palette, the language, messages and notifications with their counts, the account with its name.\" },\n ],\n },\n native: {\n imports: [\"AppHeader\", \"AppHeaderCount\", \"AppHeaderSearch\"],\n differences: [\n \"`onHome` instead of `href`/`homeLink`; no `chips` (a phone has no room).\",\n \"`AppHeaderSearch` is an icon (no ⌘K): it opens the app's search screen; `AppHeaderCount` takes `onPress`.\",\n \"The safe area's top inset is the provider's (`insets`).\",\n ],\n examples: [{ name: \"family\", title: \"The family's bar\", description: \"Search, language, notifications with their count and the account, at 360 pt.\" }],\n },\n} satisfies ComponentMeta;\n"
467
+ },
468
+ {
469
+ "path": "app-header/search/index.ts",
470
+ "content": "/** @krizaka/ui/app-header/search — `AppHeaderSearch`, the top bar's search trigger (a field from `md`, an icon below; ⌘K opens the host's palette), `isSearchShortcut`. Client: its global shortcut. */\nexport { AppHeaderSearch, appHeaderSearch, type AppHeaderSearchProps, isSearchShortcut } from \"./search\";\n"
471
+ },
472
+ {
473
+ "path": "app-header/search/search.tsx",
474
+ "content": "// Client (a global shortcut). The top bar's search trigger: a field-like button (the glyph, the label, ⌘K) from `md`, an\n// icon below. It does not search: it opens the host's palette (`CommandDialog` of @krizaka/ui/command) — on a click, on\n// Enter, and on ⌘K / Ctrl+K from anywhere on the page.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { SearchGlyph } from \"../../glyphs\";\nimport { Kbd } from \"../../kbd/kbd\";\n\nexport const appHeaderSearch = tv({\n slots: {\n root:\n \"inline-flex h-9 w-9 shrink-0 items-center justify-center gap-2 rounded-lg text-fg-secondary transition-colors \" +\n \"hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none \" +\n \"md:w-56 md:justify-start md:border md:border-border-default md:bg-surface-2/60 md:px-3 md:text-xs md:hover:border-border-strong lg:w-72\",\n glyph: \"h-5 w-5 shrink-0 md:h-4 md:w-4\",\n // Hidden on a phone, still the button's name there (sr-only), so the name is the same at every width.\n label: \"sr-only md:not-sr-only md:flex-1 md:truncate md:text-left\",\n keys: \"ml-auto hidden shrink-0 items-center gap-0.5 lg:flex\",\n },\n});\nconst s = appHeaderSearch();\n\nconst isApple = () => typeof navigator !== \"undefined\" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n\n/** Whether a keydown is the shortcut: ⌘ or Ctrl with the key, no Alt. */\nexport function isSearchShortcut(event: Pick<KeyboardEvent, \"key\" | \"metaKey\" | \"ctrlKey\" | \"altKey\">, key: string): boolean {\n return event.key.toLowerCase() === key.toLowerCase() && (event.metaKey || event.ctrlKey) && !event.altKey;\n}\n\nexport type AppHeaderSearchProps = Omit<React.ComponentProps<\"button\">, \"children\" | \"onClick\"> & {\n /** What it searches (« Search files and checks ») — passed translated: the field's words from `md`, the button's name at every width. */\n label: string;\n /** Opens the palette: a click, Enter, and the shortcut. */\n onOpen: () => void;\n /**\n * The shortcut's key with ⌘ (Apple) or Ctrl (elsewhere), global to the page; `false` for none. One per page.\n * @default \"k\"\n */\n shortcut?: string | false;\n};\n\n/** The top bar's search trigger: a field from `md`, an icon below; it opens the host's palette, also on ⌘K. */\nexport function AppHeaderSearch({ label, onOpen, shortcut = \"k\", className, type, ...props }: AppHeaderSearchProps) {\n const [apple, setApple] = React.useState(true);\n const open = React.useRef(onOpen);\n React.useEffect(() => {\n open.current = onOpen;\n }, [onOpen]);\n\n React.useEffect(() => {\n if (!shortcut) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- the platform is only known in the browser (no hydration mismatch)\n setApple(isApple());\n const onKey = (event: KeyboardEvent) => {\n if (!isSearchShortcut(event, shortcut)) return;\n event.preventDefault();\n open.current();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [shortcut]);\n\n const key = shortcut ? shortcut.toUpperCase() : null;\n return (\n <button\n type={type ?? \"button\"}\n aria-haspopup=\"dialog\"\n aria-keyshortcuts={key ? `Meta+${key} Control+${key}` : undefined}\n {...props}\n onClick={() => onOpen()}\n className={s.root({ className })}\n >\n <SearchGlyph className={s.glyph()} />\n <span className={s.label()}>{label}</span>\n {key && (\n <span aria-hidden className={s.keys()}>\n <Kbd size=\"sm\">{apple ? \"⌘\" : \"Ctrl\"}</Kbd>\n <Kbd size=\"sm\">{key}</Kbd>\n </span>\n )}\n </button>\n );\n}\n"
475
+ },
476
+ {
477
+ "path": "glyphs.tsx",
478
+ "content": "// The small line glyphs the data and media primitives draw (play, volume, search, chevrons…): decorative, 24-unit\n// box, `currentColor`, sized by the caller's class. Shared by the entries that need them; never exported on its own —\n// a product draws its own icons with @krizaka/icons.\nimport type * as React from \"react\";\n\ntype GlyphProps = { className?: string };\n\nfunction Glyph({ className = \"h-4 w-4\", fill, children }: GlyphProps & { fill?: boolean; children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n className={className}\n fill={fill ? \"currentColor\" : \"none\"}\n stroke={fill ? \"none\" : \"currentColor\"}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n focusable=\"false\"\n >\n {children}\n </svg>\n );\n}\n\nexport const PlayGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <path d=\"M8 5.5v13a1 1 0 0 0 1.5.86l10.4-6.5a1 1 0 0 0 0-1.72L9.5 4.64A1 1 0 0 0 8 5.5Z\" />\n </Glyph>\n);\nexport const PauseGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n <rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n </Glyph>\n);\nexport const VolumeGlyph = ({ muted, ...p }: GlyphProps & { muted?: boolean }) => (\n <Glyph {...p}>\n <path d=\"M11 5 6 9H3v6h3l5 4V5Z\" />\n {muted ? <path d=\"m22 9-6 6m0-6 6 6\" /> : <path d=\"M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13\" />}\n </Glyph>\n);\nexport const FullscreenGlyph = ({ exit, ...p }: GlyphProps & { exit?: boolean }) => (\n <Glyph {...p}>\n {exit ? <path d=\"M9 4v5H4m11-5v5h5M9 20v-5H4m11 5v-5h5\" /> : <path d=\"M4 9V4h5m6 0h5v5M4 15v5h5m11-5v5h-5\" />}\n </Glyph>\n);\nexport const CaptionsGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"M10 10.5a2 2 0 1 0 0 3M17 10.5a2 2 0 1 0 0 3\" />\n </Glyph>\n);\nexport const PipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <rect x=\"12\" y=\"12\" width=\"6\" height=\"4\" rx=\"1\" />\n </Glyph>\n);\nexport const SearchGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </Glyph>\n);\nexport const CloseGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </Glyph>\n);\nexport const ChevronGlyph = ({ direction = \"down\", ...p }: GlyphProps & { direction?: \"left\" | \"right\" | \"up\" | \"down\" }) => (\n <Glyph {...p}>\n <path d={{ left: \"m15 6-6 6 6 6\", right: \"m9 6 6 6-6 6\", up: \"m6 15 6-6 6 6\", down: \"m6 9 6 6 6-6\" }[direction]} />\n </Glyph>\n);\nexport const CheckGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Glyph>\n);\nexport const PlusGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 5v14M5 12h14\" />\n </Glyph>\n);\nexport const CalendarGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\" />\n <path d=\"M3 10h18M8 3v4M16 3v4\" />\n </Glyph>\n);\nexport const UploadGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 16V4m0 0L7 9m5-5 5 5\" />\n <path d=\"M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\" />\n </Glyph>\n);\nexport const FileGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z\" />\n <path d=\"M14 3v5h5\" />\n </Glyph>\n);\nexport const CameraGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 8h3l2-3h6l2 3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1Z\" />\n <circle cx=\"12\" cy=\"13.5\" r=\"3.5\" />\n </Glyph>\n);\nexport const FlipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 0 1 14-5.3M20 12a8 8 0 0 1-14 5.3\" />\n <path d=\"M18 3v4h-4M6 21v-4h4\" />\n </Glyph>\n);\nexport const RetryGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 1 0 2.3-5.7L4 8.5\" />\n <path d=\"M4 4v4.5h4.5\" />\n </Glyph>\n);\nexport const SortGlyph = ({ direction, ...p }: GlyphProps & { direction?: \"ascending\" | \"descending\" }) => (\n <Glyph {...p}>\n {direction === \"ascending\" ? <path d=\"m7 14 5-5 5 5\" /> : direction === \"descending\" ? <path d=\"m7 10 5 5 5-5\" /> : <path d=\"m8 9 4-4 4 4M8 15l4 4 4-4\" />}\n </Glyph>\n);\nexport const ArrowGlyph = ({ direction = \"right\", ...p }: GlyphProps & { direction?: \"left\" | \"right\" }) => (\n <Glyph {...p}>\n <path d={direction === \"left\" ? \"M19 12H5m0 0 6-6m-6 6 6 6\" : \"M5 12h14m0 0-6-6m6 6-6 6\"} />\n </Glyph>\n);\nexport const ExternalGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M14 4h6v6M20 4l-9 9\" />\n <path d=\"M18 14v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4\" />\n </Glyph>\n);\nexport const WarningGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 4 2.5 20h19L12 4Z\" />\n <path d=\"M12 10v4M12 17.2v.1\" />\n </Glyph>\n);\nexport const CopyGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n <path d=\"M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3\" />\n </Glyph>\n);\n"
138
479
  },
139
480
  {
140
481
  "path": "meta.ts",
141
- "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 | \"agenda\"\n | \"account-settings\"\n | \"drop-overlay\"\n | \"upload-queue\"\n | \"achievement\"\n | \"anatomy-plate\"\n | \"bird-plate\"\n | \"taped-photo\"\n | \"text-style\"\n | \"flock\"\n | \"elephant\"\n | \"footnote\"\n | \"page-index\"\n | \"chapter\"\n | \"specimen-tag\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-header\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"captcha\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"chip-row\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"comparison-table\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cover-hero\"\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 | \"docs-nav\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"engine-flow\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"genui\"\n | \"glossary\"\n | \"glossary-term\"\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 | \"kpi-explained\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"meta-row\"\n | \"waveform\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"language-switch\"\n | \"media-gallery\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"opening-hours\"\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 | \"router\"\n | \"rail-nav\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"service-card\"\n | \"settings-layout\"\n | \"account-menu\"\n | \"showcase\"\n | \"step-showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot-picker\"\n | \"slot\"\n | \"spinner\"\n | \"sticky-action\"\n | \"splash-handoff\"\n | \"split-view\"\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 | \"update-prompt\"\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 * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\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 /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\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"
482
+ "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 | \"notification-preferences\"\n | \"quota-meter\"\n | \"agenda\"\n | \"account-settings\"\n | \"drop-overlay\"\n | \"upload-queue\"\n | \"achievement\"\n | \"anatomy-plate\"\n | \"bird-plate\"\n | \"taped-photo\"\n | \"stuck\"\n | \"doodle\"\n | \"signature\"\n | \"text-style\"\n | \"flock\"\n | \"elephant\"\n | \"footnote\"\n | \"page-index\"\n | \"chapter\"\n | \"specimen-tag\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-header\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"captcha\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"chip-row\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"comparison-table\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cover-hero\"\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 | \"docs-nav\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"engine-flow\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"genui\"\n | \"glossary\"\n | \"glossary-term\"\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 | \"kpi-explained\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"meta-row\"\n | \"waveform\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"language-switch\"\n | \"media-gallery\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"opening-hours\"\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 | \"retro-tv\"\n | \"radio-group\"\n | \"router\"\n | \"rail-nav\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"service-card\"\n | \"settings-layout\"\n | \"account-menu\"\n | \"showcase\"\n | \"step-showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot-picker\"\n | \"slot\"\n | \"spinner\"\n | \"sticky-action\"\n | \"splash-handoff\"\n | \"split-view\"\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 | \"update-prompt\"\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 * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\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 /** The React Native entry alone is deprecated while the web one stays (ADR-006 §7); a deprecated component says it once, above. */\n readonly deprecated?: Deprecation;\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 /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\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"
142
483
  }
143
484
  ],
144
485
  "dependencies": [
145
486
  "tailwind-variants@^3.3.1"
146
487
  ],
147
- "registryDependencies": [],
488
+ "registryDependencies": [
489
+ "kbd"
490
+ ],
148
491
  "demo": {
149
492
  "path": "examples/app-header/basic.tsx",
150
493
  "content": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppHeader } from \"@krizaka/ui/app-header\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\n\n// The name is text; the mark shows below `md` only (a desktop's rail carries it). Chips say a state.\nexport default function AppHeaderBasic() {\n return (\n <AppHeader\n name=\"Orazaka\"\n mark={<ProductLogo id=\"orazaka\" size={22} animated={false} />}\n href=\"#home\"\n chips={<Badge tone=\"success\" dot>Local · Sovereign</Badge>}\n actions={<Button size=\"sm\" variant=\"secondary\">Account</Button>}\n />\n );\n}\n"
@@ -152,7 +495,7 @@
152
495
  "props": [
153
496
  {
154
497
  "component": "AppHeader",
155
- "description": "The app's top bar: the name next to the rail, the mark on a phone, chips, actions.",
498
+ "description": "The app's top bar: the name next to the rail, the mark on a phone, chips, then search · language · actions · messages · notifications · primary · account.",
156
499
  "props": [
157
500
  {
158
501
  "name": "name",
@@ -161,11 +504,18 @@
161
504
  "description": "The product's name, as text (`Orazaka`): the only brand element on a desktop.",
162
505
  "required": true
163
506
  },
507
+ {
508
+ "name": "account",
509
+ "type": "ReactNode",
510
+ "default": null,
511
+ "description": "The account, last: `AccountMenu` with `showName` (the name from `sm`), or the visitor's Sign in.",
512
+ "required": false
513
+ },
164
514
  {
165
515
  "name": "actions",
166
516
  "type": "ReactNode",
167
517
  "default": null,
168
- "description": "On the right: theme, notifications, the account menu.",
518
+ "description": "The host's own controls, after the language (a theme toggle). Alone, it is the whole right side, as before.",
169
519
  "required": false
170
520
  },
171
521
  {
@@ -189,12 +539,109 @@
189
539
  "description": "Makes the name (and the mark) a link home: pass your router's link as `homeLink` or give a `href`.",
190
540
  "required": false
191
541
  },
542
+ {
543
+ "name": "language",
544
+ "type": "ReactNode",
545
+ "default": null,
546
+ "description": "The language: `LanguageSwitch` (`@krizaka/ui/language-switch`) — a globe and « FR », labelled.",
547
+ "required": false
548
+ },
192
549
  {
193
550
  "name": "mark",
194
551
  "type": "ReactNode",
195
552
  "default": null,
196
553
  "description": "The product's mark (`<ProductLogo id=\"orazaka\" size={22} />`): shown below `md` only, where the rail (which carries it) is a bottom bar.",
197
554
  "required": false
555
+ },
556
+ {
557
+ "name": "messages",
558
+ "type": "ReactNode",
559
+ "default": null,
560
+ "description": "Messages: an `AppHeaderCount` (an icon button or link with its unread count). Empty: hidden.",
561
+ "required": false
562
+ },
563
+ {
564
+ "name": "notifications",
565
+ "type": "ReactNode",
566
+ "default": null,
567
+ "description": "Notifications: an `AppHeaderCount` (or the trigger of the host's popover). Empty: hidden.",
568
+ "required": false
569
+ },
570
+ {
571
+ "name": "primary",
572
+ "type": "ReactNode",
573
+ "default": null,
574
+ "description": "The one primary action of the app (« New check », #588), before the account.",
575
+ "required": false
576
+ },
577
+ {
578
+ "name": "search",
579
+ "type": "ReactNode",
580
+ "default": null,
581
+ "description": "The search trigger, first of the controls: `AppHeaderSearch` (`@krizaka/ui/app-header/search`) — a field from `md`, an icon below, ⌘K.",
582
+ "required": false
583
+ }
584
+ ]
585
+ },
586
+ {
587
+ "component": "AppHeaderCount",
588
+ "description": "An icon button or link of the top bar with an unread count, the count announced in its name.",
589
+ "props": [
590
+ {
591
+ "name": "icon",
592
+ "type": "ReactNode",
593
+ "default": null,
594
+ "description": "The icon (`<NotificationIcon />` of\n@krizaka /icons): decorative, the name is `label`.",
595
+ "required": true
596
+ },
597
+ {
598
+ "name": "label",
599
+ "type": "string",
600
+ "default": null,
601
+ "description": "What it is (« Notifications », « Messages ») — passed translated; its accessible name, with the count.",
602
+ "required": true
603
+ },
604
+ {
605
+ "name": "count",
606
+ "type": "number",
607
+ "default": null,
608
+ "description": "The unread count; 0 or absent draws no badge.",
609
+ "required": false
610
+ },
611
+ {
612
+ "name": "countLabel",
613
+ "type": "string",
614
+ "default": null,
615
+ "description": "The count in words, said after the label (« 3 new », « 3 nouvelles ») — passed translated, with its plural. Default: the number.",
616
+ "required": false
617
+ },
618
+ {
619
+ "name": "current",
620
+ "type": "boolean",
621
+ "default": null,
622
+ "description": "The page is the one it leads to (`aria-current=\"page\"`).",
623
+ "required": false
624
+ },
625
+ {
626
+ "name": "href",
627
+ "type": "string",
628
+ "default": null,
629
+ "description": "A link to the page (`/notifications`) instead of a button.",
630
+ "required": false
631
+ },
632
+ {
633
+ "name": "linkAs",
634
+ "type": "ElementType<any, keyof IntrinsicElements>",
635
+ "default": null,
636
+ "description": "Your router's link (Next's `Link`) for `href`; an `<a>` otherwise.",
637
+ "required": false
638
+ },
639
+ {
640
+ "name": "max",
641
+ "type": "number",
642
+ "default": "99",
643
+ "description": "Above it the badge shows `max+`.",
644
+ "required": false
198
645
  }
199
646
  ]
200
647
  }