wensity 0.1.8 → 0.1.9

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 (3) hide show
  1. package/README.md +4 -3
  2. package/dist/index.js +1490 -132
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/commands.ts
4
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
5
- import { dirname as dirname6, join as join6 } from "path";
4
+ import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
5
+ import { dirname as dirname7, join as join7 } from "path";
6
6
 
7
7
  // src/constants.ts
8
8
  var CLI_NAME = "wensity";
9
- var CLI_VERSION = "0.1.8";
9
+ var CLI_VERSION = "0.1.9";
10
10
  var CONFIG_FILE_NAME = "wensity.json";
11
11
  var DEFAULT_API_ORIGIN = "https://ui.wensity.com";
12
12
  var DEFAULT_REGISTRY_PATH = "/api/registry";
@@ -18,7 +18,9 @@ var INIT_PRIMITIVE_CSS_SLUG = "button";
18
18
  var HELP_TEXT = `Wensity CLI ${CLI_VERSION}
19
19
 
20
20
  Usage:
21
- wensity init [--preset <code>] [--preset-file <path>] [options]
21
+ wensity init [--preset <code>] [--preset-file <path>] [--template next|vite|astro|react-router] [options]
22
+ wensity apply --preset <code> [options]
23
+ wensity preset inspect <code> [--json]
22
24
  wensity login [options]
23
25
  wensity logout
24
26
  wensity whoami [--json]
@@ -28,7 +30,9 @@ Usage:
28
30
  wensity version
29
31
 
30
32
  Commands:
31
- init Create wensity.json and local helper files (optional --preset applies style + CSS)
33
+ init Create wensity.json and local helper files (optional --preset applies style + CSS; optional --template scaffolds an app)
34
+ apply Apply a preset to an existing Wensity project
35
+ preset Inspect or work with shareable Wensity style presets
32
36
  login Save a Wensity API token for premium component access
33
37
  logout Remove saved CLI credentials
34
38
  whoami Verify the saved token and print the authenticated account
@@ -44,6 +48,7 @@ Options:
44
48
  --token <token> Provide a token non-interactively for login
45
49
  --preset <code> Apply a shareable Wensity style preset (wsty1.\u2026); merges style when config exists
46
50
  --preset-file <path> Read a preset code or JSON payload from a file
51
+ --template <name> Scaffold a new app before init: next, vite, astro, react-router
47
52
  --force Overwrite generated files when adding or initializing
48
53
  --no-install Write files without running the package manager
49
54
  --dry-run Print planned writes and installs without changing files
@@ -53,8 +58,11 @@ Options:
53
58
 
54
59
  Preset notes:
55
60
  Generate codes at ${DEFAULT_API_ORIGIN}/docs/primitives/setup
61
+ preset inspect decodes the token, font packages, and resolved primitive tokens
62
+ apply --preset updates existing style + primitive CSS without scaffolding helper files
56
63
  Plain init refuses an existing wensity.json unless --force
57
64
  init --preset merges style + primitive CSS; does not rewrite installed source
65
+ init --template writes a minimal app scaffold in the current directory before Wensity files
58
66
  add/update read style.iconLibrary for UI primitives only (Tabler|Lucide)
59
67
  Invalid/expired/unsupported codes exit non-zero with an actionable message
60
68
  `;
@@ -118,7 +126,7 @@ function hasFlag(args, name) {
118
126
  return args.options.get(name) === true;
119
127
  }
120
128
  function expectsValue(name) {
121
- return ["api-url", "registry", "token", "type", "preset", "preset-file"].includes(name);
129
+ return ["api-url", "registry", "token", "type", "preset", "preset-file", "template"].includes(name);
122
130
  }
123
131
  function normalizeOptionName(name) {
124
132
  if (name === "dry") return "dry-run";
@@ -138,9 +146,14 @@ var BASE_COLOR_IDS = [
138
146
  "inherit",
139
147
  "neutral",
140
148
  "zinc",
149
+ "gray",
141
150
  "slate",
151
+ "cool-gray",
142
152
  "stone",
143
- "taupe"
153
+ "taupe",
154
+ "mauve",
155
+ "olive",
156
+ "mist"
144
157
  ];
145
158
  var THEME_COLOR_IDS = [
146
159
  "ink",
@@ -148,16 +161,68 @@ var THEME_COLOR_IDS = [
148
161
  "blue",
149
162
  "violet",
150
163
  "green",
151
- "orange"
164
+ "orange",
165
+ "amber",
166
+ "cyan",
167
+ "emerald",
168
+ "indigo",
169
+ "pink",
170
+ "purple",
171
+ "rose",
172
+ "sky",
173
+ "teal",
174
+ "yellow",
175
+ "fuchsia",
176
+ "lime"
152
177
  ];
153
178
  var CHART_PALETTE_IDS = [
154
179
  "theme",
155
180
  "warm",
156
181
  "cool",
157
- "mixed"
182
+ "mixed",
183
+ "neutral",
184
+ "zinc",
185
+ "stone",
186
+ "gray",
187
+ "amber",
188
+ "cyan",
189
+ "emerald",
190
+ "indigo",
191
+ "lime",
192
+ "pink",
193
+ "purple",
194
+ "rose",
195
+ "sky",
196
+ "teal",
197
+ "yellow",
198
+ "taupe"
199
+ ];
200
+ var HEADING_FONT_IDS = [
201
+ "system",
202
+ "playfair",
203
+ "instrument-serif",
204
+ "fraunces",
205
+ "space-grotesk",
206
+ "noto-serif",
207
+ "eb-garamond",
208
+ "lora",
209
+ "montserrat",
210
+ "manrope",
211
+ "inter"
212
+ ];
213
+ var BODY_FONT_IDS = [
214
+ "system",
215
+ "inter",
216
+ "geist",
217
+ "dm-sans",
218
+ "plus-jakarta-sans",
219
+ "figtree",
220
+ "noto-sans",
221
+ "public-sans",
222
+ "outfit",
223
+ "manrope",
224
+ "ibm-plex-sans"
158
225
  ];
159
- var HEADING_FONT_IDS = ["system", "playfair"];
160
- var BODY_FONT_IDS = ["system", "inter"];
161
226
  var RADIUS_PRESET_CSS = {
162
227
  none: "0px",
163
228
  small: "0.5rem",
@@ -274,8 +339,8 @@ var DEFAULT_STYLE_CONFIG_CANONICAL = {
274
339
  baseColor: "inherit",
275
340
  themeColor: "ink",
276
341
  chartPalette: "theme",
277
- headingFont: "system",
278
- bodyFont: "system",
342
+ headingFont: "inter",
343
+ bodyFont: "inter",
279
344
  iconLibrary: "tabler",
280
345
  radius: "medium"
281
346
  };
@@ -604,7 +669,7 @@ function validateStyleConfigCanonicalPartial(value) {
604
669
  if ("baseColor" in value) {
605
670
  if (typeof value.baseColor !== "string" || !isBaseColorId(value.baseColor)) {
606
671
  throw new PresetValidationError(
607
- "Invalid style.baseColor. Expected inherit | neutral | zinc | slate | stone | taupe.",
672
+ "Invalid style.baseColor. Expected inherit | neutral | zinc | gray | slate | cool-gray | stone | taupe.",
608
673
  "style.baseColor"
609
674
  );
610
675
  }
@@ -613,7 +678,7 @@ function validateStyleConfigCanonicalPartial(value) {
613
678
  if ("themeColor" in value) {
614
679
  if (typeof value.themeColor !== "string" || !isThemeColorId(value.themeColor)) {
615
680
  throw new PresetValidationError(
616
- "Invalid style.themeColor. Expected ink | red | blue | violet | green | orange.",
681
+ "Invalid style.themeColor. Expected a supported theme color id.",
617
682
  "style.themeColor"
618
683
  );
619
684
  }
@@ -622,7 +687,7 @@ function validateStyleConfigCanonicalPartial(value) {
622
687
  if ("chartPalette" in value) {
623
688
  if (typeof value.chartPalette !== "string" || !isChartPaletteId(value.chartPalette)) {
624
689
  throw new PresetValidationError(
625
- "Invalid style.chartPalette. Expected theme | warm | cool | mixed.",
690
+ "Invalid style.chartPalette. Expected a supported chart palette id.",
626
691
  "style.chartPalette"
627
692
  );
628
693
  }
@@ -631,7 +696,7 @@ function validateStyleConfigCanonicalPartial(value) {
631
696
  if ("headingFont" in value) {
632
697
  if (typeof value.headingFont !== "string" || !isHeadingFontId(value.headingFont)) {
633
698
  throw new PresetValidationError(
634
- "Invalid style.headingFont. Expected system | playfair.",
699
+ "Invalid style.headingFont. Expected a supported heading font id.",
635
700
  "style.headingFont"
636
701
  );
637
702
  }
@@ -640,7 +705,7 @@ function validateStyleConfigCanonicalPartial(value) {
640
705
  if ("bodyFont" in value) {
641
706
  if (typeof value.bodyFont !== "string" || !isBodyFontId(value.bodyFont)) {
642
707
  throw new PresetValidationError(
643
- "Invalid style.bodyFont. Expected system | inter.",
708
+ "Invalid style.bodyFont. Expected a supported body font id.",
644
709
  "style.bodyFont"
645
710
  );
646
711
  }
@@ -769,9 +834,29 @@ function utf8ByteLength(text) {
769
834
  return new TextEncoder().encode(text).byteLength;
770
835
  }
771
836
  var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
772
- var COMPACT_CANONICAL_PREFIX = "c";
837
+ var COMPACT_CANONICAL_LEGACY_PREFIX = "c";
838
+ var COMPACT_CANONICAL_PREFIX = "d";
839
+ var COMPACT_CANONICAL_EXPANDED_PREFIX = "e";
840
+ var COMPACT_CANONICAL_CHART_PREFIX = "f";
841
+ var COMPACT_CANONICAL_THEME_PREFIX = "g";
842
+ var COMPACT_CANONICAL_BASE_PREFIX = "h";
773
843
  var COMPACT_CANONICAL_BODY_LENGTH = 3;
774
844
  var COMPACT_CANONICAL_ENCODED_LENGTH = COMPACT_CANONICAL_PREFIX.length + COMPACT_CANONICAL_BODY_LENGTH;
845
+ var COMPACT_CANONICAL_EXPANDED_BODY_LENGTH = 4;
846
+ var COMPACT_CANONICAL_EXPANDED_ENCODED_LENGTH = COMPACT_CANONICAL_EXPANDED_PREFIX.length + COMPACT_CANONICAL_EXPANDED_BODY_LENGTH;
847
+ var COMPACT_CANONICAL_CHART_ENCODED_LENGTH = COMPACT_CANONICAL_CHART_PREFIX.length + COMPACT_CANONICAL_EXPANDED_BODY_LENGTH;
848
+ var COMPACT_CANONICAL_THEME_ENCODED_LENGTH = COMPACT_CANONICAL_THEME_PREFIX.length + COMPACT_CANONICAL_EXPANDED_BODY_LENGTH;
849
+ var COMPACT_CANONICAL_BASE_BODY_LENGTH = 5;
850
+ var COMPACT_CANONICAL_BASE_ENCODED_LENGTH = COMPACT_CANONICAL_BASE_PREFIX.length + COMPACT_CANONICAL_BASE_BODY_LENGTH;
851
+ var COMPACT_CANONICAL_LEGACY_BASE_MAX_INDEX = 7;
852
+ var COMPACT_CANONICAL_LEGACY_THEME_MAX_INDEX = 5;
853
+ var COMPACT_CANONICAL_LEGACY_CHART_MAX_INDEX = 3;
854
+ var COMPACT_CANONICAL_LEGACY_FONT_MAX_INDEX = 1;
855
+ var COMPACT_CANONICAL_FONT_MAX_INDEX = 7;
856
+ var COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX = 15;
857
+ var COMPACT_CANONICAL_EXPANDED_CHART_MAX_INDEX = 31;
858
+ var COMPACT_CANONICAL_EXPANDED_THEME_MAX_INDEX = 31;
859
+ var COMPACT_CANONICAL_EXPANDED_BASE_MAX_INDEX = 31;
775
860
  function toBase64Url(bytes) {
776
861
  let out = "";
777
862
  for (let i = 0; i < bytes.length; i += 3) {
@@ -823,40 +908,216 @@ function readCompactChar(char) {
823
908
  return BASE64URL_ALPHABET.indexOf(char);
824
909
  }
825
910
  function encodeCompactCanonicalPayload(payload) {
826
- let value = indexOfPresetValue(BASE_COLOR_IDS, payload.baseColor, "preset.baseColor");
827
- value = value << 3 | indexOfPresetValue(THEME_COLOR_IDS, payload.themeColor, "preset.themeColor");
828
- value = value << 2 | indexOfPresetValue(CHART_PALETTE_IDS, payload.chartPalette, "preset.chartPalette");
829
- value = value << 1 | indexOfPresetValue(HEADING_FONT_IDS, payload.headingFont, "preset.headingFont");
830
- value = value << 1 | indexOfPresetValue(BODY_FONT_IDS, payload.bodyFont, "preset.bodyFont");
831
- value = value << 1 | indexOfPresetValue(ICON_LIBRARIES, payload.iconLibrary, "preset.iconLibrary");
832
- value = value << 2 | indexOfPresetValue(RADIUS_PRESETS, payload.radius, "preset.radius");
833
- return `${COMPACT_CANONICAL_PREFIX}${BASE64URL_ALPHABET[value >> 12 & 63]}${BASE64URL_ALPHABET[value >> 6 & 63]}${BASE64URL_ALPHABET[value & 63]}`;
911
+ const baseColorIndex = indexOfPresetValue(BASE_COLOR_IDS, payload.baseColor, "preset.baseColor");
912
+ const themeColorIndex = indexOfPresetValue(
913
+ THEME_COLOR_IDS,
914
+ payload.themeColor,
915
+ "preset.themeColor"
916
+ );
917
+ const chartPaletteIndex = indexOfPresetValue(
918
+ CHART_PALETTE_IDS,
919
+ payload.chartPalette,
920
+ "preset.chartPalette"
921
+ );
922
+ const headingFontIndex = indexOfPresetValue(
923
+ HEADING_FONT_IDS,
924
+ payload.headingFont,
925
+ "preset.headingFont"
926
+ );
927
+ const bodyFontIndex = indexOfPresetValue(BODY_FONT_IDS, payload.bodyFont, "preset.bodyFont");
928
+ const iconLibraryIndex = indexOfPresetValue(
929
+ ICON_LIBRARIES,
930
+ payload.iconLibrary,
931
+ "preset.iconLibrary"
932
+ );
933
+ const radiusIndex = indexOfPresetValue(RADIUS_PRESETS, payload.radius, "preset.radius");
934
+ if (baseColorIndex > 7) {
935
+ if (baseColorIndex > COMPACT_CANONICAL_EXPANDED_BASE_MAX_INDEX) {
936
+ throw new PresetValidationError(
937
+ "Too many curated base colors for compact preset encoding.",
938
+ "preset.baseColor"
939
+ );
940
+ }
941
+ if (themeColorIndex > COMPACT_CANONICAL_EXPANDED_THEME_MAX_INDEX) {
942
+ throw new PresetValidationError(
943
+ "Too many curated theme colors for compact preset encoding.",
944
+ "preset.themeColor"
945
+ );
946
+ }
947
+ if (chartPaletteIndex > COMPACT_CANONICAL_EXPANDED_CHART_MAX_INDEX) {
948
+ throw new PresetValidationError(
949
+ "Too many curated chart palettes for compact preset encoding.",
950
+ "preset.chartPalette"
951
+ );
952
+ }
953
+ if (headingFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX || bodyFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX) {
954
+ throw new PresetValidationError(
955
+ "Too many curated fonts for compact preset encoding.",
956
+ "preset.headingFont"
957
+ );
958
+ }
959
+ let baseValue = baseColorIndex;
960
+ baseValue = baseValue << 5 | themeColorIndex;
961
+ baseValue = baseValue << 5 | chartPaletteIndex;
962
+ baseValue = baseValue << 4 | headingFontIndex;
963
+ baseValue = baseValue << 4 | bodyFontIndex;
964
+ baseValue = baseValue << 1 | iconLibraryIndex;
965
+ baseValue = baseValue << 2 | radiusIndex;
966
+ return encodeCompactCanonicalValue(
967
+ COMPACT_CANONICAL_BASE_PREFIX,
968
+ baseValue,
969
+ COMPACT_CANONICAL_BASE_BODY_LENGTH
970
+ );
971
+ }
972
+ if (themeColorIndex > 7) {
973
+ if (themeColorIndex > COMPACT_CANONICAL_EXPANDED_THEME_MAX_INDEX) {
974
+ throw new PresetValidationError(
975
+ "Too many curated theme colors for compact preset encoding.",
976
+ "preset.themeColor"
977
+ );
978
+ }
979
+ if (chartPaletteIndex > COMPACT_CANONICAL_EXPANDED_CHART_MAX_INDEX) {
980
+ throw new PresetValidationError(
981
+ "Too many curated chart palettes for compact preset encoding.",
982
+ "preset.chartPalette"
983
+ );
984
+ }
985
+ if (headingFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX || bodyFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX) {
986
+ throw new PresetValidationError(
987
+ "Too many curated fonts for compact preset encoding.",
988
+ "preset.headingFont"
989
+ );
990
+ }
991
+ let themeValue = baseColorIndex;
992
+ themeValue = themeValue << 5 | themeColorIndex;
993
+ themeValue = themeValue << 5 | chartPaletteIndex;
994
+ themeValue = themeValue << 4 | headingFontIndex;
995
+ themeValue = themeValue << 4 | bodyFontIndex;
996
+ themeValue = themeValue << 1 | iconLibraryIndex;
997
+ themeValue = themeValue << 2 | radiusIndex;
998
+ return encodeCompactCanonicalValue(
999
+ COMPACT_CANONICAL_THEME_PREFIX,
1000
+ themeValue,
1001
+ COMPACT_CANONICAL_EXPANDED_BODY_LENGTH
1002
+ );
1003
+ }
1004
+ if (baseColorIndex <= COMPACT_CANONICAL_LEGACY_BASE_MAX_INDEX && themeColorIndex <= COMPACT_CANONICAL_LEGACY_THEME_MAX_INDEX && chartPaletteIndex <= COMPACT_CANONICAL_LEGACY_CHART_MAX_INDEX && headingFontIndex <= COMPACT_CANONICAL_LEGACY_FONT_MAX_INDEX && bodyFontIndex <= COMPACT_CANONICAL_LEGACY_FONT_MAX_INDEX) {
1005
+ let legacyValue = baseColorIndex;
1006
+ legacyValue = legacyValue << 3 | themeColorIndex;
1007
+ legacyValue = legacyValue << 2 | chartPaletteIndex;
1008
+ legacyValue = legacyValue << 1 | headingFontIndex;
1009
+ legacyValue = legacyValue << 1 | bodyFontIndex;
1010
+ legacyValue = legacyValue << 1 | iconLibraryIndex;
1011
+ legacyValue = legacyValue << 2 | radiusIndex;
1012
+ return encodeCompactCanonicalValue(COMPACT_CANONICAL_LEGACY_PREFIX, legacyValue);
1013
+ }
1014
+ if (chartPaletteIndex > 3) {
1015
+ if (chartPaletteIndex > COMPACT_CANONICAL_EXPANDED_CHART_MAX_INDEX) {
1016
+ throw new PresetValidationError(
1017
+ "Too many curated chart palettes for compact preset encoding.",
1018
+ "preset.chartPalette"
1019
+ );
1020
+ }
1021
+ if (headingFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX || bodyFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX) {
1022
+ throw new PresetValidationError(
1023
+ "Too many curated fonts for compact preset encoding.",
1024
+ "preset.headingFont"
1025
+ );
1026
+ }
1027
+ let chartValue = baseColorIndex;
1028
+ chartValue = chartValue << 3 | themeColorIndex;
1029
+ chartValue = chartValue << 5 | chartPaletteIndex;
1030
+ chartValue = chartValue << 4 | headingFontIndex;
1031
+ chartValue = chartValue << 4 | bodyFontIndex;
1032
+ chartValue = chartValue << 1 | iconLibraryIndex;
1033
+ chartValue = chartValue << 2 | radiusIndex;
1034
+ return encodeCompactCanonicalValue(
1035
+ COMPACT_CANONICAL_CHART_PREFIX,
1036
+ chartValue,
1037
+ COMPACT_CANONICAL_EXPANDED_BODY_LENGTH
1038
+ );
1039
+ }
1040
+ if (headingFontIndex > COMPACT_CANONICAL_FONT_MAX_INDEX || bodyFontIndex > COMPACT_CANONICAL_FONT_MAX_INDEX) {
1041
+ if (headingFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX || bodyFontIndex > COMPACT_CANONICAL_EXPANDED_FONT_MAX_INDEX) {
1042
+ throw new PresetValidationError(
1043
+ "Too many curated fonts for compact preset encoding.",
1044
+ "preset.headingFont"
1045
+ );
1046
+ }
1047
+ let expandedValue = baseColorIndex;
1048
+ expandedValue = expandedValue << 3 | themeColorIndex;
1049
+ expandedValue = expandedValue << 2 | chartPaletteIndex;
1050
+ expandedValue = expandedValue << 4 | headingFontIndex;
1051
+ expandedValue = expandedValue << 4 | bodyFontIndex;
1052
+ expandedValue = expandedValue << 1 | iconLibraryIndex;
1053
+ expandedValue = expandedValue << 2 | radiusIndex;
1054
+ return encodeCompactCanonicalValue(
1055
+ COMPACT_CANONICAL_EXPANDED_PREFIX,
1056
+ expandedValue,
1057
+ COMPACT_CANONICAL_EXPANDED_BODY_LENGTH
1058
+ );
1059
+ }
1060
+ let value = baseColorIndex;
1061
+ value = value << 3 | themeColorIndex;
1062
+ value = value << 2 | chartPaletteIndex;
1063
+ value = value << 3 | headingFontIndex;
1064
+ value = value << 3 | bodyFontIndex;
1065
+ value = value << 1 | iconLibraryIndex;
1066
+ value = value << 2 | radiusIndex;
1067
+ return encodeCompactCanonicalValue(
1068
+ COMPACT_CANONICAL_PREFIX,
1069
+ value,
1070
+ COMPACT_CANONICAL_BODY_LENGTH
1071
+ );
1072
+ }
1073
+ function encodeCompactCanonicalValue(prefix, value, bodyLength = 3) {
1074
+ let out = prefix;
1075
+ for (let shift = (bodyLength - 1) * 6; shift >= 0; shift -= 6) {
1076
+ out += BASE64URL_ALPHABET[value >> shift & 63];
1077
+ }
1078
+ return out;
834
1079
  }
835
1080
  function decodeCompactCanonicalPayload(encoded) {
836
- if (encoded.length !== COMPACT_CANONICAL_ENCODED_LENGTH || !encoded.startsWith(COMPACT_CANONICAL_PREFIX)) {
1081
+ const isLegacyCompact = encoded.startsWith(COMPACT_CANONICAL_LEGACY_PREFIX);
1082
+ const isCurrentCompact = encoded.startsWith(COMPACT_CANONICAL_PREFIX);
1083
+ const isExpandedCompact = encoded.startsWith(COMPACT_CANONICAL_EXPANDED_PREFIX);
1084
+ const isChartCompact = encoded.startsWith(COMPACT_CANONICAL_CHART_PREFIX);
1085
+ const isThemeCompact = encoded.startsWith(COMPACT_CANONICAL_THEME_PREFIX);
1086
+ const isBaseCompact = encoded.startsWith(COMPACT_CANONICAL_BASE_PREFIX);
1087
+ const expectedLength = isExpandedCompact ? COMPACT_CANONICAL_EXPANDED_ENCODED_LENGTH : isChartCompact ? COMPACT_CANONICAL_CHART_ENCODED_LENGTH : isThemeCompact ? COMPACT_CANONICAL_THEME_ENCODED_LENGTH : isBaseCompact ? COMPACT_CANONICAL_BASE_ENCODED_LENGTH : COMPACT_CANONICAL_ENCODED_LENGTH;
1088
+ if (encoded.length !== expectedLength || !isCurrentCompact && !isLegacyCompact && !isExpandedCompact && !isChartCompact && !isThemeCompact && !isBaseCompact) {
837
1089
  throw new PresetValidationError("Compact preset code has an invalid length.", "preset");
838
1090
  }
839
- const a = readCompactChar(encoded[1]);
840
- const b = readCompactChar(encoded[2]);
841
- const c = readCompactChar(encoded[3]);
842
- if (a < 0 || b < 0 || c < 0) {
843
- throw new PresetValidationError("Compact preset code is not valid base64url.", "preset");
1091
+ let value = 0;
1092
+ for (let i = 1; i < encoded.length; i += 1) {
1093
+ const char = readCompactChar(encoded[i]);
1094
+ if (char < 0) {
1095
+ throw new PresetValidationError("Compact preset code is not valid base64url.", "preset");
1096
+ }
1097
+ value = value << 6 | char;
844
1098
  }
845
- let value = a << 12 | b << 6 | c;
846
1099
  const radius = RADIUS_PRESETS[value & 3];
847
1100
  value >>= 2;
848
1101
  const iconLibrary = ICON_LIBRARIES[value & 1];
849
1102
  value >>= 1;
850
- const bodyFont = BODY_FONT_IDS[value & 1];
851
- value >>= 1;
852
- const headingFont = HEADING_FONT_IDS[value & 1];
853
- value >>= 1;
854
- const chartPalette = CHART_PALETTE_IDS[value & 3];
855
- value >>= 2;
856
- const themeColor = THEME_COLOR_IDS[value & 7];
857
- value >>= 3;
858
- const baseColor = BASE_COLOR_IDS[value & 7];
859
- value >>= 3;
1103
+ const fontMask = isLegacyCompact ? 1 : isExpandedCompact || isChartCompact || isThemeCompact || isBaseCompact ? 15 : 7;
1104
+ const fontShift = isLegacyCompact ? 1 : isExpandedCompact || isChartCompact || isThemeCompact || isBaseCompact ? 4 : 3;
1105
+ const bodyFont = BODY_FONT_IDS[value & fontMask];
1106
+ value >>= fontShift;
1107
+ const headingFont = HEADING_FONT_IDS[value & fontMask];
1108
+ value >>= fontShift;
1109
+ const chartMask = isChartCompact || isThemeCompact || isBaseCompact ? 31 : 3;
1110
+ const chartShift = isChartCompact || isThemeCompact || isBaseCompact ? 5 : 2;
1111
+ const chartPalette = CHART_PALETTE_IDS[value & chartMask];
1112
+ value >>= chartShift;
1113
+ const themeMask = isThemeCompact || isBaseCompact ? 31 : 7;
1114
+ const themeShift = isThemeCompact || isBaseCompact ? 5 : 3;
1115
+ const themeColor = THEME_COLOR_IDS[value & themeMask];
1116
+ value >>= themeShift;
1117
+ const baseMask = isBaseCompact ? 31 : 7;
1118
+ const baseShift = isBaseCompact ? 5 : 3;
1119
+ const baseColor = BASE_COLOR_IDS[value & baseMask];
1120
+ value >>= baseShift;
860
1121
  if (value !== 0 || !baseColor || !themeColor || !chartPalette || !headingFont || !bodyFont || !iconLibrary || !radius) {
861
1122
  throw new PresetValidationError("Compact preset code contains an out-of-range value.", "preset");
862
1123
  }
@@ -1057,25 +1318,25 @@ function validateCodecPayloadCanonical(value) {
1057
1318
  }
1058
1319
  if (typeof value.themeColor !== "string" || !isThemeColorId(value.themeColor)) {
1059
1320
  throw new PresetValidationError(
1060
- "Invalid preset.themeColor. Expected ink | red | blue | violet | green | orange.",
1321
+ "Invalid preset.themeColor. Expected a supported theme color id.",
1061
1322
  "preset.themeColor"
1062
1323
  );
1063
1324
  }
1064
1325
  if (typeof value.chartPalette !== "string" || !isChartPaletteId(value.chartPalette)) {
1065
1326
  throw new PresetValidationError(
1066
- "Invalid preset.chartPalette. Expected theme | warm | cool | mixed.",
1327
+ "Invalid preset.chartPalette. Expected a supported chart palette id.",
1067
1328
  "preset.chartPalette"
1068
1329
  );
1069
1330
  }
1070
1331
  if (typeof value.headingFont !== "string" || !isHeadingFontId(value.headingFont)) {
1071
1332
  throw new PresetValidationError(
1072
- "Invalid preset.headingFont. Expected system | playfair.",
1333
+ "Invalid preset.headingFont. Expected a supported heading font id.",
1073
1334
  "preset.headingFont"
1074
1335
  );
1075
1336
  }
1076
1337
  if (typeof value.bodyFont !== "string" || !isBodyFontId(value.bodyFont)) {
1077
1338
  throw new PresetValidationError(
1078
- "Invalid preset.bodyFont. Expected system | inter.",
1339
+ "Invalid preset.bodyFont. Expected a supported body font id.",
1079
1340
  "preset.bodyFont"
1080
1341
  );
1081
1342
  }
@@ -1144,7 +1405,7 @@ function decodePresetEnvelope(code) {
1144
1405
  if (!encoded) {
1145
1406
  throw new PresetValidationError("Preset code payload is empty.", "preset");
1146
1407
  }
1147
- if (encoded.length === COMPACT_CANONICAL_ENCODED_LENGTH && encoded.startsWith(COMPACT_CANONICAL_PREFIX)) {
1408
+ if ((encoded.length === COMPACT_CANONICAL_ENCODED_LENGTH || encoded.length === COMPACT_CANONICAL_EXPANDED_ENCODED_LENGTH || encoded.length === COMPACT_CANONICAL_CHART_ENCODED_LENGTH || encoded.length === COMPACT_CANONICAL_THEME_ENCODED_LENGTH || encoded.length === COMPACT_CANONICAL_BASE_ENCODED_LENGTH) && (encoded.startsWith(COMPACT_CANONICAL_PREFIX) || encoded.startsWith(COMPACT_CANONICAL_LEGACY_PREFIX) || encoded.startsWith(COMPACT_CANONICAL_EXPANDED_PREFIX) || encoded.startsWith(COMPACT_CANONICAL_CHART_PREFIX) || encoded.startsWith(COMPACT_CANONICAL_THEME_PREFIX) || encoded.startsWith(COMPACT_CANONICAL_BASE_PREFIX))) {
1148
1409
  return decodeCompactCanonicalPayload(encoded);
1149
1410
  }
1150
1411
  if (encoded.includes("=") || /[^A-Za-z0-9_-]/.test(encoded)) {
@@ -1275,8 +1536,8 @@ var LUCIDE_TO_TABLER = Object.fromEntries(
1275
1536
  var BASE_COLOR_PALETTE = {
1276
1537
  zinc: {
1277
1538
  light: {
1278
- surfaceElevated: "#ffffff",
1279
- surfaceOverlay: "#ffffff",
1539
+ surfaceElevated: "#fafafa",
1540
+ surfaceOverlay: "#f4f4f5",
1280
1541
  foreground: "#09090b",
1281
1542
  border: "rgba(9, 9, 11, 0.08)",
1282
1543
  mutedForeground: "#71717a"
@@ -1291,8 +1552,8 @@ var BASE_COLOR_PALETTE = {
1291
1552
  },
1292
1553
  neutral: {
1293
1554
  light: {
1294
- surfaceElevated: "#ffffff",
1295
- surfaceOverlay: "#ffffff",
1555
+ surfaceElevated: "#fafafa",
1556
+ surfaceOverlay: "#f5f5f5",
1296
1557
  foreground: "#171717",
1297
1558
  border: "rgba(23, 23, 23, 0.08)",
1298
1559
  mutedForeground: "#737373"
@@ -1305,10 +1566,26 @@ var BASE_COLOR_PALETTE = {
1305
1566
  mutedForeground: "#a3a3a3"
1306
1567
  }
1307
1568
  },
1569
+ gray: {
1570
+ light: {
1571
+ surfaceElevated: "#f9fafb",
1572
+ surfaceOverlay: "#f3f4f6",
1573
+ foreground: "#111827",
1574
+ border: "rgba(17, 24, 39, 0.08)",
1575
+ mutedForeground: "#6b7280"
1576
+ },
1577
+ dark: {
1578
+ surfaceElevated: "#1a1f2b",
1579
+ surfaceOverlay: "#262c3a",
1580
+ foreground: "#f9fafb",
1581
+ border: "rgba(249, 250, 251, 0.10)",
1582
+ mutedForeground: "#9ca3af"
1583
+ }
1584
+ },
1308
1585
  slate: {
1309
1586
  light: {
1310
- surfaceElevated: "#ffffff",
1311
- surfaceOverlay: "#ffffff",
1587
+ surfaceElevated: "#f8fafc",
1588
+ surfaceOverlay: "#f1f5f9",
1312
1589
  foreground: "#0f172a",
1313
1590
  border: "rgba(15, 23, 42, 0.08)",
1314
1591
  mutedForeground: "#64748b"
@@ -1321,10 +1598,26 @@ var BASE_COLOR_PALETTE = {
1321
1598
  mutedForeground: "#94a3b8"
1322
1599
  }
1323
1600
  },
1601
+ "cool-gray": {
1602
+ light: {
1603
+ surfaceElevated: "#f6fafb",
1604
+ surfaceOverlay: "#eaf3f4",
1605
+ foreground: "#13343b",
1606
+ border: "rgba(19, 52, 59, 0.08)",
1607
+ mutedForeground: "#5f7d84"
1608
+ },
1609
+ dark: {
1610
+ surfaceElevated: "#101c1f",
1611
+ surfaceOverlay: "#1b2d31",
1612
+ foreground: "#eef6f7",
1613
+ border: "rgba(238, 246, 247, 0.10)",
1614
+ mutedForeground: "#9db3b8"
1615
+ }
1616
+ },
1324
1617
  stone: {
1325
1618
  light: {
1326
- surfaceElevated: "#ffffff",
1327
- surfaceOverlay: "#ffffff",
1619
+ surfaceElevated: "#fafaf9",
1620
+ surfaceOverlay: "#f5f5f4",
1328
1621
  foreground: "#1c1917",
1329
1622
  border: "rgba(28, 25, 23, 0.08)",
1330
1623
  mutedForeground: "#78716c"
@@ -1340,7 +1633,7 @@ var BASE_COLOR_PALETTE = {
1340
1633
  taupe: {
1341
1634
  light: {
1342
1635
  surfaceElevated: "#faf8f5",
1343
- surfaceOverlay: "#ffffff",
1636
+ surfaceOverlay: "#f3ede6",
1344
1637
  foreground: "#3b342e",
1345
1638
  border: "rgba(59, 52, 46, 0.10)",
1346
1639
  mutedForeground: "#8a7f74"
@@ -1352,6 +1645,54 @@ var BASE_COLOR_PALETTE = {
1352
1645
  border: "rgba(245, 240, 234, 0.10)",
1353
1646
  mutedForeground: "#a89f94"
1354
1647
  }
1648
+ },
1649
+ mauve: {
1650
+ light: {
1651
+ surfaceElevated: "#fbf8fb",
1652
+ surfaceOverlay: "#f4eef5",
1653
+ foreground: "#34283a",
1654
+ border: "rgba(52, 40, 58, 0.10)",
1655
+ mutedForeground: "#837388"
1656
+ },
1657
+ dark: {
1658
+ surfaceElevated: "#1b151f",
1659
+ surfaceOverlay: "#2a2130",
1660
+ foreground: "#f5eef7",
1661
+ border: "rgba(245, 238, 247, 0.10)",
1662
+ mutedForeground: "#b3a5b8"
1663
+ }
1664
+ },
1665
+ olive: {
1666
+ light: {
1667
+ surfaceElevated: "#fafbf4",
1668
+ surfaceOverlay: "#f0f3e4",
1669
+ foreground: "#2d331f",
1670
+ border: "rgba(45, 51, 31, 0.10)",
1671
+ mutedForeground: "#747b61"
1672
+ },
1673
+ dark: {
1674
+ surfaceElevated: "#171a12",
1675
+ surfaceOverlay: "#24291b",
1676
+ foreground: "#f2f5e8",
1677
+ border: "rgba(242, 245, 232, 0.10)",
1678
+ mutedForeground: "#abb196"
1679
+ }
1680
+ },
1681
+ mist: {
1682
+ light: {
1683
+ surfaceElevated: "#f7fbfb",
1684
+ surfaceOverlay: "#edf5f5",
1685
+ foreground: "#253638",
1686
+ border: "rgba(37, 54, 56, 0.10)",
1687
+ mutedForeground: "#697d80"
1688
+ },
1689
+ dark: {
1690
+ surfaceElevated: "#111b1d",
1691
+ surfaceOverlay: "#1d2b2e",
1692
+ foreground: "#edf7f8",
1693
+ border: "rgba(237, 247, 248, 0.10)",
1694
+ mutedForeground: "#9fb5b8"
1695
+ }
1355
1696
  }
1356
1697
  };
1357
1698
  var THEME_COLOR_PALETTE = {
@@ -1476,6 +1817,174 @@ var THEME_COLOR_PALETTE = {
1476
1817
  controlSolidForeground: "#fff7ed"
1477
1818
  // orange-50
1478
1819
  }
1820
+ },
1821
+ amber: {
1822
+ light: {
1823
+ controlSolid: "#b45309",
1824
+ controlSolidHover: "#d97706",
1825
+ controlSolidActive: "#92400e",
1826
+ controlSolidForeground: "#fffbeb"
1827
+ },
1828
+ dark: {
1829
+ controlSolid: "#92400e",
1830
+ controlSolidHover: "#b45309",
1831
+ controlSolidActive: "#78350f",
1832
+ controlSolidForeground: "#fffbeb"
1833
+ }
1834
+ },
1835
+ cyan: {
1836
+ light: {
1837
+ controlSolid: "#0e7490",
1838
+ controlSolidHover: "#0891b2",
1839
+ controlSolidActive: "#155e75",
1840
+ controlSolidForeground: "#ecfeff"
1841
+ },
1842
+ dark: {
1843
+ controlSolid: "#155e75",
1844
+ controlSolidHover: "#0e7490",
1845
+ controlSolidActive: "#164e63",
1846
+ controlSolidForeground: "#ecfeff"
1847
+ }
1848
+ },
1849
+ emerald: {
1850
+ light: {
1851
+ controlSolid: "#047857",
1852
+ controlSolidHover: "#059669",
1853
+ controlSolidActive: "#065f46",
1854
+ controlSolidForeground: "#ecfdf5"
1855
+ },
1856
+ dark: {
1857
+ controlSolid: "#065f46",
1858
+ controlSolidHover: "#047857",
1859
+ controlSolidActive: "#064e3b",
1860
+ controlSolidForeground: "#ecfdf5"
1861
+ }
1862
+ },
1863
+ indigo: {
1864
+ light: {
1865
+ controlSolid: "#4338ca",
1866
+ controlSolidHover: "#4f46e5",
1867
+ controlSolidActive: "#3730a3",
1868
+ controlSolidForeground: "#eef2ff"
1869
+ },
1870
+ dark: {
1871
+ controlSolid: "#3730a3",
1872
+ controlSolidHover: "#4338ca",
1873
+ controlSolidActive: "#312e81",
1874
+ controlSolidForeground: "#eef2ff"
1875
+ }
1876
+ },
1877
+ pink: {
1878
+ light: {
1879
+ controlSolid: "#be185d",
1880
+ controlSolidHover: "#db2777",
1881
+ controlSolidActive: "#9d174d",
1882
+ controlSolidForeground: "#fdf2f8"
1883
+ },
1884
+ dark: {
1885
+ controlSolid: "#9d174d",
1886
+ controlSolidHover: "#be185d",
1887
+ controlSolidActive: "#831843",
1888
+ controlSolidForeground: "#fdf2f8"
1889
+ }
1890
+ },
1891
+ purple: {
1892
+ light: {
1893
+ controlSolid: "#7e22ce",
1894
+ controlSolidHover: "#9333ea",
1895
+ controlSolidActive: "#6b21a8",
1896
+ controlSolidForeground: "#faf5ff"
1897
+ },
1898
+ dark: {
1899
+ controlSolid: "#6b21a8",
1900
+ controlSolidHover: "#7e22ce",
1901
+ controlSolidActive: "#581c87",
1902
+ controlSolidForeground: "#faf5ff"
1903
+ }
1904
+ },
1905
+ rose: {
1906
+ light: {
1907
+ controlSolid: "#be123c",
1908
+ controlSolidHover: "#e11d48",
1909
+ controlSolidActive: "#9f1239",
1910
+ controlSolidForeground: "#fff1f2"
1911
+ },
1912
+ dark: {
1913
+ controlSolid: "#9f1239",
1914
+ controlSolidHover: "#be123c",
1915
+ controlSolidActive: "#881337",
1916
+ controlSolidForeground: "#fff1f2"
1917
+ }
1918
+ },
1919
+ sky: {
1920
+ light: {
1921
+ controlSolid: "#0369a1",
1922
+ controlSolidHover: "#0284c7",
1923
+ controlSolidActive: "#075985",
1924
+ controlSolidForeground: "#f0f9ff"
1925
+ },
1926
+ dark: {
1927
+ controlSolid: "#075985",
1928
+ controlSolidHover: "#0369a1",
1929
+ controlSolidActive: "#0c4a6e",
1930
+ controlSolidForeground: "#f0f9ff"
1931
+ }
1932
+ },
1933
+ teal: {
1934
+ light: {
1935
+ controlSolid: "#0f766e",
1936
+ controlSolidHover: "#0d9488",
1937
+ controlSolidActive: "#115e59",
1938
+ controlSolidForeground: "#f0fdfa"
1939
+ },
1940
+ dark: {
1941
+ controlSolid: "#115e59",
1942
+ controlSolidHover: "#0f766e",
1943
+ controlSolidActive: "#134e4a",
1944
+ controlSolidForeground: "#f0fdfa"
1945
+ }
1946
+ },
1947
+ yellow: {
1948
+ light: {
1949
+ controlSolid: "#a16207",
1950
+ controlSolidHover: "#ca8a04",
1951
+ controlSolidActive: "#854d0e",
1952
+ controlSolidForeground: "#fefce8"
1953
+ },
1954
+ dark: {
1955
+ controlSolid: "#854d0e",
1956
+ controlSolidHover: "#a16207",
1957
+ controlSolidActive: "#713f12",
1958
+ controlSolidForeground: "#fefce8"
1959
+ }
1960
+ },
1961
+ fuchsia: {
1962
+ light: {
1963
+ controlSolid: "#a21caf",
1964
+ controlSolidHover: "#c026d3",
1965
+ controlSolidActive: "#86198f",
1966
+ controlSolidForeground: "#fdf4ff"
1967
+ },
1968
+ dark: {
1969
+ controlSolid: "#86198f",
1970
+ controlSolidHover: "#a21caf",
1971
+ controlSolidActive: "#701a75",
1972
+ controlSolidForeground: "#fdf4ff"
1973
+ }
1974
+ },
1975
+ lime: {
1976
+ light: {
1977
+ controlSolid: "#4d7c0f",
1978
+ controlSolidHover: "#65a30d",
1979
+ controlSolidActive: "#3f6212",
1980
+ controlSolidForeground: "#f7fee7"
1981
+ },
1982
+ dark: {
1983
+ controlSolid: "#3f6212",
1984
+ controlSolidHover: "#4d7c0f",
1985
+ controlSolidActive: "#365314",
1986
+ controlSolidForeground: "#f7fee7"
1987
+ }
1479
1988
  }
1480
1989
  };
1481
1990
  var DEFAULT_CHART_STOPS = {
@@ -1518,6 +2027,54 @@ var CHART_PALETTES = {
1518
2027
  orange: {
1519
2028
  light: ["#eda100", "#f97316", "#e34948", "#ca8a04", "#c2410c", "#db2777"],
1520
2029
  dark: ["#c98500", "#fb923c", "#e66767", "#facc15", "#fdba74", "#f472b6"]
2030
+ },
2031
+ amber: {
2032
+ light: ["#d97706", "#f59e0b", "#b45309", "#fbbf24", "#92400e", "#f97316"],
2033
+ dark: ["#fbbf24", "#f59e0b", "#fcd34d", "#d97706", "#fde68a", "#fb923c"]
2034
+ },
2035
+ cyan: {
2036
+ light: ["#0891b2", "#06b6d4", "#0e7490", "#22d3ee", "#155e75", "#0284c7"],
2037
+ dark: ["#22d3ee", "#67e8f9", "#06b6d4", "#38bdf8", "#a5f3fc", "#0891b2"]
2038
+ },
2039
+ emerald: {
2040
+ light: ["#059669", "#10b981", "#047857", "#34d399", "#065f46", "#0d9488"],
2041
+ dark: ["#34d399", "#6ee7b7", "#10b981", "#2dd4bf", "#a7f3d0", "#059669"]
2042
+ },
2043
+ indigo: {
2044
+ light: ["#4f46e5", "#6366f1", "#4338ca", "#818cf8", "#3730a3", "#7c3aed"],
2045
+ dark: ["#818cf8", "#a5b4fc", "#6366f1", "#a78bfa", "#c7d2fe", "#4f46e5"]
2046
+ },
2047
+ pink: {
2048
+ light: ["#db2777", "#ec4899", "#be185d", "#f472b6", "#9d174d", "#e11d48"],
2049
+ dark: ["#f472b6", "#f9a8d4", "#ec4899", "#fb7185", "#fbcfe8", "#db2777"]
2050
+ },
2051
+ purple: {
2052
+ light: ["#9333ea", "#a855f7", "#7e22ce", "#c084fc", "#6b21a8", "#7c3aed"],
2053
+ dark: ["#c084fc", "#d8b4fe", "#a855f7", "#a78bfa", "#e9d5ff", "#9333ea"]
2054
+ },
2055
+ rose: {
2056
+ light: ["#e11d48", "#f43f5e", "#be123c", "#fb7185", "#9f1239", "#db2777"],
2057
+ dark: ["#fb7185", "#fda4af", "#f43f5e", "#f472b6", "#ffe4e6", "#e11d48"]
2058
+ },
2059
+ sky: {
2060
+ light: ["#0284c7", "#0ea5e9", "#0369a1", "#38bdf8", "#075985", "#2563eb"],
2061
+ dark: ["#38bdf8", "#7dd3fc", "#0ea5e9", "#60a5fa", "#bae6fd", "#0284c7"]
2062
+ },
2063
+ teal: {
2064
+ light: ["#0d9488", "#14b8a6", "#0f766e", "#2dd4bf", "#115e59", "#0891b2"],
2065
+ dark: ["#2dd4bf", "#5eead4", "#14b8a6", "#22d3ee", "#99f6e4", "#0d9488"]
2066
+ },
2067
+ yellow: {
2068
+ light: ["#ca8a04", "#eab308", "#a16207", "#facc15", "#854d0e", "#f59e0b"],
2069
+ dark: ["#facc15", "#fde047", "#eab308", "#fbbf24", "#fef08a", "#ca8a04"]
2070
+ },
2071
+ fuchsia: {
2072
+ light: ["#c026d3", "#d946ef", "#a21caf", "#e879f9", "#86198f", "#db2777"],
2073
+ dark: ["#e879f9", "#f0abfc", "#d946ef", "#f472b6", "#fae8ff", "#c026d3"]
2074
+ },
2075
+ lime: {
2076
+ light: ["#65a30d", "#84cc16", "#4d7c0f", "#a3e635", "#3f6212", "#16a34a"],
2077
+ dark: ["#a3e635", "#bef264", "#84cc16", "#4ade80", "#d9f99d", "#65a30d"]
1521
2078
  }
1522
2079
  },
1523
2080
  warm: {
@@ -1531,6 +2088,70 @@ var CHART_PALETTES = {
1531
2088
  mixed: {
1532
2089
  light: ["#2a78d6", "#e34948", "#1baf7a", "#eda100", "#4a3aa7", "#db2777"],
1533
2090
  dark: ["#3987e5", "#e66767", "#199e70", "#c98500", "#9085e9", "#f472b6"]
2091
+ },
2092
+ neutral: {
2093
+ light: ["#404040", "#737373", "#a3a3a3", "#525252", "#262626", "#d4d4d4"],
2094
+ dark: ["#d4d4d4", "#a3a3a3", "#737373", "#e5e5e5", "#f5f5f5", "#525252"]
2095
+ },
2096
+ zinc: {
2097
+ light: ["#3f3f46", "#71717a", "#a1a1aa", "#52525b", "#27272a", "#d4d4d8"],
2098
+ dark: ["#d4d4d8", "#a1a1aa", "#71717a", "#e4e4e7", "#f4f4f5", "#52525b"]
2099
+ },
2100
+ stone: {
2101
+ light: ["#44403c", "#78716c", "#a8a29e", "#57534e", "#292524", "#d6d3d1"],
2102
+ dark: ["#d6d3d1", "#a8a29e", "#78716c", "#e7e5e4", "#fafaf9", "#57534e"]
2103
+ },
2104
+ gray: {
2105
+ light: ["#374151", "#6b7280", "#9ca3af", "#4b5563", "#1f2937", "#d1d5db"],
2106
+ dark: ["#d1d5db", "#9ca3af", "#6b7280", "#e5e7eb", "#f9fafb", "#4b5563"]
2107
+ },
2108
+ amber: {
2109
+ light: ["#d97706", "#f59e0b", "#b45309", "#fbbf24", "#92400e", "#f97316"],
2110
+ dark: ["#fbbf24", "#f59e0b", "#fcd34d", "#d97706", "#fde68a", "#fb923c"]
2111
+ },
2112
+ cyan: {
2113
+ light: ["#0891b2", "#06b6d4", "#0e7490", "#22d3ee", "#155e75", "#0284c7"],
2114
+ dark: ["#22d3ee", "#67e8f9", "#06b6d4", "#38bdf8", "#a5f3fc", "#0891b2"]
2115
+ },
2116
+ emerald: {
2117
+ light: ["#059669", "#10b981", "#047857", "#34d399", "#065f46", "#0d9488"],
2118
+ dark: ["#34d399", "#6ee7b7", "#10b981", "#2dd4bf", "#a7f3d0", "#059669"]
2119
+ },
2120
+ indigo: {
2121
+ light: ["#4f46e5", "#6366f1", "#4338ca", "#818cf8", "#3730a3", "#7c3aed"],
2122
+ dark: ["#818cf8", "#a5b4fc", "#6366f1", "#a78bfa", "#c7d2fe", "#4f46e5"]
2123
+ },
2124
+ lime: {
2125
+ light: ["#65a30d", "#84cc16", "#4d7c0f", "#a3e635", "#3f6212", "#16a34a"],
2126
+ dark: ["#a3e635", "#bef264", "#84cc16", "#4ade80", "#d9f99d", "#65a30d"]
2127
+ },
2128
+ pink: {
2129
+ light: ["#db2777", "#ec4899", "#be185d", "#f472b6", "#9d174d", "#e11d48"],
2130
+ dark: ["#f472b6", "#f9a8d4", "#ec4899", "#fb7185", "#fbcfe8", "#db2777"]
2131
+ },
2132
+ purple: {
2133
+ light: ["#9333ea", "#a855f7", "#7e22ce", "#c084fc", "#6b21a8", "#7c3aed"],
2134
+ dark: ["#c084fc", "#d8b4fe", "#a855f7", "#a78bfa", "#e9d5ff", "#9333ea"]
2135
+ },
2136
+ rose: {
2137
+ light: ["#e11d48", "#f43f5e", "#be123c", "#fb7185", "#9f1239", "#db2777"],
2138
+ dark: ["#fb7185", "#fda4af", "#f43f5e", "#f472b6", "#ffe4e6", "#e11d48"]
2139
+ },
2140
+ sky: {
2141
+ light: ["#0284c7", "#0ea5e9", "#0369a1", "#38bdf8", "#075985", "#2563eb"],
2142
+ dark: ["#38bdf8", "#7dd3fc", "#0ea5e9", "#60a5fa", "#bae6fd", "#0284c7"]
2143
+ },
2144
+ teal: {
2145
+ light: ["#0d9488", "#14b8a6", "#0f766e", "#2dd4bf", "#115e59", "#0891b2"],
2146
+ dark: ["#2dd4bf", "#5eead4", "#14b8a6", "#22d3ee", "#99f6e4", "#0d9488"]
2147
+ },
2148
+ yellow: {
2149
+ light: ["#ca8a04", "#eab308", "#a16207", "#facc15", "#854d0e", "#f59e0b"],
2150
+ dark: ["#facc15", "#fde047", "#eab308", "#fbbf24", "#fef08a", "#ca8a04"]
2151
+ },
2152
+ taupe: {
2153
+ light: ["#8a6f55", "#a78b71", "#6f5a46", "#c4aa8f", "#4f4034", "#d97706"],
2154
+ dark: ["#c4aa8f", "#dcc8b2", "#a78b71", "#f3ede6", "#8a6f55", "#fbbf24"]
1534
2155
  }
1535
2156
  };
1536
2157
 
@@ -1540,6 +2161,51 @@ var HEADING_FONT_PACKAGES = {
1540
2161
  packageName: "@fontsource/playfair-display",
1541
2162
  importSpecifier: "@fontsource/playfair-display",
1542
2163
  familyName: "Playfair Display"
2164
+ },
2165
+ "instrument-serif": {
2166
+ packageName: "@fontsource/instrument-serif",
2167
+ importSpecifier: "@fontsource/instrument-serif",
2168
+ familyName: "Instrument Serif"
2169
+ },
2170
+ fraunces: {
2171
+ packageName: "@fontsource/fraunces",
2172
+ importSpecifier: "@fontsource/fraunces",
2173
+ familyName: "Fraunces"
2174
+ },
2175
+ "space-grotesk": {
2176
+ packageName: "@fontsource/space-grotesk",
2177
+ importSpecifier: "@fontsource/space-grotesk",
2178
+ familyName: "Space Grotesk"
2179
+ },
2180
+ "noto-serif": {
2181
+ packageName: "@fontsource/noto-serif",
2182
+ importSpecifier: "@fontsource/noto-serif",
2183
+ familyName: "Noto Serif"
2184
+ },
2185
+ "eb-garamond": {
2186
+ packageName: "@fontsource/eb-garamond",
2187
+ importSpecifier: "@fontsource/eb-garamond",
2188
+ familyName: "EB Garamond"
2189
+ },
2190
+ lora: {
2191
+ packageName: "@fontsource/lora",
2192
+ importSpecifier: "@fontsource/lora",
2193
+ familyName: "Lora"
2194
+ },
2195
+ montserrat: {
2196
+ packageName: "@fontsource/montserrat",
2197
+ importSpecifier: "@fontsource/montserrat",
2198
+ familyName: "Montserrat"
2199
+ },
2200
+ manrope: {
2201
+ packageName: "@fontsource/manrope",
2202
+ importSpecifier: "@fontsource/manrope",
2203
+ familyName: "Manrope"
2204
+ },
2205
+ inter: {
2206
+ packageName: "@fontsource/inter",
2207
+ importSpecifier: "@fontsource/inter",
2208
+ familyName: "Inter"
1543
2209
  }
1544
2210
  };
1545
2211
  var BODY_FONT_PACKAGES = {
@@ -1547,15 +2213,78 @@ var BODY_FONT_PACKAGES = {
1547
2213
  packageName: "@fontsource/inter",
1548
2214
  importSpecifier: "@fontsource/inter",
1549
2215
  familyName: "Inter"
2216
+ },
2217
+ geist: {
2218
+ packageName: "@fontsource/geist",
2219
+ importSpecifier: "@fontsource/geist",
2220
+ familyName: "Geist"
2221
+ },
2222
+ "dm-sans": {
2223
+ packageName: "@fontsource/dm-sans",
2224
+ importSpecifier: "@fontsource/dm-sans",
2225
+ familyName: "DM Sans"
2226
+ },
2227
+ "plus-jakarta-sans": {
2228
+ packageName: "@fontsource/plus-jakarta-sans",
2229
+ importSpecifier: "@fontsource/plus-jakarta-sans",
2230
+ familyName: "Plus Jakarta Sans"
2231
+ },
2232
+ figtree: {
2233
+ packageName: "@fontsource/figtree",
2234
+ importSpecifier: "@fontsource/figtree",
2235
+ familyName: "Figtree"
2236
+ },
2237
+ "noto-sans": {
2238
+ packageName: "@fontsource/noto-sans",
2239
+ importSpecifier: "@fontsource/noto-sans",
2240
+ familyName: "Noto Sans"
2241
+ },
2242
+ "public-sans": {
2243
+ packageName: "@fontsource/public-sans",
2244
+ importSpecifier: "@fontsource/public-sans",
2245
+ familyName: "Public Sans"
2246
+ },
2247
+ outfit: {
2248
+ packageName: "@fontsource/outfit",
2249
+ importSpecifier: "@fontsource/outfit",
2250
+ familyName: "Outfit"
2251
+ },
2252
+ manrope: {
2253
+ packageName: "@fontsource/manrope",
2254
+ importSpecifier: "@fontsource/manrope",
2255
+ familyName: "Manrope"
2256
+ },
2257
+ "ibm-plex-sans": {
2258
+ packageName: "@fontsource/ibm-plex-sans",
2259
+ importSpecifier: "@fontsource/ibm-plex-sans",
2260
+ familyName: "IBM Plex Sans"
1550
2261
  }
1551
2262
  };
1552
2263
  var HEADING_FONT_CSS = {
1553
2264
  system: "var(--font-display)",
1554
- playfair: "'Playfair Display', Georgia, 'Times New Roman', serif"
2265
+ playfair: "'Playfair Display', Georgia, 'Times New Roman', serif",
2266
+ "instrument-serif": "'Instrument Serif', Georgia, 'Times New Roman', serif",
2267
+ fraunces: "'Fraunces', Georgia, 'Times New Roman', serif",
2268
+ "space-grotesk": "'Space Grotesk', ui-sans-serif, system-ui, sans-serif",
2269
+ "noto-serif": "'Noto Serif', Georgia, 'Times New Roman', serif",
2270
+ "eb-garamond": "'EB Garamond', Georgia, 'Times New Roman', serif",
2271
+ lora: "'Lora', Georgia, 'Times New Roman', serif",
2272
+ montserrat: "'Montserrat', ui-sans-serif, system-ui, sans-serif",
2273
+ manrope: "'Manrope', ui-sans-serif, system-ui, sans-serif",
2274
+ inter: "'Inter', ui-sans-serif, system-ui, sans-serif"
1555
2275
  };
1556
2276
  var BODY_FONT_CSS = {
1557
2277
  system: "var(--font-sans)",
1558
- inter: "'Inter', ui-sans-serif, system-ui, sans-serif"
2278
+ inter: "'Inter', ui-sans-serif, system-ui, sans-serif",
2279
+ geist: "'Geist', ui-sans-serif, system-ui, sans-serif",
2280
+ "dm-sans": "'DM Sans', ui-sans-serif, system-ui, sans-serif",
2281
+ "plus-jakarta-sans": "'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif",
2282
+ figtree: "'Figtree', ui-sans-serif, system-ui, sans-serif",
2283
+ "noto-sans": "'Noto Sans', ui-sans-serif, system-ui, sans-serif",
2284
+ "public-sans": "'Public Sans', ui-sans-serif, system-ui, sans-serif",
2285
+ outfit: "'Outfit', ui-sans-serif, system-ui, sans-serif",
2286
+ manrope: "'Manrope', ui-sans-serif, system-ui, sans-serif",
2287
+ "ibm-plex-sans": "'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif"
1559
2288
  };
1560
2289
 
1561
2290
  // ../wensity-preset/src/resolve.ts
@@ -1614,7 +2343,19 @@ var COLORED_THEME_IDS = /* @__PURE__ */ new Set([
1614
2343
  "blue",
1615
2344
  "violet",
1616
2345
  "green",
1617
- "orange"
2346
+ "orange",
2347
+ "amber",
2348
+ "cyan",
2349
+ "emerald",
2350
+ "indigo",
2351
+ "pink",
2352
+ "purple",
2353
+ "rose",
2354
+ "sky",
2355
+ "teal",
2356
+ "yellow",
2357
+ "fuchsia",
2358
+ "lime"
1618
2359
  ]);
1619
2360
  function colorMix(source, percent) {
1620
2361
  return `color-mix(in srgb, ${source} ${percent}%, transparent)`;
@@ -1732,6 +2473,34 @@ function resolveStyleConfigCanonical(style) {
1732
2473
  dark: resolveMode("dark")
1733
2474
  };
1734
2475
  }
2476
+ function resolvedTokensToCssVars(tokens, mode) {
2477
+ const modeTokens = tokens[mode];
2478
+ return {
2479
+ [RESOLVED_TOKEN_CSS_VARS.surfaceElevated]: modeTokens.surfaceElevated,
2480
+ [RESOLVED_TOKEN_CSS_VARS.surfaceOverlay]: modeTokens.surfaceOverlay,
2481
+ [RESOLVED_TOKEN_CSS_VARS.surfaceHover]: modeTokens.surfaceHover,
2482
+ [RESOLVED_TOKEN_CSS_VARS.surfaceActive]: modeTokens.surfaceActive,
2483
+ [RESOLVED_TOKEN_CSS_VARS.surfaceSelected]: modeTokens.surfaceSelected,
2484
+ [RESOLVED_TOKEN_CSS_VARS.borderSubtle]: modeTokens.borderSubtle,
2485
+ [RESOLVED_TOKEN_CSS_VARS.ring]: modeTokens.ring,
2486
+ [RESOLVED_TOKEN_CSS_VARS.textSecondary]: modeTokens.textSecondary,
2487
+ [RESOLVED_TOKEN_CSS_VARS.textPlaceholder]: modeTokens.textPlaceholder,
2488
+ [RESOLVED_TOKEN_CSS_VARS.controlSolid]: modeTokens.controlSolid,
2489
+ [RESOLVED_TOKEN_CSS_VARS.controlSolidHover]: modeTokens.controlSolidHover,
2490
+ [RESOLVED_TOKEN_CSS_VARS.controlSolidActive]: modeTokens.controlSolidActive,
2491
+ [RESOLVED_TOKEN_CSS_VARS.controlSolidForeground]: modeTokens.controlSolidForeground,
2492
+ [RESOLVED_TOKEN_CSS_VARS.chart1]: modeTokens.chart1,
2493
+ [RESOLVED_TOKEN_CSS_VARS.chart2]: modeTokens.chart2,
2494
+ [RESOLVED_TOKEN_CSS_VARS.chart3]: modeTokens.chart3,
2495
+ [RESOLVED_TOKEN_CSS_VARS.chart4]: modeTokens.chart4,
2496
+ [RESOLVED_TOKEN_CSS_VARS.chart5]: modeTokens.chart5,
2497
+ [RESOLVED_TOKEN_CSS_VARS.chart6]: modeTokens.chart6,
2498
+ [RESOLVED_TOKEN_CSS_VARS.radius]: tokens.radius,
2499
+ [RESOLVED_TOKEN_CSS_VARS.fontSans]: tokens.fontSans,
2500
+ [RESOLVED_TOKEN_CSS_VARS.fontDisplay]: tokens.fontDisplay,
2501
+ [RESOLVED_TOKEN_CSS_VARS.fontMono]: tokens.fontMono
2502
+ };
2503
+ }
1735
2504
 
1736
2505
  // src/paths.ts
1737
2506
  import { dirname, isAbsolute, relative, resolve, sep } from "path";
@@ -2702,13 +3471,13 @@ function missingDependencies(projectRoot, dependencies) {
2702
3471
  function readInstalledPackageNames(projectRoot) {
2703
3472
  const packageJsonPath = join4(projectRoot, "package.json");
2704
3473
  if (!existsSync2(packageJsonPath)) return void 0;
2705
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
3474
+ const packageJson2 = JSON.parse(readFileSync(packageJsonPath, "utf8"));
2706
3475
  return new Set(
2707
3476
  Object.keys({
2708
- ...packageJson.dependencies,
2709
- ...packageJson.devDependencies,
2710
- ...packageJson.peerDependencies,
2711
- ...packageJson.optionalDependencies
3477
+ ...packageJson2.dependencies,
3478
+ ...packageJson2.devDependencies,
3479
+ ...packageJson2.peerDependencies,
3480
+ ...packageJson2.optionalDependencies
2712
3481
  })
2713
3482
  );
2714
3483
  }
@@ -2851,6 +3620,109 @@ function toCliPresetError(error) {
2851
3620
  return new CliError(`Invalid preset: ${message}`);
2852
3621
  }
2853
3622
 
3623
+ // src/preset-inspect.ts
3624
+ function createPresetInspectSummary(input) {
3625
+ const tokens = resolveTokensForStyle(input.style);
3626
+ const fontPlan = fontPlanForStyle(input.style);
3627
+ return {
3628
+ presetCode: input.presetCode,
3629
+ kind: isCanonicalStyleShape(input.style) ? "canonical" : "legacy",
3630
+ style: input.style,
3631
+ fontPackages: fontPlan.packages,
3632
+ fontImports: fontPlan.importLines,
3633
+ tokens: {
3634
+ radius: tokens.radius,
3635
+ fonts: {
3636
+ sans: tokens.fontSans,
3637
+ display: tokens.fontDisplay,
3638
+ mono: tokens.fontMono
3639
+ },
3640
+ light: summarizeMode(tokens, "light"),
3641
+ dark: summarizeMode(tokens, "dark")
3642
+ }
3643
+ };
3644
+ }
3645
+ function formatPresetInspectSummary(summary) {
3646
+ return [
3647
+ panel("Preset", [
3648
+ `Code: ${summary.presetCode}`,
3649
+ `Shape: ${summary.kind}`,
3650
+ `Icon library: ${summary.style.iconLibrary}`,
3651
+ `Radius: ${summary.style.radius} (${summary.tokens.radius})`
3652
+ ]),
3653
+ formatStylePanel(summary),
3654
+ formatFontPanel(summary),
3655
+ formatTokenPanel(summary)
3656
+ ].join("\n");
3657
+ }
3658
+ function fontPlanForStyle(style) {
3659
+ if (!isCanonicalStyleShape(style)) {
3660
+ return { packages: [], importLines: [], block: null };
3661
+ }
3662
+ return planFontInstall(style.headingFont, style.bodyFont);
3663
+ }
3664
+ function summarizeMode(tokens, mode) {
3665
+ const cssVars = resolvedTokensToCssVars(tokens, mode);
3666
+ return {
3667
+ controlSolid: cssVars["--primitive-control-solid"] ?? tokens[mode].controlSolid,
3668
+ ring: cssVars["--primitive-ring"] ?? tokens[mode].ring,
3669
+ surfaceSelected: cssVars["--primitive-surface-selected"] ?? tokens[mode].surfaceSelected,
3670
+ chart: [
3671
+ cssVars["--primitive-chart-1"] ?? tokens[mode].chart1,
3672
+ cssVars["--primitive-chart-2"] ?? tokens[mode].chart2,
3673
+ cssVars["--primitive-chart-3"] ?? tokens[mode].chart3,
3674
+ cssVars["--primitive-chart-4"] ?? tokens[mode].chart4,
3675
+ cssVars["--primitive-chart-5"] ?? tokens[mode].chart5,
3676
+ cssVars["--primitive-chart-6"] ?? tokens[mode].chart6
3677
+ ]
3678
+ };
3679
+ }
3680
+ function formatStylePanel(summary) {
3681
+ if (isCanonicalStyleShape(summary.style)) {
3682
+ return panel("Style", [
3683
+ `Base color: ${summary.style.baseColor}`,
3684
+ `Theme color: ${summary.style.themeColor}`,
3685
+ `Chart palette: ${summary.style.chartPalette}`,
3686
+ `Heading font: ${summary.style.headingFont}`,
3687
+ `Body font: ${summary.style.bodyFont}`
3688
+ ]);
3689
+ }
3690
+ return panel("Style", [
3691
+ "Legacy payload",
3692
+ `Font sans: ${summary.style.fonts.sans}`,
3693
+ `Font display: ${summary.style.fonts.display}`,
3694
+ `Font mono: ${summary.style.fonts.mono}`
3695
+ ]);
3696
+ }
3697
+ function formatFontPanel(summary) {
3698
+ const rows = [
3699
+ `--primitive-font-sans: ${summary.tokens.fonts.sans}`,
3700
+ `--primitive-font-display: ${summary.tokens.fonts.display}`,
3701
+ `--primitive-font-mono: ${summary.tokens.fonts.mono}`
3702
+ ];
3703
+ const install = summary.fontPackages.length ? [
3704
+ "",
3705
+ action(`Install packages: ${summary.fontPackages.join(" ")}`),
3706
+ action(`CSS imports: ${summary.fontImports.join(" ")}`)
3707
+ ].join("\n") : ["", action("No Fontsource packages required.")].join("\n");
3708
+ return `${panel("Fonts", rows)}${install}`;
3709
+ }
3710
+ function formatTokenPanel(summary) {
3711
+ return panel("Resolved primitive tokens", [
3712
+ "Light",
3713
+ ` control: ${summary.tokens.light.controlSolid}`,
3714
+ ` ring: ${summary.tokens.light.ring}`,
3715
+ ` selected: ${summary.tokens.light.surfaceSelected}`,
3716
+ ` chart: ${summary.tokens.light.chart.join(", ")}`,
3717
+ "",
3718
+ "Dark",
3719
+ ` control: ${summary.tokens.dark.controlSolid}`,
3720
+ ` ring: ${summary.tokens.dark.ring}`,
3721
+ ` selected: ${summary.tokens.dark.surfaceSelected}`,
3722
+ ` chart: ${summary.tokens.dark.chart.join(", ")}`
3723
+ ]);
3724
+ }
3725
+
2854
3726
  // src/preset-init-writes.ts
2855
3727
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
2856
3728
  import { dirname as dirname4 } from "path";
@@ -2878,6 +3750,351 @@ async function commitPresetInitWrites(input) {
2878
3750
  }
2879
3751
  }
2880
3752
 
3753
+ // src/project-templates.ts
3754
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
3755
+ import { dirname as dirname5, join as join5 } from "path";
3756
+ var PROJECT_TEMPLATE_IDS = ["next", "vite", "astro", "react-router"];
3757
+ function parseProjectTemplate(value) {
3758
+ if (!value) return void 0;
3759
+ const normalized = value.toLowerCase();
3760
+ if (normalized === "nextjs" || normalized === "next.js") return "next";
3761
+ if (normalized === "reactrouter" || normalized === "react-router") return "react-router";
3762
+ if (isProjectTemplateId(normalized)) return normalized;
3763
+ throw new CliError(
3764
+ `Unsupported template "${value}". Use one of: ${PROJECT_TEMPLATE_IDS.join(", ")}.`
3765
+ );
3766
+ }
3767
+ function getProjectTemplateDefinition(id) {
3768
+ return templateDefinitions[id];
3769
+ }
3770
+ function applyTemplateToConfig(config, template) {
3771
+ return {
3772
+ ...config,
3773
+ tailwind: {
3774
+ ...config.tailwind,
3775
+ css: template.cssPath
3776
+ }
3777
+ };
3778
+ }
3779
+ async function scaffoldProjectTemplate(projectRoot, template, options) {
3780
+ const planned = template.files.map((file) => file.path);
3781
+ if (!options.force) {
3782
+ const conflicts = [];
3783
+ for (const file of template.files) {
3784
+ if (await fileExists(join5(projectRoot, file.path))) {
3785
+ conflicts.push(file.path);
3786
+ }
3787
+ }
3788
+ if (conflicts.length > 0) {
3789
+ throw new CliError(
3790
+ `Template "${template.id}" would overwrite existing files: ${conflicts.join(", ")}. Use --force to overwrite.`
3791
+ );
3792
+ }
3793
+ }
3794
+ if (options.dryRun) return planned;
3795
+ for (const file of template.files) {
3796
+ await writeTemplateFile(projectRoot, file);
3797
+ }
3798
+ return planned;
3799
+ }
3800
+ async function writeTemplateFile(projectRoot, file) {
3801
+ const absolute = join5(projectRoot, file.path);
3802
+ await mkdir4(dirname5(absolute), { recursive: true });
3803
+ if (file.path === "package.json" && await fileExists(absolute)) {
3804
+ const existing = JSON.parse(await readFile4(absolute, "utf8"));
3805
+ const next = JSON.parse(file.content);
3806
+ await writeFile4(
3807
+ absolute,
3808
+ `${JSON.stringify(mergePackageJson(existing, next), null, 2)}
3809
+ `,
3810
+ "utf8"
3811
+ );
3812
+ return;
3813
+ }
3814
+ await writeFile4(absolute, file.content, "utf8");
3815
+ }
3816
+ function mergePackageJson(existing, next) {
3817
+ return {
3818
+ ...existing,
3819
+ ...next,
3820
+ scripts: {
3821
+ ...isRecord(existing.scripts) ? existing.scripts : {},
3822
+ ...isRecord(next.scripts) ? next.scripts : {}
3823
+ },
3824
+ dependencies: {
3825
+ ...isRecord(existing.dependencies) ? existing.dependencies : {},
3826
+ ...isRecord(next.dependencies) ? next.dependencies : {}
3827
+ },
3828
+ devDependencies: {
3829
+ ...isRecord(existing.devDependencies) ? existing.devDependencies : {},
3830
+ ...isRecord(next.devDependencies) ? next.devDependencies : {}
3831
+ }
3832
+ };
3833
+ }
3834
+ function isRecord(value) {
3835
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3836
+ }
3837
+ function isProjectTemplateId(value) {
3838
+ return PROJECT_TEMPLATE_IDS.includes(value);
3839
+ }
3840
+ function packageJson(name, input) {
3841
+ return `${JSON.stringify({
3842
+ name,
3843
+ private: true,
3844
+ version: "0.1.0",
3845
+ type: "module",
3846
+ scripts: input.scripts,
3847
+ dependencies: input.dependencies,
3848
+ devDependencies: input.devDependencies ?? {}
3849
+ }, null, 2)}
3850
+ `;
3851
+ }
3852
+ var templateDefinitions = {
3853
+ next: {
3854
+ id: "next",
3855
+ label: "Next.js",
3856
+ cssPath: "src/app/globals.css",
3857
+ files: [
3858
+ {
3859
+ path: "package.json",
3860
+ content: packageJson("wensity-next-app", {
3861
+ scripts: {
3862
+ dev: "next dev",
3863
+ build: "next build",
3864
+ start: "next start"
3865
+ },
3866
+ dependencies: {
3867
+ "next": "^16.2.4",
3868
+ "react": "^19.2.4",
3869
+ "react-dom": "^19.2.4"
3870
+ },
3871
+ devDependencies: {
3872
+ "@types/node": "^22.10.2",
3873
+ "@types/react": "^19.0.0",
3874
+ "@types/react-dom": "^19.0.0",
3875
+ "tailwindcss": "^4.0.0",
3876
+ "typescript": "^5.7.2"
3877
+ }
3878
+ })
3879
+ },
3880
+ { path: "src/app/globals.css", content: '@import "tailwindcss";\n' },
3881
+ {
3882
+ path: "src/app/layout.tsx",
3883
+ content: `import type { ReactNode } from "react";
3884
+ import "./globals.css";
3885
+
3886
+ export default function RootLayout({ children }: { children: ReactNode }) {
3887
+ return (
3888
+ <html lang="en">
3889
+ <body>{children}</body>
3890
+ </html>
3891
+ );
3892
+ }
3893
+ `
3894
+ },
3895
+ {
3896
+ path: "src/app/page.tsx",
3897
+ content: `export default function Page() {
3898
+ return <main className="min-h-screen bg-background text-foreground" />;
3899
+ }
3900
+ `
3901
+ },
3902
+ {
3903
+ path: "tsconfig.json",
3904
+ content: `${JSON.stringify({
3905
+ compilerOptions: {
3906
+ target: "ES2017",
3907
+ lib: ["dom", "dom.iterable", "esnext"],
3908
+ allowJs: true,
3909
+ skipLibCheck: true,
3910
+ strict: true,
3911
+ noEmit: true,
3912
+ esModuleInterop: true,
3913
+ module: "esnext",
3914
+ moduleResolution: "bundler",
3915
+ resolveJsonModule: true,
3916
+ isolatedModules: true,
3917
+ jsx: "preserve",
3918
+ incremental: true,
3919
+ paths: { "@/*": ["./src/*"] }
3920
+ },
3921
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
3922
+ exclude: ["node_modules"]
3923
+ }, null, 2)}
3924
+ `
3925
+ }
3926
+ ]
3927
+ },
3928
+ vite: {
3929
+ id: "vite",
3930
+ label: "Vite",
3931
+ cssPath: "src/index.css",
3932
+ files: [
3933
+ {
3934
+ path: "package.json",
3935
+ content: packageJson("wensity-vite-app", {
3936
+ scripts: {
3937
+ dev: "vite",
3938
+ build: "vite build",
3939
+ preview: "vite preview"
3940
+ },
3941
+ dependencies: {
3942
+ "vite": "^7.3.0",
3943
+ "react": "^19.2.4",
3944
+ "react-dom": "^19.2.4"
3945
+ },
3946
+ devDependencies: {
3947
+ "@vitejs/plugin-react": "^5.1.2",
3948
+ "@types/react": "^19.0.0",
3949
+ "@types/react-dom": "^19.0.0",
3950
+ "tailwindcss": "^4.0.0",
3951
+ "typescript": "^5.7.2"
3952
+ }
3953
+ })
3954
+ },
3955
+ { path: "index.html", content: '<div id="root"></div><script type="module" src="/src/main.tsx"></script>\n' },
3956
+ { path: "src/index.css", content: '@import "tailwindcss";\n' },
3957
+ {
3958
+ path: "src/main.tsx",
3959
+ content: `import { createRoot } from "react-dom/client";
3960
+ import "./index.css";
3961
+
3962
+ createRoot(document.getElementById("root")!).render(
3963
+ <main className="min-h-screen bg-background text-foreground" />,
3964
+ );
3965
+ `
3966
+ },
3967
+ {
3968
+ path: "vite.config.ts",
3969
+ content: `import { defineConfig } from "vite";
3970
+ import react from "@vitejs/plugin-react";
3971
+
3972
+ export default defineConfig({
3973
+ plugins: [react()],
3974
+ resolve: {
3975
+ alias: {
3976
+ "@": new URL("./src", import.meta.url).pathname,
3977
+ },
3978
+ },
3979
+ });
3980
+ `
3981
+ }
3982
+ ]
3983
+ },
3984
+ astro: {
3985
+ id: "astro",
3986
+ label: "Astro",
3987
+ cssPath: "src/styles/global.css",
3988
+ files: [
3989
+ {
3990
+ path: "package.json",
3991
+ content: packageJson("wensity-astro-app", {
3992
+ scripts: {
3993
+ dev: "astro dev",
3994
+ build: "astro build",
3995
+ preview: "astro preview"
3996
+ },
3997
+ dependencies: {
3998
+ "@astrojs/react": "^4.4.0",
3999
+ "astro": "^5.16.0",
4000
+ "react": "^19.2.4",
4001
+ "react-dom": "^19.2.4"
4002
+ },
4003
+ devDependencies: {
4004
+ "tailwindcss": "^4.0.0",
4005
+ "typescript": "^5.7.2"
4006
+ }
4007
+ })
4008
+ },
4009
+ { path: "src/styles/global.css", content: '@import "tailwindcss";\n' },
4010
+ {
4011
+ path: "astro.config.mjs",
4012
+ content: `import { defineConfig } from "astro/config";
4013
+ import react from "@astrojs/react";
4014
+
4015
+ export default defineConfig({
4016
+ integrations: [react()],
4017
+ });
4018
+ `
4019
+ },
4020
+ {
4021
+ path: "src/pages/index.astro",
4022
+ content: `---
4023
+ import "../styles/global.css";
4024
+ ---
4025
+
4026
+ <main class="min-h-screen bg-background text-foreground"></main>
4027
+ `
4028
+ }
4029
+ ]
4030
+ },
4031
+ "react-router": {
4032
+ id: "react-router",
4033
+ label: "React Router",
4034
+ cssPath: "src/app/globals.css",
4035
+ files: [
4036
+ {
4037
+ path: "package.json",
4038
+ content: packageJson("wensity-react-router-app", {
4039
+ scripts: {
4040
+ dev: "vite",
4041
+ build: "vite build",
4042
+ preview: "vite preview"
4043
+ },
4044
+ dependencies: {
4045
+ "vite": "^7.3.0",
4046
+ "react": "^19.2.4",
4047
+ "react-dom": "^19.2.4",
4048
+ "react-router": "^7.12.0"
4049
+ },
4050
+ devDependencies: {
4051
+ "@vitejs/plugin-react": "^5.1.2",
4052
+ "@types/react": "^19.0.0",
4053
+ "@types/react-dom": "^19.0.0",
4054
+ "tailwindcss": "^4.0.0",
4055
+ "typescript": "^5.7.2"
4056
+ }
4057
+ })
4058
+ },
4059
+ { path: "index.html", content: '<div id="root"></div><script type="module" src="/src/main.tsx"></script>\n' },
4060
+ { path: "src/app/globals.css", content: '@import "tailwindcss";\n' },
4061
+ {
4062
+ path: "src/main.tsx",
4063
+ content: `import { createRoot } from "react-dom/client";
4064
+ import { BrowserRouter, Route, Routes } from "react-router";
4065
+ import "./app/globals.css";
4066
+
4067
+ createRoot(document.getElementById("root")!).render(
4068
+ <BrowserRouter>
4069
+ <Routes>
4070
+ <Route path="/" element={<main className="min-h-screen bg-background text-foreground" />} />
4071
+ </Routes>
4072
+ </BrowserRouter>,
4073
+ );
4074
+ `
4075
+ },
4076
+ {
4077
+ path: "vite.config.ts",
4078
+ content: `import { defineConfig } from "vite";
4079
+ import react from "@vitejs/plugin-react";
4080
+
4081
+ export default defineConfig({
4082
+ plugins: [react()],
4083
+ resolve: {
4084
+ alias: {
4085
+ "@": new URL("./src", import.meta.url).pathname,
4086
+ },
4087
+ },
4088
+ });
4089
+ `
4090
+ }
4091
+ ]
4092
+ }
4093
+ };
4094
+ function formatTemplateFiles(files) {
4095
+ return files.map(toPosixPath);
4096
+ }
4097
+
2881
4098
  // src/style-css.ts
2882
4099
  var ROOT_SELECTOR = ":root";
2883
4100
  var DARK_SELECTOR = ".dark";
@@ -2963,8 +4180,8 @@ function escapeRegExp(value) {
2963
4180
  }
2964
4181
 
2965
4182
  // src/component-install.ts
2966
- import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
2967
- import { dirname as dirname5, join as join5, relative as relative3 } from "path";
4183
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
4184
+ import { dirname as dirname6, join as join6, relative as relative3 } from "path";
2968
4185
 
2969
4186
  // src/templates.ts
2970
4187
  function cnUtilityTemplate() {
@@ -3172,7 +4389,7 @@ async function prepareInstall(projectRoot, config, payload, options = {}) {
3172
4389
  }
3173
4390
  }
3174
4391
  if (!hasGraphHelperFiles && helperNeeds.primitives) {
3175
- const primitivesPath = toPosixPath(join5(config.components, "primitives.tsx"));
4392
+ const primitivesPath = toPosixPath(join6(config.components, "primitives.tsx"));
3176
4393
  if (!await fileExists(resolveInside(projectRoot, primitivesPath))) {
3177
4394
  const primitives = primitivesTemplate(config.aliases.utils);
3178
4395
  files.push({
@@ -3213,7 +4430,7 @@ async function planPrimitiveCssFile(projectRoot, config, payload) {
3213
4430
  const cssRelative = config.tailwind?.css;
3214
4431
  if (!cssRelative) return void 0;
3215
4432
  const absolutePath = resolveInside(projectRoot, cssRelative);
3216
- const existing = await fileExists(absolutePath) ? await readFile4(absolutePath, "utf8") : "";
4433
+ const existing = await fileExists(absolutePath) ? await readFile5(absolutePath, "utf8") : "";
3217
4434
  const withTokens = upsertPrimitiveCssBlock(existing, primitiveCss);
3218
4435
  const fonts = curatedFontsFromStyle(style);
3219
4436
  const fontPlan = planFontInstall(fonts.headingFont, fonts.bodyFont);
@@ -3262,8 +4479,8 @@ ${conflicts.map((path) => ` - ${path}`).join("\n")}`);
3262
4479
  }
3263
4480
  async function writePlannedFiles(files) {
3264
4481
  for (const file of files) {
3265
- await mkdir4(dirname5(file.absolutePath), { recursive: true });
3266
- await writeFile4(file.absolutePath, file.content, "utf8");
4482
+ await mkdir5(dirname6(file.absolutePath), { recursive: true });
4483
+ await writeFile5(file.absolutePath, file.content, "utf8");
3267
4484
  }
3268
4485
  }
3269
4486
  function summarizeFiles(projectRoot, files) {
@@ -3288,13 +4505,13 @@ function getPayloadFileKind(payloadFile) {
3288
4505
  }
3289
4506
  function getPayloadOutputPath(payloadFile, fallbackFileName, config) {
3290
4507
  if (isUtilsHelperFile(payloadFile)) return withSourceExtension(config.utils, "ts");
3291
- if (isOverlayHelperFile(payloadFile)) return toPosixPath(join5(config.components, "overlay.tsx"));
3292
- if (isPrimitivesHelperFile(payloadFile)) return toPosixPath(join5(config.components, "primitives.tsx"));
4508
+ if (isOverlayHelperFile(payloadFile)) return toPosixPath(join6(config.components, "overlay.tsx"));
4509
+ if (isPrimitivesHelperFile(payloadFile)) return toPosixPath(join6(config.components, "primitives.tsx"));
3293
4510
  if (payloadFile.kind === "helper") {
3294
4511
  throw new CliError(`Unsupported registry helper file path: ${payloadFile.path}`);
3295
4512
  }
3296
4513
  const safePayloadPath = sanitizePayloadPath(payloadFile.path, fallbackFileName);
3297
- return toPosixPath(join5(config.components, safePayloadPath));
4514
+ return toPosixPath(join6(config.components, safePayloadPath));
3298
4515
  }
3299
4516
  function isGraphHelperFile(payloadFile) {
3300
4517
  return isUtilsHelperFile(payloadFile) || isOverlayHelperFile(payloadFile) || isPrimitivesHelperFile(payloadFile);
@@ -3391,7 +4608,7 @@ function normalizePackageName(specifier) {
3391
4608
  function withSourceExtension(path, extension) {
3392
4609
  const directory = parentDirectory(path);
3393
4610
  const fileName = extensionlessPath(path.split("/").at(-1) ?? path);
3394
- return toPosixPath(join5(directory === "." ? "" : directory, `${fileName}.${extension}`));
4611
+ return toPosixPath(join6(directory === "." ? "" : directory, `${fileName}.${extension}`));
3395
4612
  }
3396
4613
 
3397
4614
  // src/commands.ts
@@ -3410,6 +4627,12 @@ async function run(context) {
3410
4627
  case "init":
3411
4628
  await initCommand(context, parsed);
3412
4629
  return;
4630
+ case "apply":
4631
+ await applyCommand(context, parsed);
4632
+ return;
4633
+ case "preset":
4634
+ await presetCommand(context, parsed, positionals);
4635
+ return;
3413
4636
  case "login":
3414
4637
  await loginCommand(context, parsed);
3415
4638
  return;
@@ -3437,6 +4660,35 @@ async function run(context) {
3437
4660
  ${HELP_TEXT.trimEnd()}`);
3438
4661
  }
3439
4662
  }
4663
+ async function presetCommand(_context, parsed, positionals) {
4664
+ const [subcommand, positionalCode, ...extra] = positionals;
4665
+ if (subcommand !== "inspect") {
4666
+ throw new CliError(
4667
+ `Unknown preset command: ${subcommand ?? ""}
4668
+
4669
+ Usage: wensity preset inspect <code> [--json]`
4670
+ );
4671
+ }
4672
+ if (extra.length > 0) {
4673
+ throw new CliError("Too many arguments.\n\nUsage: wensity preset inspect <code> [--json]");
4674
+ }
4675
+ const optionCode = getStringOption(parsed, "preset");
4676
+ const presetFile = getStringOption(parsed, "preset-file");
4677
+ if (positionalCode && optionCode) {
4678
+ throw new CliError("Use either a positional preset code or --preset <code>, not both.");
4679
+ }
4680
+ const { style, presetCode } = await resolvePresetStyle({
4681
+ presetCode: optionCode ?? positionalCode,
4682
+ presetFile
4683
+ });
4684
+ const summary = createPresetInspectSummary({ presetCode, style });
4685
+ if (hasFlag(parsed, "json")) {
4686
+ printJson(summary);
4687
+ return;
4688
+ }
4689
+ printTitle("Preset inspection");
4690
+ print(formatPresetInspectSummary(summary));
4691
+ }
3440
4692
  async function initCommand(context, parsed) {
3441
4693
  const dryRun = hasFlag(parsed, "dry-run");
3442
4694
  const force = hasFlag(parsed, "force");
@@ -3444,6 +4696,8 @@ async function initCommand(context, parsed) {
3444
4696
  const apiUrl = getStringOption(parsed, "api-url") ?? context.env.WENSITY_API_URL ?? DEFAULT_API_ORIGIN;
3445
4697
  const presetCode = getStringOption(parsed, "preset");
3446
4698
  const presetFile = getStringOption(parsed, "preset-file");
4699
+ const templateId = parseProjectTemplate(getStringOption(parsed, "template"));
4700
+ const template = templateId ? getProjectTemplateDefinition(templateId) : void 0;
3447
4701
  const hasPreset = Boolean(presetCode || presetFile);
3448
4702
  const dependencies = ["clsx", "tailwind-merge"];
3449
4703
  if (hasPreset) {
@@ -3454,18 +4708,21 @@ async function initCommand(context, parsed) {
3454
4708
  apiUrl,
3455
4709
  presetCode,
3456
4710
  presetFile,
3457
- dependencies
4711
+ dependencies,
4712
+ template
3458
4713
  });
3459
4714
  return;
3460
4715
  }
3461
- const config = createDefaultConfig(context.cwd, apiUrl);
3462
- const configPath = join6(context.cwd, CONFIG_FILE_NAME);
4716
+ const templateFiles = template ? await scaffoldProjectTemplate(context.cwd, template, { dryRun, force }) : [];
4717
+ const config = template ? applyTemplateToConfig(createDefaultConfig(context.cwd, apiUrl), template) : createDefaultConfig(context.cwd, apiUrl);
4718
+ const configPath = join7(context.cwd, CONFIG_FILE_NAME);
3463
4719
  const utilityPath = resolveConfiguredFilePath(context.cwd, config.utils);
3464
4720
  const utilityRelativePath = toPosixPath(config.utils.endsWith(".ts") ? config.utils : `${config.utils}.ts`);
3465
4721
  if (dryRun) {
3466
4722
  printTitle("Project initialization preview");
3467
4723
  print(action("Would write:"));
3468
- print(formatList([CONFIG_FILE_NAME, utilityRelativePath]));
4724
+ print(formatList([...formatTemplateFiles(templateFiles), CONFIG_FILE_NAME, utilityRelativePath]));
4725
+ if (template) print(action(`Template: ${template.label}`));
3469
4726
  if (install) print(action(`Would install: ${dependencies.join(" ")}`));
3470
4727
  return;
3471
4728
  }
@@ -3493,13 +4750,17 @@ async function initWithPreset(context, options) {
3493
4750
  presetCode: options.presetCode,
3494
4751
  presetFile: options.presetFile
3495
4752
  });
3496
- const fonts = curatedFontsFromStyle(style);
3497
- const fontPlan = planFontInstall(fonts.headingFont, fonts.bodyFont);
3498
- const installDependencies = [...options.dependencies, ...fontPlan.packages];
3499
- const configPath = join6(context.cwd, CONFIG_FILE_NAME);
4753
+ const configPath = join7(context.cwd, CONFIG_FILE_NAME);
3500
4754
  const configExists = await fileExists(configPath);
4755
+ const templateFiles = options.template ? await scaffoldProjectTemplate(context.cwd, options.template, {
4756
+ dryRun: options.dryRun,
4757
+ force: options.force
4758
+ }) : [];
3501
4759
  let config;
3502
4760
  if (configExists && !options.force) {
4761
+ if (options.template) {
4762
+ throw new CliError(`${CONFIG_FILE_NAME} already exists. Omit --template for existing projects or use --force.`);
4763
+ }
3503
4764
  const loaded = await loadConfig(context.cwd, { apiUrl: options.apiUrl });
3504
4765
  config = {
3505
4766
  ...loaded.config,
@@ -3507,63 +4768,36 @@ async function initWithPreset(context, options) {
3507
4768
  };
3508
4769
  } else {
3509
4770
  config = {
3510
- ...createDefaultConfig(context.cwd, options.apiUrl),
4771
+ ...options.template ? applyTemplateToConfig(createDefaultConfig(context.cwd, options.apiUrl), options.template) : createDefaultConfig(context.cwd, options.apiUrl),
3511
4772
  style
3512
4773
  };
3513
4774
  }
3514
4775
  const utilityPath = resolveConfiguredFilePath(context.cwd, config.utils);
3515
4776
  const utilityRelativePath = toPosixPath(config.utils.endsWith(".ts") ? config.utils : `${config.utils}.ts`);
3516
- const registry = config.registry;
3517
- let primitiveCss;
3518
- try {
3519
- const payload = await withSpinner(
3520
- `Fetching primitive tokens (${INIT_PRIMITIVE_CSS_SLUG})`,
3521
- () => fetchComponentPayload({ slug: INIT_PRIMITIVE_CSS_SLUG, registry }),
3522
- "Fetched primitive tokens"
3523
- );
3524
- if (typeof payload.primitiveCss !== "string" || !payload.primitiveCss.trim()) {
3525
- throw new CliError(
3526
- `Pinned slug "${INIT_PRIMITIVE_CSS_SLUG}" did not return primitiveCss. Cannot apply preset styles.`
3527
- );
3528
- }
3529
- primitiveCss = applyStyleToPrimitiveCss(payload.primitiveCss, style).trim();
3530
- } catch (error) {
3531
- if (error instanceof CliError) throw error;
3532
- const message = error instanceof Error ? error.message : String(error);
3533
- throw new CliError(`Failed to fetch primitive CSS for preset init: ${message}`);
3534
- }
3535
- const cssRelative = config.tailwind?.css;
3536
- if (!cssRelative) {
3537
- throw new CliError("No Tailwind CSS path configured. Set tailwind.css in wensity.json.");
3538
- }
3539
- const cssAbsolute = resolveInside(context.cwd, cssRelative);
3540
- const existingCss = await fileExists(cssAbsolute) ? await readFile5(cssAbsolute, "utf8") : "";
3541
- const withTokens = upsertPrimitiveCssBlock(existingCss, primitiveCss);
3542
- const nextCss = upsertFontImportBlock(withTokens, fontPlan.block);
3543
- const cssChanged = nextCss !== existingCss;
3544
- const installedPackages = readInstalledPackageNames(context.cwd);
3545
- const unusedFontWarning = installedPackages ? warnUnusedFontPackageMessage(fonts.headingFont, fonts.bodyFont, installedPackages) : void 0;
4777
+ const prepared = await preparePresetStyleApplication(context.cwd, config, style, "preset init");
4778
+ const installDependencies = [...options.dependencies, ...prepared.fontPlan.packages];
3546
4779
  if (options.dryRun) {
3547
4780
  printTitle("Project initialization preview (preset)");
3548
4781
  print(action("Would write:"));
3549
- const planned = [CONFIG_FILE_NAME, utilityRelativePath];
3550
- if (cssChanged) planned.push(toPosixPath(cssRelative));
4782
+ const planned = [...formatTemplateFiles(templateFiles), CONFIG_FILE_NAME, utilityRelativePath];
4783
+ if (prepared.cssChanged) planned.push(toPosixPath(prepared.cssRelative));
3551
4784
  print(formatList(planned));
4785
+ if (options.template) print(action(`Template: ${options.template.label}`));
3552
4786
  print(action(`Preset: ${presetCode.slice(0, 24)}\u2026`));
3553
4787
  print(action(`Style: icon=${style.iconLibrary} radius=${style.radius}`));
3554
- if (fontPlan.packages.length > 0) {
3555
- print(action(`Fonts: ${fontPlan.packages.join(" ")}`));
4788
+ if (prepared.fontPlan.packages.length > 0) {
4789
+ print(action(`Fonts: ${prepared.fontPlan.packages.join(" ")}`));
3556
4790
  }
3557
4791
  if (options.install) print(action(`Would install: ${installDependencies.join(" ")}`));
3558
- if (unusedFontWarning) warn(unusedFontWarning);
4792
+ if (prepared.unusedFontWarning) warn(prepared.unusedFontWarning);
3559
4793
  return;
3560
4794
  }
3561
4795
  const shouldWriteUtility = options.force || !await fileExists(utilityPath);
3562
4796
  await commitPresetInitWrites({
3563
- cssAbsolute,
3564
- existingCss,
3565
- nextCss,
3566
- cssChanged,
4797
+ cssAbsolute: prepared.cssAbsolute,
4798
+ existingCss: prepared.existingCss,
4799
+ nextCss: prepared.nextCss,
4800
+ cssChanged: prepared.cssChanged,
3567
4801
  writeUtility: shouldWriteUtility,
3568
4802
  utilityAbsolutePath: utilityPath,
3569
4803
  utilityContent: cnUtilityTemplate(),
@@ -3580,26 +4814,150 @@ async function initWithPreset(context, options) {
3580
4814
  }
3581
4815
  printTitle("Project initialized");
3582
4816
  const created = [
4817
+ ...options.template ? [success(`template: ${options.template.label}`)] : [],
3583
4818
  success(CONFIG_FILE_NAME),
3584
4819
  success(utilityRelativePath),
3585
4820
  success(`registry: ${options.apiUrl}`),
3586
4821
  success(`preset applied (${style.iconLibrary}, radius=${style.radius})`)
3587
4822
  ];
3588
- if (cssChanged) {
3589
- created.push(success(`CSS tokens: ${toPosixPath(cssRelative)}`));
4823
+ if (prepared.cssChanged) {
4824
+ created.push(success(`CSS tokens: ${toPosixPath(prepared.cssRelative)}`));
3590
4825
  } else {
3591
- created.push(action(`CSS tokens unchanged: ${toPosixPath(cssRelative)}`));
4826
+ created.push(action(`CSS tokens unchanged: ${toPosixPath(prepared.cssRelative)}`));
3592
4827
  }
3593
- if (fontPlan.packages.length > 0) {
3594
- created.push(success(`fonts: ${fontPlan.packages.join(", ")}`));
4828
+ if (prepared.fontPlan.packages.length > 0) {
4829
+ created.push(success(`fonts: ${prepared.fontPlan.packages.join(", ")}`));
3595
4830
  }
3596
4831
  print(panel("Created", created));
3597
- if (unusedFontWarning) warn(unusedFontWarning);
4832
+ if (prepared.unusedFontWarning) warn(prepared.unusedFontWarning);
3598
4833
  print(nextSteps([
3599
4834
  "Browse available components: npx wensity list",
3600
4835
  "Install a primitive: npx wensity add button"
3601
4836
  ]));
3602
4837
  }
4838
+ async function applyCommand(context, parsed) {
4839
+ const dryRun = hasFlag(parsed, "dry-run");
4840
+ const install = !hasFlag(parsed, "no-install");
4841
+ const apiUrl = getStringOption(parsed, "api-url") ?? context.env.WENSITY_API_URL;
4842
+ const registryOverride = getStringOption(parsed, "registry");
4843
+ const presetCodeInput = getStringOption(parsed, "preset");
4844
+ const presetFile = getStringOption(parsed, "preset-file");
4845
+ if (!presetCodeInput && !presetFile) {
4846
+ throw new CliError("Missing preset. Pass --preset <code> or --preset-file <path>.");
4847
+ }
4848
+ const loaded = await loadConfig(context.cwd, { registry: registryOverride, apiUrl });
4849
+ if (!loaded.exists) {
4850
+ throw new CliError(`${CONFIG_FILE_NAME} not found. Run "wensity init" first, then "wensity apply --preset <code>".`);
4851
+ }
4852
+ const { style, presetCode } = await resolvePresetStyle({
4853
+ presetCode: presetCodeInput,
4854
+ presetFile
4855
+ });
4856
+ const prepared = await preparePresetStyleApplication(
4857
+ loaded.projectRoot,
4858
+ {
4859
+ ...loaded.config,
4860
+ style
4861
+ },
4862
+ style,
4863
+ "preset apply"
4864
+ );
4865
+ const installDependencies = prepared.fontPlan.packages;
4866
+ if (dryRun) {
4867
+ printTitle("Preset apply preview");
4868
+ print(action("Would write:"));
4869
+ const planned = [CONFIG_FILE_NAME];
4870
+ if (prepared.cssChanged) planned.push(toPosixPath(prepared.cssRelative));
4871
+ print(formatList(planned));
4872
+ print(action(`Preset: ${presetCode.slice(0, 24)}\u2026`));
4873
+ print(action(`Style: icon=${style.iconLibrary} radius=${style.radius}`));
4874
+ if (prepared.fontPlan.packages.length > 0) {
4875
+ print(action(`Fonts: ${prepared.fontPlan.packages.join(" ")}`));
4876
+ }
4877
+ if (install && installDependencies.length > 0) {
4878
+ print(action(`Would install: ${installDependencies.join(" ")}`));
4879
+ }
4880
+ if (prepared.unusedFontWarning) warn(prepared.unusedFontWarning);
4881
+ return;
4882
+ }
4883
+ await commitPresetInitWrites({
4884
+ cssAbsolute: prepared.cssAbsolute,
4885
+ existingCss: prepared.existingCss,
4886
+ nextCss: prepared.nextCss,
4887
+ cssChanged: prepared.cssChanged,
4888
+ writeUtility: false,
4889
+ utilityAbsolutePath: resolveConfiguredFilePath(loaded.projectRoot, loaded.config.utils),
4890
+ utilityContent: cnUtilityTemplate(),
4891
+ commitConfig: async () => {
4892
+ await mergeStyleIntoConfigFile(loaded.configPath, style);
4893
+ }
4894
+ });
4895
+ if (install && installDependencies.length > 0) {
4896
+ runInstall(loaded.projectRoot, missingDependencies(loaded.projectRoot, installDependencies));
4897
+ }
4898
+ printTitle("Preset applied");
4899
+ const applied = [
4900
+ success(CONFIG_FILE_NAME),
4901
+ success(`preset applied (${style.iconLibrary}, radius=${style.radius})`)
4902
+ ];
4903
+ if (prepared.cssChanged) {
4904
+ applied.push(success(`CSS tokens: ${toPosixPath(prepared.cssRelative)}`));
4905
+ } else {
4906
+ applied.push(action(`CSS tokens unchanged: ${toPosixPath(prepared.cssRelative)}`));
4907
+ }
4908
+ if (prepared.fontPlan.packages.length > 0) {
4909
+ applied.push(success(`fonts: ${prepared.fontPlan.packages.join(", ")}`));
4910
+ }
4911
+ print(panel("Updated", applied));
4912
+ if (prepared.unusedFontWarning) warn(prepared.unusedFontWarning);
4913
+ print(nextSteps([
4914
+ "Review your changed CSS tokens",
4915
+ "Run your app to verify the updated primitive theme"
4916
+ ]));
4917
+ }
4918
+ async function preparePresetStyleApplication(projectRoot, config, style, operation) {
4919
+ const fonts = curatedFontsFromStyle(style);
4920
+ const fontPlan = planFontInstall(fonts.headingFont, fonts.bodyFont);
4921
+ const registry = config.registry;
4922
+ let primitiveCss;
4923
+ try {
4924
+ const payload = await withSpinner(
4925
+ `Fetching primitive tokens (${INIT_PRIMITIVE_CSS_SLUG})`,
4926
+ () => fetchComponentPayload({ slug: INIT_PRIMITIVE_CSS_SLUG, registry }),
4927
+ "Fetched primitive tokens"
4928
+ );
4929
+ if (typeof payload.primitiveCss !== "string" || !payload.primitiveCss.trim()) {
4930
+ throw new CliError(
4931
+ `Pinned slug "${INIT_PRIMITIVE_CSS_SLUG}" did not return primitiveCss. Cannot apply preset styles.`
4932
+ );
4933
+ }
4934
+ primitiveCss = applyStyleToPrimitiveCss(payload.primitiveCss, style).trim();
4935
+ } catch (error) {
4936
+ if (error instanceof CliError) throw error;
4937
+ const message = error instanceof Error ? error.message : String(error);
4938
+ throw new CliError(`Failed to fetch primitive CSS for ${operation}: ${message}`);
4939
+ }
4940
+ const cssRelative = config.tailwind?.css;
4941
+ if (!cssRelative) {
4942
+ throw new CliError("No Tailwind CSS path configured. Set tailwind.css in wensity.json.");
4943
+ }
4944
+ const cssAbsolute = resolveInside(projectRoot, cssRelative);
4945
+ const existingCss = await fileExists(cssAbsolute) ? await readFile6(cssAbsolute, "utf8") : "";
4946
+ const withTokens = upsertPrimitiveCssBlock(existingCss, primitiveCss);
4947
+ const nextCss = upsertFontImportBlock(withTokens, fontPlan.block);
4948
+ const cssChanged = nextCss !== existingCss;
4949
+ const installedPackages = readInstalledPackageNames(projectRoot);
4950
+ const unusedFontWarning = installedPackages ? warnUnusedFontPackageMessage(fonts.headingFont, fonts.bodyFont, installedPackages) : void 0;
4951
+ return {
4952
+ fontPlan,
4953
+ cssRelative,
4954
+ cssAbsolute,
4955
+ existingCss,
4956
+ nextCss,
4957
+ cssChanged,
4958
+ unusedFontWarning
4959
+ };
4960
+ }
3603
4961
  async function loginCommand(context, parsed) {
3604
4962
  const apiUrl = getStringOption(parsed, "api-url") ?? context.env.WENSITY_API_URL ?? DEFAULT_API_ORIGIN;
3605
4963
  const apiBaseUrl = normalizeUrlOrigin(apiUrl);
@@ -3889,8 +5247,8 @@ function formatAccountPanel(whoami, savedPath) {
3889
5247
  return panel(isPro ? "Wensity Pro" : "Wensity Free", lines);
3890
5248
  }
3891
5249
  async function writeTextFile(path, content) {
3892
- await mkdir5(dirname6(path), { recursive: true });
3893
- await writeFile5(path, content, "utf8");
5250
+ await mkdir6(dirname7(path), { recursive: true });
5251
+ await writeFile6(path, content, "utf8");
3894
5252
  }
3895
5253
 
3896
5254
  // src/index.ts