create-lumen 2.0.0-alpha.2 → 2.0.0-beta.2
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/README.md +24 -6
- package/bin/cli.js +2 -1
- package/package.json +2 -2
- package/schema/lumen.config.v2.json +29 -0
- package/src/agent-context.js +71 -47
- package/src/cleanup.js +19 -1
- package/src/cli-args.js +131 -5
- package/src/configure.js +8 -4
- package/src/dependencies.js +46 -13
- package/src/engine/capabilities.js +71 -1
- package/src/engine/composition.js +334 -17
- package/src/engine/providers/base-provider.js +4 -4
- package/src/env.js +24 -6
- package/src/main.js +38 -15
- package/src/manifest/emit.js +14 -1
- package/src/manifest/schema.js +30 -0
- package/src/prompts.js +227 -23
- package/templates/bases/next/app-router/js/.provenance.json +19 -0
- package/templates/bases/next/app-router/js/README.md +36 -0
- package/templates/bases/next/app-router/js/jsconfig.json +7 -0
- package/templates/bases/next/app-router/js/next.config.mjs +6 -0
- package/templates/bases/next/app-router/js/package.json +15 -0
- package/templates/bases/next/app-router/js/public/file.svg +1 -0
- package/templates/bases/next/app-router/js/public/globe.svg +1 -0
- package/templates/bases/next/app-router/js/public/next.svg +1 -0
- package/templates/bases/next/app-router/js/public/vercel.svg +1 -0
- package/templates/bases/next/app-router/js/public/window.svg +1 -0
- package/templates/bases/next/app-router/js/src/app/api/hello/route.js +5 -0
- package/templates/bases/next/app-router/js/src/app/blog/[slug]/page.js +33 -0
- package/templates/bases/next/app-router/js/src/app/favicon.ico +0 -0
- package/templates/bases/next/app-router/js/src/app/globals.css +49 -0
- package/templates/bases/next/app-router/js/src/app/layout.js +25 -0
- package/templates/bases/next/app-router/js/src/app/loading.js +15 -0
- package/templates/bases/next/app-router/js/src/app/not-found.js +29 -0
- package/templates/bases/next/app-router/js/src/app/page.js +69 -0
- package/templates/bases/next/app-router/js/src/app/page.module.css +150 -0
- package/templates/bases/next/app-router/ts/.provenance.json +19 -0
- package/templates/bases/next/app-router/ts/README.md +36 -0
- package/templates/bases/next/app-router/ts/next.config.ts +7 -0
- package/templates/bases/next/app-router/ts/package.json +21 -0
- package/templates/bases/next/app-router/ts/public/file.svg +1 -0
- package/templates/bases/next/app-router/ts/public/globe.svg +1 -0
- package/templates/bases/next/app-router/ts/public/next.svg +1 -0
- package/templates/bases/next/app-router/ts/public/vercel.svg +1 -0
- package/templates/bases/next/app-router/ts/public/window.svg +1 -0
- package/templates/bases/next/app-router/ts/src/app/api/hello/route.ts +5 -0
- package/templates/bases/next/app-router/ts/src/app/blog/[slug]/page.tsx +38 -0
- package/templates/bases/next/app-router/ts/src/app/favicon.ico +0 -0
- package/templates/bases/next/app-router/ts/src/app/globals.css +49 -0
- package/templates/bases/next/app-router/ts/src/app/layout.tsx +30 -0
- package/templates/bases/next/app-router/ts/src/app/loading.tsx +15 -0
- package/templates/bases/next/app-router/ts/src/app/not-found.tsx +29 -0
- package/templates/bases/next/app-router/ts/src/app/page.module.css +150 -0
- package/templates/bases/next/app-router/ts/src/app/page.tsx +69 -0
- package/templates/bases/next/app-router/ts/tsconfig.json +34 -0
- package/templates/bases/next/pages-router/js/.provenance.json +19 -0
- package/templates/bases/next/pages-router/js/README.md +40 -0
- package/templates/bases/next/pages-router/js/jsconfig.json +7 -0
- package/templates/bases/next/pages-router/js/next.config.mjs +7 -0
- package/templates/bases/next/pages-router/js/package.json +15 -0
- package/templates/bases/next/pages-router/js/public/favicon.ico +0 -0
- package/templates/bases/next/pages-router/js/public/file.svg +1 -0
- package/templates/bases/next/pages-router/js/public/globe.svg +1 -0
- package/templates/bases/next/pages-router/js/public/next.svg +1 -0
- package/templates/bases/next/pages-router/js/public/vercel.svg +1 -0
- package/templates/bases/next/pages-router/js/public/window.svg +1 -0
- package/templates/bases/next/pages-router/js/src/pages/404.js +35 -0
- package/templates/bases/next/pages-router/js/src/pages/500.js +35 -0
- package/templates/bases/next/pages-router/js/src/pages/_app.js +20 -0
- package/templates/bases/next/pages-router/js/src/pages/_document.js +15 -0
- package/templates/bases/next/pages-router/js/src/pages/api/hello.js +5 -0
- package/templates/bases/next/pages-router/js/src/pages/blog/[slug].js +41 -0
- package/templates/bases/next/pages-router/js/src/pages/index.js +77 -0
- package/templates/bases/next/pages-router/js/src/styles/Home.module.css +149 -0
- package/templates/bases/next/pages-router/js/src/styles/globals.css +42 -0
- package/templates/bases/next/pages-router/ts/.provenance.json +19 -0
- package/templates/bases/next/pages-router/ts/README.md +40 -0
- package/templates/bases/next/pages-router/ts/next.config.ts +8 -0
- package/templates/bases/next/pages-router/ts/package.json +21 -0
- package/templates/bases/next/pages-router/ts/public/favicon.ico +0 -0
- package/templates/bases/next/pages-router/ts/public/file.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/globe.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/next.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/vercel.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/window.svg +1 -0
- package/templates/bases/next/pages-router/ts/src/pages/404.tsx +35 -0
- package/templates/bases/next/pages-router/ts/src/pages/500.tsx +35 -0
- package/templates/bases/next/pages-router/ts/src/pages/_app.tsx +21 -0
- package/templates/bases/next/pages-router/ts/src/pages/_document.tsx +15 -0
- package/templates/bases/next/pages-router/ts/src/pages/api/hello.ts +13 -0
- package/templates/bases/next/pages-router/ts/src/pages/blog/[slug].tsx +46 -0
- package/templates/bases/next/pages-router/ts/src/pages/index.tsx +77 -0
- package/templates/bases/next/pages-router/ts/src/styles/Home.module.css +149 -0
- package/templates/bases/next/pages-router/ts/src/styles/globals.css +42 -0
- package/templates/bases/next/pages-router/ts/tsconfig.json +29 -0
- package/templates/conditional/database/prisma/db.js +14 -0
- package/templates/conditional/database/prisma/db.ts +16 -0
- package/templates/conditional/database/prisma/schema.prisma +9 -0
- package/templates/conditional/database/prisma/seed.js +16 -0
- package/templates/conditional/database/prisma/seed.ts +16 -0
- package/templates/conditional/docker/.dockerignore +10 -0
- package/templates/conditional/docker/Dockerfile +57 -0
- package/templates/conditional/docker/docker-compose.yml +35 -0
- package/templates/conditional/linting/biome/biome.json +31 -0
- package/templates/conditional/next/config/cloudflare/next.config.mjs +6 -0
- package/templates/conditional/next/config/cloudflare/next.config.ts +7 -0
- package/templates/conditional/next/config/node/next.config.mjs +6 -0
- package/templates/conditional/next/config/node/next.config.ts +7 -0
- package/templates/conditional/next/config/static/next.config.mjs +6 -0
- package/templates/conditional/next/config/static/next.config.ts +7 -0
- package/templates/conditional/next/config/turbopack/next.config.mjs +7 -0
- package/templates/conditional/next/config/turbopack/next.config.ts +8 -0
- package/templates/conditional/next/config/vercel/next.config.mjs +6 -0
- package/templates/conditional/next/config/vercel/next.config.ts +7 -0
- package/templates/conditional/next/config/webpack/next.config.mjs +9 -0
- package/templates/conditional/next/config/webpack/next.config.ts +10 -0
- package/templates/conditional/next/font/BUILD_SIZE_NOTE.md +17 -0
- package/templates/conditional/next/font/app-router/src/app/layout.js +25 -0
- package/templates/conditional/next/font/app-router/src/app/layout.tsx +30 -0
- package/templates/conditional/next/font/pages-router/src/pages/_app.js +20 -0
- package/templates/conditional/next/font/pages-router/src/pages/_app.tsx +21 -0
- package/templates/conditional/next/font/pages-router/src/pages/_document.js +18 -0
- package/templates/conditional/next/font/pages-router/src/pages/_document.tsx +18 -0
- package/templates/conditional/next/font/src/app/layout.js +25 -0
- package/templates/conditional/next/font/src/app/layout.tsx +30 -0
- package/templates/conditional/next/font/src/lib/fonts.js +11 -0
- package/templates/conditional/next/font/src/lib/fonts.ts +11 -0
- package/templates/conditional/next/font/src/pages/_app.js +20 -0
- package/templates/conditional/next/font/src/pages/_app.tsx +21 -0
- package/templates/conditional/next/font/src/pages/_document.js +18 -0
- package/templates/conditional/next/font/src/pages/_document.tsx +18 -0
- package/templates/conditional/next/image/BUILD_SIZE_NOTE.md +15 -0
- package/templates/conditional/next/image/app-router/src/components/ImageExample.jsx +34 -0
- package/templates/conditional/next/image/app-router/src/components/ImageExample.tsx +44 -0
- package/templates/conditional/next/image/pages-router/src/components/ImageExample.jsx +34 -0
- package/templates/conditional/next/image/pages-router/src/components/ImageExample.tsx +44 -0
- package/templates/conditional/next/image/src/components/ImageExample.jsx +44 -0
- package/templates/conditional/next/image/src/components/ImageExample.tsx +54 -0
- package/templates/conditional/next/image/src/components/image/index.js +1 -0
- package/templates/conditional/next/image/src/components/image/index.ts +2 -0
- package/templates/conditional/next/route-groups/js/src/app/(admin)/layout.jsx +23 -0
- package/templates/conditional/next/route-groups/js/src/app/(auth)/layout.jsx +17 -0
- package/templates/conditional/next/route-groups/js/src/app/(private)/layout.jsx +27 -0
- package/templates/conditional/next/route-groups/js/src/app/(public)/layout.jsx +28 -0
- package/templates/conditional/next/route-groups/js/src/app/(public)/page.jsx +38 -0
- package/templates/conditional/next/route-groups/js/src/middleware.js +48 -0
- package/templates/conditional/next/route-groups/ts/src/app/(admin)/layout.tsx +28 -0
- package/templates/conditional/next/route-groups/ts/src/app/(auth)/layout.tsx +22 -0
- package/templates/conditional/next/route-groups/ts/src/app/(private)/layout.tsx +32 -0
- package/templates/conditional/next/route-groups/ts/src/app/(public)/layout.tsx +33 -0
- package/templates/conditional/next/route-groups/ts/src/app/(public)/page.tsx +38 -0
- package/templates/conditional/next/route-groups/ts/src/middleware.ts +49 -0
- package/templates/conditional/next/server-components/README.md +13 -0
- package/templates/conditional/next/server-components/eslint.config.mjs +47 -0
- package/templates/conditional/next/server-components/rules/no-server-imports-in-client.js +89 -0
- package/templates/conditional/next/server-components/rules/no-server-imports-in-client.mjs +88 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.jsx +48 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.tsx +53 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.jsx +41 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.tsx +46 -0
- package/templates/conditional/next/server-components/src/components/boundaries/index.js +2 -0
- package/templates/conditional/next/server-components/src/components/boundaries/index.ts +4 -0
- package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.jsx +10 -0
- package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.tsx +10 -0
- package/templates/conditional/testing/jest/next/jest.config.mjs +20 -0
- package/templates/conditional/testing/jest/next/jest.config.ts +20 -0
- package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.jsx +10 -0
- package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.tsx +10 -0
- package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.jsx +11 -0
- package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.tsx +11 -0
- package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.jsx +11 -0
- package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.tsx +11 -0
- package/templates/conditional/testing/vitest/next/vitest.config.js +16 -0
- package/templates/conditional/testing/vitest/next/vitest.config.ts +16 -0
package/src/manifest/schema.js
CHANGED
|
@@ -23,6 +23,8 @@ export const DOCS_LANGUAGES = ["en", "es"];
|
|
|
23
23
|
export const TOOLING_LANGUAGES = ["ts", "js"];
|
|
24
24
|
export const TOOLING_LINTERS = ["eslint", "oxlint", "biome", "none"];
|
|
25
25
|
export const TOOLING_FORMATTERS = ["prettier", "oxfmt", "none"];
|
|
26
|
+
export const DATABASE_ORMS = ["prisma", "none"];
|
|
27
|
+
export const DATABASE_PROVIDERS = ["postgresql"];
|
|
26
28
|
|
|
27
29
|
// ---------------------------------------------------------------------------
|
|
28
30
|
// Sub-schemas
|
|
@@ -121,6 +123,13 @@ const toolingSchema = z
|
|
|
121
123
|
})
|
|
122
124
|
.strict();
|
|
123
125
|
|
|
126
|
+
const databaseSchema = z
|
|
127
|
+
.object({
|
|
128
|
+
orm: z.enum(DATABASE_ORMS),
|
|
129
|
+
provider: z.enum(DATABASE_PROVIDERS).optional().default("postgresql"),
|
|
130
|
+
})
|
|
131
|
+
.strict();
|
|
132
|
+
|
|
124
133
|
// ---------------------------------------------------------------------------
|
|
125
134
|
// Top-level manifest v2 - strict, no extra keys except $schema
|
|
126
135
|
// ---------------------------------------------------------------------------
|
|
@@ -136,6 +145,9 @@ export const manifestSchemaV2 = z
|
|
|
136
145
|
docs: docsSchema,
|
|
137
146
|
paths: pathsSchema,
|
|
138
147
|
tooling: toolingSchema,
|
|
148
|
+
database: databaseSchema.optional(),
|
|
149
|
+
docker: z.boolean().optional(),
|
|
150
|
+
routeGroups: z.boolean().optional(),
|
|
139
151
|
reactCompiler: z.boolean().optional(),
|
|
140
152
|
agentDocs: z.boolean().optional(),
|
|
141
153
|
})
|
|
@@ -177,6 +189,24 @@ export const manifestSchemaV2 = z
|
|
|
177
189
|
});
|
|
178
190
|
}
|
|
179
191
|
|
|
192
|
+
// database only for Next.js (#62)
|
|
193
|
+
if (val.database !== undefined && val.database.orm !== "none" && framework.name !== "next") {
|
|
194
|
+
ctx.addIssue({
|
|
195
|
+
code: "custom",
|
|
196
|
+
path: ["database", "orm"],
|
|
197
|
+
message: `database is only valid when framework.name is "next"`,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// routeGroups only for Next.js App Router (#64)
|
|
202
|
+
if (val.routeGroups === true && (framework.name !== "next" || framework.variant !== "app-router")) {
|
|
203
|
+
ctx.addIssue({
|
|
204
|
+
code: "custom",
|
|
205
|
+
path: ["routeGroups"],
|
|
206
|
+
message: `routeGroups is only valid when framework is "next:app-router"`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
180
210
|
// architecture scoping per framework (#34) — canonical `preset`, legacy `type` fallback
|
|
181
211
|
const archValue = architecture.preset ?? architecture.type;
|
|
182
212
|
const allowedByFramework =
|
package/src/prompts.js
CHANGED
|
@@ -46,32 +46,53 @@ export function formatConfig(responses) {
|
|
|
46
46
|
? "Redux Toolkit"
|
|
47
47
|
: "Zustand";
|
|
48
48
|
|
|
49
|
-
const docsLang = responses.docsLanguage === "es" ? "ES" : responses.docsLanguage === "en" ? "EN" : "
|
|
49
|
+
const docsLang = responses.docsLanguage === "es" ? "ES" : responses.docsLanguage === "en" ? "EN" : "—";
|
|
50
|
+
const fwLabel =
|
|
51
|
+
responses.frameworkName === "next" && responses.frameworkVariant === "pages-router"
|
|
52
|
+
? "Next.js (Pages Router)"
|
|
53
|
+
: responses.frameworkName === "next"
|
|
54
|
+
? "Next.js (App Router)"
|
|
55
|
+
: "React + Vite";
|
|
50
56
|
const lines = [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
`• ${chalk.bold("Framework:")} ${chalk.green(fwLabel)}`,
|
|
58
|
+
`• ${chalk.bold("Architecture:")} ${chalk.green(arch)}`,
|
|
59
|
+
`• ${chalk.bold("Language:")} ${chalk.green(lang)}`,
|
|
60
|
+
`• ${chalk.bold("CSS:")} ${chalk.yellow(css)}`,
|
|
61
|
+
`• ${chalk.bold("UI Kit:")} ${responses.uiKit === "shadcn" ? chalk.cyan("shadcn/ui") : chalk.gray("None")}`,
|
|
62
|
+
`• ${chalk.bold("Testing:")} ${chalk.magenta(testing)}`,
|
|
55
63
|
];
|
|
56
64
|
|
|
57
65
|
if (responses.router !== undefined) {
|
|
58
|
-
lines.push(
|
|
66
|
+
lines.push(`• ${chalk.bold("Router:")} ${responses.router ? chalk.green("Yes") : chalk.red("No")}`);
|
|
59
67
|
}
|
|
60
68
|
if (responses.bundler) {
|
|
61
|
-
lines.push(
|
|
69
|
+
lines.push(`• ${chalk.bold("Bundler:")} ${chalk.cyan(responses.bundler)}`);
|
|
62
70
|
}
|
|
63
71
|
if (responses.adapter && responses.adapter !== "none") {
|
|
64
|
-
lines.push(
|
|
72
|
+
lines.push(`• ${chalk.bold("Adapter:")} ${chalk.cyan(responses.adapter)}`);
|
|
73
|
+
}
|
|
74
|
+
if (responses.database && responses.database !== "none") {
|
|
75
|
+
lines.push(`• ${chalk.bold("Database:")} ${chalk.cyan(responses.database === "prisma" ? "Prisma ORM" : responses.database)}`);
|
|
76
|
+
}
|
|
77
|
+
if (responses.reactCompiler !== undefined) {
|
|
78
|
+
lines.push(`• ${chalk.bold("React Compiler:")} ${responses.reactCompiler ? chalk.green("Yes") : chalk.red("No")}`);
|
|
79
|
+
}
|
|
80
|
+
if (responses.routeGroups !== undefined) {
|
|
81
|
+
lines.push(`• ${chalk.bold("Route Groups:")} ${responses.routeGroups ? chalk.green("Yes") : chalk.red("No")}`);
|
|
82
|
+
}
|
|
83
|
+
if (responses.docker !== undefined) {
|
|
84
|
+
lines.push(`• ${chalk.bold("Docker:")} ${responses.docker ? chalk.green("Yes") : chalk.red("No")}`);
|
|
65
85
|
}
|
|
66
86
|
lines.push(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
`• ${chalk.bold("State:")} ${chalk.blue(state)}`,
|
|
88
|
+
`• ${chalk.bold("Icons:")} ${chalk.blue(responses.iconLibrary === "none" ? "None" : responses.iconLibrary === "lucide" ? "Lucide" : "Huge")}`,
|
|
89
|
+
`• ${chalk.bold("API Client:")} ${responses.apiClient === "axios" ? chalk.green("Axios") : responses.apiClient === "fetch" ? chalk.yellow("Fetch") : chalk.red("None")}`,
|
|
90
|
+
`• ${chalk.bold("Linter:")} ${responses.linter === "oxlint" ? chalk.green("Oxlint") : responses.linter === "biome" ? chalk.cyan("Biome") : responses.linter === "eslint" ? chalk.yellow("ESLint") : chalk.red("None")}`,
|
|
91
|
+
`• ${chalk.bold("Formatter:")} ${responses.formatter === "oxfmt" ? chalk.green("Oxfmt") : responses.formatter === "prettier" ? chalk.yellow("Prettier") : chalk.red("None")}`,
|
|
92
|
+
`• ${chalk.bold("Docs:")} ${chalk.cyan(docsLang)}`,
|
|
93
|
+
`• ${chalk.bold("Agent Docs:")} ${responses.agentDocs ? chalk.green("Yes") : chalk.gray("No")}`,
|
|
94
|
+
`• ${chalk.bold("Git:")} ${responses.gitInit ? chalk.green("Yes") : chalk.red("No")}`,
|
|
95
|
+
`• ${chalk.bold("README:")} ${responses.readme ? chalk.green("Yes") : chalk.red("No")}`
|
|
75
96
|
);
|
|
76
97
|
return lines.join("\n");
|
|
77
98
|
}
|
|
@@ -112,7 +133,7 @@ export async function runPromptFlow({
|
|
|
112
133
|
const options = [
|
|
113
134
|
...stepOptions,
|
|
114
135
|
...(canGoBack
|
|
115
|
-
? [{ label: chalk.dim("
|
|
136
|
+
? [{ label: chalk.dim("← Back"), value: BACK, hint: "Return to previous question" }]
|
|
116
137
|
: []),
|
|
117
138
|
];
|
|
118
139
|
|
|
@@ -176,13 +197,20 @@ export async function getUserInputs(
|
|
|
176
197
|
loadConfigFn = loadConfig,
|
|
177
198
|
} = {}
|
|
178
199
|
) {
|
|
179
|
-
|
|
200
|
+
let framework = normalizeFramework(rawFramework);
|
|
201
|
+
// When no framework flag is given, ask interactively as the first step so all
|
|
202
|
+
// downstream prompts gate on the user's selection instead of the default.
|
|
203
|
+
const frameworkLocked =
|
|
204
|
+
rawFramework !== undefined && rawFramework !== null && String(rawFramework).trim() !== "";
|
|
205
|
+
// Variant is locked only when the flag names it explicitly (next:app-router).
|
|
206
|
+
// A bare `-f next` still asks App vs Pages interactively.
|
|
207
|
+
const variantLocked = frameworkLocked && String(rawFramework).includes(":");
|
|
180
208
|
// Non-interactive quick setup: capability-aware defaults without prompting.
|
|
181
209
|
if (quickSetup) {
|
|
182
210
|
const responses = getDefaultResponses(framework, { projectName });
|
|
183
211
|
if (initialArch) responses.architecture = initialArch;
|
|
184
|
-
await saveConfigFn(responses);
|
|
185
|
-
return responses;
|
|
212
|
+
await saveConfigFn(sanitizeResponsesForFramework(framework, responses));
|
|
213
|
+
return sanitizeResponsesForFramework(framework, responses);
|
|
186
214
|
}
|
|
187
215
|
|
|
188
216
|
let oldConfig = null;
|
|
@@ -233,6 +261,68 @@ export async function getUserInputs(
|
|
|
233
261
|
}
|
|
234
262
|
|
|
235
263
|
steps.push(
|
|
264
|
+
{
|
|
265
|
+
id: "framework",
|
|
266
|
+
skip: () => frameworkLocked,
|
|
267
|
+
onSkip: (ans) => {
|
|
268
|
+
ans.frameworkName = framework.name;
|
|
269
|
+
ans.frameworkVariant = framework.variant;
|
|
270
|
+
},
|
|
271
|
+
message: "Which framework do you want to scaffold?",
|
|
272
|
+
getOptions: () => [
|
|
273
|
+
{
|
|
274
|
+
label: "React + Vite",
|
|
275
|
+
value: "react:vite",
|
|
276
|
+
hint: "Client-side SPA with React Router",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
label: "Next.js",
|
|
280
|
+
value: "next",
|
|
281
|
+
hint: "Filesystem routing, server components, route handlers",
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
defaultInitial: "react:vite",
|
|
285
|
+
onAnswer: (val, ans, { steps: allSteps }) => {
|
|
286
|
+
const [name, ...rest] = String(val).split(":");
|
|
287
|
+
framework = normalizeFramework({ name, variant: rest.join(":") });
|
|
288
|
+
ans.frameworkName = framework.name;
|
|
289
|
+
ans.frameworkVariant = framework.variant;
|
|
290
|
+
const qs = allSteps.find((s) => s.id === "quickSetup");
|
|
291
|
+
if (qs) {
|
|
292
|
+
qs.message = isPromptVisible(framework, "router")
|
|
293
|
+
? "Quick Setup? (TypeScript + Tailwind + Feature-based + Router + ESLint + Vitest)"
|
|
294
|
+
: "Quick Setup? (TypeScript + Tailwind + Feature-based + ESLint + Vitest)";
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "nextRouter",
|
|
301
|
+
skip: () => framework.name !== "next" || variantLocked,
|
|
302
|
+
onSkip: (ans) => {
|
|
303
|
+
ans.frameworkVariant = framework.variant;
|
|
304
|
+
},
|
|
305
|
+
message: "Which Next.js router do you want to use?",
|
|
306
|
+
getOptions: () => [
|
|
307
|
+
{
|
|
308
|
+
label: "App Router",
|
|
309
|
+
value: "app-router",
|
|
310
|
+
hint: "Recommended: layouts, server components, route handlers",
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
label: "Pages Router",
|
|
314
|
+
value: "pages-router",
|
|
315
|
+
hint: "Classic pages directory with API routes",
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
defaultInitial: "app-router",
|
|
319
|
+
onAnswer: (val, ans) => {
|
|
320
|
+
framework = normalizeFramework({ name: "next", variant: String(val) });
|
|
321
|
+
ans.frameworkName = framework.name;
|
|
322
|
+
ans.frameworkVariant = framework.variant;
|
|
323
|
+
return null;
|
|
324
|
+
},
|
|
325
|
+
},
|
|
236
326
|
{
|
|
237
327
|
id: "quickSetup",
|
|
238
328
|
message: isPromptVisible(framework, "router")
|
|
@@ -303,6 +393,24 @@ export async function getUserInputs(
|
|
|
303
393
|
]),
|
|
304
394
|
defaultInitial: "tailwind",
|
|
305
395
|
},
|
|
396
|
+
{
|
|
397
|
+
id: "uiKit",
|
|
398
|
+
skip: (ans) => ans.cssFramework !== "tailwind",
|
|
399
|
+
onSkip: (ans) => {
|
|
400
|
+
ans.uiKit = "none";
|
|
401
|
+
},
|
|
402
|
+
message: "Which UI kit do you want to use?",
|
|
403
|
+
getOptions: () =>
|
|
404
|
+
filterCompatibleChoices(framework, "uiKit", [
|
|
405
|
+
{ label: "None", value: "none" },
|
|
406
|
+
{
|
|
407
|
+
label: "shadcn/ui",
|
|
408
|
+
value: "shadcn",
|
|
409
|
+
hint: "Requires Tailwind CSS — primitives land in paths.ui",
|
|
410
|
+
},
|
|
411
|
+
]),
|
|
412
|
+
defaultInitial: "none",
|
|
413
|
+
},
|
|
306
414
|
{
|
|
307
415
|
id: "testing",
|
|
308
416
|
message: "Which testing framework do you want to set up?",
|
|
@@ -311,7 +419,15 @@ export async function getUserInputs(
|
|
|
311
419
|
{ label: "Jest", value: "jest" },
|
|
312
420
|
{ label: "None", value: "none" },
|
|
313
421
|
],
|
|
314
|
-
defaultInitial:
|
|
422
|
+
defaultInitial: (ans) => {
|
|
423
|
+
const fw = normalizeFramework(
|
|
424
|
+
ans.framework || {
|
|
425
|
+
name: ans.frameworkName,
|
|
426
|
+
variant: ans.frameworkVariant,
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
return fw.name === "next" ? "none" : "vitest";
|
|
430
|
+
},
|
|
315
431
|
},
|
|
316
432
|
{
|
|
317
433
|
id: "router",
|
|
@@ -357,6 +473,63 @@ export async function getUserInputs(
|
|
|
357
473
|
]),
|
|
358
474
|
defaultInitial: "none",
|
|
359
475
|
},
|
|
476
|
+
{
|
|
477
|
+
id: "database",
|
|
478
|
+
skip: () => !isPromptVisible(framework, "database"),
|
|
479
|
+
onSkip: (ans) => {
|
|
480
|
+
ans.database = "none";
|
|
481
|
+
},
|
|
482
|
+
message: "Which database ORM do you want to use?",
|
|
483
|
+
getOptions: () =>
|
|
484
|
+
filterCompatibleChoices(framework, "database", [
|
|
485
|
+
{
|
|
486
|
+
label: "Prisma ORM",
|
|
487
|
+
value: "prisma",
|
|
488
|
+
hint: "PostgreSQL schema, client singleton, seed script",
|
|
489
|
+
},
|
|
490
|
+
{ label: "None", value: "none" },
|
|
491
|
+
]),
|
|
492
|
+
defaultInitial: "none",
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
id: "reactCompiler",
|
|
496
|
+
skip: () => !isPromptVisible(framework, "reactCompiler"),
|
|
497
|
+
onSkip: (ans) => {
|
|
498
|
+
ans.reactCompiler = false;
|
|
499
|
+
},
|
|
500
|
+
message: "Would you like to enable the React Compiler (experimental)?",
|
|
501
|
+
getOptions: () => [
|
|
502
|
+
{ label: "No (default)", value: false },
|
|
503
|
+
{ label: "Yes", value: true },
|
|
504
|
+
],
|
|
505
|
+
defaultInitial: false,
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
id: "routeGroups",
|
|
509
|
+
skip: () => !isPromptVisible(framework, "routeGroups"),
|
|
510
|
+
onSkip: (ans) => {
|
|
511
|
+
ans.routeGroups = false;
|
|
512
|
+
},
|
|
513
|
+
message: "Include route groups preset (public, auth, private, admin)?",
|
|
514
|
+
getOptions: () => [
|
|
515
|
+
{ label: "No (default)", value: false },
|
|
516
|
+
{ label: "Yes", value: true },
|
|
517
|
+
],
|
|
518
|
+
defaultInitial: false,
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
id: "docker",
|
|
522
|
+
skip: () => !isPromptVisible(framework, "docker"),
|
|
523
|
+
onSkip: (ans) => {
|
|
524
|
+
ans.docker = false;
|
|
525
|
+
},
|
|
526
|
+
message: "Include Docker setup?",
|
|
527
|
+
getOptions: () => [
|
|
528
|
+
{ label: "No (default)", value: false },
|
|
529
|
+
{ label: "Yes", value: true, hint: "Multi-stage Dockerfile and docker-compose" },
|
|
530
|
+
],
|
|
531
|
+
defaultInitial: false,
|
|
532
|
+
},
|
|
360
533
|
{
|
|
361
534
|
id: "stateManagement",
|
|
362
535
|
message: "Which state management library do you want to use?",
|
|
@@ -446,6 +619,15 @@ export async function getUserInputs(
|
|
|
446
619
|
return compatible.includes(preferred) ? preferred : compatible[0];
|
|
447
620
|
},
|
|
448
621
|
},
|
|
622
|
+
{
|
|
623
|
+
id: "agentDocs",
|
|
624
|
+
message: "Generate AGENTS.md for AI assistants?",
|
|
625
|
+
getOptions: () => [
|
|
626
|
+
{ label: "No (default)", value: false },
|
|
627
|
+
{ label: "Yes", value: true, hint: "Emits AGENTS.md referencing lumen.config.json (SSOT)" },
|
|
628
|
+
],
|
|
629
|
+
defaultInitial: false,
|
|
630
|
+
},
|
|
449
631
|
{
|
|
450
632
|
id: "docsLanguage",
|
|
451
633
|
message: "Which docs language do you want?",
|
|
@@ -499,20 +681,42 @@ export async function getUserInputs(
|
|
|
499
681
|
quickSetup: _quickSetup,
|
|
500
682
|
bundler,
|
|
501
683
|
adapter,
|
|
684
|
+
database,
|
|
685
|
+
reactCompiler,
|
|
686
|
+
routeGroups,
|
|
687
|
+
agentDocs,
|
|
688
|
+
docker,
|
|
502
689
|
...picked
|
|
503
690
|
} = responses;
|
|
504
|
-
|
|
691
|
+
// Final safety net: BACK-navigation can reselect the framework after other
|
|
692
|
+
// answers were collected, so re-validate everything before persisting.
|
|
693
|
+
const finalResponses = sanitizeResponsesForFramework(framework, {
|
|
505
694
|
projectName,
|
|
506
695
|
frameworkName: framework.name,
|
|
507
696
|
frameworkVariant: framework.variant,
|
|
508
697
|
...picked,
|
|
509
|
-
};
|
|
698
|
+
});
|
|
510
699
|
if (bundler !== undefined) {
|
|
511
700
|
finalResponses.bundler = bundler;
|
|
512
701
|
}
|
|
513
702
|
if (adapter !== undefined && adapter !== "none") {
|
|
514
703
|
finalResponses.adapter = adapter;
|
|
515
704
|
}
|
|
705
|
+
if (database !== undefined && database !== "none") {
|
|
706
|
+
finalResponses.database = database;
|
|
707
|
+
}
|
|
708
|
+
if (reactCompiler !== undefined) {
|
|
709
|
+
finalResponses.reactCompiler = reactCompiler;
|
|
710
|
+
}
|
|
711
|
+
if (routeGroups !== undefined) {
|
|
712
|
+
finalResponses.routeGroups = routeGroups;
|
|
713
|
+
}
|
|
714
|
+
if (agentDocs !== undefined) {
|
|
715
|
+
finalResponses.agentDocs = agentDocs;
|
|
716
|
+
}
|
|
717
|
+
if (docker !== undefined) {
|
|
718
|
+
finalResponses.docker = docker;
|
|
719
|
+
}
|
|
516
720
|
|
|
517
721
|
// No config cache write until final confirm!
|
|
518
722
|
await saveConfigFn(finalResponses);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": "create-next-app",
|
|
3
|
+
"source": "create-next-app",
|
|
4
|
+
"version": "16.3.8",
|
|
5
|
+
"flags": [
|
|
6
|
+
"--js",
|
|
7
|
+
"--app",
|
|
8
|
+
"--src-dir",
|
|
9
|
+
"--import-alias",
|
|
10
|
+
"@/*",
|
|
11
|
+
"--no-tailwind",
|
|
12
|
+
"--no-eslint",
|
|
13
|
+
"--skip-install",
|
|
14
|
+
"--disable-git",
|
|
15
|
+
"--no-agents-md",
|
|
16
|
+
"--no-react-compiler"
|
|
17
|
+
],
|
|
18
|
+
"date": "2026-10-06"
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"next": "^16.0.0",
|
|
12
|
+
"react": "^19.0.0",
|
|
13
|
+
"react-dom": "^19.0.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
|
|
3
|
+
export async function generateMetadata({ params }) {
|
|
4
|
+
const resolvedParams = await params;
|
|
5
|
+
const slug = resolvedParams?.slug || "";
|
|
6
|
+
return {
|
|
7
|
+
title: slug,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default async function BlogPost({ params }) {
|
|
12
|
+
const resolvedParams = await params;
|
|
13
|
+
const slug = resolvedParams?.slug || "";
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<main
|
|
17
|
+
style={{
|
|
18
|
+
padding: "2rem",
|
|
19
|
+
fontFamily: "var(--font-geist-sans, sans-serif)",
|
|
20
|
+
maxWidth: "800px",
|
|
21
|
+
margin: "0 auto",
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
<h1>{slug}</h1>
|
|
25
|
+
<p style={{ marginTop: "1rem" }}>Post slug: {slug}</p>
|
|
26
|
+
<p style={{ marginTop: "1.5rem" }}>
|
|
27
|
+
<Link href="/" style={{ textDecoration: "underline" }}>
|
|
28
|
+
Back to home
|
|
29
|
+
</Link>
|
|
30
|
+
</p>
|
|
31
|
+
</main>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--background: #ffffff;
|
|
3
|
+
--foreground: #171717;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@media (prefers-color-scheme: dark) {
|
|
7
|
+
:root {
|
|
8
|
+
--background: #0a0a0a;
|
|
9
|
+
--foreground: #ededed;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
html {
|
|
14
|
+
height: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
html,
|
|
18
|
+
body {
|
|
19
|
+
max-width: 100vw;
|
|
20
|
+
overflow-x: hidden;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
body {
|
|
24
|
+
min-height: 100%;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
color: var(--foreground);
|
|
28
|
+
background: var(--background);
|
|
29
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
30
|
+
-webkit-font-smoothing: antialiased;
|
|
31
|
+
-moz-osx-font-smoothing: grayscale;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
* {
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
padding: 0;
|
|
37
|
+
margin: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
a {
|
|
41
|
+
color: inherit;
|
|
42
|
+
text-decoration: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media (prefers-color-scheme: dark) {
|
|
46
|
+
html {
|
|
47
|
+
color-scheme: dark;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
2
|
+
import "./globals.css";
|
|
3
|
+
|
|
4
|
+
const geistSans = Geist({
|
|
5
|
+
variable: "--font-geist-sans",
|
|
6
|
+
subsets: ["latin"],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const geistMono = Geist_Mono({
|
|
10
|
+
variable: "--font-geist-mono",
|
|
11
|
+
subsets: ["latin"],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const metadata = {
|
|
15
|
+
title: "Create Next App",
|
|
16
|
+
description: "Generated by create next app",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default function RootLayout({ children }) {
|
|
20
|
+
return (
|
|
21
|
+
<html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
|
|
22
|
+
<body>{children}</body>
|
|
23
|
+
</html>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function Loading() {
|
|
2
|
+
return (
|
|
3
|
+
<div
|
|
4
|
+
style={{
|
|
5
|
+
display: "flex",
|
|
6
|
+
alignItems: "center",
|
|
7
|
+
justifyContent: "center",
|
|
8
|
+
minHeight: "50vh",
|
|
9
|
+
fontFamily: "var(--font-geist-sans, sans-serif)",
|
|
10
|
+
}}
|
|
11
|
+
>
|
|
12
|
+
<p>Loading...</p>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
|
|
3
|
+
export default function NotFound() {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
style={{
|
|
7
|
+
display: "flex",
|
|
8
|
+
flexDirection: "column",
|
|
9
|
+
alignItems: "center",
|
|
10
|
+
justifyContent: "center",
|
|
11
|
+
minHeight: "100vh",
|
|
12
|
+
fontFamily: "var(--font-geist-sans, sans-serif)",
|
|
13
|
+
padding: "2rem",
|
|
14
|
+
textAlign: "center",
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
<h1>404 - Page Not Found</h1>
|
|
18
|
+
<p style={{ marginTop: "1rem" }}>
|
|
19
|
+
The page you are looking for does not exist.
|
|
20
|
+
</p>
|
|
21
|
+
<Link
|
|
22
|
+
href="/"
|
|
23
|
+
style={{ marginTop: "1.5rem", textDecoration: "underline" }}
|
|
24
|
+
>
|
|
25
|
+
Go back home
|
|
26
|
+
</Link>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|