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/CommandBar.tsx | 146 ++++++++++++++++++++++++++++++------------------
1 files changed, 91 insertions(+), 55 deletions(-)
diff --git a/components/chat/CommandBar.tsx b/components/chat/CommandBar.tsx
index 9853d8f..de9c0f4 100644
--- a/components/chat/CommandBar.tsx
+++ b/components/chat/CommandBar.tsx
@@ -1,67 +1,103 @@
import React, { useState } from "react";
-import { Pressable, ScrollView, Text, View } from "react-native";
-
-interface Command {
- label: string;
- value: string;
-}
-
-const DEFAULT_COMMANDS: Command[] = [
- { label: "/s", value: "/s" },
- { label: "/ss", value: "/ss" },
- { label: "/clear", value: "/clear" },
- { label: "/help", value: "/help" },
- { label: "/status", value: "/status" },
-];
+import { Pressable, Text, View, useWindowDimensions } from "react-native";
+import * as Haptics from "expo-haptics";
interface CommandBarProps {
- onCommand: (command: string) => void;
- commands?: Command[];
+ onSessions: () => void;
+ onScreenshot: () => void;
+ onHelp: () => void;
}
-export function CommandBar({
- onCommand,
- commands = DEFAULT_COMMANDS,
-}: CommandBarProps) {
- const [activeCommand, setActiveCommand] = useState<string | null>(null);
+export function CommandBar({ onSessions, onScreenshot, onHelp }: CommandBarProps) {
+ return (
+ <View
+ style={{
+ flexDirection: "row",
+ paddingHorizontal: 12,
+ paddingVertical: 6,
+ gap: 8,
+ }}
+ >
+ <CmdBtn icon="📋" label="Sessions" bg="#1A2744" border="#2E4A7A" onPress={onSessions} />
+ <CmdBtn icon="📸" label="Screen" bg="#1A3A2A" border="#2E6A4A" onPress={onScreenshot} />
+ <CmdBtn icon="❓" label="Help" bg="#3A1A2A" border="#6A2E4A" onPress={onHelp} />
+ </View>
+ );
+}
- function handlePress(command: Command) {
- setActiveCommand(command.value);
- onCommand(command.value);
- setTimeout(() => setActiveCommand(null), 200);
- }
+interface TextModeCommandBarProps {
+ onSessions: () => void;
+ onScreenshot: () => void;
+ onNavigate: () => void;
+ onClear: () => void;
+}
+
+export function TextModeCommandBar({
+ onSessions,
+ onScreenshot,
+ onNavigate,
+ onClear,
+}: TextModeCommandBarProps) {
+ return (
+ <View
+ style={{
+ flexDirection: "row",
+ paddingHorizontal: 12,
+ paddingVertical: 6,
+ gap: 8,
+ }}
+ >
+ <CmdBtn icon="📋" label="Sessions" bg="#1A2744" border="#2E4A7A" onPress={onSessions} />
+ <CmdBtn icon="📸" label="Screen" bg="#1A3A2A" border="#2E6A4A" onPress={onScreenshot} />
+ <CmdBtn icon="🧭" label="Navigate" bg="#2A2A1A" border="#5A5A2E" onPress={onNavigate} />
+ <CmdBtn icon="🗑" label="Clear" bg="#3A1A1A" border="#6A2E2E" onPress={onClear} />
+ </View>
+ );
+}
+
+function CmdBtn({
+ icon,
+ label,
+ bg,
+ border,
+ onPress,
+}: {
+ icon: string;
+ label: string;
+ bg: string;
+ border: string;
+ onPress: () => void;
+}) {
+ const [pressed, setPressed] = useState(false);
+ const { width } = useWindowDimensions();
return (
- <View className="border-t border-pai-border">
- <ScrollView
- horizontal
- showsHorizontalScrollIndicator={false}
- contentContainerStyle={{ paddingHorizontal: 12, paddingVertical: 8, gap: 8 }}
+ <View style={{ flex: 1 }}>
+ <Pressable
+ onPressIn={() => setPressed(true)}
+ onPressOut={() => setPressed(false)}
+ onPress={() => {
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+ onPress();
+ }}
>
- {commands.map((cmd) => (
- <Pressable
- key={cmd.value}
- onPress={() => handlePress(cmd)}
- className="rounded-full px-4 py-2"
- style={({ pressed }) => ({
- backgroundColor:
- activeCommand === cmd.value || pressed
- ? "#4A9EFF"
- : "#1E1E2E",
- })}
- >
- <Text
- className="text-sm font-medium"
- style={{
- color:
- activeCommand === cmd.value ? "#FFFFFF" : "#9898B0",
- }}
- >
- {cmd.label}
- </Text>
- </Pressable>
- ))}
- </ScrollView>
+ <View
+ style={{
+ height: 68,
+ borderRadius: 16,
+ alignItems: "center",
+ justifyContent: "center",
+ backgroundColor: pressed ? "#4A9EFF" : bg,
+ borderWidth: 1.5,
+ borderColor: pressed ? "#4A9EFF" : border,
+ }}
+ >
+ <Text style={{ fontSize: 26, marginBottom: 2 }}>{icon}</Text>
+ <Text style={{ color: "#C8C8E0", fontSize: 13, fontWeight: "700" }}>
+ {label}
+ </Text>
+ </View>
+ </Pressable>
</View>
);
}
--
Gitblit v1.3.1