Files
handler/app/App.tsx
T
Claude daecaa7e6b Implement Handler mobile app (React Native + Expo iOS)
Build the committed 2a interactive prototype from Handler Mobile.dc.html as
a real Expo app: six wired screens (Fleet, Agent detail, Answer, Spawn, Log,
Settings) with the exact state logic ported from the design's renderVals().

- Port Leeworks tokens (colors light/dark, typography, spacing, radii,
  shadows) to typed RN values in src/theme.
- Reimplement the design-system components used by the screens (Button,
  Badge, Icon, Switch, Select, TextField, segmented control, chip, tab bar).
- Shared store mirrors the prototype's single-screen navigation and the
  Waiting -> Running flip when agt-7a1d is answered.
- Real OS status bar / home indicator (safe-area insets) replace the mock
  phone chrome; dark mode follows system appearance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdF2hHVQ4UUBFPtpeMQ81o
2026-07-20 18:48:30 +00:00

103 lines
2.5 KiB
TypeScript

import React from "react";
import { View } from "react-native";
import { StatusBar } from "expo-status-bar";
import {
SafeAreaProvider,
useSafeAreaInsets,
} from "react-native-safe-area-context";
import {
useFonts,
Outfit_400Regular,
Outfit_500Medium,
Outfit_600SemiBold,
Outfit_700Bold,
Outfit_800ExtraBold,
} from "@expo-google-fonts/outfit";
import {
Figtree_400Regular,
Figtree_500Medium,
Figtree_600SemiBold,
Figtree_700Bold,
Figtree_400Regular_Italic,
} from "@expo-google-fonts/figtree";
import {
SplineSansMono_400Regular,
SplineSansMono_500Medium,
SplineSansMono_600SemiBold,
} from "@expo-google-fonts/spline-sans-mono";
import { useTheme } from "./src/theme/useTheme";
import { AppStateProvider, useAppState } from "./src/state/AppState";
import { FleetScreen } from "./src/screens/FleetScreen";
import { AgentDetailScreen } from "./src/screens/AgentDetailScreen";
import { AnswerScreen } from "./src/screens/AnswerScreen";
import { SpawnScreen } from "./src/screens/SpawnScreen";
import { LogScreen } from "./src/screens/LogScreen";
import { SettingsScreen } from "./src/screens/SettingsScreen";
function Router() {
const { screen } = useAppState();
const { scheme, colors } = useTheme();
const Screen = {
fleet: FleetScreen,
detail: AgentDetailScreen,
answer: AnswerScreen,
spawn: SpawnScreen,
log: LogScreen,
settings: SettingsScreen,
}[screen];
return (
<View style={{ flex: 1, backgroundColor: colors.surfacePage }}>
<StatusBar style={scheme === "dark" ? "light" : "dark"} />
<Screen />
</View>
);
}
export default function App() {
const [fontsLoaded] = useFonts({
Outfit_400Regular,
Outfit_500Medium,
Outfit_600SemiBold,
Outfit_700Bold,
Outfit_800ExtraBold,
Figtree_400Regular,
Figtree_500Medium,
Figtree_600SemiBold,
Figtree_700Bold,
Figtree_400Regular_Italic,
SplineSansMono_400Regular,
SplineSansMono_500Medium,
SplineSansMono_600SemiBold,
});
return (
<SafeAreaProvider>
{fontsLoaded ? (
<AppStateProvider>
<Router />
</AppStateProvider>
) : (
<SplashPlaceholder />
)}
</SafeAreaProvider>
);
}
/** Blank page-colored screen while fonts load (avoids a font flash). */
function SplashPlaceholder() {
const { colors } = useTheme();
const insets = useSafeAreaInsets();
return (
<View
style={{
flex: 1,
backgroundColor: colors.surfacePage,
paddingTop: insets.top,
}}
/>
);
}