From a0f39302919fbacf7a0d407f01b1a50413ea6f70 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Mon, 02 Mar 2026 23:15:13 +0100
Subject: [PATCH] feat: on-device speech recognition, navigation screen, session picker

---
 components/chat/InputBar.tsx |  157 +++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 125 insertions(+), 32 deletions(-)

diff --git a/components/chat/InputBar.tsx b/components/chat/InputBar.tsx
index 71a576a..6f1bc35 100644
--- a/components/chat/InputBar.tsx
+++ b/components/chat/InputBar.tsx
@@ -6,16 +6,23 @@
   TextInput,
   View,
 } from "react-native";
+import * as Haptics from "expo-haptics";
 import { VoiceButton } from "./VoiceButton";
 
 interface InputBarProps {
   onSendText: (text: string) => void;
-  onSendVoice: (audioUri: string, durationMs: number) => void;
+  onReplay: () => void;
+  isTextMode: boolean;
+  onToggleMode: () => void;
 }
 
-export function InputBar({ onSendText, onSendVoice }: InputBarProps) {
+export function InputBar({
+  onSendText,
+  onReplay,
+  isTextMode,
+  onToggleMode,
+}: InputBarProps) {
   const [text, setText] = useState("");
-  const [isVoiceMode, setIsVoiceMode] = useState(false);
   const inputRef = useRef<TextInput>(null);
 
   const handleSend = useCallback(() => {
@@ -25,42 +32,108 @@
     setText("");
   }, [text, onSendText]);
 
-  const toggleMode = useCallback(() => {
-    setIsVoiceMode((prev) => {
-      if (prev) {
-        // Switching to text mode — focus input after mode switch
-        setTimeout(() => inputRef.current?.focus(), 100);
-      } else {
-        Keyboard.dismiss();
-      }
-      return !prev;
-    });
-  }, []);
-
-  if (isVoiceMode) {
+  if (!isTextMode) {
+    // Voice mode: [Replay] [Talk] [Aa]
     return (
-      <View className="border-t border-pai-border bg-pai-bg">
-        {/* Mode toggle */}
+      <View
+        style={{
+          flexDirection: "row",
+          gap: 10,
+          paddingHorizontal: 16,
+          paddingVertical: 10,
+          paddingBottom: 6,
+          borderTopWidth: 1,
+          borderTopColor: "#2E2E45",
+          alignItems: "center",
+        }}
+      >
+        {/* Replay last message */}
         <Pressable
-          onPress={toggleMode}
-          className="absolute top-3 right-4 z-10 w-10 h-10 items-center justify-center"
+          onPress={() => {
+            Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+            onReplay();
+          }}
         >
-          <Text className="text-2xl">⌨️</Text>
+          <View
+            style={{
+              width: 68,
+              height: 68,
+              borderRadius: 34,
+              alignItems: "center",
+              justifyContent: "center",
+              backgroundColor: "#1A2E1A",
+              borderWidth: 1.5,
+              borderColor: "#3A6A3A",
+            }}
+          >
+            <Text style={{ fontSize: 24 }}>▶</Text>
+            <Text style={{ color: "#8ABF8A", fontSize: 10, marginTop: 1, fontWeight: "600" }}>Replay</Text>
+          </View>
         </Pressable>
 
-        <VoiceButton onVoiceMessage={onSendVoice} />
+        {/* Talk button — center, biggest */}
+        <View style={{ flex: 1, alignItems: "center" }}>
+          <VoiceButton onTranscript={onSendText} />
+        </View>
+
+        {/* Text mode toggle */}
+        <Pressable
+          onPress={() => {
+            Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+            onToggleMode();
+            setTimeout(() => inputRef.current?.focus(), 150);
+          }}
+        >
+          <View
+            style={{
+              width: 68,
+              height: 68,
+              borderRadius: 34,
+              alignItems: "center",
+              justifyContent: "center",
+              backgroundColor: "#1A1A3E",
+              borderWidth: 1.5,
+              borderColor: "#3A3A7A",
+            }}
+          >
+            <Text style={{ fontSize: 22, color: "#9898D0", fontWeight: "700" }}>Aa</Text>
+          </View>
+        </Pressable>
       </View>
     );
   }
 
+  // Text mode: [Mic] [TextInput] [Send]
   return (
-    <View className="border-t border-pai-border bg-pai-bg px-3 py-2 flex-row items-end gap-2">
+    <View
+      style={{
+        flexDirection: "row",
+        gap: 8,
+        paddingHorizontal: 12,
+        paddingVertical: 8,
+        borderTopWidth: 1,
+        borderTopColor: "#2E2E45",
+        alignItems: "flex-end",
+      }}
+    >
       {/* Voice mode toggle */}
       <Pressable
-        onPress={toggleMode}
-        className="w-10 h-10 items-center justify-center rounded-full bg-pai-bg-tertiary mb-0.5"
+        onPress={() => {
+          Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+          Keyboard.dismiss();
+          onToggleMode();
+        }}
+        style={{
+          width: 40,
+          height: 40,
+          borderRadius: 20,
+          alignItems: "center",
+          justifyContent: "center",
+          backgroundColor: "#1E1E2E",
+          marginBottom: 2,
+        }}
       >
-        <Text className="text-xl">🎤</Text>
+        <Text style={{ fontSize: 20 }}>🎤</Text>
       </Pressable>
 
       {/* Text input */}
@@ -75,19 +148,39 @@
         onSubmitEditing={handleSend}
         returnKeyType="send"
         blurOnSubmit
-        className="flex-1 bg-pai-bg-tertiary rounded-2xl px-4 py-2.5 text-pai-text text-base"
-        style={{ maxHeight: 120 }}
+        style={{
+          flex: 1,
+          backgroundColor: "#1E1E2E",
+          borderRadius: 20,
+          paddingHorizontal: 16,
+          paddingVertical: 10,
+          maxHeight: 120,
+          color: "#E8E8F0",
+          fontSize: 16,
+        }}
       />
 
       {/* Send button */}
       <Pressable
         onPress={handleSend}
         disabled={!text.trim()}
-        className={`w-10 h-10 rounded-full items-center justify-center mb-0.5 ${
-          text.trim() ? "bg-pai-accent" : "bg-pai-bg-tertiary"
-        }`}
+        style={{
+          width: 40,
+          height: 40,
+          borderRadius: 20,
+          alignItems: "center",
+          justifyContent: "center",
+          marginBottom: 2,
+          backgroundColor: text.trim() ? "#4A9EFF" : "#1E1E2E",
+        }}
       >
-        <Text className={`text-xl ${text.trim() ? "text-white" : "text-pai-text-muted"}`}>
+        <Text
+          style={{
+            fontSize: 18,
+            fontWeight: "bold",
+            color: text.trim() ? "#FFFFFF" : "#5A5A78",
+          }}
+        >
           ↑
         </Text>
       </Pressable>

--
Gitblit v1.3.1