create-better-t-stack 2.14.4 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/index.js +18 -14
  2. package/package.json +1 -1
  3. package/templates/examples/ai/native/nativewind/app/(drawer)/ai.tsx.hbs +155 -0
  4. package/templates/examples/ai/native/nativewind/polyfills.js +25 -0
  5. package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +279 -0
  6. package/templates/examples/ai/native/unistyles/polyfills.js +25 -0
  7. package/templates/examples/todo/native/unistyles/app/(drawer)/todos.tsx.hbs +340 -0
  8. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/_layout.tsx +24 -7
  9. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/index.tsx +15 -13
  10. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/two.tsx +15 -13
  11. package/templates/frontend/native/nativewind/app/(drawer)/_layout.tsx.hbs +25 -9
  12. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +43 -30
  13. package/templates/frontend/native/nativewind/app/+not-found.tsx +20 -9
  14. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +3 -0
  15. package/templates/frontend/native/nativewind/app/modal.tsx +9 -7
  16. package/templates/frontend/native/nativewind/components/container.tsx +2 -3
  17. package/templates/frontend/native/nativewind/components/header-button.tsx +24 -29
  18. package/templates/frontend/native/nativewind/components/tabbar-icon.tsx +3 -10
  19. package/templates/frontend/native/nativewind/global.css +36 -11
  20. package/templates/frontend/native/nativewind/lib/constants.ts +8 -8
  21. package/templates/frontend/native/nativewind/{package.json → package.json.hbs} +4 -0
  22. package/templates/frontend/native/nativewind/tailwind.config.js +27 -0
  23. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/_layout.tsx +9 -4
  24. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/index.tsx +25 -17
  25. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/two.tsx +26 -18
  26. package/templates/frontend/native/unistyles/app/(drawer)/{_layout.tsx → _layout.tsx.hbs} +35 -7
  27. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +121 -58
  28. package/templates/frontend/native/unistyles/app/+not-found.tsx +45 -14
  29. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +9 -6
  30. package/templates/frontend/native/unistyles/app/modal.tsx +18 -14
  31. package/templates/frontend/native/unistyles/components/container.tsx +3 -8
  32. package/templates/frontend/native/unistyles/components/header-button.tsx +33 -28
  33. package/templates/frontend/native/unistyles/components/tabbar-icon.tsx +3 -10
  34. package/templates/frontend/native/unistyles/{package.json → package.json.hbs} +5 -1
  35. package/templates/frontend/native/unistyles/theme.ts +82 -19
  36. package/templates/frontend/native/unistyles/unistyles.ts +4 -4
  37. /package/templates/examples/todo/native/nativewind/app/{todo.tsx.hbs → (drawer)/todos.tsx.hbs} +0 -0
@@ -0,0 +1,340 @@
1
+ import { useState } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ TouchableOpacity,
7
+ ScrollView,
8
+ ActivityIndicator,
9
+ Alert,
10
+ } from "react-native";
11
+ import { Ionicons } from "@expo/vector-icons";
12
+ import { StyleSheet, useUnistyles } from "react-native-unistyles";
13
+
14
+ {{#if (eq backend "convex")}}
15
+ import { useMutation, useQuery } from "convex/react";
16
+ import { api } from "@{{projectName}}/backend/convex/_generated/api";
17
+ import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
18
+ {{else}}
19
+ import { useMutation, useQuery } from "@tanstack/react-query";
20
+ {{/if}}
21
+
22
+ import { Container } from "@/components/container";
23
+ {{#unless (eq backend "convex")}}
24
+ {{#if (eq api "orpc")}}
25
+ import { orpc } from "@/utils/orpc";
26
+ {{/if}}
27
+ {{#if (eq api "trpc")}}
28
+ import { trpc } from "@/utils/trpc";
29
+ {{/if}}
30
+ {{/unless}}
31
+
32
+ export default function TodosScreen() {
33
+ const [newTodoText, setNewTodoText] = useState("");
34
+ const { theme } = useUnistyles();
35
+
36
+ {{#if (eq backend "convex")}}
37
+ const todos = useQuery(api.todos.getAll);
38
+ const createTodoMutation = useMutation(api.todos.create);
39
+ const toggleTodoMutation = useMutation(api.todos.toggle);
40
+ const deleteTodoMutation = useMutation(api.todos.deleteTodo);
41
+
42
+ const handleAddTodo = async () => {
43
+ const text = newTodoText.trim();
44
+ if (!text) return;
45
+ await createTodoMutation({ text });
46
+ setNewTodoText("");
47
+ };
48
+
49
+ const handleToggleTodo = (id: Id<"todos">, currentCompleted: boolean) => {
50
+ toggleTodoMutation({ id, completed: !currentCompleted });
51
+ };
52
+
53
+ const handleDeleteTodo = (id: Id<"todos">) => {
54
+ Alert.alert("Delete Todo", "Are you sure you want to delete this todo?", [
55
+ { text: "Cancel", style: "cancel" },
56
+ {
57
+ text: "Delete",
58
+ style: "destructive",
59
+ onPress: () => deleteTodoMutation({ id }),
60
+ },
61
+ ]);
62
+ };
63
+ {{else}}
64
+ {{#if (eq api "orpc")}}
65
+ const todos = useQuery(orpc.todo.getAll.queryOptions());
66
+ const createMutation = useMutation(
67
+ orpc.todo.create.mutationOptions({
68
+ onSuccess: () => {
69
+ todos.refetch();
70
+ setNewTodoText("");
71
+ },
72
+ })
73
+ );
74
+ const toggleMutation = useMutation(
75
+ orpc.todo.toggle.mutationOptions({
76
+ onSuccess: () => todos.refetch(),
77
+ })
78
+ );
79
+ const deleteMutation = useMutation(
80
+ orpc.todo.delete.mutationOptions({
81
+ onSuccess: () => todos.refetch(),
82
+ })
83
+ );
84
+ {{/if}}
85
+ {{#if (eq api "trpc")}}
86
+ const todos = useQuery(trpc.todo.getAll.queryOptions());
87
+ const createMutation = useMutation(
88
+ trpc.todo.create.mutationOptions({
89
+ onSuccess: () => {
90
+ todos.refetch();
91
+ setNewTodoText("");
92
+ },
93
+ })
94
+ );
95
+ const toggleMutation = useMutation(
96
+ trpc.todo.toggle.mutationOptions({
97
+ onSuccess: () => todos.refetch(),
98
+ })
99
+ );
100
+ const deleteMutation = useMutation(
101
+ trpc.todo.delete.mutationOptions({
102
+ onSuccess: () => todos.refetch(),
103
+ })
104
+ );
105
+ {{/if}}
106
+
107
+ const handleAddTodo = () => {
108
+ if (newTodoText.trim()) {
109
+ createMutation.mutate({ text: newTodoText });
110
+ }
111
+ };
112
+
113
+ const handleToggleTodo = (id: number, completed: boolean) => {
114
+ toggleMutation.mutate({ id, completed: !completed });
115
+ };
116
+
117
+ const handleDeleteTodo = (id: number) => {
118
+ Alert.alert("Delete Todo", "Are you sure you want to delete this todo?", [
119
+ { text: "Cancel", style: "cancel" },
120
+ {
121
+ text: "Delete",
122
+ style: "destructive",
123
+ onPress: () => deleteMutation.mutate({ id }),
124
+ },
125
+ ]);
126
+ };
127
+ {{/if}}
128
+
129
+ const isLoading = {{#if (eq backend "convex")}}!todos{{else}}todos.isLoading{{/if}};
130
+ const isCreating = {{#if (eq backend "convex")}}false{{else}}createMutation.isPending{{/if}};
131
+ const primaryButtonTextColor = theme.colors.background;
132
+
133
+ return (
134
+ <Container>
135
+ <ScrollView style={styles.scrollView}>
136
+ <View style={styles.headerContainer}>
137
+ <Text style={styles.headerTitle}>Todo List</Text>
138
+ <Text style={styles.headerSubtitle}>
139
+ Manage your tasks efficiently
140
+ </Text>
141
+
142
+ <View style={styles.inputContainer}>
143
+ <TextInput
144
+ value={newTodoText}
145
+ onChangeText={setNewTodoText}
146
+ placeholder="Add a new task..."
147
+ placeholderTextColor={theme.colors.border}
148
+ editable={!isCreating}
149
+ style={styles.textInput}
150
+ onSubmitEditing={handleAddTodo}
151
+ returnKeyType="done"
152
+ />
153
+ <TouchableOpacity
154
+ onPress={handleAddTodo}
155
+ disabled={isCreating || !newTodoText.trim()}
156
+ style={[
157
+ styles.addButton,
158
+ (isCreating || !newTodoText.trim()) && styles.addButtonDisabled,
159
+ ]}
160
+ >
161
+ {isCreating ? (
162
+ <ActivityIndicator size="small" color={primaryButtonTextColor} />
163
+ ) : (
164
+ <Ionicons
165
+ name="add"
166
+ size={24}
167
+ color={primaryButtonTextColor}
168
+ />
169
+ )}
170
+ </TouchableOpacity>
171
+ </View>
172
+ </View>
173
+
174
+ {isLoading && (
175
+ <View style={styles.loadingContainer}>
176
+ <ActivityIndicator size="large" color={theme.colors.primary} />
177
+ <Text style={styles.loadingText}>Loading todos...</Text>
178
+ </View>
179
+ )}
180
+
181
+ {{#if (eq backend "convex")}}
182
+ {todos && todos.length === 0 && !isLoading && (
183
+ <Text style={styles.emptyText}>No todos yet. Add one!</Text>
184
+ )}
185
+ {todos?.map((todo) => (
186
+ <View key={todo._id} style={styles.todoItem}>
187
+ <TouchableOpacity
188
+ onPress={() => handleToggleTodo(todo._id, todo.completed)}
189
+ style={styles.todoContent}
190
+ >
191
+ <Ionicons
192
+ name={todo.completed ? "checkbox" : "square-outline"}
193
+ size={24}
194
+ color={todo.completed ? theme.colors.primary : theme.colors.typography}
195
+ style={styles.checkbox}
196
+ />
197
+ <Text
198
+ style={[
199
+ styles.todoText,
200
+ todo.completed && styles.todoTextCompleted,
201
+ ]}
202
+ >
203
+ {todo.text}
204
+ </Text>
205
+ </TouchableOpacity>
206
+ <TouchableOpacity onPress={() => handleDeleteTodo(todo._id)}>
207
+ <Ionicons name="trash-outline" size={24} color={theme.colors.destructive} />
208
+ </TouchableOpacity>
209
+ </View>
210
+ ))}
211
+ {{else}}
212
+ {todos.data && todos.data.length === 0 && !isLoading && (
213
+ <Text style={styles.emptyText}>No todos yet. Add one!</Text>
214
+ )}
215
+ {todos.data?.map((todo: { id: number; text: string; completed: boolean }) => (
216
+ <View key={todo.id} style={styles.todoItem}>
217
+ <TouchableOpacity
218
+ onPress={() => handleToggleTodo(todo.id, todo.completed)}
219
+ style={styles.todoContent}
220
+ >
221
+ <Ionicons
222
+ name={todo.completed ? "checkbox" : "square-outline"}
223
+ size={24}
224
+ color={todo.completed ? theme.colors.primary : theme.colors.typography}
225
+ style={styles.checkbox}
226
+ />
227
+ <Text
228
+ style={[
229
+ styles.todoText,
230
+ todo.completed && styles.todoTextCompleted,
231
+ ]}
232
+ >
233
+ {todo.text}
234
+ </Text>
235
+ </TouchableOpacity>
236
+ <TouchableOpacity onPress={() => handleDeleteTodo(todo.id)}>
237
+ <Ionicons name="trash-outline" size={24} color={theme.colors.destructive} />
238
+ </TouchableOpacity>
239
+ </View>
240
+ ))}
241
+ {{/if}}
242
+ </ScrollView>
243
+ </Container>
244
+ );
245
+ }
246
+
247
+ const styles = StyleSheet.create((theme) => ({
248
+ scrollView: {
249
+ flex: 1,
250
+ },
251
+ headerContainer: {
252
+ paddingHorizontal: theme.spacing.md,
253
+ paddingVertical: theme.spacing.lg,
254
+ borderBottomWidth: 1,
255
+ borderBottomColor: theme.colors.border,
256
+ backgroundColor: theme.colors.background,
257
+ },
258
+ headerTitle: {
259
+ fontSize: 28,
260
+ fontWeight: "bold",
261
+ color: theme.colors.typography,
262
+ marginBottom: theme.spacing.sm,
263
+ },
264
+ headerSubtitle: {
265
+ fontSize: 16,
266
+ color: theme.colors.typography,
267
+ marginBottom: theme.spacing.md,
268
+ },
269
+ inputContainer: {
270
+ flexDirection: "row",
271
+ alignItems: "center",
272
+ marginBottom: theme.spacing.md,
273
+ },
274
+ textInput: {
275
+ flex: 1,
276
+ borderWidth: 1,
277
+ borderColor: theme.colors.border,
278
+ borderRadius: 8,
279
+ paddingHorizontal: theme.spacing.md,
280
+ paddingVertical: theme.spacing.sm,
281
+ color: theme.colors.typography,
282
+ backgroundColor: theme.colors.background,
283
+ marginRight: theme.spacing.sm,
284
+ fontSize: 16,
285
+ },
286
+ addButton: {
287
+ backgroundColor: theme.colors.primary,
288
+ padding: theme.spacing.sm,
289
+ borderRadius: 8,
290
+ justifyContent: "center",
291
+ alignItems: "center",
292
+ },
293
+ addButtonDisabled: {
294
+ backgroundColor: theme.colors.border,
295
+ },
296
+ loadingContainer: {
297
+ flex: 1,
298
+ justifyContent: "center",
299
+ alignItems: "center",
300
+ padding: theme.spacing.lg,
301
+ },
302
+ loadingText: {
303
+ marginTop: theme.spacing.sm,
304
+ fontSize: 16,
305
+ color: theme.colors.typography,
306
+ },
307
+ emptyText: {
308
+ textAlign: "center",
309
+ marginTop: theme.spacing.xl,
310
+ fontSize: 16,
311
+ color: theme.colors.typography,
312
+ },
313
+ todoItem: {
314
+ flexDirection: "row",
315
+ justifyContent: "space-between",
316
+ alignItems: "center",
317
+ paddingVertical: theme.spacing.md,
318
+ paddingHorizontal: theme.spacing.md,
319
+ borderBottomWidth: 1,
320
+ borderBottomColor: theme.colors.border,
321
+ backgroundColor: theme.colors.background,
322
+ },
323
+ todoContent: {
324
+ flexDirection: "row",
325
+ alignItems: "center",
326
+ flex: 1,
327
+ },
328
+ checkbox: {
329
+ marginRight: theme.spacing.md,
330
+ },
331
+ todoText: {
332
+ fontSize: 16,
333
+ color: theme.colors.typography,
334
+ flex: 1,
335
+ },
336
+ todoTextCompleted: {
337
+ textDecorationLine: "line-through",
338
+ color: theme.colors.border,
339
+ },
340
+ }));
@@ -1,27 +1,44 @@
1
- import { Tabs } from "expo-router";
2
-
3
1
  import { TabBarIcon } from "@/components/tabbar-icon";
2
+ import { useColorScheme } from "@/lib/use-color-scheme";
3
+ import { Tabs } from "expo-router";
4
4
 
5
5
  export default function TabLayout() {
6
+ const { isDarkColorScheme } = useColorScheme();
7
+
6
8
  return (
7
9
  <Tabs
8
10
  screenOptions={{
9
11
  headerShown: false,
10
- tabBarActiveTintColor: "black",
12
+ tabBarActiveTintColor: isDarkColorScheme
13
+ ? "hsl(217.2 91.2% 59.8%)"
14
+ : "hsl(221.2 83.2% 53.3%)",
15
+ tabBarInactiveTintColor: isDarkColorScheme
16
+ ? "hsl(215 20.2% 65.1%)"
17
+ : "hsl(215.4 16.3% 46.9%)",
18
+ tabBarStyle: {
19
+ backgroundColor: isDarkColorScheme
20
+ ? "hsl(222.2 84% 4.9%)"
21
+ : "hsl(0 0% 100%)",
22
+ borderTopColor: isDarkColorScheme
23
+ ? "hsl(217.2 32.6% 17.5%)"
24
+ : "hsl(214.3 31.8% 91.4%)",
25
+ },
11
26
  }}
12
27
  >
13
28
  <Tabs.Screen
14
29
  name="index"
15
30
  options={{
16
- title: "Tab One",
17
- tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
31
+ title: "Home",
32
+ tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
18
33
  }}
19
34
  />
20
35
  <Tabs.Screen
21
36
  name="two"
22
37
  options={{
23
- title: "Tab Two",
24
- tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
38
+ title: "Explore",
39
+ tabBarIcon: ({ color }) => (
40
+ <TabBarIcon name="compass" color={color} />
41
+ ),
25
42
  }}
26
43
  />
27
44
  </Tabs>
@@ -1,17 +1,19 @@
1
1
  import { Container } from "@/components/container";
2
- import { Text, View } from "react-native";
2
+ import { ScrollView, Text, View } from "react-native";
3
3
 
4
4
  export default function TabOne() {
5
- return (
6
- <Container>
7
- <View className="p-6 flex-1 justify-center">
8
- <Text className="text-2xl font-bold text-foreground text-center mb-4">
9
- Tab One
10
- </Text>
11
- <Text className="text-foreground text-center">
12
- This is the first tab of the application.
13
- </Text>
14
- </View>
15
- </Container>
16
- );
5
+ return (
6
+ <Container>
7
+ <ScrollView className="flex-1 p-6">
8
+ <View className="py-8">
9
+ <Text className="text-3xl font-bold text-foreground mb-2">
10
+ Tab One
11
+ </Text>
12
+ <Text className="text-lg text-muted-foreground">
13
+ Explore the first section of your app
14
+ </Text>
15
+ </View>
16
+ </ScrollView>
17
+ </Container>
18
+ );
17
19
  }
@@ -1,17 +1,19 @@
1
1
  import { Container } from "@/components/container";
2
- import { Text, View } from "react-native";
2
+ import { ScrollView, Text, View } from "react-native";
3
3
 
4
4
  export default function TabTwo() {
5
- return (
6
- <Container>
7
- <View className="p-6 flex-1 justify-center">
8
- <Text className="text-2xl font-bold text-foreground text-center mb-4">
9
- Tab Two
10
- </Text>
11
- <Text className="text-foreground text-center">
12
- This is the second tab of the application.
13
- </Text>
14
- </View>
15
- </Container>
16
- );
5
+ return (
6
+ <Container>
7
+ <ScrollView className="flex-1 p-6">
8
+ <View className="py-8">
9
+ <Text className="text-3xl font-bold text-foreground mb-2">
10
+ Tab Two
11
+ </Text>
12
+ <Text className="text-lg text-muted-foreground">
13
+ Discover more features and content
14
+ </Text>
15
+ </View>
16
+ </ScrollView>
17
+ </Container>
18
+ );
17
19
  }
@@ -17,6 +17,21 @@ const DrawerLayout = () => {
17
17
  ),
18
18
  }}
19
19
  />
20
+ <Drawer.Screen
21
+ name="(tabs)"
22
+ options=\{{
23
+ headerTitle: "Tabs",
24
+ drawerLabel: "Tabs",
25
+ drawerIcon: ({ size, color }) => (
26
+ <MaterialIcons name="border-bottom" size={size} color={color} />
27
+ ),
28
+ headerRight: () => (
29
+ <Link href="/modal" asChild>
30
+ <HeaderButton />
31
+ </Link>
32
+ ),
33
+ }}
34
+ />
20
35
  {{#if (includes examples "todo")}}
21
36
  <Drawer.Screen
22
37
  name="todos"
@@ -29,21 +44,22 @@ const DrawerLayout = () => {
29
44
  }}
30
45
  />
31
46
  {{/if}}
47
+ {{#if (includes examples "ai")}}
32
48
  <Drawer.Screen
33
- name="(tabs)"
49
+ name="ai"
34
50
  options=\{{
35
- headerTitle: "Tabs",
36
- drawerLabel: "Tabs",
51
+ headerTitle: "AI",
52
+ drawerLabel: "AI",
37
53
  drawerIcon: ({ size, color }) => (
38
- <MaterialIcons name="border-bottom" size={size} color={color} />
39
- ),
40
- headerRight: () => (
41
- <Link href="/modal" asChild>
42
- <HeaderButton />
43
- </Link>
54
+ <Ionicons
55
+ name="chatbubble-ellipses-outline"
56
+ size={size}
57
+ color={color}
58
+ />
44
59
  ),
45
60
  }}
46
61
  />
62
+ {{/if}}
47
63
  </Drawer>
48
64
  );
49
65
  };
@@ -26,52 +26,65 @@ export default function Home() {
26
26
 
27
27
  return (
28
28
  <Container>
29
- <ScrollView className="py-4 flex-1">
30
- <Text className="font-mono text-foreground text-2xl font-bold mb-6">
31
- BETTER T STACK
32
- </Text>
33
-
34
- <View className="rounded-lg border border-foreground p-4">
35
- <Text className="mb-2 font-medium text-foreground">API Status</Text>
29
+ <ScrollView showsVerticalScrollIndicator={false} className="flex-1">
30
+ <Text className="font-mono text-foreground text-3xl font-bold mb-4">
31
+ BETTER T STACK
32
+ </Text>
33
+ <View className="bg-card border border-border rounded-xl p-6 mb-6 shadow-sm">
36
34
  {{#if (eq backend "convex")}}
37
- <View className="flex-row items-center gap-2">
35
+ <View className="flex-row items-center gap-3">
38
36
  <View
39
- className={`h-2.5 w-2.5 rounded-full ${
40
- healthCheck ? "bg-green-500" : "bg-red-500"
37
+ className={`h-3 w-3 rounded-full ${
38
+ healthCheck ? "bg-green-500" : "bg-orange-500"
41
39
  }`}
42
40
  />
43
- <Text className="text-sm text-foreground">
41
+ <View className="flex-1">
42
+ <Text className="text-sm font-medium text-card-foreground">
43
+ Convex
44
+ </Text>
45
+ <Text className="text-xs text-muted-foreground">
44
46
  {healthCheck === undefined
45
- ? "Checking..."
47
+ ? "Checking connection..."
46
48
  : healthCheck === "OK"
47
- ? "Connected"
48
- : "Error"}
49
- </Text>
49
+ ? "All systems operational"
50
+ : "Service unavailable"}
51
+ </Text>
52
+ </View>
50
53
  </View>
51
54
  {{else}}
52
55
  {{#unless (eq api "none")}}
53
- <View className="flex-row items-center gap-2">
56
+ <View className="flex-row items-center gap-3">
54
57
  <View
55
- className={`h-2.5 w-2.5 rounded-full ${
56
- healthCheck.data ? "bg-green-500" : "bg-red-500"
58
+ className={`h-3 w-3 rounded-full ${
59
+ healthCheck.data ? "bg-green-500" : "bg-orange-500"
57
60
  }`}
58
61
  />
59
- <Text className="text-sm text-foreground">
60
- {{#if (eq api "orpc")}}
62
+ <View className="flex-1">
63
+ <Text className="text-sm font-medium text-card-foreground">
64
+ {{#if (eq api "orpc")}}
65
+ ORPC
66
+ {{/if}}
67
+ {{#if (eq api "trpc")}}
68
+ TRPC
69
+ {{/if}}
70
+ </Text>
71
+ <Text className="text-xs text-muted-foreground">
72
+ {{#if (eq api "orpc")}}
61
73
  {healthCheck.isLoading
62
- ? "Checking..."
74
+ ? "Checking connection..."
63
75
  : healthCheck.data
64
- ? "Connected"
65
- : "Disconnected"}
66
- {{/if}}
67
- {{#if (eq api "trpc")}}
76
+ ? "All systems operational"
77
+ : "Service unavailable"}
78
+ {{/if}}
79
+ {{#if (eq api "trpc")}}
68
80
  {healthCheck.isLoading
69
- ? "Checking..."
81
+ ? "Checking connection..."
70
82
  : healthCheck.data
71
- ? "Connected"
72
- : "Disconnected"}
73
- {{/if}}
74
- </Text>
83
+ ? "All systems operational"
84
+ : "Service unavailable"}
85
+ {{/if}}
86
+ </Text>
87
+ </View>
75
88
  </View>
76
89
  {{/unless}}
77
90
  {{/if}}
@@ -1,17 +1,28 @@
1
- import { Link, Stack } from 'expo-router';
2
- import { Text } from 'react-native';
3
-
4
- import { Container } from '@/components/container';
1
+ import { Container } from "@/components/container";
2
+ import { Link, Stack } from "expo-router";
3
+ import { Text, View } from "react-native";
5
4
 
6
5
  export default function NotFoundScreen() {
7
6
  return (
8
7
  <>
9
- <Stack.Screen options={{ title: 'Oops!' }} />
8
+ <Stack.Screen options={{ title: "Oops!" }} />
10
9
  <Container>
11
- <Text className="text-xl font-bold">This screen doesn't exist.</Text>
12
- <Link href="/" className="mt-4 pt-4">
13
- <Text className="text-base text-[#2e78b7]">Go to home screen!</Text>
14
- </Link>
10
+ <View className="flex-1 justify-center items-center p-6">
11
+ <View className="items-center">
12
+ <Text className="text-6xl mb-4">🤔</Text>
13
+ <Text className="text-2xl font-bold text-foreground mb-2 text-center">
14
+ Page Not Found
15
+ </Text>
16
+ <Text className="text-muted-foreground text-center mb-8 max-w-sm">
17
+ Sorry, the page you're looking for doesn't exist.
18
+ </Text>
19
+ <Link href="/" asChild>
20
+ <Text className="text-primary font-medium bg-primary/10 px-6 py-3 rounded-lg">
21
+ Go to Home
22
+ </Text>
23
+ </Link>
24
+ </View>
25
+ </View>
15
26
  </Container>
16
27
  </>
17
28
  );
@@ -1,3 +1,6 @@
1
+ {{#if (includes examples "ai")}}
2
+ import "@/polyfills";
3
+ {{/if}}
1
4
  {{#if (eq backend "convex")}}
2
5
  import { ConvexProvider, ConvexReactClient } from "convex/react";
3
6
  {{else}}
@@ -2,11 +2,13 @@ import { Container } from "@/components/container";
2
2
  import { Text, View } from "react-native";
3
3
 
4
4
  export default function Modal() {
5
- return (
6
- <Container>
7
- <View className="flex-1 justify-center items-center">
8
- <Text className="text-xl font-bold text-foreground">Modal View</Text>
9
- </View>
10
- </Container>
11
- );
5
+ return (
6
+ <Container>
7
+ <View className="flex-1 p-6">
8
+ <View className="flex-row items-center justify-between mb-8">
9
+ <Text className="text-2xl font-bold text-foreground">Modal</Text>
10
+ </View>
11
+ </View>
12
+ </Container>
13
+ );
12
14
  }