@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.
- package/lib/commonjs/chatbot/AIImage.js +126 -0
- package/lib/commonjs/chatbot/AIImage.js.map +1 -0
- package/lib/commonjs/chatbot/Attachments.js +317 -0
- package/lib/commonjs/chatbot/Attachments.js.map +1 -0
- package/lib/commonjs/chatbot/ChatErrorBoundary.js +201 -0
- package/lib/commonjs/chatbot/ChatErrorBoundary.js.map +1 -0
- package/lib/commonjs/chatbot/ChatMessageItem.js +169 -0
- package/lib/commonjs/chatbot/ChatMessageItem.js.map +1 -0
- package/lib/commonjs/chatbot/Conversation.js +415 -0
- package/lib/commonjs/chatbot/Conversation.js.map +1 -0
- package/lib/commonjs/chatbot/ConversationScrollButton.js +131 -0
- package/lib/commonjs/chatbot/ConversationScrollButton.js.map +1 -0
- package/lib/commonjs/chatbot/Message.js +203 -0
- package/lib/commonjs/chatbot/Message.js.map +1 -0
- package/lib/commonjs/chatbot/PromptInput.js +352 -0
- package/lib/commonjs/chatbot/PromptInput.js.map +1 -0
- package/lib/commonjs/chatbot/Reasoning.js +184 -0
- package/lib/commonjs/chatbot/Reasoning.js.map +1 -0
- package/lib/commonjs/chatbot/Shimmer.js +116 -0
- package/lib/commonjs/chatbot/Shimmer.js.map +1 -0
- package/lib/commonjs/chatbot/Sources.js +212 -0
- package/lib/commonjs/chatbot/Sources.js.map +1 -0
- package/lib/commonjs/chatbot/Suggestion.js +99 -0
- package/lib/commonjs/chatbot/Suggestion.js.map +1 -0
- package/lib/commonjs/chatbot/Tool.js +307 -0
- package/lib/commonjs/chatbot/Tool.js.map +1 -0
- package/lib/commonjs/chatbot/adapters/uiMessageAdapter.js +141 -0
- package/lib/commonjs/chatbot/adapters/uiMessageAdapter.js.map +1 -0
- package/lib/commonjs/chatbot/index.js +140 -0
- package/lib/commonjs/chatbot/index.js.map +1 -0
- package/lib/commonjs/chatbot/types.js +6 -0
- package/lib/commonjs/chatbot/types.js.map +1 -0
- package/lib/commonjs/hooks/index.js +34 -0
- package/lib/commonjs/hooks/index.js.map +1 -0
- package/lib/commonjs/hooks/useAutoScroll.js +39 -0
- package/lib/commonjs/hooks/useAutoScroll.js.map +1 -0
- package/lib/commonjs/hooks/useClipboard.js +44 -0
- package/lib/commonjs/hooks/useClipboard.js.map +1 -0
- package/lib/commonjs/hooks/useCollapsible.js +35 -0
- package/lib/commonjs/hooks/useCollapsible.js.map +1 -0
- package/lib/commonjs/hooks/useStickToBottom.js +68 -0
- package/lib/commonjs/hooks/useStickToBottom.js.map +1 -0
- package/lib/commonjs/index.js +257 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/primitives/Badge.js +119 -0
- package/lib/commonjs/primitives/Badge.js.map +1 -0
- package/lib/commonjs/primitives/Button.js +185 -0
- package/lib/commonjs/primitives/Button.js.map +1 -0
- package/lib/commonjs/primitives/Card.js +166 -0
- package/lib/commonjs/primitives/Card.js.map +1 -0
- package/lib/commonjs/primitives/Collapsible.js +137 -0
- package/lib/commonjs/primitives/Collapsible.js.map +1 -0
- package/lib/commonjs/primitives/ScrollArea.js +40 -0
- package/lib/commonjs/primitives/ScrollArea.js.map +1 -0
- package/lib/commonjs/primitives/index.js +83 -0
- package/lib/commonjs/primitives/index.js.map +1 -0
- package/lib/commonjs/streaming/StreamingMarkdown.js +252 -0
- package/lib/commonjs/streaming/StreamingMarkdown.js.map +1 -0
- package/lib/commonjs/streaming/index.js +13 -0
- package/lib/commonjs/streaming/index.js.map +1 -0
- package/lib/commonjs/streaming/parser.js +482 -0
- package/lib/commonjs/streaming/parser.js.map +1 -0
- package/lib/commonjs/streaming/renderers/BlockquoteRenderer.js +35 -0
- package/lib/commonjs/streaming/renderers/BlockquoteRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/CodeRenderer.js +128 -0
- package/lib/commonjs/streaming/renderers/CodeRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/HeadingRenderer.js +61 -0
- package/lib/commonjs/streaming/renderers/HeadingRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/ImageRenderer.js +53 -0
- package/lib/commonjs/streaming/renderers/ImageRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/LinkRenderer.js +49 -0
- package/lib/commonjs/streaming/renderers/LinkRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/ListRenderer.js +63 -0
- package/lib/commonjs/streaming/renderers/ListRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/TableRenderer.js +77 -0
- package/lib/commonjs/streaming/renderers/TableRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/TextRenderer.js +41 -0
- package/lib/commonjs/streaming/renderers/TextRenderer.js.map +1 -0
- package/lib/commonjs/streaming/renderers/index.js +76 -0
- package/lib/commonjs/streaming/renderers/index.js.map +1 -0
- package/lib/commonjs/streaming/renderers/renderInlineChildren.js +112 -0
- package/lib/commonjs/streaming/renderers/renderInlineChildren.js.map +1 -0
- package/lib/commonjs/streaming/renderers/renderNode.js +81 -0
- package/lib/commonjs/streaming/renderers/renderNode.js.map +1 -0
- package/lib/commonjs/theme/ThemeProvider.js +68 -0
- package/lib/commonjs/theme/ThemeProvider.js.map +1 -0
- package/lib/commonjs/theme/defaultTheme.js +96 -0
- package/lib/commonjs/theme/defaultTheme.js.map +1 -0
- package/lib/commonjs/theme/index.js +32 -0
- package/lib/commonjs/theme/index.js.map +1 -0
- package/lib/commonjs/theme/tokens.js +2 -0
- package/lib/commonjs/theme/tokens.js.map +1 -0
- package/lib/commonjs/types.d.js +2 -0
- package/lib/commonjs/types.d.js.map +1 -0
- package/lib/commonjs/voice/index.js +13 -0
- package/lib/commonjs/voice/index.js.map +1 -0
- package/lib/commonjs/voice/useSpeechRecognition.js +172 -0
- package/lib/commonjs/voice/useSpeechRecognition.js.map +1 -0
- package/lib/module/chatbot/AIImage.js +121 -0
- package/lib/module/chatbot/AIImage.js.map +1 -0
- package/lib/module/chatbot/Attachments.js +312 -0
- package/lib/module/chatbot/Attachments.js.map +1 -0
- package/lib/module/chatbot/ChatErrorBoundary.js +196 -0
- package/lib/module/chatbot/ChatErrorBoundary.js.map +1 -0
- package/lib/module/chatbot/ChatMessageItem.js +164 -0
- package/lib/module/chatbot/ChatMessageItem.js.map +1 -0
- package/lib/module/chatbot/Conversation.js +412 -0
- package/lib/module/chatbot/Conversation.js.map +1 -0
- package/lib/module/chatbot/ConversationScrollButton.js +126 -0
- package/lib/module/chatbot/ConversationScrollButton.js.map +1 -0
- package/lib/module/chatbot/Message.js +198 -0
- package/lib/module/chatbot/Message.js.map +1 -0
- package/lib/module/chatbot/PromptInput.js +347 -0
- package/lib/module/chatbot/PromptInput.js.map +1 -0
- package/lib/module/chatbot/Reasoning.js +179 -0
- package/lib/module/chatbot/Reasoning.js.map +1 -0
- package/lib/module/chatbot/Shimmer.js +111 -0
- package/lib/module/chatbot/Shimmer.js.map +1 -0
- package/lib/module/chatbot/Sources.js +207 -0
- package/lib/module/chatbot/Sources.js.map +1 -0
- package/lib/module/chatbot/Suggestion.js +94 -0
- package/lib/module/chatbot/Suggestion.js.map +1 -0
- package/lib/module/chatbot/Tool.js +303 -0
- package/lib/module/chatbot/Tool.js.map +1 -0
- package/lib/module/chatbot/adapters/uiMessageAdapter.js +137 -0
- package/lib/module/chatbot/adapters/uiMessageAdapter.js.map +1 -0
- package/lib/module/chatbot/index.js +39 -0
- package/lib/module/chatbot/index.js.map +1 -0
- package/lib/module/chatbot/types.js +4 -0
- package/lib/module/chatbot/types.js.map +1 -0
- package/lib/module/hooks/index.js +7 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/useAutoScroll.js +35 -0
- package/lib/module/hooks/useAutoScroll.js.map +1 -0
- package/lib/module/hooks/useClipboard.js +40 -0
- package/lib/module/hooks/useClipboard.js.map +1 -0
- package/lib/module/hooks/useCollapsible.js +31 -0
- package/lib/module/hooks/useCollapsible.js.map +1 -0
- package/lib/module/hooks/useStickToBottom.js +64 -0
- package/lib/module/hooks/useStickToBottom.js.map +1 -0
- package/lib/module/index.js +19 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/primitives/Badge.js +114 -0
- package/lib/module/primitives/Badge.js.map +1 -0
- package/lib/module/primitives/Button.js +180 -0
- package/lib/module/primitives/Button.js.map +1 -0
- package/lib/module/primitives/Card.js +156 -0
- package/lib/module/primitives/Card.js.map +1 -0
- package/lib/module/primitives/Collapsible.js +130 -0
- package/lib/module/primitives/Collapsible.js.map +1 -0
- package/lib/module/primitives/ScrollArea.js +35 -0
- package/lib/module/primitives/ScrollArea.js.map +1 -0
- package/lib/module/primitives/index.js +8 -0
- package/lib/module/primitives/index.js.map +1 -0
- package/lib/module/streaming/StreamingMarkdown.js +246 -0
- package/lib/module/streaming/StreamingMarkdown.js.map +1 -0
- package/lib/module/streaming/index.js +4 -0
- package/lib/module/streaming/index.js.map +1 -0
- package/lib/module/streaming/parser.js +477 -0
- package/lib/module/streaming/parser.js.map +1 -0
- package/lib/module/streaming/renderers/BlockquoteRenderer.js +30 -0
- package/lib/module/streaming/renderers/BlockquoteRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/CodeRenderer.js +123 -0
- package/lib/module/streaming/renderers/CodeRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/HeadingRenderer.js +56 -0
- package/lib/module/streaming/renderers/HeadingRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/ImageRenderer.js +48 -0
- package/lib/module/streaming/renderers/ImageRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/LinkRenderer.js +44 -0
- package/lib/module/streaming/renderers/LinkRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/ListRenderer.js +58 -0
- package/lib/module/streaming/renderers/ListRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/TableRenderer.js +72 -0
- package/lib/module/streaming/renderers/TableRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/TextRenderer.js +36 -0
- package/lib/module/streaming/renderers/TextRenderer.js.map +1 -0
- package/lib/module/streaming/renderers/index.js +13 -0
- package/lib/module/streaming/renderers/index.js.map +1 -0
- package/lib/module/streaming/renderers/renderInlineChildren.js +107 -0
- package/lib/module/streaming/renderers/renderInlineChildren.js.map +1 -0
- package/lib/module/streaming/renderers/renderNode.js +78 -0
- package/lib/module/streaming/renderers/renderNode.js.map +1 -0
- package/lib/module/theme/ThemeProvider.js +62 -0
- package/lib/module/theme/ThemeProvider.js.map +1 -0
- package/lib/module/theme/defaultTheme.js +92 -0
- package/lib/module/theme/defaultTheme.js.map +1 -0
- package/lib/module/theme/index.js +5 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/tokens.js +2 -0
- package/lib/module/theme/tokens.js.map +1 -0
- package/lib/module/types.d.js +2 -0
- package/lib/module/types.d.js.map +1 -0
- package/lib/module/voice/index.js +14 -0
- package/lib/module/voice/index.js.map +1 -0
- package/lib/module/voice/useSpeechRecognition.js +169 -0
- package/lib/module/voice/useSpeechRecognition.js.map +1 -0
- package/lib/typescript/src/chatbot/AIImage.d.ts +24 -0
- package/lib/typescript/src/chatbot/AIImage.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Attachments.d.ts +20 -0
- package/lib/typescript/src/chatbot/Attachments.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/ChatErrorBoundary.d.ts +57 -0
- package/lib/typescript/src/chatbot/ChatErrorBoundary.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/ChatMessageItem.d.ts +45 -0
- package/lib/typescript/src/chatbot/ChatMessageItem.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Conversation.d.ts +94 -0
- package/lib/typescript/src/chatbot/Conversation.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/ConversationScrollButton.d.ts +62 -0
- package/lib/typescript/src/chatbot/ConversationScrollButton.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Message.d.ts +39 -0
- package/lib/typescript/src/chatbot/Message.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/PromptInput.d.ts +93 -0
- package/lib/typescript/src/chatbot/PromptInput.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Reasoning.d.ts +14 -0
- package/lib/typescript/src/chatbot/Reasoning.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Shimmer.d.ts +13 -0
- package/lib/typescript/src/chatbot/Shimmer.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Sources.d.ts +17 -0
- package/lib/typescript/src/chatbot/Sources.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Suggestion.d.ts +15 -0
- package/lib/typescript/src/chatbot/Suggestion.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/Tool.d.ts +30 -0
- package/lib/typescript/src/chatbot/Tool.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/adapters/uiMessageAdapter.d.ts +24 -0
- package/lib/typescript/src/chatbot/adapters/uiMessageAdapter.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/index.d.ts +29 -0
- package/lib/typescript/src/chatbot/index.d.ts.map +1 -0
- package/lib/typescript/src/chatbot/types.d.ts +49 -0
- package/lib/typescript/src/chatbot/types.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +9 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useAutoScroll.d.ts +23 -0
- package/lib/typescript/src/hooks/useAutoScroll.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useClipboard.d.ts +22 -0
- package/lib/typescript/src/hooks/useClipboard.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useCollapsible.d.ts +28 -0
- package/lib/typescript/src/hooks/useCollapsible.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useStickToBottom.d.ts +39 -0
- package/lib/typescript/src/hooks/useStickToBottom.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +11 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Badge.d.ts +10 -0
- package/lib/typescript/src/primitives/Badge.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Button.d.ts +16 -0
- package/lib/typescript/src/primitives/Button.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Card.d.ts +33 -0
- package/lib/typescript/src/primitives/Card.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Collapsible.d.ts +20 -0
- package/lib/typescript/src/primitives/Collapsible.d.ts.map +1 -0
- package/lib/typescript/src/primitives/ScrollArea.d.ts +10 -0
- package/lib/typescript/src/primitives/ScrollArea.d.ts.map +1 -0
- package/lib/typescript/src/primitives/index.d.ts +11 -0
- package/lib/typescript/src/primitives/index.d.ts.map +1 -0
- package/lib/typescript/src/streaming/StreamingMarkdown.d.ts +47 -0
- package/lib/typescript/src/streaming/StreamingMarkdown.d.ts.map +1 -0
- package/lib/typescript/src/streaming/index.d.ts +3 -0
- package/lib/typescript/src/streaming/index.d.ts.map +1 -0
- package/lib/typescript/src/streaming/parser.d.ts +41 -0
- package/lib/typescript/src/streaming/parser.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/BlockquoteRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/BlockquoteRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/CodeRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/CodeRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/HeadingRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/HeadingRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/ImageRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/ImageRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/LinkRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/LinkRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/ListRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/ListRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/TableRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/TableRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/TextRenderer.d.ts +7 -0
- package/lib/typescript/src/streaming/renderers/TextRenderer.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/index.d.ts +19 -0
- package/lib/typescript/src/streaming/renderers/index.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/renderInlineChildren.d.ts +12 -0
- package/lib/typescript/src/streaming/renderers/renderInlineChildren.d.ts.map +1 -0
- package/lib/typescript/src/streaming/renderers/renderNode.d.ts +8 -0
- package/lib/typescript/src/streaming/renderers/renderNode.d.ts.map +1 -0
- package/lib/typescript/src/theme/ThemeProvider.d.ts +14 -0
- package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/src/theme/defaultTheme.d.ts +4 -0
- package/lib/typescript/src/theme/defaultTheme.d.ts.map +1 -0
- package/lib/typescript/src/theme/index.d.ts +5 -0
- package/lib/typescript/src/theme/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/tokens.d.ts +66 -0
- package/lib/typescript/src/theme/tokens.d.ts.map +1 -0
- package/lib/typescript/src/voice/index.d.ts +3 -0
- package/lib/typescript/src/voice/index.d.ts.map +1 -0
- package/lib/typescript/src/voice/useSpeechRecognition.d.ts +77 -0
- package/lib/typescript/src/voice/useSpeechRecognition.d.ts.map +1 -0
- package/package.json +132 -0
- package/src/chatbot/AIImage.tsx +166 -0
- package/src/chatbot/Attachments.tsx +382 -0
- package/src/chatbot/ChatErrorBoundary.tsx +230 -0
- package/src/chatbot/ChatMessageItem.tsx +195 -0
- package/src/chatbot/Conversation.tsx +537 -0
- package/src/chatbot/ConversationScrollButton.tsx +149 -0
- package/src/chatbot/Message.tsx +266 -0
- package/src/chatbot/PromptInput.tsx +532 -0
- package/src/chatbot/Reasoning.tsx +198 -0
- package/src/chatbot/Shimmer.tsx +146 -0
- package/src/chatbot/Sources.tsx +263 -0
- package/src/chatbot/Suggestion.tsx +123 -0
- package/src/chatbot/Tool.tsx +340 -0
- package/src/chatbot/adapters/uiMessageAdapter.ts +177 -0
- package/src/chatbot/index.ts +97 -0
- package/src/chatbot/types.ts +66 -0
- package/src/hooks/index.ts +17 -0
- package/src/hooks/useAutoScroll.ts +43 -0
- package/src/hooks/useClipboard.ts +46 -0
- package/src/hooks/useCollapsible.ts +42 -0
- package/src/hooks/useStickToBottom.ts +82 -0
- package/src/index.ts +139 -0
- package/src/primitives/Badge.tsx +119 -0
- package/src/primitives/Button.tsx +213 -0
- package/src/primitives/Card.tsx +221 -0
- package/src/primitives/Collapsible.tsx +168 -0
- package/src/primitives/ScrollArea.tsx +53 -0
- package/src/primitives/index.ts +36 -0
- package/src/streaming/StreamingMarkdown.tsx +282 -0
- package/src/streaming/index.ts +2 -0
- package/src/streaming/parser.ts +506 -0
- package/src/streaming/renderers/BlockquoteRenderer.tsx +42 -0
- package/src/streaming/renderers/CodeRenderer.tsx +158 -0
- package/src/streaming/renderers/HeadingRenderer.tsx +64 -0
- package/src/streaming/renderers/ImageRenderer.tsx +62 -0
- package/src/streaming/renderers/LinkRenderer.tsx +53 -0
- package/src/streaming/renderers/ListRenderer.tsx +65 -0
- package/src/streaming/renderers/TableRenderer.tsx +103 -0
- package/src/streaming/renderers/TextRenderer.tsx +39 -0
- package/src/streaming/renderers/index.ts +26 -0
- package/src/streaming/renderers/renderInlineChildren.tsx +115 -0
- package/src/streaming/renderers/renderNode.tsx +72 -0
- package/src/theme/ThemeProvider.tsx +77 -0
- package/src/theme/defaultTheme.ts +93 -0
- package/src/theme/index.ts +4 -0
- package/src/theme/tokens.ts +69 -0
- package/src/types.d.ts +71 -0
- package/src/voice/index.ts +15 -0
- package/src/voice/useSpeechRecognition.ts +230 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text, StyleSheet, type TextStyle } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { AIElementsTheme } from '../../theme';
|
|
5
|
+
import type { MarkdownNode } from '../parser';
|
|
6
|
+
import { renderInlineChildren } from './renderInlineChildren';
|
|
7
|
+
|
|
8
|
+
export interface HeadingRendererProps {
|
|
9
|
+
node: MarkdownNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function headingStyle(level: number, theme: AIElementsTheme): TextStyle {
|
|
13
|
+
const { typography, colors } = theme;
|
|
14
|
+
const weight = typography.fontWeight.bold as TextStyle['fontWeight'];
|
|
15
|
+
|
|
16
|
+
const sizeMap: Record<number, number> = {
|
|
17
|
+
1: typography.fontSize['2xl'],
|
|
18
|
+
2: typography.fontSize.xl,
|
|
19
|
+
3: typography.fontSize.lg,
|
|
20
|
+
4: typography.fontSize.md,
|
|
21
|
+
5: typography.fontSize.sm,
|
|
22
|
+
6: typography.fontSize.xs,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const marginMap: Record<number, number> = {
|
|
26
|
+
1: 20,
|
|
27
|
+
2: 16,
|
|
28
|
+
3: 14,
|
|
29
|
+
4: 12,
|
|
30
|
+
5: 10,
|
|
31
|
+
6: 8,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const fontSize = sizeMap[level] ?? typography.fontSize.md;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
fontSize,
|
|
38
|
+
fontWeight: weight,
|
|
39
|
+
fontFamily: typography.fontFamily,
|
|
40
|
+
color: colors.foreground,
|
|
41
|
+
lineHeight: fontSize * typography.lineHeight.tight,
|
|
42
|
+
marginTop: marginMap[level] ?? 12,
|
|
43
|
+
marginBottom: 8,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const HeadingRenderer = React.memo(function HeadingRenderer({
|
|
48
|
+
node,
|
|
49
|
+
}: HeadingRendererProps) {
|
|
50
|
+
const theme = useAIElementsTheme();
|
|
51
|
+
const level = node.level ?? 1;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Text style={[styles.base, headingStyle(level, theme)]}>
|
|
55
|
+
{renderInlineChildren(node.children, theme)}
|
|
56
|
+
</Text>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const styles = StyleSheet.create({
|
|
61
|
+
base: {
|
|
62
|
+
flexShrink: 1,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Image, View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
|
|
6
|
+
export interface ImageRendererProps {
|
|
7
|
+
node: MarkdownNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ImageRenderer = React.memo(function ImageRenderer({
|
|
11
|
+
node,
|
|
12
|
+
}: ImageRendererProps) {
|
|
13
|
+
const theme = useAIElementsTheme();
|
|
14
|
+
|
|
15
|
+
if (!node.url) return null;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<View style={styles.container}>
|
|
19
|
+
<Image
|
|
20
|
+
source={{ uri: node.url }}
|
|
21
|
+
style={[
|
|
22
|
+
styles.image,
|
|
23
|
+
{
|
|
24
|
+
borderRadius: theme.radius.md,
|
|
25
|
+
backgroundColor: theme.colors.muted,
|
|
26
|
+
},
|
|
27
|
+
]}
|
|
28
|
+
resizeMode="contain"
|
|
29
|
+
accessibilityLabel={node.alt ?? undefined}
|
|
30
|
+
/>
|
|
31
|
+
{node.alt ? (
|
|
32
|
+
<Text
|
|
33
|
+
style={[
|
|
34
|
+
styles.caption,
|
|
35
|
+
{
|
|
36
|
+
color: theme.colors.mutedForeground,
|
|
37
|
+
fontSize: theme.typography.fontSize.sm,
|
|
38
|
+
fontFamily: theme.typography.fontFamily,
|
|
39
|
+
},
|
|
40
|
+
]}
|
|
41
|
+
>
|
|
42
|
+
{node.alt}
|
|
43
|
+
</Text>
|
|
44
|
+
) : null}
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const styles = StyleSheet.create({
|
|
50
|
+
container: {
|
|
51
|
+
marginVertical: 8,
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
},
|
|
54
|
+
image: {
|
|
55
|
+
width: '100%',
|
|
56
|
+
height: 200,
|
|
57
|
+
},
|
|
58
|
+
caption: {
|
|
59
|
+
marginTop: 4,
|
|
60
|
+
textAlign: 'center',
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { Text, Pressable, Linking, StyleSheet, type TextStyle } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
import { renderInlineChildren } from './renderInlineChildren';
|
|
6
|
+
|
|
7
|
+
export interface LinkRendererProps {
|
|
8
|
+
node: MarkdownNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const LinkRenderer = React.memo(function LinkRenderer({
|
|
12
|
+
node,
|
|
13
|
+
}: LinkRendererProps) {
|
|
14
|
+
const theme = useAIElementsTheme();
|
|
15
|
+
|
|
16
|
+
const handlePress = useCallback(() => {
|
|
17
|
+
if (node.url) {
|
|
18
|
+
Linking.openURL(node.url).catch(() => {
|
|
19
|
+
// Silently ignore -- the URL may not be openable.
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}, [node.url]);
|
|
23
|
+
|
|
24
|
+
const textStyle: TextStyle = {
|
|
25
|
+
color: theme.colors.primary,
|
|
26
|
+
fontSize: theme.typography.fontSize.md,
|
|
27
|
+
fontFamily: theme.typography.fontFamily,
|
|
28
|
+
textDecorationLine: 'underline',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Pressable
|
|
33
|
+
onPress={handlePress}
|
|
34
|
+
style={({ pressed }) => [
|
|
35
|
+
styles.pressable,
|
|
36
|
+
pressed && styles.pressed,
|
|
37
|
+
]}
|
|
38
|
+
>
|
|
39
|
+
<Text style={textStyle}>
|
|
40
|
+
{renderInlineChildren(node.children, theme)}
|
|
41
|
+
</Text>
|
|
42
|
+
</Pressable>
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const styles = StyleSheet.create({
|
|
47
|
+
pressable: {
|
|
48
|
+
alignSelf: 'flex-start',
|
|
49
|
+
},
|
|
50
|
+
pressed: {
|
|
51
|
+
opacity: 0.7,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, type TextStyle } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
import { renderInlineChildren } from './renderInlineChildren';
|
|
6
|
+
|
|
7
|
+
export interface ListRendererProps {
|
|
8
|
+
node: MarkdownNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const ListRenderer = React.memo(function ListRenderer({
|
|
12
|
+
node,
|
|
13
|
+
}: ListRendererProps) {
|
|
14
|
+
const theme = useAIElementsTheme();
|
|
15
|
+
const ordered = node.ordered ?? false;
|
|
16
|
+
|
|
17
|
+
const bulletStyle: TextStyle = {
|
|
18
|
+
color: theme.colors.mutedForeground,
|
|
19
|
+
fontSize: theme.typography.fontSize.md,
|
|
20
|
+
fontFamily: theme.typography.fontFamily,
|
|
21
|
+
lineHeight: theme.typography.fontSize.md * theme.typography.lineHeight.normal,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const textStyle: TextStyle = {
|
|
25
|
+
color: theme.colors.foreground,
|
|
26
|
+
fontSize: theme.typography.fontSize.md,
|
|
27
|
+
fontFamily: theme.typography.fontFamily,
|
|
28
|
+
lineHeight: theme.typography.fontSize.md * theme.typography.lineHeight.normal,
|
|
29
|
+
flexShrink: 1,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<View style={styles.list}>
|
|
34
|
+
{(node.children ?? []).map((item, index) => (
|
|
35
|
+
<View key={`list-item-${index}`} style={styles.item}>
|
|
36
|
+
<Text style={[styles.bullet, bulletStyle]}>
|
|
37
|
+
{ordered ? `${index + 1}.` : '\u2022'}
|
|
38
|
+
</Text>
|
|
39
|
+
<Text style={[styles.content, textStyle]}>
|
|
40
|
+
{renderInlineChildren(item.children, theme)}
|
|
41
|
+
</Text>
|
|
42
|
+
</View>
|
|
43
|
+
))}
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const styles = StyleSheet.create({
|
|
49
|
+
list: {
|
|
50
|
+
marginVertical: 4,
|
|
51
|
+
},
|
|
52
|
+
item: {
|
|
53
|
+
flexDirection: 'row',
|
|
54
|
+
alignItems: 'flex-start',
|
|
55
|
+
marginBottom: 4,
|
|
56
|
+
},
|
|
57
|
+
bullet: {
|
|
58
|
+
width: 24,
|
|
59
|
+
textAlign: 'right',
|
|
60
|
+
marginRight: 8,
|
|
61
|
+
},
|
|
62
|
+
content: {
|
|
63
|
+
flex: 1,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, type TextStyle } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
import { renderInlineChildren } from './renderInlineChildren';
|
|
6
|
+
|
|
7
|
+
export interface TableRendererProps {
|
|
8
|
+
node: MarkdownNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const TableRenderer = React.memo(function TableRenderer({
|
|
12
|
+
node,
|
|
13
|
+
}: TableRendererProps) {
|
|
14
|
+
const theme = useAIElementsTheme();
|
|
15
|
+
|
|
16
|
+
const rows = node.children ?? [];
|
|
17
|
+
|
|
18
|
+
const headerTextStyle: TextStyle = {
|
|
19
|
+
fontWeight: theme.typography.fontWeight.semibold as TextStyle['fontWeight'],
|
|
20
|
+
color: theme.colors.foreground,
|
|
21
|
+
fontSize: theme.typography.fontSize.sm,
|
|
22
|
+
fontFamily: theme.typography.fontFamily,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const cellTextStyle: TextStyle = {
|
|
26
|
+
color: theme.colors.foreground,
|
|
27
|
+
fontSize: theme.typography.fontSize.sm,
|
|
28
|
+
fontFamily: theme.typography.fontFamily,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View
|
|
33
|
+
style={[
|
|
34
|
+
styles.table,
|
|
35
|
+
{
|
|
36
|
+
borderColor: theme.colors.border,
|
|
37
|
+
borderRadius: theme.radius.sm,
|
|
38
|
+
},
|
|
39
|
+
]}
|
|
40
|
+
>
|
|
41
|
+
{rows.map((row, rowIndex) => {
|
|
42
|
+
const cells = row.children ?? [];
|
|
43
|
+
const isHeaderRow = cells.some((c) => c.header);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<View
|
|
47
|
+
key={`row-${rowIndex}`}
|
|
48
|
+
style={[
|
|
49
|
+
styles.row,
|
|
50
|
+
{
|
|
51
|
+
borderBottomColor: theme.colors.border,
|
|
52
|
+
backgroundColor: isHeaderRow
|
|
53
|
+
? theme.colors.muted
|
|
54
|
+
: 'transparent',
|
|
55
|
+
},
|
|
56
|
+
rowIndex === rows.length - 1 && styles.lastRow,
|
|
57
|
+
]}
|
|
58
|
+
>
|
|
59
|
+
{cells.map((cell, cellIndex) => (
|
|
60
|
+
<View
|
|
61
|
+
key={`cell-${rowIndex}-${cellIndex}`}
|
|
62
|
+
style={[
|
|
63
|
+
styles.cell,
|
|
64
|
+
{
|
|
65
|
+
borderRightColor: theme.colors.border,
|
|
66
|
+
},
|
|
67
|
+
cellIndex === cells.length - 1 && styles.lastCell,
|
|
68
|
+
]}
|
|
69
|
+
>
|
|
70
|
+
<Text style={cell.header ? headerTextStyle : cellTextStyle}>
|
|
71
|
+
{renderInlineChildren(cell.children, theme)}
|
|
72
|
+
</Text>
|
|
73
|
+
</View>
|
|
74
|
+
))}
|
|
75
|
+
</View>
|
|
76
|
+
);
|
|
77
|
+
})}
|
|
78
|
+
</View>
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const styles = StyleSheet.create({
|
|
83
|
+
table: {
|
|
84
|
+
borderWidth: 1,
|
|
85
|
+
marginVertical: 8,
|
|
86
|
+
overflow: 'hidden',
|
|
87
|
+
},
|
|
88
|
+
row: {
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
borderBottomWidth: 1,
|
|
91
|
+
},
|
|
92
|
+
lastRow: {
|
|
93
|
+
borderBottomWidth: 0,
|
|
94
|
+
},
|
|
95
|
+
cell: {
|
|
96
|
+
flex: 1,
|
|
97
|
+
padding: 8,
|
|
98
|
+
borderRightWidth: 1,
|
|
99
|
+
},
|
|
100
|
+
lastCell: {
|
|
101
|
+
borderRightWidth: 0,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text, StyleSheet, type TextStyle } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
import { renderInlineChildren } from './renderInlineChildren';
|
|
6
|
+
|
|
7
|
+
export interface TextRendererProps {
|
|
8
|
+
node: MarkdownNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const TextRenderer = React.memo(function TextRenderer({
|
|
12
|
+
node,
|
|
13
|
+
}: TextRendererProps) {
|
|
14
|
+
const theme = useAIElementsTheme();
|
|
15
|
+
|
|
16
|
+
const baseStyle: TextStyle = {
|
|
17
|
+
color: theme.colors.foreground,
|
|
18
|
+
fontSize: theme.typography.fontSize.md,
|
|
19
|
+
lineHeight: theme.typography.fontSize.md * theme.typography.lineHeight.normal,
|
|
20
|
+
fontFamily: theme.typography.fontFamily,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (node.type === 'text') {
|
|
24
|
+
return <Text style={baseStyle}>{node.content}</Text>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// paragraph
|
|
28
|
+
return (
|
|
29
|
+
<Text style={[baseStyle, styles.paragraph]}>
|
|
30
|
+
{renderInlineChildren(node.children, theme)}
|
|
31
|
+
</Text>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const styles = StyleSheet.create({
|
|
36
|
+
paragraph: {
|
|
37
|
+
marginBottom: 8,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { TextRenderer } from './TextRenderer';
|
|
2
|
+
export type { TextRendererProps } from './TextRenderer';
|
|
3
|
+
|
|
4
|
+
export { HeadingRenderer } from './HeadingRenderer';
|
|
5
|
+
export type { HeadingRendererProps } from './HeadingRenderer';
|
|
6
|
+
|
|
7
|
+
export { CodeRenderer } from './CodeRenderer';
|
|
8
|
+
export type { CodeRendererProps } from './CodeRenderer';
|
|
9
|
+
|
|
10
|
+
export { ListRenderer } from './ListRenderer';
|
|
11
|
+
export type { ListRendererProps } from './ListRenderer';
|
|
12
|
+
|
|
13
|
+
export { LinkRenderer } from './LinkRenderer';
|
|
14
|
+
export type { LinkRendererProps } from './LinkRenderer';
|
|
15
|
+
|
|
16
|
+
export { ImageRenderer } from './ImageRenderer';
|
|
17
|
+
export type { ImageRendererProps } from './ImageRenderer';
|
|
18
|
+
|
|
19
|
+
export { BlockquoteRenderer } from './BlockquoteRenderer';
|
|
20
|
+
export type { BlockquoteRendererProps } from './BlockquoteRenderer';
|
|
21
|
+
|
|
22
|
+
export { TableRenderer } from './TableRenderer';
|
|
23
|
+
export type { TableRendererProps } from './TableRenderer';
|
|
24
|
+
|
|
25
|
+
export { renderNode } from './renderNode';
|
|
26
|
+
export { renderInlineChildren } from './renderInlineChildren';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react';
|
|
2
|
+
import { Text, type TextStyle } from 'react-native';
|
|
3
|
+
import type { AIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Recursively render inline markdown nodes (text, bold, italic, bold_italic,
|
|
8
|
+
* code_inline, link, image) as nested React Native <Text> elements.
|
|
9
|
+
*
|
|
10
|
+
* This is extracted as a shared helper so every block-level renderer can
|
|
11
|
+
* render its inline children consistently.
|
|
12
|
+
*/
|
|
13
|
+
export function renderInlineChildren(
|
|
14
|
+
children: MarkdownNode[] | undefined,
|
|
15
|
+
theme: AIElementsTheme,
|
|
16
|
+
): ReactNode {
|
|
17
|
+
if (!children || children.length === 0) return null;
|
|
18
|
+
|
|
19
|
+
return children.map((child, index) => {
|
|
20
|
+
const key = `${child.type}-${index}`;
|
|
21
|
+
|
|
22
|
+
switch (child.type) {
|
|
23
|
+
case 'text':
|
|
24
|
+
return (
|
|
25
|
+
<Text key={key} style={{ color: theme.colors.foreground }}>
|
|
26
|
+
{child.content}
|
|
27
|
+
</Text>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
case 'bold': {
|
|
31
|
+
const style: TextStyle = {
|
|
32
|
+
fontWeight: theme.typography.fontWeight.bold as TextStyle['fontWeight'],
|
|
33
|
+
color: theme.colors.foreground,
|
|
34
|
+
};
|
|
35
|
+
return (
|
|
36
|
+
<Text key={key} style={style}>
|
|
37
|
+
{renderInlineChildren(child.children, theme)}
|
|
38
|
+
</Text>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
case 'italic': {
|
|
43
|
+
const style: TextStyle = {
|
|
44
|
+
fontStyle: 'italic',
|
|
45
|
+
color: theme.colors.foreground,
|
|
46
|
+
};
|
|
47
|
+
return (
|
|
48
|
+
<Text key={key} style={style}>
|
|
49
|
+
{renderInlineChildren(child.children, theme)}
|
|
50
|
+
</Text>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'bold_italic': {
|
|
55
|
+
const style: TextStyle = {
|
|
56
|
+
fontWeight: theme.typography.fontWeight.bold as TextStyle['fontWeight'],
|
|
57
|
+
fontStyle: 'italic',
|
|
58
|
+
color: theme.colors.foreground,
|
|
59
|
+
};
|
|
60
|
+
return (
|
|
61
|
+
<Text key={key} style={style}>
|
|
62
|
+
{renderInlineChildren(child.children, theme)}
|
|
63
|
+
</Text>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case 'code_inline': {
|
|
68
|
+
const style: TextStyle = {
|
|
69
|
+
fontFamily: theme.typography.monoFontFamily,
|
|
70
|
+
fontSize: theme.typography.fontSize.sm,
|
|
71
|
+
backgroundColor: theme.colors.muted,
|
|
72
|
+
color: theme.colors.foreground,
|
|
73
|
+
borderRadius: theme.radius.sm,
|
|
74
|
+
paddingHorizontal: 4,
|
|
75
|
+
paddingVertical: 1,
|
|
76
|
+
};
|
|
77
|
+
return (
|
|
78
|
+
<Text key={key} style={style}>
|
|
79
|
+
{child.content}
|
|
80
|
+
</Text>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case 'link': {
|
|
85
|
+
const style: TextStyle = {
|
|
86
|
+
color: theme.colors.primary,
|
|
87
|
+
textDecorationLine: 'underline',
|
|
88
|
+
};
|
|
89
|
+
// Links inline are rendered as styled text.
|
|
90
|
+
// The LinkRenderer handles the Pressable wrapper at block level.
|
|
91
|
+
// Inside inline contexts we just show styled text.
|
|
92
|
+
return (
|
|
93
|
+
<Text key={key} style={style}>
|
|
94
|
+
{renderInlineChildren(child.children, theme)}
|
|
95
|
+
</Text>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
case 'image':
|
|
100
|
+
// Images inline are rendered as their alt text within a Text tree.
|
|
101
|
+
return (
|
|
102
|
+
<Text key={key} style={{ color: theme.colors.mutedForeground }}>
|
|
103
|
+
{child.alt ?? '[image]'}
|
|
104
|
+
</Text>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
default:
|
|
108
|
+
return (
|
|
109
|
+
<Text key={key} style={{ color: theme.colors.foreground }}>
|
|
110
|
+
{child.content}
|
|
111
|
+
</Text>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { useAIElementsTheme } from '../../theme';
|
|
4
|
+
import type { MarkdownNode } from '../parser';
|
|
5
|
+
import { TextRenderer } from './TextRenderer';
|
|
6
|
+
import { HeadingRenderer } from './HeadingRenderer';
|
|
7
|
+
import { CodeRenderer } from './CodeRenderer';
|
|
8
|
+
import { ListRenderer } from './ListRenderer';
|
|
9
|
+
import { LinkRenderer } from './LinkRenderer';
|
|
10
|
+
import { ImageRenderer } from './ImageRenderer';
|
|
11
|
+
import { BlockquoteRenderer } from './BlockquoteRenderer';
|
|
12
|
+
import { TableRenderer } from './TableRenderer';
|
|
13
|
+
|
|
14
|
+
/** Themed horizontal rule. */
|
|
15
|
+
const HorizontalRule = React.memo(function HorizontalRule() {
|
|
16
|
+
const theme = useAIElementsTheme();
|
|
17
|
+
return (
|
|
18
|
+
<View
|
|
19
|
+
style={[
|
|
20
|
+
hrStyles.rule,
|
|
21
|
+
{ backgroundColor: theme.colors.border },
|
|
22
|
+
]}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const hrStyles = StyleSheet.create({
|
|
28
|
+
rule: {
|
|
29
|
+
height: 1,
|
|
30
|
+
marginVertical: 16,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Map a single MarkdownNode to its corresponding React Native renderer.
|
|
36
|
+
* Used by both StreamingMarkdown and BlockquoteRenderer (for nested blocks).
|
|
37
|
+
*/
|
|
38
|
+
export function renderNode(node: MarkdownNode, key: string): ReactNode {
|
|
39
|
+
switch (node.type) {
|
|
40
|
+
case 'paragraph':
|
|
41
|
+
case 'text':
|
|
42
|
+
return <TextRenderer key={key} node={node} />;
|
|
43
|
+
|
|
44
|
+
case 'heading':
|
|
45
|
+
return <HeadingRenderer key={key} node={node} />;
|
|
46
|
+
|
|
47
|
+
case 'code_inline':
|
|
48
|
+
case 'code_block':
|
|
49
|
+
return <CodeRenderer key={key} node={node} />;
|
|
50
|
+
|
|
51
|
+
case 'list':
|
|
52
|
+
return <ListRenderer key={key} node={node} />;
|
|
53
|
+
|
|
54
|
+
case 'link':
|
|
55
|
+
return <LinkRenderer key={key} node={node} />;
|
|
56
|
+
|
|
57
|
+
case 'image':
|
|
58
|
+
return <ImageRenderer key={key} node={node} />;
|
|
59
|
+
|
|
60
|
+
case 'blockquote':
|
|
61
|
+
return <BlockquoteRenderer key={key} node={node} />;
|
|
62
|
+
|
|
63
|
+
case 'horizontal_rule':
|
|
64
|
+
return <HorizontalRule key={key} />;
|
|
65
|
+
|
|
66
|
+
case 'table':
|
|
67
|
+
return <TableRenderer key={key} node={node} />;
|
|
68
|
+
|
|
69
|
+
default:
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useMemo,
|
|
5
|
+
type PropsWithChildren,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import { useColorScheme } from 'react-native';
|
|
8
|
+
import type { AIElementsTheme } from './tokens';
|
|
9
|
+
import { lightTheme, darkTheme } from './defaultTheme';
|
|
10
|
+
|
|
11
|
+
const ThemeContext = createContext<AIElementsTheme>(lightTheme);
|
|
12
|
+
|
|
13
|
+
export type ColorSchemeMode = 'light' | 'dark' | 'system';
|
|
14
|
+
|
|
15
|
+
type DeepPartial<T> = {
|
|
16
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface AIElementsProviderProps {
|
|
20
|
+
theme?: DeepPartial<AIElementsTheme>;
|
|
21
|
+
mode?: ColorSchemeMode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mergeTheme(
|
|
25
|
+
base: AIElementsTheme,
|
|
26
|
+
overrides?: DeepPartial<AIElementsTheme>
|
|
27
|
+
): AIElementsTheme {
|
|
28
|
+
if (!overrides) return base;
|
|
29
|
+
return {
|
|
30
|
+
...base,
|
|
31
|
+
...overrides,
|
|
32
|
+
colors: { ...base.colors, ...overrides.colors },
|
|
33
|
+
spacing: { ...base.spacing, ...overrides.spacing },
|
|
34
|
+
radius: { ...base.radius, ...overrides.radius },
|
|
35
|
+
typography: {
|
|
36
|
+
...base.typography,
|
|
37
|
+
...overrides.typography,
|
|
38
|
+
fontSize: {
|
|
39
|
+
...base.typography.fontSize,
|
|
40
|
+
...overrides.typography?.fontSize,
|
|
41
|
+
},
|
|
42
|
+
lineHeight: {
|
|
43
|
+
...base.typography.lineHeight,
|
|
44
|
+
...overrides.typography?.lineHeight,
|
|
45
|
+
},
|
|
46
|
+
fontWeight: {
|
|
47
|
+
...base.typography.fontWeight,
|
|
48
|
+
...overrides.typography?.fontWeight,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function AIElementsProvider({
|
|
55
|
+
theme: themeOverrides,
|
|
56
|
+
mode = 'system',
|
|
57
|
+
children,
|
|
58
|
+
}: PropsWithChildren<AIElementsProviderProps>) {
|
|
59
|
+
const systemScheme = useColorScheme();
|
|
60
|
+
|
|
61
|
+
const resolvedTheme = useMemo(() => {
|
|
62
|
+
const isDark =
|
|
63
|
+
mode === 'system' ? systemScheme === 'dark' : mode === 'dark';
|
|
64
|
+
const base = isDark ? darkTheme : lightTheme;
|
|
65
|
+
return mergeTheme(base, themeOverrides);
|
|
66
|
+
}, [mode, systemScheme, themeOverrides]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ThemeContext.Provider value={resolvedTheme}>
|
|
70
|
+
{children}
|
|
71
|
+
</ThemeContext.Provider>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function useAIElementsTheme(): AIElementsTheme {
|
|
76
|
+
return useContext(ThemeContext);
|
|
77
|
+
}
|