@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
@@ -0,0 +1,150 @@
1
+ // src/markdown/markdown.tsx
2
+ import { CodeBlock } from "@krizaka/ui/code-block";
3
+
4
+ // src/cn.ts
5
+ import { clsx } from "clsx";
6
+ import { cnMerge } from "tailwind-variants";
7
+ var twMergeConfig = {
8
+ extend: {
9
+ classGroups: {
10
+ "bg-image": ["bg-sheen", "bg-fill-shade", "bg-halo"],
11
+ shadow: ["shadow-raised", "shadow-floating", "shadow-overlay", "shadow-glow", "shadow-glow-lg"],
12
+ "inset-shadow": ["inset-shadow-edge", "inset-shadow-edge-fill", "inset-shadow-well"]
13
+ }
14
+ }
15
+ };
16
+ function cn(...inputs) {
17
+ return cnMerge(clsx(inputs))({ twMergeConfig }) ?? "";
18
+ }
19
+
20
+ // src/markdown/parse.ts
21
+ import { streamingMarkdownExtension } from "@tanstack/markdown/extensions/streaming";
22
+ import { parseMarkdown } from "@tanstack/markdown/parser";
23
+ var FENCE = /^ {0,3}(```|~~~)/gm;
24
+ function healLink(text) {
25
+ const open = text.lastIndexOf("[");
26
+ if (open < 0) return text;
27
+ const rest = text.slice(open + 1);
28
+ if (rest.includes("\n")) return text;
29
+ const image = open > 0 && text[open - 1] === "!";
30
+ const close = rest.indexOf("]");
31
+ const after = close < 0 ? "" : rest.slice(close + 1);
32
+ const cut = close < 0 || after === "" || after.startsWith("(") && !after.includes(")");
33
+ if (!cut) return text;
34
+ const head = text.slice(0, image ? open - 1 : open);
35
+ return image ? head : head + (close < 0 ? rest : rest.slice(0, close));
36
+ }
37
+ function healStreaming(source) {
38
+ if (((source.match(FENCE) ?? []).length & 1) === 1) return source;
39
+ let out = healLink(source);
40
+ const cut = out.lastIndexOf("\n\n");
41
+ const block = cut < 0 ? out : out.slice(cut + 2);
42
+ const open = (marker) => (block.split(marker).length - 1) % 2 === 1;
43
+ const close = (marker) => out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`;
44
+ if (open("`")) return close("`");
45
+ for (const marker of ["**", "~~"]) if (open(marker)) out = close(marker);
46
+ return out;
47
+ }
48
+ function parseSource(source, { streaming = false, headingIds = false } = {}) {
49
+ return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...streaming && { extensions: [streamingMarkdownExtension()] } }).children;
50
+ }
51
+
52
+ // src/markdown/markdown.tsx
53
+ import { jsx, jsxs } from "react/jsx-runtime";
54
+ var external = (href) => /^[a-z][a-z0-9+.-]*:/i.test(href);
55
+ function Inlines({ nodes, ctx }) {
56
+ return nodes.map((node, i) => /* @__PURE__ */ jsx(Inline, { node, ctx }, i));
57
+ }
58
+ function Inline({ node, ctx }) {
59
+ switch (node.type) {
60
+ case "text":
61
+ return node.value;
62
+ case "inlineCode":
63
+ return /* @__PURE__ */ jsx("code", { children: node.value });
64
+ case "strong":
65
+ return /* @__PURE__ */ jsx("strong", { children: /* @__PURE__ */ jsx(Inlines, { nodes: node.children, ctx }) });
66
+ case "emphasis":
67
+ return /* @__PURE__ */ jsx("em", { children: /* @__PURE__ */ jsx(Inlines, { nodes: node.children, ctx }) });
68
+ case "strike":
69
+ return /* @__PURE__ */ jsx("del", { children: /* @__PURE__ */ jsx(Inlines, { nodes: node.children, ctx }) });
70
+ case "break":
71
+ return /* @__PURE__ */ jsx("br", {});
72
+ case "link": {
73
+ const Link = external(node.href) ? "a" : ctx.linkAs ?? "a";
74
+ const props = external(node.href) && !node.href.startsWith("mailto:") && !node.href.startsWith("tel:") ? { rel: "noopener noreferrer" } : {};
75
+ return /* @__PURE__ */ jsx(Link, { href: node.href, title: node.title, ...props, children: /* @__PURE__ */ jsx(Inlines, { nodes: node.children, ctx }) });
76
+ }
77
+ case "image":
78
+ return /* @__PURE__ */ jsx("img", { src: node.src, alt: node.alt, title: node.title, loading: "lazy" });
79
+ case "footnoteReference":
80
+ return /* @__PURE__ */ jsx("sup", { children: /* @__PURE__ */ jsx("a", { id: `kz-fnref-${node.id}`, href: `#kz-fn-${node.id}`, "data-footnote-ref": "", children: node.number }) });
81
+ // Raw HTML is never enabled by the parser: it arrives as text. A node of this kind is shown as text too.
82
+ case "inlineHtml":
83
+ return node.value;
84
+ default:
85
+ return null;
86
+ }
87
+ }
88
+ function Blocks({ nodes, ctx }) {
89
+ return nodes.map((node, i) => /* @__PURE__ */ jsx(Block, { node, ctx }, i));
90
+ }
91
+ function Block({ node, ctx }) {
92
+ switch (node.type) {
93
+ case "heading": {
94
+ const Tag = `h${node.depth}`;
95
+ return /* @__PURE__ */ jsx(Tag, { id: node.id, children: /* @__PURE__ */ jsx(Inlines, { nodes: node.children, ctx }) });
96
+ }
97
+ case "paragraph":
98
+ return /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(Inlines, { nodes: node.children, ctx }) });
99
+ case "code":
100
+ return /* @__PURE__ */ jsx(
101
+ CodeBlock,
102
+ {
103
+ code: node.value,
104
+ language: node.lang ?? "text",
105
+ file: node.title ?? node.file,
106
+ copyLabel: ctx.codeLabels?.copy,
107
+ copiedLabel: ctx.codeLabels?.copied,
108
+ annotations: node.highlightLines?.map((line) => ({ lines: line, kind: "highlight" }))
109
+ }
110
+ );
111
+ case "list": {
112
+ const Tag = node.ordered ? "ol" : "ul";
113
+ const tasks = node.items.some((item) => item.checked !== void 0);
114
+ return /* @__PURE__ */ jsx(Tag, { start: node.ordered && node.start !== void 0 && node.start !== 1 ? node.start : void 0, "data-tasks": tasks ? "" : void 0, children: node.items.map((item, i) => /* @__PURE__ */ jsx("li", { children: item.checked !== void 0 ? /* @__PURE__ */ jsxs("label", { children: [
115
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked: item.checked, disabled: true, readOnly: true }),
116
+ " ",
117
+ /* @__PURE__ */ jsx(ListItem, { blocks: item.children, ctx })
118
+ ] }) : /* @__PURE__ */ jsx(ListItem, { blocks: item.children, ctx }) }, i)) });
119
+ }
120
+ case "blockquote":
121
+ return /* @__PURE__ */ jsx("blockquote", { children: /* @__PURE__ */ jsx(Blocks, { nodes: node.children, ctx }) });
122
+ case "table":
123
+ return /* @__PURE__ */ jsx("div", { className: "kz-md-table", children: /* @__PURE__ */ jsxs("table", { children: [
124
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: node.header.map((cell, i) => /* @__PURE__ */ jsx("th", { scope: "col", style: { textAlign: node.align[i] ?? void 0 }, children: /* @__PURE__ */ jsx(Inlines, { nodes: cell.children, ctx }) }, i)) }) }),
125
+ /* @__PURE__ */ jsx("tbody", { children: node.rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: row.map((cell, i) => /* @__PURE__ */ jsx("td", { style: { textAlign: node.align[i] ?? void 0 }, children: /* @__PURE__ */ jsx(Inlines, { nodes: cell.children, ctx }) }, i)) }, r)) })
126
+ ] }) });
127
+ case "thematicBreak":
128
+ return /* @__PURE__ */ jsx("hr", {});
129
+ case "footnotes":
130
+ return /* @__PURE__ */ jsx("section", { "data-footnotes": "", children: /* @__PURE__ */ jsx("ol", { children: node.items.map((item) => /* @__PURE__ */ jsx("li", { id: `kz-fn-${item.id}`, children: /* @__PURE__ */ jsx(Blocks, { nodes: item.children, ctx }) }, item.id)) }) });
131
+ // Raw HTML (never enabled) arrives as a paragraph of text; extensions are not enabled either. Shown as text.
132
+ case "html":
133
+ return /* @__PURE__ */ jsx("p", { children: node.value });
134
+ default:
135
+ return null;
136
+ }
137
+ }
138
+ function ListItem({ blocks, ctx }) {
139
+ return blocks.map((block, i) => block.type === "paragraph" && blocks.length === 1 ? /* @__PURE__ */ jsx(Inlines, { nodes: block.children, ctx }, i) : /* @__PURE__ */ jsx(Block, { node: block, ctx }, i));
140
+ }
141
+ function Markdown({ children, streaming, headingIds, codeLabels, linkAs, className, ...props }) {
142
+ const blocks = parseSource(children, { streaming, headingIds });
143
+ return /* @__PURE__ */ jsx("div", { className: cn("kz-md", className), "data-streaming": streaming ? "" : void 0, "aria-busy": streaming || void 0, ...props, children: /* @__PURE__ */ jsx(Blocks, { nodes: blocks, ctx: { codeLabels, linkAs } }) });
144
+ }
145
+ export {
146
+ Markdown,
147
+ healStreaming,
148
+ parseSource
149
+ };
150
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markdown/markdown.tsx","../src/cn.ts","../src/markdown/parse.ts"],"sourcesContent":["// Server-safe (no hook, no context): a Markdown text rendered on the tokens, from TanStack Markdown's AST. Raw HTML is\n// never enabled (it is shown as text), executable URLs are dropped, links to another site carry `rel=\"noopener\n// noreferrer\"`, and every fenced block is a `CodeBlock` (the same grammars, the same colours). `streaming` is for a\n// text that arrives in pieces — an AI answer: the unfinished end is closed and a caret marks where it goes on.\nimport { CodeBlock } from \"@krizaka/ui/code-block\";\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { type BlockNode, type InlineNode, parseSource, textOf } from \"./parse\";\n\nexport type MarkdownLinkProps = React.ComponentProps<\"a\">;\n\nexport type MarkdownProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The Markdown source. */\n children: string;\n /** The text is still arriving: the unfinished end is closed and a caret marks the end. */\n streaming?: boolean;\n /** Gives every heading an `id`, for `#section` links. */\n headingIds?: boolean;\n /** The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button. */\n codeLabels?: { copy: string; copied: string };\n /** Your router's link (Next's `Link`) for the links that stay on the site (`/docs`, `#section`); the others are plain `a`. */\n linkAs?: React.ElementType<MarkdownLinkProps>;\n};\n\ntype Context = Pick<MarkdownProps, \"codeLabels\" | \"linkAs\">;\n\nconst external = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nfunction Inlines({ nodes, ctx }: { nodes: readonly InlineNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Inline key={i} node={node} ctx={ctx} />);\n}\n\nfunction Inline({ node, ctx }: { node: InlineNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"text\":\n return node.value;\n case \"inlineCode\":\n return <code>{node.value}</code>;\n case \"strong\":\n return (\n <strong>\n <Inlines nodes={node.children} ctx={ctx} />\n </strong>\n );\n case \"emphasis\":\n return (\n <em>\n <Inlines nodes={node.children} ctx={ctx} />\n </em>\n );\n case \"strike\":\n return (\n <del>\n <Inlines nodes={node.children} ctx={ctx} />\n </del>\n );\n case \"break\":\n return <br />;\n case \"link\": {\n const Link = external(node.href) ? \"a\" : (ctx.linkAs ?? \"a\");\n const props = external(node.href) && !node.href.startsWith(\"mailto:\") && !node.href.startsWith(\"tel:\") ? { rel: \"noopener noreferrer\" } : {};\n return (\n <Link href={node.href} title={node.title} {...props}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Link>\n );\n }\n case \"image\":\n return <img src={node.src} alt={node.alt} title={node.title} loading=\"lazy\" />;\n case \"footnoteReference\":\n return (\n <sup>\n <a id={`kz-fnref-${node.id}`} href={`#kz-fn-${node.id}`} data-footnote-ref=\"\">\n {node.number}\n </a>\n </sup>\n );\n // Raw HTML is never enabled by the parser: it arrives as text. A node of this kind is shown as text too.\n case \"inlineHtml\":\n return node.value;\n default:\n return null;\n }\n}\n\nfunction Blocks({ nodes, ctx }: { nodes: readonly BlockNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Block key={i} node={node} ctx={ctx} />);\n}\n\nfunction Block({ node, ctx }: { node: BlockNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"heading\": {\n const Tag = `h${node.depth}` as const;\n return (\n <Tag id={node.id}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Tag>\n );\n }\n case \"paragraph\":\n return (\n <p>\n <Inlines nodes={node.children} ctx={ctx} />\n </p>\n );\n case \"code\":\n return (\n <CodeBlock\n code={node.value}\n language={node.lang ?? \"text\"}\n file={node.title ?? node.file}\n copyLabel={ctx.codeLabels?.copy}\n copiedLabel={ctx.codeLabels?.copied}\n annotations={node.highlightLines?.map((line) => ({ lines: line, kind: \"highlight\" as const }))}\n />\n );\n case \"list\": {\n const Tag = node.ordered ? \"ol\" : \"ul\";\n const tasks = node.items.some((item) => item.checked !== undefined);\n return (\n <Tag start={node.ordered && node.start !== undefined && node.start !== 1 ? node.start : undefined} data-tasks={tasks ? \"\" : undefined}>\n {node.items.map((item, i) => (\n <li key={i}>\n {item.checked !== undefined ? (\n <label>\n <input type=\"checkbox\" checked={item.checked} disabled readOnly />{\" \"}\n <ListItem blocks={item.children} ctx={ctx} />\n </label>\n ) : (\n <ListItem blocks={item.children} ctx={ctx} />\n )}\n </li>\n ))}\n </Tag>\n );\n }\n case \"blockquote\":\n return (\n <blockquote>\n <Blocks nodes={node.children} ctx={ctx} />\n </blockquote>\n );\n case \"table\":\n return (\n <div className=\"kz-md-table\">\n <table>\n <thead>\n <tr>\n {node.header.map((cell, i) => (\n <th key={i} scope=\"col\" style={{ textAlign: node.align[i] ?? undefined }}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {node.rows.map((row, r) => (\n <tr key={r}>\n {row.map((cell, i) => (\n <td key={i} style={{ textAlign: node.align[i] ?? undefined }}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"thematicBreak\":\n return <hr />;\n case \"footnotes\":\n return (\n <section data-footnotes=\"\">\n <ol>\n {node.items.map((item) => (\n <li key={item.id} id={`kz-fn-${item.id}`}>\n <Blocks nodes={item.children} ctx={ctx} />\n </li>\n ))}\n </ol>\n </section>\n );\n // Raw HTML (never enabled) arrives as a paragraph of text; extensions are not enabled either. Shown as text.\n case \"html\":\n return <p>{node.value}</p>;\n default:\n return null;\n }\n}\n\n/** A list item: a lone paragraph is inline (a tight list), anything else keeps its blocks. */\nfunction ListItem({ blocks, ctx }: { blocks: readonly BlockNode[]; ctx: Context }) {\n return blocks.map((block, i) => (block.type === \"paragraph\" && blocks.length === 1 ? <Inlines key={i} nodes={block.children} ctx={ctx} /> : <Block key={i} node={block} ctx={ctx} />));\n}\n\n/** A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes. */\nexport function Markdown({ children, streaming, headingIds, codeLabels, linkAs, className, ...props }: MarkdownProps) {\n const blocks = parseSource(children, { streaming, headingIds });\n return (\n <div className={cn(\"kz-md\", className)} data-streaming={streaming ? \"\" : undefined} aria-busy={streaming || undefined} {...props}>\n <Blocks nodes={blocks} ctx={{ codeLabels, linkAs }} />\n </div>\n );\n}\n\nexport { textOf };\n","import { type ClassValue, clsx } from \"clsx\";\nimport { cnMerge, type TWMergeConfig } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * The preset's own utilities, told to the merge engine: the material (BRAND.md §6: `bg-sheen`, `bg-fill-shade`,\n * `bg-halo` are background images; `shadow-raised` · `-floating` · `-overlay` · `-glow` are shadows; `inset-shadow-edge`\n * · `-edge-fill` · `-well` are inset shadows). Unknown, tailwind-merge would take `bg-sheen` for a colour and drop the\n * `bg-surface-2` it lies on.\n */\nexport const twMergeConfig: TWMergeConfig = {\n extend: {\n classGroups: {\n \"bg-image\": [\"bg-sheen\", \"bg-fill-shade\", \"bg-halo\"],\n shadow: [\"shadow-raised\", \"shadow-floating\", \"shadow-overlay\", \"shadow-glow\", \"shadow-glow-lg\"],\n \"inset-shadow\": [\"inset-shadow-edge\", \"inset-shadow-edge-fill\", \"inset-shadow-well\"],\n },\n },\n};\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return cnMerge(clsx(inputs))({ twMergeConfig }) ?? \"\";\n}\n","// The shared half of Markdown, web and React Native: the source becomes TanStack Markdown's serializable AST\n// (`@tanstack/markdown/parser`) — raw HTML is never enabled (it stays text), executable URLs are dropped (`javascript:`,\n// `data:`), and a text that is still arriving is healed so the half-written end reads like the finished one.\nimport type { BlockNode } from \"@tanstack/markdown\";\nimport { streamingMarkdownExtension } from \"@tanstack/markdown/extensions/streaming\";\nimport { parseMarkdown } from \"@tanstack/markdown/parser\";\n\nexport type { BlockNode, InlineNode, ListItemNode, TableNode } from \"@tanstack/markdown\";\n\nexport type ParseOptions = {\n /** The text is still arriving (an AI answer): the unfinished end is closed, so it never shows `**` or a half link. */\n streaming?: boolean;\n /** Gives every heading an `id` (`#section` links, a table of contents). */\n headingIds?: boolean;\n};\n\nconst FENCE = /^ {0,3}(```|~~~)/gm;\n\n/** A link or image cut at its end: `[label`, `[label]`, `[label](http…` become `label`, an image is dropped. Linear: one scan of the last `[`. */\nfunction healLink(text: string): string {\n const open = text.lastIndexOf(\"[\");\n if (open < 0) return text;\n const rest = text.slice(open + 1);\n if (rest.includes(\"\\n\")) return text;\n const image = open > 0 && text[open - 1] === \"!\";\n const close = rest.indexOf(\"]\");\n const after = close < 0 ? \"\" : rest.slice(close + 1);\n const cut = close < 0 || after === \"\" || (after.startsWith(\"(\") && !after.includes(\")\"));\n if (!cut) return text;\n const head = text.slice(0, image ? open - 1 : open);\n return image ? head : head + (close < 0 ? rest : rest.slice(0, close));\n}\n\n/**\n * Closes what a text cut in the middle leaves open: an emphasis (`**`, `~~`) or a code span, a link or image whose\n * address has not arrived. Inside an open code fence nothing is healed — it is shown as code as it comes.\n */\nexport function healStreaming(source: string): string {\n if (((source.match(FENCE) ?? []).length & 1) === 1) return source;\n let out = healLink(source);\n const cut = out.lastIndexOf(\"\\n\\n\");\n const block = cut < 0 ? out : out.slice(cut + 2);\n const open = (marker: string) => (block.split(marker).length - 1) % 2 === 1;\n // A marker just opened, with nothing after it yet, is dropped rather than shown as an empty emphasis.\n const close = (marker: string) => (out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`);\n if (open(\"`\")) return close(\"`\");\n for (const marker of [\"**\", \"~~\"]) if (open(marker)) out = close(marker);\n return out;\n}\n\n/** The blocks of a Markdown source. */\nexport function parseSource(source: string, { streaming = false, headingIds = false }: ParseOptions = {}): BlockNode[] {\n return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...(streaming && { extensions: [streamingMarkdownExtension()] }) }).children;\n}\n\n/** The text of inline nodes (a heading's label, an image's alt). */\nexport function textOf(nodes: readonly { type: string; value?: string; children?: readonly unknown[] }[]): string {\n return nodes\n .map((node) => (node.children ? textOf(node.children as typeof nodes) : (node.value ?? \"\")))\n .join(\"\");\n}\n"],"mappings":"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","names":[]}
@@ -7,7 +7,8 @@ import "./chunk-JXLDUDTD.js";
7
7
  import {
8
8
  Button,
9
9
  IconButton
10
- } from "./chunk-UOMP6VOZ.js";
10
+ } from "./chunk-276PTQMQ.js";
11
+ import "./chunk-D3T54RSL.js";
11
12
 
12
13
  // src/dialog/dialog.tsx
13
14
  import { Dialog as DialogPrimitive } from "radix-ui";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dialog/dialog.tsx","../src/dialog/return-focus.ts","../src/match-dialog/match-dialog.tsx"],"sourcesContent":["// Client (a portal). Radix does the focus trap, the scroll lock, Escape, the outside click, aria-modal, the\n// aria-labelledby / aria-describedby wiring and the focus return: this file only holds the shape.\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { IconButton } from \"../button/button\";\nimport { useReturnFocus } from \"./return-focus\";\n\nexport const dialog = tv({\n slots: {\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay backdrop-blur-xs\",\n content:\n \"kz-dialog fixed z-50 flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg shadow-lg focus:outline-hidden\",\n close: \"absolute right-3 top-3 h-9 w-9\",\n header: \"flex flex-col gap-1 px-5 pb-2 pr-14 pt-5\",\n title: \"text-base font-bold text-fg\",\n description: \"text-sm text-fg-secondary\",\n body: \"min-h-0 flex-1 overflow-y-auto px-5 pb-5\",\n footer:\n \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]\",\n },\n variants: {\n placement: {\n center: { content: \"left-1/2 top-1/2 max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl\" },\n /* A sheet: anchored at the bottom on a phone, centred from `sm` up. */\n bottom: {\n content:\n \"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[1.75rem] sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:w-full \" +\n \"sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-[1.75rem]\",\n },\n right: { content: \"inset-y-0 right-0 h-full w-full max-w-md rounded-none border-y-0 border-r-0\" },\n /* A navigation drawer: full height, from the left, clear of the notch. */\n left: { content: \"inset-y-0 left-0 h-full w-full max-w-md rounded-none border-y-0 border-l-0 pl-[env(safe-area-inset-left)]\" },\n },\n size: { sm: { content: \"sm:max-w-sm\" }, md: { content: \"sm:max-w-md\" }, lg: { content: \"sm:max-w-2xl\" } },\n },\n defaultVariants: { placement: \"center\", size: \"md\" },\n});\nconst s = dialog();\n\nexport type DialogVariants = VariantProps<typeof dialog>;\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype CloseButtonProps =\n | {\n /** The accessible name of the close button — passed translated. */\n closeLabel: string;\n /** Leaves the close button out (the content brings its own way out, or none: see `dismissible`). */\n hideClose?: false;\n }\n | { closeLabel?: string; hideClose: true };\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &\n DialogVariants &\n CloseButtonProps & {\n /**\n * center · bottom (a sheet on a phone, centred from `sm` up) · right (a side panel, full height) · left (a\n * navigation drawer, full height).\n * @default \"center\"\n */\n placement?: DialogVariants[\"placement\"];\n /**\n * sm · md · lg: the width of the dialog.\n * @default \"md\"\n */\n size?: DialogVariants[\"size\"];\n /**\n * `false`: Escape and a click outside do not close it (a gate the user must answer: age, terms). It still closes\n * through `open` / `onOpenChange` and any `Dialog.Close` inside. Default `true`.\n */\n dismissible?: boolean;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n };\n\nconst prevent = (event: Event) => event.preventDefault();\n\n/** Radix's content, mounted only while open: it remembers the opener then and gives focus back on close (WCAG 2.4.3). */\nfunction ReturnFocusContent({ onCloseAutoFocus, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {\n return <DialogPrimitive.Content onCloseAutoFocus={useReturnFocus(onCloseAutoFocus)} {...props} />;\n}\n\n/** The dialog itself, portalled over a dimmed overlay, with a close button. Give it a `Dialog.Title`. */\nexport function DialogContent({\n placement,\n size,\n closeLabel,\n hideClose,\n dismissible = true,\n container,\n className,\n children,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n ...props\n}: DialogContentProps) {\n const v = dialog({ placement, size });\n return (\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay className={v.overlay()} />\n <ReturnFocusContent\n data-placement={placement ?? \"center\"}\n data-dismissible={dismissible ? undefined : \"false\"}\n className={v.content({ className })}\n onEscapeKeyDown={dismissible ? onEscapeKeyDown : chain(onEscapeKeyDown, prevent)}\n onPointerDownOutside={dismissible ? onPointerDownOutside : chain(onPointerDownOutside, prevent)}\n onInteractOutside={dismissible ? onInteractOutside : chain(onInteractOutside, prevent)}\n {...props}\n >\n {children}\n {!hideClose && (\n <DialogPrimitive.Close asChild>\n <IconButton label={closeLabel as string} variant=\"ghost\" shape=\"pill\" className={v.close()}>\n <CloseIcon />\n </IconButton>\n </DialogPrimitive.Close>\n )}\n </ReturnFocusContent>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction chain<E extends Event>(handler: ((event: E) => void) | undefined, then: (event: E) => void) {\n return (event: E) => {\n handler?.(event);\n then(event);\n };\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.header({ className })} {...props} />;\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={s.title({ className })} {...props} />;\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={s.description({ className })} {...props} />;\n}\n\nexport function DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.footer({ className })} {...props} />;\n}\n\n/** `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const Dialog = {\n Root: DialogPrimitive.Root,\n Trigger: DialogPrimitive.Trigger,\n Close: DialogPrimitive.Close,\n Content: DialogContent,\n Header: DialogHeader,\n Title: DialogTitle,\n Description: DialogDescription,\n Body: DialogBody,\n Footer: DialogFooter,\n};\n\n/** `Omit` that keeps the close-button union (`closeLabel` required unless `hideClose`). */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type SheetProps = DistributiveOmit<DialogContentProps, \"placement\">;\n\n/** A sheet is a dialog anchored at the bottom (centred from `sm` up): `Dialog.Content placement=\"bottom\"`. */\nexport function Sheet(props: SheetProps) {\n return <DialogContent placement=\"bottom\" {...props} />;\n}\n","// Client. WCAG 2.4.3: when a dialog closes, focus goes back to what opened it. Radix only does it for `Dialog.Trigger`;\n// most app dialogs are controlled (`open` / `onOpenChange`) and opened by a plain button, so on Escape focus fell to\n// <body>. The content remembers the focused element when it mounts (it is still the opener: Radix moves focus in an\n// effect) and gives it back on close, unless the consumer's `onCloseAutoFocus` prevented the default.\nimport * as React from \"react\";\n\n/** The handler for `onCloseAutoFocus`: yours first, then focus back on the opener if it is still on the page. */\nexport function useReturnFocus(onCloseAutoFocus?: (event: Event) => void) {\n const [opener] = React.useState<Element | null>(() => (typeof document === \"undefined\" ? null : document.activeElement));\n return React.useCallback(\n (event: Event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n if (opener instanceof HTMLElement && opener !== document.body && opener.isConnected) {\n event.preventDefault();\n opener.focus();\n }\n },\n [opener, onCloseAutoFocus],\n );\n}\n","// Client (a portal). The moment two people choose each other — a match, a pair, a deal: a dialog in the brand\n// gradient, the two avatars meeting at a heart, the next step. The entrance plays once; still under reduced motion.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport { Button } from \"../button\";\nimport { Dialog } from \"../dialog\";\n\nexport type MatchDialogProps = {\n /** Shown (controlled). */\n open: boolean;\n /** Called when it closes. */\n onOpenChange: (open: boolean) => void;\n /** The headline (“It's a match!”) — passed translated; the dialog's name. */\n title: string;\n /** A line under it. */\n description?: string;\n /** The two people: avatar URLs and names. */\n people: readonly [{ src?: string | null; name: string }, { src?: string | null; name: string }];\n /** The main action (“Send a message”). */\n primaryLabel: string;\n /** What it does. */\n onPrimary: () => void;\n /** The way back (“Keep swiping”). */\n secondaryLabel: string;\n /** Where it is drawn (the page's body by default): give an element inside a brand scope (`.brand-…`) so the dialog wears the brand too. */\n container?: HTMLElement | null;\n};\n\n/** Two people choose each other: the avatars meet at a heart in the brand gradient, the next step under them. */\nexport function MatchDialog({ open, onOpenChange, title, description, people, primaryLabel, onPrimary, secondaryLabel, container }: MatchDialogProps) {\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Content hideClose size=\"sm\" container={container} className=\"overflow-hidden border-0 bg-gradient-to-br from-accent to-accent-2 text-center text-on-accent\">\n <style href=\"krizaka-match\" precedence=\"default\">\n {\"@media (prefers-reduced-motion:no-preference){[data-kz-match] [data-side=a]{animation:kz-match-a .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-side=b]{animation:kz-match-b .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-heart]{animation:kz-match-heart .45s .25s cubic-bezier(.3,1.6,.5,1) both}}@keyframes kz-match-a{from{transform:translateX(-60px) rotate(-8deg);opacity:0}}@keyframes kz-match-b{from{transform:translateX(60px) rotate(8deg);opacity:0}}@keyframes kz-match-heart{from{transform:scale(0)}}\"}\n </style>\n <div data-kz-match=\"\" className=\"flex flex-col items-center gap-3 px-2 pb-2 pt-6\">\n <Dialog.Title className=\"font-display text-4xl font-black text-on-accent\">{title}</Dialog.Title>\n {description ? <Dialog.Description className=\"max-w-xs text-sm text-on-accent\">{description}</Dialog.Description> : null}\n <div className=\"relative my-6 flex h-36 w-64 items-center justify-center\" aria-hidden>\n <span data-side=\"a\" className=\"absolute left-6 -rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[0].src ?? null} alt=\"\" fallback={people[0].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-side=\"b\" className=\"absolute right-6 rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[1].src ?? null} alt=\"\" fallback={people[1].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-heart=\"\" className=\"absolute -bottom-1 flex h-12 w-12 items-center justify-center rounded-full bg-on-accent text-accent shadow-lg\">\n <svg viewBox=\"0 0 24 24\" className=\"h-6 w-6\" fill=\"currentColor\">\n <path d=\"M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z\" />\n </svg>\n </span>\n </div>\n <div className=\"flex w-full flex-col gap-2\">\n <Button\n size=\"lg\"\n onClick={() => {\n onOpenChange(false);\n onPrimary();\n }}\n >\n {primaryLabel}\n </Button>\n <Dialog.Close asChild>\n <Button size=\"lg\" variant=\"ghost\" className=\"text-on-accent hover:bg-on-accent/15\">\n {secondaryLabel}\n </Button>\n </Dialog.Close>\n </div>\n </div>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/dialog/dialog.tsx","../src/dialog/return-focus.ts","../src/match-dialog/match-dialog.tsx"],"sourcesContent":["// Client (a portal). Radix does the focus trap, the scroll lock, Escape, the outside click, aria-modal, the\n// aria-labelledby / aria-describedby wiring and the focus return: this file only holds the shape.\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { IconButton } from \"../button/button\";\nimport { useReturnFocus } from \"./return-focus\";\n\nexport const dialog = tv({\n slots: {\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay backdrop-blur-xs\",\n content:\n \"kz-dialog fixed z-50 flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg shadow-lg focus:outline-hidden\",\n close: \"absolute right-3 top-3 h-9 w-9\",\n header: \"flex flex-col gap-1 px-5 pb-2 pr-14 pt-5\",\n title: \"text-base font-bold text-fg\",\n description: \"text-sm text-fg-secondary\",\n body: \"min-h-0 flex-1 overflow-y-auto px-5 pb-5\",\n footer:\n \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]\",\n },\n variants: {\n placement: {\n center: { content: \"left-1/2 top-1/2 max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl\" },\n /* A sheet: anchored at the bottom on a phone, centred from `sm` up. */\n bottom: {\n content:\n \"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[1.75rem] sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:w-full \" +\n \"sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-[1.75rem]\",\n },\n right: { content: \"inset-y-0 right-0 h-full w-full max-w-md rounded-none border-y-0 border-r-0\" },\n /* A navigation drawer: full height, from the left, clear of the notch. */\n left: { content: \"inset-y-0 left-0 h-full w-full max-w-md rounded-none border-y-0 border-l-0 pl-[env(safe-area-inset-left)]\" },\n },\n size: { sm: { content: \"sm:max-w-sm\" }, md: { content: \"sm:max-w-md\" }, lg: { content: \"sm:max-w-2xl\" } },\n },\n defaultVariants: { placement: \"center\", size: \"md\" },\n});\nconst s = dialog();\n\nexport type DialogVariants = VariantProps<typeof dialog>;\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype CloseButtonProps =\n | {\n /** The accessible name of the close button — passed translated. */\n closeLabel: string;\n /** Leaves the close button out (the content brings its own way out, or none: see `dismissible`). */\n hideClose?: false;\n }\n | { closeLabel?: string; hideClose: true };\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &\n DialogVariants &\n CloseButtonProps & {\n /**\n * center · bottom (a sheet on a phone, centred from `sm` up) · right (a side panel, full height) · left (a\n * navigation drawer, full height).\n * @default \"center\"\n */\n placement?: DialogVariants[\"placement\"];\n /**\n * sm · md · lg: the width of the dialog.\n * @default \"md\"\n */\n size?: DialogVariants[\"size\"];\n /**\n * `false`: Escape and a click outside do not close it (a gate the user must answer: age, terms). It still closes\n * through `open` / `onOpenChange` and any `Dialog.Close` inside. Default `true`.\n */\n dismissible?: boolean;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n };\n\nconst prevent = (event: Event) => event.preventDefault();\n\n/** Radix's content, mounted only while open: it remembers the opener then and gives focus back on close (WCAG 2.4.3). */\nfunction ReturnFocusContent({ onCloseAutoFocus, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {\n return <DialogPrimitive.Content onCloseAutoFocus={useReturnFocus(onCloseAutoFocus)} {...props} />;\n}\n\n/** The dialog itself, portalled over a dimmed overlay, with a close button. Give it a `Dialog.Title`. */\nexport function DialogContent({\n placement,\n size,\n closeLabel,\n hideClose,\n dismissible = true,\n container,\n className,\n children,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n ...props\n}: DialogContentProps) {\n const v = dialog({ placement, size });\n return (\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay className={v.overlay()} />\n <ReturnFocusContent\n data-placement={placement ?? \"center\"}\n data-dismissible={dismissible ? undefined : \"false\"}\n className={v.content({ className })}\n onEscapeKeyDown={dismissible ? onEscapeKeyDown : chain(onEscapeKeyDown, prevent)}\n onPointerDownOutside={dismissible ? onPointerDownOutside : chain(onPointerDownOutside, prevent)}\n onInteractOutside={dismissible ? onInteractOutside : chain(onInteractOutside, prevent)}\n {...props}\n >\n {children}\n {!hideClose && (\n <DialogPrimitive.Close asChild>\n <IconButton label={closeLabel as string} variant=\"ghost\" shape=\"pill\" className={v.close()}>\n <CloseIcon />\n </IconButton>\n </DialogPrimitive.Close>\n )}\n </ReturnFocusContent>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction chain<E extends Event>(handler: ((event: E) => void) | undefined, then: (event: E) => void) {\n return (event: E) => {\n handler?.(event);\n then(event);\n };\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.header({ className })} {...props} />;\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={s.title({ className })} {...props} />;\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={s.description({ className })} {...props} />;\n}\n\nexport function DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.footer({ className })} {...props} />;\n}\n\n/** `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const Dialog = {\n Root: DialogPrimitive.Root,\n Trigger: DialogPrimitive.Trigger,\n Close: DialogPrimitive.Close,\n Content: DialogContent,\n Header: DialogHeader,\n Title: DialogTitle,\n Description: DialogDescription,\n Body: DialogBody,\n Footer: DialogFooter,\n};\n\n/** `Omit` that keeps the close-button union (`closeLabel` required unless `hideClose`). */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type SheetProps = DistributiveOmit<DialogContentProps, \"placement\">;\n\n/** A sheet is a dialog anchored at the bottom (centred from `sm` up): `Dialog.Content placement=\"bottom\"`. */\nexport function Sheet(props: SheetProps) {\n return <DialogContent placement=\"bottom\" {...props} />;\n}\n","// Client. WCAG 2.4.3: when a dialog closes, focus goes back to what opened it. Radix only does it for `Dialog.Trigger`;\n// most app dialogs are controlled (`open` / `onOpenChange`) and opened by a plain button, so on Escape focus fell to\n// <body>. The content remembers the focused element when it mounts (it is still the opener: Radix moves focus in an\n// effect) and gives it back on close, unless the consumer's `onCloseAutoFocus` prevented the default.\nimport * as React from \"react\";\n\n/** The handler for `onCloseAutoFocus`: yours first, then focus back on the opener if it is still on the page. */\nexport function useReturnFocus(onCloseAutoFocus?: (event: Event) => void) {\n const [opener] = React.useState<Element | null>(() => (typeof document === \"undefined\" ? null : document.activeElement));\n return React.useCallback(\n (event: Event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n if (opener instanceof HTMLElement && opener !== document.body && opener.isConnected) {\n event.preventDefault();\n opener.focus();\n }\n },\n [opener, onCloseAutoFocus],\n );\n}\n","// Client (a portal). The moment two people choose each other — a match, a pair, a deal: a dialog in the brand\n// gradient, the two avatars meeting at a heart, the next step. The entrance plays once; still under reduced motion.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport { Button } from \"../button\";\nimport { Dialog } from \"../dialog\";\n\nexport type MatchDialogProps = {\n /** Shown (controlled). */\n open: boolean;\n /** Called when it closes. */\n onOpenChange: (open: boolean) => void;\n /** The headline (“It's a match!”) — passed translated; the dialog's name. */\n title: string;\n /** A line under it. */\n description?: string;\n /** The two people: avatar URLs and names. */\n people: readonly [{ src?: string | null; name: string }, { src?: string | null; name: string }];\n /** The main action (“Send a message”). */\n primaryLabel: string;\n /** What it does. */\n onPrimary: () => void;\n /** The way back (“Keep swiping”). */\n secondaryLabel: string;\n /** Where it is drawn (the page's body by default): give an element inside a brand scope (`.brand-…`) so the dialog wears the brand too. */\n container?: HTMLElement | null;\n};\n\n/** Two people choose each other: the avatars meet at a heart in the brand gradient, the next step under them. */\nexport function MatchDialog({ open, onOpenChange, title, description, people, primaryLabel, onPrimary, secondaryLabel, container }: MatchDialogProps) {\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Content hideClose size=\"sm\" container={container} className=\"overflow-hidden border-0 bg-gradient-to-br from-accent to-accent-2 text-center text-on-accent\">\n <style href=\"krizaka-match\" precedence=\"default\">\n {\"@media (prefers-reduced-motion:no-preference){[data-kz-match] [data-side=a]{animation:kz-match-a .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-side=b]{animation:kz-match-b .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-heart]{animation:kz-match-heart .45s .25s cubic-bezier(.3,1.6,.5,1) both}}@keyframes kz-match-a{from{transform:translateX(-60px) rotate(-8deg);opacity:0}}@keyframes kz-match-b{from{transform:translateX(60px) rotate(8deg);opacity:0}}@keyframes kz-match-heart{from{transform:scale(0)}}\"}\n </style>\n <div data-kz-match=\"\" className=\"flex flex-col items-center gap-3 px-2 pb-2 pt-6\">\n <Dialog.Title className=\"font-display text-4xl font-black text-on-accent\">{title}</Dialog.Title>\n {description ? <Dialog.Description className=\"max-w-xs text-sm text-on-accent\">{description}</Dialog.Description> : null}\n <div className=\"relative my-6 flex h-36 w-64 items-center justify-center\" aria-hidden>\n <span data-side=\"a\" className=\"absolute left-6 -rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[0].src ?? null} alt=\"\" fallback={people[0].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-side=\"b\" className=\"absolute right-6 rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[1].src ?? null} alt=\"\" fallback={people[1].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-heart=\"\" className=\"absolute -bottom-1 flex h-12 w-12 items-center justify-center rounded-full bg-on-accent text-accent shadow-lg\">\n <svg viewBox=\"0 0 24 24\" className=\"h-6 w-6\" fill=\"currentColor\">\n <path d=\"M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z\" />\n </svg>\n </span>\n </div>\n <div className=\"flex w-full flex-col gap-2\">\n <Button\n size=\"lg\"\n onClick={() => {\n onOpenChange(false);\n onPrimary();\n }}\n >\n {primaryLabel}\n </Button>\n <Dialog.Close asChild>\n <Button size=\"lg\" variant=\"ghost\" className=\"text-on-accent hover:bg-on-accent/15\">\n {secondaryLabel}\n </Button>\n </Dialog.Close>\n </div>\n </div>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
@@ -3,7 +3,7 @@ import {
3
3
  formatClock,
4
4
  playSafely,
5
5
  usePrefersReducedMotion
6
- } from "./chunk-BRKMQ6JV.js";
6
+ } from "./chunk-FZBWEDQI.js";
7
7
  import {
8
8
  CameraGlyph,
9
9
  FlipGlyph
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+ import { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ /** The text of inline nodes (a heading's label, an image's alt). */
5
+ declare function textOf(nodes: readonly {
6
+ type: string;
7
+ value?: string;
8
+ children?: readonly unknown[];
9
+ }[]): string;
10
+
11
+ type MarkdownProps = {
12
+ /** The Markdown source. */
13
+ children: string;
14
+ /** The text is still arriving (an AI answer): the unfinished end is closed, so no `**` or half a link shows. */
15
+ streaming?: boolean;
16
+ /** Called with the address of a link that is pressed. Default: opens `http(s)`, `mailto:` and `tel:` links with the system. */
17
+ onLinkPress?: (href: string) => void;
18
+ /** Styles merged on the container. */
19
+ style?: StyleProp<ViewStyle>;
20
+ };
21
+ /** A Markdown text in Text and View: headings, paragraphs, lists (and tasks), quotes, tables, coloured code, links, pictures. */
22
+ declare function Markdown({ children, streaming, onLinkPress, style }: MarkdownProps): React.JSX.Element;
23
+
24
+ export { Markdown, type MarkdownProps, textOf };
@@ -0,0 +1,200 @@
1
+ // src/native/markdown.tsx
2
+ import { Txt, useTheme } from "@krizaka/ui/native";
3
+ import * as React from "react";
4
+ import { Image, Linking, ScrollView, StyleSheet, Text, View } from "react-native";
5
+
6
+ // src/code-block/highlighter.ts
7
+ import { createHighlighter } from "@tanstack/highlight/core";
8
+ import { css } from "@tanstack/highlight/languages/css";
9
+ import { diff } from "@tanstack/highlight/languages/diff";
10
+ import { env } from "@tanstack/highlight/languages/env";
11
+ import { html } from "@tanstack/highlight/languages/html";
12
+ import { java } from "@tanstack/highlight/languages/java";
13
+ import { js } from "@tanstack/highlight/languages/js";
14
+ import { json } from "@tanstack/highlight/languages/json";
15
+ import { jsx } from "@tanstack/highlight/languages/jsx";
16
+ import { kotlin } from "@tanstack/highlight/languages/kotlin";
17
+ import { plaintext } from "@tanstack/highlight/languages/plaintext";
18
+ import { shell } from "@tanstack/highlight/languages/shell";
19
+ import { ts } from "@tanstack/highlight/languages/ts";
20
+ import { tsx } from "@tanstack/highlight/languages/tsx";
21
+ import { yaml } from "@tanstack/highlight/languages/yaml";
22
+ var codeHighlighter = createHighlighter({
23
+ languages: [css, diff, { ...env, aliases: [...env.aliases ?? [], "properties"] }, html, java, js, json, jsx, kotlin, plaintext, shell, ts, tsx, yaml],
24
+ fallbackLanguage: "plaintext"
25
+ });
26
+
27
+ // src/markdown/parse.ts
28
+ import { streamingMarkdownExtension } from "@tanstack/markdown/extensions/streaming";
29
+ import { parseMarkdown } from "@tanstack/markdown/parser";
30
+ var FENCE = /^ {0,3}(```|~~~)/gm;
31
+ function healLink(text) {
32
+ const open = text.lastIndexOf("[");
33
+ if (open < 0) return text;
34
+ const rest = text.slice(open + 1);
35
+ if (rest.includes("\n")) return text;
36
+ const image = open > 0 && text[open - 1] === "!";
37
+ const close = rest.indexOf("]");
38
+ const after = close < 0 ? "" : rest.slice(close + 1);
39
+ const cut = close < 0 || after === "" || after.startsWith("(") && !after.includes(")");
40
+ if (!cut) return text;
41
+ const head = text.slice(0, image ? open - 1 : open);
42
+ return image ? head : head + (close < 0 ? rest : rest.slice(0, close));
43
+ }
44
+ function healStreaming(source) {
45
+ if (((source.match(FENCE) ?? []).length & 1) === 1) return source;
46
+ let out = healLink(source);
47
+ const cut = out.lastIndexOf("\n\n");
48
+ const block = cut < 0 ? out : out.slice(cut + 2);
49
+ const open = (marker) => (block.split(marker).length - 1) % 2 === 1;
50
+ const close = (marker) => out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`;
51
+ if (open("`")) return close("`");
52
+ for (const marker of ["**", "~~"]) if (open(marker)) out = close(marker);
53
+ return out;
54
+ }
55
+ function parseSource(source, { streaming = false, headingIds = false } = {}) {
56
+ return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...streaming && { extensions: [streamingMarkdownExtension()] } }).children;
57
+ }
58
+ function textOf(nodes) {
59
+ return nodes.map((node) => node.children ? textOf(node.children) : node.value ?? "").join("");
60
+ }
61
+
62
+ // src/native/markdown.tsx
63
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
64
+ var SYSTEM = /^(https?:|mailto:|tel:)/i;
65
+ function Inlines({ nodes, ctx }) {
66
+ return nodes.map((node, i) => /* @__PURE__ */ jsx2(Inline, { node, ctx }, i));
67
+ }
68
+ function Inline({ node, ctx }) {
69
+ const { theme } = ctx;
70
+ switch (node.type) {
71
+ case "text":
72
+ return node.value;
73
+ case "inlineCode":
74
+ return /* @__PURE__ */ jsx2(Text, { style: { fontFamily: ctx.mono, fontSize: 13.5, backgroundColor: theme.surface3 }, children: ` ${node.value} ` });
75
+ case "strong":
76
+ return /* @__PURE__ */ jsx2(Text, { style: styles.strong, children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) });
77
+ case "emphasis":
78
+ return /* @__PURE__ */ jsx2(Text, { style: styles.em, children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) });
79
+ case "strike":
80
+ return /* @__PURE__ */ jsx2(Text, { style: styles.strike, children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) });
81
+ case "break":
82
+ return "\n";
83
+ case "link":
84
+ return /* @__PURE__ */ jsx2(Text, { role: "link", onPress: () => ctx.open(node.href), style: { color: theme.accentText, textDecorationLine: "underline" }, children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) });
85
+ // A picture inside a sentence cannot be drawn in a Text everywhere: its description stands in.
86
+ case "image":
87
+ return node.alt;
88
+ case "footnoteReference":
89
+ return /* @__PURE__ */ jsx2(Text, { style: { color: theme.accentText, fontSize: 11 }, children: ` [${node.number}]` });
90
+ case "inlineHtml":
91
+ return node.value;
92
+ default:
93
+ return null;
94
+ }
95
+ }
96
+ function Code({ node, ctx }) {
97
+ const { theme } = ctx;
98
+ const colour = {
99
+ comment: theme.textSecondary,
100
+ string: theme.successText,
101
+ inserted: theme.successText,
102
+ attr: theme.warningText,
103
+ meta: theme.warningText,
104
+ keyword: theme.accentText,
105
+ tag: theme.accentText,
106
+ selector: theme.accentText,
107
+ command: theme.accentText,
108
+ heading: theme.accentText,
109
+ link: theme.accentText,
110
+ type: theme.infoText,
111
+ function: theme.infoText,
112
+ number: theme.infoText,
113
+ literal: theme.infoText,
114
+ deleted: theme.dangerText
115
+ };
116
+ const { tokens } = codeHighlighter.tokenize(node.value, { lang: node.lang ?? "text" });
117
+ return /* @__PURE__ */ jsxs(View, { style: [styles.code, { backgroundColor: theme.surface1, borderColor: theme.borderDefault }], children: [
118
+ node.title || node.file ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", style: [styles.codeTitle, { backgroundColor: theme.surface2 }], children: node.title ?? node.file }) : null,
119
+ /* @__PURE__ */ jsx2(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, contentContainerStyle: styles.codeBody, children: /* @__PURE__ */ jsx2(Text, { style: { fontFamily: ctx.mono, fontSize: 12.5, lineHeight: 20, color: theme.textPrimary }, children: tokens.map((token, i) => /* @__PURE__ */ jsx2(Text, { style: token.className && colour[token.className] ? { color: colour[token.className] } : null, children: token.value }, i)) }) })
120
+ ] });
121
+ }
122
+ function Blocks({ nodes, ctx }) {
123
+ return nodes.map((node, i) => /* @__PURE__ */ jsx2(Block, { node, ctx }, i));
124
+ }
125
+ function Block({ node, ctx }) {
126
+ const { theme } = ctx;
127
+ switch (node.type) {
128
+ case "heading":
129
+ return node.depth === 1 ? /* @__PURE__ */ jsx2(Txt, { variant: "display", children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) }) : node.depth === 2 ? /* @__PURE__ */ jsx2(Txt, { variant: "title", style: styles.h2, children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) }) : /* @__PURE__ */ jsx2(Txt, { variant: "label", role: "heading", style: styles.h3, children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) });
130
+ case "paragraph": {
131
+ const pictures = node.children.filter((child) => child.type === "image");
132
+ const onlyPictures = pictures.length > 0 && node.children.every((child) => child.type === "image" || child.type === "text" && !child.value.trim());
133
+ if (onlyPictures) {
134
+ return pictures.map((picture, i) => /* @__PURE__ */ jsx2(Image, { source: { uri: picture.src }, alt: picture.alt, accessibilityLabel: picture.alt, resizeMode: "contain", style: styles.image }, i));
135
+ }
136
+ return /* @__PURE__ */ jsx2(Txt, { children: /* @__PURE__ */ jsx2(Inlines, { nodes: node.children, ctx }) });
137
+ }
138
+ case "code":
139
+ return /* @__PURE__ */ jsx2(Code, { node, ctx });
140
+ case "list":
141
+ return /* @__PURE__ */ jsx2(View, { role: "list", style: styles.list, children: node.items.map((item, i) => /* @__PURE__ */ jsxs(View, { role: "listitem", style: styles.item, children: [
142
+ /* @__PURE__ */ jsx2(Txt, { tone: "secondary", style: styles.marker, children: item.checked !== void 0 ? item.checked ? "\u2611" : "\u2610" : node.ordered ? `${(node.start ?? 1) + i}.` : "\u2022" }),
143
+ /* @__PURE__ */ jsx2(View, { style: styles.itemBody, children: /* @__PURE__ */ jsx2(Blocks, { nodes: item.children, ctx }) })
144
+ ] }, i)) });
145
+ case "blockquote":
146
+ return /* @__PURE__ */ jsx2(View, { style: [styles.quote, { borderLeftColor: theme.accent }], children: /* @__PURE__ */ jsx2(Blocks, { nodes: node.children, ctx }) });
147
+ case "table":
148
+ return /* @__PURE__ */ jsx2(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, children: /* @__PURE__ */ jsx2(View, { style: [styles.table, { borderColor: theme.borderSubtle }], children: [node.header, ...node.rows].map((row, r) => /* @__PURE__ */ jsx2(View, { style: [styles.row, { borderBottomColor: theme.borderSubtle }, r === 0 ? { backgroundColor: theme.surface2 } : null], children: row.map((cell, c) => /* @__PURE__ */ jsx2(Txt, { variant: r === 0 ? "label" : "body", tone: r === 0 ? "secondary" : "text", style: [styles.cell, { textAlign: node.align[c] ?? "left" }], children: /* @__PURE__ */ jsx2(Inlines, { nodes: cell.children, ctx }) }, c)) }, r)) }) });
149
+ case "thematicBreak":
150
+ return /* @__PURE__ */ jsx2(View, { style: [styles.rule, { backgroundColor: theme.borderDefault }] });
151
+ case "footnotes":
152
+ return /* @__PURE__ */ jsx2(View, { style: styles.list, children: node.items.map((item) => /* @__PURE__ */ jsxs(View, { style: styles.item, children: [
153
+ /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", style: styles.marker, children: `${item.number}.` }),
154
+ /* @__PURE__ */ jsx2(View, { style: styles.itemBody, children: /* @__PURE__ */ jsx2(Blocks, { nodes: item.children, ctx }) })
155
+ ] }, item.id)) });
156
+ case "html":
157
+ return /* @__PURE__ */ jsx2(Txt, { children: node.value });
158
+ default:
159
+ return null;
160
+ }
161
+ }
162
+ function Markdown({ children, streaming, onLinkPress, style }) {
163
+ const { theme, fonts } = useTheme();
164
+ const blocks = React.useMemo(() => parseSource(children, { streaming }), [children, streaming]);
165
+ const open = React.useCallback(
166
+ (href) => {
167
+ if (onLinkPress) return onLinkPress(href);
168
+ if (SYSTEM.test(href)) void Linking.openURL(href);
169
+ },
170
+ [onLinkPress]
171
+ );
172
+ const ctx = { theme, mono: fonts.mono ?? "Menlo", open };
173
+ return /* @__PURE__ */ jsx2(View, { "aria-busy": streaming || void 0, style: [styles.root, style], children: /* @__PURE__ */ jsx2(Blocks, { nodes: blocks, ctx }) });
174
+ }
175
+ var styles = StyleSheet.create({
176
+ root: { gap: 12 },
177
+ strong: { fontWeight: "700" },
178
+ em: { fontStyle: "italic" },
179
+ strike: { textDecorationLine: "line-through" },
180
+ h2: { marginTop: 8 },
181
+ h3: { marginTop: 4 },
182
+ image: { width: "100%", aspectRatio: 16 / 9, borderRadius: 12 },
183
+ list: { gap: 6 },
184
+ item: { flexDirection: "row", gap: 8 },
185
+ marker: { minWidth: 20, textAlign: "right" },
186
+ itemBody: { flex: 1, gap: 6 },
187
+ quote: { gap: 8, paddingLeft: 12, borderLeftWidth: 3 },
188
+ code: { overflow: "hidden", borderWidth: StyleSheet.hairlineWidth, borderRadius: 12 },
189
+ codeTitle: { paddingHorizontal: 12, paddingVertical: 6 },
190
+ codeBody: { padding: 12 },
191
+ table: { borderWidth: StyleSheet.hairlineWidth, borderRadius: 8, overflow: "hidden" },
192
+ row: { flexDirection: "row", borderBottomWidth: StyleSheet.hairlineWidth },
193
+ cell: { width: 120, paddingHorizontal: 10, paddingVertical: 8 },
194
+ rule: { height: StyleSheet.hairlineWidth }
195
+ });
196
+ export {
197
+ Markdown,
198
+ textOf
199
+ };
200
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/native/markdown.tsx","../../src/code-block/highlighter.ts","../../src/markdown/parse.ts"],"sourcesContent":["// React Native: the web `Markdown`'s AST (TanStack Markdown, via the same `parseSource`) drawn in Text and View. Raw HTML\n// is never rendered, executable URLs are dropped, `streaming` heals the unfinished end of a text that is arriving. Its own\n// entry (`@krizaka/ui/native/markdown`): the parser and the code grammars never load in an app that does not use it.\nimport { type Theme, Txt, useTheme } from \"@krizaka/ui/native\";\nimport * as React from \"react\";\nimport { Image, Linking, ScrollView, type StyleProp, StyleSheet, Text, View, type ViewStyle } from \"react-native\";\n\nimport { codeHighlighter } from \"../code-block/highlighter\";\nimport { type BlockNode, type InlineNode, parseSource, textOf } from \"../markdown/parse\";\n\nexport type MarkdownProps = {\n /** The Markdown source. */\n children: string;\n /** The text is still arriving (an AI answer): the unfinished end is closed, so no `**` or half a link shows. */\n streaming?: boolean;\n /** Called with the address of a link that is pressed. Default: opens `http(s)`, `mailto:` and `tel:` links with the system. */\n onLinkPress?: (href: string) => void;\n /** Styles merged on the container. */\n style?: StyleProp<ViewStyle>;\n};\n\ntype Context = { theme: Theme; mono: string | undefined; open: (href: string) => void };\n\nconst SYSTEM = /^(https?:|mailto:|tel:)/i;\n\nfunction Inlines({ nodes, ctx }: { nodes: readonly InlineNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Inline key={i} node={node} ctx={ctx} />);\n}\n\nfunction Inline({ node, ctx }: { node: InlineNode; ctx: Context }): React.ReactNode {\n const { theme } = ctx;\n switch (node.type) {\n case \"text\":\n return node.value;\n case \"inlineCode\":\n return <Text style={{ fontFamily: ctx.mono, fontSize: 13.5, backgroundColor: theme.surface3 }}>{` ${node.value} `}</Text>;\n case \"strong\":\n return (\n <Text style={styles.strong}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Text>\n );\n case \"emphasis\":\n return (\n <Text style={styles.em}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Text>\n );\n case \"strike\":\n return (\n <Text style={styles.strike}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Text>\n );\n case \"break\":\n return \"\\n\";\n case \"link\":\n return (\n <Text role=\"link\" onPress={() => ctx.open(node.href)} style={{ color: theme.accentText, textDecorationLine: \"underline\" }}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Text>\n );\n // A picture inside a sentence cannot be drawn in a Text everywhere: its description stands in.\n case \"image\":\n return node.alt;\n case \"footnoteReference\":\n return <Text style={{ color: theme.accentText, fontSize: 11 }}>{` [${node.number}]`}</Text>;\n case \"inlineHtml\":\n return node.value;\n default:\n return null;\n }\n}\n\n/** Code, coloured by the web's grammars from the theme's text roles. */\nfunction Code({ node, ctx }: { node: Extract<BlockNode, { type: \"code\" }>; ctx: Context }) {\n const { theme } = ctx;\n const colour: Record<string, string> = {\n comment: theme.textSecondary,\n string: theme.successText,\n inserted: theme.successText,\n attr: theme.warningText,\n meta: theme.warningText,\n keyword: theme.accentText,\n tag: theme.accentText,\n selector: theme.accentText,\n command: theme.accentText,\n heading: theme.accentText,\n link: theme.accentText,\n type: theme.infoText,\n function: theme.infoText,\n number: theme.infoText,\n literal: theme.infoText,\n deleted: theme.dangerText,\n };\n const { tokens } = codeHighlighter.tokenize(node.value, { lang: node.lang ?? \"text\" });\n return (\n <View style={[styles.code, { backgroundColor: theme.surface1, borderColor: theme.borderDefault }]}>\n {node.title || node.file ? (\n <Txt variant=\"caption\" tone=\"secondary\" style={[styles.codeTitle, { backgroundColor: theme.surface2 }]}>\n {node.title ?? node.file}\n </Txt>\n ) : null}\n <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.codeBody}>\n <Text style={{ fontFamily: ctx.mono, fontSize: 12.5, lineHeight: 20, color: theme.textPrimary }}>\n {tokens.map((token, i) => (\n <Text key={i} style={token.className && colour[token.className] ? { color: colour[token.className] } : null}>\n {token.value}\n </Text>\n ))}\n </Text>\n </ScrollView>\n </View>\n );\n}\n\nfunction Blocks({ nodes, ctx }: { nodes: readonly BlockNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Block key={i} node={node} ctx={ctx} />);\n}\n\nfunction Block({ node, ctx }: { node: BlockNode; ctx: Context }): React.ReactNode {\n const { theme } = ctx;\n switch (node.type) {\n case \"heading\":\n return node.depth === 1 ? (\n <Txt variant=\"display\">\n <Inlines nodes={node.children} ctx={ctx} />\n </Txt>\n ) : node.depth === 2 ? (\n <Txt variant=\"title\" style={styles.h2}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Txt>\n ) : (\n <Txt variant=\"label\" role=\"heading\" style={styles.h3}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Txt>\n );\n case \"paragraph\": {\n const pictures = node.children.filter((child) => child.type === \"image\");\n const onlyPictures = pictures.length > 0 && node.children.every((child) => child.type === \"image\" || (child.type === \"text\" && !child.value.trim()));\n if (onlyPictures) {\n return pictures.map((picture, i) => <Image key={i} source={{ uri: picture.src }} alt={picture.alt} accessibilityLabel={picture.alt} resizeMode=\"contain\" style={styles.image} />);\n }\n return (\n <Txt>\n <Inlines nodes={node.children} ctx={ctx} />\n </Txt>\n );\n }\n case \"code\":\n return <Code node={node} ctx={ctx} />;\n case \"list\":\n return (\n <View role=\"list\" style={styles.list}>\n {node.items.map((item, i) => (\n <View key={i} role=\"listitem\" style={styles.item}>\n <Txt tone=\"secondary\" style={styles.marker}>\n {item.checked !== undefined ? (item.checked ? \"☑\" : \"☐\") : node.ordered ? `${(node.start ?? 1) + i}.` : \"•\"}\n </Txt>\n <View style={styles.itemBody}>\n <Blocks nodes={item.children} ctx={ctx} />\n </View>\n </View>\n ))}\n </View>\n );\n case \"blockquote\":\n return (\n <View style={[styles.quote, { borderLeftColor: theme.accent }]}>\n <Blocks nodes={node.children} ctx={ctx} />\n </View>\n );\n case \"table\":\n return (\n <ScrollView horizontal showsHorizontalScrollIndicator={false}>\n <View style={[styles.table, { borderColor: theme.borderSubtle }]}>\n {[node.header, ...node.rows].map((row, r) => (\n <View key={r} style={[styles.row, { borderBottomColor: theme.borderSubtle }, r === 0 ? { backgroundColor: theme.surface2 } : null]}>\n {row.map((cell, c) => (\n <Txt key={c} variant={r === 0 ? \"label\" : \"body\"} tone={r === 0 ? \"secondary\" : \"text\"} style={[styles.cell, { textAlign: node.align[c] ?? \"left\" }]}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </Txt>\n ))}\n </View>\n ))}\n </View>\n </ScrollView>\n );\n case \"thematicBreak\":\n return <View style={[styles.rule, { backgroundColor: theme.borderDefault }]} />;\n case \"footnotes\":\n return (\n <View style={styles.list}>\n {node.items.map((item) => (\n <View key={item.id} style={styles.item}>\n <Txt variant=\"caption\" tone=\"secondary\" style={styles.marker}>{`${item.number}.`}</Txt>\n <View style={styles.itemBody}>\n <Blocks nodes={item.children} ctx={ctx} />\n </View>\n </View>\n ))}\n </View>\n );\n case \"html\":\n return <Txt>{node.value}</Txt>;\n default:\n return null;\n }\n}\n\n/** A Markdown text in Text and View: headings, paragraphs, lists (and tasks), quotes, tables, coloured code, links, pictures. */\nexport function Markdown({ children, streaming, onLinkPress, style }: MarkdownProps) {\n const { theme, fonts } = useTheme();\n const blocks = React.useMemo(() => parseSource(children, { streaming }), [children, streaming]);\n const open = React.useCallback(\n (href: string) => {\n if (onLinkPress) return onLinkPress(href);\n if (SYSTEM.test(href)) void Linking.openURL(href);\n },\n [onLinkPress],\n );\n const ctx: Context = { theme, mono: fonts.mono ?? \"Menlo\", open };\n return (\n <View aria-busy={streaming || undefined} style={[styles.root, style]}>\n <Blocks nodes={blocks} ctx={ctx} />\n </View>\n );\n}\n\nexport { textOf };\n\nconst styles = StyleSheet.create({\n root: { gap: 12 },\n strong: { fontWeight: \"700\" },\n em: { fontStyle: \"italic\" },\n strike: { textDecorationLine: \"line-through\" },\n h2: { marginTop: 8 },\n h3: { marginTop: 4 },\n image: { width: \"100%\", aspectRatio: 16 / 9, borderRadius: 12 },\n list: { gap: 6 },\n item: { flexDirection: \"row\", gap: 8 },\n marker: { minWidth: 20, textAlign: \"right\" },\n itemBody: { flex: 1, gap: 6 },\n quote: { gap: 8, paddingLeft: 12, borderLeftWidth: 3 },\n code: { overflow: \"hidden\", borderWidth: StyleSheet.hairlineWidth, borderRadius: 12 },\n codeTitle: { paddingHorizontal: 12, paddingVertical: 6 },\n codeBody: { padding: 12 },\n table: { borderWidth: StyleSheet.hairlineWidth, borderRadius: 8, overflow: \"hidden\" },\n row: { flexDirection: \"row\", borderBottomWidth: StyleSheet.hairlineWidth },\n cell: { width: 120, paddingHorizontal: 10, paddingVertical: 8 },\n rule: { height: StyleSheet.hairlineWidth },\n});\n","// The highlighter of every code sample: TanStack Highlight (a deterministic, synchronous tokenizer — the same markup on\n// the server and on the client) with the grammars a Krizaka page shows. Each grammar is registered by hand: nothing\n// else is bundled. An unknown language is plain text, never an error.\nimport { createHighlighter } from \"@tanstack/highlight/core\";\nimport { css } from \"@tanstack/highlight/languages/css\";\nimport { diff } from \"@tanstack/highlight/languages/diff\";\nimport { env } from \"@tanstack/highlight/languages/env\";\nimport { html } from \"@tanstack/highlight/languages/html\";\nimport { java } from \"@tanstack/highlight/languages/java\";\nimport { js } from \"@tanstack/highlight/languages/js\";\nimport { json } from \"@tanstack/highlight/languages/json\";\nimport { jsx } from \"@tanstack/highlight/languages/jsx\";\nimport { kotlin } from \"@tanstack/highlight/languages/kotlin\";\nimport { plaintext } from \"@tanstack/highlight/languages/plaintext\";\nimport { shell } from \"@tanstack/highlight/languages/shell\";\nimport { ts } from \"@tanstack/highlight/languages/ts\";\nimport { tsx } from \"@tanstack/highlight/languages/tsx\";\nimport { yaml } from \"@tanstack/highlight/languages/yaml\";\n\n/** The languages a sample can be written in, with the names they answer to (`bash`, `xml`, `yml`, `kt`, `properties`…). */\nexport const codeHighlighter = createHighlighter({\n languages: [css, diff, { ...env, aliases: [...(env.aliases ?? []), \"properties\"] }, html, java, js, json, jsx, kotlin, plaintext, shell, ts, tsx, yaml],\n fallbackLanguage: \"plaintext\",\n});\n","// The shared half of Markdown, web and React Native: the source becomes TanStack Markdown's serializable AST\n// (`@tanstack/markdown/parser`) — raw HTML is never enabled (it stays text), executable URLs are dropped (`javascript:`,\n// `data:`), and a text that is still arriving is healed so the half-written end reads like the finished one.\nimport type { BlockNode } from \"@tanstack/markdown\";\nimport { streamingMarkdownExtension } from \"@tanstack/markdown/extensions/streaming\";\nimport { parseMarkdown } from \"@tanstack/markdown/parser\";\n\nexport type { BlockNode, InlineNode, ListItemNode, TableNode } from \"@tanstack/markdown\";\n\nexport type ParseOptions = {\n /** The text is still arriving (an AI answer): the unfinished end is closed, so it never shows `**` or a half link. */\n streaming?: boolean;\n /** Gives every heading an `id` (`#section` links, a table of contents). */\n headingIds?: boolean;\n};\n\nconst FENCE = /^ {0,3}(```|~~~)/gm;\n\n/** A link or image cut at its end: `[label`, `[label]`, `[label](http…` become `label`, an image is dropped. Linear: one scan of the last `[`. */\nfunction healLink(text: string): string {\n const open = text.lastIndexOf(\"[\");\n if (open < 0) return text;\n const rest = text.slice(open + 1);\n if (rest.includes(\"\\n\")) return text;\n const image = open > 0 && text[open - 1] === \"!\";\n const close = rest.indexOf(\"]\");\n const after = close < 0 ? \"\" : rest.slice(close + 1);\n const cut = close < 0 || after === \"\" || (after.startsWith(\"(\") && !after.includes(\")\"));\n if (!cut) return text;\n const head = text.slice(0, image ? open - 1 : open);\n return image ? head : head + (close < 0 ? rest : rest.slice(0, close));\n}\n\n/**\n * Closes what a text cut in the middle leaves open: an emphasis (`**`, `~~`) or a code span, a link or image whose\n * address has not arrived. Inside an open code fence nothing is healed — it is shown as code as it comes.\n */\nexport function healStreaming(source: string): string {\n if (((source.match(FENCE) ?? []).length & 1) === 1) return source;\n let out = healLink(source);\n const cut = out.lastIndexOf(\"\\n\\n\");\n const block = cut < 0 ? out : out.slice(cut + 2);\n const open = (marker: string) => (block.split(marker).length - 1) % 2 === 1;\n // A marker just opened, with nothing after it yet, is dropped rather than shown as an empty emphasis.\n const close = (marker: string) => (out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`);\n if (open(\"`\")) return close(\"`\");\n for (const marker of [\"**\", \"~~\"]) if (open(marker)) out = close(marker);\n return out;\n}\n\n/** The blocks of a Markdown source. */\nexport function parseSource(source: string, { streaming = false, headingIds = false }: ParseOptions = {}): BlockNode[] {\n return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...(streaming && { extensions: [streamingMarkdownExtension()] }) }).children;\n}\n\n/** The text of inline nodes (a heading's label, an image's alt). */\nexport function textOf(nodes: readonly { type: string; value?: string; children?: readonly unknown[] }[]): string {\n return nodes\n .map((node) => (node.children ? textOf(node.children as typeof nodes) : (node.value ?? \"\")))\n .join(\"\");\n}\n"],"mappings":"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","names":["jsx"]}
package/dist/native.d.ts CHANGED
@@ -306,15 +306,9 @@ type CardOverlayProps = ViewProps & {
306
306
  };
307
307
  /** What sits on the media (badges, a caption), in a corner. */
308
308
  declare function CardOverlay({ corner, style, ...props }: CardOverlayProps): React$1.JSX.Element;
309
- declare const PADDING: {
310
- readonly none: 0;
311
- readonly sm: 12;
312
- readonly md: 16;
313
- readonly lg: 24;
314
- };
315
309
  type CardBodyProps = ViewProps & {
316
310
  /** none · sm · md (default) · lg. */
317
- padding?: keyof typeof PADDING;
311
+ padding?: "none" | "sm" | "md" | "lg";
318
312
  };
319
313
  declare function CardBody({ padding, style, ...props }: CardBodyProps): React$1.JSX.Element;
320
314
  /** The card's name: one line, a header for screen readers. */