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
@@ -16,7 +16,7 @@ export const auth = betterAuth<BetterAuthOptions>({
16
16
  }),
17
17
  trustedOrigins: [
18
18
  process.env.CORS_ORIGIN || "",
19
- {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
19
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
20
20
  "mybettertapp://", "exp://"
21
21
  {{/if}}
22
22
  ],
@@ -77,7 +77,7 @@ export const auth = betterAuth<BetterAuthOptions>({
77
77
  }),
78
78
  trustedOrigins: [
79
79
  process.env.CORS_ORIGIN || "",
80
- {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
80
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
81
81
  "mybettertapp://", "exp://"
82
82
  {{/if}}
83
83
  ],
@@ -138,7 +138,7 @@ export const auth = betterAuth<BetterAuthOptions>({
138
138
  }),
139
139
  trustedOrigins: [
140
140
  env.CORS_ORIGIN,
141
- {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
141
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
142
142
  "mybettertapp://", "exp://"
143
143
  {{/if}}
144
144
  ],
@@ -206,7 +206,7 @@ export const auth = betterAuth<BetterAuthOptions>({
206
206
  database: mongodbAdapter(client),
207
207
  trustedOrigins: [
208
208
  process.env.CORS_ORIGIN || "",
209
- {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
209
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
210
210
  "mybettertapp://", "exp://"
211
211
  {{/if}}
212
212
  ],
@@ -258,7 +258,7 @@ export const auth = betterAuth<BetterAuthOptions>({
258
258
  database: "", // Invalid configuration
259
259
  trustedOrigins: [
260
260
  process.env.CORS_ORIGIN || "",
261
- {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
261
+ {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
262
262
  "mybettertapp://", "exp://"
263
263
  {{/if}}
264
264
  ],
@@ -0,0 +1,287 @@
1
+ import { useRef, useEffect, useState } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ TouchableOpacity,
7
+ ScrollView,
8
+ KeyboardAvoidingView,
9
+ Platform,
10
+ StyleSheet,
11
+ } from "react-native";
12
+ import { useChat } from "@ai-sdk/react";
13
+ import { DefaultChatTransport } from "ai";
14
+ import { fetch as expoFetch } from "expo/fetch";
15
+ import { Ionicons } from "@expo/vector-icons";
16
+ import { Container } from "@/components/container";
17
+ import { useColorScheme } from "@/lib/use-color-scheme";
18
+ import { NAV_THEME } from "@/lib/constants";
19
+
20
+ const generateAPIUrl = (relativePath: string) => {
21
+ const serverUrl = process.env.EXPO_PUBLIC_SERVER_URL;
22
+ if (!serverUrl) {
23
+ throw new Error(
24
+ "EXPO_PUBLIC_SERVER_URL environment variable is not defined"
25
+ );
26
+ }
27
+ const path = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
28
+ return serverUrl.concat(path);
29
+ };
30
+
31
+ export default function AIScreen() {
32
+ const { colorScheme } = useColorScheme();
33
+ const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
34
+ const [input, setInput] = useState("");
35
+ const { messages, error, sendMessage } = useChat({
36
+ transport: new DefaultChatTransport({
37
+ fetch: expoFetch as unknown as typeof globalThis.fetch,
38
+ api: generateAPIUrl("/ai"),
39
+ }),
40
+ onError: (error) => console.error(error, "AI Chat Error"),
41
+ });
42
+ const scrollViewRef = useRef<ScrollView>(null);
43
+
44
+ useEffect(() => {
45
+ scrollViewRef.current?.scrollToEnd({ animated: true });
46
+ }, [messages]);
47
+
48
+ function onSubmit() {
49
+ const value = input.trim();
50
+ if (value) {
51
+ sendMessage({ text: value });
52
+ setInput("");
53
+ }
54
+ }
55
+
56
+ if (error) {
57
+ return (
58
+ <Container>
59
+ <View style={styles.errorContainer}>
60
+ <View style={[styles.errorCard, { backgroundColor: theme.notification + "20", borderColor: theme.notification }]}>
61
+ <Text style={[styles.errorTitle, { color: theme.notification }]}>
62
+ Error: {error.message}
63
+ </Text>
64
+ <Text style={[styles.errorText, { color: theme.text, opacity: 0.7 }]}>
65
+ Please check your connection and try again.
66
+ </Text>
67
+ </View>
68
+ </View>
69
+ </Container>
70
+ );
71
+ }
72
+
73
+ return (
74
+ <Container>
75
+ <KeyboardAvoidingView
76
+ style={styles.container}
77
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
78
+ >
79
+ <View style={styles.content}>
80
+ <View style={styles.header}>
81
+ <Text style={[styles.headerTitle, { color: theme.text }]}>
82
+ AI Chat
83
+ </Text>
84
+ <Text style={[styles.headerSubtitle, { color: theme.text, opacity: 0.7 }]}>
85
+ Chat with our AI assistant
86
+ </Text>
87
+ </View>
88
+ <ScrollView
89
+ ref={scrollViewRef}
90
+ style={styles.scrollView}
91
+ showsVerticalScrollIndicator={false}
92
+ >
93
+ {messages.length === 0 ? (
94
+ <View style={styles.emptyContainer}>
95
+ <Text style={[styles.emptyText, { color: theme.text, opacity: 0.7 }]}>
96
+ Ask me anything to get started!
97
+ </Text>
98
+ </View>
99
+ ) : (
100
+ <View style={styles.messagesList}>
101
+ {messages.map((message) => (
102
+ <View
103
+ key={message.id}
104
+ style={[
105
+ styles.messageCard,
106
+ {
107
+ backgroundColor: message.role === "user"
108
+ ? theme.primary + "20"
109
+ : theme.card,
110
+ borderColor: theme.border,
111
+ alignSelf: message.role === "user" ? "flex-end" : "flex-start",
112
+ marginLeft: message.role === "user" ? 32 : 0,
113
+ marginRight: message.role === "user" ? 0 : 32,
114
+ },
115
+ ]}
116
+ >
117
+ <Text style={[styles.messageRole, { color: theme.text }]}>
118
+ {message.role === "user" ? "You" : "AI Assistant"}
119
+ </Text>
120
+ <View style={styles.messageParts}>
121
+ {message.parts.map((part, i) =>
122
+ part.type === "text" ? (
123
+ <Text
124
+ key={`${message.id}-${i}`}
125
+ style={[styles.messageText, { color: theme.text }]}
126
+ >
127
+ {part.text}
128
+ </Text>
129
+ ) : (
130
+ <Text
131
+ key={`${message.id}-${i}`}
132
+ style={[styles.messageText, { color: theme.text }]}
133
+ >
134
+ {JSON.stringify(part)}
135
+ </Text>
136
+ )
137
+ )}
138
+ </View>
139
+ </View>
140
+ ))}
141
+ </View>
142
+ )}
143
+ </ScrollView>
144
+ <View style={[styles.inputContainer, { borderTopColor: theme.border }]}>
145
+ <View style={styles.inputRow}>
146
+ <TextInput
147
+ value={input}
148
+ onChangeText={setInput}
149
+ placeholder="Type your message..."
150
+ placeholderTextColor={theme.text}
151
+ style={[
152
+ styles.input,
153
+ {
154
+ color: theme.text,
155
+ borderColor: theme.border,
156
+ backgroundColor: theme.background,
157
+ },
158
+ ]}
159
+ onSubmitEditing={(e) => {
160
+ e.preventDefault();
161
+ onSubmit();
162
+ }}
163
+ autoFocus={true}
164
+ multiline
165
+ />
166
+ <TouchableOpacity
167
+ onPress={onSubmit}
168
+ disabled={!input.trim()}
169
+ style={[
170
+ styles.sendButton,
171
+ {
172
+ backgroundColor: input.trim() ? theme.primary : theme.border,
173
+ opacity: input.trim() ? 1 : 0.5,
174
+ },
175
+ ]}
176
+ >
177
+ <Ionicons
178
+ name="send"
179
+ size={20}
180
+ color="#ffffff"
181
+ />
182
+ </TouchableOpacity>
183
+ </View>
184
+ </View>
185
+ </View>
186
+ </KeyboardAvoidingView>
187
+ </Container>
188
+ );
189
+ }
190
+
191
+ const styles = StyleSheet.create({
192
+ container: {
193
+ flex: 1,
194
+ },
195
+ content: {
196
+ flex: 1,
197
+ padding: 16,
198
+ },
199
+ header: {
200
+ marginBottom: 16,
201
+ },
202
+ headerTitle: {
203
+ fontSize: 20,
204
+ fontWeight: "bold",
205
+ marginBottom: 4,
206
+ },
207
+ headerSubtitle: {
208
+ fontSize: 14,
209
+ },
210
+ scrollView: {
211
+ flex: 1,
212
+ marginBottom: 16,
213
+ },
214
+ emptyContainer: {
215
+ flex: 1,
216
+ justifyContent: "center",
217
+ alignItems: "center",
218
+ },
219
+ emptyText: {
220
+ fontSize: 16,
221
+ textAlign: "center",
222
+ },
223
+ messagesList: {
224
+ gap: 8,
225
+ paddingBottom: 16,
226
+ },
227
+ messageCard: {
228
+ borderWidth: 1,
229
+ padding: 12,
230
+ maxWidth: "80%",
231
+ },
232
+ messageRole: {
233
+ fontSize: 12,
234
+ fontWeight: "bold",
235
+ marginBottom: 4,
236
+ },
237
+ messageParts: {
238
+ gap: 4,
239
+ },
240
+ messageText: {
241
+ fontSize: 14,
242
+ lineHeight: 20,
243
+ },
244
+ inputContainer: {
245
+ borderTopWidth: 1,
246
+ paddingTop: 12,
247
+ },
248
+ inputRow: {
249
+ flexDirection: "row",
250
+ alignItems: "flex-end",
251
+ gap: 8,
252
+ },
253
+ input: {
254
+ flex: 1,
255
+ borderWidth: 1,
256
+ padding: 8,
257
+ fontSize: 14,
258
+ minHeight: 36,
259
+ maxHeight: 100,
260
+ },
261
+ sendButton: {
262
+ padding: 8,
263
+ justifyContent: "center",
264
+ alignItems: "center",
265
+ },
266
+ errorContainer: {
267
+ flex: 1,
268
+ justifyContent: "center",
269
+ alignItems: "center",
270
+ padding: 16,
271
+ },
272
+ errorCard: {
273
+ borderWidth: 1,
274
+ padding: 16,
275
+ },
276
+ errorTitle: {
277
+ fontSize: 16,
278
+ fontWeight: "bold",
279
+ marginBottom: 8,
280
+ textAlign: "center",
281
+ },
282
+ errorText: {
283
+ fontSize: 14,
284
+ textAlign: "center",
285
+ },
286
+ });
287
+
@@ -23,3 +23,4 @@ if (Platform.OS !== "web") {
23
23
  }
24
24
 
25
25
  export {};
26
+
@@ -3,7 +3,7 @@ import {
3
3
  View,
4
4
  Text,
5
5
  TextInput,
6
- TouchableOpacity,
6
+ Pressable,
7
7
  ScrollView,
8
8
  KeyboardAvoidingView,
9
9
  Platform,
@@ -13,14 +13,16 @@ import { DefaultChatTransport } from "ai";
13
13
  import { fetch as expoFetch } from "expo/fetch";
14
14
  import { Ionicons } from "@expo/vector-icons";
15
15
  import { Container } from "@/components/container";
16
+ import { Card, useThemeColor } from "heroui-native";
16
17
 
17
18
  const generateAPIUrl = (relativePath: string) => {
18
19
  const serverUrl = process.env.EXPO_PUBLIC_SERVER_URL;
19
20
  if (!serverUrl) {
20
- throw new Error("EXPO_PUBLIC_SERVER_URL environment variable is not defined");
21
+ throw new Error(
22
+ "EXPO_PUBLIC_SERVER_URL environment variable is not defined"
23
+ );
21
24
  }
22
-
23
- const path = relativePath.startsWith('/') ? relativePath : `/${relativePath}`;
25
+ const path = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
24
26
  return serverUrl.concat(path);
25
27
  };
26
28
 
@@ -29,12 +31,15 @@ export default function AIScreen() {
29
31
  const { messages, error, sendMessage } = useChat({
30
32
  transport: new DefaultChatTransport({
31
33
  fetch: expoFetch as unknown as typeof globalThis.fetch,
32
- api: generateAPIUrl('/ai'),
34
+ api: generateAPIUrl("/ai"),
33
35
  }),
34
- onError: error => console.error(error, 'AI Chat Error'),
36
+ onError: (error) => console.error(error, "AI Chat Error"),
35
37
  });
36
-
37
38
  const scrollViewRef = useRef<ScrollView>(null);
39
+ const mutedColor = useThemeColor("muted");
40
+ const accentColor = useThemeColor("accent");
41
+ const foregroundColor = useThemeColor("foreground");
42
+ const dangerColor = useThemeColor("danger");
38
43
 
39
44
  useEffect(() => {
40
45
  scrollViewRef.current?.scrollToEnd({ animated: true });
@@ -52,12 +57,14 @@ export default function AIScreen() {
52
57
  return (
53
58
  <Container>
54
59
  <View className="flex-1 justify-center items-center px-4">
55
- <Text className="text-destructive text-center text-lg mb-4">
56
- Error: {error.message}
57
- </Text>
58
- <Text className="text-muted-foreground text-center">
59
- Please check your connection and try again.
60
- </Text>
60
+ <Card variant="flat" className="p-4 bg-danger/10">
61
+ <Text className="text-danger text-center text-lg mb-2 font-semibold">
62
+ Error: {error.message}
63
+ </Text>
64
+ <Text className="text-muted text-center">
65
+ Please check your connection and try again.
66
+ </Text>
67
+ </Card>
61
68
  </View>
62
69
  </Container>
63
70
  );
@@ -65,7 +72,7 @@ export default function AIScreen() {
65
72
 
66
73
  return (
67
74
  <Container>
68
- <KeyboardAvoidingView
75
+ <KeyboardAvoidingView
69
76
  className="flex-1"
70
77
  behavior={Platform.OS === "ios" ? "padding" : "height"}
71
78
  >
@@ -74,11 +81,10 @@ export default function AIScreen() {
74
81
  <Text className="text-foreground text-2xl font-bold mb-2">
75
82
  AI Chat
76
83
  </Text>
77
- <Text className="text-muted-foreground">
84
+ <Text className="text-muted">
78
85
  Chat with our AI assistant
79
86
  </Text>
80
87
  </View>
81
-
82
88
  <ScrollView
83
89
  ref={scrollViewRef}
84
90
  className="flex-1 mb-4"
@@ -86,85 +92,80 @@ export default function AIScreen() {
86
92
  >
87
93
  {messages.length === 0 ? (
88
94
  <View className="flex-1 justify-center items-center">
89
- <Text className="text-center text-muted-foreground text-lg">
95
+ <Text className="text-center text-muted text-lg">
90
96
  Ask me anything to get started!
91
97
  </Text>
92
98
  </View>
93
99
  ) : (
94
- <View className="space-y-4">
100
+ <View className="gap-3">
95
101
  {messages.map((message) => (
96
- <View
102
+ <Card
97
103
  key={message.id}
98
- className={`p-3 rounded-lg ${
104
+ variant={message.role === "user" ? "flat" : "secondary"}
105
+ className={`p-3 ${
99
106
  message.role === "user"
100
- ? "bg-primary/10 ml-8"
101
- : "bg-card mr-8 border border-border"
107
+ ? "ml-8 bg-accent/10"
108
+ : "mr-8"
102
109
  }`}
103
110
  >
104
111
  <Text className="text-sm font-semibold mb-1 text-foreground">
105
112
  {message.role === "user" ? "You" : "AI Assistant"}
106
113
  </Text>
107
- <View className="space-y-1">
108
- {message.parts.map((part, i) => {
109
- if (part.type === 'text') {
110
- return (
111
- <Text
112
- key={`${message.id}-${i}`}
113
- className="text-foreground leading-relaxed"
114
- >
115
- {part.text}
116
- </Text>
117
- );
118
- }
119
- return (
114
+ <View className="gap-1">
115
+ {message.parts.map((part, i) =>
116
+ part.type === "text" ? (
117
+ <Text
118
+ key={`${message.id}-${i}`}
119
+ className="text-foreground leading-relaxed"
120
+ >
121
+ {part.text}
122
+ </Text>
123
+ ) : (
120
124
  <Text
121
125
  key={`${message.id}-${i}`}
122
126
  className="text-foreground leading-relaxed"
123
127
  >
124
128
  {JSON.stringify(part)}
125
129
  </Text>
126
- );
127
- })}
130
+ )
131
+ )}
128
132
  </View>
129
- </View>
133
+ </Card>
130
134
  ))}
131
135
  </View>
132
136
  )}
133
137
  </ScrollView>
134
-
135
- <View className="border-t border-border pt-4">
136
- <View className="flex-row items-end space-x-2">
138
+ <View className="border-t border-divider pt-4">
139
+ <View className="flex-row items-end gap-2">
137
140
  <TextInput
138
141
  value={input}
139
142
  onChangeText={setInput}
140
143
  placeholder="Type your message..."
141
- placeholderTextColor="#6b7280"
142
- className="flex-1 border border-border rounded-md px-3 py-2 text-foreground bg-background min-h-[40px] max-h-[120px]"
144
+ placeholderTextColor={mutedColor}
145
+ className="flex-1 border border-divider rounded-lg px-3 py-2 text-foreground bg-surface min-h-[40px] max-h-[120px]"
143
146
  onSubmitEditing={(e) => {
144
147
  e.preventDefault();
145
148
  onSubmit();
146
149
  }}
147
150
  autoFocus={true}
148
151
  />
149
- <TouchableOpacity
152
+ <Pressable
150
153
  onPress={onSubmit}
151
154
  disabled={!input.trim()}
152
- className={`p-2 rounded-md ${
153
- input.trim()
154
- ? "bg-primary"
155
- : "bg-muted"
155
+ className={`p-2 rounded-lg active:opacity-70 ${
156
+ input.trim() ? "bg-accent" : "bg-surface"
156
157
  }`}
157
158
  >
158
159
  <Ionicons
159
160
  name="send"
160
161
  size={20}
161
- color={input.trim() ? "#ffffff" : "#6b7280"}
162
+ color={input.trim() ? foregroundColor : mutedColor}
162
163
  />
163
- </TouchableOpacity>
164
+ </Pressable>
164
165
  </View>
165
166
  </View>
166
167
  </View>
167
168
  </KeyboardAvoidingView>
168
169
  </Container>
169
170
  );
170
- }
171
+ }
@@ -0,0 +1,26 @@
1
+ import structuredClone from "@ungap/structured-clone";
2
+ import { Platform } from "react-native";
3
+
4
+ if (Platform.OS !== "web") {
5
+ const setupPolyfills = async () => {
6
+ const { polyfillGlobal } = await import(
7
+ "react-native/Libraries/Utilities/PolyfillFunctions"
8
+ );
9
+
10
+ const { TextEncoderStream, TextDecoderStream } = await import(
11
+ "@stardazed/streams-text-encoding"
12
+ );
13
+
14
+ if (!("structuredClone" in global)) {
15
+ polyfillGlobal("structuredClone", () => structuredClone);
16
+ }
17
+
18
+ polyfillGlobal("TextEncoderStream", () => TextEncoderStream);
19
+ polyfillGlobal("TextDecoderStream", () => TextDecoderStream);
20
+ };
21
+
22
+ setupPolyfills();
23
+ }
24
+
25
+ export {};
26
+