create-better-t-stack 3.2.21 → 3.2.23-canary.2b547ed5

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 (86) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +1 -1
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.js +1 -1
  5. package/dist/{src-WIwtBCHf.js → src-ehteLbIu.js} +105 -65
  6. package/package.json +1 -1
  7. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +5 -5
  8. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +1 -1
  9. package/templates/auth/better-auth/convex/native/bare/components/sign-in.tsx.hbs +127 -0
  10. package/templates/auth/better-auth/convex/native/bare/components/sign-up.tsx.hbs +138 -0
  11. package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +91 -0
  12. package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +102 -0
  13. package/templates/auth/better-auth/native/bare/app/(drawer)/index.tsx.hbs +186 -0
  14. package/templates/auth/better-auth/native/bare/components/sign-in.tsx.hbs +131 -0
  15. package/templates/auth/better-auth/native/bare/components/sign-up.tsx.hbs +150 -0
  16. package/templates/auth/better-auth/native/unistyles/app/(drawer)/index.tsx.hbs +9 -1
  17. package/templates/auth/better-auth/native/unistyles/components/sign-in.tsx.hbs +5 -0
  18. package/templates/auth/better-auth/native/unistyles/components/sign-up.tsx.hbs +5 -0
  19. package/templates/auth/better-auth/native/uniwind/app/(drawer)/index.tsx.hbs +123 -0
  20. package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +90 -0
  21. package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +116 -0
  22. package/templates/auth/better-auth/server/base/src/index.ts.hbs +5 -5
  23. package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +287 -0
  24. package/templates/examples/ai/native/{nativewind → bare}/polyfills.js +1 -0
  25. package/templates/examples/ai/native/{nativewind → uniwind}/app/(drawer)/ai.tsx.hbs +52 -51
  26. package/templates/examples/ai/native/uniwind/polyfills.js +26 -0
  27. package/templates/examples/todo/native/bare/app/(drawer)/todos.tsx.hbs +430 -0
  28. package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +295 -0
  29. package/templates/extras/bunfig.toml.hbs +3 -3
  30. package/templates/frontend/native/bare/_gitignore +18 -0
  31. package/templates/frontend/native/{nativewind → bare}/app/(drawer)/(tabs)/_layout.tsx.hbs +7 -12
  32. package/templates/frontend/native/bare/app/(drawer)/(tabs)/index.tsx.hbs +43 -0
  33. package/templates/frontend/native/bare/app/(drawer)/(tabs)/two.tsx.hbs +43 -0
  34. package/templates/frontend/native/{nativewind → bare}/app/(drawer)/_layout.tsx.hbs +24 -1
  35. package/templates/frontend/native/bare/app/(drawer)/index.tsx.hbs +234 -0
  36. package/templates/frontend/native/bare/app/+not-found.tsx.hbs +65 -0
  37. package/templates/frontend/native/bare/app/_layout.tsx.hbs +163 -0
  38. package/templates/frontend/native/bare/app/modal.tsx.hbs +34 -0
  39. package/templates/frontend/native/{nativewind → bare}/app.json.hbs +1 -0
  40. package/templates/frontend/native/bare/components/container.tsx.hbs +25 -0
  41. package/templates/frontend/native/bare/components/header-button.tsx.hbs +47 -0
  42. package/templates/frontend/native/{nativewind → bare}/components/tabbar-icon.tsx.hbs +1 -0
  43. package/templates/frontend/native/{nativewind → bare}/lib/android-navigation-bar.tsx.hbs +1 -0
  44. package/templates/frontend/native/{nativewind → bare}/lib/constants.ts.hbs +1 -0
  45. package/templates/frontend/native/bare/lib/use-color-scheme.ts.hbs +20 -0
  46. package/templates/frontend/native/bare/metro.config.js.hbs +9 -0
  47. package/templates/frontend/native/{nativewind → bare}/package.json.hbs +1 -2
  48. package/templates/frontend/native/bare/tsconfig.json.hbs +11 -0
  49. package/templates/frontend/native/{nativewind → uniwind}/_gitignore +4 -8
  50. package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/_layout.tsx.hbs +46 -0
  51. package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/index.tsx.hbs +15 -0
  52. package/templates/frontend/native/uniwind/app/(drawer)/(tabs)/two.tsx.hbs +15 -0
  53. package/templates/frontend/native/uniwind/app/(drawer)/_layout.tsx.hbs +83 -0
  54. package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +151 -0
  55. package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +32 -0
  56. package/templates/frontend/native/uniwind/app/_layout.tsx.hbs +131 -0
  57. package/templates/frontend/native/uniwind/app/modal.tsx.hbs +53 -0
  58. package/templates/frontend/native/uniwind/app.json.hbs +19 -0
  59. package/templates/frontend/native/uniwind/components/container.tsx.hbs +33 -0
  60. package/templates/frontend/native/uniwind/components/theme-toggle.tsx.hbs +35 -0
  61. package/templates/frontend/native/uniwind/contexts/app-theme-context.tsx.hbs +62 -0
  62. package/templates/frontend/native/uniwind/global.css +5 -0
  63. package/templates/frontend/native/uniwind/metro.config.js.hbs +13 -0
  64. package/templates/frontend/native/uniwind/package.json.hbs +54 -0
  65. package/templates/frontend/native/{nativewind → uniwind}/tsconfig.json.hbs +4 -8
  66. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +2 -8
  67. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +3 -6
  68. package/templates/auth/better-auth/convex/native/nativewind/components/sign-in.tsx.hbs +0 -86
  69. package/templates/auth/better-auth/convex/native/nativewind/components/sign-up.tsx.hbs +0 -97
  70. package/templates/auth/better-auth/native/nativewind/app/(drawer)/index.tsx.hbs +0 -95
  71. package/templates/auth/better-auth/native/nativewind/components/sign-in.tsx.hbs +0 -93
  72. package/templates/auth/better-auth/native/nativewind/components/sign-up.tsx.hbs +0 -104
  73. package/templates/examples/todo/native/nativewind/app/(drawer)/todos.tsx.hbs +0 -295
  74. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/index.tsx.hbs +0 -19
  75. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/two.tsx.hbs +0 -19
  76. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +0 -178
  77. package/templates/frontend/native/nativewind/app/+not-found.tsx.hbs +0 -29
  78. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +0 -175
  79. package/templates/frontend/native/nativewind/app/modal.tsx.hbs +0 -14
  80. package/templates/frontend/native/nativewind/babel.config.js.hbs +0 -14
  81. package/templates/frontend/native/nativewind/components/container.tsx.hbs +0 -8
  82. package/templates/frontend/native/nativewind/components/header-button.tsx.hbs +0 -26
  83. package/templates/frontend/native/nativewind/global.css +0 -50
  84. package/templates/frontend/native/nativewind/lib/use-color-scheme.ts.hbs +0 -12
  85. package/templates/frontend/native/nativewind/metro.config.js.hbs +0 -12
  86. package/templates/frontend/native/nativewind/tailwind.config.js.hbs +0 -59
@@ -0,0 +1,430 @@
1
+ import { useState } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ ScrollView,
7
+ ActivityIndicator,
8
+ Alert,
9
+ TouchableOpacity,
10
+ StyleSheet,
11
+ } from "react-native";
12
+ import { Ionicons } from "@expo/vector-icons";
13
+ {{#if (eq backend "convex")}}
14
+ import { useMutation, useQuery } from "convex/react";
15
+ import { api } from "@{{projectName}}/backend/convex/_generated/api";
16
+ import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel";
17
+ {{else}}
18
+ import { useMutation, useQuery } from "@tanstack/react-query";
19
+ {{/if}}
20
+ import { Container } from "@/components/container";
21
+ import { useColorScheme } from "@/lib/use-color-scheme";
22
+ import { NAV_THEME } from "@/lib/constants";
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 { colorScheme } = useColorScheme();
34
+ const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
35
+ const [newTodoText, setNewTodoText] = useState("");
36
+
37
+ {{#if (eq backend "convex")}}
38
+ const todos = useQuery(api.todos.getAll);
39
+ const createTodoMutation = useMutation(api.todos.create);
40
+ const toggleTodoMutation = useMutation(api.todos.toggle);
41
+ const deleteTodoMutation = useMutation(api.todos.deleteTodo);
42
+ {{else}}
43
+ {{#if (eq api "orpc")}}
44
+ const todos = useQuery(orpc.todo.getAll.queryOptions());
45
+ const createMutation = useMutation(orpc.todo.create.mutationOptions({
46
+ onSuccess: () => {
47
+ todos.refetch();
48
+ setNewTodoText("");
49
+ },
50
+ }));
51
+ const toggleMutation = useMutation(orpc.todo.toggle.mutationOptions({
52
+ onSuccess: () => {
53
+ todos.refetch();
54
+ },
55
+ }));
56
+ const deleteMutation = useMutation(orpc.todo.delete.mutationOptions({
57
+ onSuccess: () => {
58
+ todos.refetch();
59
+ },
60
+ }));
61
+ {{/if}}
62
+ {{#if (eq api "trpc")}}
63
+ const todos = useQuery(trpc.todo.getAll.queryOptions());
64
+ const createMutation = useMutation(trpc.todo.create.mutationOptions({
65
+ onSuccess: () => {
66
+ todos.refetch();
67
+ setNewTodoText("");
68
+ },
69
+ }));
70
+ const toggleMutation = useMutation(trpc.todo.toggle.mutationOptions({
71
+ onSuccess: () => {
72
+ todos.refetch();
73
+ },
74
+ }));
75
+ const deleteMutation = useMutation(trpc.todo.delete.mutationOptions({
76
+ onSuccess: () => {
77
+ todos.refetch();
78
+ },
79
+ }));
80
+ {{/if}}
81
+ {{/if}}
82
+
83
+ {{#if (eq backend "convex")}}
84
+ async function handleAddTodo() {
85
+ const text = newTodoText.trim();
86
+ if (!text) return;
87
+ await createTodoMutation({ text });
88
+ setNewTodoText("");
89
+ }
90
+
91
+ function handleToggleTodo(id: Id<"todos">, currentCompleted: boolean) {
92
+ toggleTodoMutation({ id, completed: !currentCompleted });
93
+ }
94
+
95
+ function handleDeleteTodo(id: Id<"todos">) {
96
+ Alert.alert("Delete Todo", "Are you sure you want to delete this todo?", [
97
+ { text: "Cancel", style: "cancel" },
98
+ {
99
+ text: "Delete",
100
+ style: "destructive",
101
+ onPress: () => deleteTodoMutation({ id }),
102
+ },
103
+ ]);
104
+ }
105
+
106
+ const isLoading = !todos;
107
+ const completedCount = todos?.filter((t) => t.completed).length || 0;
108
+ const totalCount = todos?.length || 0;
109
+ {{else}}
110
+ function handleAddTodo() {
111
+ if (newTodoText.trim()) {
112
+ createMutation.mutate({ text: newTodoText });
113
+ }
114
+ }
115
+
116
+ function handleToggleTodo(id: number, completed: boolean) {
117
+ toggleMutation.mutate({ id, completed: !completed });
118
+ }
119
+
120
+ function handleDeleteTodo(id: number) {
121
+ Alert.alert("Delete Todo", "Are you sure you want to delete this todo?", [
122
+ { text: "Cancel", style: "cancel" },
123
+ {
124
+ text: "Delete",
125
+ style: "destructive",
126
+ onPress: () => deleteMutation.mutate({ id }),
127
+ },
128
+ ]);
129
+ }
130
+
131
+ const isLoading = todos?.isLoading;
132
+ const completedCount = todos?.data?.filter((t) => t.completed).length || 0;
133
+ const totalCount = todos?.data?.length || 0;
134
+ {{/if}}
135
+
136
+ return (
137
+ <Container>
138
+ <ScrollView style={styles.scrollView} contentContainerStyle={styles.contentContainer}>
139
+ <View style={styles.header}>
140
+ <View style={styles.headerRow}>
141
+ <Text style={[styles.title, { color: theme.text }]}>
142
+ Todo List
143
+ </Text>
144
+ {totalCount > 0 && (
145
+ <View style={[styles.badge, { backgroundColor: theme.primary }]}>
146
+ <Text style={styles.badgeText}>
147
+ {completedCount}/{totalCount}
148
+ </Text>
149
+ </View>
150
+ )}
151
+ </View>
152
+ </View>
153
+
154
+ <View style={[styles.inputCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
155
+ <View style={styles.inputRow}>
156
+ <View style={styles.inputContainer}>
157
+ <TextInput
158
+ value={newTodoText}
159
+ onChangeText={setNewTodoText}
160
+ placeholder="Add a new task..."
161
+ placeholderTextColor={theme.text}
162
+ {{#unless (eq backend "convex")}}
163
+ editable={!createMutation.isPending}
164
+ {{/unless}}
165
+ onSubmitEditing={handleAddTodo}
166
+ returnKeyType="done"
167
+ style={[styles.input, { color: theme.text, borderColor: theme.border, backgroundColor: theme.background }]}
168
+ />
169
+ </View>
170
+ <TouchableOpacity
171
+ onPress={handleAddTodo}
172
+ {{#if (eq backend "convex")}}
173
+ disabled={!newTodoText.trim()}
174
+ style={[
175
+ styles.addButton,
176
+ {
177
+ backgroundColor: !newTodoText.trim() ? theme.border : theme.primary,
178
+ opacity: !newTodoText.trim() ? 0.5 : 1,
179
+ },
180
+ ]}
181
+ >
182
+ <Ionicons
183
+ name="add"
184
+ size={24}
185
+ color={newTodoText.trim() ? "#ffffff" : theme.text}
186
+ />
187
+ {{else}}
188
+ disabled={createMutation.isPending || !newTodoText.trim()}
189
+ style={[
190
+ styles.addButton,
191
+ {
192
+ backgroundColor: (createMutation.isPending || !newTodoText.trim()) ? theme.border : theme.primary,
193
+ opacity: (createMutation.isPending || !newTodoText.trim()) ? 0.5 : 1,
194
+ },
195
+ ]}
196
+ >
197
+ {createMutation.isPending ? (
198
+ <ActivityIndicator size="small" color="#ffffff" />
199
+ ) : (
200
+ <Ionicons
201
+ name="add"
202
+ size={24}
203
+ color="#ffffff"
204
+ />
205
+ )}
206
+ {{/if}}
207
+ </TouchableOpacity>
208
+ </View>
209
+ </View>
210
+
211
+ {{#if (eq backend "convex")}}
212
+ {isLoading && (
213
+ <View style={styles.centerContainer}>
214
+ <ActivityIndicator size="large" color={theme.primary} />
215
+ <Text style=\{{ styles.loadingText, { color: theme.text, opacity: 0.7 } }}>
216
+ Loading todos...
217
+ </Text>
218
+ </View>
219
+ )}
220
+
221
+ {todos && todos.length === 0 && !isLoading && (
222
+ <View style={[styles.emptyCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
223
+ <Ionicons name="checkbox-outline" size={64} color={theme.text} style=\{{ opacity: 0.5, marginBottom: 16 }} />
224
+ <Text style=\{{ styles.emptyTitle, { color: theme.text } }}>
225
+ No todos yet
226
+ </Text>
227
+ <Text style=\{{ styles.emptyText, { color: theme.text, opacity: 0.7 } }}>
228
+ Add your first task to get started!
229
+ </Text>
230
+ </View>
231
+ )}
232
+
233
+ {todos && todos.length > 0 && (
234
+ <View style={styles.todosList}>
235
+ {todos.map((todo) => (
236
+ <View key={todo._id} style={[styles.todoCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
237
+ <View style={styles.todoRow}>
238
+ <TouchableOpacity
239
+ onPress={() => handleToggleTodo(todo._id, todo.completed)}
240
+ style={[styles.checkbox, { borderColor: theme.border }]}
241
+ >
242
+ {todo.completed && (
243
+ <Ionicons name="checkmark" size={16} color={theme.primary} />
244
+ )}
245
+ </TouchableOpacity>
246
+ <View style={styles.todoTextContainer}>
247
+ <Text style={[
248
+ styles.todoText,
249
+ { color: theme.text },
250
+ todo.completed && { textDecorationLine: "line-through", opacity: 0.5 },
251
+ ]}>
252
+ {todo.text}
253
+ </Text>
254
+ </View>
255
+ <TouchableOpacity
256
+ onPress={() => handleDeleteTodo(todo._id)}
257
+ style=\{{ styles.deleteButton }}
258
+ >
259
+ <Ionicons name="trash-outline" size={24} color={theme.notification} />
260
+ </TouchableOpacity>
261
+ </View>
262
+ </View>
263
+ ))}
264
+ </View>
265
+ )}
266
+ {{else}}
267
+ {isLoading && (
268
+ <View style={styles.centerContainer}>
269
+ <ActivityIndicator size="large" color={theme.primary} />
270
+ <Text style={[styles.loadingText, { color: theme.text, opacity: 0.7 }]}>
271
+ Loading todos...
272
+ </Text>
273
+ </View>
274
+ )}
275
+
276
+ {todos?.data && todos.data.length === 0 && !isLoading && (
277
+ <View style={[styles.emptyCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
278
+ <Ionicons name="checkbox-outline" size={64} color={theme.text} style=\{{ opacity: 0.5, marginBottom: 16 }} />
279
+ <Text style=\{{ styles.emptyTitle, { color: theme.text } }}>
280
+ No todos yet
281
+ </Text>
282
+ <Text style=\{{ styles.emptyText, { color: theme.text, opacity: 0.7 } }}>
283
+ Add your first task to get started!
284
+ </Text>
285
+ </View>
286
+ )}
287
+
288
+ {todos?.data && todos.data.length > 0 && (
289
+ <View style={styles.todosList}>
290
+ {todos.data.map((todo) => (
291
+ <View key={todo.id} style={[styles.todoCard, { backgroundColor: theme.card, borderColor: theme.border }]}>
292
+ <View style={styles.todoRow}>
293
+ <TouchableOpacity
294
+ onPress={() => handleToggleTodo(todo.id, todo.completed)}
295
+ style={[styles.checkbox, { borderColor: theme.border }]}
296
+ >
297
+ {todo.completed && (
298
+ <Ionicons name="checkmark" size={16} color={theme.primary} />
299
+ )}
300
+ </TouchableOpacity>
301
+ <View style={styles.todoTextContainer}>
302
+ <Text style={[
303
+ styles.todoText,
304
+ { color: theme.text },
305
+ todo.completed && { textDecorationLine: "line-through", opacity: 0.5 },
306
+ ]}>
307
+ {todo.text}
308
+ </Text>
309
+ </View>
310
+ <TouchableOpacity
311
+ onPress={() => handleDeleteTodo(todo.id)}
312
+ style={styles.deleteButton}
313
+ >
314
+ <Ionicons name="trash-outline" size={24} color={theme.notification} />
315
+ </TouchableOpacity>
316
+ </View>
317
+ </View>
318
+ ))}
319
+ </View>
320
+ )}
321
+ {{/if}}
322
+ </ScrollView>
323
+ </Container>
324
+ );
325
+ }
326
+
327
+ const styles = StyleSheet.create({
328
+ scrollView: {
329
+ flex: 1,
330
+ },
331
+ contentContainer: {
332
+ padding: 16,
333
+ },
334
+ header: {
335
+ marginBottom: 16,
336
+ },
337
+ headerRow: {
338
+ flexDirection: "row",
339
+ alignItems: "center",
340
+ justifyContent: "space-between",
341
+ },
342
+ title: {
343
+ fontSize: 24,
344
+ fontWeight: "bold",
345
+ },
346
+ badge: {
347
+ paddingHorizontal: 8,
348
+ paddingVertical: 4,
349
+ },
350
+ badgeText: {
351
+ color: "#ffffff",
352
+ fontSize: 12,
353
+ },
354
+ inputCard: {
355
+ borderWidth: 1,
356
+ padding: 12,
357
+ marginBottom: 16,
358
+ },
359
+ inputRow: {
360
+ flexDirection: "row",
361
+ alignItems: "center",
362
+ gap: 8,
363
+ },
364
+ inputContainer: {
365
+ flex: 1,
366
+ },
367
+ input: {
368
+ borderWidth: 1,
369
+ padding: 12,
370
+ fontSize: 16,
371
+ },
372
+ addButton: {
373
+ padding: 12,
374
+ justifyContent: "center",
375
+ alignItems: "center",
376
+ },
377
+ centerContainer: {
378
+ alignItems: "center",
379
+ justifyContent: "center",
380
+ paddingVertical: 32,
381
+ },
382
+ loadingText: {
383
+ marginTop: 16,
384
+ fontSize: 14,
385
+ },
386
+ emptyCard: {
387
+ borderWidth: 1,
388
+ padding: 32,
389
+ alignItems: "center",
390
+ justifyContent: "center",
391
+ },
392
+ emptyTitle: {
393
+ fontSize: 16,
394
+ fontWeight: "bold",
395
+ marginBottom: 8,
396
+ },
397
+ emptyText: {
398
+ fontSize: 14,
399
+ textAlign: "center",
400
+ },
401
+ todosList: {
402
+ gap: 8,
403
+ },
404
+ todoCard: {
405
+ borderWidth: 1,
406
+ padding: 12,
407
+ },
408
+ todoRow: {
409
+ flexDirection: "row",
410
+ alignItems: "center",
411
+ gap: 12,
412
+ },
413
+ checkbox: {
414
+ width: 20,
415
+ height: 20,
416
+ borderWidth: 2,
417
+ justifyContent: "center",
418
+ alignItems: "center",
419
+ },
420
+ todoTextContainer: {
421
+ flex: 1,
422
+ },
423
+ todoText: {
424
+ fontSize: 16,
425
+ },
426
+ deleteButton: {
427
+ padding: 8,
428
+ },
429
+ });
430
+