shapes-ui 0.5.0 → 0.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.
Files changed (83) hide show
  1. package/.github/workflows/pr-preview.yml +9 -2
  2. package/CHANGELOG.md +11 -0
  3. package/README.md +13 -0
  4. package/content/components/accordion.mdx +13 -0
  5. package/content/components/alert-dialog.mdx +34 -0
  6. package/content/components/autocomplete.mdx +62 -0
  7. package/content/components/avatar.mdx +11 -0
  8. package/content/components/button.mdx +8 -0
  9. package/content/components/checkbox.mdx +11 -0
  10. package/content/components/collapsible.mdx +11 -0
  11. package/content/components/combobox.mdx +33 -0
  12. package/content/components/context-menu.mdx +29 -0
  13. package/content/components/dialog.mdx +33 -0
  14. package/content/components/drawer.mdx +38 -0
  15. package/content/components/field.mdx +21 -0
  16. package/content/components/fieldset.mdx +10 -0
  17. package/content/components/form.mdx +8 -0
  18. package/content/components/input.mdx +4 -0
  19. package/content/components/menu.mdx +27 -0
  20. package/content/components/menubar.mdx +31 -0
  21. package/content/components/meter.mdx +14 -0
  22. package/content/components/navigation-menu.mdx +28 -0
  23. package/content/components/number-field.mdx +25 -0
  24. package/content/components/popover.mdx +22 -0
  25. package/content/components/preview-card.mdx +14 -2
  26. package/content/components/progress.mdx +15 -1
  27. package/content/components/radio.mdx +11 -0
  28. package/content/components/scroll-area.mdx +23 -0
  29. package/content/components/select.mdx +27 -0
  30. package/content/components/separator.mdx +29 -0
  31. package/content/components/slider.mdx +4 -0
  32. package/content/components/switch.mdx +4 -0
  33. package/content/components/tabs.mdx +15 -0
  34. package/content/components/toast.mdx +10 -0
  35. package/content/components/toggle-group.mdx +37 -0
  36. package/content/components/toggle.mdx +12 -0
  37. package/content/components/toolbar.mdx +22 -0
  38. package/content/components/tooltip.mdx +13 -0
  39. package/content/docs/installation.mdx +30 -0
  40. package/content-collections.ts +65 -1
  41. package/dist/cli.js +947 -101
  42. package/examples/__index.tsx +136 -68
  43. package/examples/autocomplete-align.tsx +39 -0
  44. package/examples/autocomplete-controlled.tsx +44 -0
  45. package/examples/autocomplete-groups.tsx +65 -0
  46. package/examples/autocomplete-no-clear.tsx +40 -0
  47. package/examples/avatar-demo.tsx +3 -3
  48. package/examples/input-group-with-button.tsx +1 -1
  49. package/examples/separator-demo.tsx +13 -0
  50. package/examples/separator-horizontal.tsx +18 -0
  51. package/package.json +19 -18
  52. package/public/base-ui.svg +1 -0
  53. package/src/assets/base-ui.svg +1 -0
  54. package/src/commands/add.ts +79 -38
  55. package/src/commands/cli.ts +50 -3
  56. package/src/commands/create.ts +262 -0
  57. package/src/commands/init.ts +45 -12
  58. package/src/commands/palette.ts +55 -0
  59. package/src/components/docs/layout/footer.tsx +2 -2
  60. package/src/components/docs/layout/header.tsx +7 -9
  61. package/src/components/docs/layout/mobile-menu.tsx +0 -1
  62. package/src/components/docs/layout/nav-list.tsx +2 -2
  63. package/src/components/docs/layout/page-header.tsx +52 -7
  64. package/src/components/docs/layout/split-layout.tsx +9 -10
  65. package/src/components/docs/layout/table-of-content.tsx +145 -0
  66. package/src/components/docs/markdown/components.tsx +142 -29
  67. package/src/components/docs/markdown/copy-button.tsx +41 -0
  68. package/src/components/docs/markdown/installation-block.tsx +5 -24
  69. package/src/components/docs/markdown/render-preview.tsx +1 -1
  70. package/src/components/ui/button-group.tsx +1 -1
  71. package/src/components/ui/scroll-area.tsx +11 -2
  72. package/src/lib/docs-headings.ts +72 -0
  73. package/src/routeTree.gen.ts +60 -3
  74. package/src/routes/__root.tsx +2 -2
  75. package/src/routes/components.$slug.tsx +20 -4
  76. package/src/routes/docs.$slug.tsx +78 -0
  77. package/src/routes/docs.tsx +15 -0
  78. package/src/styles/styles.css +1 -1
  79. package/src/utils/cli-utils.ts +8 -8
  80. package/src/utils/dependency-installer.ts +30 -0
  81. package/src/utils/package-manager.ts +4 -4
  82. package/src/utils/palette.ts +666 -0
  83. package/src/utils/schema.ts +6 -0
package/dist/cli.js CHANGED
@@ -784,10 +784,10 @@ function mergeDefs(...defs) {
784
784
  function cloneDef(schema) {
785
785
  return mergeDefs(schema._zod.def);
786
786
  }
787
- function getElementAtPath(obj, path4) {
788
- if (!path4)
787
+ function getElementAtPath(obj, path7) {
788
+ if (!path7)
789
789
  return obj;
790
- return path4.reduce((acc, key) => acc?.[key], obj);
790
+ return path7.reduce((acc, key) => acc?.[key], obj);
791
791
  }
792
792
  function promiseAllObject(promisesObj) {
793
793
  const keys = Object.keys(promisesObj);
@@ -1170,11 +1170,11 @@ function aborted(x, startIndex = 0) {
1170
1170
  }
1171
1171
  return false;
1172
1172
  }
1173
- function prefixIssues(path4, issues) {
1173
+ function prefixIssues(path7, issues) {
1174
1174
  return issues.map((iss) => {
1175
1175
  var _a2;
1176
1176
  (_a2 = iss).path ?? (_a2.path = []);
1177
- iss.path.unshift(path4);
1177
+ iss.path.unshift(path7);
1178
1178
  return iss;
1179
1179
  });
1180
1180
  }
@@ -1357,7 +1357,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
1357
1357
  }
1358
1358
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
1359
1359
  const result = { errors: [] };
1360
- const processError = (error49, path4 = []) => {
1360
+ const processError = (error49, path7 = []) => {
1361
1361
  var _a2, _b;
1362
1362
  for (const issue2 of error49.issues) {
1363
1363
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -1367,7 +1367,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
1367
1367
  } else if (issue2.code === "invalid_element") {
1368
1368
  processError({ issues: issue2.issues }, issue2.path);
1369
1369
  } else {
1370
- const fullpath = [...path4, ...issue2.path];
1370
+ const fullpath = [...path7, ...issue2.path];
1371
1371
  if (fullpath.length === 0) {
1372
1372
  result.errors.push(mapper(issue2));
1373
1373
  continue;
@@ -1399,8 +1399,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
1399
1399
  }
1400
1400
  function toDotPath(_path) {
1401
1401
  const segs = [];
1402
- const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1403
- for (const seg of path4) {
1402
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1403
+ for (const seg of path7) {
1404
1404
  if (typeof seg === "number")
1405
1405
  segs.push(`[${seg}]`);
1406
1406
  else if (typeof seg === "symbol")
@@ -7249,8 +7249,8 @@ function ko_default() {
7249
7249
  }
7250
7250
 
7251
7251
  // node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/locales/lt.js
7252
- var capitalizeFirstCharacter = (text2) => {
7253
- return text2.charAt(0).toUpperCase() + text2.slice(1);
7252
+ var capitalizeFirstCharacter = (text3) => {
7253
+ return text3.charAt(0).toUpperCase() + text3.slice(1);
7254
7254
  };
7255
7255
  function getUnitTypeFromNumber(number4) {
7256
7256
  const abs = Math.abs(number4);
@@ -13377,13 +13377,13 @@ function resolveRef(ref, ctx) {
13377
13377
  if (!ref.startsWith("#")) {
13378
13378
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
13379
13379
  }
13380
- const path4 = ref.slice(1).split("/").filter(Boolean);
13381
- if (path4.length === 0) {
13380
+ const path7 = ref.slice(1).split("/").filter(Boolean);
13381
+ if (path7.length === 0) {
13382
13382
  return ctx.rootSchema;
13383
13383
  }
13384
13384
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
13385
- if (path4[0] === defsKey) {
13386
- const key = path4[1];
13385
+ if (path7[0] === defsKey) {
13386
+ const key = path7[1];
13387
13387
  if (!key || !ctx.defs[key]) {
13388
13388
  throw new Error(`Reference not found: ${ref}`);
13389
13389
  }
@@ -13790,6 +13790,10 @@ import { zodToJsonSchema } from "zod-to-json-schema";
13790
13790
  var configSchema = external_exports.object({
13791
13791
  $schema: external_exports.string().optional(),
13792
13792
  style: external_exports.enum(["default", "brutalist", "minimal"]).default("default"),
13793
+ palette: external_exports.object({
13794
+ name: external_exports.string().default("blue"),
13795
+ contrastMode: external_exports.enum(["deterministic", "dynamic"]).default("deterministic")
13796
+ }).default({ name: "blue", contrastMode: "deterministic" }),
13793
13797
  tailwind: external_exports.object({
13794
13798
  config: external_exports.string().optional(),
13795
13799
  css: external_exports.string().default("src/styles/globals.css"),
@@ -13809,13 +13813,13 @@ function exitIfCancelled(value) {
13809
13813
  }
13810
13814
  return value;
13811
13815
  }
13812
- async function getConfig() {
13813
- const configPath = path.join(process.cwd(), "shapes.json");
13816
+ async function getConfig(cwd = process.cwd()) {
13817
+ const configPath = path.join(cwd, "shapes.json");
13814
13818
  if (!fs2.existsSync(configPath)) return null;
13815
13819
  return configSchema.parse(await fs2.readJSON(configPath));
13816
13820
  }
13817
- async function readPackageJson() {
13818
- const pkgPath = path.join(process.cwd(), "package.json");
13821
+ async function readPackageJson(cwd = process.cwd()) {
13822
+ const pkgPath = path.join(cwd, "package.json");
13819
13823
  if (!await fs2.pathExists(pkgPath)) return null;
13820
13824
  return fs2.readJSON(pkgPath);
13821
13825
  }
@@ -13835,30 +13839,33 @@ function getMissingDeps(pkg, deps) {
13835
13839
  if (!pkg) return deps;
13836
13840
  return deps.filter((dep) => !pkg.dependencies?.[dep] && !pkg.devDependencies?.[dep]);
13837
13841
  }
13838
- async function isViteProject() {
13839
- const viteConfigTs = path.join(process.cwd(), "vite.config.ts");
13840
- const viteConfigJs = path.join(process.cwd(), "vite.config.js");
13841
- const viteConfigMjs = path.join(process.cwd(), "vite.config.mjs");
13842
+ async function isViteProject(cwd = process.cwd()) {
13843
+ const viteConfigTs = path.join(cwd, "vite.config.ts");
13844
+ const viteConfigJs = path.join(cwd, "vite.config.js");
13845
+ const viteConfigMjs = path.join(cwd, "vite.config.mjs");
13842
13846
  return await fs2.pathExists(viteConfigTs) || await fs2.pathExists(viteConfigJs) || await fs2.pathExists(viteConfigMjs);
13843
13847
  }
13844
13848
 
13845
13849
  // src/commands/add.ts
13846
13850
  import path2 from "path";
13847
- import { cancel as cancel2, multiselect, note, spinner } from "@clack/prompts";
13848
- import { execa } from "execa";
13851
+ import { cancel as cancel2, multiselect, note, spinner as spinner2 } from "@clack/prompts";
13849
13852
  import fs3 from "fs-extra";
13850
13853
 
13854
+ // src/utils/dependency-installer.ts
13855
+ import { spinner } from "@clack/prompts";
13856
+ import { execa } from "execa";
13857
+
13851
13858
  // src/utils/package-manager.ts
13852
13859
  import { detect } from "detect-package-manager";
13853
- async function getPackageManager() {
13860
+ async function getPackageManager(cwd = process.cwd()) {
13854
13861
  try {
13855
- return await detect();
13862
+ return await detect({ cwd });
13856
13863
  } catch {
13857
13864
  return "npm";
13858
13865
  }
13859
13866
  }
13860
- async function getInstallCommand(deps, dev = false) {
13861
- const pm = await getPackageManager();
13867
+ async function getInstallCommand(deps, dev = false, cwd = process.cwd()) {
13868
+ const pm = await getPackageManager(cwd);
13862
13869
  if (pm === "yarn") {
13863
13870
  return ["yarn", "add", ...dev ? ["-D"] : [], ...deps];
13864
13871
  }
@@ -13871,10 +13878,22 @@ async function getInstallCommand(deps, dev = false) {
13871
13878
  return ["npm", "install", ...dev ? ["--save-dev"] : [], ...deps];
13872
13879
  }
13873
13880
 
13881
+ // src/utils/dependency-installer.ts
13882
+ async function installDependencies(deps, options = {}) {
13883
+ if (!deps.length) return;
13884
+ const spin = spinner();
13885
+ const label = options.label ?? (options.dev ? "Installing dev dependencies" : "Installing dependencies");
13886
+ spin.start(label);
13887
+ const cwd = options.cwd ?? process.cwd();
13888
+ const [command, ...args] = await getInstallCommand(deps, options.dev ?? false, cwd);
13889
+ await execa(command, args, { cwd });
13890
+ spin.stop(options.successMessage ?? "Dependencies installed");
13891
+ }
13892
+
13874
13893
  // src/commands/add.ts
13875
13894
  var REGISTRY_URL = "https://shapes-ui.com/r";
13876
- async function loadRegistryIndex() {
13877
- const localRegistryDir = path2.resolve(process.cwd(), "public/r");
13895
+ async function loadRegistryIndex(cwd = process.cwd()) {
13896
+ const localRegistryDir = path2.resolve(cwd, "public/r");
13878
13897
  if (await fs3.pathExists(localRegistryDir)) {
13879
13898
  const files = await fs3.readdir(localRegistryDir);
13880
13899
  const entries = [];
@@ -13899,7 +13918,7 @@ async function loadRegistryIndex() {
13899
13918
  async function pickComponents() {
13900
13919
  let components = [];
13901
13920
  try {
13902
- components = await loadRegistryIndex();
13921
+ components = await loadRegistryIndex(process.cwd());
13903
13922
  } catch (error48) {
13904
13923
  cancel2(error48 instanceof Error ? error48.message : "Failed to load registry.");
13905
13924
  process.exit(1);
@@ -13916,59 +13935,607 @@ async function pickComponents() {
13916
13935
  );
13917
13936
  return selected;
13918
13937
  }
13919
- async function installComponent(name, config2) {
13920
- const spin = spinner();
13921
- spin.start(`Fetching ${name}`);
13922
- let data;
13923
- const localFile = path2.resolve(process.cwd(), `public/r/${name}.json`);
13924
- if (await fs3.pathExists(localFile)) {
13925
- data = await fs3.readJSON(localFile);
13926
- } else {
13927
- const res = await fetch(`${REGISTRY_URL}/${name}.json`);
13928
- if (!res.ok) {
13929
- spin.stop("Fetch failed");
13930
- throw new Error(`Component ${name} not found in registry.`);
13931
- }
13932
- data = await res.json();
13938
+ function createInstallContext() {
13939
+ return {
13940
+ installing: /* @__PURE__ */ new Set(),
13941
+ installed: /* @__PURE__ */ new Set()
13942
+ };
13943
+ }
13944
+ async function installComponent(name, config2, context = createInstallContext(), cwd = process.cwd()) {
13945
+ if (context.installed.has(name)) return;
13946
+ if (context.installing.has(name)) {
13947
+ note(`Skipped circular dependency while resolving ${name}`);
13948
+ return;
13933
13949
  }
13934
- spin.stop(`Fetched ${name}`);
13935
- if (data.registryDependencies) {
13936
- for (const dep of data.registryDependencies) {
13937
- const depPath = path2.join(process.cwd(), config2.paths.ui, `${dep}.tsx`);
13938
- if (!fs3.existsSync(depPath)) {
13939
- await installComponent(dep, config2);
13950
+ context.installing.add(name);
13951
+ try {
13952
+ const spin = spinner2();
13953
+ spin.start(`Fetching ${name}`);
13954
+ let data;
13955
+ const localFile = path2.resolve(cwd, `public/r/${name}.json`);
13956
+ if (await fs3.pathExists(localFile)) {
13957
+ data = await fs3.readJSON(localFile);
13958
+ } else {
13959
+ const res = await fetch(`${REGISTRY_URL}/${name}.json`);
13960
+ if (!res.ok) {
13961
+ spin.stop("Fetch failed");
13962
+ throw new Error(`Component ${name} not found in registry.`);
13940
13963
  }
13964
+ data = await res.json();
13941
13965
  }
13966
+ spin.stop(`Fetched ${name}`);
13967
+ if (data.registryDependencies) {
13968
+ for (const dep of data.registryDependencies) {
13969
+ const depPath = path2.join(cwd, config2.paths.ui, `${dep}.tsx`);
13970
+ if (!fs3.existsSync(depPath)) {
13971
+ await installComponent(dep, config2, context, cwd);
13972
+ }
13973
+ }
13974
+ }
13975
+ if (data.dependencies?.length) {
13976
+ await installDependencies(data.dependencies, {
13977
+ label: `Installing dependencies for ${name}`,
13978
+ successMessage: `Dependencies installed for ${name}`,
13979
+ cwd
13980
+ });
13981
+ }
13982
+ for (const file2 of data.files) {
13983
+ const target = path2.join(cwd, config2.paths.ui, file2.path);
13984
+ await fs3.ensureDir(path2.dirname(target));
13985
+ await fs3.writeFile(target, file2.content);
13986
+ }
13987
+ context.installed.add(name);
13988
+ note(`Added ${name}`);
13989
+ } finally {
13990
+ context.installing.delete(name);
13942
13991
  }
13943
- if (data.dependencies?.length) {
13944
- const [command, ...args] = await getInstallCommand(data.dependencies);
13945
- await execa(command, args);
13946
- }
13947
- for (const file2 of data.files) {
13948
- const target = path2.join(process.cwd(), config2.paths.ui, file2.path);
13949
- await fs3.ensureDir(path2.dirname(target));
13950
- await fs3.writeFile(target, file2.content);
13992
+ }
13993
+ async function addAllComponents(config2, cwd = process.cwd()) {
13994
+ const all = await loadRegistryIndex(cwd);
13995
+ const context = createInstallContext();
13996
+ for (const name of all) {
13997
+ await installComponent(name, config2, context, cwd);
13951
13998
  }
13952
- note(`Added ${name}`);
13953
13999
  }
13954
14000
  async function addCommand(components, config2) {
13955
14001
  let selections = components;
13956
14002
  if (!selections?.length) {
13957
14003
  selections = await pickComponents();
13958
14004
  }
14005
+ const context = createInstallContext();
13959
14006
  for (const name of selections) {
13960
- await installComponent(name, config2);
14007
+ await installComponent(name, config2, context, process.cwd());
13961
14008
  }
13962
14009
  }
13963
14010
 
13964
- // src/commands/init.ts
13965
- import path3 from "path";
13966
- import { confirm, intro, note as note2, outro, select, text, spinner as spinner2 } from "@clack/prompts";
14011
+ // src/commands/create.ts
14012
+ import path4 from "path";
14013
+ import { confirm, intro, note as note2, outro, spinner as spinner3, text } from "@clack/prompts";
13967
14014
  import { execa as execa2 } from "execa";
13968
- import figlet from "figlet";
13969
- import fs4 from "fs-extra";
13970
- import gradient from "gradient-string";
14015
+ import fs5 from "fs-extra";
13971
14016
  import pc from "picocolors";
14017
+
14018
+ // src/utils/palette.ts
14019
+ import os from "os";
14020
+ import path3 from "path";
14021
+ import fs4 from "fs-extra";
14022
+ var CACHE_VERSION = 1;
14023
+ var TAILWIND_COLORS_URL = "https://tailwindcss.com/docs/colors";
14024
+ var MARKER_START = "/* shapes-ui:tokens:start v1 */";
14025
+ var MARKER_END = "/* shapes-ui:tokens:end */";
14026
+ var SHADE_STEPS = [
14027
+ "50",
14028
+ "100",
14029
+ "200",
14030
+ "300",
14031
+ "400",
14032
+ "500",
14033
+ "600",
14034
+ "700",
14035
+ "800",
14036
+ "900",
14037
+ "950"
14038
+ ];
14039
+ var DEFAULT_BRAND_PALETTES = [
14040
+ "red",
14041
+ "orange",
14042
+ "amber",
14043
+ "yellow",
14044
+ "lime",
14045
+ "green",
14046
+ "emerald",
14047
+ "teal",
14048
+ "cyan",
14049
+ "sky",
14050
+ "blue",
14051
+ "indigo",
14052
+ "violet",
14053
+ "purple",
14054
+ "fuchsia",
14055
+ "pink",
14056
+ "rose"
14057
+ ];
14058
+ var FOREGROUND_PAIRS = [
14059
+ { background: "card", foreground: "card-foreground" },
14060
+ { background: "popup", foreground: "popup-foreground" },
14061
+ { background: "primary", foreground: "primary-foreground" },
14062
+ { background: "secondary", foreground: "secondary-foreground" },
14063
+ { background: "muted", foreground: "muted-foreground" },
14064
+ { background: "accent", foreground: "accent-foreground" },
14065
+ { background: "destructive", foreground: "destructive-foreground" },
14066
+ { background: "success", foreground: "success-foreground" },
14067
+ { background: "warning", foreground: "warning-foreground" },
14068
+ { background: "info", foreground: "info-foreground" }
14069
+ ];
14070
+ var DETERMINISTIC_FOREGROUND_SHADE = {
14071
+ light: {
14072
+ background: "950",
14073
+ foreground: "950",
14074
+ card: "950",
14075
+ "card-foreground": "950",
14076
+ popup: "950",
14077
+ "popup-foreground": "950",
14078
+ primary: "50",
14079
+ "primary-foreground": "50",
14080
+ secondary: "900",
14081
+ "secondary-foreground": "900",
14082
+ muted: "500",
14083
+ "muted-foreground": "500",
14084
+ accent: "900",
14085
+ "accent-foreground": "900",
14086
+ destructive: "50",
14087
+ "destructive-foreground": "50",
14088
+ success: "50",
14089
+ "success-foreground": "50",
14090
+ warning: "950",
14091
+ "warning-foreground": "950",
14092
+ info: "50",
14093
+ "info-foreground": "50",
14094
+ border: "900",
14095
+ input: "900",
14096
+ ring: "50"
14097
+ },
14098
+ dark: {
14099
+ background: "50",
14100
+ foreground: "50",
14101
+ card: "100",
14102
+ "card-foreground": "100",
14103
+ popup: "100",
14104
+ "popup-foreground": "100",
14105
+ primary: "950",
14106
+ "primary-foreground": "950",
14107
+ secondary: "100",
14108
+ "secondary-foreground": "100",
14109
+ muted: "400",
14110
+ "muted-foreground": "400",
14111
+ accent: "100",
14112
+ "accent-foreground": "100",
14113
+ destructive: "950",
14114
+ "destructive-foreground": "950",
14115
+ success: "950",
14116
+ "success-foreground": "950",
14117
+ warning: "950",
14118
+ "warning-foreground": "950",
14119
+ info: "950",
14120
+ "info-foreground": "950",
14121
+ border: "100",
14122
+ input: "100",
14123
+ ring: "950"
14124
+ }
14125
+ };
14126
+ function normalizeName(value) {
14127
+ return value.trim().toLowerCase();
14128
+ }
14129
+ function normalizeColorValue(value) {
14130
+ const trimmed = value.trim();
14131
+ if (trimmed.startsWith("oklch(")) {
14132
+ return trimmed.slice(6, -1).trim();
14133
+ }
14134
+ return trimmed;
14135
+ }
14136
+ function isValidScale(value) {
14137
+ if (!value || typeof value !== "object") return false;
14138
+ return SHADE_STEPS.every((step) => typeof value[step] === "string");
14139
+ }
14140
+ function getCacheFilePath() {
14141
+ return path3.join(os.homedir(), ".shapes-ui", "cache", "tailwind-colors-v4.json");
14142
+ }
14143
+ async function readCache() {
14144
+ const cachePath = getCacheFilePath();
14145
+ if (!await fs4.pathExists(cachePath)) return null;
14146
+ const payload = await fs4.readJSON(cachePath);
14147
+ if (payload.version !== CACHE_VERSION || !payload.palettes || typeof payload.palettes !== "object") {
14148
+ return null;
14149
+ }
14150
+ const validated = {};
14151
+ for (const [name, scale] of Object.entries(payload.palettes)) {
14152
+ if (isValidScale(scale)) {
14153
+ validated[normalizeName(name)] = scale;
14154
+ }
14155
+ }
14156
+ return Object.keys(validated).length > 0 ? validated : null;
14157
+ }
14158
+ async function writeCache(palettes) {
14159
+ const cachePath = getCacheFilePath();
14160
+ await fs4.ensureDir(path3.dirname(cachePath));
14161
+ const payload = {
14162
+ version: CACHE_VERSION,
14163
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
14164
+ palettes
14165
+ };
14166
+ await fs4.writeJSON(cachePath, payload, { spaces: 2 });
14167
+ }
14168
+ async function loadFromTailwindPackage() {
14169
+ try {
14170
+ const colorsModule = await import("tailwindcss/colors");
14171
+ const source = colorsModule.default ?? colorsModule;
14172
+ const palettes = {};
14173
+ for (const [familyName, rawScale] of Object.entries(source)) {
14174
+ if (!rawScale || typeof rawScale !== "object") continue;
14175
+ const family = normalizeName(familyName);
14176
+ const scale = rawScale;
14177
+ const next = {};
14178
+ for (const step of SHADE_STEPS) {
14179
+ const value = scale[step];
14180
+ if (typeof value !== "string") {
14181
+ next[step] = void 0;
14182
+ continue;
14183
+ }
14184
+ next[step] = normalizeColorValue(value);
14185
+ }
14186
+ if (isValidScale(next)) {
14187
+ palettes[family] = next;
14188
+ }
14189
+ }
14190
+ return Object.keys(palettes).length > 0 ? palettes : null;
14191
+ } catch {
14192
+ return null;
14193
+ }
14194
+ }
14195
+ function parseTailwindColorsFromDocs(markup) {
14196
+ const regex = /--color-([a-z]+)-(50|100|200|300|400|500|600|700|800|900|950)\s*:\s*(oklch\([^)]+\))/g;
14197
+ const palettes = {};
14198
+ let match = regex.exec(markup);
14199
+ while (match) {
14200
+ const family = normalizeName(match[1]);
14201
+ const shade = match[2];
14202
+ const value = normalizeColorValue(match[3]);
14203
+ palettes[family] ??= {};
14204
+ palettes[family][shade] = value;
14205
+ match = regex.exec(markup);
14206
+ }
14207
+ const validated = {};
14208
+ for (const [family, scale] of Object.entries(palettes)) {
14209
+ if (isValidScale(scale)) {
14210
+ validated[family] = scale;
14211
+ }
14212
+ }
14213
+ return validated;
14214
+ }
14215
+ async function loadFromRemote() {
14216
+ try {
14217
+ const response = await fetch(TAILWIND_COLORS_URL);
14218
+ if (!response.ok) return null;
14219
+ const markup = await response.text();
14220
+ const palettes = parseTailwindColorsFromDocs(markup);
14221
+ if (Object.keys(palettes).length === 0) return null;
14222
+ return palettes;
14223
+ } catch {
14224
+ return null;
14225
+ }
14226
+ }
14227
+ async function resolvePaletteSource(refresh = false) {
14228
+ if (!refresh) {
14229
+ const fromCache2 = await readCache();
14230
+ if (fromCache2) {
14231
+ return { source: "cache", palettes: fromCache2 };
14232
+ }
14233
+ }
14234
+ const fromRemote = await loadFromRemote();
14235
+ if (fromRemote) {
14236
+ await writeCache(fromRemote);
14237
+ return { source: "remote", palettes: fromRemote };
14238
+ }
14239
+ const fromCache = await readCache();
14240
+ if (fromCache) {
14241
+ return { source: "cache", palettes: fromCache };
14242
+ }
14243
+ const fromPackage = await loadFromTailwindPackage();
14244
+ if (fromPackage) {
14245
+ return { source: "package", palettes: fromPackage };
14246
+ }
14247
+ throw new Error("Could not load Tailwind color palettes from docs, cache, or local package.");
14248
+ }
14249
+ function resolveFamily(palettes, familyName) {
14250
+ const family = normalizeName(familyName);
14251
+ const scale = palettes[family];
14252
+ if (!scale) {
14253
+ const available = Object.keys(palettes).sort().join(", ");
14254
+ throw new Error(`Palette "${familyName}" is not available. Available palettes: ${available}`);
14255
+ }
14256
+ return { family, scale };
14257
+ }
14258
+ function parseLightness(value) {
14259
+ const first = value.split(/\s+/)[0]?.trim() ?? "";
14260
+ if (!first) return Number.NaN;
14261
+ if (first.endsWith("%")) {
14262
+ return Number.parseFloat(first.slice(0, -1));
14263
+ }
14264
+ const numeric = Number.parseFloat(first);
14265
+ if (Number.isNaN(numeric)) return Number.NaN;
14266
+ return numeric <= 1 ? numeric * 100 : numeric;
14267
+ }
14268
+ function pickDynamicForegroundShade(scale, backgroundShade) {
14269
+ const backgroundValue = scale[backgroundShade];
14270
+ const backgroundLightness = parseLightness(backgroundValue);
14271
+ if (!Number.isFinite(backgroundLightness)) {
14272
+ return backgroundShade === "950" ? "50" : "950";
14273
+ }
14274
+ let bestShade = "950";
14275
+ let bestDelta = -1;
14276
+ for (const shade of SHADE_STEPS) {
14277
+ const delta = Math.abs(parseLightness(scale[shade]) - backgroundLightness);
14278
+ if (delta > bestDelta) {
14279
+ bestDelta = delta;
14280
+ bestShade = shade;
14281
+ }
14282
+ }
14283
+ return bestShade;
14284
+ }
14285
+ function buildAssignments(brandFamily, neutralFamily) {
14286
+ return {
14287
+ light: {
14288
+ background: { family: neutralFamily, shade: "50" },
14289
+ foreground: { family: neutralFamily, shade: "950" },
14290
+ card: { family: neutralFamily, shade: "50" },
14291
+ "card-foreground": { family: neutralFamily, shade: "950" },
14292
+ popup: { family: neutralFamily, shade: "50" },
14293
+ "popup-foreground": { family: neutralFamily, shade: "950" },
14294
+ primary: { family: brandFamily, shade: "600" },
14295
+ "primary-foreground": { family: brandFamily, shade: "50" },
14296
+ secondary: { family: neutralFamily, shade: "100" },
14297
+ "secondary-foreground": { family: neutralFamily, shade: "900" },
14298
+ muted: { family: neutralFamily, shade: "100" },
14299
+ "muted-foreground": { family: neutralFamily, shade: "500" },
14300
+ accent: { family: brandFamily, shade: "100" },
14301
+ "accent-foreground": { family: brandFamily, shade: "900" },
14302
+ destructive: { family: "red", shade: "600" },
14303
+ "destructive-foreground": { family: "red", shade: "50" },
14304
+ success: { family: "green", shade: "600" },
14305
+ "success-foreground": { family: "green", shade: "50" },
14306
+ warning: { family: "amber", shade: "500" },
14307
+ "warning-foreground": { family: "amber", shade: "950" },
14308
+ info: { family: brandFamily, shade: "600" },
14309
+ "info-foreground": { family: brandFamily, shade: "50" },
14310
+ border: { family: neutralFamily, shade: "200" },
14311
+ input: { family: neutralFamily, shade: "300" },
14312
+ ring: { family: brandFamily, shade: "500" }
14313
+ },
14314
+ dark: {
14315
+ background: { family: neutralFamily, shade: "950" },
14316
+ foreground: { family: neutralFamily, shade: "50" },
14317
+ card: { family: neutralFamily, shade: "900" },
14318
+ "card-foreground": { family: neutralFamily, shade: "100" },
14319
+ popup: { family: neutralFamily, shade: "900" },
14320
+ "popup-foreground": { family: neutralFamily, shade: "100" },
14321
+ primary: { family: brandFamily, shade: "500" },
14322
+ "primary-foreground": { family: brandFamily, shade: "950" },
14323
+ secondary: { family: neutralFamily, shade: "800" },
14324
+ "secondary-foreground": { family: neutralFamily, shade: "100" },
14325
+ muted: { family: neutralFamily, shade: "800" },
14326
+ "muted-foreground": { family: neutralFamily, shade: "400" },
14327
+ accent: { family: brandFamily, shade: "800" },
14328
+ "accent-foreground": { family: brandFamily, shade: "100" },
14329
+ destructive: { family: "red", shade: "500" },
14330
+ "destructive-foreground": { family: "red", shade: "950" },
14331
+ success: { family: "green", shade: "500" },
14332
+ "success-foreground": { family: "green", shade: "950" },
14333
+ warning: { family: "amber", shade: "500" },
14334
+ "warning-foreground": { family: "amber", shade: "950" },
14335
+ info: { family: brandFamily, shade: "500" },
14336
+ "info-foreground": { family: brandFamily, shade: "950" },
14337
+ border: { family: neutralFamily, shade: "800" },
14338
+ input: { family: neutralFamily, shade: "700" },
14339
+ ring: { family: brandFamily, shade: "400" }
14340
+ }
14341
+ };
14342
+ }
14343
+ function assignForegrounds(palettes, assignments, contrastMode) {
14344
+ if (contrastMode === "deterministic") {
14345
+ for (const mode of ["light", "dark"]) {
14346
+ for (const pair of FOREGROUND_PAIRS) {
14347
+ const bg = assignments[mode][pair.background];
14348
+ assignments[mode][pair.foreground] = {
14349
+ family: bg.family,
14350
+ shade: DETERMINISTIC_FOREGROUND_SHADE[mode][pair.background]
14351
+ };
14352
+ }
14353
+ }
14354
+ return;
14355
+ }
14356
+ for (const mode of ["light", "dark"]) {
14357
+ for (const pair of FOREGROUND_PAIRS) {
14358
+ const background = assignments[mode][pair.background];
14359
+ const scale = palettes[background.family];
14360
+ if (!scale) continue;
14361
+ assignments[mode][pair.foreground] = {
14362
+ family: background.family,
14363
+ shade: pickDynamicForegroundShade(scale, background.shade)
14364
+ };
14365
+ }
14366
+ }
14367
+ }
14368
+ function resolveTokenThemes(palettes, brandFamily, neutralFamily, contrastMode) {
14369
+ const assignments = buildAssignments(brandFamily, neutralFamily);
14370
+ assignForegrounds(palettes, assignments, contrastMode);
14371
+ const result = {
14372
+ light: {},
14373
+ dark: {}
14374
+ };
14375
+ for (const mode of ["light", "dark"]) {
14376
+ for (const [role, ref] of Object.entries(assignments[mode])) {
14377
+ const scale = palettes[ref.family];
14378
+ if (!scale) {
14379
+ throw new Error(`Palette "${ref.family}" is missing required shades.`);
14380
+ }
14381
+ result[mode][role] = scale[ref.shade];
14382
+ }
14383
+ }
14384
+ return result;
14385
+ }
14386
+ function formatThemeBlock(theme) {
14387
+ return [
14388
+ ` --background: oklch(${theme.background});`,
14389
+ ` --foreground: oklch(${theme.foreground});`,
14390
+ ` --card: oklch(${theme.card});`,
14391
+ ` --card-foreground: oklch(${theme["card-foreground"]});`,
14392
+ ` --popup: oklch(${theme.popup});`,
14393
+ ` --popup-foreground: oklch(${theme["popup-foreground"]});`,
14394
+ ` --primary: oklch(${theme.primary});`,
14395
+ ` --primary-foreground: oklch(${theme["primary-foreground"]});`,
14396
+ ` --secondary: oklch(${theme.secondary});`,
14397
+ ` --secondary-foreground: oklch(${theme["secondary-foreground"]});`,
14398
+ ` --muted: oklch(${theme.muted});`,
14399
+ ` --muted-foreground: oklch(${theme["muted-foreground"]});`,
14400
+ ` --accent: oklch(${theme.accent});`,
14401
+ ` --accent-foreground: oklch(${theme["accent-foreground"]});`,
14402
+ ` --destructive: oklch(${theme.destructive});`,
14403
+ ` --destructive-foreground: oklch(${theme["destructive-foreground"]});`,
14404
+ ` --success: oklch(${theme.success});`,
14405
+ ` --success-foreground: oklch(${theme["success-foreground"]});`,
14406
+ ` --warning: oklch(${theme.warning});`,
14407
+ ` --warning-foreground: oklch(${theme["warning-foreground"]});`,
14408
+ ` --info: oklch(${theme.info});`,
14409
+ ` --info-foreground: oklch(${theme["info-foreground"]});`,
14410
+ ` --border: oklch(${theme.border});`,
14411
+ ` --input: oklch(${theme.input});`,
14412
+ ` --ring: oklch(${theme.ring});`
14413
+ ].join("\n");
14414
+ }
14415
+ function buildTokenBlock(assignments, result) {
14416
+ return [
14417
+ MARKER_START,
14418
+ `/* source=${result.source} palette=${result.paletteName} neutral=${result.neutralPalette} contrast=${result.contrastMode} */`,
14419
+ ":root {",
14420
+ formatThemeBlock(assignments.light),
14421
+ "}",
14422
+ "",
14423
+ ".dark {",
14424
+ formatThemeBlock(assignments.dark),
14425
+ "}",
14426
+ "",
14427
+ "@theme inline {",
14428
+ " --color-background: var(--background);",
14429
+ " --color-foreground: var(--foreground);",
14430
+ " --color-card: var(--card);",
14431
+ " --color-card-foreground: var(--card-foreground);",
14432
+ " --color-popup: var(--popup);",
14433
+ " --color-popup-foreground: var(--popup-foreground);",
14434
+ " --color-primary: var(--primary);",
14435
+ " --color-primary-foreground: var(--primary-foreground);",
14436
+ " --color-secondary: var(--secondary);",
14437
+ " --color-secondary-foreground: var(--secondary-foreground);",
14438
+ " --color-muted: var(--muted);",
14439
+ " --color-muted-foreground: var(--muted-foreground);",
14440
+ " --color-accent: var(--accent);",
14441
+ " --color-accent-foreground: var(--accent-foreground);",
14442
+ " --color-destructive: var(--destructive);",
14443
+ " --color-destructive-foreground: var(--destructive-foreground);",
14444
+ " --color-success: var(--success);",
14445
+ " --color-success-foreground: var(--success-foreground);",
14446
+ " --color-warning: var(--warning);",
14447
+ " --color-warning-foreground: var(--warning-foreground);",
14448
+ " --color-info: var(--info);",
14449
+ " --color-info-foreground: var(--info-foreground);",
14450
+ " --color-border: var(--border);",
14451
+ " --color-input: var(--input);",
14452
+ " --color-ring: var(--ring);",
14453
+ "}",
14454
+ MARKER_END
14455
+ ].join("\n");
14456
+ }
14457
+ function upsertGeneratedBlock(current, generatedBlock) {
14458
+ const hasStart = current.includes(MARKER_START);
14459
+ const hasEnd = current.includes(MARKER_END);
14460
+ if (hasStart !== hasEnd) {
14461
+ throw new Error("Found an incomplete generated token block. Please fix it manually and retry.");
14462
+ }
14463
+ if (!hasStart) {
14464
+ const trimmed = current.trimEnd();
14465
+ return `${trimmed}
14466
+
14467
+ ${generatedBlock}
14468
+ `;
14469
+ }
14470
+ const startIndex = current.indexOf(MARKER_START);
14471
+ const endIndex = current.indexOf(MARKER_END);
14472
+ if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
14473
+ throw new Error("Could not safely update generated token block.");
14474
+ }
14475
+ const endMarkerIndex = endIndex + MARKER_END.length;
14476
+ const before = current.slice(0, startIndex).trimEnd();
14477
+ const after = current.slice(endMarkerIndex).trimStart();
14478
+ if (!before && !after) {
14479
+ return `${generatedBlock}
14480
+ `;
14481
+ }
14482
+ if (!after) {
14483
+ return `${before}
14484
+
14485
+ ${generatedBlock}
14486
+ `;
14487
+ }
14488
+ if (!before) {
14489
+ return `${generatedBlock}
14490
+
14491
+ ${after}`;
14492
+ }
14493
+ return `${before}
14494
+
14495
+ ${generatedBlock}
14496
+
14497
+ ${after}`;
14498
+ }
14499
+ function getDefaultBrandPaletteOptions() {
14500
+ return [...DEFAULT_BRAND_PALETTES];
14501
+ }
14502
+ function normalizeContrastMode(value) {
14503
+ const normalized = value ? value.trim().toLowerCase() : "deterministic";
14504
+ if (normalized === "deterministic" || normalized === "dynamic") {
14505
+ return normalized;
14506
+ }
14507
+ throw new Error(`Invalid contrast mode "${value}". Use deterministic or dynamic.`);
14508
+ }
14509
+ async function writePaletteTokens(options) {
14510
+ const cwd = options.cwd ?? process.cwd();
14511
+ const cssFilePath = path3.join(cwd, options.cssPath);
14512
+ const resolved = await resolvePaletteSource(options.refresh);
14513
+ const brand = resolveFamily(resolved.palettes, options.paletteName);
14514
+ const neutral = resolveFamily(resolved.palettes, options.neutralPalette);
14515
+ resolveFamily(resolved.palettes, "red");
14516
+ resolveFamily(resolved.palettes, "green");
14517
+ resolveFamily(resolved.palettes, "amber");
14518
+ const result = {
14519
+ source: resolved.source,
14520
+ paletteName: brand.family,
14521
+ neutralPalette: neutral.family,
14522
+ contrastMode: options.contrastMode
14523
+ };
14524
+ const assignments = resolveTokenThemes(
14525
+ resolved.palettes,
14526
+ brand.family,
14527
+ neutral.family,
14528
+ options.contrastMode
14529
+ );
14530
+ const generatedBlock = buildTokenBlock(assignments, result);
14531
+ const current = await fs4.pathExists(cssFilePath) ? await fs4.readFile(cssFilePath, "utf-8") : "";
14532
+ const updated = upsertGeneratedBlock(current, generatedBlock);
14533
+ await fs4.ensureDir(path3.dirname(cssFilePath));
14534
+ await fs4.writeFile(cssFilePath, updated);
14535
+ return result;
14536
+ }
14537
+
14538
+ // src/commands/create.ts
13972
14539
  var BASE_DEPS = [
13973
14540
  "@base-ui/react",
13974
14541
  "class-variance-authority",
@@ -13977,44 +14544,229 @@ var BASE_DEPS = [
13977
14544
  "tailwind-merge",
13978
14545
  "tw-animate-css"
13979
14546
  ];
14547
+ async function ensureTailwindStyles(cwd, cssPath) {
14548
+ const template = '@import "tailwindcss";\n';
14549
+ const absPath = path4.join(cwd, cssPath);
14550
+ await fs5.ensureDir(path4.dirname(absPath));
14551
+ if (await fs5.pathExists(absPath)) {
14552
+ const current = await fs5.readFile(absPath, "utf-8");
14553
+ if (current.includes("tailwindcss") || current.includes("@tailwind")) return;
14554
+ await fs5.writeFile(absPath, `${current.trimEnd()}
14555
+
14556
+ ${template}`);
14557
+ return;
14558
+ }
14559
+ await fs5.writeFile(absPath, template);
14560
+ }
14561
+ async function setupShapesFull(projectDir, options) {
14562
+ const setupSpin = spinner3();
14563
+ setupSpin.start("Configuring Shapes UI");
14564
+ const cssPath = options.cssPath ?? "src/index.css";
14565
+ const uiPath = options.uiPath ?? "./src/components/ui";
14566
+ const style = options.style ?? "default";
14567
+ const palette = options.palette ?? "blue";
14568
+ const contrastMode = options.contrastMode ?? "deterministic";
14569
+ const pkg = await readPackageJson(projectDir);
14570
+ const hasTailwindV4 = isTailwindV4Installed(pkg);
14571
+ const isVite = await isViteProject(projectDir);
14572
+ if (!hasTailwindV4) {
14573
+ const tailwindDeps = ["tailwindcss"];
14574
+ if (isVite) {
14575
+ tailwindDeps.push("@tailwindcss/vite");
14576
+ }
14577
+ await installDependencies(tailwindDeps, {
14578
+ dev: true,
14579
+ label: "Installing Tailwind dependencies",
14580
+ successMessage: "Tailwind dependencies installed",
14581
+ cwd: projectDir
14582
+ });
14583
+ }
14584
+ const latestPkg = await readPackageJson(projectDir);
14585
+ const missingBaseDeps = getMissingDeps(latestPkg, BASE_DEPS);
14586
+ await installDependencies(missingBaseDeps, {
14587
+ label: "Installing Shapes base dependencies",
14588
+ successMessage: "Shapes base dependencies installed",
14589
+ cwd: projectDir
14590
+ });
14591
+ await ensureTailwindStyles(projectDir, cssPath);
14592
+ const paletteResult = await writePaletteTokens({
14593
+ cwd: projectDir,
14594
+ cssPath,
14595
+ paletteName: palette,
14596
+ neutralPalette: "zinc",
14597
+ contrastMode
14598
+ });
14599
+ const config2 = {
14600
+ $schema: "https://shapes-ui.com/schema.json",
14601
+ style,
14602
+ palette: {
14603
+ name: paletteResult.paletteName,
14604
+ contrastMode: paletteResult.contrastMode
14605
+ },
14606
+ tailwind: { css: cssPath, baseColor: "zinc" },
14607
+ paths: { ui: uiPath, lib: "./src/lib" }
14608
+ };
14609
+ await fs5.writeJSON(path4.join(projectDir, "shapes.json"), config2, { spaces: 2 });
14610
+ await addAllComponents(config2, projectDir);
14611
+ setupSpin.stop("Shapes UI configured with all components");
14612
+ }
14613
+ function getCreateCommand(packageManager, projectName) {
14614
+ if (packageManager === "pnpm") {
14615
+ return ["pnpm", ["create", "vite", projectName, "--template", "react-ts"]];
14616
+ }
14617
+ if (packageManager === "yarn") {
14618
+ return ["yarn", ["create", "vite", projectName, "--template", "react-ts"]];
14619
+ }
14620
+ if (packageManager === "bun") {
14621
+ return ["bun", ["create", "vite", projectName, "--template", "react-ts"]];
14622
+ }
14623
+ return ["npm", ["create", "vite@latest", projectName, "--", "--template", "react-ts"]];
14624
+ }
14625
+ function getProjectInstallCommand(packageManager) {
14626
+ if (packageManager === "pnpm") {
14627
+ return ["pnpm", ["install"]];
14628
+ }
14629
+ if (packageManager === "yarn") {
14630
+ return ["yarn", ["install"]];
14631
+ }
14632
+ if (packageManager === "bun") {
14633
+ return ["bun", ["install"]];
14634
+ }
14635
+ return ["npm", ["install"]];
14636
+ }
14637
+ async function createCommand(projectNameArg, options = {}) {
14638
+ intro(pc.bgCyan(pc.black(" Create a Shapes UI app ")));
14639
+ const allowedStyles = /* @__PURE__ */ new Set(["default", "brutalist", "minimal"]);
14640
+ if (options.style && !allowedStyles.has(options.style)) {
14641
+ throw new Error(`Invalid style "${options.style}". Use one of: default, brutalist, minimal.`);
14642
+ }
14643
+ if (options.palette) {
14644
+ const normalizedPalette = options.palette.toLowerCase();
14645
+ const allowedPalettes = new Set(getDefaultBrandPaletteOptions());
14646
+ if (!allowedPalettes.has(normalizedPalette)) {
14647
+ throw new Error(
14648
+ `Invalid palette "${options.palette}". Use one of: ${getDefaultBrandPaletteOptions().join(", ")}.`
14649
+ );
14650
+ }
14651
+ options.palette = normalizedPalette;
14652
+ }
14653
+ if (options.contrastMode) {
14654
+ options.contrastMode = normalizeContrastMode(options.contrastMode);
14655
+ }
14656
+ if (options.uiPath !== void 0 && !options.uiPath.trim()) {
14657
+ throw new Error("Invalid ui path. Please provide a non-empty value.");
14658
+ }
14659
+ if (options.cssPath !== void 0 && !options.cssPath.trim()) {
14660
+ throw new Error("Invalid css path. Please provide a non-empty value.");
14661
+ }
14662
+ const projectName = projectNameArg && projectNameArg.trim().length > 0 ? projectNameArg.trim() : exitIfCancelled(
14663
+ await text({
14664
+ message: "Project name",
14665
+ initialValue: "my-shapes-app",
14666
+ validate(value) {
14667
+ if (!value || !value.trim()) return "Project name is required.";
14668
+ return void 0;
14669
+ }
14670
+ })
14671
+ ).trim();
14672
+ const projectDir = path4.resolve(process.cwd(), projectName);
14673
+ const exists = await fs5.pathExists(projectDir);
14674
+ if (exists) {
14675
+ const overwrite = options.force ?? exitIfCancelled(
14676
+ await confirm({
14677
+ message: `${projectName} already exists. Overwrite it?`,
14678
+ initialValue: false
14679
+ })
14680
+ );
14681
+ if (!overwrite) {
14682
+ outro(pc.yellow("Create cancelled."));
14683
+ return;
14684
+ }
14685
+ await fs5.remove(projectDir);
14686
+ }
14687
+ const packageManager = await getPackageManager();
14688
+ const [createCommandName, createArgs] = getCreateCommand(packageManager, projectName);
14689
+ const createSpin = spinner3();
14690
+ createSpin.start(`Scaffolding ${projectName}`);
14691
+ await execa2(createCommandName, createArgs, { stdio: "ignore" });
14692
+ createSpin.stop(`Created ${projectName}`);
14693
+ note2(`Project scaffolded with ${packageManager} + Vite (React + TypeScript).`, "Create");
14694
+ const shouldInstallProjectDeps = options.install || options.full;
14695
+ if (shouldInstallProjectDeps) {
14696
+ const [installCommand, installArgs] = getProjectInstallCommand(packageManager);
14697
+ const installSpin = spinner3();
14698
+ installSpin.start("Installing project dependencies");
14699
+ await execa2(installCommand, installArgs, {
14700
+ cwd: projectDir,
14701
+ stdio: "ignore"
14702
+ });
14703
+ installSpin.stop("Dependencies installed");
14704
+ }
14705
+ if (options.full) {
14706
+ await setupShapesFull(projectDir, options);
14707
+ }
14708
+ const runShapesInit = `${packageManager === "npm" ? "npx" : packageManager === "pnpm" ? "pnpm dlx" : packageManager === "yarn" ? "yarn dlx" : "bunx"} shapes-ui init`;
14709
+ outro(
14710
+ `${pc.green(options.full ? "Project ready with Shapes UI." : "Project ready.")}
14711
+
14712
+ ${pc.bold("Next steps:")}
14713
+ cd ${projectName}
14714
+ ${shouldInstallProjectDeps ? "" : `${packageManager} install
14715
+ `}${options.full ? "" : `${runShapesInit}
14716
+ `} ${packageManager} run dev`
14717
+ );
14718
+ }
14719
+
14720
+ // src/commands/init.ts
14721
+ import path5 from "path";
14722
+ import { confirm as confirm2, intro as intro2, note as note3, outro as outro2, select, text as text2 } from "@clack/prompts";
14723
+ import figlet from "figlet";
14724
+ import fs6 from "fs-extra";
14725
+ import gradient from "gradient-string";
14726
+ import pc2 from "picocolors";
14727
+ var BASE_DEPS2 = [
14728
+ "@base-ui/react",
14729
+ "class-variance-authority",
14730
+ "clsx",
14731
+ "lucide-react",
14732
+ "tailwind-merge",
14733
+ "tw-animate-css"
14734
+ ];
13980
14735
  async function installDeps(deps, dev = false) {
13981
- if (!deps.length) return;
13982
- const spin = spinner2();
13983
- const label = dev ? "Installing dev dependencies" : "Installing dependencies";
13984
- spin.start(label);
13985
- const [command, ...args] = await getInstallCommand(deps, dev);
13986
- await execa2(command, args);
13987
- spin.stop(pc.green("Dependencies installed"));
14736
+ await installDependencies(deps, {
14737
+ dev,
14738
+ successMessage: pc2.green("Dependencies installed")
14739
+ });
13988
14740
  }
13989
- async function ensureTailwindStyles(cssPath) {
14741
+ async function ensureTailwindStyles2(cssPath) {
13990
14742
  const template = '@import "tailwindcss";\n';
13991
- const absPath = path3.join(process.cwd(), cssPath);
13992
- await fs4.ensureDir(path3.dirname(absPath));
13993
- if (await fs4.pathExists(absPath)) {
13994
- const current = await fs4.readFile(absPath, "utf-8");
14743
+ const absPath = path5.join(process.cwd(), cssPath);
14744
+ await fs6.ensureDir(path5.dirname(absPath));
14745
+ if (await fs6.pathExists(absPath)) {
14746
+ const current = await fs6.readFile(absPath, "utf-8");
13995
14747
  if (current.includes("tailwindcss") || current.includes("@tailwind")) return;
13996
- await fs4.writeFile(absPath, `${current.trimEnd()}
14748
+ await fs6.writeFile(absPath, `${current.trimEnd()}
13997
14749
 
13998
14750
  ${template}`);
13999
14751
  return;
14000
14752
  }
14001
- await fs4.writeFile(absPath, template);
14753
+ await fs6.writeFile(absPath, template);
14002
14754
  }
14003
14755
  function renderTitle() {
14004
- const text2 = figlet.textSync("Shapes UI", { font: "Standard" });
14005
- console.log(gradient.pastel.multiline(text2));
14756
+ const text3 = figlet.textSync("Shapes UI", { font: "Standard" });
14757
+ console.log(gradient.pastel.multiline(text3));
14006
14758
  }
14007
14759
  async function initCommand() {
14008
14760
  console.clear();
14009
14761
  renderTitle();
14010
- intro(pc.bgCyan(pc.black(" Welcome to Shapes UI ")));
14011
- const configPath = path3.join(process.cwd(), "shapes.json");
14012
- if (await fs4.pathExists(configPath)) {
14762
+ intro2(pc2.bgCyan(pc2.black(" Welcome to Shapes UI ")));
14763
+ const configPath = path5.join(process.cwd(), "shapes.json");
14764
+ if (await fs6.pathExists(configPath)) {
14013
14765
  const overwrite = exitIfCancelled(
14014
- await confirm({ message: "shapes.json already exists. Overwrite it?" })
14766
+ await confirm2({ message: "shapes.json already exists. Overwrite it?" })
14015
14767
  );
14016
14768
  if (!overwrite) {
14017
- outro(pc.yellow("Init cancelled."));
14769
+ outro2(pc2.yellow("Init cancelled."));
14018
14770
  return;
14019
14771
  }
14020
14772
  }
@@ -14023,12 +14775,33 @@ async function initCommand() {
14023
14775
  message: "Which style do you want to use?",
14024
14776
  options: [
14025
14777
  { label: "Default", value: "default" },
14026
- { label: "Brutalist", value: "brutalist" }
14778
+ { label: "Brutalist", value: "brutalist" },
14779
+ { label: "Minimal", value: "minimal" }
14027
14780
  ]
14028
14781
  })
14029
14782
  );
14783
+ const palette = exitIfCancelled(
14784
+ await select({
14785
+ message: "Pick a brand palette",
14786
+ options: getDefaultBrandPaletteOptions().map((name) => ({
14787
+ label: name[0].toUpperCase() + name.slice(1),
14788
+ value: name
14789
+ })),
14790
+ initialValue: "blue"
14791
+ })
14792
+ );
14793
+ const contrastMode = exitIfCancelled(
14794
+ await select({
14795
+ message: "Choose foreground contrast strategy",
14796
+ options: [
14797
+ { label: "Deterministic shades", value: "deterministic" },
14798
+ { label: "Dynamic contrast", value: "dynamic" }
14799
+ ],
14800
+ initialValue: "deterministic"
14801
+ })
14802
+ );
14030
14803
  const uiPath = exitIfCancelled(
14031
- await text({
14804
+ await text2({
14032
14805
  message: "Where should we install components?",
14033
14806
  initialValue: "./src/components/ui"
14034
14807
  })
@@ -14037,10 +14810,10 @@ async function initCommand() {
14037
14810
  const hasTailwindV4 = isTailwindV4Installed(pkg);
14038
14811
  const isVite = await isViteProject();
14039
14812
  const hasExistingCss = exitIfCancelled(
14040
- await confirm({ message: "Do you already have a CSS file?" })
14813
+ await confirm2({ message: "Do you already have a CSS file?" })
14041
14814
  );
14042
14815
  const cssPath = hasExistingCss ? exitIfCancelled(
14043
- await text({
14816
+ await text2({
14044
14817
  message: "Path to your CSS file",
14045
14818
  initialValue: "src/styles/globals.css"
14046
14819
  })
@@ -14052,18 +14825,28 @@ async function initCommand() {
14052
14825
  }
14053
14826
  await installDeps(tailwindDeps, true);
14054
14827
  }
14055
- const missingBaseDeps = getMissingDeps(pkg, BASE_DEPS);
14828
+ const missingBaseDeps = getMissingDeps(pkg, BASE_DEPS2);
14056
14829
  await installDeps(missingBaseDeps, false);
14057
- await ensureTailwindStyles(cssPath);
14830
+ await ensureTailwindStyles2(cssPath);
14831
+ const paletteResult = await writePaletteTokens({
14832
+ cssPath,
14833
+ paletteName: palette,
14834
+ neutralPalette: "zinc",
14835
+ contrastMode
14836
+ });
14058
14837
  const config2 = {
14059
14838
  $schema: "https://shapes-ui.com/schema.json",
14060
14839
  style,
14840
+ palette: {
14841
+ name: paletteResult.paletteName,
14842
+ contrastMode: paletteResult.contrastMode
14843
+ },
14061
14844
  tailwind: { css: cssPath, baseColor: "zinc" },
14062
14845
  paths: { ui: uiPath, lib: "./src/lib" }
14063
14846
  };
14064
- await fs4.writeJSON("shapes.json", config2, { spaces: 2 });
14065
- note2("Created shapes.json", "Configuration");
14066
- const addNow = exitIfCancelled(await confirm({ message: "Add components now?" }));
14847
+ await fs6.writeJSON("shapes.json", config2, { spaces: 2 });
14848
+ note3("Created shapes.json", "Configuration");
14849
+ const addNow = exitIfCancelled(await confirm2({ message: "Add components now?" }));
14067
14850
  if (addNow) {
14068
14851
  const selected = await pickComponents();
14069
14852
  if (selected.length > 0) {
@@ -14072,19 +14855,82 @@ async function initCommand() {
14072
14855
  }
14073
14856
  }
14074
14857
  }
14075
- outro(pc.green("Shapes UI is ready."));
14858
+ outro2(pc2.green("Shapes UI is ready."));
14859
+ }
14860
+
14861
+ // src/commands/palette.ts
14862
+ import path6 from "path";
14863
+ import { intro as intro3, note as note4, outro as outro3 } from "@clack/prompts";
14864
+ import fs7 from "fs-extra";
14865
+ import pc3 from "picocolors";
14866
+ async function paletteSetCommand(name, options = {}) {
14867
+ intro3(pc3.bgCyan(pc3.black(" Update Shapes palette ")));
14868
+ const config2 = await getConfig();
14869
+ if (!config2) {
14870
+ throw new Error("Could not find shapes.json. Run `shapes-ui init` first.");
14871
+ }
14872
+ const contrastMode = normalizeContrastMode(options.contrastMode ?? config2.palette?.contrastMode);
14873
+ const cssPath = options.cssPath ?? config2.tailwind.css;
14874
+ const neutralPalette = config2.tailwind.baseColor;
14875
+ const result = await writePaletteTokens({
14876
+ cssPath,
14877
+ paletteName: name,
14878
+ neutralPalette,
14879
+ contrastMode,
14880
+ refresh: options.refresh
14881
+ });
14882
+ const updatedConfig = {
14883
+ ...config2,
14884
+ palette: {
14885
+ name: result.paletteName,
14886
+ contrastMode: result.contrastMode
14887
+ },
14888
+ tailwind: {
14889
+ ...config2.tailwind,
14890
+ css: cssPath
14891
+ }
14892
+ };
14893
+ await fs7.writeJSON(path6.join(process.cwd(), "shapes.json"), updatedConfig, { spaces: 2 });
14894
+ note4(
14895
+ `palette=${result.paletteName} neutral=${result.neutralPalette} contrast=${result.contrastMode} source=${result.source}`,
14896
+ "Palette updated"
14897
+ );
14898
+ outro3(pc3.green("Shapes UI palette updated."));
14076
14899
  }
14077
14900
 
14078
14901
  // src/commands/cli.ts
14079
14902
  var program = new Command();
14080
- program.name("shapes").description("Shapes UI CLI").version("0.0.1");
14903
+ program.name("shapes-ui").description("Shapes UI CLI").version("0.0.1");
14081
14904
  program.command("init").description("Configure Shapes UI for your project").action(initCommand);
14082
- program.command("add [components...]").description("Add components to your project").action(async (components) => {
14905
+ program.command("create [project-name]").description("Create a new Vite React TypeScript app ready for Shapes UI").option("-i, --install", "Install project dependencies").option("--full", "Configure Shapes UI and install all components").option("--style <style>", "Shapes style for --full (default|brutalist|minimal)").option("--palette <name>", "Brand palette for --full (e.g. blue, emerald, rose)").option("--contrast-mode <mode>", "Foreground contrast mode for --full (deterministic|dynamic)").option("--ui-path <path>", "UI components path for --full (e.g. ./src/components/ui)").option("--css-path <path>", "Tailwind CSS file path for --full (e.g. src/index.css)").option("-f, --force", "Overwrite target directory if it already exists").action(async (projectName, options) => {
14906
+ await createCommand(projectName, {
14907
+ install: options.install,
14908
+ full: options.full,
14909
+ style: options.style,
14910
+ palette: options.palette,
14911
+ contrastMode: options.contrastMode,
14912
+ uiPath: options.uiPath,
14913
+ cssPath: options.cssPath,
14914
+ force: options.force
14915
+ });
14916
+ });
14917
+ program.command("add [components...]").description("Add components to your project").option("-a, --all", "Add all available components").action(async (components, options) => {
14083
14918
  const config2 = await getConfig();
14084
14919
  if (!config2) {
14085
14920
  console.error("Please run 'init' first.");
14086
14921
  return;
14087
14922
  }
14923
+ if (options.all) {
14924
+ await addAllComponents(config2);
14925
+ return;
14926
+ }
14088
14927
  await addCommand(components, config2);
14089
14928
  });
14929
+ program.command("palette").description("Manage color palettes").command("set <name>").description("Set brand palette and regenerate semantic tokens").option("--css-path <path>", "Override Tailwind CSS file path").option("--contrast-mode <mode>", "Foreground contrast mode (deterministic|dynamic)").option("--refresh", "Refresh Tailwind palette data from docs").action(async (name, options) => {
14930
+ await paletteSetCommand(name, {
14931
+ cssPath: options.cssPath,
14932
+ contrastMode: options.contrastMode,
14933
+ refresh: options.refresh
14934
+ });
14935
+ });
14090
14936
  program.parse();