nxds-cli 0.6.1 → 0.8.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 (2) hide show
  1. package/dist/index.js +281 -46
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,5 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // ../claude-design/src/theme-fields.ts
4
+ var isExportThemeFlag = (flag) => flag !== "mode" && flag !== "icons";
5
+ function shellValue(value) {
6
+ if (/^[\w.-]+$/.test(value)) return value;
7
+ return `'${value.replaceAll("'", `'\\''`)}'`;
8
+ }
9
+ function formatThemeFlags(flags) {
10
+ return flags.map(({ flag, value }) => `--${flag} ${shellValue(value)}`).join(" ");
11
+ }
12
+
13
+ // ../core/src/token-source/ui-scale.ts
14
+ var UI_SCALES = {
15
+ small: { spacing: 0.9, text: 12 / 14, icon: 14 / 16 },
16
+ default: { spacing: 1, text: 1, icon: 1 },
17
+ large: { spacing: 1.1, text: 16 / 14, icon: 18 / 16 },
18
+ larger: { spacing: 1.25, text: 18 / 14, icon: 20 / 16 }
19
+ };
20
+ var NON_DEFAULT_UI_SCALES = Object.keys(UI_SCALES).filter((scale) => scale !== "default");
21
+ var TEXT_SIZES = [12, 13, 14, 15, 16];
22
+ var MIN_TEXT_PX = 8;
23
+
3
24
  // ../../node_modules/culori/src/rgb/parseNumber.js
4
25
  var parseNumber = (color, len) => {
5
26
  if (typeof color !== "number") return;
@@ -3838,6 +3859,13 @@ var BASE_TONE_OPTIONS = [
3838
3859
  { value: "slate", label: "Slate", color: "#717376" },
3839
3860
  { value: "gray", label: "Gray", color: "#707274" }
3840
3861
  ];
3862
+ var UI_SCALE_OPTIONS = [
3863
+ { value: "small", label: "Small" },
3864
+ { value: "default", label: "Default" },
3865
+ { value: "large", label: "Large" },
3866
+ { value: "larger", label: "Larger" }
3867
+ ];
3868
+ var TEXT_SIZE_OPTIONS = TEXT_SIZES.map((value) => ({ value, label: `${value}px` }));
3841
3869
  var DENSITY_OPTIONS = [
3842
3870
  { value: "tight", label: "Tight" },
3843
3871
  { value: "compact", label: "Compact" },
@@ -3871,6 +3899,8 @@ var DEFAULT_NXDS_APPEARANCE = {
3871
3899
  surfaceTone: "zinc",
3872
3900
  lightContrast: 50,
3873
3901
  darkContrast: 50,
3902
+ uiScale: "default",
3903
+ textSize: 14,
3874
3904
  density: "default",
3875
3905
  corners: "subtle",
3876
3906
  elevation: "quiet",
@@ -3879,18 +3909,21 @@ var DEFAULT_NXDS_APPEARANCE = {
3879
3909
  uiFont: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
3880
3910
  headingFont: null,
3881
3911
  codeFont: 'ui-monospace, "SF Mono", Menlo, monospace',
3882
- uiFontSize: 14,
3883
3912
  codeFontSize: 12,
3884
3913
  reduceMotion: false,
3885
3914
  pointerCursors: false,
3886
3915
  fontSmoothing: false
3887
3916
  }
3888
3917
  };
3889
- var FONT_SIZE_PX_MIN = 8;
3918
+ var FONT_SIZE_PX_MIN = MIN_TEXT_PX;
3890
3919
  var FONT_SIZE_PX_MAX = 32;
3891
3920
  var SURFACE_TONES = new Set(
3892
3921
  BASE_TONE_OPTIONS.map((option) => option.value)
3893
3922
  );
3923
+ var SCALES = new Set(
3924
+ UI_SCALE_OPTIONS.map((option) => option.value)
3925
+ );
3926
+ var TEXT_SIZE_VALUES = new Set(TEXT_SIZES);
3894
3927
  var DENSITIES = new Set(
3895
3928
  DENSITY_OPTIONS.map((option) => option.value)
3896
3929
  );
@@ -4043,20 +4076,6 @@ function contrastField(flag, key, description) {
4043
4076
  })
4044
4077
  };
4045
4078
  }
4046
- function fontSizeField(flag, key, description) {
4047
- return {
4048
- kind: "integer",
4049
- flag,
4050
- key: `prefs.${key}`,
4051
- description,
4052
- min: FONT_SIZE_PX_MIN,
4053
- max: FONT_SIZE_PX_MAX,
4054
- read: (theme) => String(theme.appearance.prefs[key]),
4055
- write: (theme, value) => withPrefs(theme, {
4056
- [key]: integer(flag, value, FONT_SIZE_PX_MIN, FONT_SIZE_PX_MAX)
4057
- })
4058
- };
4059
- }
4060
4079
  function fontField(flag, key, description, categories, fallback) {
4061
4080
  const fonts = FONT_OPTIONS.filter(
4062
4081
  (font) => categories.includes(font.category)
@@ -4087,6 +4106,9 @@ function fontField(flag, key, description, categories, fallback) {
4087
4106
  }
4088
4107
  };
4089
4108
  }
4109
+ var TEXT_SIZES2 = TEXT_SIZE_OPTIONS.map((option) => option.value);
4110
+ var TEXT_SIZE_MIN = Math.min(...TEXT_SIZES2);
4111
+ var TEXT_SIZE_MAX = Math.max(...TEXT_SIZES2);
4090
4112
  var DEFAULT_PREFS = DEFAULT_NXDS_APPEARANCE.prefs;
4091
4113
  var TEXT_FONTS = ["sans", "serif"];
4092
4114
  var NXDS_THEME_FIELDS = [
@@ -4107,6 +4129,30 @@ var NXDS_THEME_FIELDS = [
4107
4129
  "neutral tone of backgrounds and borders",
4108
4130
  BASE_TONE_OPTIONS.map((option) => option.value)
4109
4131
  ),
4132
+ enumField(
4133
+ "ui-scale",
4134
+ "uiScale",
4135
+ "size of text, icons and spacing together",
4136
+ UI_SCALE_OPTIONS.map((option) => option.value)
4137
+ ),
4138
+ {
4139
+ kind: "integer",
4140
+ flag: "text-size",
4141
+ key: "textSize",
4142
+ description: `label text size in px at the default UI scale, ${TEXT_SIZE_MIN} to ${TEXT_SIZE_MAX}`,
4143
+ min: TEXT_SIZE_MIN,
4144
+ max: TEXT_SIZE_MAX,
4145
+ read: (theme) => String(theme.appearance.textSize),
4146
+ write: (theme, value) => withAppearance(theme, {
4147
+ // Every whole number from the minimum to the maximum is a text size.
4148
+ textSize: integer(
4149
+ "text-size",
4150
+ value,
4151
+ TEXT_SIZE_MIN,
4152
+ TEXT_SIZE_MAX
4153
+ )
4154
+ })
4155
+ },
4110
4156
  enumField(
4111
4157
  "density",
4112
4158
  "density",
@@ -4157,8 +4203,23 @@ var NXDS_THEME_FIELDS = [
4157
4203
  value: "system",
4158
4204
  family: DEFAULT_PREFS.codeFont
4159
4205
  }),
4160
- fontSizeField("font-size", "uiFontSize", "UI text size in px"),
4161
- fontSizeField("code-font-size", "codeFontSize", "code text size in px"),
4206
+ {
4207
+ kind: "integer",
4208
+ flag: "code-font-size",
4209
+ key: "prefs.codeFontSize",
4210
+ description: "code text size in px",
4211
+ min: FONT_SIZE_PX_MIN,
4212
+ max: FONT_SIZE_PX_MAX,
4213
+ read: (theme) => String(theme.appearance.prefs.codeFontSize),
4214
+ write: (theme, value) => withPrefs(theme, {
4215
+ codeFontSize: integer(
4216
+ "code-font-size",
4217
+ value,
4218
+ FONT_SIZE_PX_MIN,
4219
+ FONT_SIZE_PX_MAX
4220
+ )
4221
+ })
4222
+ },
4162
4223
  {
4163
4224
  kind: "choice",
4164
4225
  flag: "icons",
@@ -4543,7 +4604,7 @@ async function fetchRelease(registry, version) {
4543
4604
  if (isNewerSchema(value)) {
4544
4605
  const release = version === "latest" ? "The latest nxds release" : `nxds ${version}`;
4545
4606
  throw new CliError(
4546
- `${release} needs a newer nxds-cli than ${"0.6.1"}. Run \`${CLI_COMMAND}\` to get the newest one.`
4607
+ `${release} needs a newer nxds-cli than ${"0.8.0"}. Run \`${CLI_COMMAND}\` to get the newest one.`
4547
4608
  );
4548
4609
  }
4549
4610
  const invalid2 = (problems) => new CliError(
@@ -6123,14 +6184,18 @@ function report(checks) {
6123
6184
  // ../claude-design/src/system-index.ts
6124
6185
  var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
6125
6186
  var PAGE_PATHS = ["manifest.json", "api/"];
6126
- function mergeSystemIndex(current, exported) {
6127
- return {
6128
- ...current,
6187
+ function mergeSystemIndex(current, exported, { retitle }) {
6188
+ const { theme: _theme, ...kept } = current;
6189
+ const merged = {
6190
+ ...kept,
6129
6191
  namespace: exported.namespace,
6130
6192
  editing: exported.editing,
6131
6193
  libraries: exported.libraries,
6132
6194
  lastChange: exported.lastChange
6133
6195
  };
6196
+ if (retitle) merged["title"] = exported.title;
6197
+ if (exported.theme) merged["theme"] = exported.theme;
6198
+ return merged;
6134
6199
  }
6135
6200
  function isPageGenerated(file) {
6136
6201
  return PAGE_PATHS.some((page) => file === page || file.startsWith(page));
@@ -6179,6 +6244,7 @@ var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
6179
6244
  var DEFAULT_EXPORT_DIR = "nxds-design-system";
6180
6245
  var FETCH_TIMEOUT_MS2 = 6e4;
6181
6246
  var dispatcher2 = new EnvHttpProxyAgent2();
6247
+ var THEME_FILE = "theme.js";
6182
6248
  function redactUrl(url) {
6183
6249
  const redacted = httpUrl(url);
6184
6250
  if (!redacted) return "<invalid URL>";
@@ -6313,9 +6379,8 @@ function stalePaths(existing, exported) {
6313
6379
  var EXPORT_NAMES = /* @__PURE__ */ new Set(["project", "files.json", "removed.json"]);
6314
6380
  var isRootName = (name) => name !== "" && name !== "." && name !== ".." && path10.basename(name) === name && !EXPORT_NAMES.has(name);
6315
6381
  var sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
6316
- var isEntryList = (value) => Array.isArray(value) && value.every(
6317
- (entry) => typeof entry?.path === "string" && typeof entry.sha256 === "string"
6318
- );
6382
+ var isEntry = (value) => typeof value?.path === "string" && typeof value.sha256 === "string";
6383
+ var isEntryList = (value) => Array.isArray(value) && value.every(isEntry);
6319
6384
  function readManifest(root) {
6320
6385
  const file = path10.join(root, "files.json");
6321
6386
  if (!existsSync9(file)) {
@@ -6324,12 +6389,12 @@ function readManifest(root) {
6324
6389
  );
6325
6390
  }
6326
6391
  const value = readJson(file);
6327
- if (!isEntryList(value?.project) || !isEntryList(value.root)) {
6392
+ if (!isEntryList(value?.project) || !isEntryList(value.root) || !(value.theme === void 0 || isEntry(value.theme) && value.theme.path === THEME_FILE)) {
6328
6393
  throw new CliError(
6329
- `${file} must hold project and root, each a JSON array of { path, sha256 } entries.`
6394
+ `${file} must hold project and root, each a JSON array of { path, sha256 } entries, and may hold theme, the { path, sha256 } entry for ${THEME_FILE}.`
6330
6395
  );
6331
6396
  }
6332
- return { project: value.project, root: value.root };
6397
+ return { project: value.project, root: value.root, theme: value.theme };
6333
6398
  }
6334
6399
  function readEntries(dir, entries, prefix) {
6335
6400
  const files = /* @__PURE__ */ new Map();
@@ -6373,7 +6438,8 @@ function readRelease(root) {
6373
6438
  }
6374
6439
  return {
6375
6440
  files: readEntries(projectDir, manifest.project, "project/"),
6376
- rootFiles: readEntries(root, manifest.root, "")
6441
+ rootFiles: readEntries(root, manifest.root, ""),
6442
+ themeModule: manifest.theme ? readEntries(root, [manifest.theme], "").get(THEME_FILE) : void 0
6377
6443
  };
6378
6444
  }
6379
6445
  function droppedRootFiles(out, rootFiles) {
@@ -6417,13 +6483,132 @@ function writeManifest(out, files, rootFiles) {
6417
6483
  `
6418
6484
  );
6419
6485
  }
6420
- function mergeIndex(files, currentFile) {
6486
+ function readIndex(file) {
6487
+ const index = readJson(file);
6488
+ if (typeof index !== "object" || index === null || Array.isArray(index)) {
6489
+ throw new UsageError(`${file} must be a system's design-system.json.`);
6490
+ }
6491
+ return index;
6492
+ }
6493
+ function storedTheme(index, file) {
6494
+ const theme = index["theme"];
6495
+ if (theme === void 0) return {};
6496
+ if (typeof theme !== "object" || theme === null || Array.isArray(theme) || !Object.values(theme).every((value) => typeof value === "string")) {
6497
+ throw new UsageError(
6498
+ `${file}: "theme" must be an object of theme flag values, such as {"corners": "round"}. Fix it, or remove it to export the default theme.`
6499
+ );
6500
+ }
6501
+ return theme;
6502
+ }
6503
+ function layeredTheme(stored, passed) {
6504
+ const flags = { ...stored, ...passed };
6505
+ for (const [flag, value] of Object.entries(passed)) {
6506
+ if (value === "") delete flags[flag];
6507
+ }
6508
+ return flags;
6509
+ }
6510
+ var noFlagError = (flag, version) => new UsageError(
6511
+ `nxds ${version} has no --${flag}. Pass --release latest to export a release that has it.`
6512
+ );
6513
+ function noThemeModuleError(theme, { passed, indexFile, version }) {
6514
+ const flag = Object.keys(theme).find((name) => name in passed);
6515
+ if (flag !== void 0) return noFlagError(flag, version);
6516
+ return new UsageError(
6517
+ `${indexFile} has a theme, and nxds ${version} can't theme an export. Pass --release latest to keep the theme.`
6518
+ );
6519
+ }
6520
+ function assertThemeFlags(flags, taken, { passed, indexFile, version }) {
6521
+ for (const flag of Object.keys(flags)) {
6522
+ if (taken.includes(flag)) continue;
6523
+ if (flag in passed) throw noFlagError(flag, version);
6524
+ if (flag === "mode" || flag === "icons") {
6525
+ throw new UsageError(
6526
+ `${indexFile} theme: a system carries Light and Dark and one icon library, so it has no "${flag}". Remove it from "theme".`
6527
+ );
6528
+ }
6529
+ throw new UsageError(
6530
+ `${indexFile} theme: "${flag}" isn't a theme flag nxds ${version} takes. Remove it from "theme".`
6531
+ );
6532
+ }
6533
+ }
6534
+ var isThemeModule = (value) => typeof value === "object" && value !== null && value.api === 1 && Array.isArray(value.flags) && typeof value.render === "function" && typeof value.title === "function";
6535
+ async function importTheme(bytes, version) {
6536
+ let imported;
6537
+ try {
6538
+ imported = await import(`data:text/javascript;base64,${bytes.toString("base64")}`);
6539
+ } catch (error) {
6540
+ throw new CliError(
6541
+ `nxds ${version}'s theme.js failed to load (${error.message}). Report it at https://github.com/nexuslabs-ai/nxds/issues.`
6542
+ );
6543
+ }
6544
+ if (!isThemeModule(imported.default)) {
6545
+ throw new CliError(
6546
+ `nxds ${version}'s theme.js is newer than this nxds-cli. Run \`npx nxds-cli@latest\` to get the newest one.`
6547
+ );
6548
+ }
6549
+ return imported.default;
6550
+ }
6551
+ var isRendered = (value) => {
6552
+ const rendered = value;
6553
+ return typeof rendered?.guide === "function" && typeof rendered.index?.title === "string" && typeof rendered.index.theme === "object" && ["tokens.json", "tokens.css", "README.md"].every(
6554
+ (file) => typeof rendered.project?.[file] === "string"
6555
+ );
6556
+ };
6557
+ function themeError(error, { passed, indexFile, version }) {
6558
+ const { message, flag } = error;
6559
+ if (typeof flag === "string" && flag in passed) {
6560
+ return new UsageError(message);
6561
+ }
6562
+ if (typeof flag === "string" && indexFile !== void 0) {
6563
+ return new UsageError(
6564
+ `${indexFile} theme: ${message} Pass --${flag} <value> to change it, or --${flag}= to reset it.`
6565
+ );
6566
+ }
6567
+ return new CliError(`nxds ${version}'s theme.js failed: ${message}`);
6568
+ }
6569
+ function renderTheme(theme, flags, source) {
6570
+ const { version } = source;
6571
+ let rendered;
6572
+ try {
6573
+ rendered = theme.render(flags);
6574
+ } catch (error) {
6575
+ throw themeError(error, source);
6576
+ }
6577
+ if (!isRendered(rendered)) {
6578
+ throw new CliError(
6579
+ `nxds ${version}'s theme.js rendered no token files, README or setup guide.`
6580
+ );
6581
+ }
6582
+ return rendered;
6583
+ }
6584
+ function storedThemeTitle(stored, module, defaultTitle) {
6585
+ if (Object.keys(stored).length === 0) return defaultTitle;
6586
+ try {
6587
+ return module?.title(stored);
6588
+ } catch {
6589
+ return void 0;
6590
+ }
6591
+ }
6592
+ function exportedIndex(files) {
6421
6593
  const exported = files.get("design-system.json");
6422
6594
  if (!exported) throw new CliError("The release has no design-system.json.");
6423
- const merged = mergeSystemIndex(
6424
- readJson(currentFile),
6425
- JSON.parse(exported.toString("utf8"))
6595
+ return JSON.parse(exported.toString("utf8"));
6596
+ }
6597
+ var jsonBytes = (value) => Buffer.from(`${JSON.stringify(value, null, 2)}
6598
+ `);
6599
+ function applyTheme(rendered, files, rootFiles) {
6600
+ const { project } = rendered;
6601
+ files.set("tokens.json", Buffer.from(project["tokens.json"]));
6602
+ files.set("tokens.css", Buffer.from(project["tokens.css"]));
6603
+ files.set("README.md", Buffer.from(project["README.md"]));
6604
+ files.set(
6605
+ "design-system.json",
6606
+ jsonBytes({ ...exportedIndex(files), ...rendered.index })
6426
6607
  );
6608
+ rootFiles.set("SETUP.md", Buffer.from(rendered.guide(files.size)));
6609
+ }
6610
+ function mergeIndex(files, current, currentFile, retitle) {
6611
+ const merged = mergeSystemIndex(current, exportedIndex(files), { retitle });
6427
6612
  const errors = validateIndex(merged);
6428
6613
  if (errors.length > 0) {
6429
6614
  throw new UsageError(
@@ -6431,11 +6616,7 @@ function mergeIndex(files, currentFile) {
6431
6616
  ${errors.map((error) => ` ${error}`).join("\n")}`
6432
6617
  );
6433
6618
  }
6434
- files.set(
6435
- "design-system.json",
6436
- Buffer.from(`${JSON.stringify(merged, null, 2)}
6437
- `)
6438
- );
6619
+ files.set("design-system.json", jsonBytes(merged));
6439
6620
  }
6440
6621
  function writeProject(out, files) {
6441
6622
  mkdirSync2(out, { recursive: true });
@@ -6492,8 +6673,31 @@ async function exportClaudeDesign(cwd, options) {
6492
6673
  options.release ?? "latest"
6493
6674
  );
6494
6675
  try {
6495
- const { files, rootFiles } = readRelease(pkg.root);
6496
- if (options.index) mergeIndex(files, path10.resolve(cwd, options.index));
6676
+ const { files, rootFiles, themeModule } = readRelease(pkg.root);
6677
+ const indexFile = options.index && path10.resolve(cwd, options.index);
6678
+ const current = indexFile ? readIndex(indexFile) : void 0;
6679
+ const passed = options.theme;
6680
+ const stored = current && indexFile ? storedTheme(current, indexFile) : {};
6681
+ const theme = layeredTheme(stored, passed);
6682
+ const source = {
6683
+ passed,
6684
+ indexFile: options.index,
6685
+ version: pkg.version
6686
+ };
6687
+ const defaultTitle = exportedIndex(files).title;
6688
+ const module = themeModule && (Object.keys(theme).length > 0 || Object.keys(stored).length > 0) ? await importTheme(themeModule, pkg.version) : void 0;
6689
+ let themed = {};
6690
+ if (Object.keys(theme).length > 0) {
6691
+ if (!module) throw noThemeModuleError(theme, source);
6692
+ assertThemeFlags(theme, module.flags, source);
6693
+ const rendered = renderTheme(module, theme, source);
6694
+ applyTheme(rendered, files, rootFiles);
6695
+ themed = rendered.index.theme;
6696
+ }
6697
+ if (current && indexFile) {
6698
+ const retitle = current["title"] === storedThemeTitle(stored, module, defaultTitle);
6699
+ mergeIndex(files, current, indexFile, retitle);
6700
+ }
6497
6701
  const exported = new Set(
6498
6702
  [...files.keys()].map((file) => `project/${file}`)
6499
6703
  );
@@ -6514,6 +6718,11 @@ async function exportClaudeDesign(cwd, options) {
6514
6718
  const lines2 = [
6515
6719
  `Exported nxds ${pkg.version} as a Claude Design System: ${files.size} files in ${path10.join(relative, "project")}.`
6516
6720
  ];
6721
+ if (Object.keys(themed).length > 0) {
6722
+ lines2.push(
6723
+ `Themed with ${formatThemeFlags(Object.entries(themed).map(([flag, value]) => ({ flag, value })))}.`
6724
+ );
6725
+ }
6517
6726
  if (rootFiles.has("coverage.md")) {
6518
6727
  lines2.push(
6519
6728
  `The props the canvas can't set are in ${path10.join(relative, "coverage.md")}.`
@@ -6537,6 +6746,7 @@ async function exportClaudeDesign(cwd, options) {
6537
6746
  lines: lines2,
6538
6747
  data: {
6539
6748
  version: pkg.version,
6749
+ theme: themed,
6540
6750
  out,
6541
6751
  files: files.size,
6542
6752
  removed,
@@ -6976,7 +7186,7 @@ function program(done, write) {
6976
7186
  // Errors are printed once, by `main`, as text or JSON.
6977
7187
  outputError: () => {
6978
7188
  }
6979
- }).exitOverride().name(CLI_COMMAND).description("Add nxds to your app.").version("0.6.1", "-v, --version", "print the version").helpOption("-h, --help", "show help").helpCommand("help [command]", "show help for a command").option("--json", "print machine-readable JSON").option("--cwd <path>", "run in another folder").addOption(
7189
+ }).exitOverride().name(CLI_COMMAND).description("Add nxds to your app.").version("0.8.0", "-v, --version", "print the version").helpOption("-h, --help", "show help").helpCommand("help [command]", "show help for a command").option("--json", "print machine-readable JSON").option("--cwd <path>", "run in another folder").addOption(
6980
7190
  new Option(
6981
7191
  "--registry <url|path>",
6982
7192
  "where to get nxds releases (default: https://nxds.dev/r)"
@@ -7058,13 +7268,13 @@ Docs: https://nxds.dev/getting-started/install`
7058
7268
  const { cwd, registry } = context(command);
7059
7269
  done(await diff(cwd, registry, items));
7060
7270
  });
7061
- cli.command("export").description(
7062
- "export nxds as a Claude Design System folder, ready to publish"
7271
+ const exportCommand = cli.command("export").description(
7272
+ "export nxds as a Claude Design System folder, ready to publish. Theme flags set the system's theme, as they do for init; with --index, one left out keeps the system's value, and an empty one, such as --density=, resets it"
7063
7273
  ).addArgument(
7064
7274
  new Argument("<target>", "what to export").choices(["claude-design"])
7065
7275
  ).option("--out <path>", `folder to write (default: ${DEFAULT_EXPORT_DIR})`).option("--release <version>", "nxds release to export (default: latest)").option(
7066
7276
  "--index <path>",
7067
- "the system's current design-system.json, to keep its title and creation date"
7277
+ "the system's current design-system.json, to keep its title, creation date and theme"
7068
7278
  ).option(
7069
7279
  "--existing <path>",
7070
7280
  "a JSON list of the system's current file paths, to list the ones this release removes"
@@ -7076,9 +7286,34 @@ Docs: https://nxds.dev/getting-started/install`
7076
7286
  "--npm-registry <url>",
7077
7287
  `npm registry to download from (default: ${DEFAULT_NPM_REGISTRY})`
7078
7288
  ).env("npm_config_registry")
7079
- ).action(async (_target, _options, command) => {
7289
+ );
7290
+ const exportThemeOptions = NXDS_THEME_FIELDS.filter(
7291
+ (field) => isExportThemeFlag(field.flag)
7292
+ ).map((field) => {
7293
+ const option = new Option(`--${field.flag} <value>`, field.description);
7294
+ exportCommand.addOption(option);
7295
+ return { flag: field.flag, key: option.attributeName() };
7296
+ });
7297
+ exportCommand.action(async (_target, _options, command) => {
7298
+ const options = command.opts();
7299
+ const theme = Object.fromEntries(
7300
+ exportThemeOptions.flatMap(({ flag, key }) => {
7301
+ const value = options[key];
7302
+ return value === void 0 ? [] : [[flag, value]];
7303
+ })
7304
+ );
7305
+ const { out, release, source, npmRegistry, index, existing, overwrite } = command.opts();
7080
7306
  done(
7081
- await exportClaudeDesign(cwdOf(command), command.opts())
7307
+ await exportClaudeDesign(cwdOf(command), {
7308
+ out,
7309
+ release,
7310
+ source,
7311
+ npmRegistry,
7312
+ index,
7313
+ existing,
7314
+ overwrite,
7315
+ theme
7316
+ })
7082
7317
  );
7083
7318
  });
7084
7319
  cli.command("doctor").description("check that nxds is wired up correctly").action(async (_options, command) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nxds-cli",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "description": "Install nxds components, blocks and setup into your app.",
6
6
  "keywords": [
@@ -45,9 +45,9 @@
45
45
  "@types/semver": "^7.8.0",
46
46
  "tsup": "^8.5.1",
47
47
  "typescript": "^5.9.3",
48
- "@nxds/catalog": "0.0.1",
49
- "@nxds/claude-design": "0.13.2",
50
- "@nxds/core": "0.12.0"
48
+ "@nxds/catalog": "0.0.3",
49
+ "@nxds/claude-design": "0.17.0",
50
+ "@nxds/core": "0.14.0"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "tsup",