diff --git a/app/_layout.tsx b/app/_layout.tsx index c669aac9..5fdaa3a4 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -4,6 +4,7 @@ import { StatusBar } from "expo-status-bar" import { useColorScheme, View, ActivityIndicator, AppState } from "react-native" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { GestureHandlerRootView } from "react-native-gesture-handler" +import { KeyboardProvider } from "react-native-keyboard-controller" import { BottomSheetModalProvider } from "@gorhom/bottom-sheet" import { I18nextProvider, useTranslation } from "react-i18next" import i18n from "../src/lib/i18n/config" @@ -171,6 +172,7 @@ function RootLayout() { + @@ -212,6 +214,7 @@ function RootLayout() { + {/* Telemetry consent modal — shown once on first launch */} - + + {/* Model picker bottom sheet */} =3.0.0" + } + }, "node_modules/react-native-marked": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/react-native-marked/-/react-native-marked-8.0.0.tgz", diff --git a/package.json b/package.json index b24a7338..bca4f246 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "react-i18next": "^17.0.10", "react-native": "0.81.5", "react-native-gesture-handler": "~2.28.0", + "react-native-keyboard-controller": "^1.22.4", "react-native-marked": "8.0.0", "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", diff --git a/src/components/chat/MessageBubble.tsx b/src/components/chat/MessageBubble.tsx index 0f82ecaf..1e8b1cc2 100644 --- a/src/components/chat/MessageBubble.tsx +++ b/src/components/chat/MessageBubble.tsx @@ -1,5 +1,5 @@ import { memo } from "react" -import { View, Text, Image, StyleSheet, ScrollView, TouchableOpacity, Dimensions } from "react-native" +import { View, Text, Image, StyleSheet, ScrollView, TouchableOpacity, Dimensions, ActivityIndicator } from "react-native" import { Ionicons } from "@expo/vector-icons" import { Markdown } from "../markdown" import { ToolCallCard } from "./ToolCallCard" @@ -96,6 +96,14 @@ export const MessageBubble = memo( ))} + {/* Typing placeholder — assistant message arrived before any part */} + {!isUser && text.length === 0 && reasoning.length === 0 && toolParts.length === 0 && fileParts.length === 0 && ( + + + Typing… + + )} + {/* Tool calls */} {toolParts.map((tool) => ( @@ -170,4 +178,8 @@ const s = StyleSheet.create({ }, imageLabel: { fontSize: 10, color: "#666666", marginTop: 2, maxWidth: 200 }, imageLabelDark: { color: "#888888" }, + + typingRow: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 4 }, + typingText: { fontSize: 14, color: "#8b5cf6", fontStyle: "italic" }, + typingTextDark: { color: "#a78bfa" }, })