Files
pi-mobile/apps/mobile/components/AssistantMessage.tsx
T
2026-07-09 12:07:57 -05:00

207 lines
4.0 KiB
TypeScript

import Markdown from "@ronradtke/react-native-markdown-display";
import { memo } from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";
import { CollapsibleMessage } from "@/components/CollapsibleMessage";
import { colors, monoFont } from "@/lib/theme";
type Props = {
text: string;
streaming?: boolean;
};
function AssistantMessageComponent({ text, streaming }: Props) {
if (!text.trim()) {
if (!streaming) {
return null;
}
return (
<View style={styles.thinking}>
<ActivityIndicator size="small" color={colors.textMuted} />
</View>
);
}
return (
<CollapsibleMessage text={text} forceExpanded={streaming} style={styles.container}>
<Markdown style={markdownStyles} colorScheme="dark">
{text}
</Markdown>
</CollapsibleMessage>
);
}
export const AssistantMessage = memo(AssistantMessageComponent);
const styles = StyleSheet.create({
container: {
alignSelf: "stretch",
},
thinking: {
alignSelf: "flex-start",
paddingVertical: 4,
},
});
const markdownStyles = StyleSheet.create({
body: {
color: colors.text,
fontSize: 15,
lineHeight: 22,
},
paragraph: {
marginTop: 0,
marginBottom: 10,
},
heading1: {
color: colors.text,
fontSize: 22,
fontWeight: "700",
marginTop: 12,
marginBottom: 8,
},
heading2: {
color: colors.text,
fontSize: 19,
fontWeight: "700",
marginTop: 12,
marginBottom: 6,
},
heading3: {
color: colors.text,
fontSize: 17,
fontWeight: "700",
marginTop: 10,
marginBottom: 6,
},
heading4: {
color: colors.text,
fontSize: 15,
fontWeight: "700",
marginTop: 8,
marginBottom: 4,
},
heading5: {
color: colors.text,
fontSize: 15,
fontWeight: "600",
},
heading6: {
color: colors.textSecondary,
fontSize: 14,
fontWeight: "600",
},
strong: {
fontWeight: "700",
},
em: {
fontStyle: "italic",
},
link: {
color: colors.accent,
textDecorationLine: "underline",
},
blockquote: {
backgroundColor: colors.surfaceDeep,
borderLeftWidth: 3,
borderLeftColor: colors.border,
paddingHorizontal: 12,
paddingVertical: 4,
marginVertical: 6,
borderRadius: 4,
},
code_inline: {
backgroundColor: colors.surface,
color: colors.accent,
fontFamily: monoFont,
fontSize: 13,
borderRadius: 4,
paddingHorizontal: 4,
},
code_block: {
backgroundColor: colors.surfaceDeep,
borderColor: colors.border,
borderWidth: 1,
borderRadius: 8,
color: colors.text,
fontFamily: monoFont,
fontSize: 12.5,
lineHeight: 18,
padding: 10,
marginVertical: 6,
},
fence: {
backgroundColor: colors.surfaceDeep,
borderColor: colors.border,
borderWidth: 1,
borderRadius: 8,
overflow: "hidden",
marginVertical: 6,
},
fence_header: {
backgroundColor: colors.surface,
borderBottomColor: colors.border,
borderBottomWidth: 1,
paddingHorizontal: 10,
paddingVertical: 4,
},
fence_code: {
backgroundColor: colors.surfaceDeep,
padding: 10,
},
fence_language_label: {
color: colors.textMuted,
fontFamily: monoFont,
fontSize: 11,
},
fence_copy_text: {
color: colors.textMuted,
fontSize: 11,
},
fence_token: {
color: colors.text,
fontFamily: monoFont,
fontSize: 12.5,
lineHeight: 18,
},
bullet_list: {
marginBottom: 10,
},
ordered_list: {
marginBottom: 10,
},
list_item: {
flexDirection: "row",
marginBottom: 4,
},
bullet_list_icon: {
color: colors.textMuted,
marginRight: 8,
},
ordered_list_icon: {
color: colors.textMuted,
marginRight: 8,
},
hr: {
backgroundColor: colors.border,
height: 1,
marginVertical: 12,
},
table: {
borderWidth: 1,
borderColor: colors.border,
borderRadius: 6,
marginVertical: 6,
},
th: {
padding: 6,
fontWeight: "700",
},
td: {
padding: 6,
},
tr: {
borderBottomWidth: 1,
borderColor: colors.borderSubtle,
},
});