kofi-stack-template-generator 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +20 -0
  2. package/dist/index.d.ts +94 -0
  3. package/dist/index.js +744 -0
  4. package/package.json +29 -0
  5. package/scripts/generate-templates.js +104 -0
  6. package/src/core/index.ts +7 -0
  7. package/src/core/template-processor.ts +127 -0
  8. package/src/core/virtual-fs.ts +189 -0
  9. package/src/generator.ts +429 -0
  10. package/src/index.ts +19 -0
  11. package/src/templates.generated.ts +39 -0
  12. package/templates/base/_gitignore.hbs +45 -0
  13. package/templates/base/biome.json.hbs +34 -0
  14. package/templates/convex/_env.local.hbs +52 -0
  15. package/templates/convex/convex/auth.ts.hbs +7 -0
  16. package/templates/convex/convex/http.ts.hbs +8 -0
  17. package/templates/convex/convex/schema.ts.hbs +15 -0
  18. package/templates/convex/convex/users.ts.hbs +13 -0
  19. package/templates/integrations/posthog/src/components/providers/posthog-provider.tsx.hbs +17 -0
  20. package/templates/monorepo/package.json.hbs +29 -0
  21. package/templates/monorepo/pnpm-workspace.yaml.hbs +3 -0
  22. package/templates/monorepo/turbo.json.hbs +42 -0
  23. package/templates/packages/config-biome/biome.json.hbs +4 -0
  24. package/templates/packages/config-biome/package.json.hbs +6 -0
  25. package/templates/packages/config-typescript/base.json.hbs +17 -0
  26. package/templates/packages/config-typescript/nextjs.json.hbs +7 -0
  27. package/templates/packages/config-typescript/package.json.hbs +10 -0
  28. package/templates/packages/ui/components.json.hbs +20 -0
  29. package/templates/packages/ui/package.json.hbs +34 -0
  30. package/templates/packages/ui/src/index.ts.hbs +3 -0
  31. package/templates/packages/ui/src/lib/utils.ts.hbs +6 -0
  32. package/templates/packages/ui/tsconfig.json.hbs +22 -0
  33. package/templates/web/components.json.hbs +20 -0
  34. package/templates/web/next.config.ts.hbs +9 -0
  35. package/templates/web/package.json.hbs +62 -0
  36. package/templates/web/postcss.config.mjs.hbs +5 -0
  37. package/templates/web/src/app/globals.css.hbs +122 -0
  38. package/templates/web/src/app/layout.tsx.hbs +55 -0
  39. package/templates/web/src/app/page.tsx.hbs +74 -0
  40. package/templates/web/src/components/providers/convex-provider.tsx.hbs +18 -0
  41. package/templates/web/src/lib/auth.ts.hbs +23 -0
  42. package/templates/web/src/lib/utils.ts.hbs +6 -0
  43. package/templates/web/tsconfig.json.hbs +23 -0
  44. package/tsconfig.json +15 -0
@@ -0,0 +1,7 @@
1
+ import GitHub from '@auth/core/providers/github'
2
+ import Google from '@auth/core/providers/google'
3
+ import { convexAuth } from '@convex-dev/auth/server'
4
+
5
+ export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
6
+ providers: [GitHub, Google],
7
+ })
@@ -0,0 +1,8 @@
1
+ import { httpRouter } from 'convex/server'
2
+ import { auth } from './auth'
3
+
4
+ const http = httpRouter()
5
+
6
+ auth.addHttpRoutes(http)
7
+
8
+ export default http
@@ -0,0 +1,15 @@
1
+ import { defineSchema, defineTable } from 'convex/server'
2
+ import { authTables } from '@convex-dev/auth/server'
3
+ import { v } from 'convex/values'
4
+
5
+ export default defineSchema({
6
+ ...authTables,
7
+ // Add your custom tables here
8
+ // Example:
9
+ // posts: defineTable({
10
+ // title: v.string(),
11
+ // content: v.string(),
12
+ // authorId: v.id('users'),
13
+ // createdAt: v.number(),
14
+ // }).index('by_author', ['authorId']),
15
+ })
@@ -0,0 +1,13 @@
1
+ import { query } from './_generated/server'
2
+ import { auth } from './auth'
3
+
4
+ export const current = query({
5
+ args: {},
6
+ handler: async (ctx) => {
7
+ const userId = await auth.getUserId(ctx)
8
+ if (!userId) return null
9
+
10
+ const user = await ctx.db.get(userId)
11
+ return user
12
+ },
13
+ })
@@ -0,0 +1,17 @@
1
+ 'use client'
2
+
3
+ import posthog from 'posthog-js'
4
+ import { PostHogProvider as PHProvider } from 'posthog-js/react'
5
+ import { useEffect } from 'react'
6
+
7
+ export function PostHogProvider({ children }: { children: React.ReactNode }) {
8
+ useEffect(() => {
9
+ posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
10
+ api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://app.posthog.com',
11
+ person_profiles: 'identified_only',
12
+ capture_pageview: false, // We capture pageviews manually
13
+ })
14
+ }, [])
15
+
16
+ return <PHProvider client={posthog}>{children}</PHProvider>
17
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "node scripts/dev.mjs",
7
+ "dev:turbo": "turbo dev",
8
+ "build": "turbo build",
9
+ "lint": "turbo lint",
10
+ "lint:fix": "turbo lint:fix",
11
+ "format": "turbo format",
12
+ "typecheck": "turbo typecheck",
13
+ "test": "turbo test",
14
+ "test:e2e": "turbo test:e2e",
15
+ "clean": "turbo clean && rm -rf node_modules",
16
+ "prepare": "husky",
17
+ "setup:convex": "node apps/web/scripts/setup-convex.mjs"
18
+ },
19
+ "devDependencies": {
20
+ "turbo": "^2.0.0",
21
+ "husky": "^9.0.0",
22
+ "lint-staged": "^15.0.0"
23
+ },
24
+ "packageManager": "pnpm@9.0.0",
25
+ "lint-staged": {
26
+ "*.{js,ts,jsx,tsx}": ["biome check --apply"],
27
+ "*.{json,md}": ["biome format --write"]
28
+ }
29
+ }
@@ -0,0 +1,3 @@
1
+ packages:
2
+ - "apps/*"
3
+ - "packages/*"
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "ui": "tui",
4
+ "tasks": {
5
+ "build": {
6
+ "dependsOn": ["^build"],
7
+ "inputs": ["$TURBO_DEFAULT$", ".env*"],
8
+ "outputs": [".next/**", "!.next/cache/**", "dist/**"]
9
+ },
10
+ "lint": {
11
+ "dependsOn": ["^lint"],
12
+ "inputs": ["$TURBO_DEFAULT$"]
13
+ },
14
+ "lint:fix": {
15
+ "dependsOn": ["^lint:fix"],
16
+ "inputs": ["$TURBO_DEFAULT$"]
17
+ },
18
+ "format": {
19
+ "dependsOn": ["^format"],
20
+ "inputs": ["$TURBO_DEFAULT$"]
21
+ },
22
+ "typecheck": {
23
+ "dependsOn": ["^typecheck"],
24
+ "inputs": ["$TURBO_DEFAULT$"]
25
+ },
26
+ "dev": {
27
+ "cache": false,
28
+ "persistent": true
29
+ },
30
+ "test": {
31
+ "dependsOn": ["^build"],
32
+ "inputs": ["$TURBO_DEFAULT$"]
33
+ },
34
+ "test:e2e": {
35
+ "dependsOn": ["build"],
36
+ "inputs": ["$TURBO_DEFAULT$"]
37
+ },
38
+ "clean": {
39
+ "cache": false
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3
+ "extends": ["../../biome.json"]
4
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@repo/config-biome",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "files": ["biome.json"]
6
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "incremental": true
15
+ },
16
+ "exclude": ["node_modules"]
17
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./base.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "plugins": [{ "name": "next" }]
6
+ }
7
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@repo/config-typescript",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "exports": {
6
+ "./base.json": "./base.json",
7
+ "./nextjs.json": "./nextjs.json"
8
+ },
9
+ "files": ["base.json", "nextjs.json"]
10
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "{{shadcn.componentLibrary}}-{{shadcn.styleVariant}}",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "",
9
+ "baseColor": "{{shadcn.baseColor}}",
10
+ "cssVariables": true
11
+ },
12
+ "iconLibrary": "{{shadcn.iconLibrary}}",
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ }
20
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@repo/ui",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./components/*": "./src/components/*.tsx",
10
+ "./lib/*": "./src/lib/*.ts"
11
+ },
12
+ "scripts": {
13
+ "lint": "biome check .",
14
+ "lint:fix": "biome check --write .",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@hugeicons/react": "^0.3.0",
19
+ "class-variance-authority": "^0.7.0",
20
+ "clsx": "^2.1.0",
21
+ "tailwind-merge": "^2.5.0"
22
+ },
23
+ "devDependencies": {
24
+ "@repo/config-typescript": "workspace:*",
25
+ "@types/react": "^19.0.0",
26
+ "@types/react-dom": "^19.0.0",
27
+ "react": "^19.0.0",
28
+ "tailwindcss": "^4.0.0",
29
+ "typescript": "^5.0.0"
30
+ },
31
+ "peerDependencies": {
32
+ "react": "^19.0.0"
33
+ }
34
+ }
@@ -0,0 +1,3 @@
1
+ export { cn } from './lib/utils'
2
+ // Export components as they are added
3
+ // export * from './components/ui/button'
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "paths": {
17
+ "@/*": ["./src/*"]
18
+ }
19
+ },
20
+ "include": ["src/**/*"],
21
+ "exclude": ["node_modules"]
22
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "{{shadcn.componentLibrary}}-{{shadcn.styleVariant}}",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/app/globals.css",
9
+ "baseColor": "{{shadcn.baseColor}}",
10
+ "cssVariables": true
11
+ },
12
+ "iconLibrary": "{{shadcn.iconLibrary}}",
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ }
20
+ }
@@ -0,0 +1,9 @@
1
+ import type { NextConfig } from 'next'
2
+
3
+ const nextConfig: NextConfig = {
4
+ {{#if (eq structure 'monorepo')}}
5
+ transpilePackages: ['@repo/ui', '@repo/backend'],
6
+ {{/if}}
7
+ }
8
+
9
+ export default nextConfig
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "{{#if (eq structure 'monorepo')}}@repo/web{{else}}{{projectName}}{{/if}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "{{#if (eq structure 'monorepo')}}next dev --turbopack{{else}}node scripts/dev.mjs{{/if}}",
8
+ "dev:next": "next dev --turbopack",
9
+ "build": "next build",
10
+ "start": "next start",
11
+ "lint": "biome check .",
12
+ "lint:fix": "biome check --write .",
13
+ "typecheck": "tsc --noEmit",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:e2e": "playwright test",
17
+ "setup:convex": "node scripts/setup-convex.mjs"
18
+ },
19
+ "dependencies": {
20
+ "next": "^16.0.0",
21
+ "react": "^19.0.0",
22
+ "react-dom": "^19.0.0",
23
+ "convex": "^1.25.0",
24
+ "@convex-dev/auth": "^0.0.90",
25
+ "@auth/core": "^0.37.0",
26
+ "@hugeicons/react": "^0.3.0",
27
+ "class-variance-authority": "^0.7.0",
28
+ "clsx": "^2.1.0",
29
+ "tailwind-merge": "^2.5.0",
30
+ "tw-animate-css": "^1.3.0",
31
+ "resend": "^4.0.0",
32
+ "react-email": "^3.0.0",
33
+ "@react-email/components": "^0.0.36"{{#if (eq integrations.analytics 'posthog')}},
34
+ "posthog-js": "^1.200.0",
35
+ "posthog-node": "^5.0.0"{{/if}}{{#if (eq integrations.analytics 'vercel')}},
36
+ "@vercel/analytics": "^1.4.0",
37
+ "@vercel/speed-insights": "^1.1.0"{{/if}}{{#if (eq integrations.uploads 'uploadthing')}},
38
+ "uploadthing": "^7.0.0",
39
+ "@uploadthing/react": "^7.0.0"{{/if}}{{#if (eq integrations.uploads 's3')}},
40
+ "@aws-sdk/client-s3": "^3.700.0",
41
+ "@aws-sdk/s3-request-presigner": "^3.700.0"{{/if}}{{#if (eq integrations.uploads 'vercel-blob')}},
42
+ "@vercel/blob": "^2.0.0"{{/if}}{{#if (includes addons 'rate-limiting')}},
43
+ "@arcjet/next": "^1.0.0-beta.16"{{/if}}{{#if (includes addons 'monitoring')}},
44
+ "@sentry/nextjs": "^8.0.0"{{/if}}
45
+ },
46
+ "devDependencies": {
47
+ {{#if (eq structure 'monorepo')}} "@repo/config-typescript": "workspace:*",
48
+ {{/if}} "@types/node": "^20.0.0",
49
+ "@types/react": "^19.0.0",
50
+ "@types/react-dom": "^19.0.0",
51
+ "tailwindcss": "^4.0.0",
52
+ "@tailwindcss/postcss": "^4.0.0",
53
+ "postcss": "^8.4.0",
54
+ "typescript": "^5.0.0",
55
+ "vitest": "^3.0.0",
56
+ "@vitejs/plugin-react": "^4.3.0",
57
+ "@testing-library/react": "^16.0.0",
58
+ "jsdom": "^26.0.0",
59
+ "playwright": "^1.50.0",
60
+ "@playwright/test": "^1.50.0"
61
+ }
62
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ }
@@ -0,0 +1,122 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --color-background: var(--background);
8
+ --color-foreground: var(--foreground);
9
+ --font-sans: var(--font-geist-sans);
10
+ --font-mono: var(--font-geist-mono);
11
+ --color-sidebar-ring: var(--sidebar-ring);
12
+ --color-sidebar-border: var(--sidebar-border);
13
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14
+ --color-sidebar-accent: var(--sidebar-accent);
15
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16
+ --color-sidebar-primary: var(--sidebar-primary);
17
+ --color-sidebar-foreground: var(--sidebar-foreground);
18
+ --color-sidebar: var(--sidebar);
19
+ --color-chart-5: var(--chart-5);
20
+ --color-chart-4: var(--chart-4);
21
+ --color-chart-3: var(--chart-3);
22
+ --color-chart-2: var(--chart-2);
23
+ --color-chart-1: var(--chart-1);
24
+ --color-ring: var(--ring);
25
+ --color-input: var(--input);
26
+ --color-border: var(--border);
27
+ --color-destructive: var(--destructive);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-accent: var(--accent);
30
+ --color-muted-foreground: var(--muted-foreground);
31
+ --color-muted: var(--muted);
32
+ --color-secondary-foreground: var(--secondary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-primary-foreground: var(--primary-foreground);
35
+ --color-primary: var(--primary);
36
+ --color-popover-foreground: var(--popover-foreground);
37
+ --color-popover: var(--popover);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-card: var(--card);
40
+ --radius-sm: calc(var(--radius) - 4px);
41
+ --radius-md: calc(var(--radius) - 2px);
42
+ --radius-lg: var(--radius);
43
+ --radius-xl: calc(var(--radius) + 4px);
44
+ }
45
+
46
+ :root {
47
+ --radius: 0.625rem;
48
+ --background: oklch(1 0 0);
49
+ --foreground: oklch(0.145 0 0);
50
+ --card: oklch(1 0 0);
51
+ --card-foreground: oklch(0.145 0 0);
52
+ --popover: oklch(1 0 0);
53
+ --popover-foreground: oklch(0.145 0 0);
54
+ --primary: oklch(0.205 0 0);
55
+ --primary-foreground: oklch(0.985 0 0);
56
+ --secondary: oklch(0.97 0 0);
57
+ --secondary-foreground: oklch(0.205 0 0);
58
+ --muted: oklch(0.97 0 0);
59
+ --muted-foreground: oklch(0.556 0 0);
60
+ --accent: oklch(0.97 0 0);
61
+ --accent-foreground: oklch(0.205 0 0);
62
+ --destructive: oklch(0.577 0.245 27.325);
63
+ --border: oklch(0.922 0 0);
64
+ --input: oklch(0.922 0 0);
65
+ --ring: oklch(0.708 0 0);
66
+ --chart-1: oklch(0.646 0.222 41.116);
67
+ --chart-2: oklch(0.6 0.118 184.704);
68
+ --chart-3: oklch(0.398 0.07 227.392);
69
+ --chart-4: oklch(0.828 0.189 84.429);
70
+ --chart-5: oklch(0.769 0.188 70.08);
71
+ --sidebar: oklch(0.985 0 0);
72
+ --sidebar-foreground: oklch(0.145 0 0);
73
+ --sidebar-primary: oklch(0.205 0 0);
74
+ --sidebar-primary-foreground: oklch(0.985 0 0);
75
+ --sidebar-accent: oklch(0.97 0 0);
76
+ --sidebar-accent-foreground: oklch(0.205 0 0);
77
+ --sidebar-border: oklch(0.922 0 0);
78
+ --sidebar-ring: oklch(0.708 0 0);
79
+ }
80
+
81
+ .dark {
82
+ --background: oklch(0.145 0 0);
83
+ --foreground: oklch(0.985 0 0);
84
+ --card: oklch(0.205 0 0);
85
+ --card-foreground: oklch(0.985 0 0);
86
+ --popover: oklch(0.205 0 0);
87
+ --popover-foreground: oklch(0.985 0 0);
88
+ --primary: oklch(0.922 0 0);
89
+ --primary-foreground: oklch(0.205 0 0);
90
+ --secondary: oklch(0.269 0 0);
91
+ --secondary-foreground: oklch(0.985 0 0);
92
+ --muted: oklch(0.269 0 0);
93
+ --muted-foreground: oklch(0.708 0 0);
94
+ --accent: oklch(0.269 0 0);
95
+ --accent-foreground: oklch(0.985 0 0);
96
+ --destructive: oklch(0.704 0.191 22.216);
97
+ --border: oklch(1 0 0 / 10%);
98
+ --input: oklch(1 0 0 / 15%);
99
+ --ring: oklch(0.556 0 0);
100
+ --chart-1: oklch(0.488 0.243 264.376);
101
+ --chart-2: oklch(0.696 0.17 162.48);
102
+ --chart-3: oklch(0.769 0.188 70.08);
103
+ --chart-4: oklch(0.627 0.265 303.9);
104
+ --chart-5: oklch(0.645 0.246 16.439);
105
+ --sidebar: oklch(0.205 0 0);
106
+ --sidebar-foreground: oklch(0.985 0 0);
107
+ --sidebar-primary: oklch(0.488 0.243 264.376);
108
+ --sidebar-primary-foreground: oklch(0.985 0 0);
109
+ --sidebar-accent: oklch(0.269 0 0);
110
+ --sidebar-accent-foreground: oklch(0.985 0 0);
111
+ --sidebar-border: oklch(1 0 0 / 10%);
112
+ --sidebar-ring: oklch(0.556 0 0);
113
+ }
114
+
115
+ @layer base {
116
+ * {
117
+ @apply border-border outline-ring/50;
118
+ }
119
+ body {
120
+ @apply bg-background text-foreground;
121
+ }
122
+ }
@@ -0,0 +1,55 @@
1
+ import type { Metadata } from 'next'
2
+ import { Geist, Geist_Mono } from 'next/font/google'
3
+ import { ConvexAuthNextjsServerProvider } from '@convex-dev/auth/nextjs/server'
4
+ import { ConvexClientProvider } from '@/components/providers/convex-provider'
5
+ {{#if (eq integrations.analytics 'posthog')}}
6
+ import { PostHogProvider } from '@/components/providers/posthog-provider'
7
+ {{/if}}
8
+ {{#if (eq integrations.analytics 'vercel')}}
9
+ import { Analytics } from '@vercel/analytics/react'
10
+ import { SpeedInsights } from '@vercel/speed-insights/next'
11
+ {{/if}}
12
+ import './globals.css'
13
+
14
+ const geistSans = Geist({
15
+ variable: '--font-geist-sans',
16
+ subsets: ['latin'],
17
+ })
18
+
19
+ const geistMono = Geist_Mono({
20
+ variable: '--font-geist-mono',
21
+ subsets: ['latin'],
22
+ })
23
+
24
+ export const metadata: Metadata = {
25
+ title: '{{projectName}}',
26
+ description: 'Built with create-kofi-stack',
27
+ }
28
+
29
+ export default function RootLayout({
30
+ children,
31
+ }: {
32
+ children: React.ReactNode
33
+ }) {
34
+ return (
35
+ <ConvexAuthNextjsServerProvider>
36
+ <html lang="en" suppressHydrationWarning>
37
+ <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
38
+ <ConvexClientProvider>
39
+ {{#if (eq integrations.analytics 'posthog')}}
40
+ <PostHogProvider>
41
+ {children}
42
+ </PostHogProvider>
43
+ {{else}}
44
+ {children}
45
+ {{/if}}
46
+ </ConvexClientProvider>
47
+ {{#if (eq integrations.analytics 'vercel')}}
48
+ <Analytics />
49
+ <SpeedInsights />
50
+ {{/if}}
51
+ </body>
52
+ </html>
53
+ </ConvexAuthNextjsServerProvider>
54
+ )
55
+ }
@@ -0,0 +1,74 @@
1
+ 'use client'
2
+
3
+ import { useAuth } from '@/lib/auth'
4
+
5
+ export default function HomePage() {
6
+ const { isAuthenticated, isLoading, signIn, signOut, user } = useAuth()
7
+
8
+ if (isLoading) {
9
+ return (
10
+ <main className="min-h-screen flex items-center justify-center">
11
+ <div className="animate-pulse">Loading...</div>
12
+ </main>
13
+ )
14
+ }
15
+
16
+ return (
17
+ <main className="min-h-screen flex flex-col items-center justify-center p-8">
18
+ <div className="max-w-2xl text-center space-y-8">
19
+ <h1 className="text-4xl font-bold">{{projectName}}</h1>
20
+ <p className="text-xl text-muted-foreground">
21
+ Built with Next.js, Convex, Better-Auth, and shadcn/ui
22
+ </p>
23
+
24
+ {isAuthenticated ? (
25
+ <div className="space-y-4">
26
+ <p className="text-lg">
27
+ Welcome, <span className="font-semibold">{user?.name || user?.email}</span>!
28
+ </p>
29
+ <button
30
+ onClick={() => signOut()}
31
+ className="px-6 py-2 bg-secondary text-secondary-foreground rounded-lg hover:opacity-90 transition"
32
+ >
33
+ Sign Out
34
+ </button>
35
+ </div>
36
+ ) : (
37
+ <div className="space-y-4">
38
+ <p className="text-muted-foreground">
39
+ Sign in to get started
40
+ </p>
41
+ <div className="flex gap-4 justify-center">
42
+ <button
43
+ onClick={() => signIn('github')}
44
+ className="px-6 py-2 bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition"
45
+ >
46
+ Sign in with GitHub
47
+ </button>
48
+ <button
49
+ onClick={() => signIn('google')}
50
+ className="px-6 py-2 bg-secondary text-secondary-foreground rounded-lg hover:opacity-90 transition"
51
+ >
52
+ Sign in with Google
53
+ </button>
54
+ </div>
55
+ </div>
56
+ )}
57
+
58
+ <div className="pt-8 border-t border-border">
59
+ <p className="text-sm text-muted-foreground">
60
+ Created with{' '}
61
+ <a
62
+ href="https://github.com/theodenanyoh11/create-kofi-stack"
63
+ className="text-primary hover:underline"
64
+ target="_blank"
65
+ rel="noopener noreferrer"
66
+ >
67
+ create-kofi-stack
68
+ </a>
69
+ </p>
70
+ </div>
71
+ </div>
72
+ </main>
73
+ )
74
+ }
@@ -0,0 +1,18 @@
1
+ 'use client'
2
+
3
+ import { ConvexAuthNextjsProvider } from '@convex-dev/auth/nextjs'
4
+ import { ConvexReactClient } from 'convex/react'
5
+
6
+ const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!)
7
+
8
+ export function ConvexClientProvider({
9
+ children,
10
+ }: {
11
+ children: React.ReactNode
12
+ }) {
13
+ return (
14
+ <ConvexAuthNextjsProvider client={convex}>
15
+ {children}
16
+ </ConvexAuthNextjsProvider>
17
+ )
18
+ }