create-better-t-stack 3.12.5 → 3.12.7

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/cli.mjs +1 -1
  2. package/dist/index.mjs +1 -1
  3. package/dist/{src-D3EHRs6z.mjs → src-DNjxspj9.mjs} +32 -21
  4. package/package.json +2 -2
  5. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +3 -3
  6. package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +32 -50
  7. package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +40 -57
  8. package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +32 -35
  9. package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +49 -37
  10. package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +40 -35
  11. package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +47 -40
  12. package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +9 -7
  13. package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +61 -29
  14. package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +6 -3
  15. package/templates/backend/server/elysia/src/index.ts.hbs +8 -3
  16. package/templates/backend/server/express/src/index.ts.hbs +8 -3
  17. package/templates/backend/server/fastify/src/index.ts.hbs +8 -3
  18. package/templates/backend/server/hono/src/index.ts.hbs +16 -6
  19. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +8 -3
  20. package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +8 -3
  21. package/templates/examples/ai/native/bare/polyfills.js +14 -11
  22. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +104 -124
  23. package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +22 -22
  24. package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +67 -80
  25. package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +115 -90
  26. package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +60 -54
  27. package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +16 -21
  28. package/templates/frontend/native/uniwind/app/modal.tsx.hbs +18 -34
  29. package/templates/frontend/native/uniwind/package.json.hbs +10 -10
  30. package/templates/frontend/nuxt/app/components/Header.vue.hbs +25 -29
  31. package/templates/frontend/nuxt/app/layouts/default.vue.hbs +7 -8
  32. package/templates/frontend/nuxt/app/pages/index.vue.hbs +58 -39
  33. package/templates/frontend/nuxt/package.json.hbs +1 -1
  34. package/templates/frontend/react/tanstack-router/package.json.hbs +1 -1
  35. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +5 -5
  36. package/templates/frontend/nuxt/app/components/Loader.vue.hbs +0 -5
  37. package/templates/frontend/nuxt/app/components/ModeToggle.vue.hbs +0 -25
@@ -1,4 +1,4 @@
1
- import { Text, View, TouchableOpacity } from "react-native";
1
+ import { Text, View } from "react-native";
2
2
  import { Container } from "@/components/container";
3
3
  {{#if (eq api "orpc")}}
4
4
  import { useQuery } from "@tanstack/react-query";
@@ -27,7 +27,7 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
27
27
  {{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
28
28
  import { Ionicons } from "@expo/vector-icons";
29
29
  {{/unless}}
30
- import { Card, Chip, useThemeColor } from "heroui-native";
30
+ import { Button, Chip, Divider, Spinner, Surface, useThemeColor } from "heroui-native";
31
31
 
32
32
  export default function Home() {
33
33
  {{#if (eq api "orpc")}}
@@ -48,7 +48,6 @@ const user = useQuery(api.auth.getCurrentUser, isAuthenticated ? {} : "skip");
48
48
  const healthCheck = useQuery(api.healthCheck.get);
49
49
  {{/if}}
50
50
  {{#unless (eq backend "none")}}
51
- const mutedColor = useThemeColor("muted");
52
51
  const successColor = useThemeColor("success");
53
52
  const dangerColor = useThemeColor("danger");
54
53
 
@@ -64,28 +63,32 @@ const isLoading = healthCheck?.isLoading;
64
63
  {{/unless}}
65
64
 
66
65
  return (
67
- <Container className="p-6">
68
- <View className="py-4 mb-6">
69
- <Text className="text-4xl font-bold text-foreground mb-2">
70
- BETTER T STACK
66
+ <Container className="p-4">
67
+ <View className="py-6 mb-4">
68
+ <Text className="text-3xl font-semibold text-foreground tracking-tight">
69
+ Better T Stack
71
70
  </Text>
71
+ <Text className="text-muted text-sm mt-1">Full-stack TypeScript starter</Text>
72
72
  </View>
73
73
 
74
74
  {{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
75
- <Card variant="secondary" className="p-6">
76
- <View className="flex-row items-center justify-between mb-4">
77
- <Card.Title>System Status</Card.Title>
75
+ <Surface variant="secondary" className="p-4 rounded-lg">
76
+ <View className="flex-row items-center justify-between mb-3">
77
+ <Text className="text-foreground font-medium">System Status</Text>
78
78
  <Chip variant="secondary" color={isConnected ? "success" : "danger" } size="sm">
79
79
  <Chip.Label>
80
80
  {isConnected ? "LIVE" : "OFFLINE"}
81
81
  </Chip.Label>
82
82
  </Chip>
83
83
  </View>
84
- <Card className="p-4">
84
+
85
+ <Divider className="mb-3" />
86
+
87
+ <Surface variant="tertiary" className="p-3 rounded-md">
85
88
  <View className="flex-row items-center">
86
- <View className={`w-3 h-3 rounded-full mr-3 ${ isConnected ? "bg-success" : "bg-muted" }`} />
89
+ <View className={`w-2 h-2 rounded-full mr-3 ${ isConnected ? "bg-success" : "bg-muted" }`} />
87
90
  <View className="flex-1">
88
- <Text className="text-foreground font-medium mb-1">
91
+ <Text className="text-foreground text-sm font-medium">
89
92
  {{#if (eq backend "convex")}}
90
93
  Convex Backend
91
94
  {{else}}
@@ -94,77 +97,80 @@ return (
94
97
  {{/unless}}
95
98
  {{/if}}
96
99
  </Text>
97
- <Card.Description>
100
+ <Text className="text-muted text-xs mt-0.5">
98
101
  {isLoading
99
102
  ? "Checking connection..."
100
103
  : isConnected
101
104
  ? "Connected to API"
102
105
  : "API Disconnected"}
103
- </Card.Description>
106
+ </Text>
104
107
  </View>
105
- {isLoading && (
106
- <Ionicons name="hourglass-outline" size={20} color={mutedColor} />
107
- )}
108
+ {isLoading && <Spinner size="sm" />}
108
109
  {!isLoading && isConnected && (
109
- <Ionicons name="checkmark-circle" size={20} color={successColor} />
110
+ <Ionicons name="checkmark-circle" size={18} color={successColor} />
110
111
  )}
111
112
  {!isLoading && !isConnected && (
112
- <Ionicons name="close-circle" size={20} color={dangerColor} />
113
+ <Ionicons name="close-circle" size={18} color={dangerColor} />
113
114
  )}
114
115
  </View>
115
- </Card>
116
- </Card>
116
+ </Surface>
117
+ </Surface>
117
118
  {{/unless}}
118
119
 
119
120
  {{#if (and (eq backend "convex") (eq auth "clerk"))}}
120
121
  <Authenticated>
121
- <Card variant="secondary" className="mt-6 p-4">
122
- <Text className="text-foreground text-base mb-2">
123
- Hello {user?.emailAddresses[0].emailAddress}
124
- </Text>
125
- <Text className="text-muted text-sm mb-4">
126
- Private Data: {privateData?.message}
127
- </Text>
128
- <SignOutButton />
129
- </Card>
122
+ <Surface variant="secondary" className="mt-4 p-4 rounded-lg">
123
+ <View className="flex-row items-center justify-between">
124
+ <View className="flex-1">
125
+ <Text className="text-foreground font-medium">{user?.emailAddresses[0].emailAddress}</Text>
126
+ <Text className="text-muted text-xs mt-0.5">Private: {privateData?.message}</Text>
127
+ </View>
128
+ <SignOutButton />
129
+ </View>
130
+ </Surface>
130
131
  </Authenticated>
131
132
  <Unauthenticated>
132
- <View className="mt-6 gap-4">
133
+ <View className="mt-4 gap-3">
133
134
  <Link href="/(auth)/sign-in" asChild>
134
- <Text className="text-primary font-semibold">Sign in</Text>
135
+ <Button variant="secondary"><Button.Label>Sign In</Button.Label></Button>
135
136
  </Link>
136
137
  <Link href="/(auth)/sign-up" asChild>
137
- <Text className="text-primary font-semibold">Sign up</Text>
138
+ <Button variant="ghost"><Button.Label>Sign Up</Button.Label></Button>
138
139
  </Link>
139
140
  </View>
140
141
  </Unauthenticated>
141
142
  <AuthLoading>
142
- <Text className="text-muted">Loading...</Text>
143
+ <View className="mt-4 items-center">
144
+ <Spinner size="sm" />
145
+ </View>
143
146
  </AuthLoading>
144
147
  {{/if}}
145
148
 
146
149
  {{#if (and (eq backend "convex") (eq auth "better-auth"))}}
147
150
  {user ? (
148
- <Card variant="secondary" className="p-4 mb-4">
149
- <View className="mb-2">
150
- <Text className="text-foreground text-base">
151
- Welcome, <Text className="font-semibold">{user.name}</Text>
152
- </Text>
153
- </View>
154
- <Text className="text-muted text-sm mb-4">{user.email}</Text>
155
- <TouchableOpacity className="bg-danger px-4 py-2 rounded-md self-start" onPress={()=> {
156
- authClient.signOut();
157
- }}
151
+ <Surface variant="secondary" className="mb-4 p-4 rounded-lg">
152
+ <View className="flex-row items-center justify-between">
153
+ <View className="flex-1">
154
+ <Text className="text-foreground font-medium">{user.name}</Text>
155
+ <Text className="text-muted text-xs mt-0.5">{user.email}</Text>
156
+ </View>
157
+ <Button
158
+ variant="destructive"
159
+ size="sm"
160
+ onPress={() => {
161
+ authClient.signOut();
162
+ }}
158
163
  >
159
- <Text className="text-danger-foreground font-medium">Sign Out</Text>
160
- </TouchableOpacity>
161
- </Card>
164
+ Sign Out
165
+ </Button>
166
+ </View>
167
+ </Surface>
162
168
  ) : null}
163
- <Card variant="secondary" className="p-4 mb-4">
169
+ <Surface variant="secondary" className="p-4 rounded-lg">
164
170
  <Text className="text-foreground font-medium mb-2">API Status</Text>
165
171
  <View className="flex-row items-center gap-2">
166
- <View className={`w-3 h-3 rounded-full ${healthCheck==="OK" ? "bg-success" : "bg-danger" }`} />
167
- <Text className="text-muted">
172
+ <View className={`w-2 h-2 rounded-full ${healthCheck==="OK" ? "bg-success" : "bg-danger" }`} />
173
+ <Text className="text-muted text-xs">
168
174
  {healthCheck === undefined
169
175
  ? "Checking..."
170
176
  : healthCheck === "OK"
@@ -172,12 +178,12 @@ return (
172
178
  : "API Disconnected"}
173
179
  </Text>
174
180
  </View>
175
- </Card>
181
+ </Surface>
176
182
  {!user && (
177
- <>
183
+ <View className="mt-4 gap-4">
178
184
  <SignIn />
179
185
  <SignUp />
180
- </>
186
+ </View>
181
187
  )}
182
188
  {{/if}}
183
189
  </Container>
@@ -1,30 +1,25 @@
1
- import { Container } from "@/components/container";
2
1
  import { Link, Stack } from "expo-router";
3
- import { Text, View, Pressable } from "react-native";
4
- import { Card } from "heroui-native";
2
+ import { Button, Surface } from "heroui-native";
3
+ import { Text, View } from "react-native";
4
+
5
+ import { Container } from "@/components/container";
5
6
 
6
7
  export default function NotFoundScreen() {
7
8
  return (
8
9
  <>
9
- <Stack.Screen options=\{{ title: "Oops!" }} />
10
+ <Stack.Screen options=\{{ title: "Not Found" }} />
10
11
  <Container>
11
- <View className="flex-1 justify-center items-center p-6">
12
- <Card variant="secondary" className="items-center p-8 max-w-md">
13
- <Text className="text-6xl mb-4">🤔</Text>
14
- <Card.Title className="text-2xl text-center mb-2">
15
- Page Not Found
16
- </Card.Title>
17
- <Card.Description className="text-center mb-6">
18
- Sorry, the page you're looking for doesn't exist.
19
- </Card.Description>
20
- <Link href="/" asChild>
21
- <Pressable className="bg-accent px-6 py-3 rounded-lg active:opacity-70">
22
- <Text className="text-accent-foreground font-medium text-base">
23
- Go to Home
24
- </Text>
25
- </Pressable>
26
- </Link>
27
- </Card>
12
+ <View className="flex-1 justify-center items-center p-4">
13
+ <Surface variant="secondary" className="items-center p-6 max-w-sm rounded-lg">
14
+ <Text className="text-4xl mb-3">🤔</Text>
15
+ <Text className="text-foreground font-medium text-lg mb-1">Page Not Found</Text>
16
+ <Text className="text-muted text-sm text-center mb-4">
17
+ The page you're looking for doesn't exist.
18
+ </Text>
19
+ <Link href="/" asChild>
20
+ <Button size="sm">Go Home</Button>
21
+ </Link>
22
+ </Surface>
28
23
  </View>
29
24
  </Container>
30
25
  </>
@@ -1,8 +1,9 @@
1
- import { Container } from "@/components/container";
2
- import { Text, View, Pressable } from "react-native";
3
1
  import { Ionicons } from "@expo/vector-icons";
4
2
  import { router } from "expo-router";
5
- import { Card, useThemeColor } from "heroui-native";
3
+ import { Button, Surface, useThemeColor } from "heroui-native";
4
+ import { Text, View } from "react-native";
5
+
6
+ import { Container } from "@/components/container";
6
7
 
7
8
  function Modal() {
8
9
  const accentForegroundColor = useThemeColor("accent-foreground");
@@ -13,38 +14,21 @@ function Modal() {
13
14
 
14
15
  return (
15
16
  <Container>
16
- <View className="flex-1 justify-center items-center p-6">
17
- <Card variant="secondary" className="p-6 w-full max-w-sm">
18
- <Card.Body className="gap-4 items-center">
19
- <View className="w-16 h-16 bg-accent rounded-full items-center justify-center mb-2">
20
- <Ionicons name="checkmark" size={32} color={accentForegroundColor} />
17
+ <View className="flex-1 justify-center items-center p-4">
18
+ <Surface variant="secondary" className="p-5 w-full max-w-sm rounded-lg">
19
+ <View className="items-center">
20
+ <View className="w-12 h-12 bg-accent rounded-lg items-center justify-center mb-3">
21
+ <Ionicons name="checkmark" size={24} color={accentForegroundColor} />
21
22
  </View>
22
- <Card.Title className="text-center text-xl">
23
- Modal Screen
24
- </Card.Title>
25
- <Card.Description className="text-center">
26
- This is an example modal screen. You can use this pattern for
27
- dialogs, confirmations, or any overlay content.
28
- </Card.Description>
29
- </Card.Body>
30
- <Card.Footer className="mt-4">
31
- <Pressable
32
- onPress={handleClose}
33
- className="bg-accent p-4 rounded-lg w-full active:opacity-70"
34
- >
35
- <View className="flex-row items-center justify-center">
36
- <Text className="text-accent-foreground font-semibold text-base mr-2">
37
- Close Modal
38
- </Text>
39
- <Ionicons
40
- name="close-circle"
41
- size={20}
42
- color={accentForegroundColor}
43
- />
44
- </View>
45
- </Pressable>
46
- </Card.Footer>
47
- </Card>
23
+ <Text className="text-foreground font-medium text-lg mb-1">Modal Screen</Text>
24
+ <Text className="text-muted text-sm text-center mb-4">
25
+ This is an example modal screen for dialogs and confirmations.
26
+ </Text>
27
+ </View>
28
+ <Button onPress={handleClose} className="w-full" size="sm">
29
+ <Button.Label>Close</Button.Label>
30
+ </Button>
31
+ </Surface>
48
32
  </View>
49
33
  </Container>
50
34
  );
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "native",
3
3
  "version": "1.0.0",
4
+ "private": true,
4
5
  "main": "expo-router/entry",
5
6
  "scripts": {
6
7
  "start": "expo start",
@@ -13,7 +14,7 @@
13
14
  "dependencies": {
14
15
  "@expo/metro-runtime": "~6.1.2",
15
16
  "@expo/vector-icons": "^15.0.3",
16
- "@gorhom/bottom-sheet": "^5.2.6",
17
+ "@gorhom/bottom-sheet": "^5",
17
18
  "@react-navigation/drawer": "^7.3.9",
18
19
  "@react-navigation/elements": "^2.8.1",
19
20
  {{#if (includes examples "ai")}}
@@ -29,26 +30,25 @@
29
30
  "expo-router": "~6.0.14",
30
31
  "expo-secure-store": "~15.0.7",
31
32
  "expo-status-bar": "~3.0.8",
32
- "heroui-native": "1.0.0-beta.3",
33
+ "heroui-native": "^1.0.0-beta.9",
33
34
  "react": "19.1.0",
34
35
  "react-dom": "19.1.0",
35
36
  "react-native": "0.81.5",
36
- "react-native-gesture-handler": "~2.28.0",
37
+ "react-native-gesture-handler": "^2.28.0",
37
38
  "react-native-keyboard-controller": "1.18.5",
38
- "react-native-reanimated": "~4.1.5",
39
- "react-native-safe-area-context": "5.6.0",
39
+ "react-native-reanimated": "~4.1.1",
40
+ "react-native-safe-area-context": "~5.6.0",
40
41
  "react-native-screens": "~4.16.0",
41
42
  "react-native-svg": "15.12.1",
42
43
  "react-native-web": "^0.21.0",
43
44
  "react-native-worklets": "0.5.1",
44
45
  "tailwind-merge": "^3.4.0",
45
- "tailwind-variants": "3.1.1",
46
- "tailwindcss": "~4.1.17",
47
- "uniwind": "^1.0.4"
46
+ "tailwind-variants": "^3.2.2",
47
+ "tailwindcss": "^4.1.18",
48
+ "uniwind": "^1.2.2"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@types/node": "^24.10.0",
51
52
  "@types/react": "~19.1.0"
52
- },
53
- "private": true
53
+ }
54
54
  }
@@ -1,44 +1,40 @@
1
1
  <script setup lang="ts">
2
- import ModeToggle from './ModeToggle.vue'
2
+ import type { NavigationMenuItem } from '@nuxt/ui'
3
3
  {{#if (eq auth "better-auth")}}
4
4
  import UserMenu from './UserMenu.vue'
5
5
  {{/if}}
6
6
 
7
- const links = [
8
- { to: "/", label: "Home" },
7
+ const route = useRoute()
8
+
9
+ const items = computed<NavigationMenuItem[]>(() => [
10
+ { label: "Home", to: "/", active: route.path === "/" },
9
11
  {{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
10
- { to: "/dashboard", label: "Dashboard" },
12
+ { label: "Dashboard", to: "/dashboard", active: route.path.startsWith("/dashboard") },
11
13
  {{/if}}
12
14
  {{#if (includes examples "todo")}}
13
- { to: "/todos", label: "Todos" },
15
+ { label: "Todos", to: "/todos", active: route.path.startsWith("/todos") },
14
16
  {{/if}}
15
17
  {{#if (includes examples "ai")}}
16
- { to: "/ai", label: "AI Chat" },
18
+ { label: "AI Chat", to: "/ai", active: route.path.startsWith("/ai") },
17
19
  {{/if}}
18
- ];
20
+ ])
19
21
  </script>
20
22
 
21
23
  <template>
22
- <div>
23
- <div class="flex flex-row items-center justify-between px-2 py-1">
24
- <nav class="flex gap-4 text-lg">
25
- <NuxtLink
26
- v-for="link in links"
27
- :key="link.to"
28
- :to="link.to"
29
- class="text-foreground hover:text-primary"
30
- active-class="text-primary font-semibold"
31
- >
32
- \{{ link.label }}
33
- </NuxtLink>
34
- </nav>
35
- <div class="flex items-center gap-2">
36
- <ModeToggle />
37
- {{#if (eq auth "better-auth")}}
38
- <UserMenu />
39
- {{/if}}
40
- </div>
41
- </div>
42
- <USeparator />
43
- </div>
24
+ <UHeader>
25
+ <template #left>
26
+ <UNavigationMenu :items="items" />
27
+ </template>
28
+
29
+ <template #right>
30
+ <UColorModeButton />
31
+ {{#if (eq auth "better-auth")}}
32
+ <UserMenu />
33
+ {{/if}}
34
+ </template>
35
+
36
+ <template #body>
37
+ <UNavigationMenu :items="items" orientation="vertical" class="-mx-2.5" />
38
+ </template>
39
+ </UHeader>
44
40
  </template>
@@ -1,11 +1,10 @@
1
- <script setup>
2
- </script>
1
+ <script setup></script>
3
2
 
4
3
  <template>
5
- <div class="grid grid-rows-[auto_1fr] h-svh">
6
- <Header />
7
- <main class="overflow-y-auto">
8
- <slot />
9
- </main>
10
- </div>
4
+ <div class="grid grid-rows-[auto_1fr] h-svh">
5
+ <Header />
6
+ <UMain>
7
+ <slot />
8
+ </UMain>
9
+ </div>
11
10
  </template>
@@ -4,8 +4,8 @@ import { api } from "@{{ projectName }}/backend/convex/_generated/api";
4
4
  import { useConvexQuery } from "convex-vue";
5
5
  {{else}}
6
6
  {{#unless (eq api "none")}}
7
- const { $orpc } = useNuxtApp()
8
- import { useQuery } from '@tanstack/vue-query'
7
+ const { $orpc } = useNuxtApp()
8
+ import { useQuery } from '@tanstack/vue-query'
9
9
  {{/unless}}
10
10
  {{/if}}
11
11
 
@@ -29,50 +29,69 @@ const TITLE_TEXT = `
29
29
  const healthCheck = useConvexQuery(api.healthCheck.get, {});
30
30
  {{else}}
31
31
  {{#unless (eq api "none")}}
32
- const healthCheck = useQuery($orpc.healthCheck.queryOptions())
32
+ const healthCheck = useQuery($orpc.healthCheck.queryOptions())
33
33
  {{/unless}}
34
34
  {{/if}}
35
35
  </script>
36
36
 
37
37
  <template>
38
- <div class="container mx-auto max-w-3xl px-4 py-2">
38
+ <UContainer class="py-8">
39
39
  <pre class="overflow-x-auto font-mono text-sm whitespace-pre-wrap">\{{ TITLE_TEXT }}</pre>
40
- <div class="grid gap-6 mt-4">
41
- <section class="rounded-lg border p-4">
42
- <h2 class="mb-2 font-medium">API Status</h2>
40
+
41
+ <div class="grid gap-6 mt-6">
42
+ <UCard>
43
+ <template #header>
44
+ <div class="font-medium">API Status</div>
45
+ </template>
46
+
47
+ {{#if (eq backend "convex")}}
48
+ <div class="flex items-center gap-2">
49
+ <UIcon
50
+ :name="healthCheck === undefined ? 'i-lucide-loader-2' : healthCheck.data.value === 'OK' ? 'i-lucide-check-circle' : 'i-lucide-x-circle'"
51
+ :class="[
52
+ healthCheck === undefined ? 'animate-spin text-muted' : '',
53
+ healthCheck?.data.value === 'OK' ? 'text-success' : 'text-error'
54
+ ]"
55
+ />
56
+ <span class="text-sm">
57
+ \{{
58
+ healthCheck === undefined
59
+ ? "Checking..."
60
+ : healthCheck.data.value === "OK"
61
+ ? "Connected"
62
+ : "Error"
63
+ }}
64
+ </span>
65
+ </div>
66
+ {{else}}
67
+ {{#unless (eq api "none")}}
43
68
  <div class="flex items-center gap-2">
44
- {{#if (eq backend "convex")}}
45
- <span class="text-sm text-muted-foreground">
46
- \{{
47
- healthCheck === undefined
48
- ? "Checking..."
49
- : healthCheck.data.value === "OK"
50
- ? "Connected"
51
- : "Error"
52
- }}
53
- </span>
54
- {{else}}
55
- {{#unless (eq api "none")}}
56
- <div class="flex items-center gap-2">
57
- <span class="text-sm text-muted-foreground">
58
- <template v-if="healthCheck.status.value === 'pending'">
59
- Checking...
60
- </template>
61
- <template v-else-if="healthCheck.status.value === 'success'">
62
- Connected (\{{ healthCheck.data.value }})
63
- </template>
64
- <template v-else-if="healthCheck.status.value === 'error'">
65
- Error: \{{ healthCheck.error.value?.message || 'Failed to connect' }}
66
- </template>
67
- <template v-else>
68
- Idle
69
- </template>
70
- </span>
71
- </div>
72
- {{/unless}}
73
- {{/if}}
69
+ <UIcon
70
+ :name="healthCheck.isLoading.value ? 'i-lucide-loader-2' : healthCheck.isSuccess.value ? 'i-lucide-check-circle' : 'i-lucide-x-circle'"
71
+ :class="[
72
+ healthCheck.isLoading.value ? 'animate-spin text-muted' : '',
73
+ healthCheck.isSuccess.value ? 'text-success' : '',
74
+ healthCheck.isError.value ? 'text-error' : ''
75
+ ]"
76
+ />
77
+ <span class="text-sm">
78
+ <template v-if="healthCheck.isLoading.value">
79
+ Checking...
80
+ </template>
81
+ <template v-else-if="healthCheck.isSuccess.value">
82
+ Connected (\{{ healthCheck.data.value }})
83
+ </template>
84
+ <template v-else-if="healthCheck.isError.value">
85
+ Error: \{{ healthCheck.error.value?.message || 'Failed to connect' }}
86
+ </template>
87
+ <template v-else>
88
+ Idle
89
+ </template>
90
+ </span>
74
91
  </div>
75
- </section>
92
+ {{/unless}}
93
+ {{/if}}
94
+ </UCard>
76
95
  </div>
77
- </div>
96
+ </UContainer>
78
97
  </template>
@@ -10,7 +10,7 @@
10
10
  "postinstall": "nuxt prepare"
11
11
  },
12
12
  "dependencies": {
13
- "@nuxt/ui": "^4.0.0",
13
+ "@nuxt/ui": "4.2.1",
14
14
  "@nuxt/content": "^3.7.1",
15
15
  "@nuxtjs/mdc": "^0.17.4",
16
16
  "nuxt": "^4.1.2",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@tanstack/react-router-devtools": "^1.141.1",
32
32
  "@tanstack/router-plugin": "^1.141.1",
33
- "@types/node": "^22.13.13",
33
+ "@types/node": "^22.13.14",
34
34
  "@types/react": "19.2.7",
35
35
  "@types/react-dom": "19.2.3",
36
36
  "@vitejs/plugin-react": "^4.3.4",
@@ -1,13 +1,13 @@
1
- import { cn } from '@/lib/utils'
1
+ import { cn } from "@/lib/utils";
2
2
 
3
- function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
3
+ function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
4
4
  return (
5
5
  <div
6
6
  data-slot="skeleton"
7
- className={cn('bg-muted rounded-none animate-pulse', className)}
7
+ className={cn("bg-muted rounded-none animate-pulse", className)}
8
8
  {...props}
9
9
  />
10
- )
10
+ );
11
11
  }
12
12
 
13
- export { Skeleton }
13
+ export { Skeleton };
@@ -1,5 +0,0 @@
1
- <template>
2
- <div class="flex h-full items-center justify-center pt-8">
3
- <UIcon name="i-lucide-loader-2" class="animate-spin text-2xl" />
4
- </div>
5
- </template>
@@ -1,25 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from "vue";
3
-
4
- const colorMode = useColorMode()
5
-
6
- const isDark = computed({
7
- get () {
8
- return colorMode.value === 'dark'
9
- },
10
- set (value) {
11
- colorMode.preference = value ? 'dark' : 'light'
12
- }
13
- })
14
- </script>
15
-
16
- <template>
17
- <div class="flex items-center">
18
- <USwitch
19
- v-model="isDark"
20
- :checked-icon="isDark ? 'i-lucide-moon' : ''"
21
- :unchecked-icon="!isDark ? 'i-lucide-sun' : ''"
22
- class="mr-2"
23
- />
24
- </div>
25
- </template>