create-better-t-stack 3.9.0 → 3.11.0-pr749.7e7198c

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 (80) hide show
  1. package/README.md +2 -1
  2. package/bin/create-better-t-stack +94 -0
  3. package/package.json +51 -55
  4. package/scripts/postinstall.mjs +129 -0
  5. package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +5 -7
  6. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +17 -17
  7. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +4 -4
  8. package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +2 -2
  9. package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +10 -10
  10. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +13 -5
  11. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +14 -12
  12. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +13 -16
  13. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +11 -5
  14. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +4 -4
  15. package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +1 -1
  16. package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +17 -15
  17. package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +16 -15
  18. package/templates/auth/better-auth/web/react/tanstack-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
  19. package/templates/auth/better-auth/web/react/tanstack-start/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
  20. package/templates/backend/convex/packages/backend/convex/README.md +4 -4
  21. package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +17 -0
  22. package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +1 -1
  23. package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +9 -0
  24. package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +67 -0
  25. package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +301 -3
  26. package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +296 -10
  27. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +180 -1
  28. package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +172 -9
  29. package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +156 -6
  30. package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +156 -4
  31. package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +159 -6
  32. package/templates/frontend/react/next/package.json.hbs +8 -7
  33. package/templates/frontend/react/next/src/app/layout.tsx.hbs +28 -1
  34. package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +4 -6
  35. package/templates/frontend/react/next/src/components/providers.tsx.hbs +14 -4
  36. package/templates/frontend/react/react-router/package.json.hbs +2 -1
  37. package/templates/frontend/react/{tanstack-router/src/components/mode-toggle.tsx → react-router/src/components/mode-toggle.tsx.hbs} +4 -6
  38. package/templates/frontend/react/tanstack-router/package.json.hbs +2 -1
  39. package/templates/frontend/react/{react-router/src/components/mode-toggle.tsx → tanstack-router/src/components/mode-toggle.tsx.hbs} +4 -6
  40. package/templates/frontend/react/tanstack-start/package.json.hbs +2 -1
  41. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +6 -0
  42. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +13 -14
  43. package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +5 -0
  44. package/templates/frontend/react/web-base/components.json +5 -2
  45. package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +57 -0
  46. package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +103 -0
  47. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +26 -0
  48. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +262 -0
  49. package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +20 -0
  50. package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +20 -0
  51. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +13 -0
  52. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +44 -0
  53. package/templates/frontend/react/web-base/src/index.css.hbs +58 -64
  54. package/dist/cli.d.mts +0 -1
  55. package/dist/cli.mjs +0 -8
  56. package/dist/index.d.mts +0 -347
  57. package/dist/index.mjs +0 -4
  58. package/dist/src-DLvUK0Qf.mjs +0 -7069
  59. package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +0 -7
  60. package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +0 -22
  61. package/templates/frontend/react/web-base/src/components/ui/button.tsx +0 -56
  62. package/templates/frontend/react/web-base/src/components/ui/card.tsx +0 -75
  63. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +0 -27
  64. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +0 -228
  65. package/templates/frontend/react/web-base/src/components/ui/input.tsx +0 -21
  66. package/templates/frontend/react/web-base/src/components/ui/label.tsx +0 -19
  67. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +0 -13
  68. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +0 -25
  69. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  70. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  71. /package/templates/auth/better-auth/web/react/tanstack-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  72. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  73. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  74. /package/templates/auth/better-auth/web/react/tanstack-start/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  75. /package/templates/auth/better-auth/web/solid/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  76. /package/templates/auth/better-auth/web/solid/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  77. /package/templates/auth/better-auth/web/solid/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  78. /package/templates/frontend/react/react-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  79. /package/templates/frontend/react/tanstack-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  80. /package/templates/frontend/react/web-base/src/lib/{utils.ts → utils.ts.hbs} +0 -0
@@ -1,3 +1,301 @@
1
+ {{#if (eq backend "convex")}}
2
+ import { Ionicons } from "@expo/vector-icons";
3
+ import {
4
+ useUIMessages,
5
+ useSmoothText,
6
+ type UIMessage,
7
+ } from "@convex-dev/agent/react";
8
+ import { api } from "@{{projectName}}/backend/convex/_generated/api";
9
+ import { useMutation } from "convex/react";
10
+ import { useRef, useEffect, useState } from "react";
11
+ import {
12
+ View,
13
+ Text,
14
+ TextInput,
15
+ TouchableOpacity,
16
+ ScrollView,
17
+ KeyboardAvoidingView,
18
+ Platform,
19
+ StyleSheet,
20
+ ActivityIndicator,
21
+ } from "react-native";
22
+
23
+ import { Container } from "@/components/container";
24
+ import { useColorScheme } from "@/lib/use-color-scheme";
25
+ import { NAV_THEME } from "@/lib/constants";
26
+
27
+ function MessageContent({
28
+ text,
29
+ isStreaming,
30
+ textColor,
31
+ }: {
32
+ text: string;
33
+ isStreaming: boolean;
34
+ textColor: string;
35
+ }) {
36
+ const [visibleText] = useSmoothText(text, {
37
+ startStreaming: isStreaming,
38
+ });
39
+ return <Text style={[styles.messageText, { color: textColor }]}>{visibleText}</Text>;
40
+ }
41
+
42
+ export default function AIScreen() {
43
+ const { colorScheme } = useColorScheme();
44
+ const theme = colorScheme === "dark" ? NAV_THEME.dark : NAV_THEME.light;
45
+ const [input, setInput] = useState("");
46
+ const [threadId, setThreadId] = useState<string | null>(null);
47
+ const [isLoading, setIsLoading] = useState(false);
48
+ const scrollViewRef = useRef<ScrollView>(null);
49
+
50
+ const createThread = useMutation(api.chat.createNewThread);
51
+ const sendMessage = useMutation(api.chat.sendMessage);
52
+
53
+ const { results: messages } = useUIMessages(
54
+ api.chat.listMessages,
55
+ threadId ? { threadId } : "skip",
56
+ { initialNumItems: 50, stream: true },
57
+ );
58
+
59
+ const hasStreamingMessage = messages?.some(
60
+ (m: UIMessage) => m.status === "streaming",
61
+ );
62
+
63
+ useEffect(() => {
64
+ scrollViewRef.current?.scrollToEnd({ animated: true });
65
+ }, [messages]);
66
+
67
+ async function onSubmit() {
68
+ const value = input.trim();
69
+ if (!value || isLoading) return;
70
+
71
+ setIsLoading(true);
72
+ setInput("");
73
+
74
+ try {
75
+ let currentThreadId = threadId;
76
+ if (!currentThreadId) {
77
+ currentThreadId = await createThread();
78
+ setThreadId(currentThreadId);
79
+ }
80
+
81
+ await sendMessage({ threadId: currentThreadId, prompt: value });
82
+ } catch (error) {
83
+ console.error("Failed to send message:", error);
84
+ } finally {
85
+ setIsLoading(false);
86
+ }
87
+ }
88
+
89
+ return (
90
+ <Container>
91
+ <KeyboardAvoidingView
92
+ style={styles.container}
93
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
94
+ >
95
+ <View style={styles.content}>
96
+ <View style={styles.header}>
97
+ <Text style={[styles.headerTitle, { color: theme.text }]}>
98
+ AI Chat
99
+ </Text>
100
+ <Text style={[styles.headerSubtitle, { color: theme.text, opacity: 0.7 }]}>
101
+ Chat with our AI assistant
102
+ </Text>
103
+ </View>
104
+ <ScrollView
105
+ ref={scrollViewRef}
106
+ style={styles.scrollView}
107
+ showsVerticalScrollIndicator={false}
108
+ >
109
+ {!messages || messages.length === 0 ? (
110
+ <View style={styles.emptyContainer}>
111
+ <Text style={[styles.emptyText, { color: theme.text, opacity: 0.7 }]}>
112
+ Ask me anything to get started!
113
+ </Text>
114
+ </View>
115
+ ) : (
116
+ <View style={styles.messagesList}>
117
+ {messages.map((message: UIMessage) => (
118
+ <View
119
+ key={message.key}
120
+ style={[
121
+ styles.messageCard,
122
+ {
123
+ backgroundColor: message.role === "user"
124
+ ? theme.primary + "20"
125
+ : theme.card,
126
+ borderColor: theme.border,
127
+ alignSelf: message.role === "user" ? "flex-end" : "flex-start",
128
+ marginLeft: message.role === "user" ? 32 : 0,
129
+ marginRight: message.role === "user" ? 0 : 32,
130
+ },
131
+ ]}
132
+ >
133
+ <Text style={[styles.messageRole, { color: theme.text }]}>
134
+ {message.role === "user" ? "You" : "AI Assistant"}
135
+ </Text>
136
+ <MessageContent
137
+ text={message.text ?? ""}
138
+ isStreaming={message.status === "streaming"}
139
+ textColor={theme.text}
140
+ />
141
+ </View>
142
+ ))}
143
+ {isLoading && !hasStreamingMessage && (
144
+ <View
145
+ style={[
146
+ styles.messageCard,
147
+ {
148
+ backgroundColor: theme.card,
149
+ borderColor: theme.border,
150
+ alignSelf: "flex-start",
151
+ marginRight: 32,
152
+ },
153
+ ]}
154
+ >
155
+ <Text style={[styles.messageRole, { color: theme.text }]}>
156
+ AI Assistant
157
+ </Text>
158
+ <View style={styles.loadingContainer}>
159
+ <ActivityIndicator size="small" color={theme.primary} />
160
+ <Text style={[styles.loadingText, { color: theme.text, opacity: 0.7 }]}>
161
+ Thinking...
162
+ </Text>
163
+ </View>
164
+ </View>
165
+ )}
166
+ </View>
167
+ )}
168
+ </ScrollView>
169
+ <View style={[styles.inputContainer, { borderTopColor: theme.border }]}>
170
+ <View style={styles.inputRow}>
171
+ <TextInput
172
+ value={input}
173
+ onChangeText={setInput}
174
+ placeholder="Type your message..."
175
+ placeholderTextColor={theme.text}
176
+ style={[
177
+ styles.input,
178
+ {
179
+ color: theme.text,
180
+ borderColor: theme.border,
181
+ backgroundColor: theme.background,
182
+ },
183
+ ]}
184
+ onSubmitEditing={(e) => {
185
+ e.preventDefault();
186
+ onSubmit();
187
+ }}
188
+ editable={!isLoading}
189
+ autoFocus={true}
190
+ multiline
191
+ />
192
+ <TouchableOpacity
193
+ onPress={onSubmit}
194
+ disabled={!input.trim() || isLoading}
195
+ style={[
196
+ styles.sendButton,
197
+ {
198
+ backgroundColor: input.trim() && !isLoading ? theme.primary : theme.border,
199
+ opacity: input.trim() && !isLoading ? 1 : 0.5,
200
+ },
201
+ ]}
202
+ >
203
+ <Ionicons
204
+ name="send"
205
+ size={20}
206
+ color="#ffffff"
207
+ />
208
+ </TouchableOpacity>
209
+ </View>
210
+ </View>
211
+ </View>
212
+ </KeyboardAvoidingView>
213
+ </Container>
214
+ );
215
+ }
216
+
217
+ const styles = StyleSheet.create({
218
+ container: {
219
+ flex: 1,
220
+ },
221
+ content: {
222
+ flex: 1,
223
+ padding: 16,
224
+ },
225
+ header: {
226
+ marginBottom: 16,
227
+ },
228
+ headerTitle: {
229
+ fontSize: 20,
230
+ fontWeight: "bold",
231
+ marginBottom: 4,
232
+ },
233
+ headerSubtitle: {
234
+ fontSize: 14,
235
+ },
236
+ scrollView: {
237
+ flex: 1,
238
+ marginBottom: 16,
239
+ },
240
+ emptyContainer: {
241
+ flex: 1,
242
+ justifyContent: "center",
243
+ alignItems: "center",
244
+ },
245
+ emptyText: {
246
+ fontSize: 16,
247
+ textAlign: "center",
248
+ },
249
+ messagesList: {
250
+ gap: 8,
251
+ paddingBottom: 16,
252
+ },
253
+ messageCard: {
254
+ borderWidth: 1,
255
+ padding: 12,
256
+ maxWidth: "80%",
257
+ },
258
+ messageRole: {
259
+ fontSize: 12,
260
+ fontWeight: "bold",
261
+ marginBottom: 4,
262
+ },
263
+ messageText: {
264
+ fontSize: 14,
265
+ lineHeight: 20,
266
+ },
267
+ loadingContainer: {
268
+ flexDirection: "row",
269
+ alignItems: "center",
270
+ gap: 8,
271
+ },
272
+ loadingText: {
273
+ fontSize: 14,
274
+ },
275
+ inputContainer: {
276
+ borderTopWidth: 1,
277
+ paddingTop: 12,
278
+ },
279
+ inputRow: {
280
+ flexDirection: "row",
281
+ alignItems: "flex-end",
282
+ gap: 8,
283
+ },
284
+ input: {
285
+ flex: 1,
286
+ borderWidth: 1,
287
+ padding: 8,
288
+ fontSize: 14,
289
+ minHeight: 36,
290
+ maxHeight: 100,
291
+ },
292
+ sendButton: {
293
+ padding: 8,
294
+ justifyContent: "center",
295
+ alignItems: "center",
296
+ },
297
+ });
298
+ {{else}}
1
299
  import { useRef, useEffect, useState } from "react";
2
300
  import {
3
301
  View,
@@ -104,8 +402,8 @@ export default function AIScreen() {
104
402
  style={[
105
403
  styles.messageCard,
106
404
  {
107
- backgroundColor: message.role === "user"
108
- ? theme.primary + "20"
405
+ backgroundColor: message.role === "user"
406
+ ? theme.primary + "20"
109
407
  : theme.card,
110
408
  borderColor: theme.border,
111
409
  alignSelf: message.role === "user" ? "flex-end" : "flex-start",
@@ -284,4 +582,4 @@ const styles = StyleSheet.create({
284
582
  textAlign: "center",
285
583
  },
286
584
  });
287
-
585
+ {{/if}}
@@ -1,3 +1,297 @@
1
+ {{#if (eq backend "convex")}}
2
+ import { Ionicons } from "@expo/vector-icons";
3
+ import {
4
+ useUIMessages,
5
+ useSmoothText,
6
+ type UIMessage,
7
+ } from "@convex-dev/agent/react";
8
+ import { api } from "@{{projectName}}/backend/convex/_generated/api";
9
+ import { useMutation } from "convex/react";
10
+ import React, { useRef, useEffect, useState } from "react";
11
+ import {
12
+ View,
13
+ Text,
14
+ TextInput,
15
+ TouchableOpacity,
16
+ ScrollView,
17
+ KeyboardAvoidingView,
18
+ Platform,
19
+ ActivityIndicator,
20
+ } from "react-native";
21
+ import { StyleSheet, useUnistyles } from "react-native-unistyles";
22
+
23
+ import { Container } from "@/components/container";
24
+
25
+ function MessageContent({
26
+ text,
27
+ isStreaming,
28
+ style,
29
+ }: {
30
+ text: string;
31
+ isStreaming: boolean;
32
+ style: object;
33
+ }) {
34
+ const [visibleText] = useSmoothText(text, {
35
+ startStreaming: isStreaming,
36
+ });
37
+ return <Text style={style}>{visibleText}</Text>;
38
+ }
39
+
40
+ export default function AIScreen() {
41
+ const { theme } = useUnistyles();
42
+ const [input, setInput] = useState("");
43
+ const [threadId, setThreadId] = useState<string | null>(null);
44
+ const [isLoading, setIsLoading] = useState(false);
45
+ const scrollViewRef = useRef<ScrollView>(null);
46
+
47
+ const createThread = useMutation(api.chat.createNewThread);
48
+ const sendMessage = useMutation(api.chat.sendMessage);
49
+
50
+ const { results: messages } = useUIMessages(
51
+ api.chat.listMessages,
52
+ threadId ? { threadId } : "skip",
53
+ { initialNumItems: 50, stream: true },
54
+ );
55
+
56
+ const hasStreamingMessage = messages?.some(
57
+ (m: UIMessage) => m.status === "streaming",
58
+ );
59
+
60
+ useEffect(() => {
61
+ scrollViewRef.current?.scrollToEnd({ animated: true });
62
+ }, [messages]);
63
+
64
+ const onSubmit = async () => {
65
+ const value = input.trim();
66
+ if (!value || isLoading) return;
67
+
68
+ setIsLoading(true);
69
+ setInput("");
70
+
71
+ try {
72
+ let currentThreadId = threadId;
73
+ if (!currentThreadId) {
74
+ currentThreadId = await createThread();
75
+ setThreadId(currentThreadId);
76
+ }
77
+
78
+ await sendMessage({ threadId: currentThreadId, prompt: value });
79
+ } catch (error) {
80
+ console.error("Failed to send message:", error);
81
+ } finally {
82
+ setIsLoading(false);
83
+ }
84
+ };
85
+
86
+ return (
87
+ <Container>
88
+ <KeyboardAvoidingView
89
+ style={styles.container}
90
+ behavior={Platform.OS === "ios" ? "padding" : "height"}
91
+ >
92
+ <View style={styles.content}>
93
+ <View style={styles.header}>
94
+ <Text style={styles.headerTitle}>AI Chat</Text>
95
+ <Text style={styles.headerSubtitle}>
96
+ Chat with our AI assistant
97
+ </Text>
98
+ </View>
99
+
100
+ <ScrollView
101
+ ref={scrollViewRef}
102
+ style={styles.messagesContainer}
103
+ showsVerticalScrollIndicator={false}
104
+ >
105
+ {!messages || messages.length === 0 ? (
106
+ <View style={styles.emptyContainer}>
107
+ <Text style={styles.emptyText}>
108
+ Ask me anything to get started!
109
+ </Text>
110
+ </View>
111
+ ) : (
112
+ <View style={styles.messagesWrapper}>
113
+ {messages.map((message: UIMessage) => (
114
+ <View
115
+ key={message.key}
116
+ style={[
117
+ styles.messageContainer,
118
+ message.role === "user"
119
+ ? styles.userMessage
120
+ : styles.assistantMessage,
121
+ ]}
122
+ >
123
+ <Text style={styles.messageRole}>
124
+ {message.role === "user" ? "You" : "AI Assistant"}
125
+ </Text>
126
+ <MessageContent
127
+ text={message.text ?? ""}
128
+ isStreaming={message.status === "streaming"}
129
+ style={styles.messageContent}
130
+ />
131
+ </View>
132
+ ))}
133
+ {isLoading && !hasStreamingMessage && (
134
+ <View style={[styles.messageContainer, styles.assistantMessage]}>
135
+ <Text style={styles.messageRole}>AI Assistant</Text>
136
+ <View style={styles.loadingContainer}>
137
+ <ActivityIndicator size="small" color={theme.colors.primary} />
138
+ <Text style={styles.loadingText}>Thinking...</Text>
139
+ </View>
140
+ </View>
141
+ )}
142
+ </View>
143
+ )}
144
+ </ScrollView>
145
+
146
+ <View style={styles.inputSection}>
147
+ <View style={styles.inputContainer}>
148
+ <TextInput
149
+ value={input}
150
+ onChangeText={setInput}
151
+ placeholder="Type your message..."
152
+ placeholderTextColor={theme.colors.border}
153
+ style={styles.textInput}
154
+ onSubmitEditing={(e) => {
155
+ e.preventDefault();
156
+ onSubmit();
157
+ }}
158
+ editable={!isLoading}
159
+ autoFocus={true}
160
+ />
161
+ <TouchableOpacity
162
+ onPress={onSubmit}
163
+ disabled={!input.trim() || isLoading}
164
+ style={[
165
+ styles.sendButton,
166
+ (!input.trim() || isLoading) && styles.sendButtonDisabled,
167
+ ]}
168
+ >
169
+ <Ionicons
170
+ name="send"
171
+ size={20}
172
+ color={
173
+ input.trim() && !isLoading
174
+ ? theme.colors.background
175
+ : theme.colors.border
176
+ }
177
+ />
178
+ </TouchableOpacity>
179
+ </View>
180
+ </View>
181
+ </View>
182
+ </KeyboardAvoidingView>
183
+ </Container>
184
+ );
185
+ }
186
+
187
+ const styles = StyleSheet.create((theme) => ({
188
+ container: {
189
+ flex: 1,
190
+ },
191
+ content: {
192
+ flex: 1,
193
+ paddingHorizontal: theme.spacing.md,
194
+ paddingVertical: theme.spacing.lg,
195
+ },
196
+ header: {
197
+ marginBottom: theme.spacing.lg,
198
+ },
199
+ headerTitle: {
200
+ fontSize: 28,
201
+ fontWeight: "bold",
202
+ color: theme.colors.typography,
203
+ marginBottom: theme.spacing.sm,
204
+ },
205
+ headerSubtitle: {
206
+ fontSize: 16,
207
+ color: theme.colors.typography,
208
+ },
209
+ messagesContainer: {
210
+ flex: 1,
211
+ marginBottom: theme.spacing.md,
212
+ },
213
+ emptyContainer: {
214
+ flex: 1,
215
+ justifyContent: "center",
216
+ alignItems: "center",
217
+ },
218
+ emptyText: {
219
+ textAlign: "center",
220
+ color: theme.colors.typography,
221
+ fontSize: 18,
222
+ },
223
+ messagesWrapper: {
224
+ gap: theme.spacing.md,
225
+ },
226
+ messageContainer: {
227
+ padding: theme.spacing.md,
228
+ borderRadius: 8,
229
+ },
230
+ userMessage: {
231
+ backgroundColor: theme.colors.primary + "20",
232
+ marginLeft: theme.spacing.xl,
233
+ alignSelf: "flex-end",
234
+ },
235
+ assistantMessage: {
236
+ backgroundColor: theme.colors.background,
237
+ marginRight: theme.spacing.xl,
238
+ borderWidth: 1,
239
+ borderColor: theme.colors.border,
240
+ },
241
+ messageRole: {
242
+ fontSize: 14,
243
+ fontWeight: "600",
244
+ marginBottom: theme.spacing.sm,
245
+ color: theme.colors.typography,
246
+ },
247
+ messageContent: {
248
+ color: theme.colors.typography,
249
+ lineHeight: 20,
250
+ },
251
+ loadingContainer: {
252
+ flexDirection: "row",
253
+ alignItems: "center",
254
+ gap: theme.spacing.sm,
255
+ },
256
+ loadingText: {
257
+ color: theme.colors.typography,
258
+ opacity: 0.7,
259
+ },
260
+ inputSection: {
261
+ borderTopWidth: 1,
262
+ borderTopColor: theme.colors.border,
263
+ paddingTop: theme.spacing.md,
264
+ },
265
+ inputContainer: {
266
+ flexDirection: "row",
267
+ alignItems: "flex-end",
268
+ gap: theme.spacing.sm,
269
+ },
270
+ textInput: {
271
+ flex: 1,
272
+ borderWidth: 1,
273
+ borderColor: theme.colors.border,
274
+ borderRadius: 8,
275
+ paddingHorizontal: theme.spacing.md,
276
+ paddingVertical: theme.spacing.sm,
277
+ color: theme.colors.typography,
278
+ backgroundColor: theme.colors.background,
279
+ fontSize: 16,
280
+ minHeight: 40,
281
+ maxHeight: 120,
282
+ },
283
+ sendButton: {
284
+ backgroundColor: theme.colors.primary,
285
+ padding: theme.spacing.sm,
286
+ borderRadius: 8,
287
+ justifyContent: "center",
288
+ alignItems: "center",
289
+ },
290
+ sendButtonDisabled: {
291
+ backgroundColor: theme.colors.border,
292
+ },
293
+ }));
294
+ {{else}}
1
295
  import React, { useRef, useEffect, useState } from "react";
2
296
  import {
3
297
  View,
@@ -256,15 +550,6 @@ const styles = StyleSheet.create((theme) => ({
256
550
  color: theme.colors.typography,
257
551
  lineHeight: 20,
258
552
  },
259
- toolInvocations: {
260
- fontSize: 12,
261
- color: theme.colors.typography,
262
- fontFamily: "monospace",
263
- backgroundColor: theme.colors.border + "40",
264
- padding: theme.spacing.sm,
265
- borderRadius: 4,
266
- marginTop: theme.spacing.sm,
267
- },
268
553
  inputSection: {
269
554
  borderTopWidth: 1,
270
555
  borderTopColor: theme.colors.border,
@@ -298,4 +583,5 @@ const styles = StyleSheet.create((theme) => ({
298
583
  sendButtonDisabled: {
299
584
  backgroundColor: theme.colors.border,
300
585
  },
301
- }));
586
+ }));
587
+ {{/if}}