fix: backend none templates (#241)

This commit is contained in:
Aman Varshney
2025-05-10 08:10:23 +05:30
committed by GitHub
parent 4e4ad2b9ee
commit 8209713bd6
29 changed files with 519 additions and 353 deletions

View File

@@ -0,0 +1,5 @@
---
"create-better-t-stack": patch
---
fix templates when backend is none

View File

@@ -20,9 +20,7 @@ export function displayConfig(config: Partial<ProjectConfig>) {
}
if (config.backend !== undefined) {
configDisplay.push(
`${pc.blue("Backend Framework:")} ${String(config.backend)}`,
);
configDisplay.push(`${pc.blue("Backend:")} ${String(config.backend)}`);
}
if (config.runtime !== undefined) {

View File

@@ -3,40 +3,19 @@ import type { ProjectConfig } from "../types";
export function generateReproducibleCommand(config: ProjectConfig): string {
const flags: string[] = [];
if (config.database === "none") {
flags.push("--database none");
} else {
flags.push(`--database ${config.database}`);
if (config.orm) {
flags.push(`--orm ${config.orm}`);
}
if (config.dbSetup) {
flags.push(`--db-setup ${config.dbSetup}`);
}
}
if (config.api) {
flags.push(`--api ${config.api}`);
}
flags.push(config.auth ? "--auth" : "--no-auth");
flags.push(config.git ? "--git" : "--no-git");
flags.push(config.install ? "--install" : "--no-install");
if (config.runtime) {
flags.push(`--runtime ${config.runtime}`);
}
if (config.backend) {
flags.push(`--backend ${config.backend}`);
}
if (config.frontend && config.frontend.length > 0) {
flags.push(`--frontend ${config.frontend.join(" ")}`);
} else {
flags.push("--frontend none");
}
flags.push(`--backend ${config.backend}`);
flags.push(`--runtime ${config.runtime}`);
flags.push(`--database ${config.database}`);
flags.push(`--orm ${config.orm}`);
flags.push(`--api ${config.api}`);
flags.push(config.auth ? "--auth" : "--no-auth");
if (config.addons && config.addons.length > 0) {
flags.push(`--addons ${config.addons.join(" ")}`);
} else {
@@ -49,9 +28,10 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
flags.push("--examples none");
}
if (config.packageManager) {
flags.push(`--package-manager ${config.packageManager}`);
}
flags.push(`--db-setup ${config.dbSetup}`);
flags.push(config.git ? "--git" : "--no-git");
flags.push(`--package-manager ${config.packageManager}`);
flags.push(config.install ? "--install" : "--no-install");
let baseCommand = "";
const pkgManager = config.packageManager;

View File

@@ -1,5 +1,4 @@
{{#if (includes frontend 'next')}}
{{!-- Next.js tRPC Client Setup --}}
import { QueryCache, QueryClient } from '@tanstack/react-query';
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query';
@@ -47,16 +46,14 @@ export const trpc = createTRPCOptionsProxy<AppRouter>({
});
{{else if (includes frontend 'tanstack-start')}}
{{!-- TanStack Start tRPC Client Setup --}}
import { createTRPCContext } from "@trpc/tanstack-react-query";
import type { AppRouter } from "../../../server/src/routers"; {{! Adjust path if necessary }}
import type { AppRouter } from "../../../server/src/routers";
export const { TRPCProvider, useTRPC, useTRPCClient } =
createTRPCContext<AppRouter>();
{{else}}
{{!-- Default Web tRPC Client Setup (TanStack Router, React Router, etc.) --}}
import type { AppRouter } from "../../../server/src/routers"; {{! Adjust path if necessary }}
import type { AppRouter } from "../../../server/src/routers";
import { QueryCache, QueryClient } from "@tanstack/react-query";
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";

View File

@@ -12,20 +12,48 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Loader2, Trash2 } from "lucide-react";
import { useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
{{#if (eq api "orpc")}}
{{#if (eq backend "convex")}}
import { useMutation, useQuery } from "convex/react";
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
import type { Id } from "@{{projectName}}/backend/convex/_generated/dataModel.d.ts";
{{else}}
import { useMutation, useQuery } from "@tanstack/react-query";
{{#if (eq api "orpc")}}
import { orpc } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
{{/if}}
{{#if (eq api "trpc")}}
import { trpc } from "@/utils/trpc";
{{/if}}
{{/if}}
export default function TodosPage() {
const [newTodoText, setNewTodoText] = useState("");
{{#if (eq api "orpc")}}
{{#if (eq backend "convex")}}
const todos = useQuery(api.todos.getAll);
const createTodoMutation = useMutation(api.todos.create);
const toggleTodoMutation = useMutation(api.todos.toggle);
const deleteTodoMutation = useMutation(api.todos.deleteTodo);
const handleAddTodo = async (e: React.FormEvent) => {
e.preventDefault();
const text = newTodoText.trim();
if (!text) return;
await createTodoMutation({ text });
setNewTodoText("");
};
const handleToggleTodo = (id: Id<"todos">, currentCompleted: boolean) => {
toggleTodoMutation({ id, completed: !currentCompleted });
};
const handleDeleteTodo = (id: Id<"todos">) => {
deleteTodoMutation({ id });
};
{{else}}
{{#if (eq api "orpc")}}
const todos = useQuery(orpc.todo.getAll.queryOptions());
const createMutation = useMutation(
orpc.todo.create.mutationOptions({
@@ -45,8 +73,8 @@ export default function TodosPage() {
onSuccess: () => todos.refetch(),
}),
);
{{/if}}
{{#if (eq api "trpc")}}
{{/if}}
{{#if (eq api "trpc")}}
const todos = useQuery(trpc.todo.getAll.queryOptions());
const createMutation = useMutation(
trpc.todo.create.mutationOptions({
@@ -66,7 +94,7 @@ export default function TodosPage() {
onSuccess: () => todos.refetch(),
}),
);
{{/if}}
{{/if}}
const handleAddTodo = (e: React.FormEvent) => {
e.preventDefault();
@@ -82,6 +110,7 @@ export default function TodosPage() {
const handleDeleteTodo = (id: number) => {
deleteMutation.mutate({ id });
};
{{/if}}
return (
<div className="mx-auto w-full max-w-md py-10">
@@ -99,20 +128,73 @@ export default function TodosPage() {
value={newTodoText}
onChange={(e) => setNewTodoText(e.target.value)}
placeholder="Add a new task..."
{{#if (eq backend "convex")}}
{{else}}
disabled={createMutation.isPending}
{{/if}}
/>
<Button
type="submit"
{{#if (eq backend "convex")}}
disabled={!newTodoText.trim()}
{{else}}
disabled={createMutation.isPending || !newTodoText.trim()}
{{/if}}
>
{{#if (eq backend "convex")}}
Add
{{else}}
{createMutation.isPending ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
"Add"
)}
{{/if}}
</Button>
</form>
{{#if (eq backend "convex")}}
{todos === undefined ? (
<div className="flex justify-center py-4">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
) : todos.length === 0 ? (
<p className="py-4 text-center">No todos yet. Add one above!</p>
) : (
<ul className="space-y-2">
{todos.map((todo) => (
<li
key={todo._id}
className="flex items-center justify-between rounded-md border p-2"
>
<div className="flex items-center space-x-2">
<Checkbox
checked={todo.completed}
onCheckedChange={() =>
handleToggleTodo(todo._id, todo.completed)
}
id={`todo-${todo._id}`}
/>
<label
htmlFor={`todo-${todo._id}`}
className={`${todo.completed ? "line-through text-muted-foreground" : ""}`}
>
{todo.text}
</label>
</div>
<Button
variant="ghost"
size="icon"
onClick={() => handleDeleteTodo(todo._id)}
aria-label="Delete todo"
>
<Trash2 className="h-4 w-4" />
</Button>
</li>
))}
</ul>
)}
{{else}}
{todos.isLoading ? (
<div className="flex justify-center py-4">
<Loader2 className="h-6 w-6 animate-spin" />
@@ -155,6 +237,7 @@ export default function TodosPage() {
))}
</ul>
)}
{{/if}}
</CardContent>
</Card>
</div>

View File

@@ -126,7 +126,6 @@ export default function Todos() {
onChange={(e) => setNewTodoText(e.target.value)}
placeholder="Add a new task..."
{{#if (eq backend "convex")}}
{{!-- Convex mutations don't have an easy isPending state here, disable based on text --}}
{{else}}
disabled={createMutation.isPending}
{{/if}}

View File

@@ -33,40 +33,48 @@ export default function Home() {
<View className="rounded-lg border border-foreground p-4">
<Text className="mb-2 font-medium text-foreground">API Status</Text>
{{#if (eq backend "convex")}}
<View className="flex-row items-center gap-2">
<View
className={`h-2.5 w-2.5 rounded-full ${
{{#if (or (eq api "orpc") (eq api "trpc"))}}
healthCheck.data ? "bg-green-500" : "bg-red-500"
{{else}}
healthCheck ? "bg-green-500" : "bg-red-500"
{{/if}}
}`}
/>
<Text className="text-sm text-foreground">
{{#if (eq api "orpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
{{#if (eq api "trpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
{{#if (eq backend "convex")}}
{healthCheck === undefined
? "Checking..."
: healthCheck === "OK"
? "Connected"
: "Error"}
{{/if}}
</Text>
</View>
{{else}}
{{#unless (eq api "none")}}
<View className="flex-row items-center gap-2">
<View
className={`h-2.5 w-2.5 rounded-full ${
healthCheck.data ? "bg-green-500" : "bg-red-500"
}`}
/>
<Text className="text-sm text-foreground">
{{#if (eq api "orpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
{{#if (eq api "trpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
</Text>
</View>
{{/unless}}
{{/if}}
</View>
</ScrollView>
</Container>

View File

@@ -1,7 +1,9 @@
{{#if (eq backend "convex")}}
import { ConvexProvider, ConvexReactClient } from "convex/react";
{{else}}
{{#unless (eq api "none")}}
import { QueryClientProvider } from "@tanstack/react-query";
{{/unless}}
{{/if}}
import { Stack } from "expo-router";
import {
@@ -82,6 +84,7 @@ export default function RootLayout() {
</ThemeProvider>
</ConvexProvider>
{{else}}
{{#unless (eq api "none")}}
<QueryClientProvider client={queryClient}>
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
@@ -96,6 +99,20 @@ export default function RootLayout() {
</GestureHandlerRootView>
</ThemeProvider>
</QueryClientProvider>
{{else}}
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
<GestureHandlerRootView style=\{{ flex: 1 }}>
<Stack>
<Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />
<Stack.Screen
name="modal"
options=\{{ title: "Modal", presentation: "modal" }}
/>
</Stack>
</GestureHandlerRootView>
</ThemeProvider>
{{/unless}}
{{/if}}
);
}

View File

@@ -33,38 +33,54 @@ export default function Home() {
<View style={styles.apiStatusCard}>
<Text style={styles.cardTitle}>API Status</Text>
{{#if (eq backend "convex")}}
<View style={styles.apiStatusRow}>
<View
style={[
styles.statusIndicatorDot,
{{#if (or (eq api "orpc") (eq api "trpc"))}}
healthCheck.data
? styles.statusIndicatorGreen
: styles.statusIndicatorRed,
{{else}}
healthCheck === "OK"
? styles.statusIndicatorGreen
: styles.statusIndicatorRed,
{{/if}}
]}
/>
<Text style={styles.statusText}>
{{#if (or (eq api "orpc") (eq api "trpc"))}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
{{#if (eq backend "convex")}}
{healthCheck === undefined
? "Checking..."
: healthCheck === "OK"
? "Connected"
: "Error"}
{{/if}}
</Text>
</View>
{{else}}
{{#unless (eq api "none")}}
<View style={styles.apiStatusRow}>
<View
style={[
styles.statusIndicatorDot,
healthCheck.data
? styles.statusIndicatorGreen
: styles.statusIndicatorRed,
]}
/>
<Text style={styles.statusText}>
{{#if (eq api "orpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
{{#if (eq api "trpc")}}
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
{{/if}}
</Text>
</View>
{{/unless}}
{{/if}}
</View>
</ScrollView>
</Container>

View File

@@ -7,7 +7,9 @@ import { queryClient } from "@/utils/orpc";
{{#if (eq backend "convex")}}
import { ConvexProvider, ConvexReactClient } from "convex/react";
{{else}}
{{#unless (eq api "none")}}
import { QueryClientProvider } from "@tanstack/react-query";
{{/unless}}
{{/if}}
import { Stack } from "expo-router";
import { GestureHandlerRootView } from "react-native-gesture-handler";
@@ -51,6 +53,7 @@ export default function RootLayout() {
</GestureHandlerRootView>
</ConvexProvider>
{{else}}
{{#unless (eq api "none")}}
<QueryClientProvider client={queryClient}>
<GestureHandlerRootView style=\{{ flex: 1 }}>
<Stack
@@ -72,6 +75,27 @@ export default function RootLayout() {
</Stack>
</GestureHandlerRootView>
</QueryClientProvider>
{{else}}
<GestureHandlerRootView style=\{{ flex: 1 }}>
<Stack
screenOptions=\{{
headerStyle: {
backgroundColor: theme.colors.background,
},
headerTitleStyle: {
color: theme.colors.typography,
},
headerTintColor: theme.colors.typography,
}}
>
<Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />
<Stack.Screen
name="modal"
options=\{{ title: "Modal", presentation: "modal" }}
/>
</Stack>
</GestureHandlerRootView>
{{/unless}}
{{/if}}
);
}

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
{{#unless (eq api "none")}}
const { $orpc } = useNuxtApp()
import { useQuery } from '@tanstack/vue-query'
{{/unless}}
const TITLE_TEXT = `
██████╗ ███████╗████████╗████████╗███████╗██████╗
@@ -18,14 +20,16 @@ const TITLE_TEXT = `
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
`;
{{#unless (eq api "none")}}
const healthCheck = useQuery($orpc.healthCheck.queryOptions())
{{/unless}}
</script>
<template>
<div class="container mx-auto max-w-3xl px-4 py-2">
<pre class="overflow-x-auto font-mono text-sm whitespace-pre-wrap">{{ TITLE_TEXT }}</pre>
<div class="grid gap-6 mt-4">
{{#unless (eq api "none")}}
<section class="rounded-lg border p-4">
<h2 class="mb-2 font-medium">API Status</h2>
<div class="flex items-center gap-2">
@@ -58,6 +62,7 @@ const healthCheck = useQuery($orpc.healthCheck.queryOptions())
</div>
</div>
</section>
{{/unless}}
</div>
</div>
</template>

View File

@@ -2,14 +2,14 @@
{{#if (eq backend "convex")}}
import { useQuery } from "convex/react";
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
{{else}}
{{else if (or (eq api "orpc") (eq api "trpc"))}}
import { useQuery } from "@tanstack/react-query";
{{#if (eq api "orpc")}}
import { orpc } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { trpc } from "@/utils/trpc";
{{/if}}
import { useQuery } from "@tanstack/react-query";
{{/if}}
const TITLE_TEXT = `
@@ -43,8 +43,8 @@ export default function Home() {
<div className="grid gap-6">
<section className="rounded-lg border p-4">
<h2 className="mb-2 font-medium">API Status</h2>
{{#if (eq backend "convex")}}
<div className="flex items-center gap-2">
{{#if (eq backend "convex")}}
<div
className={`h-2 w-2 rounded-full ${healthCheck === "OK" ? "bg-green-500" : healthCheck === undefined ? "bg-orange-400" : "bg-red-500"}`}
/>
@@ -55,19 +55,23 @@ export default function Home() {
? "Connected"
: "Error"}
</span>
{{else}}
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-sm text-muted-foreground">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
{{/if}}
</div>
{{else}}
{{#unless (eq api "none")}}
<div className="flex items-center gap-2">
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-sm text-muted-foreground">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
</div>
{{/unless}}
{{/if}}
</section>
</div>
</div>

View File

@@ -1,49 +1,50 @@
"use client"
{{#if (eq backend "convex")}}
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ConvexProvider, ConvexReactClient } from "convex/react";
{{else}}
import { QueryClientProvider } from "@tanstack/react-query";
{{#if (eq api "orpc")}}
import { orpc, ORPCContext, queryClient } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { queryClient } from "@/utils/trpc";
{{/if}}
{{#unless (eq api "none")}}
import { QueryClientProvider } from "@tanstack/react-query";
{{#if (eq api "orpc")}}
import { orpc, ORPCContext, queryClient } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { queryClient } from "@/utils/trpc";
{{/if}}
{{/unless}}
{{/if}}
import { ThemeProvider } from "./theme-provider";
import { Toaster } from "./ui/sonner";
{{#if (eq backend "convex")}}
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
{{/if}}
export default function Providers({
children,
}: {
children: React.ReactNode
}) {
return (
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{{#if (eq backend "convex")}}
<ConvexProvider client={convex}>{children}</ConvexProvider>
{{else}}
export default function Providers({ children, }: { children: React.ReactNode })
{ return (
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{{#if (eq backend "convex")}}
<ConvexProvider client={convex}>{children}</ConvexProvider>
{{else}}
{{#unless (eq api "none")}}
<QueryClientProvider client={queryClient}>
{{#if (eq api "orpc")}}
<ORPCContext.Provider value={orpc}>
{children}
</ORPCContext.Provider>
<ORPCContext.Provider value={orpc}>
{children}
</ORPCContext.Provider>
{{/if}}
{{#if (eq api "trpc")}}
{children}
{children}
{{/if}}
</QueryClientProvider>
{{/if}}
<Toaster richColors />
</ThemeProvider>
)
}
{{else}}
{children}
{{/unless}}
{{/if}}
<Toaster richColors />
</ThemeProvider>
) }

View File

@@ -13,20 +13,25 @@ import { ThemeProvider } from "./components/theme-provider";
import { Toaster } from "./components/ui/sonner";
{{#if (eq backend "convex")}}
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ConvexProvider, ConvexReactClient } from "convex/react";
{{else}}
import { QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
{{#if (eq api "orpc")}}
import { orpc, ORPCContext, queryClient } from "./utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { queryClient } from "./utils/trpc";
{{/if}}
{{#unless (eq api "none")}}
import { QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
{{#if (eq api "orpc")}}
import { orpc, ORPCContext, queryClient } from "./utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { queryClient } from "./utils/trpc";
{{/if}}
{{/unless}}
{{/if}}
export const links: Route.LinksFunction = () => [
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
{
rel: "preconnect",
href: "https://fonts.googleapis.com",
},
{
rel: "preconnect",
href: "https://fonts.gstatic.com",
@@ -57,28 +62,12 @@ export function Layout({ children }: { children: React.ReactNode }) {
}
{{#if (eq backend "convex")}}
export default function App() {
const convex = new ConvexReactClient(
import.meta.env.VITE_CONVEX_URL as string,
);
return (
<ConvexProvider client={convex}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
<Outlet />
</div>
<Toaster richColors />
</ThemeProvider>
</ConvexProvider>
);
}
{{else if (eq api "orpc")}}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<ORPCContext.Provider value={orpc}>
export default function App() {
const convex = new ConvexReactClient(
import.meta.env.VITE_CONVEX_URL as string,
);
return (
<ConvexProvider client={convex}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
@@ -86,15 +75,44 @@ export default function App() {
</div>
<Toaster richColors />
</ThemeProvider>
</ORPCContext.Provider>
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
</QueryClientProvider>
);
}
</ConvexProvider>
);
}
{{else if (eq api "orpc")}}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<ORPCContext.Provider value={orpc}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
<Outlet />
</div>
<Toaster richColors />
</ThemeProvider>
</ORPCContext.Provider>
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
</QueryClientProvider>
);
}
{{else if (eq api "trpc")}}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
<Outlet />
</div>
<Toaster richColors />
</ThemeProvider>
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
</QueryClientProvider>
);
}
{{else}}
export default function App() {
return (
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
@@ -102,10 +120,8 @@ export default function App() {
</div>
<Toaster richColors />
</ThemeProvider>
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
</QueryClientProvider>
);
}
);
}
{{/if}}
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {

View File

@@ -2,14 +2,14 @@ import type { Route } from "./+types/_index";
{{#if (eq backend "convex")}}
import { useQuery } from "convex/react";
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
{{else}}
{{else if (or (eq api "orpc") (eq api "trpc"))}}
import { useQuery } from "@tanstack/react-query";
{{#if (eq api "orpc")}}
import { orpc } from "@/utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { trpc } from "@/utils/trpc";
{{/if}}
import { useQuery } from "@tanstack/react-query";
{{/if}}
const TITLE_TEXT = `
@@ -47,8 +47,8 @@ export default function Home() {
<div className="grid gap-6">
<section className="rounded-lg border p-4">
<h2 className="mb-2 font-medium">API Status</h2>
{{#if (eq backend "convex")}}
<div className="flex items-center gap-2">
{{#if (eq backend "convex")}}
<div
className={`h-2 w-2 rounded-full ${healthCheck === "OK" ? "bg-green-500" : healthCheck === undefined ? "bg-orange-400" : "bg-red-500"}`}
/>
@@ -59,21 +59,25 @@ export default function Home() {
? "Connected"
: "Error"}
</span>
{{else}}
<div
className={`h-2 w-2 rounded-full ${
healthCheck.data ? "bg-green-500" : "bg-red-500"
}`}
/>
<span className="text-sm text-muted-foreground">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
{{/if}}
</div>
{{else}}
{{#unless (eq api "none")}}
<div className="flex items-center gap-2">
<div
className={`h-2 w-2 rounded-full ${
healthCheck.data ? "bg-green-500" : "bg-red-500"
}`}
/>
<span className="text-sm text-muted-foreground">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
</div>
{{/unless}}
{{/if}}
</section>
</div>
</div>

View File

@@ -2,57 +2,51 @@ import { RouterProvider, createRouter } from "@tanstack/react-router";
import ReactDOM from "react-dom/client";
import Loader from "./components/loader";
import { routeTree } from "./routeTree.gen";
{{#if (eq api "orpc")}}
import { QueryClientProvider } from "@tanstack/react-query";
import { orpc, queryClient } from "./utils/orpc";
import { QueryClientProvider } from "@tanstack/react-query";
import { orpc, queryClient } from "./utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient, trpc } from "./utils/trpc";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient, trpc } from "./utils/trpc";
{{/if}}
{{#if (eq backend "convex")}}
import { ConvexProvider, ConvexReactClient } from "convex/react";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
import { ConvexProvider, ConvexReactClient } from "convex/react";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
{{/if}}
{{#if (eq api "orpc")}}
const router = createRouter({
routeTree,
defaultPreload: "intent",
defaultPendingComponent: () => <Loader />,
{{#if (eq api "orpc")}}
context: { orpc, queryClient },
Wrap: function WrapComponent({ children }) {
Wrap: function WrapComponent({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
},
});
{{/if}}
{{#if (eq api "trpc")}}
const router = createRouter({
routeTree,
defaultPreload: "intent",
defaultPendingComponent: () => <Loader />,
{{else if (eq api "trpc")}}
context: { trpc, queryClient },
Wrap: function WrapComponent({ children }) {
Wrap: function WrapComponent({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
},
});
{{/if}}
{{#if (eq backend "convex")}}
const router = createRouter({
routeTree,
defaultPreload: "intent",
defaultPendingComponent: () => <Loader />,
{{else if (eq backend "convex")}}
context: {},
Wrap: function WrapComponent({ children }) {
Wrap: function WrapComponent({ children }: { children: React.ReactNode }) {
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
},
{{else}}
context: {},
{{/if}}
});
{{/if}}
declare module "@tanstack/react-router" {
interface Register {
@@ -61,7 +55,10 @@ declare module "@tanstack/react-router" {
}
const rootElement = document.getElementById("app");
if (!rootElement) throw new Error("Root element not found");
if (!rootElement) {
throw new Error("Root element not found");
}
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);

View File

@@ -31,14 +31,12 @@ export interface RouterAppContext {
orpc: typeof orpc;
queryClient: QueryClient;
}
{{/if}}
{{#if (eq api "trpc")}}
{{else if (eq api "trpc")}}
export interface RouterAppContext {
trpc: typeof trpc;
queryClient: QueryClient;
}
{{/if}}
{{#if (eq backend "convex")}}
{{else}}
export interface RouterAppContext {}
{{/if}}
@@ -63,18 +61,21 @@ export const Route = createRootRouteWithContext<RouterAppContext>()({
}),
});
{{#if (eq api "orpc")}}
function RootComponent() {
const [client] = useState<RouterClient<typeof appRouter>>(() => createORPCClient(link))
const [orpc] = useState(() => createORPCReactQueryUtils(client))
const isFetching = useRouterState({
select: (s) => s.isLoading,
});
{{#if (eq api "orpc")}}
const [client] = useState<RouterClient<typeof appRouter>>(() => createORPCClient(link));
const [orpcUtils] = useState(() => createORPCReactQueryUtils(client));
{{/if}}
return (
<>
<HeadContent />
<ORPCContext.Provider value={orpc}>
{{#if (eq api "orpc")}}
<ORPCContext.Provider value={orpcUtils}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
@@ -83,20 +84,7 @@ function RootComponent() {
<Toaster richColors />
</ThemeProvider>
</ORPCContext.Provider>
<TanStackRouterDevtools position="bottom-left" />
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
</>
);
}
{{/if}}
{{#if (eq api "trpc")}}
function RootComponent() {
const isFetching = useRouterState({
select: (s) => s.isLoading,
});
return (
<>
<HeadContent />
{{else}}
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
@@ -104,29 +92,11 @@ function RootComponent() {
</div>
<Toaster richColors />
</ThemeProvider>
{{/if}}
<TanStackRouterDevtools position="bottom-left" />
{{#if (or (eq api "orpc") (eq api "trpc"))}}
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
{{/if}}
</>
);
}
{{/if}}
{{#if (eq backend "convex")}}
function RootComponent() {
const isFetching = useRouterState({
select: (s) => s.isLoading,
});
return (
<>
<HeadContent />
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<div className="grid grid-rows-[auto_1fr] h-svh">
<Header />
{isFetching ? <Loader /> : <Outlet />}
</div>
<Toaster richColors />
</ThemeProvider>
<TanStackRouterDevtools position="bottom-left" />
</>
);
}
{{/if}}

View File

@@ -49,20 +49,8 @@ function HomeComponent() {
<div className="grid gap-6">
<section className="rounded-lg border p-4">
<h2 className="mb-2 font-medium">API Status</h2>
{{#if (eq backend "convex")}}
<div className="flex items-center gap-2">
{{#if (or (eq api "orpc") (eq api "trpc"))}}
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-sm text-muted-foreground">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
{{/if}}
{{#if (eq backend "convex")}}
<div
className={`h-2 w-2 rounded-full ${healthCheck === "OK" ? "bg-green-500" : healthCheck === undefined ? "bg-orange-400" : "bg-red-500"}`}
/>
@@ -73,8 +61,23 @@ function HomeComponent() {
? "Connected"
: "Error"}
</span>
{{/if}}
</div>
{{else}}
{{#unless (eq api "none")}}
<div className="flex items-center gap-2">
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-sm text-muted-foreground">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
</div>
{{/unless}}
{{/if}}
</section>
</div>
</div>

View File

@@ -8,24 +8,20 @@ import { routeTree } from "./routeTree.gen";
import Loader from "./components/loader";
import "./index.css";
{{else}}
import {
QueryCache,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { createRouter as createTanstackRouter } from "@tanstack/react-router";
import Loader from "./components/loader";
import "./index.css";
import { routeTree } from "./routeTree.gen";
{{#if (eq api "trpc")}}
import { QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
import { toast } from "sonner";
import type { AppRouter } from "../../server/src/routers";
import { TRPCProvider } from "./utils/trpc";
{{/if}}
{{#if (eq api "orpc")}}
import { orpc, ORPCContext, queryClient } from "./utils/orpc";
{{else if (eq api "orpc")}}
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { orpc, ORPCContext, queryClient as orpcQueryClient } from "./utils/orpc";
{{/if}}
{{/if}}
@@ -62,7 +58,6 @@ export function createRouter() {
}),
queryClient,
);
return router;
}
{{else}}
@@ -103,6 +98,8 @@ const trpc = createTRPCOptionsProxy({
client: trpcClient,
queryClient: queryClient,
});
{{else if (eq api "orpc")}}
const queryClient = orpcQueryClient;
{{/if}}
export const createRouter = () => {
@@ -112,33 +109,37 @@ export const createRouter = () => {
defaultPreloadStaleTime: 0,
{{#if (eq api "trpc")}}
context: { trpc, queryClient },
{{/if}}
{{#if (eq api "orpc")}}
{{else if (eq api "orpc")}}
context: { orpc, queryClient },
{{else}}
context: { },
{{/if}}
defaultPendingComponent: () => <Loader />,
defaultNotFoundComponent: () => <div>Not Found</div>,
{{#if (eq api "trpc")}}
Wrap: ({ children }) => (
<QueryClientProvider client={queryClient}>
{{#if (eq api "trpc")}}
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{children}
</TRPCProvider>
{{/if}}
{{#if (eq api "orpc")}}
</QueryClientProvider>
),
{{else if (eq api "orpc")}}
Wrap: ({ children }) => (
<QueryClientProvider client={queryClient}>
<ORPCContext.Provider value={orpc}>
{children}
</ORPCContext.Provider>
{{/if}}
</QueryClientProvider>
),
{{else}}
Wrap: ({ children }) => <>{children}</>,
{{/if}}
});
return router;
};
{{/if}}
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: ReturnType<typeof createRouter>;

View File

@@ -1,5 +1,7 @@
import { Toaster } from "@/components/ui/sonner";
{{#unless (eq backend "convex")}} {{#unless (eq api "none")}}
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
{{/unless}} {{/unless}}
import {
HeadContent,
Outlet,
@@ -10,7 +12,9 @@ import {
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import Header from "../components/header";
import appCss from "../index.css?url";
{{#unless (and (eq api "none") (not (eq backend "convex"))}}
import type { QueryClient } from "@tanstack/react-query";
{{/unless}}
import Loader from "@/components/loader";
{{#if (eq backend "convex")}}
@@ -25,12 +29,14 @@ export interface RouterAppContext {
trpc: TRPCOptionsProxy<AppRouter>;
queryClient: QueryClient;
}
{{/if}}
{{#if (eq api "orpc")}}
{{else if (eq api "orpc")}}
import type { orpc } from "@/utils/orpc";
export interface RouterAppContext {
orpc: typeof orpc;
queryClient: QueryClient;
}
{{else}}
export interface RouterAppContext {
}
{{/if}}
{{/if}}
@@ -75,7 +81,11 @@ function RootDocument() {
</div>
<Toaster richColors />
<TanStackRouterDevtools position="bottom-left" />
{{#unless (eq backend "convex")}}
{{#unless (eq api "none")}}
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
{{/unless}}
{{/unless}}
<Scripts />
</body>
</html>

View File

@@ -3,14 +3,14 @@ import { createFileRoute } from "@tanstack/react-router";
import { convexQuery } from "@convex-dev/react-query";
import { useSuspenseQuery } from "@tanstack/react-query";
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
{{else}}
{{else if (or (eq api "trpc") (eq api "orpc"))}}
import { useQuery } from "@tanstack/react-query";
{{#if (eq api "trpc")}}
import { useTRPC } from "@/utils/trpc";
{{/if}}
{{#if (eq api "orpc")}}
import { useORPC } from "@/utils/orpc";
{{/if}}
import { useQuery } from "@tanstack/react-query";
{{/if}}
export const Route = createFileRoute("/")({
@@ -36,15 +36,12 @@ const TITLE_TEXT = `
function HomeComponent() {
{{#if (eq backend "convex")}}
const healthCheck = useSuspenseQuery(convexQuery(api.healthCheck.get, {}));
{{else}}
{{#if (eq api "trpc")}}
{{else if (eq api "trpc")}}
const trpc = useTRPC();
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
{{/if}}
{{#if (eq api "orpc")}}
{{else if (eq api "orpc")}}
const orpc = useORPC();
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
{{/if}}
{{/if}}
return (
@@ -53,8 +50,8 @@ function HomeComponent() {
<div className="grid gap-6">
<section className="rounded-lg border p-4">
<h2 className="mb-2 font-medium">API Status</h2>
{{#if (eq backend "convex")}}
<div className="flex items-center gap-2">
{{#if (eq backend "convex")}}
<div
className={`h-2 w-2 rounded-full ${healthCheck.data === "OK" ? "bg-green-500" : healthCheck.isLoading ? "bg-orange-400" : "bg-red-500"}`}
/>
@@ -65,19 +62,23 @@ function HomeComponent() {
? "Connected"
: "Error"}
</span>
{{else}}
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-muted-foreground text-sm">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
{{/if}}
</div>
{{else}}
{{#unless (eq api "none")}}
<div className="flex items-center gap-2">
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-muted-foreground text-sm">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
</div>
{{/unless}}
{{/if}}
</section>
</div>
</div>

View File

@@ -2,8 +2,10 @@ import { RouterProvider, createRouter } from "@tanstack/solid-router";
import { render } from "solid-js/web";
import { routeTree } from "./routeTree.gen";
import "./styles.css";
{{#if (eq api "orpc")}}
import { QueryClientProvider } from "@tanstack/solid-query";
import { queryClient } from "./utils/orpc";
{{/if}}
const router = createRouter({
routeTree,
@@ -20,9 +22,13 @@ declare module "@tanstack/solid-router" {
function App() {
return (
{{#if (eq api "orpc")}}
<QueryClientProvider client={queryClient}>
{{/if}}
<RouterProvider router={router} />
{{#if (eq api "orpc")}}
</QueryClientProvider>
{{/if}}
);
}

View File

@@ -1,9 +1,20 @@
import Header from "@/components/header";
import { Outlet, createRootRouteWithContext } from "@tanstack/solid-router";
import { TanStackRouterDevtools } from "@tanstack/solid-router-devtools";
{{#if (eq api "orpc")}}
import { SolidQueryDevtools } from "@tanstack/solid-query-devtools";
import type { QueryClient } from "@tanstack/solid-query";
import type { orpc } from "../utils/orpc";
export const Route = createRootRouteWithContext()({
export interface RouterContext {
orpc: typeof orpc;
queryClient: QueryClient;
}
{{else}}
export interface RouterContext {}
{{/if}}
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootComponent,
});
@@ -14,7 +25,9 @@ function RootComponent() {
<Header />
<Outlet />
</div>
{{#if (eq api "orpc")}}
<SolidQueryDevtools />
{{/if}}
<TanStackRouterDevtools />
</>
);

View File

@@ -1,7 +1,10 @@
import { createFileRoute } from "@tanstack/solid-router";
{{#if (eq api "orpc")}}
import { useQuery } from "@tanstack/solid-query";
import { orpc } from "../utils/orpc";
import { Match, Switch } from "solid-js";
{{else}}
{{/if}}
export const Route = createFileRoute("/")({
component: App,
@@ -24,12 +27,15 @@ const TITLE_TEXT = `
`;
function App() {
{{#if (eq api "orpc")}}
const healthCheck = useQuery(() => orpc.healthCheck.queryOptions());
{{/if}}
return (
<div class="container mx-auto max-w-3xl px-4 py-2">
<pre class="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
<div class="grid gap-6">
{{#if (eq api "orpc")}}
<section class="rounded-lg border p-4">
<h2 class="mb-2 font-medium">API Status</h2>
<Switch>
@@ -53,12 +59,13 @@ function App() {
<span class="text-sm text-muted-foreground">
{healthCheck.data
? "Connected"
: "Disconnected (Success but no data)"}
: "Disconnected"}
</span>
</div>
</Match>
</Switch>
</section>
{{/if}}
</div>
</div>
);

View File

@@ -16,16 +16,12 @@
</main>
</div>
{{else}}
{{#if (eq api "orpc")}}
<script lang="ts">
import { QueryClientProvider } from '@tanstack/svelte-query';
import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools'
import '../app.css';
{{#if (eq api "orpc")}}
import { queryClient } from '$lib/orpc';
{{/if}}
{{#if (eq api "trpc")}}
import { queryClient } from '$lib/trpc';
{{/if}}
import Header from '../components/Header.svelte';
let { children } = $props();
@@ -40,4 +36,19 @@
</div>
<SvelteQueryDevtools />
</QueryClientProvider>
{{else}}
<script lang="ts">
import '../app.css';
import Header from '../components/Header.svelte';
let { children } = $props();
</script>
<div class="grid h-svh grid-rows-[auto_1fr]">
<Header />
<main class="overflow-y-auto">
{@render children()}
</main>
</div>
{{/if}}
{{/if}}

View File

@@ -3,10 +3,8 @@
import { useQuery } from 'convex-svelte';
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
const healthCheck = useQuery(api.healthCheck.get, {});
const TITLE_TEXT = `
██████╗ ███████╗████████╗████████╗███████╗██████╗
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
@@ -28,7 +26,7 @@ const TITLE_TEXT = `
<pre class="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
<div class="grid gap-6">
<section class="rounded-lg border p-4">
<h2 class="mb-2 font-medium">API Status (Convex)</h2>
<h2 class="mb-2 font-medium">API Status</h2>
<div class="flex items-center gap-2">
<div
class={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
@@ -48,19 +46,9 @@ const TITLE_TEXT = `
<script lang="ts">
{{#if (eq api "orpc")}}
import { orpc } from "$lib/orpc";
{{/if}}
{{#if (eq api "trpc")}}
import { trpc } from "$lib/trpc";
{{/if}}
import { createQuery } from "@tanstack/svelte-query";
{{#if (eq api "orpc")}}
const healthCheck = createQuery(orpc.healthCheck.queryOptions());
{{/if}}
{{#if (eq api "trpc")}}
const healthCheck = createQuery(trpc.healthCheck.queryOptions());
{{/if}}
const TITLE_TEXT = `
██████╗ ███████╗████████╗████████╗███████╗██████╗
@@ -82,8 +70,9 @@ const TITLE_TEXT = `
<div class="container mx-auto max-w-3xl px-4 py-2">
<pre class="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
<div class="grid gap-6">
{{#if (eq api "orpc")}}
<section class="rounded-lg border p-4">
<h2 class="mb-2 font-medium">API Status{{#if (eq api "trpc")}} (tRPC){{/if}}{{#if (eq api "orpc")}} (oRPC){{/if}}</h2>
<h2 class="mb-2 font-medium">API Status</h2>
<div class="flex items-center gap-2">
<div
class={`h-2 w-2 rounded-full ${$healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
@@ -97,6 +86,7 @@ const TITLE_TEXT = `
</span>
</div>
</section>
{{/if}}
</div>
</div>
{{/if}}

View File

@@ -14,7 +14,7 @@
},
"apps/cli": {
"name": "create-better-t-stack",
"version": "2.9.0",
"version": "2.9.2",
"bin": {
"create-better-t-stack": "dist/index.js",
},
@@ -43,9 +43,9 @@
"version": "0.0.0",
"dependencies": {
"@radix-ui/react-dialog": "^1.1.13",
"@radix-ui/react-scroll-area": "^1.2.6",
"@radix-ui/react-switch": "^1.2.2",
"@radix-ui/react-tooltip": "^1.2.4",
"@radix-ui/react-scroll-area": "^1.2.8",
"@radix-ui/react-switch": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.6",
"babel-plugin-react-compiler": "^19.1.0-rc.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -53,7 +53,7 @@
"fumadocs-mdx": "11.6.1",
"fumadocs-ui": "15.2.10",
"lucide-react": "^0.503.0",
"motion": "^12.8.0",
"motion": "^12.10.5",
"next": "15.3.1",
"next-themes": "^0.4.6",
"nuqs": "^2.4.3",
@@ -64,16 +64,16 @@
"tailwind-merge": "^3.2.0",
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.4",
"@tailwindcss/postcss": "^4.1.5",
"@types/mdx": "^2.0.13",
"@types/node": "22.14.1",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"eslint": "^9.25.1",
"@types/react": "^19.1.3",
"@types/react-dom": "^19.1.3",
"eslint": "^9.26.0",
"eslint-config-next": "15.3.1",
"postcss": "^8.5.3",
"tailwindcss": "^4.1.4",
"tw-animate-css": "^1.2.8",
"tailwindcss": "^4.1.5",
"tw-animate-css": "^1.2.9",
"typescript": "^5.8.3",
},
},