feat: refactor app structure and components for improved organization and functionality

- Moved components and hooks into a new directory structure under `src/app` for better organization.
- Updated the main entry point to use `index.ts` and adjusted routing accordingly.
- Introduced new components such as `ActiveProcesses`, `AgentStreamView`, and `ConnectionStatus` to enhance the app's functionality.
- Removed deprecated components and hooks to streamline the codebase.
- Updated styles to align with the new theme structure and ensure consistency across the app.

This refactor aims to improve maintainability and scalability of the application.
This commit is contained in:
Mohamed Boudra
2025-10-22 15:48:53 +02:00
parent 2d1ab5bf79
commit 89641a18e7
31 changed files with 8915 additions and 522 deletions
@@ -0,0 +1,220 @@
import { View, Text, Pressable, ScrollView } from 'react-native';
import { StyleSheet } from 'react-native-unistyles';
import type { AgentStatus } from '@server/server/acp/types';
export interface ActiveProcessesProps {
agents: Array<{
id: string;
status: AgentStatus;
type: 'claude';
currentModeId?: string;
availableModes?: Array<{ id: string; name: string; description?: string | null }>;
}>;
commands: Array<{
id: string;
name: string;
workingDirectory: string;
currentCommand: string;
isDead: boolean;
exitCode: number | null;
}>;
activeProcessId: string | null;
activeProcessType: 'agent' | null;
onSelectAgent: (id: string) => void;
onBackToOrchestrator: () => void;
}
function getAgentStatusColor(status: AgentStatus): string {
switch (status) {
case 'initializing':
return '#f59e0b'; // orange
case 'ready':
return '#3b82f6'; // blue
case 'processing':
return '#fbbf24'; // yellow
case 'completed':
return '#22c55e'; // green
case 'failed':
return '#ef4444'; // red
case 'killed':
return '#9ca3af'; // gray
default:
return '#9ca3af';
}
}
function getModeName(modeId?: string, availableModes?: Array<{ id: string; name: string }>): string {
if (!modeId) return 'unknown';
const mode = availableModes?.find((m) => m.id === modeId);
return mode?.name || modeId;
}
function getModeColor(modeId?: string): string {
if (!modeId) return '#9ca3af'; // gray
// Color based on common mode types
if (modeId.includes('ask')) return '#f59e0b'; // orange - asks permission
if (modeId.includes('code')) return '#22c55e'; // green - writes code
if (modeId.includes('architect') || modeId.includes('plan')) return '#3b82f6'; // blue - plans
return '#9ca3af'; // gray - unknown
}
const styles = StyleSheet.create((theme) => ({
container: {
backgroundColor: theme.colors.card,
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
header: {
paddingHorizontal: theme.spacing[4],
paddingVertical: theme.spacing[3],
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
backButton: {
backgroundColor: theme.colors.muted,
paddingHorizontal: theme.spacing[4],
paddingVertical: theme.spacing[2],
borderRadius: theme.borderRadius.lg,
},
backButtonActive: {
backgroundColor: theme.colors.palette.zinc[700],
},
backButtonText: {
color: theme.colors.foreground,
fontSize: theme.fontSize.sm,
fontWeight: '600',
textAlign: 'center',
},
scrollView: {
paddingHorizontal: theme.spacing[4],
paddingVertical: theme.spacing[3],
},
processItem: {
flexDirection: 'row',
alignItems: 'center',
gap: theme.spacing[2],
paddingHorizontal: theme.spacing[3],
paddingVertical: theme.spacing[2],
borderRadius: theme.borderRadius.lg,
},
processItemActive: {
backgroundColor: theme.colors.primary,
},
processItemInactive: {
backgroundColor: theme.colors.muted,
},
agentIcon: {
width: 12,
height: 12,
borderRadius: theme.borderRadius.full,
backgroundColor: theme.colors.palette.blue[500],
},
commandIcon: {
width: 12,
height: 12,
borderRadius: theme.borderRadius.sm,
backgroundColor: theme.colors.palette.purple[500],
},
processText: {
color: theme.colors.foreground,
fontSize: theme.fontSize.xs,
fontWeight: '500',
},
statusDot: {
width: 8,
height: 8,
borderRadius: theme.borderRadius.full,
},
modeIndicator: {
width: 6,
height: 6,
borderRadius: theme.borderRadius.full,
opacity: 0.3,
},
}));
export function ActiveProcesses({
agents,
commands,
activeProcessId,
activeProcessType,
onSelectAgent,
onBackToOrchestrator,
}: ActiveProcessesProps) {
const hasActiveProcess = activeProcessId !== null && activeProcessType !== null;
if (agents.length === 0 && commands.length === 0) {
return null;
}
return (
<View style={styles.container}>
{hasActiveProcess && (
<View style={styles.header}>
<Pressable
onPress={onBackToOrchestrator}
style={({ pressed }) => [styles.backButton, pressed && styles.backButtonActive]}
>
<Text style={styles.backButtonText}>Back to Chat</Text>
</Pressable>
</View>
)}
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.scrollView}
contentContainerStyle={{ gap: 8 }}
>
{agents.map((agent) => {
const modeName = getModeName(agent.currentModeId, agent.availableModes);
const isActive = activeProcessType === 'agent' && activeProcessId === agent.id;
return (
<Pressable
key={`agent-${agent.id}`}
onPress={() => onSelectAgent(agent.id)}
style={({ pressed }) => [
styles.processItem,
isActive ? styles.processItemActive : styles.processItemInactive,
pressed && { opacity: 0.7 },
]}
>
<View style={styles.agentIcon} />
<Text style={styles.processText}>{agent.id.substring(0, 8)}</Text>
<View style={[styles.statusDot, { backgroundColor: getAgentStatusColor(agent.status) }]} />
{agent.currentModeId && (
<View style={[styles.modeIndicator, { backgroundColor: getModeColor(agent.currentModeId) }]} />
)}
</Pressable>
);
})}
{commands.map((command) => {
const statusColor = command.isDead
? command.exitCode === 0
? '#22c55e'
: '#ef4444'
: '#3b82f6';
return (
<View key={`command-${command.id}`} style={[styles.processItem, styles.processItemInactive]}>
<View style={styles.commandIcon} />
<Text style={styles.processText} numberOfLines={1}>
{command.name || command.currentCommand.substring(0, 20)}
</Text>
<View style={[styles.statusDot, { backgroundColor: statusColor }]} />
</View>
);
})}
</ScrollView>
</View>
);
}