diff --git a/packages/mobile/src/screens/OrchestratorScreen.tsx b/packages/mobile/src/screens/OrchestratorScreen.tsx index 031bf5ce4..62ebfb7ea 100644 --- a/packages/mobile/src/screens/OrchestratorScreen.tsx +++ b/packages/mobile/src/screens/OrchestratorScreen.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useCallback } from "react"; import { View, Text, @@ -6,11 +6,21 @@ import { ScrollView, TouchableOpacity, ActivityIndicator, + TextInput, + Alert, } from "react-native"; import type { NativeStackScreenProps } from "@react-navigation/native-stack"; import type { RootStackParamList } from "../navigation/RootNavigator"; import { useSessions } from "../hooks/useSessions"; -import { getAttentionLevel, isTerminal, type DashboardSession } from "../types"; +import { useSession } from "../hooks/useSession"; +import { useBackend } from "../context/BackendContext"; +import AttentionBadge from "../components/AttentionBadge"; +import { + getAttentionLevel, + relativeTime, + ATTENTION_COLORS, + type DashboardSession, +} from "../types"; type Props = NativeStackScreenProps; @@ -24,7 +34,11 @@ function getZoneCounts(sessions: DashboardSession[]) { } export default function OrchestratorScreen({ navigation }: Props) { - const { sessions, stats, orchestratorId, loading, error, refresh } = useSessions(); + const { sessions, orchestratorId, loading, error, refresh } = useSessions(); + const { sendMessage, terminalWsUrl } = useBackend(); + const { session: orchSession } = useSession(orchestratorId ?? ""); + const [message, setMessage] = useState(""); + const [sending, setSending] = useState(false); React.useLayoutEffect(() => { navigation.setOptions({ @@ -37,7 +51,19 @@ export default function OrchestratorScreen({ navigation }: Props) { }, [navigation]); const zones = getZoneCounts(sessions); - const activeSessions = sessions.filter((s) => !isTerminal(s)); + + const handleSend = useCallback(async () => { + if (!message.trim() || !orchestratorId) return; + setSending(true); + try { + await sendMessage(orchestratorId, message.trim()); + setMessage(""); + } catch (err) { + Alert.alert("Error", err instanceof Error ? err.message : "Failed to send message"); + } finally { + setSending(false); + } + }, [message, sendMessage, orchestratorId]); if (loading && sessions.length === 0) { return ( @@ -58,30 +84,76 @@ export default function OrchestratorScreen({ navigation }: Props) { ); } + const orchLevel = orchSession ? getAttentionLevel(orchSession) : null; + const orchColor = orchLevel ? ATTENTION_COLORS[orchLevel] : "#8b949e"; + return ( - {/* Orchestrator Status */} + {/* Orchestrator Session Details */} Orchestrator - {orchestratorId ? ( - navigation.navigate("SessionDetail", { sessionId: orchestratorId })} - > - - - Running + {orchestratorId && orchSession ? ( + + + + + Running + + - {orchestratorId} - Tap to view terminal - + {orchestratorId} + + {orchSession.status} + {orchSession.activity ? ` ยท ${orchSession.activity}` : ""} + + {orchSession.summary && !orchSession.summaryIsFallback && ( + {orchSession.summary} + )} + + Last activity: {relativeTime(orchSession.lastActivityAt)} + + + {/* Actions */} + + navigation.navigate("Terminal", { sessionId: orchestratorId, terminalWsUrl })} + > + Open Terminal + + + + {/* Send message */} + + + + {sending ? ( + + ) : ( + Send + )} + + + ) : ( - - + + - Not running + Not running - Start with: ao start <project> + Start with: ao start <project> )} @@ -99,15 +171,6 @@ export default function OrchestratorScreen({ navigation }: Props) { - {/* Stats */} - - Overview - - - - - - ); } @@ -121,15 +184,6 @@ function ZoneBadge({ label, count, color }: { label: string; count: number; colo ); } -function StatRow({ label, value }: { label: string; value: string }) { - return ( - - {label} - {value} - - ); -} - const styles = StyleSheet.create({ container: { flex: 1, @@ -160,40 +214,113 @@ const styles = StyleSheet.create({ textTransform: "uppercase", marginBottom: 12, }, - // Orchestrator card - orchestratorCard: { + // Orchestrator detail card + orchDetailCard: { backgroundColor: "#0d1117", borderWidth: 1, borderColor: "#30363d", + borderLeftWidth: 3, borderRadius: 8, padding: 14, }, - orchestratorRow: { + orchHeaderRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginBottom: 6, + }, + orchStatusRow: { flexDirection: "row", alignItems: "center", gap: 8, - marginBottom: 6, }, dot: { width: 10, height: 10, borderRadius: 5, }, - orchestratorText: { + orchRunning: { color: "#3fb950", fontSize: 15, fontWeight: "600", }, - orchestratorId: { + orchId: { color: "#8b949e", fontSize: 12, fontFamily: "monospace", marginBottom: 4, }, - orchestratorHint: { + orchStatus: { + color: "#8b949e", + fontSize: 13, + marginBottom: 4, + }, + orchSummary: { + color: "#e6edf3", + fontSize: 13, + lineHeight: 18, + marginBottom: 4, + }, + orchTimingRow: { + marginBottom: 10, + }, + orchTiming: { color: "#6e7681", fontSize: 12, }, + orchHint: { + color: "#6e7681", + fontSize: 12, + marginTop: 4, + }, + orchActions: { + marginBottom: 10, + }, + terminalButton: { + backgroundColor: "#21262d", + borderWidth: 1, + borderColor: "#30363d", + borderRadius: 8, + padding: 12, + alignItems: "center", + }, + terminalButtonText: { + color: "#e6edf3", + fontSize: 14, + fontWeight: "600", + }, + orchMessageRow: { + flexDirection: "row", + alignItems: "center", + gap: 8, + }, + orchMessageInput: { + flex: 1, + backgroundColor: "#161b22", + borderWidth: 1, + borderColor: "#30363d", + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 8, + color: "#e6edf3", + fontSize: 14, + }, + sendButton: { + backgroundColor: "#238636", + borderRadius: 8, + paddingHorizontal: 14, + height: 38, + alignItems: "center", + justifyContent: "center", + }, + sendButtonDisabled: { + backgroundColor: "#21262d", + }, + sendButtonText: { + color: "#fff", + fontSize: 14, + fontWeight: "700", + }, // Zones grid zonesGrid: { flexDirection: "row", @@ -221,21 +348,6 @@ const styles = StyleSheet.create({ fontWeight: "600", marginTop: 2, }, - // Stats - statRow: { - flexDirection: "row", - justifyContent: "space-between", - paddingVertical: 6, - }, - statLabel: { - color: "#8b949e", - fontSize: 13, - }, - statValue: { - color: "#e6edf3", - fontSize: 13, - fontWeight: "600", - }, // Error errorText: { color: "#f85149",