@qafka/react-native 2.0.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.
- package/CHANGELOG.md +12 -0
- package/CONTRIBUTING.md +92 -0
- package/LICENSE +22 -0
- package/README.md +109 -0
- package/SECURITY.md +67 -0
- package/android/build.gradle +35 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/qafka/attestation/QafkaAttestationModule.kt +92 -0
- package/android/src/main/java/com/qafka/attestation/QafkaAttestationPackage.kt +22 -0
- package/android/src/main/java/com/qafka/audio/QafkaAudioModule.kt +290 -0
- package/android/src/main/java/com/qafka/clipboard/QafkaClipboardModule.kt +28 -0
- package/android/src/main/java/com/qafka/storage/QafkaStorageModule.kt +80 -0
- package/app.plugin.js +1 -0
- package/dist/QafkaSDK.d.ts +174 -0
- package/dist/QafkaSDK.js +461 -0
- package/dist/cards/bindings/resolveFieldName.d.ts +25 -0
- package/dist/cards/bindings/resolveFieldName.js +82 -0
- package/dist/cards/cta/CardContext.d.ts +16 -0
- package/dist/cards/cta/CardContext.js +58 -0
- package/dist/cards/cta/dispatcher.d.ts +7 -0
- package/dist/cards/cta/dispatcher.js +90 -0
- package/dist/cards/cta/types.d.ts +66 -0
- package/dist/cards/cta/types.js +2 -0
- package/dist/cards/index.d.ts +20 -0
- package/dist/cards/index.js +34 -0
- package/dist/cards/primitives/QButton.d.ts +10 -0
- package/dist/cards/primitives/QButton.js +115 -0
- package/dist/cards/primitives/QDivider.d.ts +7 -0
- package/dist/cards/primitives/QDivider.js +17 -0
- package/dist/cards/primitives/QIcon.d.ts +13 -0
- package/dist/cards/primitives/QIcon.js +26 -0
- package/dist/cards/primitives/QImage.d.ts +9 -0
- package/dist/cards/primitives/QImage.js +22 -0
- package/dist/cards/primitives/QText.d.ts +9 -0
- package/dist/cards/primitives/QText.js +30 -0
- package/dist/cards/primitives/QView.d.ts +8 -0
- package/dist/cards/primitives/QView.js +19 -0
- package/dist/cards/renderer/CardRenderer.d.ts +19 -0
- package/dist/cards/renderer/CardRenderer.js +64 -0
- package/dist/cards/renderer/renderNode.d.ts +13 -0
- package/dist/cards/renderer/renderNode.js +42 -0
- package/dist/cards/types.d.ts +110 -0
- package/dist/cards/types.js +6 -0
- package/dist/components/ActionResultBadge.d.ts +12 -0
- package/dist/components/ActionResultBadge.js +58 -0
- package/dist/components/ChatPage.d.ts +44 -0
- package/dist/components/ChatPage.js +84 -0
- package/dist/components/DataChip.d.ts +8 -0
- package/dist/components/DataChip.js +80 -0
- package/dist/components/DataChipList.d.ts +13 -0
- package/dist/components/DataChipList.js +21 -0
- package/dist/components/FloatingButton.d.ts +11 -0
- package/dist/components/FloatingButton.js +162 -0
- package/dist/components/InputArea.d.ts +57 -0
- package/dist/components/InputArea.js +142 -0
- package/dist/components/MarkdownText.d.ts +15 -0
- package/dist/components/MarkdownText.js +283 -0
- package/dist/components/MessageBubble.d.ts +134 -0
- package/dist/components/MessageBubble.js +384 -0
- package/dist/components/NavigationSuggestion.d.ts +11 -0
- package/dist/components/NavigationSuggestion.js +109 -0
- package/dist/components/Qafka.d.ts +39 -0
- package/dist/components/Qafka.handlers.d.ts +21 -0
- package/dist/components/Qafka.handlers.js +54 -0
- package/dist/components/Qafka.js +493 -0
- package/dist/components/Qafka.styles.d.ts +19 -0
- package/dist/components/Qafka.styles.js +101 -0
- package/dist/components/Qafka.types.d.ts +744 -0
- package/dist/components/Qafka.types.js +2 -0
- package/dist/components/Qafka.utils.d.ts +7 -0
- package/dist/components/Qafka.utils.js +34 -0
- package/dist/components/QafkaProvider.d.ts +12 -0
- package/dist/components/QafkaProvider.js +87 -0
- package/dist/components/QuickReplies.d.ts +14 -0
- package/dist/components/QuickReplies.js +48 -0
- package/dist/components/StepProgressIndicator.d.ts +12 -0
- package/dist/components/StepProgressIndicator.js +48 -0
- package/dist/components/SuggestionButton.d.ts +42 -0
- package/dist/components/SuggestionButton.js +67 -0
- package/dist/components/ToolStatusPill.d.ts +20 -0
- package/dist/components/ToolStatusPill.js +43 -0
- package/dist/components/TypingIndicator.d.ts +28 -0
- package/dist/components/TypingIndicator.js +109 -0
- package/dist/components/VoicePage.d.ts +48 -0
- package/dist/components/VoicePage.js +683 -0
- package/dist/components/defaults/DefaultCard.d.ts +14 -0
- package/dist/components/defaults/DefaultCard.js +156 -0
- package/dist/components/defaults/DefaultDetail.d.ts +14 -0
- package/dist/components/defaults/DefaultDetail.js +138 -0
- package/dist/components/defaults/DefaultList.d.ts +12 -0
- package/dist/components/defaults/DefaultList.js +98 -0
- package/dist/components/defaults/DefaultTable.d.ts +14 -0
- package/dist/components/defaults/DefaultTable.js +204 -0
- package/dist/components/defaults/index.d.ts +14 -0
- package/dist/components/defaults/index.js +25 -0
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.js +36 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +13 -0
- package/dist/hooks/useChatMessages.d.ts +72 -0
- package/dist/hooks/useChatMessages.js +505 -0
- package/dist/hooks/useContextManager.d.ts +12 -0
- package/dist/hooks/useContextManager.js +46 -0
- package/dist/hooks/useProjectTheme.d.ts +19 -0
- package/dist/hooks/useProjectTheme.js +163 -0
- package/dist/hooks/useSDK.d.ts +31 -0
- package/dist/hooks/useSDK.js +103 -0
- package/dist/hooks/useVoiceChat.d.ts +110 -0
- package/dist/hooks/useVoiceChat.js +436 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +59 -0
- package/dist/native/QafkaAttestation.d.ts +23 -0
- package/dist/native/QafkaAttestation.js +70 -0
- package/dist/native/QafkaAudio.d.ts +14 -0
- package/dist/native/QafkaAudio.js +31 -0
- package/dist/native/QafkaClipboard.d.ts +11 -0
- package/dist/native/QafkaClipboard.js +14 -0
- package/dist/native/QafkaStorage.d.ts +15 -0
- package/dist/native/QafkaStorage.js +12 -0
- package/dist/resolve-project-config.d.ts +35 -0
- package/dist/resolve-project-config.js +41 -0
- package/dist/runtime-config-loader.d.ts +37 -0
- package/dist/runtime-config-loader.js +53 -0
- package/dist/services/AttestationManager.d.ts +38 -0
- package/dist/services/AttestationManager.js +296 -0
- package/dist/services/BackendService.d.ts +156 -0
- package/dist/services/BackendService.js +755 -0
- package/dist/services/ConversationManager.d.ts +43 -0
- package/dist/services/ConversationManager.js +96 -0
- package/dist/services/NavigationHandler.d.ts +29 -0
- package/dist/services/NavigationHandler.js +70 -0
- package/dist/services/RealtimeService.d.ts +83 -0
- package/dist/services/RealtimeService.js +203 -0
- package/dist/services/storage.d.ts +11 -0
- package/dist/services/storage.js +15 -0
- package/dist/services/storageCore.d.ts +17 -0
- package/dist/services/storageCore.js +46 -0
- package/dist/themes/dark.d.ts +5 -0
- package/dist/themes/dark.js +129 -0
- package/dist/themes/index.d.ts +12 -0
- package/dist/themes/index.js +33 -0
- package/dist/themes/light.d.ts +5 -0
- package/dist/themes/light.js +129 -0
- package/dist/themes/types.d.ts +155 -0
- package/dist/themes/types.js +5 -0
- package/dist/types/chat.d.ts +126 -0
- package/dist/types/chat.js +5 -0
- package/dist/types/components.d.ts +56 -0
- package/dist/types/components.js +16 -0
- package/dist/types/external-navigation.d.ts +19 -0
- package/dist/types/external-navigation.js +8 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +25 -0
- package/dist/types/navigation.d.ts +86 -0
- package/dist/types/navigation.js +5 -0
- package/dist/types/sdk.d.ts +36 -0
- package/dist/types/sdk.js +5 -0
- package/dist/utils/deepMerge.d.ts +46 -0
- package/dist/utils/deepMerge.js +70 -0
- package/dist/utils/fontUtils.d.ts +8 -0
- package/dist/utils/fontUtils.js +16 -0
- package/dist/validate-end-user.d.ts +18 -0
- package/dist/validate-end-user.js +74 -0
- package/expo-plugin/withQafkaAttestation.js +57 -0
- package/ios/QafkaAttestation.m +25 -0
- package/ios/QafkaAttestation.swift +128 -0
- package/ios/QafkaAudio.m +23 -0
- package/ios/QafkaAudio.swift +519 -0
- package/ios/QafkaClipboard.m +10 -0
- package/ios/QafkaClipboard.swift +21 -0
- package/ios/QafkaReactImports.h +2 -0
- package/ios/QafkaStorage.m +26 -0
- package/ios/QafkaStorage.swift +118 -0
- package/package.json +82 -0
- package/qafka.config.d.ts +9 -0
- package/qafka.config.js +9 -0
- package/react-native-qafka.podspec +28 -0
- package/react-native.config.js +14 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.InputArea = InputArea;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const fontUtils_1 = require("../utils/fontUtils");
|
|
40
|
+
/**
|
|
41
|
+
* InputArea Component
|
|
42
|
+
*
|
|
43
|
+
* Text input area with send button for chat messages.
|
|
44
|
+
*
|
|
45
|
+
* Features:
|
|
46
|
+
* - Multi-line text input
|
|
47
|
+
* - Character limit with counter
|
|
48
|
+
* - Send button with loading state
|
|
49
|
+
* - Auto-disable when empty or sending
|
|
50
|
+
* - Theme support
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <InputArea
|
|
55
|
+
* theme={lightTheme}
|
|
56
|
+
* placeholder="Type a message..."
|
|
57
|
+
* onSend={(msg) => handleSend(msg)}
|
|
58
|
+
* isSending={loading}
|
|
59
|
+
* maxLength={500}
|
|
60
|
+
* showCharacterCount
|
|
61
|
+
* />
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function InputArea({ theme, placeholder = 'Type a message...', onSend, isSending = false, maxLength = 500, disabled = false, showCharacterCount = false, }) {
|
|
65
|
+
const [text, setText] = (0, react_1.useState)('');
|
|
66
|
+
const handleSend = () => {
|
|
67
|
+
const trimmedText = text?.trim?.() || '';
|
|
68
|
+
if (!trimmedText || isSending || disabled)
|
|
69
|
+
return;
|
|
70
|
+
onSend(trimmedText);
|
|
71
|
+
setText('');
|
|
72
|
+
};
|
|
73
|
+
const canSend = text.trim().length > 0 && !isSending && !disabled;
|
|
74
|
+
const containerStyle = {
|
|
75
|
+
flexDirection: 'row',
|
|
76
|
+
alignItems: 'flex-end',
|
|
77
|
+
paddingHorizontal: theme.spacing.md,
|
|
78
|
+
paddingVertical: theme.spacing.sm,
|
|
79
|
+
backgroundColor: theme.colors.background,
|
|
80
|
+
borderTopWidth: 1,
|
|
81
|
+
borderTopColor: theme.colors.border,
|
|
82
|
+
};
|
|
83
|
+
const inputContainerStyle = {
|
|
84
|
+
flex: 1,
|
|
85
|
+
backgroundColor: theme.colors.inputBackground,
|
|
86
|
+
borderRadius: theme.borderRadius.xl,
|
|
87
|
+
borderWidth: 1,
|
|
88
|
+
borderColor: theme.colors.inputBorder,
|
|
89
|
+
paddingHorizontal: theme.spacing.md,
|
|
90
|
+
paddingVertical: theme.spacing.sm,
|
|
91
|
+
marginRight: theme.spacing.sm,
|
|
92
|
+
maxHeight: 100,
|
|
93
|
+
};
|
|
94
|
+
const inputStyle = {
|
|
95
|
+
fontSize: theme.typography.fontSize.md,
|
|
96
|
+
color: theme.colors.text,
|
|
97
|
+
lineHeight: theme.typography.fontSize.md * theme.typography.lineHeight.normal,
|
|
98
|
+
minHeight: 20,
|
|
99
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'regular'),
|
|
100
|
+
};
|
|
101
|
+
const sendButtonStyle = {
|
|
102
|
+
width: 40,
|
|
103
|
+
height: 40,
|
|
104
|
+
borderRadius: 20,
|
|
105
|
+
backgroundColor: canSend
|
|
106
|
+
? theme.colors.buttonBackground
|
|
107
|
+
: theme.colors.buttonDisabled,
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
justifyContent: 'center',
|
|
110
|
+
...theme.shadows.small,
|
|
111
|
+
};
|
|
112
|
+
const sendButtonTextStyle = {
|
|
113
|
+
fontSize: theme.typography.fontSize.lg,
|
|
114
|
+
color: theme.colors.buttonText,
|
|
115
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'medium'),
|
|
116
|
+
};
|
|
117
|
+
const charCountStyle = {
|
|
118
|
+
fontSize: theme.typography.fontSize.xs,
|
|
119
|
+
color: text.length >= maxLength
|
|
120
|
+
? theme.colors.error
|
|
121
|
+
: theme.colors.textSecondary,
|
|
122
|
+
marginTop: theme.spacing.xs / 2,
|
|
123
|
+
marginLeft: theme.spacing.sm,
|
|
124
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'regular'),
|
|
125
|
+
};
|
|
126
|
+
return (<react_native_1.View>
|
|
127
|
+
<react_native_1.View style={containerStyle}>
|
|
128
|
+
<react_native_1.View style={inputContainerStyle}>
|
|
129
|
+
<react_native_1.TextInput style={inputStyle} value={text} onChangeText={setText} placeholder={placeholder} placeholderTextColor={theme.colors.inputPlaceholder} multiline maxLength={maxLength} editable={!disabled && !isSending} returnKeyType="default" blurOnSubmit={false}/>
|
|
130
|
+
</react_native_1.View>
|
|
131
|
+
|
|
132
|
+
<react_native_1.TouchableOpacity style={sendButtonStyle} onPress={handleSend} disabled={!canSend} activeOpacity={0.7}>
|
|
133
|
+
{isSending ? (<react_native_1.ActivityIndicator color={theme.colors.buttonText} size="small"/>) : (<react_native_1.Text style={sendButtonTextStyle}>↑</react_native_1.Text>)}
|
|
134
|
+
</react_native_1.TouchableOpacity>
|
|
135
|
+
</react_native_1.View>
|
|
136
|
+
|
|
137
|
+
{/* Character counter */}
|
|
138
|
+
{showCharacterCount && text.length > 0 && (<react_native_1.Text style={charCountStyle}>
|
|
139
|
+
{text.length} / {maxLength}
|
|
140
|
+
</react_native_1.Text>)}
|
|
141
|
+
</react_native_1.View>);
|
|
142
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TextStyle } from 'react-native';
|
|
3
|
+
import { Theme } from '../themes/types';
|
|
4
|
+
interface MarkdownTextProps {
|
|
5
|
+
children: string;
|
|
6
|
+
style: TextStyle;
|
|
7
|
+
theme: Theme;
|
|
8
|
+
isUserMessage: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Enhanced Markdown Renderer
|
|
12
|
+
* Supports: headings (###), **bold**, *italic*, `code`, ```code blocks```, links, bullet lists, numbered lists
|
|
13
|
+
*/
|
|
14
|
+
export declare function MarkdownText({ children, style, theme, isUserMessage, }: MarkdownTextProps): React.JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MarkdownText = MarkdownText;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
const fontUtils_1 = require("../utils/fontUtils");
|
|
10
|
+
/**
|
|
11
|
+
* Enhanced Markdown Renderer
|
|
12
|
+
* Supports: headings (###), **bold**, *italic*, `code`, ```code blocks```, links, bullet lists, numbered lists
|
|
13
|
+
*/
|
|
14
|
+
function MarkdownText({ children, style, theme, isUserMessage, }) {
|
|
15
|
+
// Extract base styles (without fontWeight/fontStyle that might interfere with markdown)
|
|
16
|
+
const baseTextStyle = {
|
|
17
|
+
color: style.color,
|
|
18
|
+
fontSize: style.fontSize,
|
|
19
|
+
lineHeight: style.lineHeight,
|
|
20
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'regular'),
|
|
21
|
+
};
|
|
22
|
+
// Process line by line for block elements
|
|
23
|
+
const processLine = (line, lineIndex) => {
|
|
24
|
+
// Check for headings (### Heading)
|
|
25
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
26
|
+
if (headingMatch) {
|
|
27
|
+
const level = headingMatch[1].length;
|
|
28
|
+
const headingText = headingMatch[2];
|
|
29
|
+
const headingSize = style.fontSize + (6 - level) * 2;
|
|
30
|
+
return (<react_native_1.Text key={`heading-${lineIndex}`} style={[
|
|
31
|
+
baseTextStyle,
|
|
32
|
+
{
|
|
33
|
+
fontSize: headingSize,
|
|
34
|
+
fontWeight: '700',
|
|
35
|
+
marginTop: theme.spacing.sm,
|
|
36
|
+
marginBottom: theme.spacing.xs,
|
|
37
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'bold'),
|
|
38
|
+
},
|
|
39
|
+
]}>
|
|
40
|
+
{parseInlineMarkdown(headingText)}
|
|
41
|
+
</react_native_1.Text>);
|
|
42
|
+
}
|
|
43
|
+
// Check for bullet lists (- item or * item)
|
|
44
|
+
const bulletMatch = line.match(/^[\-\*]\s+(.+)$/);
|
|
45
|
+
if (bulletMatch) {
|
|
46
|
+
return (<react_native_1.View key={`bullet-${lineIndex}`} style={{
|
|
47
|
+
flexDirection: 'row',
|
|
48
|
+
marginBottom: theme.spacing.xs / 2,
|
|
49
|
+
paddingLeft: theme.spacing.md,
|
|
50
|
+
}}>
|
|
51
|
+
<react_native_1.Text style={[baseTextStyle, { marginRight: theme.spacing.xs }]}>
|
|
52
|
+
•
|
|
53
|
+
</react_native_1.Text>
|
|
54
|
+
<react_native_1.Text style={[baseTextStyle, { flexShrink: 1 }]}>
|
|
55
|
+
{parseInlineMarkdown(bulletMatch[1])}
|
|
56
|
+
</react_native_1.Text>
|
|
57
|
+
</react_native_1.View>);
|
|
58
|
+
}
|
|
59
|
+
// Check for numbered lists (1. item)
|
|
60
|
+
const numberedMatch = line.match(/^(\d+)\.\s+(.+)$/);
|
|
61
|
+
if (numberedMatch) {
|
|
62
|
+
return (<react_native_1.View key={`numbered-${lineIndex}`} style={{
|
|
63
|
+
flexDirection: 'row',
|
|
64
|
+
marginBottom: theme.spacing.xs / 2,
|
|
65
|
+
paddingLeft: theme.spacing.md,
|
|
66
|
+
}}>
|
|
67
|
+
<react_native_1.Text style={[baseTextStyle, { marginRight: theme.spacing.xs }]}>
|
|
68
|
+
{numberedMatch[1]}.
|
|
69
|
+
</react_native_1.Text>
|
|
70
|
+
<react_native_1.Text style={[baseTextStyle, { flexShrink: 1 }]}>
|
|
71
|
+
{parseInlineMarkdown(numberedMatch[2])}
|
|
72
|
+
</react_native_1.Text>
|
|
73
|
+
</react_native_1.View>);
|
|
74
|
+
}
|
|
75
|
+
// Check for code blocks (```code```)
|
|
76
|
+
if (line.startsWith('```')) {
|
|
77
|
+
return null; // Handle code blocks separately
|
|
78
|
+
}
|
|
79
|
+
// Regular paragraph
|
|
80
|
+
return (<react_native_1.Text key={`line-${lineIndex}`} style={baseTextStyle}>
|
|
81
|
+
{parseInlineMarkdown(line)}
|
|
82
|
+
</react_native_1.Text>);
|
|
83
|
+
};
|
|
84
|
+
const parseInlineMarkdown = (text) => {
|
|
85
|
+
const elements = [];
|
|
86
|
+
let currentIndex = 0;
|
|
87
|
+
let key = 0;
|
|
88
|
+
// Regex patterns for inline formatting
|
|
89
|
+
// Process in order: bold first, then italic (to avoid conflicts)
|
|
90
|
+
const patterns = {
|
|
91
|
+
bold: /\*\*(.+?)\*\*/g,
|
|
92
|
+
italic: /(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, // Negative lookahead/behind to avoid **
|
|
93
|
+
code: /`(.+?)`/g,
|
|
94
|
+
link: /\[(.+?)\]\((.+?)\)/g,
|
|
95
|
+
};
|
|
96
|
+
// Find all matches
|
|
97
|
+
const allMatches = [];
|
|
98
|
+
// Bold - process first
|
|
99
|
+
let boldMatch;
|
|
100
|
+
while ((boldMatch = patterns.bold.exec(text)) !== null) {
|
|
101
|
+
allMatches.push({
|
|
102
|
+
index: boldMatch.index,
|
|
103
|
+
length: boldMatch[0].length,
|
|
104
|
+
type: 'bold',
|
|
105
|
+
content: boldMatch[1],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Italic - check if not overlapping with bold
|
|
109
|
+
const italicPattern = /\*(.+?)\*/g;
|
|
110
|
+
let italicMatch;
|
|
111
|
+
while ((italicMatch = italicPattern.exec(text)) !== null) {
|
|
112
|
+
// Check if this position is inside a bold match
|
|
113
|
+
const isInsideBold = allMatches.some((m) => m.type === 'bold' &&
|
|
114
|
+
italicMatch.index >= m.index &&
|
|
115
|
+
italicMatch.index < m.index + m.length);
|
|
116
|
+
// Also check if preceded or followed by another asterisk (bold marker)
|
|
117
|
+
const prevChar = text[italicMatch.index - 1];
|
|
118
|
+
const nextChar = text[italicMatch.index + italicMatch[0].length];
|
|
119
|
+
const isBoldMarker = prevChar === '*' || nextChar === '*';
|
|
120
|
+
if (!isInsideBold && !isBoldMarker) {
|
|
121
|
+
allMatches.push({
|
|
122
|
+
index: italicMatch.index,
|
|
123
|
+
length: italicMatch[0].length,
|
|
124
|
+
type: 'italic',
|
|
125
|
+
content: italicMatch[1],
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Code
|
|
130
|
+
patterns.code.lastIndex = 0;
|
|
131
|
+
let codeMatch;
|
|
132
|
+
while ((codeMatch = patterns.code.exec(text)) !== null) {
|
|
133
|
+
allMatches.push({
|
|
134
|
+
index: codeMatch.index,
|
|
135
|
+
length: codeMatch[0].length,
|
|
136
|
+
type: 'code',
|
|
137
|
+
content: codeMatch[1],
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// Links
|
|
141
|
+
patterns.link.lastIndex = 0;
|
|
142
|
+
let linkMatch;
|
|
143
|
+
while ((linkMatch = patterns.link.exec(text)) !== null) {
|
|
144
|
+
allMatches.push({
|
|
145
|
+
index: linkMatch.index,
|
|
146
|
+
length: linkMatch[0].length,
|
|
147
|
+
type: 'link',
|
|
148
|
+
content: linkMatch[1],
|
|
149
|
+
url: linkMatch[2],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// Sort by index
|
|
153
|
+
allMatches.sort((a, b) => a.index - b.index);
|
|
154
|
+
// Build elements
|
|
155
|
+
allMatches.forEach((item) => {
|
|
156
|
+
// Add text before match
|
|
157
|
+
if (currentIndex < item.index) {
|
|
158
|
+
const plainText = text.substring(currentIndex, item.index);
|
|
159
|
+
elements.push(<react_native_1.Text key={`text-${key++}`} style={baseTextStyle}>
|
|
160
|
+
{plainText}
|
|
161
|
+
</react_native_1.Text>);
|
|
162
|
+
}
|
|
163
|
+
// Add formatted text
|
|
164
|
+
switch (item.type) {
|
|
165
|
+
case 'bold':
|
|
166
|
+
elements.push(<react_native_1.Text key={`bold-${key++}`} style={[
|
|
167
|
+
baseTextStyle,
|
|
168
|
+
{
|
|
169
|
+
fontWeight: 'bold',
|
|
170
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'bold'),
|
|
171
|
+
},
|
|
172
|
+
]}>
|
|
173
|
+
{item.content}
|
|
174
|
+
</react_native_1.Text>);
|
|
175
|
+
break;
|
|
176
|
+
case 'italic':
|
|
177
|
+
elements.push(<react_native_1.Text key={`italic-${key++}`} style={[
|
|
178
|
+
baseTextStyle,
|
|
179
|
+
{
|
|
180
|
+
fontStyle: 'italic',
|
|
181
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'italic'),
|
|
182
|
+
},
|
|
183
|
+
]}>
|
|
184
|
+
{item.content}
|
|
185
|
+
</react_native_1.Text>);
|
|
186
|
+
break;
|
|
187
|
+
case 'code':
|
|
188
|
+
elements.push(<react_native_1.Text key={`code-${key++}`} style={[
|
|
189
|
+
baseTextStyle,
|
|
190
|
+
{
|
|
191
|
+
backgroundColor: isUserMessage
|
|
192
|
+
? 'rgba(255,255,255,0.2)'
|
|
193
|
+
: theme.colors.surface,
|
|
194
|
+
paddingHorizontal: theme.spacing.xs,
|
|
195
|
+
paddingVertical: 2,
|
|
196
|
+
borderRadius: theme.borderRadius.sm,
|
|
197
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'code') || 'Courier',
|
|
198
|
+
},
|
|
199
|
+
]}>
|
|
200
|
+
{item.content}
|
|
201
|
+
</react_native_1.Text>);
|
|
202
|
+
break;
|
|
203
|
+
case 'link':
|
|
204
|
+
elements.push(<react_native_1.Text key={`link-${key++}`} style={[
|
|
205
|
+
baseTextStyle,
|
|
206
|
+
{
|
|
207
|
+
color: isUserMessage
|
|
208
|
+
? baseTextStyle.color
|
|
209
|
+
: theme.colors.primary,
|
|
210
|
+
textDecorationLine: 'underline',
|
|
211
|
+
},
|
|
212
|
+
]}>
|
|
213
|
+
{item.content}
|
|
214
|
+
</react_native_1.Text>);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
currentIndex = item.index + item.length;
|
|
218
|
+
});
|
|
219
|
+
// Add remaining text
|
|
220
|
+
if (currentIndex < text.length) {
|
|
221
|
+
const remainingText = text.substring(currentIndex);
|
|
222
|
+
elements.push(<react_native_1.Text key={`text-${key++}`} style={baseTextStyle}>
|
|
223
|
+
{remainingText}
|
|
224
|
+
</react_native_1.Text>);
|
|
225
|
+
}
|
|
226
|
+
return elements.length > 0 ? (elements) : (<react_native_1.Text style={baseTextStyle}>{text}</react_native_1.Text>);
|
|
227
|
+
};
|
|
228
|
+
// Split into lines and process
|
|
229
|
+
const lines = children.split('\n');
|
|
230
|
+
const processedLines = [];
|
|
231
|
+
let codeBlockLines = [];
|
|
232
|
+
let inCodeBlock = false;
|
|
233
|
+
let codeBlockKey = 0;
|
|
234
|
+
lines.forEach((line, index) => {
|
|
235
|
+
// Handle code blocks
|
|
236
|
+
if (line.trim().startsWith('```')) {
|
|
237
|
+
if (!inCodeBlock) {
|
|
238
|
+
// Start code block
|
|
239
|
+
inCodeBlock = true;
|
|
240
|
+
codeBlockLines = [];
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
// End code block
|
|
244
|
+
inCodeBlock = false;
|
|
245
|
+
if (codeBlockLines.length > 0) {
|
|
246
|
+
processedLines.push(<react_native_1.View key={`codeblock-${codeBlockKey++}`} style={{
|
|
247
|
+
backgroundColor: isUserMessage
|
|
248
|
+
? 'rgba(255,255,255,0.2)'
|
|
249
|
+
: theme.colors.surface,
|
|
250
|
+
padding: theme.spacing.sm,
|
|
251
|
+
borderRadius: theme.borderRadius.sm,
|
|
252
|
+
marginVertical: theme.spacing.xs,
|
|
253
|
+
}}>
|
|
254
|
+
<react_native_1.Text style={[
|
|
255
|
+
baseTextStyle,
|
|
256
|
+
{
|
|
257
|
+
fontFamily: (0, fontUtils_1.getFontFamily)(theme, 'code') || 'Courier',
|
|
258
|
+
fontSize: theme.typography.fontSize.sm,
|
|
259
|
+
},
|
|
260
|
+
]}>
|
|
261
|
+
{codeBlockLines.join('\n')}
|
|
262
|
+
</react_native_1.Text>
|
|
263
|
+
</react_native_1.View>);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (inCodeBlock) {
|
|
269
|
+
codeBlockLines.push(line);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// Process regular line
|
|
273
|
+
const processed = processLine(line, index);
|
|
274
|
+
if (processed) {
|
|
275
|
+
processedLines.push(processed);
|
|
276
|
+
}
|
|
277
|
+
// Add spacing between lines
|
|
278
|
+
if (index < lines.length - 1 && line.trim() !== '') {
|
|
279
|
+
processedLines.push(<react_native_1.View key={`spacing-${index}`} style={{ height: theme.spacing.xs / 2 }}/>);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return <react_native_1.View>{processedLines}</react_native_1.View>;
|
|
283
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Theme } from '../themes/types';
|
|
3
|
+
import { ComponentRegistry } from '../types/components';
|
|
4
|
+
import { NavigationButtonProps } from './Qafka.types';
|
|
5
|
+
import { ExternalSuggestion } from '../types/external-navigation';
|
|
6
|
+
export interface MessageBubbleProps {
|
|
7
|
+
/**
|
|
8
|
+
* Message content
|
|
9
|
+
*/
|
|
10
|
+
message: string;
|
|
11
|
+
/**
|
|
12
|
+
* Message role - user or AI assistant
|
|
13
|
+
*/
|
|
14
|
+
role: 'user' | 'assistant';
|
|
15
|
+
/**
|
|
16
|
+
* Message timestamp
|
|
17
|
+
*/
|
|
18
|
+
timestamp: Date;
|
|
19
|
+
/**
|
|
20
|
+
* Theme configuration
|
|
21
|
+
*/
|
|
22
|
+
theme: Theme;
|
|
23
|
+
/**
|
|
24
|
+
* Show timestamp below message
|
|
25
|
+
*/
|
|
26
|
+
showTimestamp?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Action buttons to display below message
|
|
29
|
+
*/
|
|
30
|
+
actions?: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
type: 'navigation' | 'api' | 'custom';
|
|
33
|
+
label: string;
|
|
34
|
+
icon?: string;
|
|
35
|
+
data?: any;
|
|
36
|
+
style?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning';
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Callback when action button is pressed
|
|
40
|
+
*/
|
|
41
|
+
onActionPress?: (action: any) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Component registry for rendering tool responses
|
|
44
|
+
*/
|
|
45
|
+
componentRegistry?: ComponentRegistry;
|
|
46
|
+
/**
|
|
47
|
+
* Custom Navigation Button Component
|
|
48
|
+
* Overrides the default navigation button
|
|
49
|
+
*/
|
|
50
|
+
NavigationButtonComponent?: React.ComponentType<NavigationButtonProps>;
|
|
51
|
+
/**
|
|
52
|
+
* Tool definition (if this message contains tool result)
|
|
53
|
+
*/
|
|
54
|
+
toolDefinition?: {
|
|
55
|
+
key: string;
|
|
56
|
+
response: {
|
|
57
|
+
type: 'list' | 'card' | 'detail' | 'table';
|
|
58
|
+
dataPath?: string;
|
|
59
|
+
itemComponent?: string;
|
|
60
|
+
maxItems?: number;
|
|
61
|
+
fallbackMessage?: string;
|
|
62
|
+
layout?: 'vertical' | 'horizontal' | 'custom';
|
|
63
|
+
position?: 'before' | 'after';
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Tool result data (if this message contains tool result)
|
|
68
|
+
*/
|
|
69
|
+
toolResultData?: any;
|
|
70
|
+
/**
|
|
71
|
+
* Action execution results to display as badges
|
|
72
|
+
*/
|
|
73
|
+
actionResults?: Array<{
|
|
74
|
+
actionType: string;
|
|
75
|
+
success: boolean;
|
|
76
|
+
message: string;
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Completed steps for multi-step tool flows
|
|
80
|
+
*/
|
|
81
|
+
completedSteps?: Array<{
|
|
82
|
+
tool: string;
|
|
83
|
+
step: string;
|
|
84
|
+
data: Record<string, any>;
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* External suggestions (WhatsApp, phone, map, app store, …)
|
|
88
|
+
* to render as tappable buttons below the message.
|
|
89
|
+
*/
|
|
90
|
+
externalSuggestions?: ExternalSuggestion[];
|
|
91
|
+
/**
|
|
92
|
+
* Callback when user taps an external suggestion button.
|
|
93
|
+
*/
|
|
94
|
+
onExternalPress?: (suggestion: ExternalSuggestion) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Card Template envelope to render below the message bubble.
|
|
97
|
+
* Populated when the tool's `uiTemplateType === "card"`.
|
|
98
|
+
*/
|
|
99
|
+
card?: {
|
|
100
|
+
templateId: string;
|
|
101
|
+
templateSlug: string;
|
|
102
|
+
definition: any;
|
|
103
|
+
data: any;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Host callbacks invoked from card CTAs.
|
|
107
|
+
* Forwarded directly to the CardRenderer.
|
|
108
|
+
*/
|
|
109
|
+
cardHostCallbacks?: import('../cards/cta/types').CardCTAHostCallbacks;
|
|
110
|
+
/**
|
|
111
|
+
* CTA telemetry / lifecycle hooks.
|
|
112
|
+
*/
|
|
113
|
+
cardLifecycle?: import('../cards/cta/types').CardCTALifecycle;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* MessageBubble Component
|
|
117
|
+
*
|
|
118
|
+
* Displays a chat message bubble with support for:
|
|
119
|
+
* - User and AI messages with different styles
|
|
120
|
+
* - Timestamps
|
|
121
|
+
* - Theming support
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```tsx
|
|
125
|
+
* <MessageBubble
|
|
126
|
+
* message="Hello, how can I help?"
|
|
127
|
+
* role="assistant"
|
|
128
|
+
* timestamp={new Date()}
|
|
129
|
+
* theme={lightTheme}
|
|
130
|
+
* showTimestamp
|
|
131
|
+
* />
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare function MessageBubble({ message, role, timestamp, theme, showTimestamp, actions, onActionPress, componentRegistry, toolDefinition, toolResultData, NavigationButtonComponent, actionResults, completedSteps, externalSuggestions, onExternalPress, card, cardHostCallbacks, cardLifecycle, }: MessageBubbleProps): React.JSX.Element;
|