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,127 @@
|
||||
import { useThemeColor } from '@/hooks/useThemeColor';
|
||||
import { BrainCircuit, ChevronDown, ChevronRight } from 'lucide-react-native';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withRepeat,
|
||||
withSequence,
|
||||
withTiming
|
||||
} from 'react-native-reanimated';
|
||||
|
||||
interface ThinkingBlockProps {
|
||||
content: string;
|
||||
isComplete: boolean;
|
||||
isStreaming?: boolean;
|
||||
}
|
||||
|
||||
export function ThinkingBlock({ content, isComplete, isStreaming }: ThinkingBlockProps) {
|
||||
const colors = useThemeColor();
|
||||
// Auto-expand while streaming/thinking, auto-collapse when complete
|
||||
const [isExpanded, setIsExpanded] = useState(!isComplete);
|
||||
|
||||
// Auto collapse when it completes streaming
|
||||
useEffect(() => {
|
||||
if (isComplete && !isStreaming) {
|
||||
setIsExpanded(false);
|
||||
} else if (!isComplete && isStreaming) {
|
||||
setIsExpanded(true);
|
||||
}
|
||||
}, [isComplete, isStreaming]);
|
||||
|
||||
// Pulse animation value
|
||||
const pulseOpacity = useSharedValue(1);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isComplete && isStreaming) {
|
||||
pulseOpacity.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(0.4, { duration: 800 }),
|
||||
withTiming(1, { duration: 800 })
|
||||
),
|
||||
-1, // infinite
|
||||
true // reverse
|
||||
);
|
||||
} else {
|
||||
pulseOpacity.value = withTiming(1, { duration: 300 });
|
||||
}
|
||||
}, [isComplete, isStreaming, pulseOpacity]);
|
||||
|
||||
const animatedHeaderStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
opacity: pulseOpacity.value,
|
||||
};
|
||||
});
|
||||
|
||||
if (!content && isComplete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { borderColor: colors.border }]}>
|
||||
<TouchableOpacity
|
||||
style={styles.header}
|
||||
activeOpacity={0.7}
|
||||
onPress={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
<Animated.View style={[styles.titleRow, animatedHeaderStyle]}>
|
||||
<BrainCircuit size={16} color={colors.textMuted} />
|
||||
<Text style={[styles.titleText, { color: colors.textMuted }]}>
|
||||
{!isComplete && isStreaming ? 'Pensando...' : 'Processo de Raciocínio'}
|
||||
</Text>
|
||||
</Animated.View>
|
||||
|
||||
{isExpanded ? (
|
||||
<ChevronDown size={18} color={colors.textMuted} />
|
||||
) : (
|
||||
<ChevronRight size={18} color={colors.textMuted} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
{isExpanded && content.length > 0 && (
|
||||
<View style={[styles.contentBlock, { borderTopColor: colors.border }]}>
|
||||
<Text style={[styles.contentText, { color: colors.textMuted }]}>
|
||||
{content}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
borderWidth: 1,
|
||||
borderRadius: 8,
|
||||
marginVertical: 8,
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
titleText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
},
|
||||
contentBlock: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
borderTopWidth: 1,
|
||||
},
|
||||
contentText: {
|
||||
fontSize: 14,
|
||||
lineHeight: 20,
|
||||
fontFamily: 'monospace',
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user