docs: add Codex integration guide and improve agent creation UX

This commit is contained in:
Mohamed Boudra
2025-10-23 09:22:11 +02:00
parent 0893a9dc33
commit a930c4156a
3 changed files with 74 additions and 52 deletions

View File

@@ -26,3 +26,13 @@ Don't run them anywhere else. Check logs there.
## Andriod ## Andriod
Take screensots like this: `adb exec-out screencap -p > screenshot.png` Take screensots like this: `adb exec-out screencap -p > screenshot.png`
## Asking Codex for a second opinion
You can ask Codex for a second opinion on a problem you're working on.
Set a high timeout, like 2 minutes.
Try not to bias it, state the problem clearly and let it work it out.
`codex exec "prompt"`

View File

@@ -30,11 +30,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { theme as defaultTheme, theme } from "../styles/theme"; import { theme as defaultTheme, theme } from "../styles/theme";
// Simple unique ID generator
let messageIdCounter = 0;
function generateMessageId(): string {
return `msg_${Date.now()}_${messageIdCounter++}`;
}
import { useWebSocket } from "@/hooks/use-websocket"; import { useWebSocket } from "@/hooks/use-websocket";
import { useAudioRecorder } from "@/hooks/use-audio-recorder"; import { useAudioRecorder } from "@/hooks/use-audio-recorder";
import { useAudioPlayer } from "@/hooks/use-audio-player"; import { useAudioPlayer } from "@/hooks/use-audio-player";
@@ -1247,9 +1242,8 @@ export default function VoiceAssistantScreen() {
function handleSelectConversation(newConversationId: string | null) { function handleSelectConversation(newConversationId: string | null) {
// Clear all state // Clear all state
setMessages([]); setMessages([]);
setAgentMessages(new Map()); setAgentStreamState(new Map());
setCurrentAssistantMessage(""); setCurrentAssistantMessage("");
setCurrentAgentMessages(new Map());
setUserInput(""); setUserInput("");
setArtifacts(new Map()); setArtifacts(new Map());
setCurrentArtifact(null); setCurrentArtifact(null);
@@ -1402,7 +1396,7 @@ export default function VoiceAssistantScreen() {
> >
{messages.length === 0 && !currentAssistantMessage && ( {messages.length === 0 && !currentAssistantMessage && (
<View style={styles.emptyState}> <View style={styles.emptyState}>
<Text style={styles.emptyStateTitle}>OnTheGo</Text> <Text style={styles.emptyStateTitle}>Hammock</Text>
<Text style={styles.emptyStateSubtitle}> <Text style={styles.emptyStateSubtitle}>
What would you like to work on? What would you like to work on?
</Text> </Text>

View File

@@ -6,9 +6,10 @@ import {
Pressable, Pressable,
TextInput, TextInput,
ScrollView, ScrollView,
KeyboardAvoidingView,
Platform,
} from "react-native"; } from "react-native";
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
import ReanimatedAnimated, { useAnimatedStyle } from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { X } from "lucide-react-native"; import { X } from "lucide-react-native";
import { StyleSheet } from "react-native-unistyles"; import { StyleSheet } from "react-native-unistyles";
import { theme as defaultTheme } from "@/styles/theme"; import { theme as defaultTheme } from "@/styles/theme";
@@ -37,10 +38,24 @@ export function CreateAgentModal({
onClose, onClose,
onCreateAgent, onCreateAgent,
}: CreateAgentModalProps) { }: CreateAgentModalProps) {
const insets = useSafeAreaInsets();
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
const [workingDir, setWorkingDir] = useState(""); const [workingDir, setWorkingDir] = useState("");
const [selectedMode, setSelectedMode] = useState("plan"); const [selectedMode, setSelectedMode] = useState("plan");
const [errorMessage, setErrorMessage] = useState(""); const [errorMessage, setErrorMessage] = useState("");
// Keyboard animation
const bottomInset = insets.bottom;
const animatedKeyboardStyle = useAnimatedStyle(() => {
"worklet";
const absoluteHeight = Math.abs(keyboardHeight.value);
const padding = Math.max(0, absoluteHeight - bottomInset);
return {
paddingBottom: padding,
};
});
function handleCreate() { function handleCreate() {
if (!workingDir.trim()) { if (!workingDir.trim()) {
setErrorMessage("Working directory is required"); setErrorMessage("Working directory is required");
@@ -65,12 +80,11 @@ export function CreateAgentModal({
transparent={true} transparent={true}
onRequestClose={handleClose} onRequestClose={handleClose}
> >
<KeyboardAvoidingView <View style={styles.modalOverlay}>
style={styles.modalOverlay}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<Pressable style={styles.modalBackdrop} onPress={handleClose} /> <Pressable style={styles.modalBackdrop} onPress={handleClose} />
<View style={styles.modalContent}> <ReanimatedAnimated.View
style={[styles.modalContent, animatedKeyboardStyle]}
>
<View style={styles.modalInner}> <View style={styles.modalInner}>
{/* Header */} {/* Header */}
<View style={styles.header}> <View style={styles.header}>
@@ -149,25 +163,29 @@ export function CreateAgentModal({
))} ))}
</View> </View>
</View> </View>
{/* Action Buttons */}
<View style={styles.buttonContainer}>
<Pressable
style={[styles.createButton, !workingDir.trim() && styles.createButtonDisabled]}
onPress={handleCreate}
disabled={!workingDir.trim()}
>
<Text style={styles.createButtonText}>Create Agent</Text>
</Pressable>
<Pressable style={styles.cancelButton} onPress={handleClose}>
<Text style={styles.cancelButtonText}>Cancel</Text>
</Pressable>
</View>
</ScrollView> </ScrollView>
{/* Fixed Footer with Create Button */}
<View
style={[
styles.footer,
{ paddingBottom: Math.max(insets.bottom, 16) },
]}
>
<Pressable
style={[
styles.createButton,
!workingDir.trim() && styles.createButtonDisabled,
]}
onPress={handleCreate}
disabled={!workingDir.trim()}
>
<Text style={styles.createButtonText}>Create Agent</Text>
</Pressable>
</View>
</View> </View>
</View> </ReanimatedAnimated.View>
</KeyboardAvoidingView> </View>
</Modal> </Modal>
); );
} }
@@ -175,19 +193,28 @@ export function CreateAgentModal({
const styles = StyleSheet.create((theme) => ({ const styles = StyleSheet.create((theme) => ({
modalOverlay: { modalOverlay: {
flex: 1, flex: 1,
backgroundColor: "rgba(0,0,0,0.5)", justifyContent: "flex-end",
}, },
modalBackdrop: { modalBackdrop: {
flex: 1, position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 1,
}, },
modalContent: { modalContent: {
backgroundColor: theme.colors.card, backgroundColor: theme.colors.card,
borderTopLeftRadius: theme.spacing[6], borderTopLeftRadius: theme.spacing[6],
borderTopRightRadius: theme.spacing[6], borderTopRightRadius: theme.spacing[6],
height: "75%", minHeight: "80%",
zIndex: 2,
elevation: 5,
}, },
modalInner: { modalInner: {
flex: 1, flex: 1,
minHeight: 0,
}, },
header: { header: {
flexDirection: "row", flexDirection: "row",
@@ -207,7 +234,7 @@ const styles = StyleSheet.create((theme) => ({
}, },
scrollContent: { scrollContent: {
padding: theme.spacing[6], padding: theme.spacing[6],
paddingBottom: theme.spacing[4] * 5, paddingBottom: theme.spacing[4],
}, },
formSection: { formSection: {
marginBottom: theme.spacing[6], marginBottom: theme.spacing[6],
@@ -289,14 +316,18 @@ const styles = StyleSheet.create((theme) => ({
color: theme.colors.mutedForeground, color: theme.colors.mutedForeground,
fontSize: theme.fontSize.sm, fontSize: theme.fontSize.sm,
}, },
buttonContainer: { footer: {
gap: theme.spacing[3], borderTopWidth: theme.borderWidth[1],
marginTop: theme.spacing[4], borderTopColor: theme.colors.border,
paddingHorizontal: theme.spacing[6],
paddingTop: theme.spacing[4],
backgroundColor: theme.colors.card,
}, },
createButton: { createButton: {
backgroundColor: theme.colors.palette.blue[500], backgroundColor: theme.colors.palette.blue[500],
paddingVertical: theme.spacing[4], paddingVertical: theme.spacing[4],
borderRadius: theme.borderRadius.lg, borderRadius: theme.borderRadius.lg,
marginBottom: theme.spacing[4],
alignItems: "center", alignItems: "center",
}, },
createButtonDisabled: { createButtonDisabled: {
@@ -308,17 +339,4 @@ const styles = StyleSheet.create((theme) => ({
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold, fontWeight: theme.fontWeight.semibold,
}, },
cancelButton: {
backgroundColor: "transparent",
paddingVertical: theme.spacing[4],
borderRadius: theme.borderRadius.lg,
alignItems: "center",
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
},
cancelButtonText: {
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold,
},
})); }));