rafters 0.0.14 → 0.0.16

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 +316 -318
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8,9 +8,9 @@ import {
8
8
  import { Command } from "commander";
9
9
 
10
10
  // src/commands/add.ts
11
- import { existsSync } from "fs";
12
- import { access, mkdir, readFile as readFile2, writeFile } from "fs/promises";
13
- import { dirname, join as join3 } from "path";
11
+ import { existsSync as existsSync2 } from "fs";
12
+ import { access, mkdir, readFile as readFile3, writeFile } from "fs/promises";
13
+ import { dirname, join as join4 } from "path";
14
14
 
15
15
  // ../../node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/external.js
16
16
  var external_exports = {};
@@ -12751,6 +12751,159 @@ var RegistryClient = class {
12751
12751
  };
12752
12752
  var registryClient = new RegistryClient();
12753
12753
 
12754
+ // src/utils/detect.ts
12755
+ import { existsSync } from "fs";
12756
+ import { readFile } from "fs/promises";
12757
+ import { join } from "path";
12758
+ var CONFIG_FILE_FRAMEWORKS = [
12759
+ { files: ["astro.config.mjs", "astro.config.ts", "astro.config.js"], framework: "astro" },
12760
+ { files: ["next.config.mjs", "next.config.ts", "next.config.js"], framework: "next" },
12761
+ { files: ["remix.config.js", "remix.config.ts"], framework: "remix" },
12762
+ { files: ["vite.config.ts", "vite.config.js", "vite.config.mjs"], framework: "vite" }
12763
+ ];
12764
+ async function detectFramework(cwd) {
12765
+ try {
12766
+ const content = await readFile(join(cwd, "package.json"), "utf-8");
12767
+ const pkg = JSON.parse(content);
12768
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
12769
+ if (deps.next) {
12770
+ return "next";
12771
+ }
12772
+ if (deps["react-router"]) {
12773
+ return "react-router";
12774
+ }
12775
+ const hasRemix = Object.keys(deps).some((dep) => dep.startsWith("@remix-run/"));
12776
+ if (hasRemix) {
12777
+ return "remix";
12778
+ }
12779
+ if (deps.astro) {
12780
+ return "astro";
12781
+ }
12782
+ if (deps.vite) {
12783
+ return "vite";
12784
+ }
12785
+ } catch {
12786
+ }
12787
+ return detectFrameworkFromConfigFiles(cwd);
12788
+ }
12789
+ function detectFrameworkFromConfigFiles(cwd) {
12790
+ for (const { files, framework } of CONFIG_FILE_FRAMEWORKS) {
12791
+ for (const file2 of files) {
12792
+ if (existsSync(join(cwd, file2))) {
12793
+ return framework;
12794
+ }
12795
+ }
12796
+ }
12797
+ return "unknown";
12798
+ }
12799
+ async function detectTailwindVersion(cwd) {
12800
+ try {
12801
+ const content = await readFile(join(cwd, "package.json"), "utf-8");
12802
+ const pkg = JSON.parse(content);
12803
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
12804
+ const tailwindVersion = deps.tailwindcss;
12805
+ if (!tailwindVersion) {
12806
+ return null;
12807
+ }
12808
+ const versionMatch = tailwindVersion.match(/\d+\.\d+\.\d+/);
12809
+ return versionMatch ? versionMatch[0] : tailwindVersion;
12810
+ } catch {
12811
+ return null;
12812
+ }
12813
+ }
12814
+ function isTailwindV3(version2) {
12815
+ if (!version2) {
12816
+ return false;
12817
+ }
12818
+ return version2.startsWith("3.");
12819
+ }
12820
+ async function detectShadcn(cwd) {
12821
+ try {
12822
+ const content = await readFile(join(cwd, "components.json"), "utf-8");
12823
+ return JSON.parse(content);
12824
+ } catch {
12825
+ return null;
12826
+ }
12827
+ }
12828
+ function parseCssVariables(css3) {
12829
+ const light = {};
12830
+ const dark = {};
12831
+ const rootMatch = css3.match(/:root\s*\{([^}]+)\}/);
12832
+ if (rootMatch?.[1]) {
12833
+ parseVariablesIntoColors(rootMatch[1], light);
12834
+ }
12835
+ const darkMatch = css3.match(/\.dark\s*\{([^}]+)\}/);
12836
+ if (darkMatch?.[1]) {
12837
+ parseVariablesIntoColors(darkMatch[1], dark);
12838
+ }
12839
+ return { light, dark };
12840
+ }
12841
+ function parseVariablesIntoColors(block, colors) {
12842
+ const varMap = {
12843
+ "--background": "background",
12844
+ "--foreground": "foreground",
12845
+ "--card": "card",
12846
+ "--card-foreground": "cardForeground",
12847
+ "--popover": "popover",
12848
+ "--popover-foreground": "popoverForeground",
12849
+ "--primary": "primary",
12850
+ "--primary-foreground": "primaryForeground",
12851
+ "--secondary": "secondary",
12852
+ "--secondary-foreground": "secondaryForeground",
12853
+ "--muted": "muted",
12854
+ "--muted-foreground": "mutedForeground",
12855
+ "--accent": "accent",
12856
+ "--accent-foreground": "accentForeground",
12857
+ "--destructive": "destructive",
12858
+ "--destructive-foreground": "destructiveForeground",
12859
+ "--border": "border",
12860
+ "--input": "input",
12861
+ "--ring": "ring"
12862
+ };
12863
+ for (const [cssVar, colorKey] of Object.entries(varMap)) {
12864
+ const regex = new RegExp(`${cssVar}:\\s*([^;]+);`);
12865
+ const match2 = block.match(regex);
12866
+ if (match2?.[1]) {
12867
+ colors[colorKey] = match2[1].trim();
12868
+ }
12869
+ }
12870
+ }
12871
+ function frameworkToTarget(framework) {
12872
+ if (framework === "astro") return "astro";
12873
+ return "react";
12874
+ }
12875
+ function targetToExtension(target) {
12876
+ const map2 = {
12877
+ react: ".tsx",
12878
+ astro: ".astro",
12879
+ vue: ".vue",
12880
+ svelte: ".svelte"
12881
+ };
12882
+ return map2[target];
12883
+ }
12884
+ async function hasAstroReact(cwd) {
12885
+ try {
12886
+ const content = await readFile(join(cwd, "package.json"), "utf-8");
12887
+ const pkg = JSON.parse(content);
12888
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
12889
+ return Boolean(deps["@astrojs/react"]);
12890
+ } catch {
12891
+ return false;
12892
+ }
12893
+ }
12894
+ async function detectProject(cwd) {
12895
+ const [framework, shadcn, tailwindVersion] = await Promise.all([
12896
+ detectFramework(cwd),
12897
+ detectShadcn(cwd),
12898
+ detectTailwindVersion(cwd)
12899
+ ]);
12900
+ return {
12901
+ framework,
12902
+ shadcn,
12903
+ tailwindVersion
12904
+ };
12905
+ }
12906
+
12754
12907
  // src/utils/exports.ts
12755
12908
  var DEFAULT_EXPORTS = {
12756
12909
  tailwind: true,
@@ -12806,8 +12959,8 @@ function selectionsToConfig(selections) {
12806
12959
  }
12807
12960
 
12808
12961
  // src/utils/install-registry-deps.ts
12809
- import { readFile } from "fs/promises";
12810
- import { join } from "path";
12962
+ import { readFile as readFile2 } from "fs/promises";
12963
+ import { join as join2 } from "path";
12811
12964
 
12812
12965
  // src/utils/ui.ts
12813
12966
  import ora from "ora";
@@ -13098,7 +13251,7 @@ function parseDependency(dep) {
13098
13251
  async function readInstalledDeps(targetDir) {
13099
13252
  let raw;
13100
13253
  try {
13101
- raw = await readFile(join(targetDir, "package.json"), "utf-8");
13254
+ raw = await readFile2(join2(targetDir, "package.json"), "utf-8");
13102
13255
  } catch {
13103
13256
  return { packageJsonFound: false, installed: /* @__PURE__ */ new Set() };
13104
13257
  }
@@ -13190,14 +13343,14 @@ async function installRegistryDependencies(items, targetDir, options = {}) {
13190
13343
  }
13191
13344
 
13192
13345
  // src/utils/paths.ts
13193
- import { join as join2 } from "path";
13346
+ import { join as join3 } from "path";
13194
13347
  function getRaftersPaths(projectRoot = process.cwd()) {
13195
- const root = join2(projectRoot, ".rafters");
13348
+ const root = join3(projectRoot, ".rafters");
13196
13349
  return {
13197
13350
  root,
13198
- config: join2(root, "config.rafters.json"),
13199
- tokens: join2(root, "tokens"),
13200
- output: join2(root, "output")
13351
+ config: join3(root, "config.rafters.json"),
13352
+ tokens: join3(root, "tokens"),
13353
+ output: join3(root, "output")
13201
13354
  };
13202
13355
  }
13203
13356
 
@@ -13214,10 +13367,10 @@ async function isInitialized(cwd) {
13214
13367
  async function loadConfig(cwd) {
13215
13368
  const paths = getRaftersPaths(cwd);
13216
13369
  try {
13217
- const content = await readFile2(paths.config, "utf-8");
13370
+ const content = await readFile3(paths.config, "utf-8");
13218
13371
  return JSON.parse(content);
13219
13372
  } catch (err) {
13220
- if (existsSync(paths.config)) {
13373
+ if (existsSync2(paths.config)) {
13221
13374
  const message = err instanceof Error ? err.message : String(err);
13222
13375
  log({ event: "add:warning", message: `Failed to load config: ${message}` });
13223
13376
  }
@@ -13237,6 +13390,33 @@ function getInstalledNames(config3) {
13237
13390
  ]);
13238
13391
  return [...names2].sort();
13239
13392
  }
13393
+ function getComponentTarget(config3) {
13394
+ if (config3?.componentTarget) return config3.componentTarget;
13395
+ if (config3?.framework) return frameworkToTarget(config3.framework);
13396
+ return "react";
13397
+ }
13398
+ var SHARED_EXTENSIONS = /* @__PURE__ */ new Set([".classes.ts", ".types.ts", ".constants.ts"]);
13399
+ function isSharedFile(path2) {
13400
+ for (const ext2 of SHARED_EXTENSIONS) {
13401
+ if (path2.endsWith(ext2)) return true;
13402
+ }
13403
+ return false;
13404
+ }
13405
+ function selectFilesForFramework(files, target) {
13406
+ const preferredExt = targetToExtension(target);
13407
+ const shared = files.filter((f) => isSharedFile(f.path));
13408
+ const matched = files.filter((f) => f.path.endsWith(preferredExt));
13409
+ if (matched.length > 0) {
13410
+ return { files: [...matched, ...shared], fallback: false };
13411
+ }
13412
+ if (target !== "react") {
13413
+ const fallbackFiles = files.filter((f) => f.path.endsWith(".tsx"));
13414
+ if (fallbackFiles.length > 0) {
13415
+ return { files: [...fallbackFiles, ...shared], fallback: true };
13416
+ }
13417
+ }
13418
+ return { files, fallback: false };
13419
+ }
13240
13420
  var FOLDER_NAMES = /* @__PURE__ */ new Set(["composites"]);
13241
13421
  function isAlreadyInstalled(config3, item) {
13242
13422
  if (!config3?.installed) return false;
@@ -13288,7 +13468,7 @@ function transformPath(registryPath, config3) {
13288
13468
  return registryPath;
13289
13469
  }
13290
13470
  function fileExists(cwd, relativePath) {
13291
- return existsSync(join3(cwd, relativePath));
13471
+ return existsSync2(join4(cwd, relativePath));
13292
13472
  }
13293
13473
  function transformFileContent(content, config3, fileType = "component") {
13294
13474
  let transformed = content;
@@ -13324,9 +13504,23 @@ function transformFileContent(content, config3, fileType = "component") {
13324
13504
  async function installItem(cwd, item, options, config3) {
13325
13505
  const installedFiles = [];
13326
13506
  let skipped = false;
13327
- for (const file2 of item.files) {
13507
+ let filesToInstall = item.files;
13508
+ if (item.type === "ui") {
13509
+ const target = getComponentTarget(config3);
13510
+ const selection = selectFilesForFramework(item.files, target);
13511
+ filesToInstall = selection.files;
13512
+ if (selection.fallback) {
13513
+ log({
13514
+ event: "add:fallback",
13515
+ component: item.name,
13516
+ target,
13517
+ message: `No ${targetToExtension(target)} version available for ${item.name}. Installing React version.`
13518
+ });
13519
+ }
13520
+ }
13521
+ for (const file2 of filesToInstall) {
13328
13522
  const projectPath = transformPath(file2.path, config3);
13329
- const targetPath = join3(cwd, projectPath);
13523
+ const targetPath = join4(cwd, projectPath);
13330
13524
  if (fileExists(cwd, projectPath)) {
13331
13525
  if (!options.overwrite) {
13332
13526
  log({
@@ -13548,7 +13742,7 @@ import { existsSync as existsSync3 } from "fs";
13548
13742
  import { copyFile, mkdir as mkdir3, readFile as readFile5, rm, writeFile as writeFile3 } from "fs/promises";
13549
13743
  import { createRequire } from "module";
13550
13744
  import { join as join9, relative } from "path";
13551
- import { checkbox, confirm } from "@inquirer/prompts";
13745
+ import { checkbox, confirm, select } from "@inquirer/prompts";
13552
13746
 
13553
13747
  // ../design-tokens/src/plugins/scale.ts
13554
13748
  function scale(registry2, tokenName, dependencies) {
@@ -27861,7 +28055,7 @@ var posix = {
27861
28055
 
27862
28056
  // ../../node_modules/.pnpm/path-unified@0.2.0/node_modules/path-unified/src/posix.js
27863
28057
  var resolve = posResolve;
27864
- var join4 = posJoin;
28058
+ var join5 = posJoin;
27865
28059
 
27866
28060
  // ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
27867
28061
  var balanced = (a, b, str) => {
@@ -37760,7 +37954,7 @@ var transforms_default = {
37760
37954
  type: value,
37761
37955
  filter: isAsset,
37762
37956
  transform: function(token, _, options) {
37763
- return join4(process?.cwd() ?? "/", options.usesDtcg ? token.$value : token.value);
37957
+ return join5(process?.cwd() ?? "/", options.usesDtcg ? token.$value : token.value);
37764
37958
  }
37765
37959
  },
37766
37960
  /**
@@ -43960,12 +44154,12 @@ var formats_default = formats2;
43960
44154
  var { androidCopyImages, copyAssets } = actions;
43961
44155
  var { silent } = logVerbosityLevels;
43962
44156
  function getAssetDir(config3) {
43963
- return join4(config3.buildPath ?? "", "android/main/res/drawable-").replace(/\\/g, "/");
44157
+ return join5(config3.buildPath ?? "", "android/main/res/drawable-").replace(/\\/g, "/");
43964
44158
  }
43965
44159
  function getAssetPath(token, imagesDir) {
43966
44160
  const name2 = token.path.slice(2, 4).join("_");
43967
44161
  const dir = `${imagesDir}${token.attributes?.state}`;
43968
- return { file: join4(dir, `${name2}.png`), dir };
44162
+ return { file: join5(dir, `${name2}.png`), dir };
43969
44163
  }
43970
44164
  var actions_default = {
43971
44165
  /**
@@ -44006,7 +44200,7 @@ var actions_default = {
44006
44200
  */
44007
44201
  [copyAssets]: {
44008
44202
  do: async function(_, config3, _options, vol2 = fs2) {
44009
- const assetsPath = join4(config3.buildPath ?? "", "assets");
44203
+ const assetsPath = join5(config3.buildPath ?? "", "assets");
44010
44204
  if (config3.log?.verbosity !== silent) {
44011
44205
  console.log(`Copying assets directory to ${assetsPath}`);
44012
44206
  }
@@ -44020,7 +44214,7 @@ var actions_default = {
44020
44214
  );
44021
44215
  },
44022
44216
  undo: async function(_, config3, _options, vol2 = fs2) {
44023
- const assetsPath = join4(config3.buildPath ?? "", "assets");
44217
+ const assetsPath = join5(config3.buildPath ?? "", "assets");
44024
44218
  if (config3.log?.verbosity !== silent) {
44025
44219
  console.log(`Removing assets directory from ${assetsPath}`);
44026
44220
  }
@@ -47486,11 +47680,11 @@ function getParser4({ startLine = 0, fence = "```", spacing = "compact", markers
47486
47680
  }
47487
47681
 
47488
47682
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/stringifier/index.js
47489
- function join5(tokens) {
47683
+ function join6(tokens) {
47490
47684
  return tokens.start + tokens.delimiter + tokens.postDelimiter + tokens.tag + tokens.postTag + tokens.type + tokens.postType + tokens.name + tokens.postName + tokens.description + tokens.end + tokens.lineEnd;
47491
47685
  }
47492
47686
  function getStringifier() {
47493
- return (block) => block.source.map(({ tokens }) => join5(tokens)).join("\n");
47687
+ return (block) => block.source.map(({ tokens }) => join6(tokens)).join("\n");
47494
47688
  }
47495
47689
 
47496
47690
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/stringifier/inspect.js
@@ -49003,74 +49197,27 @@ function validateScaleGeneration(baseColor) {
49003
49197
  }
49004
49198
  return { isValid: true };
49005
49199
  }
49006
- function generateContrastBasedLightness(baseLightness) {
49007
- const WHITE_L = 1;
49008
- const BLACK_L = 0;
49009
- const targetContrasts = {
49010
- "50": 1.01,
49011
- // Ultra-subtle (dark mode borders)
49012
- "100": 1.45,
49013
- // Subtle (dark mode disabled text)
49014
- "200": 2.05,
49015
- // Functional (dark mode secondary UI)
49016
- "300": 3,
49017
- // Large text AA minimum
49018
- "400": 4.54,
49019
- // Normal text AA (light mode)
49020
- "500": null,
49021
- // Crossover - avoid this zone
49022
- "600": 7,
49023
- // Normal text AAA
49024
- "700": 10.86,
49025
- // High contrast
49026
- "800": 11.86,
49027
- // Higher contrast
49028
- "900": 12.86,
49029
- // Maximum usable
49030
- "950": 13.86
49031
- // Absolute maximum
49032
- };
49200
+ function generateLightnessProgression(baseLightness) {
49201
+ const MAX_LIGHT = 0.95;
49202
+ const MIN_DARK = 0.05;
49203
+ const positions = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
49204
+ const baseIndex = 6;
49033
49205
  const lightness = {};
49034
- function lightnessForContrast(targetRatio, _backgroundL, onDark = false) {
49035
- if (onDark) {
49036
- const calculatedL2 = targetRatio * 0.05 - 0.05;
49037
- return Math.max(0.05, Math.min(0.98, calculatedL2));
49038
- }
49039
- const calculatedL = 1.05 / targetRatio - 0.05;
49040
- return Math.max(0.01, Math.min(0.95, calculatedL));
49041
- }
49042
- for (const [step, targetRatio] of Object.entries(targetContrasts)) {
49043
- if (targetRatio === null) {
49044
- lightness[step] = baseLightness;
49045
- continue;
49046
- }
49047
- let calculatedL;
49048
- if (Number.parseInt(step, 10) <= 400) {
49049
- calculatedL = lightnessForContrast(targetRatio, BLACK_L, true);
49050
- const stepNum = Number.parseInt(step, 10);
49051
- if (stepNum === 50)
49052
- calculatedL = 0.98;
49053
- else if (stepNum === 100)
49054
- calculatedL = 0.95;
49055
- else if (stepNum === 200)
49056
- calculatedL = 0.9;
49057
- else if (stepNum === 300)
49058
- calculatedL = 0.8;
49059
- else if (stepNum === 400) calculatedL = 0.7;
49060
- } else {
49061
- calculatedL = lightnessForContrast(targetRatio, WHITE_L, false);
49062
- const stepNum = Number.parseInt(step, 10);
49063
- if (stepNum === 600)
49064
- calculatedL = 0.4;
49065
- else if (stepNum === 700)
49066
- calculatedL = 0.25;
49067
- else if (stepNum === 800)
49068
- calculatedL = 0.15;
49069
- else if (stepNum === 900)
49070
- calculatedL = 0.08;
49071
- else if (stepNum === 950) calculatedL = 0.04;
49072
- }
49073
- lightness[step] = Math.max(5e-3, Math.min(0.98, calculatedL));
49206
+ for (let i = 0; i < baseIndex; i++) {
49207
+ const stepsFromBase = baseIndex - i;
49208
+ const totalLighterSteps = baseIndex;
49209
+ const t2 = (stepsFromBase / totalLighterSteps) ** 0.8;
49210
+ const calculatedL = baseLightness + (MAX_LIGHT - baseLightness) * t2;
49211
+ lightness[positions[i].toString()] = Math.min(MAX_LIGHT, calculatedL);
49212
+ }
49213
+ lightness["600"] = baseLightness;
49214
+ for (let i = baseIndex + 1; i < positions.length; i++) {
49215
+ const stepsFromBase = i - baseIndex;
49216
+ const totalDarkerSteps = positions.length - 1 - baseIndex;
49217
+ const t2 = stepsFromBase / totalDarkerSteps;
49218
+ const darkenAmount = (baseLightness - MIN_DARK) * t2;
49219
+ const calculatedL = Math.max(MIN_DARK, baseLightness - darkenAmount);
49220
+ lightness[positions[i].toString()] = calculatedL;
49074
49221
  }
49075
49222
  return lightness;
49076
49223
  }
@@ -49080,25 +49227,18 @@ function generateOKLCHScale(baseColor) {
49080
49227
  const adjustedColor = validation.suggestedLightness ? { ...baseColor, l: validation.suggestedLightness } : baseColor;
49081
49228
  return generateOKLCHScale(adjustedColor);
49082
49229
  }
49083
- const lightnessSteps = generateContrastBasedLightness(baseColor.l);
49230
+ const lightnessSteps = generateLightnessProgression(baseColor.l);
49084
49231
  const scale2 = {};
49085
49232
  for (const [step, lightness] of Object.entries(lightnessSteps)) {
49086
49233
  let adjustedChroma = baseColor.c;
49087
49234
  if (lightness > 0.9) {
49088
- adjustedChroma *= 0.15;
49089
- } else if (lightness > 0.8) {
49090
- adjustedChroma *= 0.25;
49091
- } else if (lightness > 0.6) {
49092
- adjustedChroma *= 0.7;
49235
+ adjustedChroma *= 0.3;
49093
49236
  } else if (lightness < 0.15) {
49094
- adjustedChroma *= 0.8;
49095
- } else if (lightness < 0.3) {
49096
- adjustedChroma *= 0.9;
49237
+ adjustedChroma *= 0.6;
49097
49238
  }
49098
49239
  scale2[step] = roundOKLCH({
49099
49240
  l: lightness,
49100
- c: Math.max(0.01, adjustedChroma),
49101
- // Ensure minimum chroma
49241
+ c: adjustedChroma,
49102
49242
  h: baseColor.h,
49103
49243
  alpha: baseColor.alpha
49104
49244
  });
@@ -50844,14 +50984,14 @@ function buildColorSystem(options = {}) {
50844
50984
  }
50845
50985
 
50846
50986
  // ../design-tokens/src/persistence/node-adapter.ts
50847
- import { mkdir as mkdir2, readdir as readdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
50848
- import { join as join6 } from "path";
50987
+ import { mkdir as mkdir2, readdir as readdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
50988
+ import { join as join7 } from "path";
50849
50989
  var SCHEMA_URL = "https://rafters.studio/schemas/namespace-tokens.json";
50850
50990
  var VERSION = "1.0.0";
50851
50991
  var NodePersistenceAdapter = class {
50852
50992
  tokensDir;
50853
50993
  constructor(projectRoot) {
50854
- this.tokensDir = join6(projectRoot, ".rafters", "tokens");
50994
+ this.tokensDir = join7(projectRoot, ".rafters", "tokens");
50855
50995
  }
50856
50996
  async load() {
50857
50997
  let files;
@@ -50863,7 +51003,7 @@ var NodePersistenceAdapter = class {
50863
51003
  const allTokens = [];
50864
51004
  for (const file2 of files) {
50865
51005
  if (!file2.endsWith(".rafters.json")) continue;
50866
- const content = await readFile3(join6(this.tokensDir, file2), "utf-8");
51006
+ const content = await readFile4(join7(this.tokensDir, file2), "utf-8");
50867
51007
  const data = NamespaceFileSchema.parse(JSON.parse(content));
50868
51008
  allTokens.push(...data.tokens);
50869
51009
  }
@@ -50891,7 +51031,7 @@ var NodePersistenceAdapter = class {
50891
51031
  };
50892
51032
  NamespaceFileSchema.parse(data);
50893
51033
  await writeFile2(
50894
- join6(this.tokensDir, `${namespace}.rafters.json`),
51034
+ join7(this.tokensDir, `${namespace}.rafters.json`),
50895
51035
  JSON.stringify(data, null, 2)
50896
51036
  );
50897
51037
  }
@@ -50900,7 +51040,7 @@ var NodePersistenceAdapter = class {
50900
51040
 
50901
51041
  // ../design-tokens/src/rule-engine.ts
50902
51042
  import { promises as fs3 } from "fs";
50903
- import { join as join7 } from "path";
51043
+ import { join as join8 } from "path";
50904
51044
  var RuleResultSchema = external_exports.union([
50905
51045
  external_exports.string(),
50906
51046
  external_exports.object({
@@ -50915,136 +51055,6 @@ var RuleContextSchema = external_exports.object({
50915
51055
  // Functions validated at runtime
50916
51056
  });
50917
51057
 
50918
- // src/utils/detect.ts
50919
- import { existsSync as existsSync2 } from "fs";
50920
- import { readFile as readFile4 } from "fs/promises";
50921
- import { join as join8 } from "path";
50922
- var CONFIG_FILE_FRAMEWORKS = [
50923
- { files: ["astro.config.mjs", "astro.config.ts", "astro.config.js"], framework: "astro" },
50924
- { files: ["next.config.mjs", "next.config.ts", "next.config.js"], framework: "next" },
50925
- { files: ["remix.config.js", "remix.config.ts"], framework: "remix" },
50926
- { files: ["vite.config.ts", "vite.config.js", "vite.config.mjs"], framework: "vite" }
50927
- ];
50928
- async function detectFramework(cwd) {
50929
- try {
50930
- const content = await readFile4(join8(cwd, "package.json"), "utf-8");
50931
- const pkg = JSON.parse(content);
50932
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50933
- if (deps.next) {
50934
- return "next";
50935
- }
50936
- if (deps["react-router"]) {
50937
- return "react-router";
50938
- }
50939
- const hasRemix = Object.keys(deps).some((dep) => dep.startsWith("@remix-run/"));
50940
- if (hasRemix) {
50941
- return "remix";
50942
- }
50943
- if (deps.astro) {
50944
- return "astro";
50945
- }
50946
- if (deps.vite) {
50947
- return "vite";
50948
- }
50949
- } catch {
50950
- }
50951
- return detectFrameworkFromConfigFiles(cwd);
50952
- }
50953
- function detectFrameworkFromConfigFiles(cwd) {
50954
- for (const { files, framework } of CONFIG_FILE_FRAMEWORKS) {
50955
- for (const file2 of files) {
50956
- if (existsSync2(join8(cwd, file2))) {
50957
- return framework;
50958
- }
50959
- }
50960
- }
50961
- return "unknown";
50962
- }
50963
- async function detectTailwindVersion(cwd) {
50964
- try {
50965
- const content = await readFile4(join8(cwd, "package.json"), "utf-8");
50966
- const pkg = JSON.parse(content);
50967
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50968
- const tailwindVersion = deps.tailwindcss;
50969
- if (!tailwindVersion) {
50970
- return null;
50971
- }
50972
- const versionMatch = tailwindVersion.match(/\d+\.\d+\.\d+/);
50973
- return versionMatch ? versionMatch[0] : tailwindVersion;
50974
- } catch {
50975
- return null;
50976
- }
50977
- }
50978
- function isTailwindV3(version2) {
50979
- if (!version2) {
50980
- return false;
50981
- }
50982
- return version2.startsWith("3.");
50983
- }
50984
- async function detectShadcn(cwd) {
50985
- try {
50986
- const content = await readFile4(join8(cwd, "components.json"), "utf-8");
50987
- return JSON.parse(content);
50988
- } catch {
50989
- return null;
50990
- }
50991
- }
50992
- function parseCssVariables(css3) {
50993
- const light = {};
50994
- const dark = {};
50995
- const rootMatch = css3.match(/:root\s*\{([^}]+)\}/);
50996
- if (rootMatch?.[1]) {
50997
- parseVariablesIntoColors(rootMatch[1], light);
50998
- }
50999
- const darkMatch = css3.match(/\.dark\s*\{([^}]+)\}/);
51000
- if (darkMatch?.[1]) {
51001
- parseVariablesIntoColors(darkMatch[1], dark);
51002
- }
51003
- return { light, dark };
51004
- }
51005
- function parseVariablesIntoColors(block, colors) {
51006
- const varMap = {
51007
- "--background": "background",
51008
- "--foreground": "foreground",
51009
- "--card": "card",
51010
- "--card-foreground": "cardForeground",
51011
- "--popover": "popover",
51012
- "--popover-foreground": "popoverForeground",
51013
- "--primary": "primary",
51014
- "--primary-foreground": "primaryForeground",
51015
- "--secondary": "secondary",
51016
- "--secondary-foreground": "secondaryForeground",
51017
- "--muted": "muted",
51018
- "--muted-foreground": "mutedForeground",
51019
- "--accent": "accent",
51020
- "--accent-foreground": "accentForeground",
51021
- "--destructive": "destructive",
51022
- "--destructive-foreground": "destructiveForeground",
51023
- "--border": "border",
51024
- "--input": "input",
51025
- "--ring": "ring"
51026
- };
51027
- for (const [cssVar, colorKey] of Object.entries(varMap)) {
51028
- const regex = new RegExp(`${cssVar}:\\s*([^;]+);`);
51029
- const match2 = block.match(regex);
51030
- if (match2?.[1]) {
51031
- colors[colorKey] = match2[1].trim();
51032
- }
51033
- }
51034
- }
51035
- async function detectProject(cwd) {
51036
- const [framework, shadcn, tailwindVersion] = await Promise.all([
51037
- detectFramework(cwd),
51038
- detectShadcn(cwd),
51039
- detectTailwindVersion(cwd)
51040
- ]);
51041
- return {
51042
- framework,
51043
- shadcn,
51044
- tailwindVersion
51045
- };
51046
- }
51047
-
51048
51058
  // src/commands/init.ts
51049
51059
  async function backupCss(cssPath) {
51050
51060
  const backupPath = cssPath.replace(/\.css$/, ".backup.css");
@@ -51252,6 +51262,19 @@ async function regenerateFromExisting(cwd, paths, shadcn, isAgentMode2, framewor
51252
51262
  if (existingConfig) {
51253
51263
  existingConfig.exports = exports;
51254
51264
  await writeFile3(paths.config, JSON.stringify(existingConfig, null, 2));
51265
+ } else {
51266
+ const frameworkPaths = COMPONENT_PATHS[framework] || COMPONENT_PATHS.unknown;
51267
+ const newConfig = {
51268
+ framework,
51269
+ componentsPath: frameworkPaths.components,
51270
+ primitivesPath: frameworkPaths.primitives,
51271
+ compositesPath: frameworkPaths.composites,
51272
+ cssPath: null,
51273
+ shadcn: !!shadcn,
51274
+ exports,
51275
+ installed: { components: [], primitives: [], composites: [] }
51276
+ };
51277
+ await writeFile3(paths.config, JSON.stringify(newConfig, null, 2));
51255
51278
  }
51256
51279
  log({
51257
51280
  event: "init:complete",
@@ -51336,6 +51359,19 @@ async function resetToDefaults(cwd, paths, shadcn, isAgentMode2, framework) {
51336
51359
  if (existingConfig) {
51337
51360
  existingConfig.exports = exports;
51338
51361
  await writeFile3(paths.config, JSON.stringify(existingConfig, null, 2));
51362
+ } else {
51363
+ const frameworkPaths = COMPONENT_PATHS[framework] || COMPONENT_PATHS.unknown;
51364
+ const newConfig = {
51365
+ framework,
51366
+ componentsPath: frameworkPaths.components,
51367
+ primitivesPath: frameworkPaths.primitives,
51368
+ compositesPath: frameworkPaths.composites,
51369
+ cssPath: null,
51370
+ shadcn: !!shadcn,
51371
+ exports,
51372
+ installed: { components: [], primitives: [], composites: [] }
51373
+ };
51374
+ await writeFile3(paths.config, JSON.stringify(newConfig, null, 2));
51339
51375
  }
51340
51376
  log({
51341
51377
  event: "init:complete",
@@ -51479,9 +51515,29 @@ async function init(options) {
51479
51515
  } else if (shadcn?.tailwind?.css) {
51480
51516
  detectedCssPath = shadcn.tailwind.css;
51481
51517
  }
51518
+ let componentTarget = frameworkToTarget(framework);
51519
+ if (framework === "astro" && isInteractive() && !isAgentMode2) {
51520
+ const astroHasReact = await hasAstroReact(cwd);
51521
+ if (astroHasReact) {
51522
+ componentTarget = await select({
51523
+ message: "This Astro project has React integration. Install components as:",
51524
+ choices: [
51525
+ {
51526
+ name: "Astro components (zero client JS, server-rendered)",
51527
+ value: "astro"
51528
+ },
51529
+ {
51530
+ name: "React components (client islands with client:load)",
51531
+ value: "react"
51532
+ }
51533
+ ]
51534
+ });
51535
+ }
51536
+ }
51482
51537
  const frameworkPaths = COMPONENT_PATHS[framework] || COMPONENT_PATHS.unknown;
51483
51538
  const config3 = {
51484
51539
  framework,
51540
+ componentTarget,
51485
51541
  componentsPath: frameworkPaths.components,
51486
51542
  primitivesPath: frameworkPaths.primitives,
51487
51543
  compositesPath: frameworkPaths.composites,
@@ -52418,23 +52474,7 @@ UTILITIES EXIST FOR EDGE CASES.
52418
52474
  If no component fits your need, check @/lib/utils for official behavioral utilities. Do not invent your own. If nothing exists there either, ask the human.
52419
52475
 
52420
52476
  COLORS ARE TAILWIND CLASSES.
52421
- Write border-l-primary, bg-success, text-info-foreground. Do not create color constants, mapping objects, or reference palette names (silver-true-sky-500, neutral-400, etc). The designer already decided what each token looks like. Palette families are internal -- never use them in consumer code.
52422
-
52423
- SYSTEM COLOR TOKENS (use these as Tailwind class fragments):
52424
- - primary / primary-foreground -- Main brand, CTA buttons, links
52425
- - secondary / secondary-foreground -- Less prominent actions
52426
- - accent / accent-foreground -- Hover highlights, emphasis
52427
- - muted / muted-foreground -- Subdued backgrounds, disabled text
52428
- - destructive / destructive-foreground -- Delete, remove, errors
52429
- - success / success-foreground -- Confirmations, positive feedback
52430
- - warning / warning-foreground -- Caution, important notices
52431
- - info / info-foreground -- Tips, help, neutral information
52432
- - chart-1 through chart-5 -- Categorical distinction (guaranteed harmonious)
52433
- - card / card-foreground -- Card surfaces
52434
- - popover / popover-foreground -- Overlay surfaces
52435
- - border, input, ring -- Structural tokens
52436
-
52437
- For categorical color coding (e.g. field types), use border-l-{token} on a container with bg-card. Example Tailwind classes: "border-l-4 border-l-primary", "border-l-4 border-l-info", "border-l-4 border-l-success".
52477
+ Write border-l-primary, bg-success, text-info-foreground. Do not create color constants, mapping objects, or reference palette names. Palette families are internal. See quickstart.colorTokens for the full list of semantic tokens and usage examples.
52438
52478
 
52439
52479
  When in doubt: less code, not more. Rafters has already made the design decision.`;
52440
52480
  var CONSUMER_QUICKSTART = {
@@ -52912,38 +52952,7 @@ var RaftersToolHandler = class {
52912
52952
  * even when dynamic token loading fails or returns empty.
52913
52953
  */
52914
52954
  async getColorVocabulary() {
52915
- const knownSemantic = [
52916
- "primary",
52917
- "primary-foreground",
52918
- "secondary",
52919
- "secondary-foreground",
52920
- "accent",
52921
- "accent-foreground",
52922
- "muted",
52923
- "muted-foreground",
52924
- "destructive",
52925
- "destructive-foreground",
52926
- "success",
52927
- "success-foreground",
52928
- "warning",
52929
- "warning-foreground",
52930
- "info",
52931
- "info-foreground",
52932
- "card",
52933
- "card-foreground",
52934
- "popover",
52935
- "popover-foreground",
52936
- "background",
52937
- "foreground",
52938
- "border",
52939
- "input",
52940
- "ring",
52941
- "chart-1",
52942
- "chart-2",
52943
- "chart-3",
52944
- "chart-4",
52945
- "chart-5"
52946
- ];
52955
+ const knownSemantic = Object.keys(DEFAULT_SEMANTIC_COLOR_MAPPINGS);
52947
52956
  try {
52948
52957
  const tokens = await this.loadNamespace("color");
52949
52958
  const dynamicSemantic = [];
@@ -52983,32 +52992,26 @@ var RaftersToolHandler = class {
52983
52992
  async getSpacingVocabulary() {
52984
52993
  try {
52985
52994
  const tokens = await this.loadNamespace("spacing");
52986
- const scale2 = {};
52995
+ const scale3 = {};
52987
52996
  for (const token of tokens) {
52988
52997
  if (typeof token.value === "string") {
52989
- scale2[token.name] = token.value;
52998
+ scale3[token.name] = token.value;
52990
52999
  }
52991
53000
  }
52992
- if (Object.keys(scale2).length > 0) {
53001
+ if (Object.keys(scale3).length > 0) {
52993
53002
  return {
52994
- scale: scale2,
53003
+ scale: scale3,
52995
53004
  usage: "Container and Grid handle spacing. Do not use gap-*, p-*, m-* directly."
52996
53005
  };
52997
53006
  }
52998
53007
  } catch {
52999
53008
  }
53009
+ const scale2 = {};
53010
+ for (const [key, multiplier] of Object.entries(DEFAULT_SPACING_MULTIPLIERS)) {
53011
+ scale2[`spacing-${key}`] = `${multiplier * 0.25}rem`;
53012
+ }
53000
53013
  return {
53001
- scale: {
53002
- "spacing-0": "0rem",
53003
- "spacing-1": "0.25rem",
53004
- "spacing-2": "0.5rem",
53005
- "spacing-3": "0.75rem",
53006
- "spacing-4": "1rem",
53007
- "spacing-6": "1.5rem",
53008
- "spacing-8": "2rem",
53009
- "spacing-12": "3rem",
53010
- "spacing-16": "4rem"
53011
- },
53014
+ scale: scale2,
53012
53015
  usage: "Container and Grid handle spacing. Do not use gap-*, p-*, m-* directly."
53013
53016
  };
53014
53017
  }
@@ -53019,38 +53022,33 @@ var RaftersToolHandler = class {
53019
53022
  async getTypographyVocabulary() {
53020
53023
  try {
53021
53024
  const tokens = await this.loadNamespace("typography");
53022
- const sizes = {};
53025
+ const sizes2 = {};
53023
53026
  const weights = /* @__PURE__ */ new Set();
53024
53027
  for (const token of tokens) {
53025
53028
  if (typeof token.value === "string") {
53026
53029
  if (token.name.includes("weight")) {
53027
53030
  weights.add(token.name.replace("-weight", ""));
53028
53031
  } else {
53029
- sizes[token.name] = token.value;
53032
+ sizes2[token.name] = token.value;
53030
53033
  }
53031
53034
  }
53032
53035
  }
53033
- if (Object.keys(sizes).length > 0) {
53036
+ if (Object.keys(sizes2).length > 0) {
53034
53037
  return {
53035
- sizes,
53038
+ sizes: sizes2,
53036
53039
  weights: [...weights],
53037
53040
  usage: "Typography components (H1-H4, P, Label) handle sizing. Do not set font sizes directly."
53038
53041
  };
53039
53042
  }
53040
53043
  } catch {
53041
53044
  }
53045
+ const sizes = {};
53046
+ for (const name2 of Object.keys(DEFAULT_TYPOGRAPHY_SCALE)) {
53047
+ sizes[`font-size-${name2}`] = name2;
53048
+ }
53042
53049
  return {
53043
- sizes: {
53044
- "font-size-xs": "0.75rem",
53045
- "font-size-sm": "0.875rem",
53046
- "font-size-base": "1rem",
53047
- "font-size-lg": "1.125rem",
53048
- "font-size-xl": "1.25rem",
53049
- "font-size-2xl": "1.5rem",
53050
- "font-size-3xl": "1.875rem",
53051
- "font-size-4xl": "2.25rem"
53052
- },
53053
- weights: ["normal", "medium", "semibold", "bold"],
53050
+ sizes,
53051
+ weights: Object.keys(DEFAULT_FONT_WEIGHTS),
53054
53052
  usage: "Typography components (H1-H4, P, Label) handle sizing. Do not set font sizes directly."
53055
53053
  };
53056
53054
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "CLI for Rafters design system - scaffold tokens and add components",
5
5
  "license": "MIT",
6
6
  "type": "module",