@percepta/create 3.5.1 → 3.6.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 +61 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/template-versions.json +1 -1
- package/templates/monorepo/package.json.template +1 -1
- package/templates/webapp/README.md +2 -0
- package/templates/webapp/e2e/rbac.spec.ts +59 -25
- package/templates/webapp/package.json.template +2 -2
- package/templates/webapp/src/app/(app)/layout.tsx +4 -8
- package/templates/webapp/src/app/(app)/page.tsx +148 -7
- package/templates/webapp/src/app/(auth)/auth/signin/CredentialsSignInForm.tsx +29 -11
- package/templates/webapp/src/app/(auth)/auth/signup/CredentialsSignUpForm.tsx +34 -12
- package/templates/webapp/src/app/(auth)/layout.tsx +30 -4
- package/templates/webapp/src/app/{(admin) → (settings)}/layout.tsx +6 -10
- package/templates/webapp/src/app/{(admin)/admin/_components/AdminTabs.tsx → (settings)/settings/_components/AccessControlTabs.tsx} +10 -6
- package/templates/webapp/src/app/{(admin)/admin/_lib/accessAdmin.ts → (settings)/settings/_lib/accessSettings.ts} +6 -6
- package/templates/webapp/src/app/{(admin)/admin → (settings)/settings}/page.tsx +99 -52
- package/templates/webapp/src/app/layout.tsx +1 -1
- package/templates/webapp/src/components/Header.tsx +27 -16
- package/templates/webapp/src/styles/globals.css +785 -8
package/dist/index.js
CHANGED
|
@@ -77,6 +77,17 @@ async function copyTemplate(targetDir, templateType) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
//#endregion
|
|
80
|
+
//#region src/utils/design-theme.ts
|
|
81
|
+
const VALID_MOSAIC_DESIGN_THEMES = [
|
|
82
|
+
"paper",
|
|
83
|
+
"modern",
|
|
84
|
+
"dense"
|
|
85
|
+
];
|
|
86
|
+
const DEFAULT_MOSAIC_DESIGN_THEME = "modern";
|
|
87
|
+
function isValidMosaicDesignTheme(value) {
|
|
88
|
+
return typeof value === "string" && VALID_MOSAIC_DESIGN_THEMES.includes(value);
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
80
91
|
//#region src/utils/detect-monorepo.ts
|
|
81
92
|
const NOT_FOUND = {
|
|
82
93
|
found: false,
|
|
@@ -172,7 +183,7 @@ async function writeManifest(dir, manifest) {
|
|
|
172
183
|
async function manifestExists(dir) {
|
|
173
184
|
return fs.pathExists(getManifestPath(dir));
|
|
174
185
|
}
|
|
175
|
-
function derivePlaceholders(appName, appTitle, repoName = appName) {
|
|
186
|
+
function derivePlaceholders(appName, appTitle, repoName = appName, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
|
|
176
187
|
const nameSnake = appName.replace(/-/g, "_");
|
|
177
188
|
const repoNameSnake = repoName.replace(/-/g, "_");
|
|
178
189
|
return {
|
|
@@ -182,7 +193,8 @@ function derivePlaceholders(appName, appTitle, repoName = appName) {
|
|
|
182
193
|
__APP_NAME_UPPER__: appName.toUpperCase(),
|
|
183
194
|
__APP_NAME_SNAKE__: nameSnake,
|
|
184
195
|
__REPO_NAME__: repoName,
|
|
185
|
-
__REPO_NAME_SNAKE__: repoNameSnake
|
|
196
|
+
__REPO_NAME_SNAKE__: repoNameSnake,
|
|
197
|
+
__MOSAIC_DESIGN_THEME__: designTheme
|
|
186
198
|
};
|
|
187
199
|
}
|
|
188
200
|
function resolveMosaicTemplatePath(options) {
|
|
@@ -241,6 +253,25 @@ async function promptName(message) {
|
|
|
241
253
|
}]);
|
|
242
254
|
return name;
|
|
243
255
|
}
|
|
256
|
+
async function promptDesignTheme() {
|
|
257
|
+
const { designTheme } = await inquirer.prompt([{
|
|
258
|
+
type: "rawlist",
|
|
259
|
+
name: "designTheme",
|
|
260
|
+
message: "Design theme?",
|
|
261
|
+
default: DEFAULT_MOSAIC_DESIGN_THEME,
|
|
262
|
+
choices: VALID_MOSAIC_DESIGN_THEMES.map((theme) => ({
|
|
263
|
+
name: theme,
|
|
264
|
+
value: theme
|
|
265
|
+
}))
|
|
266
|
+
}]);
|
|
267
|
+
return designTheme;
|
|
268
|
+
}
|
|
269
|
+
async function resolveDesignTheme(projectType, defaults) {
|
|
270
|
+
if (projectType !== "webapp") return void 0;
|
|
271
|
+
if (defaults.designTheme) return defaults.designTheme;
|
|
272
|
+
if (defaults.projectType && defaults.name) return DEFAULT_MOSAIC_DESIGN_THEME;
|
|
273
|
+
return promptDesignTheme();
|
|
274
|
+
}
|
|
244
275
|
/**
|
|
245
276
|
* Outside a monorepo we collect the repo name first, then ask a single
|
|
246
277
|
* "is it a webapp?" Y/n. Yes (default) → monorepo + webapp, no → bare
|
|
@@ -308,6 +339,7 @@ async function promptProjectDetails(defaults) {
|
|
|
308
339
|
}
|
|
309
340
|
const packageNamePrompt = projectType === "webapp" ? "Webapp name?" : "Library name?";
|
|
310
341
|
finalName = defaults.name || await promptName(packageNamePrompt);
|
|
342
|
+
const designTheme = await resolveDesignTheme(projectType, defaults);
|
|
311
343
|
const finalTitle = toTitleCase(finalName);
|
|
312
344
|
const finalDirectory = path.resolve(cwd, repoName);
|
|
313
345
|
return {
|
|
@@ -316,18 +348,21 @@ async function promptProjectDetails(defaults) {
|
|
|
316
348
|
name: finalName,
|
|
317
349
|
title: finalTitle,
|
|
318
350
|
installDeps: !defaults.skipInstall,
|
|
351
|
+
designTheme,
|
|
319
352
|
monorepoName: repoName,
|
|
320
353
|
monorepoTitle: repoTitle
|
|
321
354
|
};
|
|
322
355
|
}
|
|
323
356
|
const finalTitle = finalName ? toTitleCase(finalName) : "";
|
|
357
|
+
const designTheme = await resolveDesignTheme(projectType, defaults);
|
|
324
358
|
const finalDirectory = !inMonorepo && finalName ? path.resolve(cwd, finalName) : "";
|
|
325
359
|
return {
|
|
326
360
|
projectType,
|
|
327
361
|
directory: finalDirectory,
|
|
328
362
|
name: finalName,
|
|
329
363
|
title: finalTitle,
|
|
330
|
-
installDeps: !defaults.skipInstall
|
|
364
|
+
installDeps: !defaults.skipInstall,
|
|
365
|
+
designTheme
|
|
331
366
|
};
|
|
332
367
|
}
|
|
333
368
|
//#endregion
|
|
@@ -371,7 +406,8 @@ const PLACEHOLDERS = {
|
|
|
371
406
|
__REPO_NAME__: "repoName",
|
|
372
407
|
__REPO_NAME_SNAKE__: "repoNameSnake",
|
|
373
408
|
__CREATE_PACKAGE__: "createPackage",
|
|
374
|
-
__CREATE_VERSION__: "createVersion"
|
|
409
|
+
__CREATE_VERSION__: "createVersion",
|
|
410
|
+
__MOSAIC_DESIGN_THEME__: "designTheme"
|
|
375
411
|
};
|
|
376
412
|
const SKIP_DIRS = new Set([
|
|
377
413
|
"node_modules",
|
|
@@ -722,13 +758,13 @@ async function writeMosaicFiles(packageDir, config, projectType, templateVersion
|
|
|
722
758
|
templateVersion,
|
|
723
759
|
templateCommit,
|
|
724
760
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
725
|
-
placeholders: derivePlaceholders(config.name, config.title, config.repoName),
|
|
761
|
+
placeholders: derivePlaceholders(config.name, config.title, config.repoName, config.designTheme),
|
|
726
762
|
source: { templatePath: `packages/blueberry/templates/${projectType}` }
|
|
727
763
|
});
|
|
728
764
|
const notesPath = path.join(packageDir, "mosaic-template-notes.md");
|
|
729
765
|
await fs.writeFile(notesPath, `# Mosaic Divergence Notes\n\nDocument intentional differences from the ${projectType} template here.\nClaude reads this file during sync to preserve your customizations.\n\n## Intentional Divergences\n\n_None yet — freshly created from template._\n`);
|
|
730
766
|
}
|
|
731
|
-
function buildAppConfig(name, title = toTitleCase(name), repoName = name) {
|
|
767
|
+
function buildAppConfig(name, title = toTitleCase(name), repoName = name, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
|
|
732
768
|
const createPackage = readCreatePackageMetadata();
|
|
733
769
|
return {
|
|
734
770
|
name,
|
|
@@ -739,7 +775,8 @@ function buildAppConfig(name, title = toTitleCase(name), repoName = name) {
|
|
|
739
775
|
repoName,
|
|
740
776
|
repoNameSnake: toSnakeCase(repoName),
|
|
741
777
|
createPackage: createPackage.name,
|
|
742
|
-
createVersion: createPackage.version
|
|
778
|
+
createVersion: createPackage.version,
|
|
779
|
+
designTheme
|
|
743
780
|
};
|
|
744
781
|
}
|
|
745
782
|
/** Copy the monorepo template into `targetDir` and replace its placeholders. */
|
|
@@ -890,6 +927,10 @@ async function createProject(options) {
|
|
|
890
927
|
console.error(chalk.red(`Error: Invalid package type "${options.type}". Valid types are: ${VALID_PROJECT_TYPES.join(", ")}`));
|
|
891
928
|
process.exit(1);
|
|
892
929
|
}
|
|
930
|
+
if (options.theme !== void 0 && !isValidMosaicDesignTheme(options.theme)) {
|
|
931
|
+
console.error(chalk.red(`Error: Invalid design theme "${options.theme}". Valid themes are: ${VALID_MOSAIC_DESIGN_THEMES.join(", ")}`));
|
|
932
|
+
process.exit(1);
|
|
933
|
+
}
|
|
893
934
|
console.log();
|
|
894
935
|
console.log(chalk.bold("Creating a new Mosaic package..."));
|
|
895
936
|
console.log();
|
|
@@ -946,6 +987,7 @@ async function createProject(options) {
|
|
|
946
987
|
name: kebabName,
|
|
947
988
|
title: toTitleCase(kebabName),
|
|
948
989
|
installDeps: !options.skipInstall,
|
|
990
|
+
designTheme: projectType === "webapp" ? options.theme ?? "modern" : void 0,
|
|
949
991
|
monorepoName: monorepoContext.found ? void 0 : kebabRepoName,
|
|
950
992
|
monorepoTitle: monorepoContext.found ? void 0 : toTitleCase(kebabRepoName)
|
|
951
993
|
};
|
|
@@ -953,6 +995,7 @@ async function createProject(options) {
|
|
|
953
995
|
answers = await promptProjectDetails({
|
|
954
996
|
projectType: options.type,
|
|
955
997
|
name: projectName ? toKebabCase(projectName) : void 0,
|
|
998
|
+
designTheme: options.theme,
|
|
956
999
|
repoName: repoName ? toKebabCase(repoName) : void 0,
|
|
957
1000
|
skipInstall: options.skipInstall,
|
|
958
1001
|
monorepoContext,
|
|
@@ -967,7 +1010,7 @@ async function createProject(options) {
|
|
|
967
1010
|
const monorepoName = answers.monorepoName ?? answers.name;
|
|
968
1011
|
const monorepoConfig = buildAppConfig(monorepoName, answers.monorepoTitle ?? toTitleCase(monorepoName));
|
|
969
1012
|
const configRepoName = monorepoContext.found ? path.basename(monorepoContext.rootDir) : monorepoName;
|
|
970
|
-
const config = buildAppConfig(answers.name, answers.title, configRepoName);
|
|
1013
|
+
const config = buildAppConfig(answers.name, answers.title, configRepoName, answers.designTheme);
|
|
971
1014
|
const typeLabel = getProjectTypeLabel(answers.projectType);
|
|
972
1015
|
if (monorepoContext.found) {
|
|
973
1016
|
const monorepoRoot = monorepoContext.rootDir;
|
|
@@ -976,7 +1019,10 @@ async function createProject(options) {
|
|
|
976
1019
|
console.log(chalk.dim(" Target:"), packageDir);
|
|
977
1020
|
console.log(chalk.dim(" Name:"), config.name);
|
|
978
1021
|
console.log(chalk.dim(" Title:"), config.title);
|
|
979
|
-
if (answers.projectType === "webapp")
|
|
1022
|
+
if (answers.projectType === "webapp") {
|
|
1023
|
+
console.log(chalk.dim(" Database:"), config.dbName);
|
|
1024
|
+
console.log(chalk.dim(" Design theme:"), config.designTheme);
|
|
1025
|
+
}
|
|
980
1026
|
console.log();
|
|
981
1027
|
if (await fs.pathExists(packageDir)) {
|
|
982
1028
|
if ((await fs.readdir(packageDir)).length > 0) {
|
|
@@ -1015,7 +1061,10 @@ async function createProject(options) {
|
|
|
1015
1061
|
console.log(chalk.dim(" Package:"), `packages/${answers.name}/`);
|
|
1016
1062
|
console.log(chalk.dim(" Name:"), config.name);
|
|
1017
1063
|
console.log(chalk.dim(" Title:"), config.title);
|
|
1018
|
-
if (answers.projectType === "webapp")
|
|
1064
|
+
if (answers.projectType === "webapp") {
|
|
1065
|
+
console.log(chalk.dim(" Database:"), config.dbName);
|
|
1066
|
+
console.log(chalk.dim(" Design theme:"), config.designTheme);
|
|
1067
|
+
}
|
|
1019
1068
|
}
|
|
1020
1069
|
console.log();
|
|
1021
1070
|
if (await fs.pathExists(monorepoRoot)) {
|
|
@@ -1199,8 +1248,8 @@ function printNextStepsExisting(answers, packageDir, installNeeded) {
|
|
|
1199
1248
|
//#region src/index.ts
|
|
1200
1249
|
const packageJson = readCreatePackageMetadata();
|
|
1201
1250
|
program.name("create").description("Scaffold and manage Mosaic packages").version(packageJson.version);
|
|
1202
|
-
program.command("create", { isDefault: true }).description("Scaffold a new Mosaic package").option("-t, --type <type>", "Package type: monorepo, webapp, or library").option("--name <name>", "Package/app name").option("--repo-name <name>", "Repository name when creating a new monorepo").option("--cwd <dir>", "Run create as if started from this directory").option("--skip-install", "Skip dependency installation (also skips the auto-run setup + dev + browser)", false).option("-y, --yes", "Skip all prompts and use defaults", false).action(createProject);
|
|
1203
|
-
program.command("add").description("Add a Mosaic package to the current monorepo").argument("[typeOrName]", "Package type (webapp/library) or package name").argument("[name]", "Package/app name").option("-t, --type <type>", "Package type: webapp or library").option("--name <name>", "Package/app name").option("--skip-install", "Skip dependency installation (also skips the auto-run setup + dev + browser)", false).option("-y, --yes", "Skip all prompts and use defaults", false).action(async (typeOrName, name, options) => {
|
|
1251
|
+
program.command("create", { isDefault: true }).description("Scaffold a new Mosaic package").option("-t, --type <type>", "Package type: monorepo, webapp, or library").option("--name <name>", "Package/app name").option("--theme <theme>", "Webapp design theme: modern, paper, or dense").option("--repo-name <name>", "Repository name when creating a new monorepo").option("--cwd <dir>", "Run create as if started from this directory").option("--skip-install", "Skip dependency installation (also skips the auto-run setup + dev + browser)", false).option("-y, --yes", "Skip all prompts and use defaults", false).action(createProject);
|
|
1252
|
+
program.command("add").description("Add a Mosaic package to the current monorepo").argument("[typeOrName]", "Package type (webapp/library) or package name").argument("[name]", "Package/app name").option("-t, --type <type>", "Package type: webapp or library").option("--name <name>", "Package/app name").option("--theme <theme>", "Webapp design theme: modern, paper, or dense").option("--skip-install", "Skip dependency installation (also skips the auto-run setup + dev + browser)", false).option("-y, --yes", "Skip all prompts and use defaults", false).action(async (typeOrName, name, options) => {
|
|
1204
1253
|
let projectType = options.type;
|
|
1205
1254
|
let projectName = options.name;
|
|
1206
1255
|
if (typeOrName === "webapp" || typeOrName === "library") {
|