@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 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") console.log(chalk.dim(" Database:"), config.dbName);
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") console.log(chalk.dim(" Database:"), config.dbName);
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") {