rafters 0.0.37 → 0.0.39

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 +414 -60
  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 = {
@@ -60246,6 +60352,10 @@ var TOOL_DEFINITIONS = [
60246
60352
  category: {
60247
60353
  type: "string",
60248
60354
  description: 'Token category (required for create). E.g., "color", "spacing", "font".'
60355
+ },
60356
+ dark: {
60357
+ type: "string",
60358
+ description: 'Dark mode color reference as "family-position" (e.g., "neutral-950"). For semantic tokens, sets dependsOn[1] so the CSS dark mode layer uses this reference instead of the light value.'
60249
60359
  }
60250
60360
  },
60251
60361
  required: ["name"]
@@ -60273,7 +60383,7 @@ var TOOL_DEFINITIONS = [
60273
60383
  },
60274
60384
  {
60275
60385
  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.',
60386
+ 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
60387
  inputSchema: {
60278
60388
  type: "object",
60279
60389
  properties: {
@@ -60301,7 +60411,15 @@ var TOOL_DEFINITIONS = [
60301
60411
  },
60302
60412
  value: {
60303
60413
  type: "string",
60304
- description: "The value to set (from the original CSS)"
60414
+ 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."
60415
+ },
60416
+ light: {
60417
+ type: "string",
60418
+ 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.'
60419
+ },
60420
+ dark: {
60421
+ type: "string",
60422
+ 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
60423
  },
60306
60424
  reason: {
60307
60425
  type: "string",
@@ -60316,7 +60434,7 @@ var TOOL_DEFINITIONS = [
60316
60434
  description: "Category for new tokens (required if token does not exist)"
60317
60435
  }
60318
60436
  },
60319
- required: ["source", "target", "value", "reason"]
60437
+ required: ["source", "target", "reason"]
60320
60438
  },
60321
60439
  description: "Array of mappings to execute (required for map action)"
60322
60440
  }
@@ -60936,6 +61054,19 @@ var RaftersToolHandler = class _RaftersToolHandler {
60936
61054
  }
60937
61055
  }
60938
61056
  // ==================== Token Write Operations ====================
61057
+ static VALID_SCALE_POSITIONS = /^(50|100|200|300|400|500|600|700|800|900|950)$/;
61058
+ /**
61059
+ * Parse "family-position" string (e.g., "neutral-950") into { family, position }.
61060
+ * Returns null if the string is not a valid color reference.
61061
+ */
61062
+ static parseColorRef(ref) {
61063
+ const lastDash = ref.lastIndexOf("-");
61064
+ if (lastDash <= 0) return null;
61065
+ const position = ref.slice(lastDash + 1);
61066
+ const family = ref.slice(0, lastDash);
61067
+ if (!_RaftersToolHandler.VALID_SCALE_POSITIONS.test(position)) return null;
61068
+ return { family, position };
61069
+ }
60939
61070
  /**
60940
61071
  * Handle set, create, and reset actions for rafters_token.
60941
61072
  * Loads the full registry, mutates, cascades, persists, and regenerates outputs.
@@ -60944,6 +61075,7 @@ var RaftersToolHandler = class _RaftersToolHandler {
60944
61075
  const name2 = args.name;
60945
61076
  const reason = args.reason;
60946
61077
  const value2 = args.value;
61078
+ const dark = args.dark;
60947
61079
  if (!reason) {
60948
61080
  return {
60949
61081
  content: [
@@ -60989,14 +61121,64 @@ var RaftersToolHandler = class _RaftersToolHandler {
60989
61121
  isError: true
60990
61122
  };
60991
61123
  }
61124
+ if (dark) {
61125
+ if (existing.namespace !== "semantic") {
61126
+ return {
61127
+ content: [
61128
+ {
61129
+ type: "text",
61130
+ text: JSON.stringify({
61131
+ error: `The "dark" parameter only applies to semantic color tokens. Token "${name2}" is in the "${existing.namespace}" namespace.`
61132
+ })
61133
+ }
61134
+ ],
61135
+ isError: true
61136
+ };
61137
+ }
61138
+ if (!_RaftersToolHandler.parseColorRef(dark)) {
61139
+ return {
61140
+ content: [
61141
+ {
61142
+ type: "text",
61143
+ text: JSON.stringify({
61144
+ error: `Invalid dark reference "${dark}". Use format "family-position" (e.g., "neutral-950"). Valid positions: 50, 100-900 by 100, 950.`
61145
+ })
61146
+ }
61147
+ ],
61148
+ isError: true
61149
+ };
61150
+ }
61151
+ }
60992
61152
  const previousValue = existing.value;
60993
61153
  existing.userOverride = {
60994
61154
  previousValue: typeof previousValue === "string" ? previousValue : JSON.stringify(previousValue),
60995
61155
  reason
60996
61156
  };
60997
- await registry2.set(name2, value2);
61157
+ const parsed = existing.namespace === "semantic" && typeof value2 === "string" ? _RaftersToolHandler.parseColorRef(value2) : null;
61158
+ if (existing.namespace === "semantic" && typeof value2 === "string" && !parsed) {
61159
+ return {
61160
+ content: [
61161
+ {
61162
+ type: "text",
61163
+ text: JSON.stringify({
61164
+ error: `Semantic token "${name2}" requires a color reference in "family-position" format (e.g., "neutral-50"), not "${value2}". Use rafters_vocabulary to find available color families.`
61165
+ })
61166
+ }
61167
+ ],
61168
+ isError: true
61169
+ };
61170
+ }
61171
+ if (parsed) {
61172
+ existing.value = parsed;
61173
+ const lightRefStr = `${parsed.family}-${parsed.position}`;
61174
+ const darkRef = dark ?? existing.dependsOn?.[1] ?? lightRefStr;
61175
+ existing.dependsOn = [lightRefStr, darkRef];
61176
+ } else {
61177
+ existing.value = value2;
61178
+ }
61179
+ await registry2.setToken(existing);
60998
61180
  const affected = this.getAffectedTokens(registry2, name2);
60999
- await this.regenerateOutputs(registry2);
61181
+ const outputFiles = await this.regenerateOutputs(registry2);
61000
61182
  return {
61001
61183
  content: [
61002
61184
  {
@@ -61006,7 +61188,13 @@ var RaftersToolHandler = class _RaftersToolHandler {
61006
61188
  action: "set",
61007
61189
  name: name2,
61008
61190
  reason,
61009
- cascaded: affected
61191
+ persisted: {
61192
+ value: existing.value,
61193
+ dependsOn: existing.dependsOn,
61194
+ namespace: existing.namespace
61195
+ },
61196
+ cascaded: affected,
61197
+ outputFiles
61010
61198
  })
61011
61199
  }
61012
61200
  ]
@@ -61063,7 +61251,7 @@ var RaftersToolHandler = class _RaftersToolHandler {
61063
61251
  };
61064
61252
  registry2.add(newToken);
61065
61253
  await this.adapter.save(registry2.list());
61066
- await this.regenerateOutputs(registry2);
61254
+ const outputFiles = await this.regenerateOutputs(registry2);
61067
61255
  return {
61068
61256
  content: [
61069
61257
  {
@@ -61073,7 +61261,13 @@ var RaftersToolHandler = class _RaftersToolHandler {
61073
61261
  action: "create",
61074
61262
  name: name2,
61075
61263
  namespace,
61076
- reason
61264
+ reason,
61265
+ persisted: {
61266
+ value: newToken.value,
61267
+ dependsOn: newToken.dependsOn,
61268
+ namespace: newToken.namespace
61269
+ },
61270
+ outputFiles
61077
61271
  })
61078
61272
  }
61079
61273
  ]
@@ -61090,8 +61284,9 @@ var RaftersToolHandler = class _RaftersToolHandler {
61090
61284
  };
61091
61285
  }
61092
61286
  await registry2.set(name2, COMPUTED);
61287
+ const resolved = registry2.get(name2);
61093
61288
  const affected = this.getAffectedTokens(registry2, name2);
61094
- await this.regenerateOutputs(registry2);
61289
+ const outputFiles = await this.regenerateOutputs(registry2);
61095
61290
  return {
61096
61291
  content: [
61097
61292
  {
@@ -61101,7 +61296,13 @@ var RaftersToolHandler = class _RaftersToolHandler {
61101
61296
  action: "reset",
61102
61297
  name: name2,
61103
61298
  reason,
61104
- cascaded: affected
61299
+ persisted: {
61300
+ value: resolved?.value,
61301
+ dependsOn: resolved?.dependsOn,
61302
+ namespace: resolved?.namespace
61303
+ },
61304
+ cascaded: affected,
61305
+ outputFiles
61105
61306
  })
61106
61307
  }
61107
61308
  ]
@@ -61137,10 +61338,11 @@ var RaftersToolHandler = class _RaftersToolHandler {
61137
61338
  return affected;
61138
61339
  }
61139
61340
  /**
61140
- * Regenerate output files (CSS, TS, DTCG) from registry state
61341
+ * Regenerate output files (CSS, TS, DTCG) from registry state.
61342
+ * Returns list of files written so callers can report what changed.
61141
61343
  */
61142
61344
  async regenerateOutputs(registry2) {
61143
- if (!this.projectRoot) return;
61345
+ if (!this.projectRoot) return [];
61144
61346
  const paths = getRaftersPaths(this.projectRoot);
61145
61347
  const config3 = await this.loadConfig();
61146
61348
  const exports = config3?.exports ?? {
@@ -61150,20 +61352,28 @@ var RaftersToolHandler = class _RaftersToolHandler {
61150
61352
  compiled: false
61151
61353
  };
61152
61354
  const shadcn = config3?.shadcn ?? false;
61355
+ const written = [];
61153
61356
  await mkdir4(paths.output, { recursive: true });
61154
61357
  if (exports.tailwind) {
61155
61358
  const darkMode = config3?.darkMode ?? "class";
61156
61359
  const css3 = registryToTailwind(registry2, { includeImport: !shadcn, darkMode });
61157
- await writeFile4(join10(paths.output, "rafters.css"), css3);
61360
+ const cssPath = join10(paths.output, "rafters.css");
61361
+ await writeFile4(cssPath, css3);
61362
+ written.push(relative2(this.projectRoot, cssPath));
61158
61363
  }
61159
61364
  if (exports.typescript) {
61160
61365
  const ts = registryToTypeScript(registry2, { includeJSDoc: true });
61161
- await writeFile4(join10(paths.output, "rafters.ts"), ts);
61366
+ const tsPath = join10(paths.output, "rafters.ts");
61367
+ await writeFile4(tsPath, ts);
61368
+ written.push(relative2(this.projectRoot, tsPath));
61162
61369
  }
61163
61370
  if (exports.dtcg) {
61164
61371
  const json3 = toDTCG(registry2.list());
61165
- await writeFile4(join10(paths.output, "rafters.json"), JSON.stringify(json3, null, 2));
61372
+ const jsonPath = join10(paths.output, "rafters.json");
61373
+ await writeFile4(jsonPath, JSON.stringify(json3, null, 2));
61374
+ written.push(relative2(this.projectRoot, jsonPath));
61166
61375
  }
61376
+ return written;
61167
61377
  }
61168
61378
  // ==================== Tool 6: Onboard ====================
61169
61379
  /**
@@ -61349,6 +61559,20 @@ var RaftersToolHandler = class _RaftersToolHandler {
61349
61559
  (s) => s.status === "designer"
61350
61560
  ).length;
61351
61561
  const totalFamilies = _RaftersToolHandler.SEMANTIC_FAMILIES.length;
61562
+ const hasDarkMode = cssFindings.some(
61563
+ (f) => f.customProperties.some(
61564
+ (p2) => p2.context === ".dark" || p2.context === "prefers-color-scheme: dark"
61565
+ )
61566
+ );
61567
+ let guidance;
61568
+ if (detectedFamilies.length > 0) {
61569
+ 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.`;
61570
+ } else {
61571
+ guidance = 'Review the custom properties above. Map each to a rafters token using rafters_onboard with action: "map". For ambiguous decisions, ask the designer.';
61572
+ }
61573
+ if (hasDarkMode) {
61574
+ 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.';
61575
+ }
61352
61576
  const result = {
61353
61577
  framework,
61354
61578
  cssFiles: cssFindings,
@@ -61358,7 +61582,8 @@ var RaftersToolHandler = class _RaftersToolHandler {
61358
61582
  shadcn,
61359
61583
  designDependencies: designDeps,
61360
61584
  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.'
61585
+ hasDarkMode,
61586
+ guidance
61362
61587
  };
61363
61588
  return {
61364
61589
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -61537,15 +61762,133 @@ var RaftersToolHandler = class _RaftersToolHandler {
61537
61762
  const registry2 = new TokenRegistry(allTokens);
61538
61763
  registry2.setAdapter(this.adapter);
61539
61764
  const results = [];
61765
+ const parseRef = _RaftersToolHandler.parseColorRef;
61540
61766
  for (const mapping of mappings) {
61541
61767
  const { source, target, value: value2, reason, namespace, category } = mapping;
61542
- if (!source || !target || !value2 || !reason) {
61768
+ const lightRef = mapping.light;
61769
+ const darkRef = mapping.dark;
61770
+ if (!source || !target || !reason) {
61543
61771
  results.push({
61544
61772
  source: source ?? "?",
61545
61773
  target: target ?? "?",
61546
61774
  action: "skipped",
61547
61775
  ok: false,
61548
- error: "Missing required fields: source, target, value, reason"
61776
+ error: "Missing required fields: source, target, reason"
61777
+ });
61778
+ continue;
61779
+ }
61780
+ if (lightRef || darkRef) {
61781
+ const existing = registry2.get(target);
61782
+ if (!existing) {
61783
+ results.push({
61784
+ source,
61785
+ target,
61786
+ action: "skipped",
61787
+ ok: false,
61788
+ error: `Semantic token "${target}" not found. Only existing semantic tokens can be remapped.`
61789
+ });
61790
+ continue;
61791
+ }
61792
+ const currentValue = existing.value;
61793
+ const currentLight = typeof currentValue === "object" && currentValue !== null && "family" in currentValue ? currentValue : null;
61794
+ const newLight = lightRef ? parseRef(lightRef) : currentLight;
61795
+ if (lightRef && !newLight) {
61796
+ results.push({
61797
+ source,
61798
+ target,
61799
+ action: "skipped",
61800
+ ok: false,
61801
+ error: `Invalid light reference "${lightRef}". Use format "family-position" (e.g., "neutral-50"). Valid positions: 50, 100-900 by 100, 950.`
61802
+ });
61803
+ continue;
61804
+ }
61805
+ if (!lightRef && !currentLight) {
61806
+ results.push({
61807
+ source,
61808
+ target,
61809
+ action: "skipped",
61810
+ ok: false,
61811
+ error: `Token "${target}" has no existing ColorReference value. You must supply an explicit "light" field to remap it.`
61812
+ });
61813
+ continue;
61814
+ }
61815
+ const darkFallbackRef = existing.dependsOn?.[1];
61816
+ const newDark = darkRef ? parseRef(darkRef) : darkFallbackRef ? parseRef(darkFallbackRef) : newLight;
61817
+ if (darkRef && !newDark) {
61818
+ results.push({
61819
+ source,
61820
+ target,
61821
+ action: "skipped",
61822
+ ok: false,
61823
+ error: `Invalid dark reference "${darkRef}". Use format "family-position" (e.g., "neutral-950"). Valid positions: 50, 100-900 by 100, 950.`
61824
+ });
61825
+ continue;
61826
+ }
61827
+ if (!darkRef && darkFallbackRef && !newDark) {
61828
+ results.push({
61829
+ source,
61830
+ target,
61831
+ action: "skipped",
61832
+ ok: false,
61833
+ error: `Could not infer dark reference from existing dependsOn value "${darkFallbackRef}". Supply an explicit "dark" field.`
61834
+ });
61835
+ continue;
61836
+ }
61837
+ if (!newLight || !newDark) {
61838
+ results.push({
61839
+ source,
61840
+ target,
61841
+ action: "skipped",
61842
+ ok: false,
61843
+ error: `Could not resolve light/dark references for "${target}". Supply explicit "light" and "dark" fields.`
61844
+ });
61845
+ continue;
61846
+ }
61847
+ const lightTokenName = `${newLight.family}-${newLight.position}`;
61848
+ const darkTokenName = `${newDark.family}-${newDark.position}`;
61849
+ const missingRefs = [];
61850
+ if (!registry2.has(lightTokenName)) missingRefs.push(lightTokenName);
61851
+ if (!registry2.has(darkTokenName)) missingRefs.push(darkTokenName);
61852
+ if (missingRefs.length > 0) {
61853
+ results.push({
61854
+ source,
61855
+ target,
61856
+ action: "skipped",
61857
+ ok: false,
61858
+ error: `Referenced color tokens not found: ${missingRefs.join(", ")}. Map the color family first, then remap the semantic token.`
61859
+ });
61860
+ continue;
61861
+ }
61862
+ const previousValue = typeof existing.value === "string" ? existing.value : JSON.stringify(existing.value);
61863
+ existing.userOverride = {
61864
+ previousValue,
61865
+ reason: `Remapped from ${source}: ${reason}`
61866
+ };
61867
+ const newColorRef = { family: newLight.family, position: newLight.position };
61868
+ await registry2.setToken({
61869
+ ...existing,
61870
+ value: newColorRef,
61871
+ dependsOn: [lightTokenName, darkTokenName]
61872
+ });
61873
+ results.push({
61874
+ source,
61875
+ target,
61876
+ action: "remap",
61877
+ ok: true,
61878
+ persisted: {
61879
+ value: newColorRef,
61880
+ dependsOn: [lightTokenName, darkTokenName]
61881
+ }
61882
+ });
61883
+ continue;
61884
+ }
61885
+ if (!value2) {
61886
+ results.push({
61887
+ source,
61888
+ target,
61889
+ action: "skipped",
61890
+ ok: false,
61891
+ error: 'Missing "value" for color family mapping. Provide a CSS color value, or use "light"/"dark" for semantic remapping.'
61549
61892
  });
61550
61893
  continue;
61551
61894
  }
@@ -61569,6 +61912,14 @@ var RaftersToolHandler = class _RaftersToolHandler {
61569
61912
  };
61570
61913
  await registry2.set(target, tokenValue);
61571
61914
  results.push({ source, target, action: "set", ok: true, enriched });
61915
+ } else {
61916
+ results.push({
61917
+ source,
61918
+ target,
61919
+ action: "skipped",
61920
+ ok: false,
61921
+ error: `Token "${target}" exists in registry index but could not be retrieved.`
61922
+ });
61572
61923
  }
61573
61924
  } else {
61574
61925
  const ns = namespace ?? (enriched ? "color" : "custom");
@@ -61589,9 +61940,10 @@ var RaftersToolHandler = class _RaftersToolHandler {
61589
61940
  }
61590
61941
  }
61591
61942
  await this.adapter.save(registry2.list());
61592
- await this.regenerateOutputs(registry2);
61943
+ const outputFiles = await this.regenerateOutputs(registry2);
61593
61944
  const setCount = results.filter((r) => r.action === "set" && r.ok).length;
61594
61945
  const createCount = results.filter((r) => r.action === "create" && r.ok).length;
61946
+ const remapCount = results.filter((r) => r.action === "remap" && r.ok).length;
61595
61947
  const enrichedCount = results.filter((r) => r.enriched).length;
61596
61948
  const failCount = results.filter((r) => !r.ok).length;
61597
61949
  return {
@@ -61604,10 +61956,12 @@ var RaftersToolHandler = class _RaftersToolHandler {
61604
61956
  summary: {
61605
61957
  set: setCount,
61606
61958
  created: createCount,
61959
+ remapped: remapCount,
61607
61960
  enriched: enrichedCount,
61608
61961
  failed: failCount
61609
61962
  },
61610
- results
61963
+ results,
61964
+ outputFiles
61611
61965
  },
61612
61966
  null,
61613
61967
  2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "CLI for Rafters design system - scaffold tokens and add components",
5
5
  "license": "MIT",
6
6
  "type": "module",