refactor(app): simplify agent list UI and remove unused sidebar component

This commit is contained in:
Mohamed Boudra
2026-01-03 19:39:24 +07:00
parent e8890a2fc1
commit a12af74c7b
15 changed files with 278 additions and 536 deletions
+87 -27
View File
@@ -7,6 +7,7 @@ import {
TextInputKeyPressEventData,
Image,
Platform,
BackHandler,
} from "react-native";
import { useState, useRef, useCallback, useEffect } from "react";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
@@ -57,7 +58,7 @@ export interface MessageInputProps {
onQueue?: (payload: MessagePayload) => void;
}
const MIN_INPUT_HEIGHT = 50;
const MIN_INPUT_HEIGHT = 30;
const MAX_INPUT_HEIGHT = 160;
const IS_WEB = Platform.OS === "web";
@@ -98,7 +99,9 @@ export function MessageInput({
}: MessageInputProps) {
const { theme } = useUnistyles();
const [inputHeight, setInputHeight] = useState(MIN_INPUT_HEIGHT);
const textInputRef = useRef<TextInput | (TextInput & { getNativeRef?: () => unknown }) | null>(null);
const textInputRef = useRef<
TextInput | (TextInput & { getNativeRef?: () => unknown }) | null
>(null);
const inputHeightRef = useRef(MIN_INPUT_HEIGHT);
const baselineInputHeightRef = useRef<number | null>(null);
const overlayTransition = useSharedValue(0);
@@ -188,8 +191,14 @@ export function MessageInput({
// Animate overlay
useEffect(() => {
const showOverlay = isDictating || isDictationProcessing || dictationStatus === "retrying" || dictationStatus === "failed";
overlayTransition.value = withTiming(showOverlay ? 1 : 0, { duration: 200 });
const showOverlay =
isDictating ||
isDictationProcessing ||
dictationStatus === "retrying" ||
dictationStatus === "failed";
overlayTransition.value = withTiming(showOverlay ? 1 : 0, {
duration: 200,
});
}, [isDictating, isDictationProcessing, dictationStatus, overlayTransition]);
const overlayAnimatedStyle = useAnimatedStyle(() => ({
@@ -234,7 +243,10 @@ export function MessageInput({
const handleSendMessage = useCallback(() => {
const trimmed = value.trim();
if (!trimmed && images.length === 0) return;
const payload = { text: trimmed, images: images.length > 0 ? images : undefined };
const payload = {
text: trimmed,
images: images.length > 0 ? images : undefined,
};
if (isAgentRunning && onQueue) {
onQueue(payload);
} else {
@@ -286,7 +298,10 @@ export function MessageInput({
const baseline = baselineInputHeightRef.current ?? MIN_INPUT_HEIGHT;
const rawTarget = scrollHeight > 0 ? scrollHeight : baseline;
const bounded = Math.max(MIN_INPUT_HEIGHT, Math.min(MAX_INPUT_HEIGHT, rawTarget));
const bounded = Math.max(
MIN_INPUT_HEIGHT,
Math.min(MAX_INPUT_HEIGHT, rawTarget)
);
if (Math.abs(inputHeightRef.current - bounded) >= 1) {
inputHeightRef.current = bounded;
@@ -297,7 +312,10 @@ export function MessageInput({
}
function setBoundedInputHeight(nextHeight: number) {
const bounded = Math.max(MIN_INPUT_HEIGHT, Math.min(MAX_INPUT_HEIGHT, nextHeight));
const bounded = Math.max(
MIN_INPUT_HEIGHT,
Math.min(MAX_INPUT_HEIGHT, nextHeight)
);
if (Math.abs(inputHeightRef.current - bounded) < 1) return;
inputHeightRef.current = bounded;
setInputHeight(bounded);
@@ -364,9 +382,15 @@ export function MessageInput({
<View style={styles.imagePreviewContainer}>
{images.map((image, index) => (
<View key={`${image.uri}-${index}`} style={styles.imagePill}>
<Image source={{ uri: image.uri }} style={styles.imageThumbnail} />
<Image
source={{ uri: image.uri }}
style={styles.imageThumbnail}
/>
{onRemoveImage && (
<Pressable onPress={() => onRemoveImage(index)} style={styles.removeImageButton}>
<Pressable
onPress={() => onRemoveImage(index)}
style={styles.removeImageButton}
>
<X size={16} color={theme.colors.foreground} />
</Pressable>
)}
@@ -384,14 +408,20 @@ export function MessageInput({
placeholderTextColor={theme.colors.mutedForeground}
style={[
styles.textInput,
{ height: inputHeight, minHeight: MIN_INPUT_HEIGHT, maxHeight: MAX_INPUT_HEIGHT },
{
height: inputHeight,
minHeight: MIN_INPUT_HEIGHT,
maxHeight: MAX_INPUT_HEIGHT,
},
]}
multiline
scrollEnabled={inputHeight >= MAX_INPUT_HEIGHT}
onContentSizeChange={handleContentSizeChange}
editable={!isDictating && isConnected && !disabled}
onKeyPress={shouldHandleDesktopSubmit ? handleDesktopSubmitKeyPress : undefined}
autoFocus={autoFocus}
onKeyPress={
shouldHandleDesktopSubmit ? handleDesktopSubmitKeyPress : undefined
}
autoFocus={IS_WEB && autoFocus}
/>
{/* Button row */}
@@ -418,10 +448,19 @@ export function MessageInput({
{shouldShowSendButton && (
<Pressable
onPress={handleSendMessage}
disabled={!isConnected || isSubmitDisabled || isSubmitLoading || disabled}
disabled={
!isConnected ||
isSubmitDisabled ||
isSubmitLoading ||
disabled
}
style={[
styles.sendButton,
(!isConnected || isSubmitDisabled || isSubmitLoading || disabled) && styles.buttonDisabled,
(!isConnected ||
isSubmitDisabled ||
isSubmitLoading ||
disabled) &&
styles.buttonDisabled,
]}
>
<ArrowUp size={20} color="white" />
@@ -458,8 +497,16 @@ export function MessageInput({
onCancel={handleCancelRecording}
onAccept={handleAcceptRecording}
onAcceptAndSend={handleAcceptAndSendRecording}
onRetry={dictationStatus === "failed" ? handleRetryFailedRecording : undefined}
onDiscard={dictationStatus === "failed" ? handleDiscardFailedRecording : undefined}
onRetry={
dictationStatus === "failed"
? handleRetryFailedRecording
: undefined
}
onDiscard={
dictationStatus === "failed"
? handleDiscardFailedRecording
: undefined
}
/>
</Animated.View>
</View>
@@ -473,6 +520,18 @@ const styles = StyleSheet.create(((theme: any) => ({
inputWrapper: {
flexDirection: "column",
gap: theme.spacing[3],
backgroundColor: theme.colors.muted,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.accentBorder,
borderRadius: theme.borderRadius["2xl"],
paddingVertical: {
xs: theme.spacing[2],
md: theme.spacing[4],
},
paddingHorizontal: {
xs: theme.spacing[3],
md: theme.spacing[4],
},
},
imagePreviewContainer: {
flexDirection: "row",
@@ -500,12 +559,9 @@ const styles = StyleSheet.create(((theme: any) => ({
},
textInput: {
width: "100%",
paddingHorizontal: theme.spacing[4],
paddingVertical: theme.spacing[3],
backgroundColor: theme.colors.muted,
borderRadius: theme.borderRadius.lg,
color: theme.colors.foreground,
fontSize: theme.fontSize.lg,
fontWeight: theme.fontWeight.normal,
lineHeight: theme.fontSize.lg * 1.4,
...(IS_WEB
? {
@@ -531,16 +587,15 @@ const styles = StyleSheet.create(((theme: any) => ({
gap: theme.spacing[2],
},
attachButton: {
width: 40,
height: 40,
width: 20,
height: 20,
alignItems: "center",
justifyContent: "center",
},
voiceButton: {
width: 40,
height: 40,
width: 34,
height: 34,
borderRadius: theme.borderRadius.full,
backgroundColor: "transparent",
alignItems: "center",
justifyContent: "center",
},
@@ -548,8 +603,8 @@ const styles = StyleSheet.create(((theme: any) => ({
backgroundColor: theme.colors.destructive,
},
sendButton: {
width: 40,
height: 40,
width: 34,
height: 34,
borderRadius: theme.borderRadius.full,
backgroundColor: theme.colors.palette.blue[600],
alignItems: "center",
@@ -560,8 +615,13 @@ const styles = StyleSheet.create(((theme: any) => ({
},
overlayContainer: {
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center",
top: 0,
left: 0,
width: "100%",
height: "100%",
right: 0,
bottom: 0,
},