@crafter/rn-ai-elements 0.0.1

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 (344) hide show
  1. package/lib/commonjs/chatbot/AIImage.js +126 -0
  2. package/lib/commonjs/chatbot/AIImage.js.map +1 -0
  3. package/lib/commonjs/chatbot/Attachments.js +317 -0
  4. package/lib/commonjs/chatbot/Attachments.js.map +1 -0
  5. package/lib/commonjs/chatbot/ChatErrorBoundary.js +201 -0
  6. package/lib/commonjs/chatbot/ChatErrorBoundary.js.map +1 -0
  7. package/lib/commonjs/chatbot/ChatMessageItem.js +169 -0
  8. package/lib/commonjs/chatbot/ChatMessageItem.js.map +1 -0
  9. package/lib/commonjs/chatbot/Conversation.js +415 -0
  10. package/lib/commonjs/chatbot/Conversation.js.map +1 -0
  11. package/lib/commonjs/chatbot/ConversationScrollButton.js +131 -0
  12. package/lib/commonjs/chatbot/ConversationScrollButton.js.map +1 -0
  13. package/lib/commonjs/chatbot/Message.js +203 -0
  14. package/lib/commonjs/chatbot/Message.js.map +1 -0
  15. package/lib/commonjs/chatbot/PromptInput.js +352 -0
  16. package/lib/commonjs/chatbot/PromptInput.js.map +1 -0
  17. package/lib/commonjs/chatbot/Reasoning.js +184 -0
  18. package/lib/commonjs/chatbot/Reasoning.js.map +1 -0
  19. package/lib/commonjs/chatbot/Shimmer.js +116 -0
  20. package/lib/commonjs/chatbot/Shimmer.js.map +1 -0
  21. package/lib/commonjs/chatbot/Sources.js +212 -0
  22. package/lib/commonjs/chatbot/Sources.js.map +1 -0
  23. package/lib/commonjs/chatbot/Suggestion.js +99 -0
  24. package/lib/commonjs/chatbot/Suggestion.js.map +1 -0
  25. package/lib/commonjs/chatbot/Tool.js +307 -0
  26. package/lib/commonjs/chatbot/Tool.js.map +1 -0
  27. package/lib/commonjs/chatbot/adapters/uiMessageAdapter.js +141 -0
  28. package/lib/commonjs/chatbot/adapters/uiMessageAdapter.js.map +1 -0
  29. package/lib/commonjs/chatbot/index.js +140 -0
  30. package/lib/commonjs/chatbot/index.js.map +1 -0
  31. package/lib/commonjs/chatbot/types.js +6 -0
  32. package/lib/commonjs/chatbot/types.js.map +1 -0
  33. package/lib/commonjs/hooks/index.js +34 -0
  34. package/lib/commonjs/hooks/index.js.map +1 -0
  35. package/lib/commonjs/hooks/useAutoScroll.js +39 -0
  36. package/lib/commonjs/hooks/useAutoScroll.js.map +1 -0
  37. package/lib/commonjs/hooks/useClipboard.js +44 -0
  38. package/lib/commonjs/hooks/useClipboard.js.map +1 -0
  39. package/lib/commonjs/hooks/useCollapsible.js +35 -0
  40. package/lib/commonjs/hooks/useCollapsible.js.map +1 -0
  41. package/lib/commonjs/hooks/useStickToBottom.js +68 -0
  42. package/lib/commonjs/hooks/useStickToBottom.js.map +1 -0
  43. package/lib/commonjs/index.js +257 -0
  44. package/lib/commonjs/index.js.map +1 -0
  45. package/lib/commonjs/package.json +1 -0
  46. package/lib/commonjs/primitives/Badge.js +119 -0
  47. package/lib/commonjs/primitives/Badge.js.map +1 -0
  48. package/lib/commonjs/primitives/Button.js +185 -0
  49. package/lib/commonjs/primitives/Button.js.map +1 -0
  50. package/lib/commonjs/primitives/Card.js +166 -0
  51. package/lib/commonjs/primitives/Card.js.map +1 -0
  52. package/lib/commonjs/primitives/Collapsible.js +137 -0
  53. package/lib/commonjs/primitives/Collapsible.js.map +1 -0
  54. package/lib/commonjs/primitives/ScrollArea.js +40 -0
  55. package/lib/commonjs/primitives/ScrollArea.js.map +1 -0
  56. package/lib/commonjs/primitives/index.js +83 -0
  57. package/lib/commonjs/primitives/index.js.map +1 -0
  58. package/lib/commonjs/streaming/StreamingMarkdown.js +252 -0
  59. package/lib/commonjs/streaming/StreamingMarkdown.js.map +1 -0
  60. package/lib/commonjs/streaming/index.js +13 -0
  61. package/lib/commonjs/streaming/index.js.map +1 -0
  62. package/lib/commonjs/streaming/parser.js +482 -0
  63. package/lib/commonjs/streaming/parser.js.map +1 -0
  64. package/lib/commonjs/streaming/renderers/BlockquoteRenderer.js +35 -0
  65. package/lib/commonjs/streaming/renderers/BlockquoteRenderer.js.map +1 -0
  66. package/lib/commonjs/streaming/renderers/CodeRenderer.js +128 -0
  67. package/lib/commonjs/streaming/renderers/CodeRenderer.js.map +1 -0
  68. package/lib/commonjs/streaming/renderers/HeadingRenderer.js +61 -0
  69. package/lib/commonjs/streaming/renderers/HeadingRenderer.js.map +1 -0
  70. package/lib/commonjs/streaming/renderers/ImageRenderer.js +53 -0
  71. package/lib/commonjs/streaming/renderers/ImageRenderer.js.map +1 -0
  72. package/lib/commonjs/streaming/renderers/LinkRenderer.js +49 -0
  73. package/lib/commonjs/streaming/renderers/LinkRenderer.js.map +1 -0
  74. package/lib/commonjs/streaming/renderers/ListRenderer.js +63 -0
  75. package/lib/commonjs/streaming/renderers/ListRenderer.js.map +1 -0
  76. package/lib/commonjs/streaming/renderers/TableRenderer.js +77 -0
  77. package/lib/commonjs/streaming/renderers/TableRenderer.js.map +1 -0
  78. package/lib/commonjs/streaming/renderers/TextRenderer.js +41 -0
  79. package/lib/commonjs/streaming/renderers/TextRenderer.js.map +1 -0
  80. package/lib/commonjs/streaming/renderers/index.js +76 -0
  81. package/lib/commonjs/streaming/renderers/index.js.map +1 -0
  82. package/lib/commonjs/streaming/renderers/renderInlineChildren.js +112 -0
  83. package/lib/commonjs/streaming/renderers/renderInlineChildren.js.map +1 -0
  84. package/lib/commonjs/streaming/renderers/renderNode.js +81 -0
  85. package/lib/commonjs/streaming/renderers/renderNode.js.map +1 -0
  86. package/lib/commonjs/theme/ThemeProvider.js +68 -0
  87. package/lib/commonjs/theme/ThemeProvider.js.map +1 -0
  88. package/lib/commonjs/theme/defaultTheme.js +96 -0
  89. package/lib/commonjs/theme/defaultTheme.js.map +1 -0
  90. package/lib/commonjs/theme/index.js +32 -0
  91. package/lib/commonjs/theme/index.js.map +1 -0
  92. package/lib/commonjs/theme/tokens.js +2 -0
  93. package/lib/commonjs/theme/tokens.js.map +1 -0
  94. package/lib/commonjs/types.d.js +2 -0
  95. package/lib/commonjs/types.d.js.map +1 -0
  96. package/lib/commonjs/voice/index.js +13 -0
  97. package/lib/commonjs/voice/index.js.map +1 -0
  98. package/lib/commonjs/voice/useSpeechRecognition.js +172 -0
  99. package/lib/commonjs/voice/useSpeechRecognition.js.map +1 -0
  100. package/lib/module/chatbot/AIImage.js +121 -0
  101. package/lib/module/chatbot/AIImage.js.map +1 -0
  102. package/lib/module/chatbot/Attachments.js +312 -0
  103. package/lib/module/chatbot/Attachments.js.map +1 -0
  104. package/lib/module/chatbot/ChatErrorBoundary.js +196 -0
  105. package/lib/module/chatbot/ChatErrorBoundary.js.map +1 -0
  106. package/lib/module/chatbot/ChatMessageItem.js +164 -0
  107. package/lib/module/chatbot/ChatMessageItem.js.map +1 -0
  108. package/lib/module/chatbot/Conversation.js +412 -0
  109. package/lib/module/chatbot/Conversation.js.map +1 -0
  110. package/lib/module/chatbot/ConversationScrollButton.js +126 -0
  111. package/lib/module/chatbot/ConversationScrollButton.js.map +1 -0
  112. package/lib/module/chatbot/Message.js +198 -0
  113. package/lib/module/chatbot/Message.js.map +1 -0
  114. package/lib/module/chatbot/PromptInput.js +347 -0
  115. package/lib/module/chatbot/PromptInput.js.map +1 -0
  116. package/lib/module/chatbot/Reasoning.js +179 -0
  117. package/lib/module/chatbot/Reasoning.js.map +1 -0
  118. package/lib/module/chatbot/Shimmer.js +111 -0
  119. package/lib/module/chatbot/Shimmer.js.map +1 -0
  120. package/lib/module/chatbot/Sources.js +207 -0
  121. package/lib/module/chatbot/Sources.js.map +1 -0
  122. package/lib/module/chatbot/Suggestion.js +94 -0
  123. package/lib/module/chatbot/Suggestion.js.map +1 -0
  124. package/lib/module/chatbot/Tool.js +303 -0
  125. package/lib/module/chatbot/Tool.js.map +1 -0
  126. package/lib/module/chatbot/adapters/uiMessageAdapter.js +137 -0
  127. package/lib/module/chatbot/adapters/uiMessageAdapter.js.map +1 -0
  128. package/lib/module/chatbot/index.js +39 -0
  129. package/lib/module/chatbot/index.js.map +1 -0
  130. package/lib/module/chatbot/types.js +4 -0
  131. package/lib/module/chatbot/types.js.map +1 -0
  132. package/lib/module/hooks/index.js +7 -0
  133. package/lib/module/hooks/index.js.map +1 -0
  134. package/lib/module/hooks/useAutoScroll.js +35 -0
  135. package/lib/module/hooks/useAutoScroll.js.map +1 -0
  136. package/lib/module/hooks/useClipboard.js +40 -0
  137. package/lib/module/hooks/useClipboard.js.map +1 -0
  138. package/lib/module/hooks/useCollapsible.js +31 -0
  139. package/lib/module/hooks/useCollapsible.js.map +1 -0
  140. package/lib/module/hooks/useStickToBottom.js +64 -0
  141. package/lib/module/hooks/useStickToBottom.js.map +1 -0
  142. package/lib/module/index.js +19 -0
  143. package/lib/module/index.js.map +1 -0
  144. package/lib/module/package.json +1 -0
  145. package/lib/module/primitives/Badge.js +114 -0
  146. package/lib/module/primitives/Badge.js.map +1 -0
  147. package/lib/module/primitives/Button.js +180 -0
  148. package/lib/module/primitives/Button.js.map +1 -0
  149. package/lib/module/primitives/Card.js +156 -0
  150. package/lib/module/primitives/Card.js.map +1 -0
  151. package/lib/module/primitives/Collapsible.js +130 -0
  152. package/lib/module/primitives/Collapsible.js.map +1 -0
  153. package/lib/module/primitives/ScrollArea.js +35 -0
  154. package/lib/module/primitives/ScrollArea.js.map +1 -0
  155. package/lib/module/primitives/index.js +8 -0
  156. package/lib/module/primitives/index.js.map +1 -0
  157. package/lib/module/streaming/StreamingMarkdown.js +246 -0
  158. package/lib/module/streaming/StreamingMarkdown.js.map +1 -0
  159. package/lib/module/streaming/index.js +4 -0
  160. package/lib/module/streaming/index.js.map +1 -0
  161. package/lib/module/streaming/parser.js +477 -0
  162. package/lib/module/streaming/parser.js.map +1 -0
  163. package/lib/module/streaming/renderers/BlockquoteRenderer.js +30 -0
  164. package/lib/module/streaming/renderers/BlockquoteRenderer.js.map +1 -0
  165. package/lib/module/streaming/renderers/CodeRenderer.js +123 -0
  166. package/lib/module/streaming/renderers/CodeRenderer.js.map +1 -0
  167. package/lib/module/streaming/renderers/HeadingRenderer.js +56 -0
  168. package/lib/module/streaming/renderers/HeadingRenderer.js.map +1 -0
  169. package/lib/module/streaming/renderers/ImageRenderer.js +48 -0
  170. package/lib/module/streaming/renderers/ImageRenderer.js.map +1 -0
  171. package/lib/module/streaming/renderers/LinkRenderer.js +44 -0
  172. package/lib/module/streaming/renderers/LinkRenderer.js.map +1 -0
  173. package/lib/module/streaming/renderers/ListRenderer.js +58 -0
  174. package/lib/module/streaming/renderers/ListRenderer.js.map +1 -0
  175. package/lib/module/streaming/renderers/TableRenderer.js +72 -0
  176. package/lib/module/streaming/renderers/TableRenderer.js.map +1 -0
  177. package/lib/module/streaming/renderers/TextRenderer.js +36 -0
  178. package/lib/module/streaming/renderers/TextRenderer.js.map +1 -0
  179. package/lib/module/streaming/renderers/index.js +13 -0
  180. package/lib/module/streaming/renderers/index.js.map +1 -0
  181. package/lib/module/streaming/renderers/renderInlineChildren.js +107 -0
  182. package/lib/module/streaming/renderers/renderInlineChildren.js.map +1 -0
  183. package/lib/module/streaming/renderers/renderNode.js +78 -0
  184. package/lib/module/streaming/renderers/renderNode.js.map +1 -0
  185. package/lib/module/theme/ThemeProvider.js +62 -0
  186. package/lib/module/theme/ThemeProvider.js.map +1 -0
  187. package/lib/module/theme/defaultTheme.js +92 -0
  188. package/lib/module/theme/defaultTheme.js.map +1 -0
  189. package/lib/module/theme/index.js +5 -0
  190. package/lib/module/theme/index.js.map +1 -0
  191. package/lib/module/theme/tokens.js +2 -0
  192. package/lib/module/theme/tokens.js.map +1 -0
  193. package/lib/module/types.d.js +2 -0
  194. package/lib/module/types.d.js.map +1 -0
  195. package/lib/module/voice/index.js +14 -0
  196. package/lib/module/voice/index.js.map +1 -0
  197. package/lib/module/voice/useSpeechRecognition.js +169 -0
  198. package/lib/module/voice/useSpeechRecognition.js.map +1 -0
  199. package/lib/typescript/src/chatbot/AIImage.d.ts +24 -0
  200. package/lib/typescript/src/chatbot/AIImage.d.ts.map +1 -0
  201. package/lib/typescript/src/chatbot/Attachments.d.ts +20 -0
  202. package/lib/typescript/src/chatbot/Attachments.d.ts.map +1 -0
  203. package/lib/typescript/src/chatbot/ChatErrorBoundary.d.ts +57 -0
  204. package/lib/typescript/src/chatbot/ChatErrorBoundary.d.ts.map +1 -0
  205. package/lib/typescript/src/chatbot/ChatMessageItem.d.ts +45 -0
  206. package/lib/typescript/src/chatbot/ChatMessageItem.d.ts.map +1 -0
  207. package/lib/typescript/src/chatbot/Conversation.d.ts +94 -0
  208. package/lib/typescript/src/chatbot/Conversation.d.ts.map +1 -0
  209. package/lib/typescript/src/chatbot/ConversationScrollButton.d.ts +62 -0
  210. package/lib/typescript/src/chatbot/ConversationScrollButton.d.ts.map +1 -0
  211. package/lib/typescript/src/chatbot/Message.d.ts +39 -0
  212. package/lib/typescript/src/chatbot/Message.d.ts.map +1 -0
  213. package/lib/typescript/src/chatbot/PromptInput.d.ts +93 -0
  214. package/lib/typescript/src/chatbot/PromptInput.d.ts.map +1 -0
  215. package/lib/typescript/src/chatbot/Reasoning.d.ts +14 -0
  216. package/lib/typescript/src/chatbot/Reasoning.d.ts.map +1 -0
  217. package/lib/typescript/src/chatbot/Shimmer.d.ts +13 -0
  218. package/lib/typescript/src/chatbot/Shimmer.d.ts.map +1 -0
  219. package/lib/typescript/src/chatbot/Sources.d.ts +17 -0
  220. package/lib/typescript/src/chatbot/Sources.d.ts.map +1 -0
  221. package/lib/typescript/src/chatbot/Suggestion.d.ts +15 -0
  222. package/lib/typescript/src/chatbot/Suggestion.d.ts.map +1 -0
  223. package/lib/typescript/src/chatbot/Tool.d.ts +30 -0
  224. package/lib/typescript/src/chatbot/Tool.d.ts.map +1 -0
  225. package/lib/typescript/src/chatbot/adapters/uiMessageAdapter.d.ts +24 -0
  226. package/lib/typescript/src/chatbot/adapters/uiMessageAdapter.d.ts.map +1 -0
  227. package/lib/typescript/src/chatbot/index.d.ts +29 -0
  228. package/lib/typescript/src/chatbot/index.d.ts.map +1 -0
  229. package/lib/typescript/src/chatbot/types.d.ts +49 -0
  230. package/lib/typescript/src/chatbot/types.d.ts.map +1 -0
  231. package/lib/typescript/src/hooks/index.d.ts +9 -0
  232. package/lib/typescript/src/hooks/index.d.ts.map +1 -0
  233. package/lib/typescript/src/hooks/useAutoScroll.d.ts +23 -0
  234. package/lib/typescript/src/hooks/useAutoScroll.d.ts.map +1 -0
  235. package/lib/typescript/src/hooks/useClipboard.d.ts +22 -0
  236. package/lib/typescript/src/hooks/useClipboard.d.ts.map +1 -0
  237. package/lib/typescript/src/hooks/useCollapsible.d.ts +28 -0
  238. package/lib/typescript/src/hooks/useCollapsible.d.ts.map +1 -0
  239. package/lib/typescript/src/hooks/useStickToBottom.d.ts +39 -0
  240. package/lib/typescript/src/hooks/useStickToBottom.d.ts.map +1 -0
  241. package/lib/typescript/src/index.d.ts +11 -0
  242. package/lib/typescript/src/index.d.ts.map +1 -0
  243. package/lib/typescript/src/primitives/Badge.d.ts +10 -0
  244. package/lib/typescript/src/primitives/Badge.d.ts.map +1 -0
  245. package/lib/typescript/src/primitives/Button.d.ts +16 -0
  246. package/lib/typescript/src/primitives/Button.d.ts.map +1 -0
  247. package/lib/typescript/src/primitives/Card.d.ts +33 -0
  248. package/lib/typescript/src/primitives/Card.d.ts.map +1 -0
  249. package/lib/typescript/src/primitives/Collapsible.d.ts +20 -0
  250. package/lib/typescript/src/primitives/Collapsible.d.ts.map +1 -0
  251. package/lib/typescript/src/primitives/ScrollArea.d.ts +10 -0
  252. package/lib/typescript/src/primitives/ScrollArea.d.ts.map +1 -0
  253. package/lib/typescript/src/primitives/index.d.ts +11 -0
  254. package/lib/typescript/src/primitives/index.d.ts.map +1 -0
  255. package/lib/typescript/src/streaming/StreamingMarkdown.d.ts +47 -0
  256. package/lib/typescript/src/streaming/StreamingMarkdown.d.ts.map +1 -0
  257. package/lib/typescript/src/streaming/index.d.ts +3 -0
  258. package/lib/typescript/src/streaming/index.d.ts.map +1 -0
  259. package/lib/typescript/src/streaming/parser.d.ts +41 -0
  260. package/lib/typescript/src/streaming/parser.d.ts.map +1 -0
  261. package/lib/typescript/src/streaming/renderers/BlockquoteRenderer.d.ts +7 -0
  262. package/lib/typescript/src/streaming/renderers/BlockquoteRenderer.d.ts.map +1 -0
  263. package/lib/typescript/src/streaming/renderers/CodeRenderer.d.ts +7 -0
  264. package/lib/typescript/src/streaming/renderers/CodeRenderer.d.ts.map +1 -0
  265. package/lib/typescript/src/streaming/renderers/HeadingRenderer.d.ts +7 -0
  266. package/lib/typescript/src/streaming/renderers/HeadingRenderer.d.ts.map +1 -0
  267. package/lib/typescript/src/streaming/renderers/ImageRenderer.d.ts +7 -0
  268. package/lib/typescript/src/streaming/renderers/ImageRenderer.d.ts.map +1 -0
  269. package/lib/typescript/src/streaming/renderers/LinkRenderer.d.ts +7 -0
  270. package/lib/typescript/src/streaming/renderers/LinkRenderer.d.ts.map +1 -0
  271. package/lib/typescript/src/streaming/renderers/ListRenderer.d.ts +7 -0
  272. package/lib/typescript/src/streaming/renderers/ListRenderer.d.ts.map +1 -0
  273. package/lib/typescript/src/streaming/renderers/TableRenderer.d.ts +7 -0
  274. package/lib/typescript/src/streaming/renderers/TableRenderer.d.ts.map +1 -0
  275. package/lib/typescript/src/streaming/renderers/TextRenderer.d.ts +7 -0
  276. package/lib/typescript/src/streaming/renderers/TextRenderer.d.ts.map +1 -0
  277. package/lib/typescript/src/streaming/renderers/index.d.ts +19 -0
  278. package/lib/typescript/src/streaming/renderers/index.d.ts.map +1 -0
  279. package/lib/typescript/src/streaming/renderers/renderInlineChildren.d.ts +12 -0
  280. package/lib/typescript/src/streaming/renderers/renderInlineChildren.d.ts.map +1 -0
  281. package/lib/typescript/src/streaming/renderers/renderNode.d.ts +8 -0
  282. package/lib/typescript/src/streaming/renderers/renderNode.d.ts.map +1 -0
  283. package/lib/typescript/src/theme/ThemeProvider.d.ts +14 -0
  284. package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -0
  285. package/lib/typescript/src/theme/defaultTheme.d.ts +4 -0
  286. package/lib/typescript/src/theme/defaultTheme.d.ts.map +1 -0
  287. package/lib/typescript/src/theme/index.d.ts +5 -0
  288. package/lib/typescript/src/theme/index.d.ts.map +1 -0
  289. package/lib/typescript/src/theme/tokens.d.ts +66 -0
  290. package/lib/typescript/src/theme/tokens.d.ts.map +1 -0
  291. package/lib/typescript/src/voice/index.d.ts +3 -0
  292. package/lib/typescript/src/voice/index.d.ts.map +1 -0
  293. package/lib/typescript/src/voice/useSpeechRecognition.d.ts +77 -0
  294. package/lib/typescript/src/voice/useSpeechRecognition.d.ts.map +1 -0
  295. package/package.json +132 -0
  296. package/src/chatbot/AIImage.tsx +166 -0
  297. package/src/chatbot/Attachments.tsx +382 -0
  298. package/src/chatbot/ChatErrorBoundary.tsx +230 -0
  299. package/src/chatbot/ChatMessageItem.tsx +195 -0
  300. package/src/chatbot/Conversation.tsx +537 -0
  301. package/src/chatbot/ConversationScrollButton.tsx +149 -0
  302. package/src/chatbot/Message.tsx +266 -0
  303. package/src/chatbot/PromptInput.tsx +532 -0
  304. package/src/chatbot/Reasoning.tsx +198 -0
  305. package/src/chatbot/Shimmer.tsx +146 -0
  306. package/src/chatbot/Sources.tsx +263 -0
  307. package/src/chatbot/Suggestion.tsx +123 -0
  308. package/src/chatbot/Tool.tsx +340 -0
  309. package/src/chatbot/adapters/uiMessageAdapter.ts +177 -0
  310. package/src/chatbot/index.ts +97 -0
  311. package/src/chatbot/types.ts +66 -0
  312. package/src/hooks/index.ts +17 -0
  313. package/src/hooks/useAutoScroll.ts +43 -0
  314. package/src/hooks/useClipboard.ts +46 -0
  315. package/src/hooks/useCollapsible.ts +42 -0
  316. package/src/hooks/useStickToBottom.ts +82 -0
  317. package/src/index.ts +139 -0
  318. package/src/primitives/Badge.tsx +119 -0
  319. package/src/primitives/Button.tsx +213 -0
  320. package/src/primitives/Card.tsx +221 -0
  321. package/src/primitives/Collapsible.tsx +168 -0
  322. package/src/primitives/ScrollArea.tsx +53 -0
  323. package/src/primitives/index.ts +36 -0
  324. package/src/streaming/StreamingMarkdown.tsx +282 -0
  325. package/src/streaming/index.ts +2 -0
  326. package/src/streaming/parser.ts +506 -0
  327. package/src/streaming/renderers/BlockquoteRenderer.tsx +42 -0
  328. package/src/streaming/renderers/CodeRenderer.tsx +158 -0
  329. package/src/streaming/renderers/HeadingRenderer.tsx +64 -0
  330. package/src/streaming/renderers/ImageRenderer.tsx +62 -0
  331. package/src/streaming/renderers/LinkRenderer.tsx +53 -0
  332. package/src/streaming/renderers/ListRenderer.tsx +65 -0
  333. package/src/streaming/renderers/TableRenderer.tsx +103 -0
  334. package/src/streaming/renderers/TextRenderer.tsx +39 -0
  335. package/src/streaming/renderers/index.ts +26 -0
  336. package/src/streaming/renderers/renderInlineChildren.tsx +115 -0
  337. package/src/streaming/renderers/renderNode.tsx +72 -0
  338. package/src/theme/ThemeProvider.tsx +77 -0
  339. package/src/theme/defaultTheme.ts +93 -0
  340. package/src/theme/index.ts +4 -0
  341. package/src/theme/tokens.ts +69 -0
  342. package/src/types.d.ts +71 -0
  343. package/src/voice/index.ts +15 -0
  344. package/src/voice/useSpeechRecognition.ts +230 -0
@@ -0,0 +1,506 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Incremental markdown parser -- zero dependencies, streaming-aware.
3
+ // Designed for AI chat output: pragmatic, not spec-complete.
4
+ // ---------------------------------------------------------------------------
5
+
6
+ export type MarkdownNodeType =
7
+ | 'paragraph'
8
+ | 'heading'
9
+ | 'text'
10
+ | 'bold'
11
+ | 'italic'
12
+ | 'bold_italic'
13
+ | 'code_inline'
14
+ | 'code_block'
15
+ | 'link'
16
+ | 'image'
17
+ | 'list'
18
+ | 'list_item'
19
+ | 'blockquote'
20
+ | 'horizontal_rule'
21
+ | 'table'
22
+ | 'table_row'
23
+ | 'table_cell';
24
+
25
+ export interface MarkdownNode {
26
+ type: MarkdownNodeType;
27
+ content?: string;
28
+ children?: MarkdownNode[];
29
+ /** Heading level 1-6 */
30
+ level?: number;
31
+ /** Code block language hint */
32
+ language?: string;
33
+ /** Link / image URL */
34
+ url?: string;
35
+ /** Image alt text */
36
+ alt?: string;
37
+ /** Whether a list is ordered */
38
+ ordered?: boolean;
39
+ /** Whether a table cell is a header cell */
40
+ header?: boolean;
41
+ }
42
+
43
+ export interface ParseResult {
44
+ /** Fully-parsed AST nodes */
45
+ nodes: MarkdownNode[];
46
+ /** true when trailing content looks like an unclosed block */
47
+ isStreaming: boolean;
48
+ /** Index up to which the text has been safely consumed */
49
+ lastSafeIndex: number;
50
+ }
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Helpers
54
+ // ---------------------------------------------------------------------------
55
+
56
+ /** Split text into lines, preserving trailing empty line when text ends with \n */
57
+ function splitLines(text: string): string[] {
58
+ return text.split('\n');
59
+ }
60
+
61
+ /** Check if a line is a horizontal rule (---, ***, ___) */
62
+ function isHorizontalRule(line: string): boolean {
63
+ const trimmed = line.trim();
64
+ return /^[-*_]{3,}$/.test(trimmed) && /^(-{3,}|\*{3,}|_{3,})$/.test(trimmed);
65
+ }
66
+
67
+ /** Check if a line opens or closes a fenced code block */
68
+ function isFenceLine(line: string): { isFence: boolean; language: string } {
69
+ const match = line.match(/^```(\w*)\s*$/);
70
+ if (match) return { isFence: true, language: match[1] };
71
+ return { isFence: false, language: '' };
72
+ }
73
+
74
+ /** Detect heading line. Returns level (1-6) or 0. */
75
+ function headingLevel(line: string): number {
76
+ const match = line.match(/^(#{1,6})\s+/);
77
+ return match ? match[1].length : 0;
78
+ }
79
+
80
+ /** Detect unordered list item. Returns the content after the bullet or null. */
81
+ function unorderedListItem(line: string): string | null {
82
+ const match = line.match(/^(\s*)[-*+]\s+(.*)/);
83
+ return match ? match[2] : null;
84
+ }
85
+
86
+ /** Detect ordered list item. Returns content or null. */
87
+ function orderedListItem(line: string): string | null {
88
+ const match = line.match(/^(\s*)\d+[.)]\s+(.*)/);
89
+ return match ? match[2] : null;
90
+ }
91
+
92
+ /** Detect blockquote line. Returns inner content or null. */
93
+ function blockquoteContent(line: string): string | null {
94
+ const match = line.match(/^>\s?(.*)/);
95
+ return match ? match[1] : null;
96
+ }
97
+
98
+ /** Detect a table row (starts and ends with |, or starts with |). */
99
+ function isTableRow(line: string): boolean {
100
+ const trimmed = line.trim();
101
+ return trimmed.startsWith('|') && trimmed.endsWith('|');
102
+ }
103
+
104
+ /** Check if a table row is a separator row (| --- | --- |). */
105
+ function isTableSeparator(line: string): boolean {
106
+ const trimmed = line.trim();
107
+ if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) return false;
108
+ const inner = trimmed.slice(1, -1);
109
+ const cells = inner.split('|');
110
+ return cells.every((c) => /^\s*:?-{2,}:?\s*$/.test(c));
111
+ }
112
+
113
+ /** Parse cells from a table row string. */
114
+ function parseTableCells(line: string): string[] {
115
+ const trimmed = line.trim();
116
+ const inner = trimmed.startsWith('|') ? trimmed.slice(1) : trimmed;
117
+ const withoutTrailing = inner.endsWith('|') ? inner.slice(0, -1) : inner;
118
+ return withoutTrailing.split('|').map((c) => c.trim());
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // Inline parsing
123
+ // ---------------------------------------------------------------------------
124
+
125
+ /**
126
+ * Parse inline markdown (bold, italic, bold-italic, code, links, images)
127
+ * into a flat list of MarkdownNode children.
128
+ */
129
+ function parseInline(text: string): MarkdownNode[] {
130
+ const nodes: MarkdownNode[] = [];
131
+ let i = 0;
132
+ let buffer = '';
133
+
134
+ const flush = () => {
135
+ if (buffer.length > 0) {
136
+ nodes.push({ type: 'text', content: buffer });
137
+ buffer = '';
138
+ }
139
+ };
140
+
141
+ while (i < text.length) {
142
+ // --- Images: ![alt](url) ---
143
+ if (text[i] === '!' && text[i + 1] === '[') {
144
+ const altClose = text.indexOf(']', i + 2);
145
+ if (altClose !== -1 && text[altClose + 1] === '(') {
146
+ const urlClose = text.indexOf(')', altClose + 2);
147
+ if (urlClose !== -1) {
148
+ flush();
149
+ const alt = text.slice(i + 2, altClose);
150
+ const url = text.slice(altClose + 2, urlClose);
151
+ nodes.push({ type: 'image', alt, url });
152
+ i = urlClose + 1;
153
+ continue;
154
+ }
155
+ }
156
+ }
157
+
158
+ // --- Links: [text](url) ---
159
+ if (text[i] === '[') {
160
+ const labelClose = text.indexOf(']', i + 1);
161
+ if (labelClose !== -1 && text[labelClose + 1] === '(') {
162
+ const urlClose = text.indexOf(')', labelClose + 2);
163
+ if (urlClose !== -1) {
164
+ flush();
165
+ const label = text.slice(i + 1, labelClose);
166
+ const url = text.slice(labelClose + 2, urlClose);
167
+ nodes.push({
168
+ type: 'link',
169
+ url,
170
+ children: parseInline(label),
171
+ });
172
+ i = urlClose + 1;
173
+ continue;
174
+ }
175
+ }
176
+ }
177
+
178
+ // --- Inline code: `...` ---
179
+ if (text[i] === '`') {
180
+ const end = text.indexOf('`', i + 1);
181
+ if (end !== -1) {
182
+ flush();
183
+ nodes.push({ type: 'code_inline', content: text.slice(i + 1, end) });
184
+ i = end + 1;
185
+ continue;
186
+ }
187
+ }
188
+
189
+ // --- Bold-italic: ***...*** or ___...___ ---
190
+ if (
191
+ (text[i] === '*' && text[i + 1] === '*' && text[i + 2] === '*') ||
192
+ (text[i] === '_' && text[i + 1] === '_' && text[i + 2] === '_')
193
+ ) {
194
+ const marker = text.slice(i, i + 3);
195
+ const end = text.indexOf(marker, i + 3);
196
+ if (end !== -1) {
197
+ flush();
198
+ nodes.push({
199
+ type: 'bold_italic',
200
+ children: parseInline(text.slice(i + 3, end)),
201
+ });
202
+ i = end + 3;
203
+ continue;
204
+ }
205
+ }
206
+
207
+ // --- Bold: **...** or __...__ ---
208
+ if (
209
+ (text[i] === '*' && text[i + 1] === '*') ||
210
+ (text[i] === '_' && text[i + 1] === '_')
211
+ ) {
212
+ const marker = text.slice(i, i + 2);
213
+ const end = text.indexOf(marker, i + 2);
214
+ if (end !== -1) {
215
+ flush();
216
+ nodes.push({
217
+ type: 'bold',
218
+ children: parseInline(text.slice(i + 2, end)),
219
+ });
220
+ i = end + 2;
221
+ continue;
222
+ }
223
+ }
224
+
225
+ // --- Italic: *...* or _..._ ---
226
+ if (text[i] === '*' || text[i] === '_') {
227
+ const marker = text[i];
228
+ const end = text.indexOf(marker, i + 1);
229
+ if (end !== -1 && end > i + 1) {
230
+ flush();
231
+ nodes.push({
232
+ type: 'italic',
233
+ children: parseInline(text.slice(i + 1, end)),
234
+ });
235
+ i = end + 1;
236
+ continue;
237
+ }
238
+ }
239
+
240
+ buffer += text[i];
241
+ i++;
242
+ }
243
+
244
+ flush();
245
+ return nodes;
246
+ }
247
+
248
+ // ---------------------------------------------------------------------------
249
+ // Block-level parsing
250
+ // ---------------------------------------------------------------------------
251
+
252
+ /**
253
+ * Group consecutive lines of the same block type. Returns parsed nodes.
254
+ * `lines` must NOT include a trailing empty string from a final newline
255
+ * unless it is meaningful.
256
+ */
257
+ function parseBlocks(lines: string[]): MarkdownNode[] {
258
+ const nodes: MarkdownNode[] = [];
259
+ let i = 0;
260
+
261
+ while (i < lines.length) {
262
+ const line = lines[i];
263
+
264
+ // --- Empty line: skip ---
265
+ if (line.trim() === '') {
266
+ i++;
267
+ continue;
268
+ }
269
+
270
+ // --- Fenced code block ---
271
+ const fence = isFenceLine(line);
272
+ if (fence.isFence) {
273
+ const codeLines: string[] = [];
274
+ i++;
275
+ let closed = false;
276
+ while (i < lines.length) {
277
+ if (isFenceLine(lines[i]).isFence && lines[i].trim() === '```') {
278
+ closed = true;
279
+ i++;
280
+ break;
281
+ }
282
+ codeLines.push(lines[i]);
283
+ i++;
284
+ }
285
+ nodes.push({
286
+ type: 'code_block',
287
+ content: codeLines.join('\n'),
288
+ language: fence.language || undefined,
289
+ });
290
+ // If not closed, the outer caller marks isStreaming via lastSafeIndex
291
+ if (!closed) {
292
+ // Return what we have; caller will detect incomplete state
293
+ }
294
+ continue;
295
+ }
296
+
297
+ // --- Horizontal rule ---
298
+ if (isHorizontalRule(line)) {
299
+ nodes.push({ type: 'horizontal_rule' });
300
+ i++;
301
+ continue;
302
+ }
303
+
304
+ // --- Heading ---
305
+ const hl = headingLevel(line);
306
+ if (hl > 0) {
307
+ const content = line.replace(/^#{1,6}\s+/, '');
308
+ nodes.push({
309
+ type: 'heading',
310
+ level: hl,
311
+ children: parseInline(content),
312
+ });
313
+ i++;
314
+ continue;
315
+ }
316
+
317
+ // --- Table ---
318
+ if (isTableRow(line)) {
319
+ const tableRows: MarkdownNode[] = [];
320
+ let isHeader = true;
321
+ while (i < lines.length && isTableRow(lines[i])) {
322
+ if (isTableSeparator(lines[i])) {
323
+ // Separator row: skip it, next rows are body
324
+ isHeader = false;
325
+ i++;
326
+ continue;
327
+ }
328
+ const cellTexts = parseTableCells(lines[i]);
329
+ const cells: MarkdownNode[] = cellTexts.map((ct) => ({
330
+ type: 'table_cell' as const,
331
+ header: isHeader,
332
+ children: parseInline(ct),
333
+ }));
334
+ tableRows.push({ type: 'table_row', children: cells });
335
+ i++;
336
+ }
337
+ nodes.push({ type: 'table', children: tableRows });
338
+ continue;
339
+ }
340
+
341
+ // --- Blockquote ---
342
+ const bqContent = blockquoteContent(line);
343
+ if (bqContent !== null) {
344
+ const bqLines: string[] = [bqContent];
345
+ i++;
346
+ while (i < lines.length) {
347
+ const next = blockquoteContent(lines[i]);
348
+ if (next !== null) {
349
+ bqLines.push(next);
350
+ i++;
351
+ } else {
352
+ break;
353
+ }
354
+ }
355
+ nodes.push({
356
+ type: 'blockquote',
357
+ children: parseBlocks(bqLines),
358
+ });
359
+ continue;
360
+ }
361
+
362
+ // --- Unordered list ---
363
+ const ulContent = unorderedListItem(line);
364
+ if (ulContent !== null) {
365
+ const items: MarkdownNode[] = [];
366
+ while (i < lines.length) {
367
+ const itemContent = unorderedListItem(lines[i]);
368
+ if (itemContent !== null) {
369
+ items.push({
370
+ type: 'list_item',
371
+ children: parseInline(itemContent),
372
+ });
373
+ i++;
374
+ } else if (lines[i].trim() === '') {
375
+ // Allow a single blank line within a list
376
+ i++;
377
+ } else {
378
+ break;
379
+ }
380
+ }
381
+ nodes.push({ type: 'list', ordered: false, children: items });
382
+ continue;
383
+ }
384
+
385
+ // --- Ordered list ---
386
+ const olContent = orderedListItem(line);
387
+ if (olContent !== null) {
388
+ const items: MarkdownNode[] = [];
389
+ while (i < lines.length) {
390
+ const itemContent = orderedListItem(lines[i]);
391
+ if (itemContent !== null) {
392
+ items.push({
393
+ type: 'list_item',
394
+ children: parseInline(itemContent),
395
+ });
396
+ i++;
397
+ } else if (lines[i].trim() === '') {
398
+ i++;
399
+ } else {
400
+ break;
401
+ }
402
+ }
403
+ nodes.push({ type: 'list', ordered: true, children: items });
404
+ continue;
405
+ }
406
+
407
+ // --- Paragraph (default) ---
408
+ // Collect consecutive non-blank, non-special lines.
409
+ const paraLines: string[] = [line];
410
+ i++;
411
+ while (i < lines.length) {
412
+ const next = lines[i];
413
+ if (
414
+ next.trim() === '' ||
415
+ headingLevel(next) > 0 ||
416
+ isFenceLine(next).isFence ||
417
+ isHorizontalRule(next) ||
418
+ unorderedListItem(next) !== null ||
419
+ orderedListItem(next) !== null ||
420
+ blockquoteContent(next) !== null ||
421
+ isTableRow(next)
422
+ ) {
423
+ break;
424
+ }
425
+ paraLines.push(next);
426
+ i++;
427
+ }
428
+ nodes.push({
429
+ type: 'paragraph',
430
+ children: parseInline(paraLines.join('\n')),
431
+ });
432
+ }
433
+
434
+ return nodes;
435
+ }
436
+
437
+ // ---------------------------------------------------------------------------
438
+ // Streaming-awareness helpers
439
+ // ---------------------------------------------------------------------------
440
+
441
+ /**
442
+ * Find the last "safe" index in the source text: the position up to which
443
+ * all blocks are fully closed. An unclosed fenced code block means we
444
+ * are still streaming.
445
+ */
446
+ function computeSafeIndex(text: string): { lastSafeIndex: number; isStreaming: boolean } {
447
+ let fenceOpen = false;
448
+ let lastSafeIndex = 0;
449
+ const lines = splitLines(text);
450
+ let charIndex = 0;
451
+
452
+ for (let i = 0; i < lines.length; i++) {
453
+ const lineLength = lines[i].length;
454
+ const lineEnd = charIndex + lineLength + (i < lines.length - 1 ? 1 : 0); // +1 for \n
455
+
456
+ if (fenceOpen) {
457
+ if (lines[i].trim() === '```') {
458
+ fenceOpen = false;
459
+ lastSafeIndex = lineEnd;
460
+ }
461
+ } else {
462
+ const fence = isFenceLine(lines[i]);
463
+ if (fence.isFence) {
464
+ fenceOpen = true;
465
+ } else {
466
+ lastSafeIndex = lineEnd;
467
+ }
468
+ }
469
+
470
+ charIndex = lineEnd;
471
+ }
472
+
473
+ return { lastSafeIndex, isStreaming: fenceOpen };
474
+ }
475
+
476
+ // ---------------------------------------------------------------------------
477
+ // Public API
478
+ // ---------------------------------------------------------------------------
479
+
480
+ /**
481
+ * Parse a complete markdown string into an AST.
482
+ * Suitable for non-streaming use cases.
483
+ */
484
+ export function parseMarkdown(text: string): MarkdownNode[] {
485
+ const lines = splitLines(text);
486
+ return parseBlocks(lines);
487
+ }
488
+
489
+ /**
490
+ * Incremental streaming-aware parse.
491
+ *
492
+ * When `previousResult` is supplied the parser can skip re-parsing blocks
493
+ * that haven't changed (those before `previousResult.lastSafeIndex`).
494
+ * In practice, for simplicity in v1, we re-parse the full text but still
495
+ * return `isStreaming` and `lastSafeIndex` so the renderer can optimise.
496
+ */
497
+ export function parseMarkdownIncremental(
498
+ text: string,
499
+ _previousResult?: ParseResult,
500
+ ): ParseResult {
501
+ const { lastSafeIndex, isStreaming } = computeSafeIndex(text);
502
+ const lines = splitLines(text);
503
+ const nodes = parseBlocks(lines);
504
+
505
+ return { nodes, isStreaming, lastSafeIndex };
506
+ }
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { useAIElementsTheme } from '../../theme';
4
+ import type { MarkdownNode } from '../parser';
5
+ import { renderNode } from './renderNode';
6
+
7
+ export interface BlockquoteRendererProps {
8
+ node: MarkdownNode;
9
+ }
10
+
11
+ export const BlockquoteRenderer = React.memo(function BlockquoteRenderer({
12
+ node,
13
+ }: BlockquoteRendererProps) {
14
+ const theme = useAIElementsTheme();
15
+
16
+ return (
17
+ <View
18
+ style={[
19
+ styles.container,
20
+ {
21
+ borderLeftColor: theme.colors.border,
22
+ backgroundColor: theme.colors.muted,
23
+ borderRadius: theme.radius.sm,
24
+ },
25
+ ]}
26
+ >
27
+ {(node.children ?? []).map((child, index) =>
28
+ renderNode(child, `bq-${index}`),
29
+ )}
30
+ </View>
31
+ );
32
+ });
33
+
34
+ const styles = StyleSheet.create({
35
+ container: {
36
+ borderLeftWidth: 3,
37
+ paddingLeft: 12,
38
+ paddingVertical: 8,
39
+ paddingRight: 8,
40
+ marginVertical: 8,
41
+ },
42
+ });
@@ -0,0 +1,158 @@
1
+ import React, { useCallback } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ ScrollView,
6
+ Pressable,
7
+ StyleSheet,
8
+ type TextStyle,
9
+ } from 'react-native';
10
+ import { useAIElementsTheme } from '../../theme';
11
+ import { useClipboard } from '../../hooks/useClipboard';
12
+ import type { MarkdownNode } from '../parser';
13
+
14
+ export interface CodeRendererProps {
15
+ node: MarkdownNode;
16
+ }
17
+
18
+ export const CodeRenderer = React.memo(function CodeRenderer({
19
+ node,
20
+ }: CodeRendererProps) {
21
+ const theme = useAIElementsTheme();
22
+ const { copy, copied } = useClipboard();
23
+
24
+ const handleCopy = useCallback(() => {
25
+ if (node.content) {
26
+ copy(node.content);
27
+ }
28
+ }, [copy, node.content]);
29
+
30
+ // --- Inline code ---
31
+ if (node.type === 'code_inline') {
32
+ const inlineStyle: TextStyle = {
33
+ fontFamily: theme.typography.monoFontFamily,
34
+ fontSize: theme.typography.fontSize.sm,
35
+ backgroundColor: theme.colors.muted,
36
+ color: theme.colors.foreground,
37
+ borderRadius: theme.radius.sm,
38
+ paddingHorizontal: 4,
39
+ paddingVertical: 1,
40
+ };
41
+ return <Text style={inlineStyle}>{node.content}</Text>;
42
+ }
43
+
44
+ // --- Code block ---
45
+ return (
46
+ <View
47
+ style={[
48
+ styles.container,
49
+ {
50
+ backgroundColor: theme.colors.muted,
51
+ borderRadius: theme.radius.md,
52
+ borderColor: theme.colors.border,
53
+ },
54
+ ]}
55
+ >
56
+ {/* Header with language + copy button */}
57
+ <View
58
+ style={[
59
+ styles.header,
60
+ {
61
+ borderBottomColor: theme.colors.border,
62
+ },
63
+ ]}
64
+ >
65
+ <Text
66
+ style={[
67
+ styles.language,
68
+ {
69
+ color: theme.colors.mutedForeground,
70
+ fontFamily: theme.typography.monoFontFamily,
71
+ fontSize: theme.typography.fontSize.xs,
72
+ },
73
+ ]}
74
+ >
75
+ {node.language ?? ''}
76
+ </Text>
77
+ <Pressable
78
+ onPress={handleCopy}
79
+ hitSlop={8}
80
+ style={({ pressed }) => [
81
+ styles.copyButton,
82
+ {
83
+ backgroundColor: pressed
84
+ ? theme.colors.border
85
+ : 'transparent',
86
+ borderRadius: theme.radius.sm,
87
+ },
88
+ ]}
89
+ >
90
+ <Text
91
+ style={{
92
+ color: theme.colors.mutedForeground,
93
+ fontSize: theme.typography.fontSize.xs,
94
+ fontFamily: theme.typography.fontFamily,
95
+ }}
96
+ >
97
+ {copied ? 'Copied!' : 'Copy'}
98
+ </Text>
99
+ </Pressable>
100
+ </View>
101
+
102
+ {/* Code content */}
103
+ <ScrollView
104
+ horizontal
105
+ showsHorizontalScrollIndicator={false}
106
+ style={styles.scrollView}
107
+ contentContainerStyle={styles.scrollContent}
108
+ >
109
+ <Text
110
+ style={[
111
+ styles.code,
112
+ {
113
+ color: theme.colors.foreground,
114
+ fontFamily: theme.typography.monoFontFamily,
115
+ fontSize: theme.typography.fontSize.sm,
116
+ lineHeight:
117
+ theme.typography.fontSize.sm * theme.typography.lineHeight.normal,
118
+ },
119
+ ]}
120
+ >
121
+ {node.content}
122
+ </Text>
123
+ </ScrollView>
124
+ </View>
125
+ );
126
+ });
127
+
128
+ const styles = StyleSheet.create({
129
+ container: {
130
+ marginVertical: 8,
131
+ borderWidth: 1,
132
+ overflow: 'hidden',
133
+ },
134
+ header: {
135
+ flexDirection: 'row',
136
+ justifyContent: 'space-between',
137
+ alignItems: 'center',
138
+ paddingHorizontal: 12,
139
+ paddingVertical: 6,
140
+ borderBottomWidth: 1,
141
+ },
142
+ language: {
143
+ textTransform: 'lowercase',
144
+ },
145
+ copyButton: {
146
+ paddingHorizontal: 8,
147
+ paddingVertical: 4,
148
+ },
149
+ scrollView: {
150
+ flexGrow: 0,
151
+ },
152
+ scrollContent: {
153
+ padding: 12,
154
+ },
155
+ code: {
156
+ // fontFamily and fontSize applied dynamically
157
+ },
158
+ });