create-rykira-app 1.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 (143) hide show
  1. package/README.md +50 -0
  2. package/bin/cli.js +149 -0
  3. package/deployment.md +168 -0
  4. package/how-to-deploy-package.md +132 -0
  5. package/package.json +23 -0
  6. package/starter-usage.md +132 -0
  7. package/template/.dockerignore +11 -0
  8. package/template/.env.example +20 -0
  9. package/template/.eslintrc.js +13 -0
  10. package/template/.github/workflows/ci.yml +60 -0
  11. package/template/.prettierignore +7 -0
  12. package/template/.prettierrc +11 -0
  13. package/template/README.md +21 -0
  14. package/template/apps/admin/app/favicon.ico +0 -0
  15. package/template/apps/admin/app/layout.tsx +30 -0
  16. package/template/apps/admin/app/page.tsx +19 -0
  17. package/template/apps/admin/components/.gitkeep +0 -0
  18. package/template/apps/admin/components/theme-provider.tsx +71 -0
  19. package/template/apps/admin/components.json +23 -0
  20. package/template/apps/admin/eslint.config.js +4 -0
  21. package/template/apps/admin/hooks/.gitkeep +0 -0
  22. package/template/apps/admin/lib/.gitkeep +0 -0
  23. package/template/apps/admin/next-env.d.ts +6 -0
  24. package/template/apps/admin/next.config.mjs +6 -0
  25. package/template/apps/admin/nixpacks.toml +8 -0
  26. package/template/apps/admin/package.json +32 -0
  27. package/template/apps/admin/postcss.config.mjs +1 -0
  28. package/template/apps/admin/tsconfig.json +23 -0
  29. package/template/apps/api/nixpacks.toml +8 -0
  30. package/template/apps/api/package.json +32 -0
  31. package/template/apps/api/src/index.ts +11 -0
  32. package/template/apps/api/src/server.ts +21 -0
  33. package/template/apps/api/tsconfig.json +18 -0
  34. package/template/apps/web/app/favicon.ico +0 -0
  35. package/template/apps/web/app/layout.tsx +30 -0
  36. package/template/apps/web/app/page.tsx +19 -0
  37. package/template/apps/web/components/.gitkeep +0 -0
  38. package/template/apps/web/components/theme-provider.tsx +71 -0
  39. package/template/apps/web/components.json +23 -0
  40. package/template/apps/web/eslint.config.js +4 -0
  41. package/template/apps/web/hooks/.gitkeep +0 -0
  42. package/template/apps/web/lib/.gitkeep +0 -0
  43. package/template/apps/web/next-env.d.ts +6 -0
  44. package/template/apps/web/next.config.mjs +6 -0
  45. package/template/apps/web/nixpacks.toml +8 -0
  46. package/template/apps/web/package.json +32 -0
  47. package/template/apps/web/postcss.config.mjs +1 -0
  48. package/template/apps/web/tsconfig.json +23 -0
  49. package/template/infrastructure/docker/Dockerfile.admin +36 -0
  50. package/template/infrastructure/docker/Dockerfile.api +36 -0
  51. package/template/infrastructure/docker/Dockerfile.web +48 -0
  52. package/template/infrastructure/docker/compose.dev.yml +50 -0
  53. package/template/infrastructure/docker/compose.prod.yml +119 -0
  54. package/template/infrastructure/scripts/deploy.sh +3 -0
  55. package/template/infrastructure/scripts/dev.sh +5 -0
  56. package/template/infrastructure/scripts/init-traefik.sh +10 -0
  57. package/template/infrastructure/scripts/setup-server.sh +25 -0
  58. package/template/infrastructure/scripts/update.sh +7 -0
  59. package/template/infrastructure/traefik/acme.json +0 -0
  60. package/template/infrastructure/traefik/dynamic.yml +23 -0
  61. package/template/infrastructure/traefik/traefik.yml +26 -0
  62. package/template/package.json +25 -0
  63. package/template/packages/eslint-config/README.md +3 -0
  64. package/template/packages/eslint-config/base.js +32 -0
  65. package/template/packages/eslint-config/next.js +51 -0
  66. package/template/packages/eslint-config/package.json +26 -0
  67. package/template/packages/eslint-config/react-internal.js +41 -0
  68. package/template/packages/typescript-config/README.md +3 -0
  69. package/template/packages/typescript-config/base.json +20 -0
  70. package/template/packages/typescript-config/nextjs.json +13 -0
  71. package/template/packages/typescript-config/package.json +9 -0
  72. package/template/packages/typescript-config/react-library.json +8 -0
  73. package/template/packages/ui/components.json +23 -0
  74. package/template/packages/ui/eslint.config.js +4 -0
  75. package/template/packages/ui/package.json +52 -0
  76. package/template/packages/ui/postcss.config.mjs +6 -0
  77. package/template/packages/ui/src/components/.gitkeep +0 -0
  78. package/template/packages/ui/src/components/accordion.tsx +74 -0
  79. package/template/packages/ui/src/components/alert-dialog.tsx +187 -0
  80. package/template/packages/ui/src/components/alert.tsx +76 -0
  81. package/template/packages/ui/src/components/aspect-ratio.tsx +22 -0
  82. package/template/packages/ui/src/components/avatar.tsx +109 -0
  83. package/template/packages/ui/src/components/badge.tsx +52 -0
  84. package/template/packages/ui/src/components/breadcrumb.tsx +125 -0
  85. package/template/packages/ui/src/components/button-group.tsx +87 -0
  86. package/template/packages/ui/src/components/button.tsx +60 -0
  87. package/template/packages/ui/src/components/calendar.tsx +221 -0
  88. package/template/packages/ui/src/components/card.tsx +103 -0
  89. package/template/packages/ui/src/components/carousel.tsx +242 -0
  90. package/template/packages/ui/src/components/chart.tsx +356 -0
  91. package/template/packages/ui/src/components/checkbox.tsx +29 -0
  92. package/template/packages/ui/src/components/collapsible.tsx +21 -0
  93. package/template/packages/ui/src/components/combobox.tsx +297 -0
  94. package/template/packages/ui/src/components/command.tsx +196 -0
  95. package/template/packages/ui/src/components/context-menu.tsx +271 -0
  96. package/template/packages/ui/src/components/dialog.tsx +157 -0
  97. package/template/packages/ui/src/components/direction.tsx +6 -0
  98. package/template/packages/ui/src/components/drawer.tsx +131 -0
  99. package/template/packages/ui/src/components/dropdown-menu.tsx +268 -0
  100. package/template/packages/ui/src/components/empty.tsx +101 -0
  101. package/template/packages/ui/src/components/field.tsx +238 -0
  102. package/template/packages/ui/src/components/hover-card.tsx +51 -0
  103. package/template/packages/ui/src/components/input-group.tsx +158 -0
  104. package/template/packages/ui/src/components/input-otp.tsx +87 -0
  105. package/template/packages/ui/src/components/input.tsx +20 -0
  106. package/template/packages/ui/src/components/item.tsx +201 -0
  107. package/template/packages/ui/src/components/kbd.tsx +26 -0
  108. package/template/packages/ui/src/components/label.tsx +20 -0
  109. package/template/packages/ui/src/components/menubar.tsx +280 -0
  110. package/template/packages/ui/src/components/native-select.tsx +52 -0
  111. package/template/packages/ui/src/components/navigation-menu.tsx +168 -0
  112. package/template/packages/ui/src/components/pagination.tsx +130 -0
  113. package/template/packages/ui/src/components/popover.tsx +90 -0
  114. package/template/packages/ui/src/components/progress.tsx +83 -0
  115. package/template/packages/ui/src/components/radio-group.tsx +38 -0
  116. package/template/packages/ui/src/components/resizable.tsx +50 -0
  117. package/template/packages/ui/src/components/scroll-area.tsx +55 -0
  118. package/template/packages/ui/src/components/select.tsx +201 -0
  119. package/template/packages/ui/src/components/separator.tsx +25 -0
  120. package/template/packages/ui/src/components/sheet.tsx +135 -0
  121. package/template/packages/ui/src/components/sidebar.tsx +723 -0
  122. package/template/packages/ui/src/components/skeleton.tsx +13 -0
  123. package/template/packages/ui/src/components/slider.tsx +59 -0
  124. package/template/packages/ui/src/components/sonner.tsx +49 -0
  125. package/template/packages/ui/src/components/spinner.tsx +10 -0
  126. package/template/packages/ui/src/components/switch.tsx +32 -0
  127. package/template/packages/ui/src/components/table.tsx +116 -0
  128. package/template/packages/ui/src/components/tabs.tsx +82 -0
  129. package/template/packages/ui/src/components/textarea.tsx +18 -0
  130. package/template/packages/ui/src/components/toggle-group.tsx +89 -0
  131. package/template/packages/ui/src/components/toggle.tsx +44 -0
  132. package/template/packages/ui/src/components/tooltip.tsx +66 -0
  133. package/template/packages/ui/src/hooks/.gitkeep +0 -0
  134. package/template/packages/ui/src/hooks/use-mobile.ts +19 -0
  135. package/template/packages/ui/src/lib/.gitkeep +0 -0
  136. package/template/packages/ui/src/lib/utils.ts +6 -0
  137. package/template/packages/ui/src/styles/globals.css +128 -0
  138. package/template/packages/ui/tsconfig.json +11 -0
  139. package/template/packages/ui/tsconfig.lint.json +8 -0
  140. package/template/pnpm-lock.yaml +9103 -0
  141. package/template/pnpm-workspace.yaml +3 -0
  142. package/template/tsconfig.json +4 -0
  143. package/template/turbo.json +24 -0
@@ -0,0 +1,60 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ pull_request:
7
+ types: [opened, synchronize]
8
+
9
+ jobs:
10
+ build:
11
+ name: Build and Test
12
+ timeout-minutes: 15
13
+ runs-on: ubuntu-latest
14
+ # To use Remote Caching, uncomment the next lines
15
+ # env:
16
+ # TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
17
+ # TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
18
+
19
+ steps:
20
+ - name: Check out code
21
+ uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 2
24
+
25
+ - name: Setup Node.js environment
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: 20
29
+ cache: 'pnpm'
30
+
31
+ - uses: pnpm/action-setup@v3
32
+ name: Install pnpm
33
+ with:
34
+ version: 9
35
+ run_install: false
36
+
37
+ - name: Get pnpm store directory
38
+ shell: bash
39
+ run: |
40
+ echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
41
+
42
+ - uses: actions/cache@v4
43
+ name: Setup pnpm cache
44
+ with:
45
+ path: ${{ env.STORE_PATH }}
46
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
47
+ restore-keys: |
48
+ ${{ runner.os }}-pnpm-store-
49
+
50
+ - name: Install dependencies
51
+ run: pnpm install
52
+
53
+ - name: Build
54
+ run: pnpm build
55
+
56
+ - name: Lint
57
+ run: pnpm lint
58
+
59
+ - name: Typecheck
60
+ run: pnpm typecheck
@@ -0,0 +1,7 @@
1
+ dist/
2
+ node_modules/
3
+ .next/
4
+ .turbo/
5
+ coverage/
6
+ pnpm-lock.yaml
7
+ .pnpm-store/
@@ -0,0 +1,11 @@
1
+ {
2
+ "endOfLine": "lf",
3
+ "semi": false,
4
+ "singleQuote": false,
5
+ "tabWidth": 2,
6
+ "trailingComma": "es5",
7
+ "printWidth": 80,
8
+ "plugins": ["prettier-plugin-tailwindcss"],
9
+ "tailwindStylesheet": "packages/ui/src/styles/globals.css",
10
+ "tailwindFunctions": ["cn", "cva"]
11
+ }
@@ -0,0 +1,21 @@
1
+ # shadcn/ui monorepo template
2
+
3
+ This is a Next.js monorepo template with shadcn/ui.
4
+
5
+ ## Adding components
6
+
7
+ To add components to your app, run the following command at the root of your `web` app:
8
+
9
+ ```bash
10
+ pnpm dlx shadcn@latest add button -c apps/web
11
+ ```
12
+
13
+ This will place the ui components in the `packages/ui/src/components` directory.
14
+
15
+ ## Using components
16
+
17
+ To use the components in your app, import them from the `ui` package.
18
+
19
+ ```tsx
20
+ import { Button } from "@workspace/ui/components/button";
21
+ ```
@@ -0,0 +1,30 @@
1
+ import { Geist, Geist_Mono, Inter } from "next/font/google"
2
+
3
+ import "@workspace/ui/globals.css"
4
+ import { ThemeProvider } from "@/components/theme-provider"
5
+ import { cn } from "@workspace/ui/lib/utils";
6
+
7
+ const inter = Inter({subsets:['latin'],variable:'--font-sans'})
8
+
9
+ const fontMono = Geist_Mono({
10
+ subsets: ["latin"],
11
+ variable: "--font-mono",
12
+ })
13
+
14
+ export default function RootLayout({
15
+ children,
16
+ }: Readonly<{
17
+ children: React.ReactNode
18
+ }>) {
19
+ return (
20
+ <html
21
+ lang="en"
22
+ suppressHydrationWarning
23
+ className={cn("antialiased", fontMono.variable, "font-sans", inter.variable)}
24
+ >
25
+ <body>
26
+ <ThemeProvider>{children}</ThemeProvider>
27
+ </body>
28
+ </html>
29
+ )
30
+ }
@@ -0,0 +1,19 @@
1
+ import { Button } from "@workspace/ui/components/button"
2
+
3
+ export default function Page() {
4
+ return (
5
+ <div className="flex min-h-svh p-6">
6
+ <div className="flex max-w-md min-w-0 flex-col gap-4 text-sm leading-loose">
7
+ <div>
8
+ <h1 className="font-medium">Project ready!</h1>
9
+ <p>You may now add components and start building.</p>
10
+ <p>We&apos;ve already added the button component for you.</p>
11
+ <Button className="mt-2">Button</Button>
12
+ </div>
13
+ <div className="text-muted-foreground font-mono text-xs">
14
+ (Press <kbd>d</kbd> to toggle dark mode)
15
+ </div>
16
+ </div>
17
+ </div>
18
+ )
19
+ }
File without changes
@@ -0,0 +1,71 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"
5
+
6
+ function ThemeProvider({
7
+ children,
8
+ ...props
9
+ }: React.ComponentProps<typeof NextThemesProvider>) {
10
+ return (
11
+ <NextThemesProvider
12
+ attribute="class"
13
+ defaultTheme="system"
14
+ enableSystem
15
+ disableTransitionOnChange
16
+ {...props}
17
+ >
18
+ <ThemeHotkey />
19
+ {children}
20
+ </NextThemesProvider>
21
+ )
22
+ }
23
+
24
+ function isTypingTarget(target: EventTarget | null) {
25
+ if (!(target instanceof HTMLElement)) {
26
+ return false
27
+ }
28
+
29
+ return (
30
+ target.isContentEditable ||
31
+ target.tagName === "INPUT" ||
32
+ target.tagName === "TEXTAREA" ||
33
+ target.tagName === "SELECT"
34
+ )
35
+ }
36
+
37
+ function ThemeHotkey() {
38
+ const { resolvedTheme, setTheme } = useTheme()
39
+
40
+ React.useEffect(() => {
41
+ function onKeyDown(event: KeyboardEvent) {
42
+ if (event.defaultPrevented || event.repeat) {
43
+ return
44
+ }
45
+
46
+ if (event.metaKey || event.ctrlKey || event.altKey) {
47
+ return
48
+ }
49
+
50
+ if (event.key.toLowerCase() !== "d") {
51
+ return
52
+ }
53
+
54
+ if (isTypingTarget(event.target)) {
55
+ return
56
+ }
57
+
58
+ setTheme(resolvedTheme === "dark" ? "light" : "dark")
59
+ }
60
+
61
+ window.addEventListener("keydown", onKeyDown)
62
+
63
+ return () => {
64
+ window.removeEventListener("keydown", onKeyDown)
65
+ }
66
+ }, [resolvedTheme, setTheme])
67
+
68
+ return null
69
+ }
70
+
71
+ export { ThemeProvider }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "base-vega",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "../../packages/ui/src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true
11
+ },
12
+ "iconLibrary": "lucide",
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "hooks": "@/hooks",
16
+ "lib": "@/lib",
17
+ "utils": "@workspace/ui/lib/utils",
18
+ "ui": "@workspace/ui/components"
19
+ },
20
+ "rtl": false,
21
+ "menuColor": "default",
22
+ "menuAccent": "subtle"
23
+ }
@@ -0,0 +1,4 @@
1
+ import { nextJsConfig } from "@workspace/eslint-config/next-js"
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default nextJsConfig
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/dev/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,6 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ transpilePackages: ["@workspace/ui"],
4
+ }
5
+
6
+ export default nextConfig
@@ -0,0 +1,8 @@
1
+ [phases.setup]
2
+ nixPkgs = ['nodejs', 'pnpm']
3
+
4
+ [phases.build]
5
+ cmds = ['pnpm install', 'pnpm turbo build --filter=admin...']
6
+
7
+ [start]
8
+ cmd = 'pnpm start:admin'
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "admin",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint",
11
+ "format": "prettier --write \"**/*.{ts,tsx}\"",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "dependencies": {
15
+ "@workspace/ui": "workspace:*",
16
+ "lucide-react": "^0.577.0",
17
+ "next": "16.1.6",
18
+ "next-themes": "^0.4.6",
19
+ "react": "^19.2.4",
20
+ "react-dom": "^19.2.4"
21
+ },
22
+ "devDependencies": {
23
+ "@tailwindcss/postcss": "^4.1.18",
24
+ "@types/node": "^25.1.0",
25
+ "@types/react": "^19.2.10",
26
+ "@types/react-dom": "^19.2.3",
27
+ "@workspace/eslint-config": "workspace:^",
28
+ "@workspace/typescript-config": "workspace:*",
29
+ "eslint": "^9.39.2",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }
@@ -0,0 +1 @@
1
+ export { default } from "@workspace/ui/postcss.config";
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "@workspace/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "@/*": ["./*"],
7
+ "@workspace/ui/*": ["../../packages/ui/src/*"]
8
+ },
9
+ "plugins": [
10
+ {
11
+ "name": "next"
12
+ }
13
+ ]
14
+ },
15
+ "include": [
16
+ "next-env.d.ts",
17
+ "next.config.ts",
18
+ "**/*.ts",
19
+ "**/*.tsx",
20
+ ".next/types/**/*.ts"
21
+ ],
22
+ "exclude": ["node_modules"]
23
+ }
@@ -0,0 +1,8 @@
1
+ [phases.setup]
2
+ nixPkgs = ['nodejs', 'pnpm']
3
+
4
+ [phases.build]
5
+ cmds = ['pnpm install', 'pnpm turbo build --filter=api...']
6
+
7
+ [start]
8
+ cmd = 'node apps/api/dist/index.js'
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "api",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "scripts": {
6
+ "dev": "tsx watch ./src/index.ts",
7
+ "build": "tsup",
8
+ "start": "node dist/index.js",
9
+ "lint": "eslint . --ext .ts,.tsx",
10
+ "lint:fix": "eslint . --ext .ts,.tsx --fix",
11
+ "typecheck": "tsc --noEmit",
12
+ "seed:permissions": "tsx src/scripts/seed-permissions.ts"
13
+ },
14
+ "devDependencies": {
15
+ "@types/cors": "^2.8.19",
16
+ "@types/express": "^5.0.6",
17
+ "@types/multer": "^2.1.0",
18
+ "@types/node": "^25.1.0",
19
+ "@workspace/eslint-config": "workspace:^",
20
+ "@workspace/typescript-config": "workspace:*",
21
+ "tsup": "^8.5.1",
22
+ "typescript": "^5.9.3"
23
+ },
24
+ "dependencies": {
25
+ "cors": "^2.8.6",
26
+ "dotenv": "^17.3.1",
27
+ "express": "^5.2.1",
28
+ "multer": "^2.1.1",
29
+ "pino": "^10.3.1",
30
+ "zod": "^3.25.76"
31
+ }
32
+ }
@@ -0,0 +1,11 @@
1
+ import { createServer } from "./server"
2
+ import dotenv from "dotenv"
3
+
4
+ dotenv.config()
5
+
6
+ const port = process.env.PORT || 3000
7
+ const server = createServer()
8
+
9
+ server.listen(port, () => {
10
+ console.log(`Server is running on port ${port}`)
11
+ })
@@ -0,0 +1,21 @@
1
+ import express, { type Express, type Request, type Response } from "express"
2
+ import cors from "cors"
3
+
4
+ export const createServer = (): Express => {
5
+ const app = express()
6
+
7
+ app.use(express.json())
8
+ app.use(express.urlencoded({ extended: true }))
9
+ app.use(cors())
10
+
11
+ app.get("/health", (req: Request, res: Response) => {
12
+ res.status(200).json({
13
+ status: "ok",
14
+ service: "api",
15
+ uptime: process.uptime(),
16
+ timestamp: new Date().toISOString(),
17
+ })
18
+ })
19
+
20
+ return app
21
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": "@workspace/typescript-config/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "module": "ESNext",
7
+ "target": "ES2020",
8
+ "moduleResolution": "bundler",
9
+ "allowSyntheticDefaultImports": true,
10
+ "esModuleInterop": true,
11
+ "baseUrl": ".",
12
+ "paths": {
13
+ "@/*": ["./src/*"]
14
+ }
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }
@@ -0,0 +1,30 @@
1
+ import { Geist, Geist_Mono, Inter } from "next/font/google"
2
+
3
+ import "@workspace/ui/globals.css"
4
+ import { ThemeProvider } from "@/components/theme-provider"
5
+ import { cn } from "@workspace/ui/lib/utils";
6
+
7
+ const inter = Inter({subsets:['latin'],variable:'--font-sans'})
8
+
9
+ const fontMono = Geist_Mono({
10
+ subsets: ["latin"],
11
+ variable: "--font-mono",
12
+ })
13
+
14
+ export default function RootLayout({
15
+ children,
16
+ }: Readonly<{
17
+ children: React.ReactNode
18
+ }>) {
19
+ return (
20
+ <html
21
+ lang="en"
22
+ suppressHydrationWarning
23
+ className={cn("antialiased", fontMono.variable, "font-sans", inter.variable)}
24
+ >
25
+ <body>
26
+ <ThemeProvider>{children}</ThemeProvider>
27
+ </body>
28
+ </html>
29
+ )
30
+ }
@@ -0,0 +1,19 @@
1
+ import { Button } from "@workspace/ui/components/button"
2
+
3
+ export default function Page() {
4
+ return (
5
+ <div className="flex min-h-svh p-6">
6
+ <div className="flex max-w-md min-w-0 flex-col gap-4 text-sm leading-loose">
7
+ <div>
8
+ <h1 className="font-medium">Project ready!</h1>
9
+ <p>You may now add components and start building.</p>
10
+ <p>We&apos;ve already added the button component for you.</p>
11
+ <Button className="mt-2">Button</Button>
12
+ </div>
13
+ <div className="text-muted-foreground font-mono text-xs">
14
+ (Press <kbd>d</kbd> to toggle dark mode)
15
+ </div>
16
+ </div>
17
+ </div>
18
+ )
19
+ }
File without changes
@@ -0,0 +1,71 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"
5
+
6
+ function ThemeProvider({
7
+ children,
8
+ ...props
9
+ }: React.ComponentProps<typeof NextThemesProvider>) {
10
+ return (
11
+ <NextThemesProvider
12
+ attribute="class"
13
+ defaultTheme="system"
14
+ enableSystem
15
+ disableTransitionOnChange
16
+ {...props}
17
+ >
18
+ <ThemeHotkey />
19
+ {children}
20
+ </NextThemesProvider>
21
+ )
22
+ }
23
+
24
+ function isTypingTarget(target: EventTarget | null) {
25
+ if (!(target instanceof HTMLElement)) {
26
+ return false
27
+ }
28
+
29
+ return (
30
+ target.isContentEditable ||
31
+ target.tagName === "INPUT" ||
32
+ target.tagName === "TEXTAREA" ||
33
+ target.tagName === "SELECT"
34
+ )
35
+ }
36
+
37
+ function ThemeHotkey() {
38
+ const { resolvedTheme, setTheme } = useTheme()
39
+
40
+ React.useEffect(() => {
41
+ function onKeyDown(event: KeyboardEvent) {
42
+ if (event.defaultPrevented || event.repeat) {
43
+ return
44
+ }
45
+
46
+ if (event.metaKey || event.ctrlKey || event.altKey) {
47
+ return
48
+ }
49
+
50
+ if (event.key.toLowerCase() !== "d") {
51
+ return
52
+ }
53
+
54
+ if (isTypingTarget(event.target)) {
55
+ return
56
+ }
57
+
58
+ setTheme(resolvedTheme === "dark" ? "light" : "dark")
59
+ }
60
+
61
+ window.addEventListener("keydown", onKeyDown)
62
+
63
+ return () => {
64
+ window.removeEventListener("keydown", onKeyDown)
65
+ }
66
+ }, [resolvedTheme, setTheme])
67
+
68
+ return null
69
+ }
70
+
71
+ export { ThemeProvider }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "base-vega",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "../../packages/ui/src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true
11
+ },
12
+ "iconLibrary": "lucide",
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "hooks": "@/hooks",
16
+ "lib": "@/lib",
17
+ "utils": "@workspace/ui/lib/utils",
18
+ "ui": "@workspace/ui/components"
19
+ },
20
+ "rtl": false,
21
+ "menuColor": "default",
22
+ "menuAccent": "subtle"
23
+ }
@@ -0,0 +1,4 @@
1
+ import { nextJsConfig } from "@workspace/eslint-config/next-js"
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default nextJsConfig
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/dev/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,6 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ transpilePackages: ["@workspace/ui"],
4
+ }
5
+
6
+ export default nextConfig
@@ -0,0 +1,8 @@
1
+ [phases.setup]
2
+ nixPkgs = ['nodejs', 'pnpm']
3
+
4
+ [phases.build]
5
+ cmds = ['pnpm install', 'pnpm turbo build --filter=web...']
6
+
7
+ [start]
8
+ cmd = 'pnpm start:web'
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "web",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint",
11
+ "format": "prettier --write \"**/*.{ts,tsx}\"",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "dependencies": {
15
+ "@workspace/ui": "workspace:*",
16
+ "lucide-react": "^0.577.0",
17
+ "next": "16.1.6",
18
+ "next-themes": "^0.4.6",
19
+ "react": "^19.2.4",
20
+ "react-dom": "^19.2.4"
21
+ },
22
+ "devDependencies": {
23
+ "@tailwindcss/postcss": "^4.1.18",
24
+ "@types/node": "^25.1.0",
25
+ "@types/react": "^19.2.10",
26
+ "@types/react-dom": "^19.2.3",
27
+ "@workspace/eslint-config": "workspace:^",
28
+ "@workspace/typescript-config": "workspace:*",
29
+ "eslint": "^9.39.2",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }