rafters 0.0.37 → 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 +315 -46
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -46768,6 +46768,13 @@ var DEFAULT_SEMANTIC_COLOR_MAPPINGS = {
46768
46768
  };
46769
46769
 
46770
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
+ }
46771
46778
  function colorRefToString(ref) {
46772
46779
  return `${ref.family}-${ref.position}`;
46773
46780
  }
@@ -46965,8 +46972,12 @@ function generateThemeBlock(groups) {
46965
46972
  for (const token of groups.shadow) {
46966
46973
  const value2 = tokenValueToCSS(token);
46967
46974
  if (value2 === null) continue;
46968
- const key = token.name.replace(/^shadow-/, "");
46969
- 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
+ }
46970
46981
  }
46971
46982
  lines.push("");
46972
46983
  }
@@ -47010,6 +47021,7 @@ function generateThemeBlock(groups) {
47010
47021
  for (const token of groups.breakpoint) {
47011
47022
  const value2 = tokenValueToCSS(token);
47012
47023
  if (value2 === null) continue;
47024
+ if (isMediaQueryToken(token)) continue;
47013
47025
  lines.push(` --${token.name}: ${value2};`);
47014
47026
  }
47015
47027
  lines.push("");
@@ -47218,7 +47230,7 @@ function generateVarsRootBlock(groups) {
47218
47230
  for (const token of groups.shadow) {
47219
47231
  const value2 = tokenValueToCSS(token);
47220
47232
  if (value2 === null) continue;
47221
- lines.push(` --rafters-shadow-${token.name}: ${value2};`);
47233
+ lines.push(` --rafters-${token.name}: ${value2};`);
47222
47234
  }
47223
47235
  lines.push("");
47224
47236
  }
@@ -47260,6 +47272,7 @@ function generateVarsRootBlock(groups) {
47260
47272
  for (const token of groups.breakpoint) {
47261
47273
  const value2 = tokenValueToCSS(token);
47262
47274
  if (value2 === null) continue;
47275
+ if (isMediaQueryToken(token)) continue;
47263
47276
  lines.push(` --rafters-${token.name}: ${value2};`);
47264
47277
  }
47265
47278
  lines.push("");
@@ -57673,6 +57686,13 @@ function generateMotionTokens(config3, durationDefs, easingDefs, delayDefs) {
57673
57686
  }
57674
57687
 
57675
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
+ };
57676
57696
  function generateRadiusTokens(config3, radiusDefs) {
57677
57697
  const tokens = [];
57678
57698
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -57695,6 +57715,20 @@ function generateRadiusTokens(config3, radiusDefs) {
57695
57715
  never: ["Change without understanding scale impact"]
57696
57716
  }
57697
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
+ }
57698
57732
  for (const scale3 of RADIUS_SCALE) {
57699
57733
  const def = radiusDefs[scale3];
57700
57734
  if (!def) continue;
@@ -57715,8 +57749,9 @@ function generateRadiusTokens(config3, radiusDefs) {
57715
57749
  value2 = `calc(var(--rafters-radius-base) * ${multiplier})`;
57716
57750
  mathRelationship = `base \xD7 ${progression.ratio}^${def.step} (\xD7${multiplier})`;
57717
57751
  }
57752
+ const scaleName = scale3 === "DEFAULT" ? "radius" : `radius-${scale3}`;
57718
57753
  tokens.push({
57719
- name: scale3 === "DEFAULT" ? "radius" : `radius-${scale3}`,
57754
+ name: scaleName,
57720
57755
  value: value2,
57721
57756
  category: "radius",
57722
57757
  namespace: "radius",
@@ -57730,6 +57765,25 @@ function generateRadiusTokens(config3, radiusDefs) {
57730
57765
  generatedAt: timestamp,
57731
57766
  containerQueryAware: false
57732
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
+ }
57733
57787
  }
57734
57788
  return {
57735
57789
  namespace: "radius",
@@ -57786,26 +57840,29 @@ function pxToRem2(px) {
57786
57840
  const rem = Math.round(px / 16 * 1e3) / 1e3;
57787
57841
  return `${rem}rem`;
57788
57842
  }
57789
- function generateShadowValue(def, baseSpacing) {
57790
- if (def.opacity === 0) {
57791
- return "none";
57792
- }
57793
- const shadows = [];
57794
- const yOffsetPx = Math.round(def.yOffset * baseSpacing * 100) / 100;
57795
- const blurPx = Math.round(def.blur * baseSpacing * 100) / 100;
57796
- const spreadPx = Math.round(def.spread * baseSpacing * 100) / 100;
57797
- shadows.push(
57798
- `0 ${pxToRem2(yOffsetPx)} ${pxToRem2(blurPx)} ${pxToRem2(spreadPx)} rgb(0 0 0 / ${def.opacity})`
57799
- );
57800
- if (def.innerShadow) {
57801
- const innerYPx = Math.round(def.innerShadow.yOffset * baseSpacing * 100) / 100;
57802
- const innerBlurPx = Math.round(def.innerShadow.blur * baseSpacing * 100) / 100;
57803
- const innerSpreadPx = Math.round(def.innerShadow.spread * baseSpacing * 100) / 100;
57804
- shadows.push(
57805
- `0 ${pxToRem2(innerYPx)} ${pxToRem2(innerBlurPx)} ${pxToRem2(innerSpreadPx)} rgb(0 0 0 / ${def.innerShadow.opacity})`
57806
- );
57807
- }
57808
- 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;
57809
57866
  }
57810
57867
  function generateShadowTokens(config3, shadowDefs) {
57811
57868
  const tokens = [];
@@ -57830,23 +57887,65 @@ function generateShadowTokens(config3, shadowDefs) {
57830
57887
  const def = shadowDefs[scale3];
57831
57888
  if (!def) continue;
57832
57889
  const scaleIndex = SHADOW_SCALE.indexOf(scale3);
57833
- 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);
57834
57933
  tokens.push({
57835
- name: scale3 === "DEFAULT" ? "shadow" : `shadow-${scale3}`,
57836
- value: value2,
57934
+ name: scaleName,
57935
+ value: compositeValue,
57837
57936
  category: "shadow",
57838
57937
  namespace: "shadow",
57839
57938
  semanticMeaning: def.meaning,
57840
57939
  usageContext: def.contexts,
57841
57940
  scalePosition: scaleIndex,
57842
57941
  progressionSystem: progressionRatio,
57843
- dependsOn: scale3 === "none" ? [] : ["shadow-base-unit"],
57844
- 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.`,
57845
57944
  generatedAt: timestamp,
57846
57945
  containerQueryAware: false,
57847
57946
  usagePatterns: {
57848
- 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"],
57849
- 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"]
57850
57949
  }
57851
57950
  });
57852
57951
  }
@@ -57857,27 +57956,33 @@ function generateShadowTokens(config3, shadowDefs) {
57857
57956
  {
57858
57957
  name: "shadow-primary",
57859
57958
  desc: "Primary colored shadow for emphasis",
57860
- color: "var(--primary)"
57959
+ color: "var(--primary)",
57960
+ colorToken: "primary"
57861
57961
  },
57862
57962
  {
57863
57963
  name: "shadow-destructive",
57864
57964
  desc: "Destructive colored shadow for warnings",
57865
- color: "var(--destructive)"
57965
+ color: "var(--destructive)",
57966
+ colorToken: "destructive"
57866
57967
  }
57867
57968
  ];
57868
- for (const { name: name2, desc, color } of coloredShadows) {
57869
- const yOffsetPx = Math.round(baseDef.yOffset * baseSpacingUnit * 100) / 100;
57870
- const blurPx = Math.round(baseDef.blur * baseSpacingUnit * 100) / 100;
57871
- 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)`;
57872
57971
  tokens.push({
57873
57972
  name: name2,
57874
- value: `0 ${pxToRem2(yOffsetPx)} ${pxToRem2(blurPx)} ${pxToRem2(spreadPx)} color-mix(in oklch, ${color} ${coloredOpacity * 100}%, transparent)`,
57973
+ value: value2,
57875
57974
  category: "shadow",
57876
57975
  namespace: "shadow",
57877
57976
  semanticMeaning: desc,
57878
57977
  usageContext: ["branded-elements", "emphasis"],
57879
- dependsOn: ["shadow", color.replace("var(--", "").replace(")", "")],
57880
- 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.`,
57881
57986
  generatedAt: timestamp,
57882
57987
  containerQueryAware: false
57883
57988
  });
@@ -59853,6 +59958,7 @@ When onboarding an existing project, do NOT skip the learning step.
59853
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.
59854
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.
59855
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.
59856
59962
 
59857
59963
  When in doubt: less code, not more. Rafters has already made the design decision.`;
59858
59964
  var CONSUMER_QUICKSTART = {
@@ -60273,7 +60379,7 @@ var TOOL_DEFINITIONS = [
60273
60379
  },
60274
60380
  {
60275
60381
  name: "rafters_onboard",
60276
- 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.',
60277
60383
  inputSchema: {
60278
60384
  type: "object",
60279
60385
  properties: {
@@ -60301,7 +60407,15 @@ var TOOL_DEFINITIONS = [
60301
60407
  },
60302
60408
  value: {
60303
60409
  type: "string",
60304
- 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.'
60305
60419
  },
60306
60420
  reason: {
60307
60421
  type: "string",
@@ -60316,7 +60430,7 @@ var TOOL_DEFINITIONS = [
60316
60430
  description: "Category for new tokens (required if token does not exist)"
60317
60431
  }
60318
60432
  },
60319
- required: ["source", "target", "value", "reason"]
60433
+ required: ["source", "target", "reason"]
60320
60434
  },
60321
60435
  description: "Array of mappings to execute (required for map action)"
60322
60436
  }
@@ -61349,6 +61463,20 @@ var RaftersToolHandler = class _RaftersToolHandler {
61349
61463
  (s) => s.status === "designer"
61350
61464
  ).length;
61351
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
+ }
61352
61480
  const result = {
61353
61481
  framework,
61354
61482
  cssFiles: cssFindings,
@@ -61358,7 +61486,8 @@ var RaftersToolHandler = class _RaftersToolHandler {
61358
61486
  shadcn,
61359
61487
  designDependencies: designDeps,
61360
61488
  existingTokenCount,
61361
- 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
61362
61491
  };
61363
61492
  return {
61364
61493
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -61537,15 +61666,145 @@ var RaftersToolHandler = class _RaftersToolHandler {
61537
61666
  const registry2 = new TokenRegistry(allTokens);
61538
61667
  registry2.setAdapter(this.adapter);
61539
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
+ };
61540
61677
  for (const mapping of mappings) {
61541
61678
  const { source, target, value: value2, reason, namespace, category } = mapping;
61542
- if (!source || !target || !value2 || !reason) {
61679
+ const lightRef = mapping.light;
61680
+ const darkRef = mapping.dark;
61681
+ if (!source || !target || !reason) {
61543
61682
  results.push({
61544
61683
  source: source ?? "?",
61545
61684
  target: target ?? "?",
61546
61685
  action: "skipped",
61547
61686
  ok: false,
61548
- 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.'
61549
61808
  });
61550
61809
  continue;
61551
61810
  }
@@ -61569,6 +61828,14 @@ var RaftersToolHandler = class _RaftersToolHandler {
61569
61828
  };
61570
61829
  await registry2.set(target, tokenValue);
61571
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
+ });
61572
61839
  }
61573
61840
  } else {
61574
61841
  const ns = namespace ?? (enriched ? "color" : "custom");
@@ -61592,6 +61859,7 @@ var RaftersToolHandler = class _RaftersToolHandler {
61592
61859
  await this.regenerateOutputs(registry2);
61593
61860
  const setCount = results.filter((r) => r.action === "set" && r.ok).length;
61594
61861
  const createCount = results.filter((r) => r.action === "create" && r.ok).length;
61862
+ const remapCount = results.filter((r) => r.action === "remap" && r.ok).length;
61595
61863
  const enrichedCount = results.filter((r) => r.enriched).length;
61596
61864
  const failCount = results.filter((r) => !r.ok).length;
61597
61865
  return {
@@ -61604,6 +61872,7 @@ var RaftersToolHandler = class _RaftersToolHandler {
61604
61872
  summary: {
61605
61873
  set: setCount,
61606
61874
  created: createCount,
61875
+ remapped: remapCount,
61607
61876
  enriched: enrichedCount,
61608
61877
  failed: failCount
61609
61878
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.37",
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",