wip: snapshot das alterações não commitadas (2026-10-20)
Origem: D:/Documents/Github/LocalAiApp Arquivos omitidos (segredos, bancos de dados, logs, >50 MB): 0
This commit is contained in:
1 parent
ac215b4229
commit
e08189f640
17 files changed
+2914
-13236
No files matched your search
@@ -0,0 +1,32 @@
|
||||
import { Link, Stack } from 'expo-router';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { ThemedText } from '@/components/themed-text';
|
||||
import { ThemedView } from '@/components/themed-view';
|
||||
|
||||
export default function NotFoundScreen() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
||||
<ThemedView style={styles.container}>
|
||||
<ThemedText type="title">This screen doesn't exist.</ThemedText>
|
||||
<Link href="/" style={styles.link}>
|
||||
<ThemedText type="link">Go to home screen!</ThemedText>
|
||||
</Link>
|
||||
</ThemedView>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
link: {
|
||||
marginTop: 15,
|
||||
paddingVertical: 15,
|
||||
},
|
||||
});
|
||||
+1
-1
@@ -28,7 +28,7 @@ export default function RootLayout() {
|
||||
<Stack>
|
||||
<Stack.Screen name="index" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="chat" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="settings" options={{ presentation: 'modal', title: 'Settings' }} />
|
||||
<Stack.Screen name="settings/index" options={{ presentation: 'modal', title: 'Settings' }} />
|
||||
<Stack.Screen name="+not-found" />
|
||||
</Stack>
|
||||
<StatusBar style={isDark ? 'light' : 'dark'} />
|
||||
|
||||
+24
-7
@@ -26,6 +26,17 @@ export default function ChatScreen() {
|
||||
const editMessage = useAppStore(state => state.editMessage);
|
||||
const { sendMessage, stopStreaming, regenerateResponse, isStreaming, isConnected } = useLMStudio();
|
||||
const flatListRef = useRef<FlatList>(null);
|
||||
const isUserScrollingRef = useRef(false);
|
||||
const isAutoScrollEnabled = useRef(true);
|
||||
const prevMessagesLength = useRef(messages.length);
|
||||
|
||||
const handleScroll = useCallback((event: any) => {
|
||||
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
|
||||
// if user is within 100 pixels of the bottom, it's considered at the bottom
|
||||
const paddingToBottom = 100;
|
||||
const isCloseToBottom = layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom;
|
||||
isAutoScrollEnabled.current = isCloseToBottom;
|
||||
}, []);
|
||||
|
||||
// Find the editing message content
|
||||
const editingMessage = editingMessageId
|
||||
@@ -69,6 +80,7 @@ export default function ChatScreen() {
|
||||
}, 100);
|
||||
} else {
|
||||
sendMessage(text);
|
||||
isAutoScrollEnabled.current = true; // explicitly re-enable on manual send
|
||||
}
|
||||
}, [editingMessageId, editingMessage, editMessage, deleteMessagesAfter, setEditingMessageId, sendMessage]);
|
||||
|
||||
@@ -81,17 +93,19 @@ export default function ChatScreen() {
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
const isNewMessage = messages.length > prevMessagesLength.current;
|
||||
prevMessagesLength.current = messages.length;
|
||||
|
||||
if (messages.length > 0 && flatListRef.current) {
|
||||
setTimeout(() => {
|
||||
flatListRef.current?.scrollToEnd({ animated: !isStreaming });
|
||||
if ((isAutoScrollEnabled.current && !isUserScrollingRef.current) || isNewMessage) {
|
||||
flatListRef.current?.scrollToEnd({ animated: true });
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}, [messages.length, messages[messages.length - 1]?.content?.length, isStreaming]);
|
||||
}, [messages.length, messages[messages.length - 1]?.content?.length]);
|
||||
|
||||
|
||||
const showTypingIndicator = isStreaming &&
|
||||
messages.length > 0 &&
|
||||
messages[messages.length - 1]?.role === 'assistant' &&
|
||||
messages[messages.length - 1]?.content === '';
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
@@ -137,6 +151,10 @@ export default function ChatScreen() {
|
||||
ref={flatListRef}
|
||||
data={messages}
|
||||
keyExtractor={(item) => item.id}
|
||||
onScroll={handleScroll}
|
||||
onScrollBeginDrag={() => isUserScrollingRef.current = true}
|
||||
onScrollEndDrag={() => isUserScrollingRef.current = false}
|
||||
scrollEventThrottle={16}
|
||||
renderItem={({ item, index }) => (
|
||||
<Animated.View entering={FadeInDown.duration(300).delay(index > messages.length - 4 ? 100 : 0)}>
|
||||
<MessageBubble
|
||||
@@ -151,7 +169,6 @@ export default function ChatScreen() {
|
||||
)}
|
||||
contentContainerStyle={styles.listContent}
|
||||
/>
|
||||
{showTypingIndicator && <TypingIndicator />}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
Reference in new issue
Block a user