@krizaka/ui 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/README.md +10 -8
  2. package/dist/action-bar.js +2 -1
  3. package/dist/action-bar.js.map +1 -1
  4. package/dist/app-shell.d.ts +81 -3
  5. package/dist/app-shell.js +163 -35
  6. package/dist/app-shell.js.map +1 -1
  7. package/dist/audio-player.js +1 -1
  8. package/dist/badge.d.ts +36 -39
  9. package/dist/badge.js +11 -2
  10. package/dist/badge.js.map +1 -1
  11. package/dist/blocks.css +56 -4
  12. package/dist/button-BmCrLCq7.d.ts +114 -0
  13. package/dist/button.d.ts +48 -48
  14. package/dist/button.js +2 -1
  15. package/dist/card.d.ts +111 -111
  16. package/dist/card.js +10 -2
  17. package/dist/card.js.map +1 -1
  18. package/dist/chip.d.ts +24 -24
  19. package/dist/chip.js +10 -2
  20. package/dist/chip.js.map +1 -1
  21. package/dist/{chunk-UOMP6VOZ.js → chunk-276PTQMQ.js} +10 -4
  22. package/dist/chunk-276PTQMQ.js.map +1 -0
  23. package/dist/{chunk-Z6FUPB3Q.js → chunk-2NB27LM7.js} +11 -3
  24. package/dist/chunk-2NB27LM7.js.map +1 -0
  25. package/dist/chunk-2VNYHG4E.js +9 -0
  26. package/dist/chunk-2VNYHG4E.js.map +1 -0
  27. package/dist/{chunk-ND3L5VHT.js → chunk-5Z7FWCV2.js} +2 -2
  28. package/dist/{chunk-YH4LMWRU.js → chunk-7UCPGXKN.js} +11 -3
  29. package/dist/chunk-7UCPGXKN.js.map +1 -0
  30. package/dist/{chunk-AZPKXP3O.js → chunk-AFY35UCB.js} +6 -1
  31. package/dist/{chunk-AZPKXP3O.js.map → chunk-AFY35UCB.js.map} +1 -1
  32. package/dist/{chunk-LCZXS6AK.js → chunk-CKSHLSDH.js} +5 -5
  33. package/dist/chunk-CKSHLSDH.js.map +1 -0
  34. package/dist/chunk-D3T54RSL.js +11 -0
  35. package/dist/chunk-D3T54RSL.js.map +1 -0
  36. package/dist/{chunk-ZKVQGXLD.js → chunk-FKBHWS4N.js} +2 -2
  37. package/dist/{chunk-BRKMQ6JV.js → chunk-FZBWEDQI.js} +1 -23
  38. package/dist/chunk-FZBWEDQI.js.map +1 -0
  39. package/dist/{chunk-HCNOOPZE.js → chunk-KVDB4GXV.js} +2 -2
  40. package/dist/{chunk-RUPNPMP3.js → chunk-OWC2LZD2.js} +11 -3
  41. package/dist/chunk-OWC2LZD2.js.map +1 -0
  42. package/dist/{chunk-FDCMBWUU.js → chunk-REPTKUJ6.js} +4 -4
  43. package/dist/chunk-REPTKUJ6.js.map +1 -0
  44. package/dist/code-block.d.ts +32 -10
  45. package/dist/code-block.js +51 -43
  46. package/dist/code-block.js.map +1 -1
  47. package/dist/combobox.d.ts +3 -3
  48. package/dist/combobox.js +4 -3
  49. package/dist/command.js +3 -2
  50. package/dist/command.js.map +1 -1
  51. package/dist/component-page.js.map +1 -1
  52. package/dist/confirm-button.d.ts +1 -1
  53. package/dist/confirm-button.js +2 -1
  54. package/dist/confirm-button.js.map +1 -1
  55. package/dist/continue-prompt.js +11 -3
  56. package/dist/continue-prompt.js.map +1 -1
  57. package/dist/data-table.js +7 -6
  58. package/dist/data-table.js.map +1 -1
  59. package/dist/date-picker.js +1 -1
  60. package/dist/dialog.js +3 -2
  61. package/dist/dialog.js.map +1 -1
  62. package/dist/file-upload.d.ts +12 -1
  63. package/dist/file-upload.js +73 -89
  64. package/dist/file-upload.js.map +1 -1
  65. package/dist/filter-bar.js +3 -2
  66. package/dist/image-cropper.js +1 -1
  67. package/dist/list.js +1 -1
  68. package/dist/map.js.map +1 -1
  69. package/dist/markdown.d.ts +38 -0
  70. package/dist/markdown.js +150 -0
  71. package/dist/markdown.js.map +1 -0
  72. package/dist/match-dialog.js +2 -1
  73. package/dist/match-dialog.js.map +1 -1
  74. package/dist/media-capture.js +1 -1
  75. package/dist/native/markdown.d.ts +24 -0
  76. package/dist/native/markdown.js +200 -0
  77. package/dist/native/markdown.js.map +1 -0
  78. package/dist/native.d.ts +1 -7
  79. package/dist/native.js +357 -95
  80. package/dist/native.js.map +1 -1
  81. package/dist/recipes.d.ts +143 -0
  82. package/dist/recipes.js +180 -0
  83. package/dist/recipes.js.map +1 -0
  84. package/dist/search-field.js +2 -2
  85. package/dist/swipe-deck.js +1 -1
  86. package/dist/tag-input.js +4 -3
  87. package/dist/tag-input.js.map +1 -1
  88. package/dist/tag-selector.js +2 -1
  89. package/dist/tag-selector.js.map +1 -1
  90. package/dist/theme.d.ts +1 -1
  91. package/dist/theme.js +2 -1
  92. package/dist/theme.js.map +1 -1
  93. package/dist/tree-view.js +1 -1
  94. package/dist/vertical-video.js +2 -1
  95. package/dist/vertical-video.js.map +1 -1
  96. package/dist/video-player.js +1 -1
  97. package/dist/video-trimmer.js +1 -1
  98. package/dist/virtual.js +2 -2
  99. package/package.json +47 -16
  100. package/registry/achievement.json +1 -1
  101. package/registry/action-bar.json +1 -1
  102. package/registry/activity-feed.json +1 -1
  103. package/registry/alert.json +1 -1
  104. package/registry/app-shell.json +135 -12
  105. package/registry/argument-card.json +1 -1
  106. package/registry/audio-player.json +2 -2
  107. package/registry/auth-layout.json +1 -1
  108. package/registry/avatar.json +1 -1
  109. package/registry/badge.json +9 -3
  110. package/registry/button.json +7 -2
  111. package/registry/card-grid.json +1 -1
  112. package/registry/card.json +8 -3
  113. package/registry/catalogue-page.json +1 -1
  114. package/registry/changelog-page.json +1 -1
  115. package/registry/chat.json +1 -1
  116. package/registry/checkbox.json +1 -1
  117. package/registry/chip-link.json +1 -1
  118. package/registry/chip.json +9 -3
  119. package/registry/choice-grid.json +1 -1
  120. package/registry/code-block.json +65 -21
  121. package/registry/combobox.json +4 -3
  122. package/registry/command.json +1 -1
  123. package/registry/component-page.json +4 -4
  124. package/registry/confirm-button.json +1 -1
  125. package/registry/contact-page.json +1 -1
  126. package/registry/continue-prompt.json +1 -1
  127. package/registry/countdown.json +1 -1
  128. package/registry/cta-panel.json +1 -1
  129. package/registry/dashboard-layout.json +1 -1
  130. package/registry/data-table.json +2 -2
  131. package/registry/date-picker.json +3 -3
  132. package/registry/decision-list.json +1 -1
  133. package/registry/description-list.json +1 -1
  134. package/registry/detail-layout.json +1 -1
  135. package/registry/dialog.json +1 -1
  136. package/registry/docs-article-page.json +1 -1
  137. package/registry/docs-hub-page.json +1 -1
  138. package/registry/docs-layout.json +1 -1
  139. package/registry/dropdown-menu.json +1 -1
  140. package/registry/empty-state.json +1 -1
  141. package/registry/error-page.json +1 -1
  142. package/registry/examples/app-shell/console.d.ts +4 -0
  143. package/registry/examples/app-shell/console.js +93 -0
  144. package/registry/examples/app-shell/console.tsx +103 -0
  145. package/registry/examples/badge/native/dot.dark.png +0 -0
  146. package/registry/examples/badge/native/dot.light.png +0 -0
  147. package/registry/examples/badge/native/scrim.dark.png +0 -0
  148. package/registry/examples/badge/native/scrim.light.png +0 -0
  149. package/registry/examples/badge/native/tones.dark.png +0 -0
  150. package/registry/examples/badge/native/tones.light.png +0 -0
  151. package/registry/examples/card/native/default.dark.png +0 -0
  152. package/registry/examples/card/native/default.light.png +0 -0
  153. package/registry/examples/card/native/elevated.light.png +0 -0
  154. package/registry/examples/chip/native/multiple.dark.png +0 -0
  155. package/registry/examples/chip/native/multiple.light.png +0 -0
  156. package/registry/examples/chip/native/removable.dark.png +0 -0
  157. package/registry/examples/chip/native/removable.light.png +0 -0
  158. package/registry/examples/chip/native/single.dark.png +0 -0
  159. package/registry/examples/chip/native/single.light.png +0 -0
  160. package/registry/examples/chip/native/toggle.dark.png +0 -0
  161. package/registry/examples/chip/native/toggle.light.png +0 -0
  162. package/registry/examples/code-block/annotated.d.ts +4 -0
  163. package/registry/examples/code-block/annotated.js +31 -0
  164. package/registry/examples/code-block/annotated.tsx +26 -0
  165. package/registry/examples/component-page/button.tsx +1 -1
  166. package/registry/examples/form/native/amount.dark.png +0 -0
  167. package/registry/examples/form/native/amount.light.png +0 -0
  168. package/registry/examples/markdown/ai-stream.d.ts +4 -0
  169. package/registry/examples/markdown/ai-stream.js +31 -0
  170. package/registry/examples/markdown/ai-stream.tsx +32 -0
  171. package/registry/examples/markdown/basic.d.ts +4 -0
  172. package/registry/examples/markdown/basic.js +26 -0
  173. package/registry/examples/markdown/basic.tsx +22 -0
  174. package/registry/examples/markdown/native/ai-stream.d.ts +4 -0
  175. package/registry/examples/markdown/native/ai-stream.dark.png +0 -0
  176. package/registry/examples/markdown/native/ai-stream.js +17 -0
  177. package/registry/examples/markdown/native/ai-stream.light.png +0 -0
  178. package/registry/examples/markdown/native/ai-stream.tsx +17 -0
  179. package/registry/examples/markdown/native/basic.d.ts +4 -0
  180. package/registry/examples/markdown/native/basic.dark.png +0 -0
  181. package/registry/examples/markdown/native/basic.js +28 -0
  182. package/registry/examples/markdown/native/basic.light.png +0 -0
  183. package/registry/examples/markdown/native/basic.tsx +27 -0
  184. package/registry/examples/recipes/tone-chip.d.ts +4 -0
  185. package/registry/examples/recipes/tone-chip.js +46 -0
  186. package/registry/examples/recipes/tone-chip.tsx +32 -0
  187. package/registry/examples/screen/native/profile.dark.png +0 -0
  188. package/registry/examples/screen/native/profile.light.png +0 -0
  189. package/registry/examples/tag-selector/native/interests.dark.png +0 -0
  190. package/registry/examples/tag-selector/native/interests.light.png +0 -0
  191. package/registry/field.json +1 -1
  192. package/registry/file-upload.json +8 -3
  193. package/registry/filter-bar.json +1 -1
  194. package/registry/form.json +2 -2
  195. package/registry/gallery-page.json +1 -1
  196. package/registry/glow-card.json +1 -1
  197. package/registry/goal-gauge.json +1 -1
  198. package/registry/how-it-works-page.json +1 -1
  199. package/registry/image-cropper.json +2 -2
  200. package/registry/index.json +76 -16
  201. package/registry/iso.json +1 -1
  202. package/registry/kanban.json +1 -1
  203. package/registry/kbd.json +1 -1
  204. package/registry/landing-page.json +1 -1
  205. package/registry/landscape.json +1 -1
  206. package/registry/legal-page.json +1 -1
  207. package/registry/link-card.json +1 -1
  208. package/registry/list.json +2 -2
  209. package/registry/loader.json +1 -1
  210. package/registry/longform-page.json +1 -1
  211. package/registry/map.json +3 -3
  212. package/registry/markdown.json +266 -0
  213. package/registry/marketing-layout.json +1 -1
  214. package/registry/marquee.json +1 -1
  215. package/registry/mascot-quote.json +1 -1
  216. package/registry/mascot-state.json +1 -1
  217. package/registry/mascots.json +1 -1
  218. package/registry/match-dialog.json +1 -1
  219. package/registry/media-capture.json +2 -2
  220. package/registry/meter.json +1 -1
  221. package/registry/nav-bar.json +1 -1
  222. package/registry/number-ticker.json +1 -1
  223. package/registry/page-header.json +1 -1
  224. package/registry/page-hero.json +1 -1
  225. package/registry/page.json +1 -1
  226. package/registry/pagination.json +1 -1
  227. package/registry/photo-grid.json +1 -1
  228. package/registry/pointer-light.json +1 -1
  229. package/registry/popover.json +1 -1
  230. package/registry/pricing-page.json +1 -1
  231. package/registry/product-page.json +1 -1
  232. package/registry/profile-card.json +1 -1
  233. package/registry/progress.json +1 -1
  234. package/registry/promise-card.json +1 -1
  235. package/registry/radio-group.json +1 -1
  236. package/registry/recipes.json +99 -0
  237. package/registry/scroll-stage.json +1 -1
  238. package/registry/search-field.json +4 -3
  239. package/registry/section-backdrop.json +1 -1
  240. package/registry/separator.json +1 -1
  241. package/registry/settings-layout.json +1 -1
  242. package/registry/skeleton.json +1 -1
  243. package/registry/skip-link.json +1 -1
  244. package/registry/slider.json +1 -1
  245. package/registry/slot.json +1 -1
  246. package/registry/spinner.json +1 -1
  247. package/registry/split-view.json +1 -1
  248. package/registry/stage-banner.json +1 -1
  249. package/registry/stat.json +1 -1
  250. package/registry/status-page.json +1 -1
  251. package/registry/step-progress.json +1 -1
  252. package/registry/stepper.json +1 -1
  253. package/registry/swipe-deck.json +2 -2
  254. package/registry/switch.json +1 -1
  255. package/registry/tabs.json +1 -1
  256. package/registry/tag-input.json +2 -2
  257. package/registry/tag-selector.json +1 -1
  258. package/registry/terminal-replay.json +1 -1
  259. package/registry/theme.json +1 -1
  260. package/registry/toast.json +1 -1
  261. package/registry/toc.json +1 -1
  262. package/registry/tooltip.json +1 -1
  263. package/registry/tree-view.json +2 -2
  264. package/registry/verified-badge.json +1 -1
  265. package/registry/vertical-video.json +2 -2
  266. package/registry/video-player.json +2 -2
  267. package/registry/video-trimmer.json +2 -2
  268. package/registry/virtual.json +2 -2
  269. package/dist/button-BMAO7dKF.d.ts +0 -114
  270. package/dist/chunk-BRKMQ6JV.js.map +0 -1
  271. package/dist/chunk-FDCMBWUU.js.map +0 -1
  272. package/dist/chunk-LCZXS6AK.js.map +0 -1
  273. package/dist/chunk-RUPNPMP3.js.map +0 -1
  274. package/dist/chunk-UOMP6VOZ.js.map +0 -1
  275. package/dist/chunk-YH4LMWRU.js.map +0 -1
  276. package/dist/chunk-Z6FUPB3Q.js.map +0 -1
  277. /package/dist/{chunk-ND3L5VHT.js.map → chunk-5Z7FWCV2.js.map} +0 -0
  278. /package/dist/{chunk-ZKVQGXLD.js.map → chunk-FKBHWS4N.js.map} +0 -0
  279. /package/dist/{chunk-HCNOOPZE.js.map → chunk-KVDB4GXV.js.map} +0 -0
@@ -122,7 +122,7 @@
122
122
  },
123
123
  {
124
124
  "path": "meta.ts",
125
- "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
125
+ "content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"activity-feed\"\n | \"action-sheet\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"map\"\n | \"markdown\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"splash-handoff\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
126
126
  }
127
127
  ],
128
128
  "dependencies": [],
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ // registry/examples/app-shell/console.tsx
4
+ import { configureKrizakaFetch, defineKeys, keepPreviousData, krizakaFetch, KrizakaQueryProvider, useMutation, useQuery, useQueryClient } from "@krizaka/query";
5
+ import { ProductLogo } from "@krizaka/ui";
6
+ import { ActionBar } from "@krizaka/ui/action-bar";
7
+ import { AppShell } from "@krizaka/ui/app-shell";
8
+ import { Card } from "@krizaka/ui/card";
9
+ import { Command, CommandDialog } from "@krizaka/ui/command";
10
+ import { DataTable, DataTableFilters } from "@krizaka/ui/data-table";
11
+ import { createKrizakaForm } from "@krizaka/ui/form";
12
+ import { PageHeader } from "@krizaka/ui/page-header";
13
+ import { useState } from "react";
14
+ import { z } from "zod";
15
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
+ var rows = Array.from({ length: 60 }, (_, i) => ({ id: `po-${1060 - i}`, creator: ["Elena Varga", "Noah Petit", "Maya Chen"][i % 3], status: ["paid", "pending", "failed"][i % 4 % 3], amount: i * 7919 % 9e4 / 100 }));
17
+ async function fakeApi(input, init) {
18
+ if (init?.method === "POST") return Response.json(rows.unshift({ id: `po-${1061 + rows.length}`, ...JSON.parse(String(init.body)), status: "pending" }));
19
+ const q = new URL(String(input), "https://api.example").searchParams;
20
+ const [by, dir] = (q.get("sort") ?? "id:descending").split(":");
21
+ const found = rows.filter((p) => !q.get("status") || q.get("status").split(",").includes(p.status)).sort((a, b) => (a[by] > b[by] ? 1 : -1) * (dir === "descending" ? -1 : 1));
22
+ const [page, size] = [Number(q.get("page")), Number(q.get("size"))];
23
+ return Response.json({ rows: found.slice((page - 1) * size, page * size), total: found.length });
24
+ }
25
+ var keys = defineKeys("payouts", { list: (query) => [query] });
26
+ var columns = [
27
+ { id: "id", header: "Payout", sortable: true, card: "title" },
28
+ { id: "creator", header: "Creator", sortable: true },
29
+ { id: "status", header: "Status", filter: "facet", options: [{ value: "paid", label: "Paid" }, { value: "pending", label: "Pending" }, { value: "failed", label: "Failed" }] },
30
+ { id: "amount", header: "Amount", sortable: true, align: "end", cell: (p) => `$${p.amount.toFixed(2)}` }
31
+ ];
32
+ var newPayout = createKrizakaForm({ schema: z.object({ creator: z.string().min(2), amount: z.number().int().min(100) }), defaultValues: { creator: "", amount: 2500 }, i18n: { t: () => void 0 } });
33
+ var icon = (d) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d }) });
34
+ var NONE = [];
35
+ function Payouts() {
36
+ const client = useQueryClient();
37
+ const [query, setQuery] = useState({ sort: null, filters: {}, page: 1, pageSize: 6 });
38
+ const list = useQuery({
39
+ queryKey: keys.list(query),
40
+ queryFn: ({ signal }) => krizakaFetch("/api/payouts", { signal, query: { sort: query.sort && `${query.sort.column}:${query.sort.direction}`, status: query.filters.status, page: query.page, size: query.pageSize } }),
41
+ placeholderData: keepPreviousData,
42
+ meta: { list: true }
43
+ });
44
+ const create = useMutation({ mutationFn: (body) => krizakaFetch("/api/payouts", { method: "POST", json: { ...body, amount: body.amount / 100 } }), onSuccess: () => client.invalidateQueries({ queryKey: ["payouts"] }) });
45
+ const form = newPayout.useForm({ onSubmit: (values) => create.mutateAsync(values) });
46
+ return /* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-[1fr_20rem]", children: [
47
+ /* @__PURE__ */ jsx(DataTable, { caption: "Payouts", rows: list.data?.rows ?? NONE, columns, getRowId: (p) => p.id, manual: { sorting: true, filtering: true, pagination: true }, rowCount: list.data?.total, defaultPageSize: 6, onStateChange: setQuery, loading: list.isPending, error: list.error?.message, toolbar: /* @__PURE__ */ jsx(DataTableFilters, {}) }),
48
+ /* @__PURE__ */ jsx(Card.Root, { className: "h-fit", children: /* @__PURE__ */ jsxs(Card.Body, { children: [
49
+ /* @__PURE__ */ jsx(Card.Title, { children: "New payout" }),
50
+ /* @__PURE__ */ jsxs(form.Form, { "aria-label": "New payout", className: "flex flex-col gap-4", children: [
51
+ /* @__PURE__ */ jsx(form.TextField, { name: "creator", label: "Creator" }),
52
+ /* @__PURE__ */ jsx(form.Money, { name: "amount", label: "Amount", currency: "USD" }),
53
+ /* @__PURE__ */ jsx(form.Submit, { children: "Create the payout" })
54
+ ] })
55
+ ] }) })
56
+ ] });
57
+ }
58
+ function AppShellConsole() {
59
+ useState(() => configureKrizakaFetch({ fetch: fakeApi }));
60
+ const [palette, setPalette] = useState(false);
61
+ return /* @__PURE__ */ jsxs(KrizakaQueryProvider, { children: [
62
+ /* @__PURE__ */ jsxs(AppShell.Root, { children: [
63
+ /* @__PURE__ */ jsxs(AppShell.Sidebar, { label: "Console", brand: /* @__PURE__ */ jsxs(Fragment, { children: [
64
+ /* @__PURE__ */ jsx(ProductLogo, { id: "krizaka", size: 24, animated: false }),
65
+ " Console"
66
+ ] }), closeLabel: "Close the menu", collapseLabels: ["Collapse", "Expand"], children: [
67
+ /* @__PURE__ */ jsx(AppShell.Group, { children: "Money" }),
68
+ /* @__PURE__ */ jsx(AppShell.Item, { href: "#payouts", icon: icon("M3 7h18v10H3zM12 12h.01"), label: "Payouts", shortcut: "g p", current: true }),
69
+ /* @__PURE__ */ jsx(AppShell.Item, { href: "#ledger", icon: icon("M5 4h14v16H5zM8 8h8M8 12h8M8 16h5"), label: "Ledger", shortcut: "g l" }),
70
+ /* @__PURE__ */ jsx(AppShell.Group, { children: "System" }),
71
+ /* @__PURE__ */ jsx(AppShell.Item, { href: "#backups", icon: icon("M4 7c0-2 16-2 16 0v10c0 2-16 2-16 0zM4 12c0 2 16 2 16 0"), label: "Backups", shortcut: "g b" })
72
+ ] }),
73
+ /* @__PURE__ */ jsxs(AppShell.Body, { children: [
74
+ /* @__PURE__ */ jsx(AppShell.Header, { title: "Finance", menuLabel: "Open the menu", children: /* @__PURE__ */ jsx(AppShell.Search, { label: "Search", onOpen: () => setPalette(true) }) }),
75
+ /* @__PURE__ */ jsxs(AppShell.Main, { children: [
76
+ /* @__PURE__ */ jsx(PageHeader, { as: "h2", title: "Payouts", description: "Every payout, newest first. Sorting, filters and pages are answered by the API.", className: "mb-6", actions: /* @__PURE__ */ jsx(ActionBar, { size: "sm", label: "Help", actions: [{ href: "#docs", label: "Docs" }, { href: "#report", label: "Report a problem" }] }) }),
77
+ /* @__PURE__ */ jsx(Payouts, {})
78
+ ] })
79
+ ] })
80
+ ] }),
81
+ /* @__PURE__ */ jsxs(CommandDialog, { open: palette, onOpenChange: setPalette, label: "Search the console", children: [
82
+ /* @__PURE__ */ jsx(Command.Input, { placeholder: "Go to a section or find a payout" }),
83
+ /* @__PURE__ */ jsx(Command.List, { label: "Results", emptyLabel: "Nothing matches.", children: /* @__PURE__ */ jsxs(Command.Group, { heading: "Go to", children: [
84
+ /* @__PURE__ */ jsx(Command.Item, { onSelect: () => setPalette(false), children: "Payouts" }),
85
+ /* @__PURE__ */ jsx(Command.Item, { onSelect: () => setPalette(false), children: "Ledger" }),
86
+ /* @__PURE__ */ jsx(Command.Item, { onSelect: () => setPalette(false), children: "Backups" })
87
+ ] }) })
88
+ ] })
89
+ ] });
90
+ }
91
+ export {
92
+ AppShellConsole as default
93
+ };
@@ -0,0 +1,103 @@
1
+ "use client";
2
+
3
+ import { configureKrizakaFetch, defineKeys, keepPreviousData, krizakaFetch, KrizakaQueryProvider, useMutation, useQuery, useQueryClient } from "@krizaka/query";
4
+ import { ProductLogo } from "@krizaka/ui";
5
+ import { ActionBar } from "@krizaka/ui/action-bar";
6
+ import { AppShell } from "@krizaka/ui/app-shell";
7
+ import { Card } from "@krizaka/ui/card";
8
+ import { Command, CommandDialog } from "@krizaka/ui/command";
9
+ import { DataTable, type DataTableColumn, DataTableFilters, type DataTableQuery } from "@krizaka/ui/data-table";
10
+ import { createKrizakaForm } from "@krizaka/ui/form";
11
+ import { PageHeader } from "@krizaka/ui/page-header";
12
+ import { useState } from "react";
13
+ import { z } from "zod";
14
+
15
+ type Payout = { id: string; creator: string; status: string; amount: number };
16
+ const rows: Payout[] = Array.from({ length: 60 }, (_, i) => ({ id: `po-${1060 - i}`, creator: ["Elena Varga", "Noah Petit", "Maya Chen"][i % 3]!, status: ["paid", "pending", "failed"][i % 4 % 3]!, amount: ((i * 7919) % 90_000) / 100 }));
17
+ // The API, faked in the page: it lists (sorted, filtered, paged) and creates.
18
+ async function fakeApi(input: RequestInfo | URL, init?: RequestInit) {
19
+ if (init?.method === "POST") return Response.json(rows.unshift({ id: `po-${1061 + rows.length}`, ...JSON.parse(String(init.body)), status: "pending" }));
20
+ const q = new URL(String(input), "https://api.example").searchParams;
21
+ const [by, dir] = (q.get("sort") ?? "id:descending").split(":") as [keyof Payout, string];
22
+ const found = rows.filter((p) => !q.get("status") || q.get("status")!.split(",").includes(p.status)).sort((a, b) => (a[by] > b[by] ? 1 : -1) * (dir === "descending" ? -1 : 1));
23
+ const [page, size] = [Number(q.get("page")), Number(q.get("size"))];
24
+ return Response.json({ rows: found.slice((page - 1) * size, page * size), total: found.length });
25
+ }
26
+ const keys = defineKeys("payouts", { list: (query: DataTableQuery) => [query] });
27
+ const columns: DataTableColumn<Payout>[] = [
28
+ { id: "id", header: "Payout", sortable: true, card: "title" },
29
+ { id: "creator", header: "Creator", sortable: true },
30
+ { id: "status", header: "Status", filter: "facet", options: [{ value: "paid", label: "Paid" }, { value: "pending", label: "Pending" }, { value: "failed", label: "Failed" }] },
31
+ { id: "amount", header: "Amount", sortable: true, align: "end", cell: (p) => `$${p.amount.toFixed(2)}` },
32
+ ];
33
+ const newPayout = createKrizakaForm({ schema: z.object({ creator: z.string().min(2), amount: z.number().int().min(100) }), defaultValues: { creator: "", amount: 2500 }, i18n: { t: () => undefined } });
34
+ const icon = (d: string) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden><path d={d} /></svg>;
35
+ const NONE: Payout[] = [];
36
+
37
+ function Payouts() {
38
+ const client = useQueryClient();
39
+ const [query, setQuery] = useState<DataTableQuery>({ sort: null, filters: {}, page: 1, pageSize: 6 });
40
+ const list = useQuery({
41
+ queryKey: keys.list(query),
42
+ queryFn: ({ signal }) => krizakaFetch("/api/payouts", { signal, query: { sort: query.sort && `${query.sort.column}:${query.sort.direction}`, status: query.filters.status as string[] | undefined, page: query.page, size: query.pageSize } }) as Promise<{ rows: Payout[]; total: number }>,
43
+ placeholderData: keepPreviousData,
44
+ meta: { list: true },
45
+ });
46
+ const create = useMutation({ mutationFn: (body: { creator: string; amount: number }) => krizakaFetch("/api/payouts", { method: "POST", json: { ...body, amount: body.amount / 100 } }), onSuccess: () => client.invalidateQueries({ queryKey: ["payouts"] }) });
47
+ const form = newPayout.useForm({ onSubmit: (values) => create.mutateAsync(values) });
48
+ return (
49
+ <div className="grid gap-6 xl:grid-cols-[1fr_20rem]">
50
+ <DataTable caption="Payouts" rows={list.data?.rows ?? NONE} columns={columns} getRowId={(p) => p.id} manual={{ sorting: true, filtering: true, pagination: true }} rowCount={list.data?.total} defaultPageSize={6} onStateChange={setQuery} loading={list.isPending} error={list.error?.message} toolbar={<DataTableFilters />} />
51
+ <Card.Root className="h-fit">
52
+ <Card.Body>
53
+ <Card.Title>New payout</Card.Title>
54
+ <form.Form aria-label="New payout" className="flex flex-col gap-4">
55
+ <form.TextField name="creator" label="Creator" />
56
+ <form.Money name="amount" label="Amount" currency="USD" />
57
+ <form.Submit>Create the payout</form.Submit>
58
+ </form.Form>
59
+ </Card.Body>
60
+ </Card.Root>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ // TEMPLATE — a console: the shell with its sections (each with a `g` shortcut), a search that opens the palette (⌘K), a page
66
+ // header with its links, a server-side table (TanStack Table behind DataTable, filters and pages sent to the API through
67
+ // @krizaka/query) and a form that creates a row and refreshes the table. On a phone the sidebar is a drawer, the table cards.
68
+ export default function AppShellConsole() {
69
+ useState(() => configureKrizakaFetch({ fetch: fakeApi as typeof fetch }));
70
+ const [palette, setPalette] = useState(false);
71
+ return (
72
+ <KrizakaQueryProvider>
73
+ <AppShell.Root>
74
+ <AppShell.Sidebar label="Console" brand={<><ProductLogo id="krizaka" size={24} animated={false} /> Console</>} closeLabel="Close the menu" collapseLabels={["Collapse", "Expand"]}>
75
+ <AppShell.Group>Money</AppShell.Group>
76
+ <AppShell.Item href="#payouts" icon={icon("M3 7h18v10H3zM12 12h.01")} label="Payouts" shortcut="g p" current />
77
+ <AppShell.Item href="#ledger" icon={icon("M5 4h14v16H5zM8 8h8M8 12h8M8 16h5")} label="Ledger" shortcut="g l" />
78
+ <AppShell.Group>System</AppShell.Group>
79
+ <AppShell.Item href="#backups" icon={icon("M4 7c0-2 16-2 16 0v10c0 2-16 2-16 0zM4 12c0 2 16 2 16 0")} label="Backups" shortcut="g b" />
80
+ </AppShell.Sidebar>
81
+ <AppShell.Body>
82
+ <AppShell.Header title="Finance" menuLabel="Open the menu">
83
+ <AppShell.Search label="Search" onOpen={() => setPalette(true)} />
84
+ </AppShell.Header>
85
+ <AppShell.Main>
86
+ <PageHeader as="h2" title="Payouts" description="Every payout, newest first. Sorting, filters and pages are answered by the API." className="mb-6" actions={<ActionBar size="sm" label="Help" actions={[{ href: "#docs", label: "Docs" }, { href: "#report", label: "Report a problem" }]} />} />
87
+ <Payouts />
88
+ </AppShell.Main>
89
+ </AppShell.Body>
90
+ </AppShell.Root>
91
+ <CommandDialog open={palette} onOpenChange={setPalette} label="Search the console">
92
+ <Command.Input placeholder="Go to a section or find a payout" />
93
+ <Command.List label="Results" emptyLabel="Nothing matches.">
94
+ <Command.Group heading="Go to">
95
+ <Command.Item onSelect={() => setPalette(false)}>Payouts</Command.Item>
96
+ <Command.Item onSelect={() => setPalette(false)}>Ledger</Command.Item>
97
+ <Command.Item onSelect={() => setPalette(false)}>Backups</Command.Item>
98
+ </Command.Group>
99
+ </Command.List>
100
+ </CommandDialog>
101
+ </KrizakaQueryProvider>
102
+ );
103
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ // registry/examples/code-block/annotated.tsx
4
+ import { CodeBlock } from "@krizaka/ui/code-block";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var code = `export async function loadVideos(signal: AbortSignal) {
7
+ const response = await fetch("/api/videos", { signal });
8
+ if (!response.ok) throw new Error("Could not load the videos");
9
+ return response.json();
10
+ }`;
11
+ function CodeBlockAnnotated() {
12
+ return /* @__PURE__ */ jsx(
13
+ CodeBlock,
14
+ {
15
+ className: "w-full max-w-xl",
16
+ code,
17
+ language: "ts",
18
+ file: "videos.ts",
19
+ copyLabel: "Copy",
20
+ copiedLabel: "Copied",
21
+ lineNumbers: true,
22
+ annotations: [
23
+ { lines: 2, kind: "focus" },
24
+ { lines: 3, kind: "added" }
25
+ ]
26
+ }
27
+ );
28
+ }
29
+ export {
30
+ CodeBlockAnnotated as default
31
+ };
@@ -0,0 +1,26 @@
1
+ import { CodeBlock } from "@krizaka/ui/code-block";
2
+
3
+ const code = `export async function loadVideos(signal: AbortSignal) {
4
+ const response = await fetch("/api/videos", { signal });
5
+ if (!response.ok) throw new Error("Could not load the videos");
6
+ return response.json();
7
+ }`;
8
+
9
+ // Lines numbered and set apart: line 2 is where to look (everything else is dimmed), line 3 is the line to add.
10
+ export default function CodeBlockAnnotated() {
11
+ return (
12
+ <CodeBlock
13
+ className="w-full max-w-xl"
14
+ code={code}
15
+ language="ts"
16
+ file="videos.ts"
17
+ copyLabel="Copy"
18
+ copiedLabel="Copied"
19
+ lineNumbers
20
+ annotations={[
21
+ { lines: 2, kind: "focus" },
22
+ { lines: 3, kind: "added" },
23
+ ]}
24
+ />
25
+ );
26
+ }
@@ -2,7 +2,7 @@ import { Button } from "@krizaka/ui/button";
2
2
  import { ComponentPage } from "@krizaka/ui/component-page";
3
3
  import { LinkCard } from "@krizaka/ui/link-card";
4
4
 
5
- // TEMPLATE — a component's page (what krizaka.com/docs/ui generates from the registry): name, summary, status and
5
+ // TEMPLATE — a component's page (what ui.krizaka.com generates from the registry): name, summary, status and
6
6
  // platforms, the live preview, the import line, its sections; related components aside (under it on a phone).
7
7
  export default function ComponentPageButton() {
8
8
  return (
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ // registry/examples/markdown/ai-stream.tsx
4
+ import { Button } from "@krizaka/ui/button";
5
+ import { Markdown } from "@krizaka/ui/markdown";
6
+ import * as React from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var answer = `Use **a queue**, not a loop: \`createQueue\` runs two files at once, retries a failure after a pause and lets you pause one file.
9
+
10
+ 1. Add each file with its \`id\`.
11
+ 2. Pause it with \`queue.pause(id)\`.
12
+ 3. Read the progress from the snapshot.
13
+
14
+ See [the pacer docs](https://www.krizaka.com/docs/ui).`;
15
+ function MarkdownAiStream() {
16
+ const [count, setCount] = React.useState(130);
17
+ const [started, setStarted] = React.useState(false);
18
+ const playing = started && count < answer.length;
19
+ React.useEffect(() => {
20
+ if (!playing) return;
21
+ const timer = setTimeout(() => setCount((n) => n + 6), 40);
22
+ return () => clearTimeout(timer);
23
+ }, [count, playing]);
24
+ return /* @__PURE__ */ jsxs("div", { className: "grid max-w-xl gap-4", children: [
25
+ /* @__PURE__ */ jsx(Markdown, { streaming: count < answer.length, children: answer.slice(0, count) }),
26
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: () => (setCount(0), setStarted(true)), className: "justify-self-start", children: "Play it" })
27
+ ] });
28
+ }
29
+ export {
30
+ MarkdownAiStream as default
31
+ };
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import { Button } from "@krizaka/ui/button";
4
+ import { Markdown } from "@krizaka/ui/markdown";
5
+ import * as React from "react";
6
+
7
+ const answer = `Use **a queue**, not a loop: \`createQueue\` runs two files at once, retries a failure after a pause and lets you pause one file.
8
+
9
+ 1. Add each file with its \`id\`.
10
+ 2. Pause it with \`queue.pause(id)\`.
11
+ 3. Read the progress from the snapshot.
12
+
13
+ See [the pacer docs](https://www.krizaka.com/docs/ui).`;
14
+
15
+ // A text that arrives in pieces: `streaming` closes the unfinished end (no stray `**`) and shows a caret. It rests
16
+ // mid-answer; the button plays it from the first letter.
17
+ export default function MarkdownAiStream() {
18
+ const [count, setCount] = React.useState(130);
19
+ const [started, setStarted] = React.useState(false);
20
+ const playing = started && count < answer.length;
21
+ React.useEffect(() => {
22
+ if (!playing) return;
23
+ const timer = setTimeout(() => setCount((n) => n + 6), 40);
24
+ return () => clearTimeout(timer);
25
+ }, [count, playing]);
26
+ return (
27
+ <div className="grid max-w-xl gap-4">
28
+ <Markdown streaming={count < answer.length}>{answer.slice(0, count)}</Markdown>
29
+ <Button size="sm" variant="secondary" onClick={() => (setCount(0), setStarted(true))} className="justify-self-start">Play it</Button>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ // registry/examples/markdown/basic.tsx
4
+ import { Markdown } from "@krizaka/ui/markdown";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var text = `## What changed in 3.9
7
+
8
+ The queue now **retries** a failed upload with a backoff, and \`pause\` keeps its place.
9
+
10
+ - Pause, resume and retry per file
11
+ - A 429 waits for its \`Retry-After\`
12
+
13
+ | Entry | Size |
14
+ | :--- | ---: |
15
+ | \`@krizaka/pacer\` | 2 kB |
16
+
17
+ \`\`\`ts title="uploads.ts"
18
+ const queue = createQueue({ retries: 3 });
19
+ \`\`\`
20
+ `;
21
+ function MarkdownBasic() {
22
+ return /* @__PURE__ */ jsx(Markdown, { className: "max-w-xl", codeLabels: { copy: "Copy", copied: "Copied" }, children: text });
23
+ }
24
+ export {
25
+ MarkdownBasic as default
26
+ };
@@ -0,0 +1,22 @@
1
+ import { Markdown } from "@krizaka/ui/markdown";
2
+
3
+ const text = `## What changed in 3.9
4
+
5
+ The queue now **retries** a failed upload with a backoff, and \`pause\` keeps its place.
6
+
7
+ - Pause, resume and retry per file
8
+ - A 429 waits for its \`Retry-After\`
9
+
10
+ | Entry | Size |
11
+ | :--- | ---: |
12
+ | \`@krizaka/pacer\` | 2 kB |
13
+
14
+ \`\`\`ts title="uploads.ts"
15
+ const queue = createQueue({ retries: 3 });
16
+ \`\`\`
17
+ `;
18
+
19
+ // A Markdown text on the tokens: headings, lists, a table, a titled code block. Raw HTML is never rendered.
20
+ export default function MarkdownBasic() {
21
+ return <Markdown className="max-w-xl" codeLabels={{ copy: "Copy", copied: "Copied" }}>{text}</Markdown>;
22
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+
3
+ // registry/examples/markdown/native/ai-stream.tsx
4
+ import { Markdown } from "@krizaka/ui/native/markdown";
5
+ import { View } from "react-native";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var arriving = `Use **a queue**, not a loop: \`createQueue\` runs two files at once, retries a failure and lets you pause one.
8
+
9
+ 1. Add each file with its \`id\`.
10
+ 2. Pause it with \`queue.pause(id)\`.
11
+ 3. Read the progress from the **snap`;
12
+ function NativeMarkdownAiStream() {
13
+ return /* @__PURE__ */ jsx(View, { style: { width: 340 }, children: /* @__PURE__ */ jsx(Markdown, { streaming: true, children: arriving }) });
14
+ }
15
+ export {
16
+ NativeMarkdownAiStream as default
17
+ };
@@ -0,0 +1,17 @@
1
+ import { Markdown } from "@krizaka/ui/native/markdown";
2
+ import { View } from "react-native";
3
+
4
+ // An AI answer cut mid-sentence: `streaming` closes what is open, so no `**` or half a link shows on the phone.
5
+ const arriving = `Use **a queue**, not a loop: \`createQueue\` runs two files at once, retries a failure and lets you pause one.
6
+
7
+ 1. Add each file with its \`id\`.
8
+ 2. Pause it with \`queue.pause(id)\`.
9
+ 3. Read the progress from the **snap`;
10
+
11
+ export default function NativeMarkdownAiStream() {
12
+ return (
13
+ <View style={{ width: 340 }}>
14
+ <Markdown streaming>{arriving}</Markdown>
15
+ </View>
16
+ );
17
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ // registry/examples/markdown/native/basic.tsx
4
+ import { Markdown } from "@krizaka/ui/native/markdown";
5
+ import { View } from "react-native";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var text = `## What changed in 3.9
8
+
9
+ The queue now **retries** a failed upload with a backoff, and \`pause\` keeps its place.
10
+
11
+ - Pause, resume and retry per file
12
+ - A 429 waits for its \`Retry-After\`
13
+
14
+ | Entry | Size |
15
+ | :--- | ---: |
16
+ | \`@krizaka/pacer\` | 2 kB |
17
+
18
+ \`\`\`ts title="uploads.ts"
19
+ const queue = createQueue({ retries: 3 });
20
+ \`\`\`
21
+ `;
22
+ function NativeMarkdownBasic() {
23
+ return /* @__PURE__ */ jsx(View, { style: { width: 340 }, children: /* @__PURE__ */ jsx(Markdown, { onLinkPress: () => {
24
+ }, children: text }) });
25
+ }
26
+ export {
27
+ NativeMarkdownBasic as default
28
+ };