diff --git a/.changeset/ui-default-shadcn.md b/.changeset/ui-default-shadcn.md new file mode 100644 index 00000000..6a54d283 --- /dev/null +++ b/.changeset/ui-default-shadcn.md @@ -0,0 +1,5 @@ +--- +"@proofkit/cli": minor +--- + +CLI defaults to shadcn/ui for new projects. Legacy Mantine templates are still available via a hidden `--ui mantine` flag during `init`. The selected UI is persisted in `proofkit.json` as `ui`. Existing projects using Mantine are auto-detected and remain fully supported. For shadcn-based projects, adding new pages or auth via `proofkit add` is temporarily disabled while we work on a new component system. \ No newline at end of file diff --git a/packages/cli/src/cli/add/auth.ts b/packages/cli/src/cli/add/auth.ts index 8b389ba0..9e5208dd 100644 --- a/packages/cli/src/cli/add/auth.ts +++ b/packages/cli/src/cli/add/auth.ts @@ -14,6 +14,11 @@ export async function runAddAuthAction() { if (settings.appType !== "browser") { return p.cancel(`Auth is not supported for your app type.`); } + if (settings.ui === "shadcn") { + return p.cancel( + "Adding auth is not yet supported for shadcn-based projects." + ); + } const authType = state.authType ?? diff --git a/packages/cli/src/cli/add/index.ts b/packages/cli/src/cli/add/index.ts index 5989d7b5..2d1301de 100644 --- a/packages/cli/src/cli/add/index.ts +++ b/packages/cli/src/cli/add/index.ts @@ -47,6 +47,15 @@ export const runAdd = async (name: string | undefined) => { }) ); + // For shadcn projects, block adding new pages or auth for now + if (settings.ui === "shadcn") { + if (addType === "page" || addType === "auth") { + return p.cancel( + "Adding new pages or auth is not yet supported for shadcn-based projects." + ); + } + } + if (addType === "auth") { await runAddAuthAction(); } else if (addType === "data") { diff --git a/packages/cli/src/cli/add/page/index.ts b/packages/cli/src/cli/add/page/index.ts index a4eec2dd..79c9ca6f 100644 --- a/packages/cli/src/cli/add/page/index.ts +++ b/packages/cli/src/cli/add/page/index.ts @@ -29,6 +29,11 @@ export const runAddPageAction = async (opts?: { const projectDir = state.projectDir; const settings = getSettings(); + if (settings.ui === "shadcn") { + return p.cancel( + "Adding pages is not yet supported for shadcn-based projects." + ); + } const templates = state.appType === "browser" diff --git a/packages/cli/src/cli/init.ts b/packages/cli/src/cli/init.ts index e8e05c09..87c8c468 100644 --- a/packages/cli/src/cli/init.ts +++ b/packages/cli/src/cli/init.ts @@ -24,6 +24,7 @@ import { parseNameAndPath } from "~/utils/parseNameAndPath.js"; import { validateAppName } from "~/utils/validateAppName.js"; import { promptForFileMakerDataSource } from "./add/data-source/filemaker.js"; import { abortIfCancel } from "./utils.js"; +import { setSettings, type Settings } from "~/utils/parseSettings.js"; interface CliFlags { noGit: boolean; @@ -39,6 +40,8 @@ interface CliFlags { fmServerURL: string; auth: "none" | "next-auth" | "clerk"; dataSource?: "filemaker" | "none" | "supabase"; + /** @internal UI library selection; hidden flag */ + ui?: "shadcn" | "mantine"; /** @internal Used in CI. */ CI: boolean; /** @internal Used in CI. */ @@ -72,6 +75,7 @@ const defaultOptions: CliFlags = { dataApiKey: "", fmServerURL: "", dataSource: undefined, + ui: "shadcn", }; export const makeInitCommand = () => { @@ -82,6 +86,8 @@ export const makeInitCommand = () => { "The name of the application, as well as the name of the directory to create" ) .option("--appType [type]", "The type of app to create", undefined) + // hidden UI selector; default is shadcn; pass --ui mantine to opt-in legacy Mantine templates + .option("--ui [ui]", undefined, undefined) .option("--server [url]", "The URL of your FileMaker Server", undefined) .option( "--adminApiKey [key]", @@ -166,6 +172,8 @@ type ProofKitPackageJSON = PackageJson & { export const runInit = async (name?: string, opts?: CliFlags) => { const pkgManager = getUserPkgManager(); const cliOptions = opts ?? defaultOptions; + // capture ui choice early into state + state.ui = (cliOptions.ui ?? "shadcn") as "shadcn" | "mantine"; const projectName = name || @@ -238,6 +246,19 @@ export const runInit = async (name?: string, opts?: CliFlags) => { spaces: 2, }); + // Ensure proofkit.json exists with initial settings including ui + const initialSettings: Settings = { + appType: state.appType ?? "browser", + ui: (state.ui as "shadcn" | "mantine") ?? "shadcn", + auth: { type: "none" }, + envFile: ".env", + dataSources: [], + tanstackQuery: false, + replacedMainPage: false, + appliedUpgrades: ["cursorRules"], + }; + setSettings(initialSettings); + // for webviewer apps FM is required, so don't ask let dataSource = state.appType === "webviewer" ? "filemaker" : cliOptions.dataSource; diff --git a/packages/cli/src/helpers/createProject.ts b/packages/cli/src/helpers/createProject.ts index ea397701..9966dbb3 100644 --- a/packages/cli/src/helpers/createProject.ts +++ b/packages/cli/src/helpers/createProject.ts @@ -34,9 +34,9 @@ export const createBareProject = async ({ noInstall, }); - // Add new base dependencies for Tailwind v4 and shadcn/ui + // Add new base dependencies for Tailwind v4 and shadcn/ui or legacy Mantine // These should match the plan and dependencyVersionMap - const BASE_DEPS = [ + const SHADCN_BASE_DEPS = [ "@radix-ui/react-slot", "@tailwindcss/postcss", "class-variance-authority", @@ -45,22 +45,50 @@ export const createBareProject = async ({ "tailwind-merge", "tailwindcss", "tw-animate-css", + "next-themes", ] as AvailableDependencies[]; - const BASE_DEV_DEPS = [ + const SHADCN_BASE_DEV_DEPS = [ "prettier", "prettier-plugin-tailwindcss", ] as AvailableDependencies[]; - addPackageDependency({ - dependencies: BASE_DEPS, - devMode: false, - projectDir: state.projectDir, - }); - addPackageDependency({ - dependencies: BASE_DEV_DEPS, - devMode: true, - projectDir: state.projectDir, - }); + const MANTINE_DEPS = [ + "@mantine/core", + "@mantine/dates", + "@mantine/hooks", + "@mantine/modals", + "@mantine/notifications", + "mantine-react-table", + ] as AvailableDependencies[]; + const MANTINE_DEV_DEPS = [ + "postcss", + "postcss-preset-mantine", + "postcss-simple-vars", + ] as AvailableDependencies[]; + + if (state.ui === "mantine") { + addPackageDependency({ + dependencies: MANTINE_DEPS, + devMode: false, + projectDir: state.projectDir, + }); + addPackageDependency({ + dependencies: MANTINE_DEV_DEPS, + devMode: true, + projectDir: state.projectDir, + }); + } else { + addPackageDependency({ + dependencies: SHADCN_BASE_DEPS, + devMode: false, + projectDir: state.projectDir, + }); + addPackageDependency({ + dependencies: SHADCN_BASE_DEV_DEPS, + devMode: true, + projectDir: state.projectDir, + }); + } // Install the selected packages installPackages({ diff --git a/packages/cli/src/helpers/scaffoldProject.ts b/packages/cli/src/helpers/scaffoldProject.ts index 8ea7ed4c..c185197e 100644 --- a/packages/cli/src/helpers/scaffoldProject.ts +++ b/packages/cli/src/helpers/scaffoldProject.ts @@ -20,7 +20,9 @@ export const scaffoldProject = async ({ const srcDir = path.join( PKG_ROOT, - state.appType === "browser" ? "template/nextjs" : "template/vite-wv" + state.appType === "browser" + ? `template/${state.ui === "mantine" ? "nextjs-mantine" : "nextjs-shadcn"}` + : "template/vite-wv" ); if (!noInstall) { diff --git a/packages/cli/src/installers/dependencyVersionMap.ts b/packages/cli/src/installers/dependencyVersionMap.ts index 13df7f69..59edbdd3 100644 --- a/packages/cli/src/installers/dependencyVersionMap.ts +++ b/packages/cli/src/installers/dependencyVersionMap.ts @@ -88,5 +88,16 @@ export const dependencyVersionMap = { // Icons (for shadcn/ui) "lucide-react": "^0.518.0", + + // Mantine UI + "@mantine/core": "^7.15.0", + "@mantine/dates": "^7.15.0", + "@mantine/hooks": "^7.15.0", + "@mantine/modals": "^7.15.0", + "@mantine/notifications": "^7.15.0", + "mantine-react-table": "^2.0.0", + + // Theme utilities + "next-themes": "^0.4.6", } as const; export type AvailableDependencies = keyof typeof dependencyVersionMap; diff --git a/packages/cli/src/state.ts b/packages/cli/src/state.ts index e4c8ac78..14101099 100644 --- a/packages/cli/src/state.ts +++ b/packages/cli/src/state.ts @@ -10,6 +10,7 @@ const schema = z .optional() .catch(undefined), appType: z.enum(["browser", "webviewer"]).optional().catch(undefined), + ui: z.enum(["shadcn", "mantine"]).optional().catch(undefined), projectDir: z.string().default(process.cwd()), authType: z.enum(["clerk", "fmaddon"]).optional(), emailProvider: z.enum(["plunk", "resend", "none"]).optional(), diff --git a/packages/cli/src/utils/parseSettings.ts b/packages/cli/src/utils/parseSettings.ts index 5bd0f4a0..58d3b24f 100644 --- a/packages/cli/src/utils/parseSettings.ts +++ b/packages/cli/src/utils/parseSettings.ts @@ -44,8 +44,12 @@ export type DataSource = z.infer; export const appTypes = ["browser", "webviewer"] as const; +export const uiTypes = ["shadcn", "mantine"] as const; +export type Ui = (typeof uiTypes)[number]; + const settingsSchema = z.object({ appType: z.enum(appTypes).default("browser"), + ui: z.enum(uiTypes).default("shadcn"), auth: authSchema, envFile: z.string().default(".env"), dataSources: z.array(dataSourceSchema).default([]), @@ -60,11 +64,37 @@ let settings: Settings | undefined; export const getSettings = () => { if (settings) return settings; - const settingsFile: unknown = fs.readJSONSync( - path.join(state.projectDir, "proofkit.json") - ); + const settingsPath = path.join(state.projectDir, "proofkit.json"); + const settingsFile: unknown = fs.readJSONSync(settingsPath); const parsed = settingsSchema.parse(settingsFile); + + // Persist missing ui field for older projects; auto-detect mantine if present + const hasUiInFile = typeof (settingsFile as Record)?.ui === "string"; + if (!hasUiInFile) { + try { + const pkgJson = fs.readJSONSync(path.join(state.projectDir, "package.json")) as { + dependencies?: Record; + devDependencies?: Record; + }; + const depHas = (name: string) => + Boolean(pkgJson.dependencies?.[name] || pkgJson.devDependencies?.[name]); + const detectedUi: Ui = depHas("@mantine/core") ? "mantine" : "shadcn"; + const nextSettings = { ...parsed, ui: detectedUi } as Settings; + fs.writeJSONSync(settingsPath, nextSettings, { spaces: 2 }); + settings = nextSettings; + state.appType = nextSettings.appType; + return settings; + } catch { + // If detection fails, just persist default + const nextSettings = { ...parsed } as Settings; + fs.writeJSONSync(settingsPath, nextSettings, { spaces: 2 }); + settings = nextSettings; + state.appType = nextSettings.appType; + return settings; + } + } + settings = parsed; state.appType = parsed.appType; return settings; diff --git a/packages/cli/template/nextjs/.prettierrc b/packages/cli/template/nextjs-mantine/.prettierrc similarity index 100% rename from packages/cli/template/nextjs/.prettierrc rename to packages/cli/template/nextjs-mantine/.prettierrc diff --git a/packages/cli/template/nextjs/README.md b/packages/cli/template/nextjs-mantine/README.md similarity index 100% rename from packages/cli/template/nextjs/README.md rename to packages/cli/template/nextjs-mantine/README.md diff --git a/packages/cli/template/nextjs/_gitignore b/packages/cli/template/nextjs-mantine/_gitignore similarity index 100% rename from packages/cli/template/nextjs/_gitignore rename to packages/cli/template/nextjs-mantine/_gitignore diff --git a/packages/cli/template/nextjs/components.json b/packages/cli/template/nextjs-mantine/components.json similarity index 100% rename from packages/cli/template/nextjs/components.json rename to packages/cli/template/nextjs-mantine/components.json diff --git a/packages/cli/template/nextjs/next.config.ts b/packages/cli/template/nextjs-mantine/next.config.ts similarity index 100% rename from packages/cli/template/nextjs/next.config.ts rename to packages/cli/template/nextjs-mantine/next.config.ts diff --git a/packages/cli/template/nextjs/package.json b/packages/cli/template/nextjs-mantine/package.json similarity index 100% rename from packages/cli/template/nextjs/package.json rename to packages/cli/template/nextjs-mantine/package.json diff --git a/packages/cli/template/nextjs/postcss.config.cjs b/packages/cli/template/nextjs-mantine/postcss.config.cjs similarity index 100% rename from packages/cli/template/nextjs/postcss.config.cjs rename to packages/cli/template/nextjs-mantine/postcss.config.cjs diff --git a/packages/cli/template/nextjs-mantine/proofkit.json b/packages/cli/template/nextjs-mantine/proofkit.json new file mode 100644 index 00000000..c536f9bf --- /dev/null +++ b/packages/cli/template/nextjs-mantine/proofkit.json @@ -0,0 +1,7 @@ +{ + "auth": { "type": "none" }, + "envFile": ".env", + "appType": "browser", + "ui": "mantine", + "appliedUpgrades": ["cursorRules"] +} diff --git a/packages/cli/template/nextjs/public/favicon.ico b/packages/cli/template/nextjs-mantine/public/favicon.ico similarity index 100% rename from packages/cli/template/nextjs/public/favicon.ico rename to packages/cli/template/nextjs-mantine/public/favicon.ico diff --git a/packages/cli/template/nextjs/public/proofkit.png b/packages/cli/template/nextjs-mantine/public/proofkit.png similarity index 100% rename from packages/cli/template/nextjs/public/proofkit.png rename to packages/cli/template/nextjs-mantine/public/proofkit.png diff --git a/packages/cli/template/nextjs/src/app/(main)/layout.tsx b/packages/cli/template/nextjs-mantine/src/app/(main)/layout.tsx similarity index 100% rename from packages/cli/template/nextjs/src/app/(main)/layout.tsx rename to packages/cli/template/nextjs-mantine/src/app/(main)/layout.tsx diff --git a/packages/cli/template/nextjs/src/app/(main)/page.tsx b/packages/cli/template/nextjs-mantine/src/app/(main)/page.tsx similarity index 100% rename from packages/cli/template/nextjs/src/app/(main)/page.tsx rename to packages/cli/template/nextjs-mantine/src/app/(main)/page.tsx diff --git a/packages/cli/template/nextjs/src/app/layout.tsx b/packages/cli/template/nextjs-mantine/src/app/layout.tsx similarity index 100% rename from packages/cli/template/nextjs/src/app/layout.tsx rename to packages/cli/template/nextjs-mantine/src/app/layout.tsx diff --git a/packages/cli/template/nextjs/src/app/navigation.tsx b/packages/cli/template/nextjs-mantine/src/app/navigation.tsx similarity index 100% rename from packages/cli/template/nextjs/src/app/navigation.tsx rename to packages/cli/template/nextjs-mantine/src/app/navigation.tsx diff --git a/packages/cli/template/nextjs/src/components/AppLogo.tsx b/packages/cli/template/nextjs-mantine/src/components/AppLogo.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppLogo.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppLogo.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/internal/AppShell.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/internal/AppShell.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/internal/AppShell.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/internal/AppShell.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/internal/Header.module.css b/packages/cli/template/nextjs-mantine/src/components/AppShell/internal/Header.module.css similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/internal/Header.module.css rename to packages/cli/template/nextjs-mantine/src/components/AppShell/internal/Header.module.css diff --git a/packages/cli/template/nextjs/src/components/AppShell/internal/Header.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/internal/Header.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/internal/Header.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/internal/Header.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/internal/HeaderMobileMenu.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/internal/HeaderMobileMenu.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/internal/HeaderMobileMenu.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/internal/HeaderMobileMenu.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/internal/HeaderNavLink.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/internal/HeaderNavLink.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/internal/HeaderNavLink.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/internal/HeaderNavLink.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/internal/config.ts b/packages/cli/template/nextjs-mantine/src/components/AppShell/internal/config.ts similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/internal/config.ts rename to packages/cli/template/nextjs-mantine/src/components/AppShell/internal/config.ts diff --git a/packages/cli/template/nextjs/src/components/AppShell/slot-header-center.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-center.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/slot-header-center.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-center.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/slot-header-left.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-left.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/slot-header-left.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-left.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/slot-header-mobile-content.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-mobile-content.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/slot-header-mobile-content.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-mobile-content.tsx diff --git a/packages/cli/template/nextjs/src/components/AppShell/slot-header-right.tsx b/packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-right.tsx similarity index 100% rename from packages/cli/template/nextjs/src/components/AppShell/slot-header-right.tsx rename to packages/cli/template/nextjs-mantine/src/components/AppShell/slot-header-right.tsx diff --git a/packages/cli/template/nextjs/src/config/env.ts b/packages/cli/template/nextjs-mantine/src/config/env.ts similarity index 100% rename from packages/cli/template/nextjs/src/config/env.ts rename to packages/cli/template/nextjs-mantine/src/config/env.ts diff --git a/packages/cli/template/nextjs/src/config/theme/globals.css b/packages/cli/template/nextjs-mantine/src/config/theme/globals.css similarity index 100% rename from packages/cli/template/nextjs/src/config/theme/globals.css rename to packages/cli/template/nextjs-mantine/src/config/theme/globals.css diff --git a/packages/cli/template/nextjs/src/config/theme/mantine-theme.ts b/packages/cli/template/nextjs-mantine/src/config/theme/mantine-theme.ts similarity index 100% rename from packages/cli/template/nextjs/src/config/theme/mantine-theme.ts rename to packages/cli/template/nextjs-mantine/src/config/theme/mantine-theme.ts diff --git a/packages/cli/template/nextjs/src/server/safe-action.ts b/packages/cli/template/nextjs-mantine/src/server/safe-action.ts similarity index 100% rename from packages/cli/template/nextjs/src/server/safe-action.ts rename to packages/cli/template/nextjs-mantine/src/server/safe-action.ts diff --git a/packages/cli/template/nextjs/src/utils/notification-helpers.ts b/packages/cli/template/nextjs-mantine/src/utils/notification-helpers.ts similarity index 100% rename from packages/cli/template/nextjs/src/utils/notification-helpers.ts rename to packages/cli/template/nextjs-mantine/src/utils/notification-helpers.ts diff --git a/packages/cli/template/nextjs/src/utils/styles.ts b/packages/cli/template/nextjs-mantine/src/utils/styles.ts similarity index 100% rename from packages/cli/template/nextjs/src/utils/styles.ts rename to packages/cli/template/nextjs-mantine/src/utils/styles.ts diff --git a/packages/cli/template/nextjs/tsconfig.json b/packages/cli/template/nextjs-mantine/tsconfig.json similarity index 100% rename from packages/cli/template/nextjs/tsconfig.json rename to packages/cli/template/nextjs-mantine/tsconfig.json diff --git a/packages/cli/template/nextjs-shadcn/.prettierrc b/packages/cli/template/nextjs-shadcn/.prettierrc new file mode 100644 index 00000000..b4bfed35 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/packages/cli/template/nextjs-shadcn/README.md b/packages/cli/template/nextjs-shadcn/README.md new file mode 100644 index 00000000..4f416a4a --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/README.md @@ -0,0 +1,27 @@ +# ProofKit NextJS Template + +This is a [NextJS](https://nextjs.org/) project bootstrapped with `@proofkit/cli`. Learn more at [proofkit.dev](https://proofkit.dev) + +## What's next? How do I make an app with this? + +While this template is designed to be a minimal starting point, the proofkit CLI will guide you through adding additional features and pages. + +To add new things to your project, simply run the `proofkit` script from the project's root directory. + +e.g. `npm run proofkit` or `pnpm proofkit` etc. + +For more information, see the full [ProofKit documentation](https://proofkit.dev). + +## Project Structure + +ProofKit projects have an opinionated structure to help you get started and some conventions must be maintained to ensure that the CLI can properly inject new features and components. + +The `src` directory is the home for your application code. It is used for most things except for configuration and is organized as follows: + +- `app` - NextJS app router, where your pages and routes are defined +- `components` - Shared components used throughout the app +- `server` - Code that connects to backend databases and services that should not be exposed in the browser + +Anytime you see an `internal` folder, you should not modify any files inside. These files are maintained exclusively by the ProofKit CLI and changes to them may be overwritten. + +Anytime you see a componet file that begins with `slot-`, you _may_ modify the content, but do not rename, remove, or move them. These are desigend to be customized, but are still used by the CLI to inject additional content. If a slot is not needed by your app, you can have the compoment return `null` or an empty fragment: `<>` diff --git a/packages/cli/template/nextjs-shadcn/_gitignore b/packages/cli/template/nextjs-shadcn/_gitignore new file mode 100644 index 00000000..00bba9bb --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/_gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/cli/template/nextjs-shadcn/components.json b/packages/cli/template/nextjs-shadcn/components.json new file mode 100644 index 00000000..7cccc384 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/config/theme/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/utils/styles", + "ui": "@/components/ui", + "lib": "@/utils", + "hooks": "@/utils/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/packages/cli/template/nextjs-shadcn/next.config.ts b/packages/cli/template/nextjs-shadcn/next.config.ts new file mode 100644 index 00000000..5a07c277 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/next.config.ts @@ -0,0 +1,8 @@ +import { type NextConfig } from "next"; + +// Import env here to validate during build. +import "./src/config/env"; + +const nextConfig: NextConfig = {}; + +export default nextConfig; diff --git a/packages/cli/template/nextjs-shadcn/package.json b/packages/cli/template/nextjs-shadcn/package.json new file mode 100644 index 00000000..5c9833a7 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/package.json @@ -0,0 +1,42 @@ +{ + "name": "template", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint", + "proofkit": "proofkit", + "typegen": "proofkit typegen", + "deploy": "proofkit deploy" + }, + "dependencies": { + "@hookform/resolvers": "^5.1.1", + "@next-safe-action/adapter-react-hook-form": "^2.0.0", + "next-safe-action": "^8.0.4", + "react-hook-form": "^7.54.2", + "@tabler/icons-react": "^3.30.0", + "@t3-oss/env-nextjs": "^0.12.0", + "dayjs": "^1.11.13", + "next": "^15.2.3", + "react": "19.0.0", + "react-dom": "19.0.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "npm:types-react@19.0.12", + "@types/react-dom": "npm:types-react-dom@19.0.4", + "eslint": "^9.14.0", + "eslint-config-next": "15.2.3", + "postcss": "^8.4.41", + "typescript": "^5" + }, + "pnpm": { + "overrides": { + "@types/react": "npm:types-react@19.0.0-rc.1", + "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1" + } + } +} diff --git a/packages/cli/template/nextjs-shadcn/postcss.config.cjs b/packages/cli/template/nextjs-shadcn/postcss.config.cjs new file mode 100644 index 00000000..483f3785 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/postcss.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/packages/cli/template/nextjs/proofkit.json b/packages/cli/template/nextjs-shadcn/proofkit.json similarity index 87% rename from packages/cli/template/nextjs/proofkit.json rename to packages/cli/template/nextjs-shadcn/proofkit.json index daca7e48..6aca1027 100644 --- a/packages/cli/template/nextjs/proofkit.json +++ b/packages/cli/template/nextjs-shadcn/proofkit.json @@ -2,5 +2,6 @@ "auth": { "type": "none" }, "envFile": ".env", "appType": "browser", + "ui": "shadcn", "appliedUpgrades": ["shadcn", "cursorRules"] } diff --git a/packages/cli/template/nextjs-shadcn/public/favicon.ico b/packages/cli/template/nextjs-shadcn/public/favicon.ico new file mode 100644 index 00000000..ba9355b8 Binary files /dev/null and b/packages/cli/template/nextjs-shadcn/public/favicon.ico differ diff --git a/packages/cli/template/nextjs-shadcn/public/proofkit.png b/packages/cli/template/nextjs-shadcn/public/proofkit.png new file mode 100644 index 00000000..2969f842 Binary files /dev/null and b/packages/cli/template/nextjs-shadcn/public/proofkit.png differ diff --git a/packages/cli/template/nextjs-shadcn/src/app/(main)/layout.tsx b/packages/cli/template/nextjs-shadcn/src/app/(main)/layout.tsx new file mode 100644 index 00000000..57a8452b --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/app/(main)/layout.tsx @@ -0,0 +1,6 @@ +import AppShell from "@/components/AppShell/internal/AppShell"; +import React from "react"; + +export default function Layout({ children }: { children: React.ReactNode }) { + return {children}; +} diff --git a/packages/cli/template/nextjs-shadcn/src/app/(main)/page.tsx b/packages/cli/template/nextjs-shadcn/src/app/(main)/page.tsx new file mode 100644 index 00000000..5c9443b3 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/app/(main)/page.tsx @@ -0,0 +1,116 @@ +"use client"; + +import { + IconBrandGithub, + IconExternalLink, + IconCopy, + IconCheck, + IconTerminal, +} from "@tabler/icons-react"; +import { Button } from "@/components/ui/button"; +import { useState } from "react"; + +function InlineSnippet({ command }: { command: string }) { + const [copied, setCopied] = useState(false); + + const onCopy = () => { + if (typeof window === "undefined" || !navigator.clipboard?.writeText) + return; + navigator.clipboard.writeText(command).then( + () => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }, + () => {}, + ); + }; + + return ( +
+
+ +
+ + {command} + +
+ +
+
+ ); +} + +export default function Home() { + return ( +
+
+
+ ProofKit +

Welcome!

+ +

+ This is the base template home page. To add more pages, components, + or other features, run the ProofKit CLI from within your project. +

+ + + +

+ To change this page, open src/app/(main)/page.tsx +

+ +
+
+
+
+
+ Sponsored by{" "} + + Proof+Geist + {" "} + and{" "} + + Ottomatic + +
+
+ + + +
+
+
+
+ ); +} diff --git a/packages/cli/template/nextjs-shadcn/src/app/layout.tsx b/packages/cli/template/nextjs-shadcn/src/app/layout.tsx new file mode 100644 index 00000000..70a51fc1 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/app/layout.tsx @@ -0,0 +1,38 @@ +import { type Metadata } from "next"; +import "@/config/theme/globals.css"; + +export const metadata: Metadata = { + title: "My ProofKit App", + description: "Generated by proofkit", + icons: [{ rel: "icon", url: "/favicon.ico" }], +}; + +import { Geist, Geist_Mono } from "next/font/google"; + +import Providers from "@/components/providers"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/packages/cli/template/nextjs-shadcn/src/app/navigation.tsx b/packages/cli/template/nextjs-shadcn/src/app/navigation.tsx new file mode 100644 index 00000000..887073db --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/app/navigation.tsx @@ -0,0 +1,12 @@ +import { type ProofKitRoute } from "@proofkit/cli"; + +export const primaryRoutes: ProofKitRoute[] = [ + { + label: "Dashboard", + type: "link", + href: "/", + exactMatch: true, + }, +]; + +export const secondaryRoutes: ProofKitRoute[] = []; diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppLogo.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppLogo.tsx new file mode 100644 index 00000000..f5ea4966 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppLogo.tsx @@ -0,0 +1,6 @@ +import { IconInfinity } from "@tabler/icons-react"; +import React from "react"; + +export default function AppLogo() { + return ; +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/AppShell.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/AppShell.tsx new file mode 100644 index 00000000..33ccb2df --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/AppShell.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Header } from "@/components/AppShell/internal/Header"; +import { headerHeight } from "./config"; + +export default function MainAppShell({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+
+
+
+ {children} +
+
+ ); +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/Header.module.css b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/Header.module.css new file mode 100644 index 00000000..2733308e --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/Header.module.css @@ -0,0 +1,33 @@ +.header { + margin-bottom: 7.5rem; + background-color: var(--pk-header-bg, transparent); + border-bottom: 1px solid var(--pk-border, rgba(0,0,0,0.08)); +} + +.inner { + display: flex; + justify-content: space-between; + align-items: center; +} + +.link { + display: block; + line-height: 1; + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + text-decoration: none; + color: inherit; + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + background: none; + border: none; +} + +.link:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +[data-theme="dark"] .link:hover { + background-color: rgba(255, 255, 255, 0.06); +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/Header.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/Header.tsx new file mode 100644 index 00000000..a302ecce --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/Header.tsx @@ -0,0 +1,30 @@ +import SlotHeaderCenter from "../slot-header-center"; +import SlotHeaderLeft from "../slot-header-left"; +import SlotHeaderRight from "../slot-header-right"; +import { headerHeight } from "./config"; +import classes from "./Header.module.css"; +import HeaderMobileMenu from "./HeaderMobileMenu"; + +export function Header() { + return ( +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+ ); +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/HeaderMobileMenu.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/HeaderMobileMenu.tsx new file mode 100644 index 00000000..ac2a2e2b --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/HeaderMobileMenu.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { useState } from "react"; +import SlotHeaderMobileMenuContent from "../slot-header-mobile-content"; + +export default function HeaderMobileMenu() { + const [opened, setOpened] = useState(false); + + return ( +
+ + {opened && ( +
+ setOpened(false)} /> +
+ )} +
+ ); +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/HeaderNavLink.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/HeaderNavLink.tsx new file mode 100644 index 00000000..5da52246 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/HeaderNavLink.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { type ProofKitRoute } from "@proofkit/cli"; +import { usePathname } from "next/navigation"; +import React from "react"; + +import classes from "./Header.module.css"; + +export default function HeaderNavLink(route: ProofKitRoute) { + const pathname = usePathname(); + + if (route.type === "function") { + return ; + } + + const isActive = route.exactMatch + ? pathname === route.href + : pathname.startsWith(route.href); + + if (route.type === "link") { + return ( + + {route.label} + + ); + } +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/config.ts b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/config.ts new file mode 100644 index 00000000..ded639d0 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/internal/config.ts @@ -0,0 +1 @@ +export const headerHeight = 56; diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-center.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-center.tsx new file mode 100644 index 00000000..2de3b630 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-center.tsx @@ -0,0 +1,13 @@ +/** + * DO NOT REMOVE / RENAME THIS FILE + * + * You may CUSTOMIZE the content of this file, but the ProofKit CLI expects + * this file to exist and may use it to inject content for other components. + * + * If you don't want it to be used, you may return null or an empty fragment + */ +export function SlotHeaderCenter() { + return null; +} + +export default SlotHeaderCenter; diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-left.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-left.tsx new file mode 100644 index 00000000..781fcbce --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-left.tsx @@ -0,0 +1,23 @@ +import Link from "next/link"; + +import AppLogo from "../AppLogo"; + +/** + * DO NOT REMOVE / RENAME THIS FILE + * + * You may CUSTOMIZE the content of this file, but the ProofKit CLI expects this file to exist and + * may use it to inject content for other components. + * + * If you don't want it to be used, you may return null or an empty fragment + */ +export function SlotHeaderLeft() { + return ( + <> + + + + + ); +} + +export default SlotHeaderLeft; diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-mobile-content.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-mobile-content.tsx new file mode 100644 index 00000000..f63d0365 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-mobile-content.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { primaryRoutes } from "@/app/navigation"; +import { useRouter } from "next/navigation"; + +/** + * DO NOT REMOVE / RENAME THIS FILE + * + * You may CUSTOMIZE the content of this file, but the ProofKit CLI expects + * this file to exist and may use it to inject content for other components. + * + * If you don't want it to be used, you may return null or an empty fragment + */ +export function SlotHeaderMobileMenuContent({ + closeMenu, +}: { + closeMenu: () => void; +}) { + const router = useRouter(); + return ( +
+ {primaryRoutes.map((route) => ( + + ))} +
+ ); +} + +export default SlotHeaderMobileMenuContent; diff --git a/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-right.tsx b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-right.tsx new file mode 100644 index 00000000..afe06352 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/AppShell/slot-header-right.tsx @@ -0,0 +1,25 @@ +import { primaryRoutes } from "@/app/navigation"; + +import HeaderNavLink from "./internal/HeaderNavLink"; +import { ModeToggle } from "../mode-toggle"; + +/** + * DO NOT REMOVE / RENAME THIS FILE + * + * You may CUSTOMIZE the content of this file, but the ProofKit CLI expects + * this file to exist and may use it to inject content for other components. + * + * If you don't want it to be used, you may return null or an empty fragment + */ +export function SlotHeaderRight() { + return ( +
+ {primaryRoutes.map((route) => ( + + ))} + +
+ ); +} + +export default SlotHeaderRight; diff --git a/packages/cli/template/nextjs-shadcn/src/components/providers.tsx b/packages/cli/template/nextjs-shadcn/src/components/providers.tsx new file mode 100644 index 00000000..a101d447 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/providers.tsx @@ -0,0 +1,13 @@ +"use client"; + +import { ThemeProvider } from "./theme-provider"; +import { Toaster } from "./ui/sonner"; + +export default function Providers({ children }: { children: React.ReactNode }) { + return ( + + {children} + + + ); +} diff --git a/packages/cli/template/nextjs-shadcn/src/components/theme-provider.tsx b/packages/cli/template/nextjs-shadcn/src/components/theme-provider.tsx new file mode 100644 index 00000000..6459132f --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/components/theme-provider.tsx @@ -0,0 +1,11 @@ +"use client"; + +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import type * as React from "react"; + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children}; +} diff --git a/packages/cli/template/nextjs-shadcn/src/config/env.ts b/packages/cli/template/nextjs-shadcn/src/config/env.ts new file mode 100644 index 00000000..3c50ef8d --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/config/env.ts @@ -0,0 +1,13 @@ +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod/v4"; + +export const env = createEnv({ + server: { + NODE_ENV: z + .enum(["development", "test", "production"]) + .default("development"), + }, + client: {}, + // For Next.js >= 13.4.4, you only need to destructure client variables: + experimental__runtimeEnv: {}, +}); diff --git a/packages/cli/template/nextjs-shadcn/src/config/theme/globals.css b/packages/cli/template/nextjs-shadcn/src/config/theme/globals.css new file mode 100644 index 00000000..5a886a5a --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/config/theme/globals.css @@ -0,0 +1,126 @@ +/* Add global styles here */ + +@import "tailwindcss"; +@import "tw-animate-css"; + +@theme { + --font-sans: + "Inter", "Geist", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.85 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.2 0 0); + --secondary-foreground: oklch(0.9 0 0); + --muted: oklch(0.2 0 0); + --muted-foreground: oklch(0.78 0 0); + --accent: oklch(0.22 0 0); + --accent-foreground: oklch(0.9 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.27 0 0); + --input: oklch(0.27 0 0); + --ring: oklch(0.52 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.18 0 0); + --sidebar-foreground: oklch(0.97 0 0); + --sidebar-primary: var(--primary); + --sidebar-primary-foreground: var(--primary-foreground); + --sidebar-accent: oklch(0.22 0 0); + --sidebar-accent-foreground: oklch(0.9 0 0); + --sidebar-border: oklch(0.27 0 0); + --sidebar-ring: oklch(0.52 0 0); +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/packages/cli/template/nextjs-shadcn/src/server/safe-action.ts b/packages/cli/template/nextjs-shadcn/src/server/safe-action.ts new file mode 100644 index 00000000..7f62198a --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/server/safe-action.ts @@ -0,0 +1,3 @@ +import { createSafeActionClient } from "next-safe-action"; + +export const actionClient = createSafeActionClient(); diff --git a/packages/cli/template/nextjs-shadcn/src/utils/notification-helpers.ts b/packages/cli/template/nextjs-shadcn/src/utils/notification-helpers.ts new file mode 100644 index 00000000..dc0dd7c8 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/utils/notification-helpers.ts @@ -0,0 +1,15 @@ +export function showErrorNotification(): void; +export function showErrorNotification(message: string): void; +export function showErrorNotification(args?: string): void { + const message = typeof args === "string" ? args : "An unexpected error occurred."; + // TODO: Replace with your preferred toast library + if (typeof window !== "undefined") console.error(message); +} + +export function showSuccessNotification(): void; +export function showSuccessNotification(message: string): void; +export function showSuccessNotification(args?: string): void { + const message = typeof args === "string" ? args : "Success!"; + // TODO: Replace with your preferred toast library + if (typeof window !== "undefined") console.log(message); +} diff --git a/packages/cli/template/nextjs-shadcn/src/utils/styles.ts b/packages/cli/template/nextjs-shadcn/src/utils/styles.ts new file mode 100644 index 00000000..1f4cd38e --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/src/utils/styles.ts @@ -0,0 +1,6 @@ +import { clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: any[]) { + return twMerge(clsx(inputs)); +} diff --git a/packages/cli/template/nextjs-shadcn/tsconfig.json b/packages/cli/template/nextjs-shadcn/tsconfig.json new file mode 100644 index 00000000..f48e7ee6 --- /dev/null +++ b/packages/cli/template/nextjs-shadcn/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./src/*" + ] + }, + "target": "ES2017" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42a442e1..b1eccb95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,6 @@ overrides: importers: .: - dependencies: - '@proofkit/better-auth': - specifier: link:../../../../Library/pnpm/global/5/node_modules/@proofkit/better-auth - version: link:../../../../Library/pnpm/global/5/node_modules/@proofkit/better-auth devDependencies: '@changesets/cli': specifier: ^2.29.3 @@ -46,8 +42,8 @@ importers: specifier: ^1.2.10 version: 1.2.10(@libsql/client@0.6.2)(@planetscale/database@1.19.0)(@types/react@19.1.8)(kysely@0.28.2)(magicast@0.3.5)(mysql2@3.14.1)(postgres@3.4.5)(react@19.1.0) '@proofkit/better-auth': - specifier: link:../../../../../../Library/pnpm/global/5/node_modules/@proofkit/better-auth - version: link:../../../../../../Library/pnpm/global/5/node_modules/@proofkit/better-auth + specifier: link:../../../Library/pnpm/global/5/node_modules/@proofkit/better-auth + version: link:../../../Library/pnpm/global/5/node_modules/@proofkit/better-auth '@proofkit/fmdapi': specifier: workspace:* version: link:../../packages/fmdapi @@ -552,8 +548,6 @@ importers: specifier: ^3.2.3 version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(msw@2.10.2(@types/node@22.15.32)(typescript@5.8.3))(yaml@2.8.0) - packages/tmp: {} - packages/typegen: dependencies: '@clack/prompts': @@ -11568,7 +11562,7 @@ snapshots: '@typescript-eslint/parser': 8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.27.0(jiti@2.4.2)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.27.0(jiti@2.4.2)) eslint-plugin-react: 7.37.5(eslint@9.27.0(jiti@2.4.2)) @@ -11588,7 +11582,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -11603,14 +11597,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.27.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.27.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color @@ -11625,7 +11619,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.27.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.33.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.27.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3