juq-llm-kit
Version:
Customizable UI components for React Native (Expo) chat applications
232 lines (220 loc) ⢠9.17 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importStar(require("react"));
const react_native_1 = require("react-native");
const vector_icons_1 = require("@expo/vector-icons");
const Clipboard = __importStar(require("expo-clipboard"));
// Default sample messages for demo
const defaultMessages = [
{
id: 1,
role: 'user',
content: 'provide me shorter message',
timestamp: new Date().toISOString(),
},
{
id: 2,
role: 'assistant',
content: `Here are some recent scientific discoveries:
š Space: JWST found tiny asteroids, and Voyager 1 re-established contact.
𧬠Medicine: New malaria vaccines, Alzheimer's drug (Kisunla), and the Human Cell Atlas.
š Environment: 27 new species discovered in Peru, a new rock skink found in Australia.
š» Tech: Google DeepMind's AI (Gemini), advances in quantum computing.
For more, check FT and NYPost. š`,
timestamp: new Date().toISOString(),
}
];
const Messages = ({ messages = defaultMessages, onCopy, onRegenerate, containerStyle, bubbleStyle, messageTextStyle, fontFamily, theme = 'dark', customActions }) => {
const flatListRef = (0, react_1.useRef)(null);
const colors = getThemeColors(theme);
(0, react_1.useEffect)(() => {
// Scroll to bottom when messages change
if (flatListRef.current) {
setTimeout(() => {
var _a;
(_a = flatListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToEnd({ animated: true });
}, 100);
}
}, [messages]);
const formatMessageContent = (content) => {
return content.split('\n').map((line, i) => {
if (line.trim() === '') {
return <react_native_1.View key={i} style={{ height: 8 }}/>;
}
return (<react_native_1.Text key={i} style={[
styles.messageText,
{ color: colors.textColor, fontFamily },
messageTextStyle
]}>
{line}
</react_native_1.Text>);
});
};
const copyToClipboard = (text, messageId) => __awaiter(void 0, void 0, void 0, function* () {
yield Clipboard.setStringAsync(text);
if (onCopy) {
onCopy(text);
}
});
const handleRegenerate = (messageId) => {
if (onRegenerate) {
onRegenerate(messageId);
}
};
const renderMessage = ({ item }) => {
const isUser = item.role === 'user';
return (<react_native_1.View style={[
styles.messageContainer,
isUser ? styles.userMessageContainer : styles.assistantMessageContainer
]}>
<react_native_1.View style={styles.messageHeader}>
<react_native_1.Text style={[styles.senderName, { color: colors.secondaryTextColor }]}>
{isUser ? 'You' : 'Assistant'}
</react_native_1.Text>
<react_native_1.Text style={[styles.timestamp, { color: colors.secondaryTextColor }]}>
{new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</react_native_1.Text>
</react_native_1.View>
<react_native_1.View style={[
styles.messageBubble,
isUser ?
{ backgroundColor: colors.userBubbleBg } :
{ backgroundColor: colors.assistantBubbleBg },
bubbleStyle
]}>
{formatMessageContent(item.content)}
{!isUser && (<react_native_1.View style={[styles.messageActions, { borderTopColor: colors.borderColor }]}>
<react_native_1.TouchableOpacity onPress={() => copyToClipboard(item.content, item.id)} style={styles.actionButton}>
<vector_icons_1.Ionicons name="copy-outline" size={16} color={colors.iconColor}/>
</react_native_1.TouchableOpacity>
<react_native_1.TouchableOpacity style={styles.actionButton}>
<vector_icons_1.Ionicons name="square-outline" size={16} color={colors.iconColor}/>
</react_native_1.TouchableOpacity>
<react_native_1.TouchableOpacity style={styles.actionButton} onPress={() => handleRegenerate(item.id)}>
<vector_icons_1.Ionicons name="refresh-outline" size={16} color={colors.iconColor}/>
</react_native_1.TouchableOpacity>
<react_native_1.TouchableOpacity style={styles.actionButton}>
<vector_icons_1.Ionicons name="ellipsis-horizontal" size={16} color={colors.iconColor}/>
</react_native_1.TouchableOpacity>
{/* Render custom actions if provided */}
{customActions && customActions.map((action, index) => (<react_native_1.TouchableOpacity key={`custom-action-${index}`} style={styles.actionButton} onPress={() => action.onPress(item.id)}>
{action.icon}
</react_native_1.TouchableOpacity>))}
</react_native_1.View>)}
</react_native_1.View>
</react_native_1.View>);
};
return (<react_native_1.FlatList ref={flatListRef} data={messages} renderItem={renderMessage} keyExtractor={(item) => item.id.toString()} contentContainerStyle={[styles.messagesContainer, containerStyle]} showsVerticalScrollIndicator={react_native_1.Platform.OS === 'web'}/>);
};
// Theme helper function
function getThemeColors(theme) {
if (theme === 'light') {
return {
backgroundColor: '#ffffff',
userBubbleBg: '#e5e7eb',
assistantBubbleBg: '#f3f4f6',
textColor: '#111827',
secondaryTextColor: '#6b7280',
iconColor: '#6b7280',
borderColor: '#d1d5db'
};
}
return {
backgroundColor: '#111827',
userBubbleBg: '#4B5563',
assistantBubbleBg: '#1F2937',
textColor: '#F9FAFB',
secondaryTextColor: '#9CA3AF',
iconColor: '#9CA3AF',
borderColor: '#374151'
};
}
const styles = react_native_1.StyleSheet.create({
messagesContainer: {
padding: 16,
paddingBottom: 80, // Space for input
},
messageContainer: {
marginBottom: 16,
maxWidth: '80%',
},
userMessageContainer: {
alignSelf: 'flex-end',
},
assistantMessageContainer: {
alignSelf: 'flex-start',
},
messageHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 4,
},
senderName: {
fontSize: 14,
fontWeight: '600',
marginRight: 8,
},
timestamp: {
fontSize: 12,
},
messageBubble: {
padding: 12,
borderRadius: 20,
},
messageText: {
fontSize: 15,
marginBottom: 8,
},
messageActions: {
flexDirection: 'row',
marginTop: 8,
paddingTop: 8,
borderTopWidth: 1,
},
actionButton: {
marginRight: 16,
},
});
exports.default = Messages;