create-nene 0.2.0 → 0.3.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.
- package/dist/index.js +118 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/.cursor/rules/frontend-patterns.mdc +82 -0
- package/templates/default/.cursor/rules/nene-architecture.mdc +34 -4
- package/templates/default/.cursor/rules/task-management.mdc +29 -18
- package/templates/default/apps/api/src/config/configuration.ts +1 -1
- package/templates/default/apps/web/src/app/globals.css +52 -9
- package/templates/default/apps/web/src/app/layout.tsx +20 -3
- package/templates/default/apps/web/src/app/page.tsx +344 -44
- package/templates/default/apps/web/tailwind.config.ts +12 -1
- package/templates/default/docs/kanban/DOING/_gitkeep +0 -0
- package/templates/default/docs/kanban/DONE/01-project-setup.md +24 -0
- package/templates/default/docs/kanban/README.md +60 -0
- package/templates/default/docs/kanban/TODO/01-database-integration.md +24 -0
- package/templates/default/docs/{ARCHITECTURE.md → overview/ARCHITECTURE.md} +4 -1
- package/templates/default/docs/pages/README.md +50 -0
- package/templates/default/package.json +6 -2
- package/templates/default/scripts/dev.sh +59 -0
- package/templates/default/docs/PROGRESS.md +0 -40
package/dist/index.js
CHANGED
|
@@ -12,6 +12,69 @@ function validateProjectName(name) {
|
|
|
12
12
|
const validNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
13
13
|
return validNameRegex.test(name);
|
|
14
14
|
}
|
|
15
|
+
function isCommandAvailable(command) {
|
|
16
|
+
try {
|
|
17
|
+
execSync(`${command} --version`, { stdio: "ignore" });
|
|
18
|
+
return true;
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function ensurePackageManager(packageManager) {
|
|
24
|
+
if (isCommandAvailable(packageManager)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
console.log(
|
|
28
|
+
pc.yellow(`
|
|
29
|
+
${packageManager} is not installed. Attempting to install...`)
|
|
30
|
+
);
|
|
31
|
+
if (isCommandAvailable("corepack")) {
|
|
32
|
+
try {
|
|
33
|
+
console.log(pc.dim(` Using corepack to enable ${packageManager}...`));
|
|
34
|
+
execSync(`corepack enable ${packageManager}`, { stdio: "inherit" });
|
|
35
|
+
if (isCommandAvailable(packageManager)) {
|
|
36
|
+
console.log(pc.green(` \u2713 ${packageManager} installed via corepack
|
|
37
|
+
`));
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (packageManager !== "npm" && isCommandAvailable("npm")) {
|
|
44
|
+
try {
|
|
45
|
+
console.log(pc.dim(` Installing ${packageManager} globally via npm...`));
|
|
46
|
+
execSync(`npm install -g ${packageManager}`, { stdio: "inherit" });
|
|
47
|
+
if (isCommandAvailable(packageManager)) {
|
|
48
|
+
console.log(pc.green(`
|
|
49
|
+
\u2713 ${packageManager} installed via npm
|
|
50
|
+
`));
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
console.log(
|
|
57
|
+
pc.red(`
|
|
58
|
+
\u2717 Failed to install ${packageManager} automatically.`)
|
|
59
|
+
);
|
|
60
|
+
console.log(
|
|
61
|
+
pc.yellow(` Please install it manually:
|
|
62
|
+
`)
|
|
63
|
+
);
|
|
64
|
+
if (packageManager === "pnpm") {
|
|
65
|
+
console.log(pc.dim(" npm install -g pnpm"));
|
|
66
|
+
console.log(pc.dim(" # or"));
|
|
67
|
+
console.log(pc.dim(" corepack enable pnpm"));
|
|
68
|
+
console.log(pc.dim(" # or"));
|
|
69
|
+
console.log(pc.dim(" curl -fsSL https://get.pnpm.io/install.sh | sh -"));
|
|
70
|
+
} else if (packageManager === "yarn") {
|
|
71
|
+
console.log(pc.dim(" npm install -g yarn"));
|
|
72
|
+
console.log(pc.dim(" # or"));
|
|
73
|
+
console.log(pc.dim(" corepack enable yarn"));
|
|
74
|
+
}
|
|
75
|
+
console.log();
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
15
78
|
function copyDir(src, dest) {
|
|
16
79
|
fs.mkdirSync(dest, { recursive: true });
|
|
17
80
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
@@ -118,9 +181,18 @@ async function promptForOptions(projectName) {
|
|
|
118
181
|
name: "packageManager",
|
|
119
182
|
message: "Select a package manager:",
|
|
120
183
|
choices: [
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
{
|
|
185
|
+
title: isCommandAvailable("pnpm") ? "pnpm (recommended)" : "pnpm (recommended, will be installed)",
|
|
186
|
+
value: "pnpm"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
title: isCommandAvailable("npm") ? "npm" : "npm (not found)",
|
|
190
|
+
value: "npm"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
title: isCommandAvailable("yarn") ? "yarn" : "yarn (will be installed)",
|
|
194
|
+
value: "yarn"
|
|
195
|
+
}
|
|
124
196
|
],
|
|
125
197
|
initial: 0
|
|
126
198
|
}
|
|
@@ -163,37 +235,53 @@ async function createProject(options) {
|
|
|
163
235
|
renameGitignore(projectPath);
|
|
164
236
|
updatePackageJson(projectPath, projectName);
|
|
165
237
|
updateSubPackageNames(projectPath, projectName);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const installCmd = {
|
|
169
|
-
npm: "npm install",
|
|
170
|
-
yarn: "yarn",
|
|
171
|
-
pnpm: "pnpm install"
|
|
172
|
-
}[packageManager];
|
|
173
|
-
try {
|
|
174
|
-
execSync(installCmd, {
|
|
175
|
-
cwd: projectPath,
|
|
176
|
-
stdio: "inherit"
|
|
177
|
-
});
|
|
178
|
-
} catch {
|
|
238
|
+
const pmReady = ensurePackageManager(packageManager);
|
|
239
|
+
if (!pmReady) {
|
|
179
240
|
console.log(
|
|
180
241
|
pc.yellow(
|
|
181
|
-
|
|
242
|
+
`Skipping dependency installation. After installing ${packageManager}, run:`
|
|
182
243
|
)
|
|
183
244
|
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
245
|
+
console.log(pc.dim(` cd ${projectName}`));
|
|
246
|
+
console.log(pc.dim(` ${packageManager === "npm" ? "npm install" : packageManager === "yarn" ? "yarn" : "pnpm install"}`));
|
|
247
|
+
console.log(pc.dim(` cd packages/shared && ${packageManager === "npm" ? "npm run build" : `${packageManager} build`}`));
|
|
248
|
+
console.log();
|
|
249
|
+
} else {
|
|
250
|
+
console.log(pc.cyan("Installing dependencies..."));
|
|
251
|
+
console.log();
|
|
252
|
+
const installCmd = {
|
|
253
|
+
npm: "npm install",
|
|
254
|
+
yarn: "yarn",
|
|
255
|
+
pnpm: "pnpm install"
|
|
256
|
+
}[packageManager];
|
|
257
|
+
try {
|
|
258
|
+
execSync(installCmd, {
|
|
259
|
+
cwd: projectPath,
|
|
260
|
+
stdio: "inherit"
|
|
261
|
+
});
|
|
262
|
+
} catch {
|
|
263
|
+
console.log(
|
|
264
|
+
pc.yellow(
|
|
265
|
+
"\nFailed to install dependencies. You can install them manually."
|
|
266
|
+
)
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
console.log();
|
|
270
|
+
console.log(pc.cyan("Building shared package..."));
|
|
271
|
+
try {
|
|
272
|
+
const buildCmd = packageManager === "npm" ? "npm run build" : `${packageManager} build`;
|
|
273
|
+
execSync(buildCmd, {
|
|
274
|
+
cwd: path.join(projectPath, "packages", "shared"),
|
|
275
|
+
stdio: "inherit"
|
|
276
|
+
});
|
|
277
|
+
} catch {
|
|
278
|
+
console.log(
|
|
279
|
+
pc.yellow(
|
|
280
|
+
`
|
|
281
|
+
Failed to build shared package. Run '${packageManager === "npm" ? "npm run build" : `${packageManager} build`}' in packages/shared manually.`
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
197
285
|
}
|
|
198
286
|
console.log();
|
|
199
287
|
console.log(
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { program } from \"commander\";\nimport prompts from \"prompts\";\nimport pc from \"picocolors\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\ninterface ProjectOptions {\n projectName: string;\n packageManager: \"npm\" | \"yarn\" | \"pnpm\";\n}\n\nfunction validateProjectName(name: string): boolean {\n const validNameRegex =\n /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n return validNameRegex.test(name);\n}\n\nfunction detectPackageManager(): \"npm\" | \"yarn\" | \"pnpm\" {\n const userAgent = process.env.npm_config_user_agent;\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n }\n return \"pnpm\"; // Default to pnpm for monorepo\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n const entries = fs.readdirSync(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n copyDir(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n}\n\nfunction getTemplateDir(): string {\n // In development, templates are relative to src\n // In production (dist), templates are at the package root\n const devPath = path.resolve(__dirname, \"..\", \"templates\", \"default\");\n const prodPath = path.resolve(__dirname, \"..\", \"..\", \"templates\", \"default\");\n\n if (fs.existsSync(devPath)) return devPath;\n if (fs.existsSync(prodPath)) return prodPath;\n\n throw new Error(\"Template not found\");\n}\n\nfunction updatePackageJson(projectPath: string, projectName: string): void {\n const pkgPath = path.join(projectPath, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n pkg.name = projectName;\n fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n}\n\nfunction updateSubPackageNames(projectPath: string, projectName: string): void {\n // Update apps/web package.json\n const webPkgPath = path.join(projectPath, \"apps\", \"web\", \"package.json\");\n if (fs.existsSync(webPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(webPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/web`;\n // Update shared package reference\n if (pkg.dependencies?.[\"@app/shared\"]) {\n pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies[\"@app/shared\"];\n delete pkg.dependencies[\"@app/shared\"];\n }\n fs.writeFileSync(webPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update apps/api package.json\n const apiPkgPath = path.join(projectPath, \"apps\", \"api\", \"package.json\");\n if (fs.existsSync(apiPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(apiPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/api`;\n // Update shared package reference\n if (pkg.dependencies?.[\"@app/shared\"]) {\n pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies[\"@app/shared\"];\n delete pkg.dependencies[\"@app/shared\"];\n }\n fs.writeFileSync(apiPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update packages/shared package.json\n const sharedPkgPath = path.join(projectPath, \"packages\", \"shared\", \"package.json\");\n if (fs.existsSync(sharedPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(sharedPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/shared`;\n fs.writeFileSync(sharedPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update import statements in apps/web/src/app/page.tsx\n const pagePath = path.join(projectPath, \"apps\", \"web\", \"src\", \"app\", \"page.tsx\");\n if (fs.existsSync(pagePath)) {\n let content = fs.readFileSync(pagePath, \"utf-8\");\n content = content.replace(/@app\\/shared/g, `@${projectName}/shared`);\n fs.writeFileSync(pagePath, content);\n }\n\n // Update cursor rules to use correct package names\n const cursorRulesDir = path.join(projectPath, \".cursor\", \"rules\");\n if (fs.existsSync(cursorRulesDir)) {\n const ruleFiles = fs.readdirSync(cursorRulesDir);\n for (const file of ruleFiles) {\n const filePath = path.join(cursorRulesDir, file);\n let content = fs.readFileSync(filePath, \"utf-8\");\n content = content.replace(/@app\\/shared/g, `@${projectName}/shared`);\n fs.writeFileSync(filePath, content);\n }\n }\n\n // Update turbo.json filter names\n const rootPkgPath = path.join(projectPath, \"package.json\");\n if (fs.existsSync(rootPkgPath)) {\n let content = fs.readFileSync(rootPkgPath, \"utf-8\");\n content = content.replace(/@app\\/web/g, `@${projectName}/web`);\n content = content.replace(/@app\\/api/g, `@${projectName}/api`);\n fs.writeFileSync(rootPkgPath, content);\n }\n}\n\nfunction renameGitignore(projectPath: string): void {\n const gitignorePath = path.join(projectPath, \"_gitignore\");\n const targetPath = path.join(projectPath, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n fs.renameSync(gitignorePath, targetPath);\n }\n}\n\nasync function promptForOptions(projectName?: string): Promise<ProjectOptions> {\n const defaultProjectName = projectName || \"my-nene-app\";\n\n const response = await prompts(\n [\n {\n type: projectName ? null : \"text\",\n name: \"projectName\",\n message: \"Project name:\",\n initial: defaultProjectName,\n validate: (value: string) => {\n if (!value) return \"Project name is required\";\n if (!validateProjectName(value)) {\n return \"Invalid project name. Use lowercase letters, numbers, and hyphens only.\";\n }\n return true;\n },\n },\n {\n type: \"select\",\n name: \"packageManager\",\n message: \"Select a package manager:\",\n choices: [\n { title: \"pnpm (recommended)\", value: \"pnpm\" },\n { title: \"npm\", value: \"npm\" },\n { title: \"yarn\", value: \"yarn\" },\n ],\n initial: 0,\n },\n ],\n {\n onCancel: () => {\n console.log(pc.red(\"\\nOperation cancelled.\"));\n process.exit(0);\n },\n }\n );\n\n return {\n projectName: projectName || response.projectName,\n packageManager: response.packageManager,\n };\n}\n\nasync function createProject(options: ProjectOptions): Promise<void> {\n const { projectName, packageManager } = options;\n const projectPath = path.resolve(process.cwd(), projectName);\n\n // Check if directory exists\n if (fs.existsSync(projectPath)) {\n const { overwrite } = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" already exists. Overwrite?`,\n initial: false,\n });\n\n if (!overwrite) {\n console.log(pc.red(\"Operation cancelled.\"));\n process.exit(0);\n }\n\n fs.rmSync(projectPath, { recursive: true, force: true });\n }\n\n console.log();\n console.log(\n pc.cyan(`Creating a new nene.js monorepo in ${pc.bold(projectPath)}`)\n );\n console.log();\n\n // Copy template\n const templateDir = getTemplateDir();\n copyDir(templateDir, projectPath);\n\n // Rename _gitignore to .gitignore\n renameGitignore(projectPath);\n\n // Update package.json with project name\n updatePackageJson(projectPath, projectName);\n\n // Update sub-package names\n updateSubPackageNames(projectPath, projectName);\n\n // Install dependencies\n console.log(pc.cyan(\"Installing dependencies...\"));\n console.log();\n\n const installCmd = {\n npm: \"npm install\",\n yarn: \"yarn\",\n pnpm: \"pnpm install\",\n }[packageManager];\n\n try {\n execSync(installCmd, {\n cwd: projectPath,\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\n \"\\nFailed to install dependencies. You can install them manually.\"\n )\n );\n }\n\n // Build shared package first\n console.log();\n console.log(pc.cyan(\"Building shared package...\"));\n \n try {\n const buildCmd = packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`;\n execSync(buildCmd, {\n cwd: path.join(projectPath, \"packages\", \"shared\"),\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\"\\nFailed to build shared package. Run 'pnpm build' in packages/shared manually.\")\n );\n }\n\n // Success message\n console.log();\n console.log(\n pc.green(\"Success!\") + ` Created ${pc.bold(projectName)} at ${projectPath}`\n );\n console.log();\n console.log(\"Project structure:\");\n console.log();\n console.log(` ${pc.cyan(\"apps/web\")} - Next.js frontend (port 3000)`);\n console.log(` ${pc.cyan(\"apps/api\")} - NestJS backend (port 4000)`);\n console.log(` ${pc.cyan(\"packages/shared\")} - Shared types and constants`);\n console.log(` ${pc.cyan(\"docs/\")} - Project documentation`);\n console.log(` ${pc.cyan(\".cursor/rules/\")} - Cursor AI agent rules`);\n console.log();\n console.log(\"Next steps:\");\n console.log();\n console.log(` ${pc.cyan(\"cd\")} ${projectName}`);\n console.log(\n ` ${pc.cyan(packageManager === \"npm\" ? \"npm run\" : packageManager)} dev`\n );\n console.log();\n console.log(\"This will start both the frontend (port 3000) and backend (port 4000).\");\n console.log();\n console.log(\"Happy coding!\");\n console.log();\n}\n\nexport async function main(): Promise<void> {\n program\n .name(\"create-nene\")\n .description(\"Create a new nene.js monorepo with Next.js and NestJS\")\n .version(\"0.2.0\")\n .argument(\"[project-name]\", \"Name of the project\")\n .action(async (projectName: string | undefined) => {\n console.log();\n console.log(\n pc.bold(pc.cyan(\" nene.js \") + \"- The AI-native full-stack framework\")\n );\n console.log(pc.dim(\" Next.js + NestJS monorepo for AI-assisted development\"));\n console.log();\n\n const options = await promptForOptions(projectName);\n await createProject(options);\n });\n\n await program.parseAsync(process.argv);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { program } from \"commander\";\nimport prompts from \"prompts\";\nimport pc from \"picocolors\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\ninterface ProjectOptions {\n projectName: string;\n packageManager: \"npm\" | \"yarn\" | \"pnpm\";\n}\n\nfunction validateProjectName(name: string): boolean {\n const validNameRegex =\n /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n return validNameRegex.test(name);\n}\n\nfunction detectPackageManager(): \"npm\" | \"yarn\" | \"pnpm\" {\n const userAgent = process.env.npm_config_user_agent;\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n }\n return \"pnpm\"; // Default to pnpm for monorepo\n}\n\nfunction isCommandAvailable(command: string): boolean {\n try {\n execSync(`${command} --version`, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction ensurePackageManager(packageManager: \"npm\" | \"yarn\" | \"pnpm\"): boolean {\n if (isCommandAvailable(packageManager)) {\n return true;\n }\n\n console.log(\n pc.yellow(`\\n${packageManager} is not installed. Attempting to install...`)\n );\n\n // Try corepack first (built-in to Node.js 16.9+)\n if (isCommandAvailable(\"corepack\")) {\n try {\n console.log(pc.dim(` Using corepack to enable ${packageManager}...`));\n execSync(`corepack enable ${packageManager}`, { stdio: \"inherit\" });\n if (isCommandAvailable(packageManager)) {\n console.log(pc.green(` ✓ ${packageManager} installed via corepack\\n`));\n return true;\n }\n } catch {\n // corepack failed, try next method\n }\n }\n\n // Fallback: install via npm\n if (packageManager !== \"npm\" && isCommandAvailable(\"npm\")) {\n try {\n console.log(pc.dim(` Installing ${packageManager} globally via npm...`));\n execSync(`npm install -g ${packageManager}`, { stdio: \"inherit\" });\n if (isCommandAvailable(packageManager)) {\n console.log(pc.green(`\\n ✓ ${packageManager} installed via npm\\n`));\n return true;\n }\n } catch {\n // npm global install failed (possibly permission issue)\n }\n }\n\n // All methods failed\n console.log(\n pc.red(`\\n ✗ Failed to install ${packageManager} automatically.`)\n );\n console.log(\n pc.yellow(` Please install it manually:\\n`)\n );\n\n if (packageManager === \"pnpm\") {\n console.log(pc.dim(\" npm install -g pnpm\"));\n console.log(pc.dim(\" # or\"));\n console.log(pc.dim(\" corepack enable pnpm\"));\n console.log(pc.dim(\" # or\"));\n console.log(pc.dim(\" curl -fsSL https://get.pnpm.io/install.sh | sh -\"));\n } else if (packageManager === \"yarn\") {\n console.log(pc.dim(\" npm install -g yarn\"));\n console.log(pc.dim(\" # or\"));\n console.log(pc.dim(\" corepack enable yarn\"));\n }\n console.log();\n\n return false;\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n const entries = fs.readdirSync(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n copyDir(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n}\n\nfunction getTemplateDir(): string {\n // In development, templates are relative to src\n // In production (dist), templates are at the package root\n const devPath = path.resolve(__dirname, \"..\", \"templates\", \"default\");\n const prodPath = path.resolve(__dirname, \"..\", \"..\", \"templates\", \"default\");\n\n if (fs.existsSync(devPath)) return devPath;\n if (fs.existsSync(prodPath)) return prodPath;\n\n throw new Error(\"Template not found\");\n}\n\nfunction updatePackageJson(projectPath: string, projectName: string): void {\n const pkgPath = path.join(projectPath, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n pkg.name = projectName;\n fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n}\n\nfunction updateSubPackageNames(projectPath: string, projectName: string): void {\n // Update apps/web package.json\n const webPkgPath = path.join(projectPath, \"apps\", \"web\", \"package.json\");\n if (fs.existsSync(webPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(webPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/web`;\n // Update shared package reference\n if (pkg.dependencies?.[\"@app/shared\"]) {\n pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies[\"@app/shared\"];\n delete pkg.dependencies[\"@app/shared\"];\n }\n fs.writeFileSync(webPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update apps/api package.json\n const apiPkgPath = path.join(projectPath, \"apps\", \"api\", \"package.json\");\n if (fs.existsSync(apiPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(apiPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/api`;\n // Update shared package reference\n if (pkg.dependencies?.[\"@app/shared\"]) {\n pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies[\"@app/shared\"];\n delete pkg.dependencies[\"@app/shared\"];\n }\n fs.writeFileSync(apiPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update packages/shared package.json\n const sharedPkgPath = path.join(projectPath, \"packages\", \"shared\", \"package.json\");\n if (fs.existsSync(sharedPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(sharedPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/shared`;\n fs.writeFileSync(sharedPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update import statements in apps/web/src/app/page.tsx\n const pagePath = path.join(projectPath, \"apps\", \"web\", \"src\", \"app\", \"page.tsx\");\n if (fs.existsSync(pagePath)) {\n let content = fs.readFileSync(pagePath, \"utf-8\");\n content = content.replace(/@app\\/shared/g, `@${projectName}/shared`);\n fs.writeFileSync(pagePath, content);\n }\n\n // Update cursor rules to use correct package names\n const cursorRulesDir = path.join(projectPath, \".cursor\", \"rules\");\n if (fs.existsSync(cursorRulesDir)) {\n const ruleFiles = fs.readdirSync(cursorRulesDir);\n for (const file of ruleFiles) {\n const filePath = path.join(cursorRulesDir, file);\n let content = fs.readFileSync(filePath, \"utf-8\");\n content = content.replace(/@app\\/shared/g, `@${projectName}/shared`);\n fs.writeFileSync(filePath, content);\n }\n }\n\n // Update turbo.json filter names\n const rootPkgPath = path.join(projectPath, \"package.json\");\n if (fs.existsSync(rootPkgPath)) {\n let content = fs.readFileSync(rootPkgPath, \"utf-8\");\n content = content.replace(/@app\\/web/g, `@${projectName}/web`);\n content = content.replace(/@app\\/api/g, `@${projectName}/api`);\n fs.writeFileSync(rootPkgPath, content);\n }\n}\n\nfunction renameGitignore(projectPath: string): void {\n const gitignorePath = path.join(projectPath, \"_gitignore\");\n const targetPath = path.join(projectPath, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n fs.renameSync(gitignorePath, targetPath);\n }\n}\n\nasync function promptForOptions(projectName?: string): Promise<ProjectOptions> {\n const defaultProjectName = projectName || \"my-nene-app\";\n\n const response = await prompts(\n [\n {\n type: projectName ? null : \"text\",\n name: \"projectName\",\n message: \"Project name:\",\n initial: defaultProjectName,\n validate: (value: string) => {\n if (!value) return \"Project name is required\";\n if (!validateProjectName(value)) {\n return \"Invalid project name. Use lowercase letters, numbers, and hyphens only.\";\n }\n return true;\n },\n },\n {\n type: \"select\",\n name: \"packageManager\",\n message: \"Select a package manager:\",\n choices: [\n {\n title: isCommandAvailable(\"pnpm\")\n ? \"pnpm (recommended)\"\n : \"pnpm (recommended, will be installed)\",\n value: \"pnpm\",\n },\n {\n title: isCommandAvailable(\"npm\")\n ? \"npm\"\n : \"npm (not found)\",\n value: \"npm\",\n },\n {\n title: isCommandAvailable(\"yarn\")\n ? \"yarn\"\n : \"yarn (will be installed)\",\n value: \"yarn\",\n },\n ],\n initial: 0,\n },\n ],\n {\n onCancel: () => {\n console.log(pc.red(\"\\nOperation cancelled.\"));\n process.exit(0);\n },\n }\n );\n\n return {\n projectName: projectName || response.projectName,\n packageManager: response.packageManager,\n };\n}\n\nasync function createProject(options: ProjectOptions): Promise<void> {\n const { projectName, packageManager } = options;\n const projectPath = path.resolve(process.cwd(), projectName);\n\n // Check if directory exists\n if (fs.existsSync(projectPath)) {\n const { overwrite } = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" already exists. Overwrite?`,\n initial: false,\n });\n\n if (!overwrite) {\n console.log(pc.red(\"Operation cancelled.\"));\n process.exit(0);\n }\n\n fs.rmSync(projectPath, { recursive: true, force: true });\n }\n\n console.log();\n console.log(\n pc.cyan(`Creating a new nene.js monorepo in ${pc.bold(projectPath)}`)\n );\n console.log();\n\n // Copy template\n const templateDir = getTemplateDir();\n copyDir(templateDir, projectPath);\n\n // Rename _gitignore to .gitignore\n renameGitignore(projectPath);\n\n // Update package.json with project name\n updatePackageJson(projectPath, projectName);\n\n // Update sub-package names\n updateSubPackageNames(projectPath, projectName);\n\n // Ensure package manager is available\n const pmReady = ensurePackageManager(packageManager);\n\n if (!pmReady) {\n console.log(\n pc.yellow(\n `Skipping dependency installation. After installing ${packageManager}, run:`\n )\n );\n console.log(pc.dim(` cd ${projectName}`));\n console.log(pc.dim(` ${packageManager === \"npm\" ? \"npm install\" : packageManager === \"yarn\" ? \"yarn\" : \"pnpm install\"}`));\n console.log(pc.dim(` cd packages/shared && ${packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`}`));\n console.log();\n } else {\n // Install dependencies\n console.log(pc.cyan(\"Installing dependencies...\"));\n console.log();\n\n const installCmd = {\n npm: \"npm install\",\n yarn: \"yarn\",\n pnpm: \"pnpm install\",\n }[packageManager];\n\n try {\n execSync(installCmd, {\n cwd: projectPath,\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\n \"\\nFailed to install dependencies. You can install them manually.\"\n )\n );\n }\n\n // Build shared package first\n console.log();\n console.log(pc.cyan(\"Building shared package...\"));\n\n try {\n const buildCmd = packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`;\n execSync(buildCmd, {\n cwd: path.join(projectPath, \"packages\", \"shared\"),\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\n `\\nFailed to build shared package. Run '${packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`}' in packages/shared manually.`\n )\n );\n }\n }\n\n // Success message\n console.log();\n console.log(\n pc.green(\"Success!\") + ` Created ${pc.bold(projectName)} at ${projectPath}`\n );\n console.log();\n console.log(\"Project structure:\");\n console.log();\n console.log(` ${pc.cyan(\"apps/web\")} - Next.js frontend (port 3000)`);\n console.log(` ${pc.cyan(\"apps/api\")} - NestJS backend (port 4000)`);\n console.log(` ${pc.cyan(\"packages/shared\")} - Shared types and constants`);\n console.log(` ${pc.cyan(\"docs/\")} - Project documentation`);\n console.log(` ${pc.cyan(\".cursor/rules/\")} - Cursor AI agent rules`);\n console.log();\n console.log(\"Next steps:\");\n console.log();\n console.log(` ${pc.cyan(\"cd\")} ${projectName}`);\n console.log(\n ` ${pc.cyan(packageManager === \"npm\" ? \"npm run\" : packageManager)} dev`\n );\n console.log();\n console.log(\"This will start both the frontend (port 3000) and backend (port 4000).\");\n console.log();\n console.log(\"Happy coding!\");\n console.log();\n}\n\nexport async function main(): Promise<void> {\n program\n .name(\"create-nene\")\n .description(\"Create a new nene.js monorepo with Next.js and NestJS\")\n .version(\"0.2.0\")\n .argument(\"[project-name]\", \"Name of the project\")\n .action(async (projectName: string | undefined) => {\n console.log();\n console.log(\n pc.bold(pc.cyan(\" nene.js \") + \"- The AI-native full-stack framework\")\n );\n console.log(pc.dim(\" Next.js + NestJS monorepo for AI-assisted development\"));\n console.log();\n\n const options = await promptForOptions(projectName);\n await createProject(options);\n });\n\n await program.parseAsync(process.argv);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -51,3 +51,85 @@ const response = await fetch(`${API_URL}${API_ROUTES.USERS}`);
|
|
|
51
51
|
- Use Tailwind CSS for styling
|
|
52
52
|
- Follow mobile-first responsive design
|
|
53
53
|
- Use CSS variables for theming
|
|
54
|
+
|
|
55
|
+
## Post-Edit Verification (REQUIRED)
|
|
56
|
+
|
|
57
|
+
**After every web file edit (`apps/web/**`), you MUST verify the app still works.**
|
|
58
|
+
|
|
59
|
+
This is the most important rule in this file. Skipping verification leads to broken builds that the user discovers later.
|
|
60
|
+
|
|
61
|
+
### Step 1: Check if dev server is running
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Check if the Next.js dev server is running
|
|
65
|
+
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- If `000` → dev server is not running. Start it with `pnpm dev` (or `pnpm dev:web`).
|
|
69
|
+
- If `200` → proceed to Step 2.
|
|
70
|
+
- If `500` → there's an error. Proceed to Step 2 for details.
|
|
71
|
+
|
|
72
|
+
### Step 2: Fetch the page and check for errors
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Fetch the page HTML and check for runtime errors
|
|
76
|
+
curl -s http://localhost:3000 2>&1 | head -100
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**What to look for:**
|
|
80
|
+
- `Cannot find module './XXX.js'` → **Stale webpack cache** (see Troubleshooting below)
|
|
81
|
+
- `Error: ` or `error` in the output → Code error, inspect and fix
|
|
82
|
+
- `Internal Server Error` or `500` → Server-side rendering error
|
|
83
|
+
- Normal HTML with `<div>`, `<html>` → Page is working correctly
|
|
84
|
+
|
|
85
|
+
### Step 3: Auto-fix based on error type
|
|
86
|
+
|
|
87
|
+
| Error Pattern | Cause | Fix |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `Cannot find module './NNN.js'` | Stale `.next/` cache | `rm -rf apps/web/.next` then restart dev server |
|
|
90
|
+
| `Module not found: Can't resolve 'X'` | Missing import / dependency | Check import paths, install missing packages |
|
|
91
|
+
| `SyntaxError` or `TypeError` | Code bug in your edit | Review and fix the code you just changed |
|
|
92
|
+
| `Invalid hook call` | Hooks in server component | Add `'use client'` directive or restructure |
|
|
93
|
+
| `500 Internal Server Error` | Server-side rendering crash | Check terminal logs for the full stack trace |
|
|
94
|
+
|
|
95
|
+
### Step 4: Re-verify after fix
|
|
96
|
+
|
|
97
|
+
After applying any fix, **repeat Steps 1-2** to confirm the error is resolved. Do not move on to other tasks until the page loads successfully.
|
|
98
|
+
|
|
99
|
+
### Quick Verification Command (copy-paste ready)
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# One-liner: check if the web app is healthy
|
|
103
|
+
curl -s -o /dev/null -w "Web status: %{http_code}\n" http://localhost:3000 && curl -s http://localhost:3000 | grep -c "<!DOCTYPE\|<html" > /dev/null && echo "✓ Page OK" || echo "✗ Page has issues - check output above"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Troubleshooting
|
|
107
|
+
|
|
108
|
+
### "Cannot find module './XXX.js'" Runtime Error
|
|
109
|
+
|
|
110
|
+
This error occurs when the Next.js webpack cache (`.next/`) becomes stale — typically after files are modified while the dev server is running.
|
|
111
|
+
|
|
112
|
+
**How to fix:**
|
|
113
|
+
|
|
114
|
+
1. Stop the dev server (or it will auto-restart on file change)
|
|
115
|
+
2. Delete the cache: `rm -rf apps/web/.next`
|
|
116
|
+
3. Restart: `pnpm dev` (or `pnpm dev:clean` which does steps 2+3 together)
|
|
117
|
+
|
|
118
|
+
**When you encounter this error as an AI agent:**
|
|
119
|
+
|
|
120
|
+
- Do NOT try to debug the missing module — the `.js` file number is a webpack chunk hash, not your code.
|
|
121
|
+
- Run `rm -rf apps/web/.next` and restart the dev server. This resolves the issue 100% of the time.
|
|
122
|
+
- If the error persists after cache clear, THEN look for actual code issues.
|
|
123
|
+
|
|
124
|
+
### Next.js dev server port conflict
|
|
125
|
+
|
|
126
|
+
If port 3000 is already in use, Next.js will auto-select the next available port and display it in the terminal output. This is normal behavior — check the terminal for the actual port number.
|
|
127
|
+
|
|
128
|
+
### Build verification (for production)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Full build check (catches more issues than dev mode)
|
|
132
|
+
pnpm build 2>&1 | tail -20
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If the build fails, fix all errors before considering the task complete.
|
|
@@ -52,14 +52,44 @@ export class CreateUserDto {
|
|
|
52
52
|
}
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
## Documentation Structure
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
docs/
|
|
59
|
+
├── API.md # API endpoint reference
|
|
60
|
+
├── overview/ # Project overview & architecture
|
|
61
|
+
│ └── ARCHITECTURE.md
|
|
62
|
+
├── kanban/ # Task management (TODO/DOING/DONE)
|
|
63
|
+
│ ├── README.md # Board status & priority order
|
|
64
|
+
│ ├── TODO/
|
|
65
|
+
│ ├── DOING/
|
|
66
|
+
│ └── DONE/
|
|
67
|
+
└── pages/ # Page-specific documentation
|
|
68
|
+
```
|
|
69
|
+
|
|
55
70
|
## Before Making Changes
|
|
56
71
|
|
|
57
|
-
1. Check `docs/ARCHITECTURE.md` for current patterns
|
|
72
|
+
1. Check `docs/overview/ARCHITECTURE.md` for current patterns
|
|
58
73
|
2. Check `docs/API.md` for existing endpoints
|
|
59
|
-
3. Check `docs/
|
|
74
|
+
3. Check `docs/kanban/README.md` for task priorities
|
|
60
75
|
|
|
61
76
|
## After Making Changes
|
|
62
77
|
|
|
63
78
|
1. Update `docs/API.md` when adding/modifying endpoints
|
|
64
|
-
2.
|
|
65
|
-
3.
|
|
79
|
+
2. Move completed tasks in `docs/kanban/` (DOING → DONE)
|
|
80
|
+
3. Add new TODO items to `docs/kanban/TODO/` when needed
|
|
81
|
+
4. Run `pnpm build` to verify shared package changes work
|
|
82
|
+
5. **If you edited `apps/web/**` files** → Run the Post-Edit Verification from `frontend-patterns.mdc`:
|
|
83
|
+
```bash
|
|
84
|
+
curl -s -o /dev/null -w "Web: %{http_code}\n" http://localhost:3000
|
|
85
|
+
```
|
|
86
|
+
If the status is not `200`, diagnose and fix before moving on.
|
|
87
|
+
6. **If you edited `apps/api/**` files** → Verify the API:
|
|
88
|
+
```bash
|
|
89
|
+
curl -s -o /dev/null -w "API: %{http_code}\n" http://localhost:4000/api/health
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Common Errors
|
|
93
|
+
|
|
94
|
+
- **"Cannot find module './XXX.js'"** in Next.js → Stale webpack cache. Run `rm -rf apps/web/.next` and restart the dev server. Do NOT debug the chunk file — it's always a cache issue.
|
|
95
|
+
- **Port already in use** → Another process is using the port. Check with `lsof -i :3000` or `lsof -i :4000`, or use `pnpm dev:clean` to restart fresh.
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Task management workflow for docs/
|
|
3
|
-
globs: docs/
|
|
2
|
+
description: Task management workflow for docs/kanban board
|
|
3
|
+
globs: docs/kanban/**/*.md
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Task Management Rules
|
|
8
8
|
|
|
9
|
-
Rules for the project's kanban board (`docs/
|
|
9
|
+
Rules for the project's kanban board (`docs/kanban/`) task management.
|
|
10
|
+
|
|
11
|
+
## When Adding a Task
|
|
12
|
+
|
|
13
|
+
1. Create a new `.md` file in `docs/kanban/TODO/`
|
|
14
|
+
2. Use the naming convention: `NN-short-description.md` (e.g., `02-user-auth.md`)
|
|
15
|
+
3. Follow the task file format in `docs/kanban/README.md`
|
|
16
|
+
4. Update the counts in `docs/kanban/README.md`
|
|
10
17
|
|
|
11
18
|
## When Starting a Task
|
|
12
19
|
|
|
13
|
-
1. Check the Priority Order in `docs/
|
|
20
|
+
1. Check the Priority Order in `docs/kanban/README.md`
|
|
14
21
|
2. Select the highest-priority TODO file
|
|
15
22
|
3. Review the file contents before starting work
|
|
23
|
+
4. Move the file to `docs/kanban/DOING/`
|
|
24
|
+
5. Update the counts in `docs/kanban/README.md`
|
|
16
25
|
|
|
17
26
|
## When Completing a Task (Required)
|
|
18
27
|
|
|
@@ -21,7 +30,7 @@ When completing a task, you **must** do the following:
|
|
|
21
30
|
### 1. Move the File
|
|
22
31
|
|
|
23
32
|
```bash
|
|
24
|
-
mv docs/
|
|
33
|
+
mv docs/kanban/DOING/{task-file}.md docs/kanban/DONE/
|
|
25
34
|
```
|
|
26
35
|
|
|
27
36
|
### 2. Update File Contents
|
|
@@ -29,7 +38,7 @@ mv docs/plan/TODO/{task-file}.md docs/plan/DONE/
|
|
|
29
38
|
```markdown
|
|
30
39
|
# Status change
|
|
31
40
|
|
|
32
|
-
- **Status**:
|
|
41
|
+
- **Status**: TODO → - **Status**: DONE
|
|
33
42
|
|
|
34
43
|
# Complete checklist items
|
|
35
44
|
|
|
@@ -38,23 +47,25 @@ mv docs/plan/TODO/{task-file}.md docs/plan/DONE/
|
|
|
38
47
|
|
|
39
48
|
### 3. Update README.md Counts
|
|
40
49
|
|
|
50
|
+
Update the table in `docs/kanban/README.md`:
|
|
51
|
+
|
|
41
52
|
```markdown
|
|
42
|
-
|
|
|
43
|
-
|
|
|
53
|
+
| TODO | N | # decrease
|
|
54
|
+
| DONE | M | # increase
|
|
44
55
|
```
|
|
45
56
|
|
|
46
57
|
## Task Status
|
|
47
58
|
|
|
48
|
-
| Status |
|
|
49
|
-
| ------ |
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
59
|
+
| Status | Folder |
|
|
60
|
+
| ------ | -------- |
|
|
61
|
+
| TODO | `TODO/` |
|
|
62
|
+
| DOING | `DOING/` |
|
|
63
|
+
| DONE | `DONE/` |
|
|
53
64
|
|
|
54
65
|
## Priority
|
|
55
66
|
|
|
56
|
-
| Priority |
|
|
57
|
-
| -------- |
|
|
58
|
-
| High |
|
|
59
|
-
| Medium |
|
|
60
|
-
| Low |
|
|
67
|
+
| Priority | Level |
|
|
68
|
+
| -------- | ------ |
|
|
69
|
+
| High | Do first |
|
|
70
|
+
| Medium | Do next |
|
|
71
|
+
| Low | Do later |
|
|
@@ -3,18 +3,61 @@
|
|
|
3
3
|
@tailwind utilities;
|
|
4
4
|
|
|
5
5
|
:root {
|
|
6
|
-
--foreground-rgb:
|
|
7
|
-
--background-rgb:
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
@media (prefers-color-scheme: dark) {
|
|
11
|
-
:root {
|
|
12
|
-
--foreground-rgb: 255, 255, 255;
|
|
13
|
-
--background-rgb: 10, 10, 10;
|
|
14
|
-
}
|
|
6
|
+
--foreground-rgb: 255, 255, 255;
|
|
7
|
+
--background-rgb: 10, 10, 10;
|
|
15
8
|
}
|
|
16
9
|
|
|
17
10
|
body {
|
|
18
11
|
color: rgb(var(--foreground-rgb));
|
|
19
12
|
background: rgb(var(--background-rgb));
|
|
20
13
|
}
|
|
14
|
+
|
|
15
|
+
.glass-panel {
|
|
16
|
+
background: rgba(255, 255, 255, 0.03);
|
|
17
|
+
backdrop-filter: blur(12px);
|
|
18
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.glow-text {
|
|
22
|
+
text-shadow: 0 0 20px rgba(5, 183, 214, 0.4);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.pulse-dot {
|
|
26
|
+
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
|
|
27
|
+
animation: pulse-ring 2s infinite;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.pulse-dot-red {
|
|
31
|
+
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
|
|
32
|
+
animation: pulse-ring-red 2s infinite;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@keyframes pulse-ring {
|
|
36
|
+
0% {
|
|
37
|
+
transform: scale(0.95);
|
|
38
|
+
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
|
|
39
|
+
}
|
|
40
|
+
70% {
|
|
41
|
+
transform: scale(1);
|
|
42
|
+
box-shadow: 0 0 0 6px rgba(34, 197, 94, 0);
|
|
43
|
+
}
|
|
44
|
+
100% {
|
|
45
|
+
transform: scale(0.95);
|
|
46
|
+
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@keyframes pulse-ring-red {
|
|
51
|
+
0% {
|
|
52
|
+
transform: scale(0.95);
|
|
53
|
+
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
|
|
54
|
+
}
|
|
55
|
+
70% {
|
|
56
|
+
transform: scale(1);
|
|
57
|
+
box-shadow: 0 0 0 6px rgba(239, 68, 68, 0);
|
|
58
|
+
}
|
|
59
|
+
100% {
|
|
60
|
+
transform: scale(0.95);
|
|
61
|
+
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import type { Metadata } from 'next';
|
|
2
|
+
import { Inter, JetBrains_Mono } from 'next/font/google';
|
|
2
3
|
import './globals.css';
|
|
3
4
|
|
|
5
|
+
const inter = Inter({
|
|
6
|
+
subsets: ['latin'],
|
|
7
|
+
variable: '--font-inter',
|
|
8
|
+
display: 'swap',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const jetbrainsMono = JetBrains_Mono({
|
|
12
|
+
subsets: ['latin'],
|
|
13
|
+
variable: '--font-jetbrains-mono',
|
|
14
|
+
display: 'swap',
|
|
15
|
+
});
|
|
16
|
+
|
|
4
17
|
export const metadata: Metadata = {
|
|
5
|
-
title: '
|
|
18
|
+
title: 'nene.js | Local Development',
|
|
6
19
|
description: 'A full-stack app built with Next.js and NestJS',
|
|
7
20
|
};
|
|
8
21
|
|
|
@@ -12,8 +25,12 @@ export default function RootLayout({
|
|
|
12
25
|
children: React.ReactNode;
|
|
13
26
|
}) {
|
|
14
27
|
return (
|
|
15
|
-
<html lang="en">
|
|
16
|
-
<body
|
|
28
|
+
<html lang="en" className="dark">
|
|
29
|
+
<body
|
|
30
|
+
className={`${inter.variable} ${jetbrainsMono.variable} font-display`}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</body>
|
|
17
34
|
</html>
|
|
18
35
|
);
|
|
19
36
|
}
|