@nghitrum/dsforge 0.1.5-alpha.4 → 0.1.5-alpha.6

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.
@@ -0,0 +1,28 @@
1
+ // src/lib/license.ts
2
+ import { readFileSync } from "fs";
3
+ import { join } from "path";
4
+ function readKeyFromDotEnv() {
5
+ try {
6
+ const content = readFileSync(join(process.cwd(), ".env"), "utf8");
7
+ for (const raw of content.split("\n")) {
8
+ const line = raw.trim();
9
+ if (!line || line.startsWith("#")) continue;
10
+ const eq = line.indexOf("=");
11
+ if (eq === -1) continue;
12
+ const key = line.slice(0, eq).trim();
13
+ if (key !== "DSFORGE_KEY") continue;
14
+ const val = line.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
15
+ return val || void 0;
16
+ }
17
+ } catch {
18
+ }
19
+ return void 0;
20
+ }
21
+ function isProUnlocked() {
22
+ const key = process.env["DSFORGE_KEY"] ?? readKeyFromDotEnv();
23
+ return typeof key === "string" && key.length > 0;
24
+ }
25
+
26
+ export {
27
+ isProUnlocked
28
+ };
@@ -27,7 +27,7 @@ function generatePackageJson(config, componentNames) {
27
27
  componentNames.map((c) => [`./metadata/${c}`, `./metadata/${c}.json`])
28
28
  )
29
29
  },
30
- files: ["dist", "tokens", "metadata", "docs", "CHANGELOG.md"],
30
+ files: ["dist", "tokens", "metadata", "CHANGELOG.md"],
31
31
  scripts: {
32
32
  build: "tsc",
33
33
  prepublishOnly: "npm run build"
package/dist/cli/index.js CHANGED
@@ -3,7 +3,10 @@ import {
3
3
  generateChangelog,
4
4
  generatePackageJson,
5
5
  generateTsConfig
6
- } from "../chunk-RI3XDGKU.js";
6
+ } from "../chunk-QHE35QQQ.js";
7
+ import {
8
+ isProUnlocked
9
+ } from "../chunk-OAMEFG6Q.js";
7
10
 
8
11
  // src/cli/index.ts
9
12
  import { program } from "commander";
@@ -436,6 +439,32 @@ var RADIUS_PRESETS = {
436
439
  comfortable: { none: 0, sm: 2, md: 4, lg: 8, xl: 16, full: 9999 },
437
440
  spacious: { none: 0, sm: 3, md: 6, lg: 12, xl: 20, full: 9999 }
438
441
  };
442
+ function applyPreset(config, preset) {
443
+ const spacing = SPACING_PRESETS[preset];
444
+ const radius = RADIUS_PRESETS[preset];
445
+ const baseUnit = preset === "compact" ? 2 : preset === "spacious" ? 6 : 4;
446
+ config.spacing = {
447
+ ...config.spacing,
448
+ baseUnit,
449
+ scale: spacing,
450
+ semantic: {
451
+ "component-padding-xs": `${spacing["1"]}`,
452
+ "component-padding-sm": `${spacing["2"]}`,
453
+ "component-padding-md": `${spacing["4"]}`,
454
+ "component-padding-lg": `${spacing["5"]}`,
455
+ "layout-gap-xs": `${spacing["2"]}`,
456
+ "layout-gap-sm": `${spacing["3"]}`,
457
+ "layout-gap-md": `${spacing["5"]}`,
458
+ "layout-gap-lg": `${spacing["6"]}`,
459
+ "layout-section": `${spacing["7"]}`
460
+ }
461
+ };
462
+ config.radius = { ...config.radius, ...radius };
463
+ config.philosophy = {
464
+ ...config.philosophy,
465
+ density: preset
466
+ };
467
+ }
439
468
  function buildInitialConfig(name, preset = "comfortable") {
440
469
  const spacing = SPACING_PRESETS[preset];
441
470
  const radius = RADIUS_PRESETS[preset];
@@ -775,7 +804,15 @@ async function runInit(cwd, options) {
775
804
  }
776
805
  const name = rawName.replace(/\s+/g, "-").toLowerCase();
777
806
  let preset;
778
- if (options.preset && VALID_PRESETS.includes(options.preset)) {
807
+ if (!isProUnlocked()) {
808
+ if (options.preset && options.preset !== "comfortable") {
809
+ logger.hint(
810
+ `Preset "${options.preset}" requires dsforge Pro`,
811
+ `Set DSFORGE_KEY to unlock compact and spacious. Using comfortable.`
812
+ );
813
+ }
814
+ preset = "comfortable";
815
+ } else if (options.preset && VALID_PRESETS.includes(options.preset)) {
779
816
  preset = options.preset;
780
817
  } else {
781
818
  const answer = await ask(
@@ -4761,6 +4798,13 @@ async function runGenerate(cwd, options) {
4761
4798
  options.debug ?? false
4762
4799
  );
4763
4800
  }
4801
+ const presetValue = config.meta.preset;
4802
+ if (presetValue === "compact" || presetValue === "comfortable" || presetValue === "spacious") {
4803
+ applyPreset(config, presetValue);
4804
+ }
4805
+ const fullRules = Object.fromEntries(
4806
+ REACT_COMPONENTS.map((name) => [name, rules[name] ?? {}])
4807
+ );
4764
4808
  logger.step("Running pre-flight validation...");
4765
4809
  const validation = validateConfig(config, rules);
4766
4810
  const errors = validation.issues.filter((i) => i.severity === "error");
@@ -4852,48 +4896,27 @@ async function runGenerate(cwd, options) {
4852
4896
  logger.dim(` \u2192 src/${idxFile}`);
4853
4897
  logger.success(`${generatedNames.length} components generated`);
4854
4898
  }
4855
- if (!only || only === "metadata") {
4899
+ if (isProUnlocked() && (!only || only === "metadata")) {
4856
4900
  logger.step("Writing AI metadata...");
4857
4901
  const metaDir = path3.join(outRoot, "metadata");
4858
4902
  await ensureDir(metaDir);
4859
- const metaFiles = generateMetadata(config, rules, tokenCount);
4903
+ const metaFiles = generateMetadata(config, fullRules, tokenCount);
4860
4904
  for (const { filename, content } of metaFiles) {
4861
4905
  await writeFile(path3.join(metaDir, filename), content);
4862
4906
  logger.dim(` \u2192 metadata/${filename}`);
4863
4907
  }
4864
4908
  logger.success(`Metadata written (${metaFiles.length} files)`);
4865
4909
  }
4866
- if (!only || only === "docs") {
4867
- logger.step("Generating docs...");
4868
- const docsDir = path3.join(outRoot, "docs");
4869
- await ensureDir(docsDir);
4870
- const metadataFiles = generateMetadata(config, rules, tokenCount);
4871
- const metadataMap = {};
4872
- for (const { filename, content } of metadataFiles) {
4873
- const name = filename.replace(".json", "");
4874
- if (name !== "index") {
4875
- metadataMap[name] = JSON.parse(
4876
- content
4877
- );
4878
- }
4879
- }
4880
- const docFiles = reactAdapter.generateDocs(config, rules, metadataMap);
4881
- for (const { filename, content } of docFiles) {
4882
- await writeFile(path3.join(docsDir, filename), content);
4883
- logger.dim(` \u2192 docs/${filename}`);
4884
- }
4885
- logger.success(`Docs written (${docFiles.length} files)`);
4886
- }
4887
4910
  if (!only) {
4888
4911
  logger.step("Writing package files...");
4889
- const componentNames = Object.keys(rules);
4912
+ const componentNames = Object.keys(fullRules);
4890
4913
  const { filename: pkgFile, content: pkgContent } = reactAdapter.generatePackageManifest(config, componentNames);
4891
4914
  await writeFile(path3.join(outRoot, pkgFile), pkgContent);
4892
4915
  logger.dim(` \u2192 ${pkgFile}`);
4893
4916
  const tsConfig = generateTsConfig();
4894
4917
  await writeFile(path3.join(outRoot, "tsconfig.json"), tsConfig);
4895
4918
  logger.dim(` \u2192 tsconfig.json`);
4896
- const { generateReadme } = await import("../emitter-ZNRPJ4D6.js");
4919
+ const { generateReadme } = await import("../emitter-KNYIQTS5.js");
4897
4920
  await writeFile(
4898
4921
  path3.join(outRoot, "README.md"),
4899
4922
  generateReadme(config, componentNames)
@@ -4909,7 +4932,7 @@ async function runGenerate(cwd, options) {
4909
4932
  logger.success(`Package files written`);
4910
4933
  }
4911
4934
  logger.step("Generating showcase...");
4912
- const { generateShowcase } = await import("../html-6SIG34W5.js");
4935
+ const { generateShowcase } = await import("../html-BJBKRTSX.js");
4913
4936
  const showcaseHtml = generateShowcase(config, resolution);
4914
4937
  await writeFile(path3.join(outRoot, "showcase.html"), showcaseHtml);
4915
4938
  logger.dim(` \u2192 showcase.html`);
@@ -5296,7 +5319,7 @@ async function runMenu() {
5296
5319
  // package.json
5297
5320
  var package_default = {
5298
5321
  name: "@nghitrum/dsforge",
5299
- version: "0.1.5-alpha.4",
5322
+ version: "0.1.5-alpha.6",
5300
5323
  description: "AI-native design system generator \u2014 tokens \u2192 components \u2192 docs \u2192 npm",
5301
5324
  keywords: [
5302
5325
  "design-system",
@@ -3,7 +3,7 @@ import {
3
3
  generatePackageJson,
4
4
  generateReadme,
5
5
  generateTsConfig
6
- } from "./chunk-RI3XDGKU.js";
6
+ } from "./chunk-QHE35QQQ.js";
7
7
  export {
8
8
  generateChangelog,
9
9
  generatePackageJson,
@@ -1,3 +1,7 @@
1
+ import {
2
+ isProUnlocked
3
+ } from "./chunk-OAMEFG6Q.js";
4
+
1
5
  // src/generators/showcase/types.ts
2
6
  function esc(s) {
3
7
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -32,31 +36,6 @@ function componentTokens(config, tokens) {
32
36
  };
33
37
  }
34
38
 
35
- // src/lib/license.ts
36
- import { readFileSync } from "fs";
37
- import { join } from "path";
38
- function readKeyFromDotEnv() {
39
- try {
40
- const content = readFileSync(join(process.cwd(), ".env"), "utf8");
41
- for (const raw of content.split("\n")) {
42
- const line = raw.trim();
43
- if (!line || line.startsWith("#")) continue;
44
- const eq = line.indexOf("=");
45
- if (eq === -1) continue;
46
- const key = line.slice(0, eq).trim();
47
- if (key !== "DSFORGE_KEY") continue;
48
- const val = line.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
49
- return val || void 0;
50
- }
51
- } catch {
52
- }
53
- return void 0;
54
- }
55
- function isProUnlocked() {
56
- const key = process.env["DSFORGE_KEY"] ?? readKeyFromDotEnv();
57
- return typeof key === "string" && key.length > 0;
58
- }
59
-
60
39
  // src/generators/showcase/foundations.ts
61
40
  function buildColorSection(config, tokens) {
62
41
  const groups = [];
@@ -231,7 +210,9 @@ var lockedPanel = (label) => `
231
210
  function buildComponentPage(def, isPro) {
232
211
  const tabId = (tab) => `${def.id}-tab-${tab}`;
233
212
  const panelId = (tab) => `${def.id}-panel-${tab}`;
234
- const overviewHtml = `<div class="comp-overview">${def.overviewHtml}</div>`;
213
+ const overviewHtml = `
214
+ <div class="comp-overview">${def.overviewHtml}</div>
215
+ <p class="component-description">${esc(def.description)}</p>`;
235
216
  const propsTable = `
236
217
  <table class="props-table">
237
218
  <thead>
@@ -366,6 +347,9 @@ function buttonDef(config, tokens) {
366
347
  id: "button",
367
348
  label: "Button",
368
349
  description: "Triggers an action or event. Use for form submissions, dialogs, and in-page actions.",
350
+ usageExample: `<Button variant="primary" size="md" onClick={() => {}}>
351
+ Save changes
352
+ </Button>`,
369
353
  overviewHtml: `
370
354
  <div class="comp-overview-section">
371
355
  <div class="comp-overview-label">Variants</div>
@@ -552,6 +536,13 @@ function inputDef(config, tokens) {
552
536
  id: "input",
553
537
  label: "Input",
554
538
  description: "Single-line text field. Covers all standard input types with label, helper text, and validation states.",
539
+ usageExample: `<Input
540
+ label="Email"
541
+ placeholder="you@example.com"
542
+ value={email}
543
+ onChange={(e) => setEmail(e.target.value)}
544
+ error={emailError}
545
+ />`,
555
546
  overviewHtml: `
556
547
  <div class="comp-overview-section">
557
548
  <div class="comp-overview-label">States</div>
@@ -757,6 +748,10 @@ function cardDef(config, tokens) {
757
748
  id: "card",
758
749
  label: "Card",
759
750
  description: "A surface that groups related content. Supports header, body, and optional footer slots.",
751
+ usageExample: `<Card padding="lg">
752
+ <h2>Card title</h2>
753
+ <p>Card content goes here.</p>
754
+ </Card>`,
760
755
  overviewHtml: `
761
756
  <div class="comp-overview-section">
762
757
  <div class="comp-overview-label">Variants</div>
@@ -913,6 +908,8 @@ function badgeDef(config, tokens) {
913
908
  id: "badge",
914
909
  label: "Badge",
915
910
  description: "Compact label for status, categories, or counts. Display-only \u2014 not interactive.",
911
+ usageExample: `<Badge variant="success">Active</Badge>
912
+ <Badge variant="warning">Pending</Badge>`,
916
913
  overviewHtml: `
917
914
  <div class="comp-overview-section">
918
915
  <div class="comp-overview-label">Variants</div>
@@ -1057,6 +1054,11 @@ function checkboxDef(config, tokens) {
1057
1054
  id: "checkbox",
1058
1055
  label: "Checkbox",
1059
1056
  description: "Binary toggle for boolean values. Supports indeterminate state for partial selections.",
1057
+ usageExample: `<Checkbox
1058
+ label="Accept terms"
1059
+ checked={accepted}
1060
+ onChange={(e) => setAccepted(e.target.checked)}
1061
+ />`,
1060
1062
  overviewHtml: `
1061
1063
  <div class="comp-overview-section">
1062
1064
  <div class="comp-overview-label">States</div>
@@ -1239,6 +1241,8 @@ function radioDef(config, tokens) {
1239
1241
  id: "radio",
1240
1242
  label: "Radio",
1241
1243
  description: "Single selection within a mutually exclusive group. Always pair Radio with RadioGroup.",
1244
+ usageExample: `<Radio label="Option A" name="choice" value="a" checked={choice === 'a'} onChange={() => setChoice('a')} />
1245
+ <Radio label="Option B" name="choice" value="b" checked={choice === 'b'} onChange={() => setChoice('b')} />`,
1242
1246
  overviewHtml: `
1243
1247
  <div class="comp-overview-section">
1244
1248
  <div class="comp-overview-label">RadioGroup (vertical)</div>
@@ -1425,6 +1429,12 @@ function selectDef(config, tokens) {
1425
1429
  id: "select",
1426
1430
  label: "Select",
1427
1431
  description: "Dropdown picker for selecting from a list of options. Wraps native <select> for full accessibility.",
1432
+ usageExample: `<Select
1433
+ label="Country"
1434
+ options={[{ label: 'Norway', value: 'no' }, { label: 'Sweden', value: 'se' }]}
1435
+ value={country}
1436
+ onChange={(e) => setCountry(e.target.value)}
1437
+ />`,
1428
1438
  overviewHtml: `
1429
1439
  <div class="comp-overview-section">
1430
1440
  <div class="comp-overview-label">States</div>
@@ -1610,6 +1620,12 @@ function toastDef(config, tokens) {
1610
1620
  id: "toast",
1611
1621
  label: "Toast / Alert",
1612
1622
  description: "Feedback for user actions. Alert is inline and static; Toast is an overlay with auto-dismiss and a useToast() hook.",
1623
+ usageExample: `<Toast
1624
+ message="Changes saved successfully"
1625
+ variant="success"
1626
+ duration={3000}
1627
+ onDismiss={() => setToast(null)}
1628
+ />`,
1613
1629
  overviewHtml: `
1614
1630
  <div class="comp-overview-section">
1615
1631
  <div class="comp-overview-label">Alert \u2014 inline variants</div>
@@ -1791,6 +1807,7 @@ function spinnerDef(config, tokens) {
1791
1807
  id: "spinner",
1792
1808
  label: "Spinner",
1793
1809
  description: "Loading indicator for async operations. Includes a visually hidden status label for screen readers.",
1810
+ usageExample: `<Spinner size="lg" label="Saving your changes" />`,
1794
1811
  overviewHtml: `
1795
1812
  <div class="comp-overview-section">
1796
1813
  <div class="comp-overview-label">Sizes</div>
@@ -2301,6 +2318,27 @@ ${themeCssDark}
2301
2318
  .ds-card-body { padding: 12px 14px; }
2302
2319
  .ds-card-footer { padding: 10px 14px; display: flex; justify-content: flex-end; }
2303
2320
 
2321
+ /* \u2500\u2500 Component docs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2322
+ .component-docs { margin-top: 36px; }
2323
+ .component-description {
2324
+ font-size: 14px; color: var(--color-text-secondary, #64748b);
2325
+ line-height: 1.65; margin-bottom: 24px; max-width: 640px;
2326
+ }
2327
+ .component-docs h4 {
2328
+ font-size: 11px; font-weight: 600; text-transform: uppercase;
2329
+ letter-spacing: 0.07em; color: var(--color-text-secondary, #64748b);
2330
+ margin-bottom: 12px; padding-bottom: 8px;
2331
+ border-bottom: 1px solid var(--color-border-default, #e2e8f0);
2332
+ }
2333
+ .usage-example {
2334
+ margin: 0; padding: 16px 18px;
2335
+ background: var(--color-bg-default, #fff);
2336
+ color: var(--color-text-primary, #0f172a);
2337
+ border: 1px solid var(--color-border-default, #e2e8f0); border-radius: 8px;
2338
+ font-family: "SF Mono", "Fira Code", "Cascadia Code", monospace;
2339
+ font-size: 12.5px; line-height: 1.65; overflow-x: auto; white-space: pre;
2340
+ }
2341
+
2304
2342
  /* \u2500\u2500 Spinner animation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2305
2343
  @keyframes dsforge-spin { to { transform: rotate(360deg); } }
2306
2344
  @media (prefers-reduced-motion: reduce) { @keyframes dsforge-spin { to { transform: none; } } }
package/dist/index.js CHANGED
@@ -1143,6 +1143,10 @@ var rl = readline.createInterface({
1143
1143
  output: process.stdout
1144
1144
  });
1145
1145
 
1146
+ // src/lib/license.ts
1147
+ import { readFileSync } from "fs";
1148
+ import { join } from "path";
1149
+
1146
1150
  // src/cli/commands/init.ts
1147
1151
  var SPACING_PRESETS = {
1148
1152
  compact: {
@@ -3062,7 +3066,7 @@ function generatePackageJson(config, componentNames) {
3062
3066
  componentNames.map((c) => [`./metadata/${c}`, `./metadata/${c}.json`])
3063
3067
  )
3064
3068
  },
3065
- files: ["dist", "tokens", "metadata", "docs", "CHANGELOG.md"],
3069
+ files: ["dist", "tokens", "metadata", "CHANGELOG.md"],
3066
3070
  scripts: {
3067
3071
  build: "tsc",
3068
3072
  prepublishOnly: "npm run build"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nghitrum/dsforge",
3
- "version": "0.1.5-alpha.4",
3
+ "version": "0.1.5-alpha.6",
4
4
  "description": "AI-native design system generator — tokens → components → docs → npm",
5
5
  "keywords": [
6
6
  "design-system",