Files
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

104 lines
2.8 KiB
TypeScript

import {
DarkTheme,
DefaultTheme,
ThemeProvider,
type Theme,
} from "@react-navigation/native";
import { Drawer } from "expo-router/drawer";
import { StatusBar } from "expo-status-bar";
import "react-native-reanimated";
import { Platform, Pressable } from "react-native";
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import { useFonts } from "expo-font";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {
AssistantRuntimeProvider,
useAui,
Tools,
} from "@assistant-ui/react-native";
import { useAppRuntime } from "@/hooks/use-app-runtime";
import { useTheme } from "@/hooks/use-theme";
import { Icon } from "@/components/ui/icon";
import { ThreadListDrawer } from "@/components/thread-list/ThreadListDrawer";
import { haptics } from "@/lib/haptics";
import toolkit from "@/components/assistant-ui/tools";
function NewChatButton() {
const aui = useAui();
const { colors } = useTheme();
return (
<Pressable
accessibilityLabel="New chat"
hitSlop={8}
onPress={() => {
haptics.selection();
aui.threads().switchToNewThread();
}}
style={{ marginRight: 16 }}
>
<Icon name="compose" size={22} color={colors.foreground} />
</Pressable>
);
}
function DrawerLayout() {
const { isDark, colors } = useTheme();
const base = isDark ? DarkTheme : DefaultTheme;
const navTheme: Theme = {
...base,
colors: {
...base.colors,
background: colors.background,
card: colors.background,
text: colors.foreground,
border: colors.border,
primary: colors.foreground,
},
};
return (
<ThemeProvider value={navTheme}>
<Drawer
drawerContent={(props) => <ThreadListDrawer {...props} />}
screenOptions={{
headerRight: () => <NewChatButton />,
headerShadowVisible: false,
headerTintColor: colors.foreground,
headerStyle: { backgroundColor: colors.background },
headerTitleStyle: { fontWeight: "600" },
drawerType: "front",
swipeEnabled: true,
drawerStyle: { backgroundColor: colors.background },
}}
>
<Drawer.Screen name="index" options={{ title: "Chat" }} />
</Drawer>
<StatusBar style="auto" />
</ThemeProvider>
);
}
export default function RootLayout() {
// iOS renders SF Symbols, so it never needs the MaterialIcons glyph font.
const [fontsLoaded] = useFonts(
Platform.OS === "ios" ? {} : MaterialIcons.font,
);
const runtime = useAppRuntime();
const aui = useAui({
tools: Tools({ toolkit }),
});
if (!fontsLoaded) return null;
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<AssistantRuntimeProvider runtime={runtime} aui={aui}>
<DrawerLayout />
</AssistantRuntimeProvider>
</GestureHandlerRootView>
);
}