rafters 0.0.36 → 0.0.38

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 +365 -55
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12829,6 +12829,7 @@ function frameworkToTarget(framework) {
12829
12829
  if (framework === "astro") return "astro";
12830
12830
  return "react";
12831
12831
  }
12832
+ var COMPONENT_EXTENSIONS = [".tsx", ".astro", ".vue", ".svelte"];
12832
12833
  function targetToExtension(target) {
12833
12834
  const map2 = {
12834
12835
  react: ".tsx",
@@ -12838,6 +12839,11 @@ function targetToExtension(target) {
12838
12839
  };
12839
12840
  return map2[target];
12840
12841
  }
12842
+ function resolveComponentTarget(config3) {
12843
+ if (config3?.componentTarget) return config3.componentTarget;
12844
+ if (config3?.framework) return frameworkToTarget(config3.framework);
12845
+ return "react";
12846
+ }
12841
12847
  async function hasAstroReact(cwd) {
12842
12848
  try {
12843
12849
  const content = await readFile(join(cwd, "package.json"), "utf-8");
@@ -13350,9 +13356,7 @@ function getInstalledNames(config3) {
13350
13356
  return [...names2].sort();
13351
13357
  }
13352
13358
  function getComponentTarget(config3) {
13353
- if (config3?.componentTarget) return config3.componentTarget;
13354
- if (config3?.framework) return frameworkToTarget(config3.framework);
13355
- return "react";
13359
+ return resolveComponentTarget(config3);
13356
13360
  }
13357
13361
  var SHARED_EXTENSIONS = /* @__PURE__ */ new Set([".classes.ts", ".types.ts", ".constants.ts"]);
13358
13362
  function isSharedFile(path2) {
@@ -46764,6 +46768,13 @@ var DEFAULT_SEMANTIC_COLOR_MAPPINGS = {
46764
46768
  };
46765
46769
 
46766
46770
  // ../design-tokens/src/exporters/tailwind.ts
46771
+ var SHADOW_PART_SUFFIX = /-(offset-x|offset-y|blur|spread|color)$/;
46772
+ function isShadowDecomposedPart(name2) {
46773
+ return SHADOW_PART_SUFFIX.test(name2);
46774
+ }
46775
+ function isMediaQueryToken(token) {
46776
+ return typeof token.value === "string" && token.value.startsWith("(");
46777
+ }
46767
46778
  function colorRefToString(ref) {
46768
46779
  return `${ref.family}-${ref.position}`;
46769
46780
  }
@@ -46961,8 +46972,12 @@ function generateThemeBlock(groups) {
46961
46972
  for (const token of groups.shadow) {
46962
46973
  const value2 = tokenValueToCSS(token);
46963
46974
  if (value2 === null) continue;
46964
- const key = token.name.replace(/^shadow-/, "");
46965
- lines.push(` --shadow-${key}: ${value2};`);
46975
+ if (isShadowDecomposedPart(token.name)) {
46976
+ lines.push(` --rafters-${token.name}: ${value2};`);
46977
+ } else {
46978
+ const key = token.name.replace(/^shadow-/, "");
46979
+ lines.push(` --shadow-${key}: ${value2};`);
46980
+ }
46966
46981
  }
46967
46982
  lines.push("");
46968
46983
  }
@@ -47006,6 +47021,7 @@ function generateThemeBlock(groups) {
47006
47021
  for (const token of groups.breakpoint) {
47007
47022
  const value2 = tokenValueToCSS(token);
47008
47023
  if (value2 === null) continue;
47024
+ if (isMediaQueryToken(token)) continue;
47009
47025
  lines.push(` --${token.name}: ${value2};`);
47010
47026
  }
47011
47027
  lines.push("");
@@ -47214,7 +47230,7 @@ function generateVarsRootBlock(groups) {
47214
47230
  for (const token of groups.shadow) {
47215
47231
  const value2 = tokenValueToCSS(token);
47216
47232
  if (value2 === null) continue;
47217
- lines.push(` --rafters-shadow-${token.name}: ${value2};`);
47233
+ lines.push(` --rafters-${token.name}: ${value2};`);
47218
47234
  }
47219
47235
  lines.push("");
47220
47236
  }
@@ -47256,6 +47272,7 @@ function generateVarsRootBlock(groups) {
47256
47272
  for (const token of groups.breakpoint) {
47257
47273
  const value2 = tokenValueToCSS(token);
47258
47274
  if (value2 === null) continue;
47275
+ if (isMediaQueryToken(token)) continue;
47259
47276
  lines.push(` --rafters-${token.name}: ${value2};`);
47260
47277
  }
47261
47278
  lines.push("");
@@ -57669,6 +57686,13 @@ function generateMotionTokens(config3, durationDefs, easingDefs, delayDefs) {
57669
57686
  }
57670
57687
 
57671
57688
  // ../design-tokens/src/generators/radius.ts
57689
+ var CORNERS = ["tl", "tr", "bl", "br"];
57690
+ var CORNER_NAMES = {
57691
+ tl: "top-left",
57692
+ tr: "top-right",
57693
+ bl: "bottom-left",
57694
+ br: "bottom-right"
57695
+ };
57672
57696
  function generateRadiusTokens(config3, radiusDefs) {
57673
57697
  const tokens = [];
57674
57698
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -57691,6 +57715,20 @@ function generateRadiusTokens(config3, radiusDefs) {
57691
57715
  never: ["Change without understanding scale impact"]
57692
57716
  }
57693
57717
  });
57718
+ for (const corner of CORNERS) {
57719
+ tokens.push({
57720
+ name: `radius-${corner}`,
57721
+ value: "var(--rafters-radius-base)",
57722
+ category: "radius",
57723
+ namespace: "radius",
57724
+ semanticMeaning: `Base ${CORNER_NAMES[corner]} radius - override to affect all scales for this corner`,
57725
+ usageContext: ["designer-override"],
57726
+ dependsOn: ["radius-base"],
57727
+ description: `${CORNER_NAMES[corner]} radius. Defaults to radius-base. Set to 0 for a sharp corner on every component.`,
57728
+ generatedAt: timestamp,
57729
+ containerQueryAware: false
57730
+ });
57731
+ }
57694
57732
  for (const scale3 of RADIUS_SCALE) {
57695
57733
  const def = radiusDefs[scale3];
57696
57734
  if (!def) continue;
@@ -57711,8 +57749,9 @@ function generateRadiusTokens(config3, radiusDefs) {
57711
57749
  value2 = `calc(var(--rafters-radius-base) * ${multiplier})`;
57712
57750
  mathRelationship = `base \xD7 ${progression.ratio}^${def.step} (\xD7${multiplier})`;
57713
57751
  }
57752
+ const scaleName = scale3 === "DEFAULT" ? "radius" : `radius-${scale3}`;
57714
57753
  tokens.push({
57715
- name: scale3 === "DEFAULT" ? "radius" : `radius-${scale3}`,
57754
+ name: scaleName,
57716
57755
  value: value2,
57717
57756
  category: "radius",
57718
57757
  namespace: "radius",
@@ -57726,6 +57765,25 @@ function generateRadiusTokens(config3, radiusDefs) {
57726
57765
  generatedAt: timestamp,
57727
57766
  containerQueryAware: false
57728
57767
  });
57768
+ if (def.step !== "none" && def.step !== "full" && scale3 !== "DEFAULT") {
57769
+ const cornerMultiplier = def.step === 0 ? null : Math.round(progression.ratio ** def.step * 1e3) / 1e3;
57770
+ for (const corner of CORNERS) {
57771
+ const cornerValue = cornerMultiplier === null ? `var(--rafters-radius-${corner})` : `calc(var(--rafters-radius-${corner}) * ${cornerMultiplier})`;
57772
+ tokens.push({
57773
+ name: `radius-${scale3}-${corner}`,
57774
+ value: cornerValue,
57775
+ category: "radius",
57776
+ namespace: "radius",
57777
+ semanticMeaning: `${CORNER_NAMES[corner]} radius at ${scale3} scale`,
57778
+ usageContext: def.contexts,
57779
+ scalePosition: scaleIndex,
57780
+ dependsOn: [`radius-${corner}`],
57781
+ description: `${CORNER_NAMES[corner]} radius ${scale3}: derives from radius-${corner} base`,
57782
+ generatedAt: timestamp,
57783
+ containerQueryAware: false
57784
+ });
57785
+ }
57786
+ }
57729
57787
  }
57730
57788
  return {
57731
57789
  namespace: "radius",
@@ -57782,26 +57840,29 @@ function pxToRem2(px) {
57782
57840
  const rem = Math.round(px / 16 * 1e3) / 1e3;
57783
57841
  return `${rem}rem`;
57784
57842
  }
57785
- function generateShadowValue(def, baseSpacing) {
57786
- if (def.opacity === 0) {
57787
- return "none";
57788
- }
57789
- const shadows = [];
57790
- const yOffsetPx = Math.round(def.yOffset * baseSpacing * 100) / 100;
57791
- const blurPx = Math.round(def.blur * baseSpacing * 100) / 100;
57792
- const spreadPx = Math.round(def.spread * baseSpacing * 100) / 100;
57793
- shadows.push(
57794
- `0 ${pxToRem2(yOffsetPx)} ${pxToRem2(blurPx)} ${pxToRem2(spreadPx)} rgb(0 0 0 / ${def.opacity})`
57795
- );
57796
- if (def.innerShadow) {
57797
- const innerYPx = Math.round(def.innerShadow.yOffset * baseSpacing * 100) / 100;
57798
- const innerBlurPx = Math.round(def.innerShadow.blur * baseSpacing * 100) / 100;
57799
- const innerSpreadPx = Math.round(def.innerShadow.spread * baseSpacing * 100) / 100;
57800
- shadows.push(
57801
- `0 ${pxToRem2(innerYPx)} ${pxToRem2(innerBlurPx)} ${pxToRem2(innerSpreadPx)} rgb(0 0 0 / ${def.innerShadow.opacity})`
57802
- );
57803
- }
57804
- return shadows.join(", ");
57843
+ var SHADOW_PARTS = ["offset-x", "offset-y", "blur", "spread", "color"];
57844
+ function scalePx(multiplier, baseSpacing) {
57845
+ return Math.round(multiplier * baseSpacing * 100) / 100;
57846
+ }
57847
+ function resolveShadowParts(def, baseSpacing) {
57848
+ return {
57849
+ // Shadows are vertical-only by design (material elevation model)
57850
+ "offset-x": "0rem",
57851
+ "offset-y": pxToRem2(scalePx(def.yOffset, baseSpacing)),
57852
+ blur: pxToRem2(scalePx(def.blur, baseSpacing)),
57853
+ spread: pxToRem2(scalePx(def.spread, baseSpacing)),
57854
+ color: `rgb(0 0 0 / ${def.opacity})`
57855
+ };
57856
+ }
57857
+ function generateInnerShadowValue(inner, baseSpacing) {
57858
+ const y = pxToRem2(scalePx(inner.yOffset, baseSpacing));
57859
+ const blur = pxToRem2(scalePx(inner.blur, baseSpacing));
57860
+ const spread = pxToRem2(scalePx(inner.spread, baseSpacing));
57861
+ return `0 ${y} ${blur} ${spread} rgb(0 0 0 / ${inner.opacity})`;
57862
+ }
57863
+ function buildCompositeFromVars(prefix, innerValue) {
57864
+ const primary = SHADOW_PARTS.map((part) => `var(--rafters-${prefix}-${part})`).join(" ");
57865
+ return innerValue ? `${primary}, ${innerValue}` : primary;
57805
57866
  }
57806
57867
  function generateShadowTokens(config3, shadowDefs) {
57807
57868
  const tokens = [];
@@ -57826,23 +57887,65 @@ function generateShadowTokens(config3, shadowDefs) {
57826
57887
  const def = shadowDefs[scale3];
57827
57888
  if (!def) continue;
57828
57889
  const scaleIndex = SHADOW_SCALE.indexOf(scale3);
57829
- const value2 = generateShadowValue(def, baseSpacingUnit);
57890
+ const scaleName = scale3 === "DEFAULT" ? "shadow" : `shadow-${scale3}`;
57891
+ if (def.opacity === 0) {
57892
+ tokens.push({
57893
+ name: scaleName,
57894
+ value: "none",
57895
+ category: "shadow",
57896
+ namespace: "shadow",
57897
+ semanticMeaning: def.meaning,
57898
+ usageContext: def.contexts,
57899
+ scalePosition: scaleIndex,
57900
+ progressionSystem: progressionRatio,
57901
+ dependsOn: [],
57902
+ description: `Shadow ${scale3}: ${def.meaning}`,
57903
+ generatedAt: timestamp,
57904
+ containerQueryAware: false,
57905
+ usagePatterns: {
57906
+ do: ["Use for flat elements", "Use for disabled states"],
57907
+ never: ["Use on interactive elements that need depth feedback"]
57908
+ }
57909
+ });
57910
+ continue;
57911
+ }
57912
+ const parts = resolveShadowParts(def, baseSpacingUnit);
57913
+ const partDeps = [];
57914
+ for (const part of SHADOW_PARTS) {
57915
+ const partName = `${scaleName}-${part}`;
57916
+ partDeps.push(partName);
57917
+ tokens.push({
57918
+ name: partName,
57919
+ value: parts[part],
57920
+ category: "shadow",
57921
+ namespace: "shadow",
57922
+ semanticMeaning: `${part} component of ${scale3} shadow`,
57923
+ usageContext: ["designer-override"],
57924
+ scalePosition: scaleIndex,
57925
+ dependsOn: ["shadow-base-unit"],
57926
+ description: `Shadow ${scale3} ${part}: ${parts[part]}. Override to customize this shadow layer.`,
57927
+ generatedAt: timestamp,
57928
+ containerQueryAware: false
57929
+ });
57930
+ }
57931
+ const innerValue = def.innerShadow && def.innerShadow.opacity > 0 ? generateInnerShadowValue(def.innerShadow, baseSpacingUnit) : null;
57932
+ const compositeValue = buildCompositeFromVars(scaleName, innerValue);
57830
57933
  tokens.push({
57831
- name: scale3 === "DEFAULT" ? "shadow" : `shadow-${scale3}`,
57832
- value: value2,
57934
+ name: scaleName,
57935
+ value: compositeValue,
57833
57936
  category: "shadow",
57834
57937
  namespace: "shadow",
57835
57938
  semanticMeaning: def.meaning,
57836
57939
  usageContext: def.contexts,
57837
57940
  scalePosition: scaleIndex,
57838
57941
  progressionSystem: progressionRatio,
57839
- dependsOn: scale3 === "none" ? [] : ["shadow-base-unit"],
57840
- description: `Shadow ${scale3}: ${def.meaning}. Y: ${def.yOffset}\xD7base, Blur: ${def.blur}\xD7base`,
57942
+ dependsOn: partDeps,
57943
+ description: `Shadow ${scale3}: ${def.meaning}. Composed from var() refs to ${scaleName}-* tokens.`,
57841
57944
  generatedAt: timestamp,
57842
57945
  containerQueryAware: false,
57843
57946
  usagePatterns: {
57844
- do: scale3 === "none" ? ["Use for flat elements", "Use for disabled states"] : scaleIndex <= 2 ? ["Use for subtle depth", "Use for cards at rest"] : scaleIndex <= 4 ? ["Use for hovering elements", "Use for focus states"] : ["Use for floating elements", "Use for modals"],
57845
- never: scale3 === "none" ? ["Use on interactive elements that need depth feedback"] : ["Use shadows that don't match element's semantic depth"]
57947
+ do: scaleIndex <= 2 ? ["Use for subtle depth", "Use for cards at rest"] : scaleIndex <= 4 ? ["Use for hovering elements", "Use for focus states"] : ["Use for floating elements", "Use for modals"],
57948
+ never: ["Use shadows that don't match element's semantic depth"]
57846
57949
  }
57847
57950
  });
57848
57951
  }
@@ -57853,27 +57956,33 @@ function generateShadowTokens(config3, shadowDefs) {
57853
57956
  {
57854
57957
  name: "shadow-primary",
57855
57958
  desc: "Primary colored shadow for emphasis",
57856
- color: "var(--primary)"
57959
+ color: "var(--primary)",
57960
+ colorToken: "primary"
57857
57961
  },
57858
57962
  {
57859
57963
  name: "shadow-destructive",
57860
57964
  desc: "Destructive colored shadow for warnings",
57861
- color: "var(--destructive)"
57965
+ color: "var(--destructive)",
57966
+ colorToken: "destructive"
57862
57967
  }
57863
57968
  ];
57864
- for (const { name: name2, desc, color } of coloredShadows) {
57865
- const yOffsetPx = Math.round(baseDef.yOffset * baseSpacingUnit * 100) / 100;
57866
- const blurPx = Math.round(baseDef.blur * baseSpacingUnit * 100) / 100;
57867
- const spreadPx = Math.round(baseDef.spread * baseSpacingUnit * 100) / 100;
57969
+ for (const { name: name2, desc, color, colorToken } of coloredShadows) {
57970
+ const value2 = `var(--rafters-shadow-offset-x) var(--rafters-shadow-offset-y) var(--rafters-shadow-blur) var(--rafters-shadow-spread) color-mix(in oklch, ${color} ${coloredOpacity * 100}%, transparent)`;
57868
57971
  tokens.push({
57869
57972
  name: name2,
57870
- value: `0 ${pxToRem2(yOffsetPx)} ${pxToRem2(blurPx)} ${pxToRem2(spreadPx)} color-mix(in oklch, ${color} ${coloredOpacity * 100}%, transparent)`,
57973
+ value: value2,
57871
57974
  category: "shadow",
57872
57975
  namespace: "shadow",
57873
57976
  semanticMeaning: desc,
57874
57977
  usageContext: ["branded-elements", "emphasis"],
57875
- dependsOn: ["shadow", color.replace("var(--", "").replace(")", "")],
57876
- description: `${desc}. Uses color-mix for proper OKLCH blending.`,
57978
+ dependsOn: [
57979
+ "shadow-offset-x",
57980
+ "shadow-offset-y",
57981
+ "shadow-blur",
57982
+ "shadow-spread",
57983
+ colorToken
57984
+ ],
57985
+ description: `${desc}. Reuses DEFAULT shadow geometry, swaps color via color-mix.`,
57877
57986
  generatedAt: timestamp,
57878
57987
  containerQueryAware: false
57879
57988
  });
@@ -59849,6 +59958,7 @@ When onboarding an existing project, do NOT skip the learning step.
59849
59958
  6. The 11 semantic families (primary, secondary, tertiary, accent, neutral, success, warning, destructive, info, highlight, muted) MUST all be mapped. Extra colors are custom families in the color namespace.
59850
59959
  7. If analyze detects color scale patterns (e.g., --color-blaze-50 through --color-blaze-950), map the family using its base color. Do NOT create 11 individual tokens.
59851
59960
  8. Call rafters_onboard map to execute the migration once the designer confirms. Colors are automatically enriched with full OKLCH scales, harmonies, accessibility data, and API intelligence.
59961
+ 9. AFTER mapping color families, check if semantic surface tokens (background, foreground, card, popover, etc.) need remapping. By default they reference "neutral" family. For dark-themed sites or custom palettes, remap them using the "light" and "dark" fields in the mapping: { source: "--bg", target: "background", light: "neutral-50", dark: "custom-dark-950", reason: "..." }. The light/dark values are "family-position" references to existing color tokens. Map color families FIRST, then remap semantic tokens that reference them.
59852
59962
 
59853
59963
  When in doubt: less code, not more. Rafters has already made the design decision.`;
59854
59964
  var CONSUMER_QUICKSTART = {
@@ -60269,7 +60379,7 @@ var TOOL_DEFINITIONS = [
60269
60379
  },
60270
60380
  {
60271
60381
  name: "rafters_onboard",
60272
- description: 'Analyze an existing project for design decisions and map them into Rafters tokens. Use "analyze" to surface raw findings. Use "map" to execute -- but map REQUIRES the designer to confirm every mapping first. The tool will reject unconfirmed mappings and instruct you to ask the designer. This is an intentional system, not an automatic one.',
60382
+ description: 'Analyze an existing project for design decisions and map them into Rafters tokens. Use "analyze" to surface raw findings. Use "map" to execute -- but map REQUIRES the designer to confirm every mapping first. The tool will reject unconfirmed mappings and instruct you to ask the designer. Supports two mapping types: (1) color family mapping with "value" field (enriches CSS color into full ColorValue), (2) semantic remapping with "light"/"dark" fields (remaps which color family+position a semantic token like "background" or "card" references for light/dark mode). Map color families first, then remap semantic surface tokens.',
60273
60383
  inputSchema: {
60274
60384
  type: "object",
60275
60385
  properties: {
@@ -60297,7 +60407,15 @@ var TOOL_DEFINITIONS = [
60297
60407
  },
60298
60408
  value: {
60299
60409
  type: "string",
60300
- description: "The value to set (from the original CSS)"
60410
+ description: "The CSS color value to set (from the original CSS). Required for color family mappings. Omit for semantic remappings that use light/dark instead."
60411
+ },
60412
+ light: {
60413
+ type: "string",
60414
+ description: 'Light mode color reference as "family-position" (e.g., "neutral-50"). Used for semantic token remapping -- tells the token which color family+position to use in light mode.'
60415
+ },
60416
+ dark: {
60417
+ type: "string",
60418
+ description: 'Dark mode color reference as "family-position" (e.g., "neutral-950"). Used for semantic token remapping -- tells the token which color family+position to use in dark mode.'
60301
60419
  },
60302
60420
  reason: {
60303
60421
  type: "string",
@@ -60312,7 +60430,7 @@ var TOOL_DEFINITIONS = [
60312
60430
  description: "Category for new tokens (required if token does not exist)"
60313
60431
  }
60314
60432
  },
60315
- required: ["source", "target", "value", "reason"]
60433
+ required: ["source", "target", "reason"]
60316
60434
  },
60317
60435
  description: "Array of mappings to execute (required for map action)"
60318
60436
  }
@@ -60601,12 +60719,32 @@ var RaftersToolHandler = class _RaftersToolHandler {
60601
60719
  return join10(this.projectRoot, "packages/ui/src/components/ui");
60602
60720
  }
60603
60721
  /**
60604
- * Load component metadata from source file
60722
+ * Find the actual component file on disk for a given name.
60723
+ * Checks the config's componentTarget extension first, then falls back
60724
+ * to all known extensions so Astro projects with React islands also work.
60725
+ */
60726
+ async resolveComponentFile(componentsPath, name2) {
60727
+ const config3 = await this.loadConfig();
60728
+ const preferredExt = targetToExtension(resolveComponentTarget(config3));
60729
+ const preferred = join10(componentsPath, `${name2}${preferredExt}`);
60730
+ if (existsSync4(preferred)) return preferred;
60731
+ for (const ext2 of COMPONENT_EXTENSIONS) {
60732
+ if (ext2 === preferredExt) continue;
60733
+ const candidate = join10(componentsPath, `${name2}${ext2}`);
60734
+ if (existsSync4(candidate)) return candidate;
60735
+ }
60736
+ return null;
60737
+ }
60738
+ /**
60739
+ * Load component metadata from source file.
60740
+ * Resolves the actual file extension from the config's componentTarget
60741
+ * and merges variant/size data from .classes.ts companions when present.
60605
60742
  */
60606
60743
  async loadComponentMetadata(name2) {
60607
60744
  const componentsPath = await this.getComponentsPath();
60608
60745
  if (!componentsPath) return null;
60609
- const filePath = join10(componentsPath, `${name2}.tsx`);
60746
+ const filePath = await this.resolveComponentFile(componentsPath, name2);
60747
+ if (!filePath) return null;
60610
60748
  try {
60611
60749
  const source = await readFile6(filePath, "utf-8");
60612
60750
  const intelligence = parseJSDocIntelligence(source);
@@ -60616,16 +60754,28 @@ var RaftersToolHandler = class _RaftersToolHandler {
60616
60754
  jsDocDeps = extractJSDocDependencies(source);
60617
60755
  } catch {
60618
60756
  }
60757
+ let variants = extractVariants(source);
60758
+ let sizes = extractSizes(source);
60759
+ try {
60760
+ const classesSource = await readFile6(join10(componentsPath, `${name2}.classes.ts`), "utf-8");
60761
+ if (!variants || variants.length === 0) {
60762
+ variants = extractVariants(classesSource);
60763
+ }
60764
+ if (!sizes || sizes.length === 0) {
60765
+ sizes = extractSizes(classesSource);
60766
+ }
60767
+ } catch {
60768
+ }
60619
60769
  const metadata = {
60620
60770
  name: name2,
60621
60771
  displayName: toDisplayName(name2),
60622
60772
  category: this.inferCategory(name2),
60623
- variants: extractVariants(source),
60624
- sizes: extractSizes(source),
60773
+ variants,
60774
+ sizes,
60625
60775
  dependencies: extractDependencies(source),
60626
60776
  primitives: extractPrimitiveDependencies(source),
60627
60777
  // projectRoot is guaranteed non-null here: handleToolCall guards it
60628
- filePath: relative2(this.projectRoot, join10(componentsPath, `${name2}.tsx`))
60778
+ filePath: relative2(this.projectRoot, filePath)
60629
60779
  };
60630
60780
  if (hasAnyDeps(jsDocDeps)) {
60631
60781
  metadata.jsDocDependencies = jsDocDeps;
@@ -60722,7 +60872,12 @@ var RaftersToolHandler = class _RaftersToolHandler {
60722
60872
  if (componentsPath) {
60723
60873
  try {
60724
60874
  const files = await readdir3(componentsPath);
60725
- available = files.filter((f) => f.endsWith(".tsx")).map((f) => basename(f, ".tsx")).filter((n2) => n2.includes(name2) || name2.includes(n2)).slice(0, 5);
60875
+ available = files.filter(
60876
+ (f) => COMPONENT_EXTENSIONS.some((ext2) => f.endsWith(ext2)) && !f.includes(".classes.")
60877
+ ).map((f) => {
60878
+ const ext2 = COMPONENT_EXTENSIONS.find((e) => f.endsWith(e));
60879
+ return ext2 ? basename(f, ext2) : basename(f);
60880
+ }).filter((n2) => n2.includes(name2) || name2.includes(n2)).slice(0, 5);
60726
60881
  } catch {
60727
60882
  }
60728
60883
  }
@@ -61308,6 +61463,20 @@ var RaftersToolHandler = class _RaftersToolHandler {
61308
61463
  (s) => s.status === "designer"
61309
61464
  ).length;
61310
61465
  const totalFamilies = _RaftersToolHandler.SEMANTIC_FAMILIES.length;
61466
+ const hasDarkMode = cssFindings.some(
61467
+ (f) => f.customProperties.some(
61468
+ (p2) => p2.context === ".dark" || p2.context === "prefers-color-scheme: dark"
61469
+ )
61470
+ );
61471
+ let guidance;
61472
+ if (detectedFamilies.length > 0) {
61473
+ guidance = `Found ${detectedFamilies.length} color scale pattern(s): ${detectedFamilies.map((f) => f.family).join(", ")}. Map each family using its base color (position 500/600), not individual scale positions. buildColorValue() will regenerate the full 11-step scale.`;
61474
+ } else {
61475
+ guidance = 'Review the custom properties above. Map each to a rafters token using rafters_onboard with action: "map". For ambiguous decisions, ask the designer.';
61476
+ }
61477
+ if (hasDarkMode) {
61478
+ guidance += ' DARK MODE DETECTED: After mapping color families, remap semantic surface tokens (background, foreground, card, popover, etc.) to point at the correct family+position for dark mode. Use the "light" and "dark" fields in the mapping instead of "value". Default semantic tokens reference "neutral" family -- if the project uses a different dark palette, these must be remapped.';
61479
+ }
61311
61480
  const result = {
61312
61481
  framework,
61313
61482
  cssFiles: cssFindings,
@@ -61317,7 +61486,8 @@ var RaftersToolHandler = class _RaftersToolHandler {
61317
61486
  shadcn,
61318
61487
  designDependencies: designDeps,
61319
61488
  existingTokenCount,
61320
- guidance: detectedFamilies.length > 0 ? `Found ${detectedFamilies.length} color scale pattern(s): ${detectedFamilies.map((f) => f.family).join(", ")}. Map each family using its base color (position 500/600), not individual scale positions. buildColorValue() will regenerate the full 11-step scale.` : 'Review the custom properties above. Map each to a rafters token using rafters_onboard with action: "map". For ambiguous decisions, ask the designer.'
61489
+ hasDarkMode,
61490
+ guidance
61321
61491
  };
61322
61492
  return {
61323
61493
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -61496,15 +61666,145 @@ var RaftersToolHandler = class _RaftersToolHandler {
61496
61666
  const registry2 = new TokenRegistry(allTokens);
61497
61667
  registry2.setAdapter(this.adapter);
61498
61668
  const results = [];
61669
+ const parseRef = (ref) => {
61670
+ const lastDash = ref.lastIndexOf("-");
61671
+ if (lastDash <= 0) return null;
61672
+ const position = ref.slice(lastDash + 1);
61673
+ const family = ref.slice(0, lastDash);
61674
+ if (!/^(50|100|200|300|400|500|600|700|800|900|950)$/.test(position)) return null;
61675
+ return { family, position };
61676
+ };
61499
61677
  for (const mapping of mappings) {
61500
61678
  const { source, target, value: value2, reason, namespace, category } = mapping;
61501
- if (!source || !target || !value2 || !reason) {
61679
+ const lightRef = mapping.light;
61680
+ const darkRef = mapping.dark;
61681
+ if (!source || !target || !reason) {
61502
61682
  results.push({
61503
61683
  source: source ?? "?",
61504
61684
  target: target ?? "?",
61505
61685
  action: "skipped",
61506
61686
  ok: false,
61507
- error: "Missing required fields: source, target, value, reason"
61687
+ error: "Missing required fields: source, target, reason"
61688
+ });
61689
+ continue;
61690
+ }
61691
+ if (lightRef || darkRef) {
61692
+ const existing = registry2.get(target);
61693
+ if (!existing) {
61694
+ results.push({
61695
+ source,
61696
+ target,
61697
+ action: "skipped",
61698
+ ok: false,
61699
+ error: `Semantic token "${target}" not found. Only existing semantic tokens can be remapped.`
61700
+ });
61701
+ continue;
61702
+ }
61703
+ const currentValue = existing.value;
61704
+ const currentLight = typeof currentValue === "object" && currentValue !== null && "family" in currentValue ? currentValue : null;
61705
+ const newLight = lightRef ? parseRef(lightRef) : currentLight;
61706
+ if (lightRef && !newLight) {
61707
+ results.push({
61708
+ source,
61709
+ target,
61710
+ action: "skipped",
61711
+ ok: false,
61712
+ error: `Invalid light reference "${lightRef}". Use format "family-position" (e.g., "neutral-50"). Valid positions: 50, 100-900 by 100, 950.`
61713
+ });
61714
+ continue;
61715
+ }
61716
+ if (!lightRef && !currentLight) {
61717
+ results.push({
61718
+ source,
61719
+ target,
61720
+ action: "skipped",
61721
+ ok: false,
61722
+ error: `Token "${target}" has no existing ColorReference value. You must supply an explicit "light" field to remap it.`
61723
+ });
61724
+ continue;
61725
+ }
61726
+ const darkFallbackRef = existing.dependsOn?.[1];
61727
+ const newDark = darkRef ? parseRef(darkRef) : darkFallbackRef ? parseRef(darkFallbackRef) : newLight;
61728
+ if (darkRef && !newDark) {
61729
+ results.push({
61730
+ source,
61731
+ target,
61732
+ action: "skipped",
61733
+ ok: false,
61734
+ error: `Invalid dark reference "${darkRef}". Use format "family-position" (e.g., "neutral-950"). Valid positions: 50, 100-900 by 100, 950.`
61735
+ });
61736
+ continue;
61737
+ }
61738
+ if (!darkRef && darkFallbackRef && !newDark) {
61739
+ results.push({
61740
+ source,
61741
+ target,
61742
+ action: "skipped",
61743
+ ok: false,
61744
+ error: `Could not infer dark reference from existing dependsOn value "${darkFallbackRef}". Supply an explicit "dark" field.`
61745
+ });
61746
+ continue;
61747
+ }
61748
+ if (!newLight || !newDark) {
61749
+ results.push({
61750
+ source,
61751
+ target,
61752
+ action: "skipped",
61753
+ ok: false,
61754
+ error: `Could not resolve light/dark references for "${target}". Supply explicit "light" and "dark" fields.`
61755
+ });
61756
+ continue;
61757
+ }
61758
+ const lightTokenName = `${newLight.family}-${newLight.position}`;
61759
+ const darkTokenName = `${newDark.family}-${newDark.position}`;
61760
+ const missingRefs = [];
61761
+ if (!registry2.has(lightTokenName)) missingRefs.push(lightTokenName);
61762
+ if (!registry2.has(darkTokenName)) missingRefs.push(darkTokenName);
61763
+ if (missingRefs.length > 0) {
61764
+ results.push({
61765
+ source,
61766
+ target,
61767
+ action: "skipped",
61768
+ ok: false,
61769
+ error: `Referenced color tokens not found: ${missingRefs.join(", ")}. Map the color family first, then remap the semantic token.`
61770
+ });
61771
+ continue;
61772
+ }
61773
+ const previousValue = typeof existing.value === "string" ? existing.value : JSON.stringify(existing.value);
61774
+ existing.userOverride = {
61775
+ previousValue,
61776
+ reason: `Remapped from ${source}: ${reason}`
61777
+ };
61778
+ const newColorRef = { family: newLight.family, position: newLight.position };
61779
+ await registry2.set(target, newColorRef);
61780
+ const updated = registry2.get(target);
61781
+ if (updated) {
61782
+ updated.dependsOn = [lightTokenName, darkTokenName];
61783
+ } else {
61784
+ results.push({
61785
+ source,
61786
+ target,
61787
+ action: "skipped",
61788
+ ok: false,
61789
+ error: `Token "${target}" was set but could not be retrieved from registry to update dependencies.`
61790
+ });
61791
+ continue;
61792
+ }
61793
+ results.push({
61794
+ source,
61795
+ target,
61796
+ action: "remap",
61797
+ ok: true
61798
+ });
61799
+ continue;
61800
+ }
61801
+ if (!value2) {
61802
+ results.push({
61803
+ source,
61804
+ target,
61805
+ action: "skipped",
61806
+ ok: false,
61807
+ error: 'Missing "value" for color family mapping. Provide a CSS color value, or use "light"/"dark" for semantic remapping.'
61508
61808
  });
61509
61809
  continue;
61510
61810
  }
@@ -61528,6 +61828,14 @@ var RaftersToolHandler = class _RaftersToolHandler {
61528
61828
  };
61529
61829
  await registry2.set(target, tokenValue);
61530
61830
  results.push({ source, target, action: "set", ok: true, enriched });
61831
+ } else {
61832
+ results.push({
61833
+ source,
61834
+ target,
61835
+ action: "skipped",
61836
+ ok: false,
61837
+ error: `Token "${target}" exists in registry index but could not be retrieved.`
61838
+ });
61531
61839
  }
61532
61840
  } else {
61533
61841
  const ns = namespace ?? (enriched ? "color" : "custom");
@@ -61551,6 +61859,7 @@ var RaftersToolHandler = class _RaftersToolHandler {
61551
61859
  await this.regenerateOutputs(registry2);
61552
61860
  const setCount = results.filter((r) => r.action === "set" && r.ok).length;
61553
61861
  const createCount = results.filter((r) => r.action === "create" && r.ok).length;
61862
+ const remapCount = results.filter((r) => r.action === "remap" && r.ok).length;
61554
61863
  const enrichedCount = results.filter((r) => r.enriched).length;
61555
61864
  const failCount = results.filter((r) => !r.ok).length;
61556
61865
  return {
@@ -61563,6 +61872,7 @@ var RaftersToolHandler = class _RaftersToolHandler {
61563
61872
  summary: {
61564
61873
  set: setCount,
61565
61874
  created: createCount,
61875
+ remapped: remapCount,
61566
61876
  enriched: enrichedCount,
61567
61877
  failed: failCount
61568
61878
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "description": "CLI for Rafters design system - scaffold tokens and add components",
5
5
  "license": "MIT",
6
6
  "type": "module",