diff --git a/apps/web/public/icon/astro.svg b/apps/web/public/icon/astro.svg new file mode 100644 index 0000000..332bbaa --- /dev/null +++ b/apps/web/public/icon/astro.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/better-auth.svg b/apps/web/public/icon/better-auth.svg new file mode 100644 index 0000000..6d00383 --- /dev/null +++ b/apps/web/public/icon/better-auth.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/biome.svg b/apps/web/public/icon/biome.svg new file mode 100644 index 0000000..e2da0ad --- /dev/null +++ b/apps/web/public/icon/biome.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/icon/bun.svg b/apps/web/public/icon/bun.svg new file mode 100644 index 0000000..02a55d5 --- /dev/null +++ b/apps/web/public/icon/bun.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/drizzle.svg b/apps/web/public/icon/drizzle.svg new file mode 100644 index 0000000..8596582 --- /dev/null +++ b/apps/web/public/icon/drizzle.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/elysia.svg b/apps/web/public/icon/elysia.svg new file mode 100644 index 0000000..0497dcc --- /dev/null +++ b/apps/web/public/icon/elysia.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/expo.svg b/apps/web/public/icon/expo.svg new file mode 100644 index 0000000..07df455 --- /dev/null +++ b/apps/web/public/icon/expo.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/icon/express.svg b/apps/web/public/icon/express.svg new file mode 100644 index 0000000..0ee1e44 --- /dev/null +++ b/apps/web/public/icon/express.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/git.svg b/apps/web/public/icon/git.svg new file mode 100644 index 0000000..4e5fe8e --- /dev/null +++ b/apps/web/public/icon/git.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/icon/hono.svg b/apps/web/public/icon/hono.svg new file mode 100644 index 0000000..9fffa74 --- /dev/null +++ b/apps/web/public/icon/hono.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/mongodb.svg b/apps/web/public/icon/mongodb.svg new file mode 100644 index 0000000..5999461 --- /dev/null +++ b/apps/web/public/icon/mongodb.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/mysql.svg b/apps/web/public/icon/mysql.svg new file mode 100644 index 0000000..cafe01c --- /dev/null +++ b/apps/web/public/icon/mysql.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/icon/neon.svg b/apps/web/public/icon/neon.svg new file mode 100644 index 0000000..ddc31f9 --- /dev/null +++ b/apps/web/public/icon/neon.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/nextjs.svg b/apps/web/public/icon/nextjs.svg new file mode 100644 index 0000000..e2da0ad --- /dev/null +++ b/apps/web/public/icon/nextjs.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/icon/node.svg b/apps/web/public/icon/node.svg new file mode 100644 index 0000000..d92d02b --- /dev/null +++ b/apps/web/public/icon/node.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/npm.svg b/apps/web/public/icon/npm.svg new file mode 100644 index 0000000..cb38d9f --- /dev/null +++ b/apps/web/public/icon/npm.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/pnpm.svg b/apps/web/public/icon/pnpm.svg new file mode 100644 index 0000000..18b08ef --- /dev/null +++ b/apps/web/public/icon/pnpm.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/postgres.svg b/apps/web/public/icon/postgres.svg new file mode 100644 index 0000000..916884c --- /dev/null +++ b/apps/web/public/icon/postgres.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/prisma.svg b/apps/web/public/icon/prisma.svg new file mode 100644 index 0000000..ceffb2f --- /dev/null +++ b/apps/web/public/icon/prisma.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/react-router.svg b/apps/web/public/icon/react-router.svg new file mode 100644 index 0000000..7782575 --- /dev/null +++ b/apps/web/public/icon/react-router.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/public/icon/sqlite.svg b/apps/web/public/icon/sqlite.svg new file mode 100644 index 0000000..7e21294 --- /dev/null +++ b/apps/web/public/icon/sqlite.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/web/public/icon/tanstack.svg b/apps/web/public/icon/tanstack.svg new file mode 100644 index 0000000..2216a59 --- /dev/null +++ b/apps/web/public/icon/tanstack.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/tauri.svg b/apps/web/public/icon/tauri.svg new file mode 100644 index 0000000..e054c5c --- /dev/null +++ b/apps/web/public/icon/tauri.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/trpc.svg b/apps/web/public/icon/trpc.svg new file mode 100644 index 0000000..150908c --- /dev/null +++ b/apps/web/public/icon/trpc.svg @@ -0,0 +1 @@ + diff --git a/apps/web/public/icon/turborepo.svg b/apps/web/public/icon/turborepo.svg new file mode 100644 index 0000000..0cebcb4 --- /dev/null +++ b/apps/web/public/icon/turborepo.svg @@ -0,0 +1 @@ + diff --git a/apps/web/src/app/(home)/_components/StackArchitech.tsx b/apps/web/src/app/(home)/_components/StackArchitech.tsx index 59633da..4593009 100644 --- a/apps/web/src/app/(home)/_components/StackArchitech.tsx +++ b/apps/web/src/app/(home)/_components/StackArchitech.tsx @@ -19,6 +19,7 @@ import { Terminal, } from "lucide-react"; import { motion } from "motion/react"; +import Image from "next/image"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; const validateProjectName = (name: string): string | undefined => { @@ -72,6 +73,29 @@ const hasPWACompatibleFrontend = (frontend: string[]) => const hasNativeFrontend = (frontend: string[]) => frontend.includes("native"); +const TechIcon = ({ + icon, + name, + className, +}: { icon: string; name: string; className?: string }) => { + if (icon.startsWith("/icon/")) { + return ( + {`${name} + ); + } + + return ( + {icon} + ); +}; + const StackArchitect = () => { const [stack, setStack] = useState(DEFAULT_STACK); const [command, setCommand] = useState(""); @@ -118,7 +142,7 @@ const StackArchitect = () => { } }, []); - // biome-ignore lint/correctness/useExhaustiveDependencies: Dependencies are logically required for validation inside updater + // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect(() => { setStack((currentStack) => { const nextStack = { ...currentStack }; @@ -255,7 +279,7 @@ const StackArchitect = () => { } const projectName = stackState.projectName || "my-better-t-app"; - const flags: string[] = []; + const flags: string[] = ["--yes"]; const isDefault = ( key: K, @@ -584,6 +608,32 @@ const StackArchitect = () => { (category: keyof typeof TECH_OPTIONS, techId: string): string | null => { const catKey = category as keyof StackState; + if ( + (catKey === "frontend" || + catKey === "addons" || + catKey === "examples") && + Array.isArray(stack[catKey]) && + (stack[catKey] as string[]).length === 1 && + (stack[catKey] as string[])[0] === techId + ) { + if (catKey === "frontend" && techId === "none") { + } else if (catKey !== "frontend") { + } else { + return "At least one frontend option must be selected."; + } + } + + if ( + !( + catKey === "frontend" || + catKey === "addons" || + catKey === "examples" + ) && + stack[catKey] === techId + ) { + return "This option is currently selected."; + } + if (catKey === "api" && techId !== "trpc" && currentHasNativeFrontend) { return "Only tRPC API is supported with React Native."; } @@ -595,6 +645,8 @@ const StackArchitect = () => { return "MongoDB requires the Prisma ORM."; if (stack.dbSetup === "turso" && techId === "prisma") return "Turso DB setup requires the Drizzle ORM."; + if (techId === "none" && stack.database === "mongodb") + return "MongoDB requires Prisma ORM."; } if (catKey === "dbSetup" && techId !== "none") { @@ -646,10 +698,7 @@ const StackArchitect = () => { return null; }, [ - stack.database, - stack.orm, - stack.dbSetup, - stack.backendFramework, + stack, currentHasNativeFrontend, currentHasPWACompatibleFrontend, currentHasWebFrontend, @@ -891,9 +940,6 @@ const StackArchitect = () => {
- {/*

- Selected Stack Summary -

*/}
{CATEGORY_ORDER.flatMap((category) => { const categoryKey = category as keyof StackState; @@ -915,9 +961,14 @@ const StackArchitect = () => { .map((tech) => ( - {tech.icon} {tech.name} + + {tech.name} )); } @@ -938,9 +989,14 @@ const StackArchitect = () => { return ( - {tech.icon} {tech.name} + + {tech.name} ); })} @@ -1018,7 +1074,7 @@ const StackArchitect = () => { className={`list-inside list-disc space-y-1 text-xs ${notesInfo.hasIssue ? "text-orange-700 dark:text-orange-400" : "text-blue-700 dark:text-blue-400"}`} > {notesInfo.notes.map((note, index) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: Static notes per render + // biome-ignore lint/suspicious/noArrayIndexKey:
  • {note}
  • ))} @@ -1086,9 +1142,11 @@ const StackArchitect = () => {
    - - {tech.icon} - +