rafters 0.3.1 → 0.4.0

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 +158 -65
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20309,6 +20309,11 @@ var MOTION_NAMESPACE_PROPERTY = {
20309
20309
  };
20310
20310
  var MOTION_NAMESPACE_NAMES = Object.keys(MOTION_NAMESPACE_PROPERTY);
20311
20311
  var REDUCED_MOTION_ZEROED = /* @__PURE__ */ new Set(["duration", "delay"]);
20312
+ var TAILWIND_THEME_PREFIX = {
20313
+ duration: "transition-duration",
20314
+ ease: "ease",
20315
+ delay: "transition-delay"
20316
+ };
20312
20317
  var MOTION_NAMESPACE_TOKEN = new RegExp(`^rafters-(${MOTION_NAMESPACE_NAMES.join("|")})-(.+)$`);
20313
20318
  function motionNamespaceParts(name) {
20314
20319
  const match = MOTION_NAMESPACE_TOKEN.exec(name);
@@ -20565,6 +20570,11 @@ function generateThemeBlock(groups) {
20565
20570
  lines.push(bridgeLines);
20566
20571
  lines.push("");
20567
20572
  }
20573
+ const animationKeys = generateMotionAnimationKeys(groups.motion);
20574
+ if (animationKeys) {
20575
+ lines.push(animationKeys);
20576
+ lines.push("");
20577
+ }
20568
20578
  if (groups.breakpoint.length > 0) {
20569
20579
  for (const token of groups.breakpoint) {
20570
20580
  const value = tokenValueToCSS(token);
@@ -20591,10 +20601,6 @@ function generateThemeBlock(groups) {
20591
20601
  }
20592
20602
  lines.push("");
20593
20603
  }
20594
- const animationTokens = generateAnimationTokens(groups.motion);
20595
- if (animationTokens) {
20596
- lines.push(animationTokens);
20597
- }
20598
20604
  lines.push("}");
20599
20605
  return lines.join("\n");
20600
20606
  }
@@ -20685,18 +20691,6 @@ function generateKeyframes(motionTokens) {
20685
20691
  }
20686
20692
  return lines.join("\n").trim();
20687
20693
  }
20688
- function generateAnimationTokens(motionTokens) {
20689
- const animationTokens = motionTokens.filter((t) => t.name.startsWith("motion-animation-"));
20690
- if (animationTokens.length === 0) {
20691
- return "";
20692
- }
20693
- const lines = [];
20694
- for (const token of animationTokens) {
20695
- const animName = token.animationName || token.name.replace("motion-animation-", "");
20696
- lines.push(` --animate-${animName}: ${token.value};`);
20697
- }
20698
- return lines.join("\n");
20699
- }
20700
20694
  function generateDepthUtilities(depthTokens) {
20701
20695
  if (depthTokens.length === 0) return "";
20702
20696
  const lines = ["/* Depth (z-index) utilities -- words over numbers */"];
@@ -20791,8 +20785,8 @@ function generateMotionUtilities(motionTokens) {
20791
20785
  const className = token.name.replace("motion-semantic-", "motion-");
20792
20786
  lines.push(`@utility ${className} {`);
20793
20787
  lines.push(` transition-property: ${spec.properties.join(", ")};`);
20794
- lines.push(` transition-duration: var(--duration-${spec.durationTier});`);
20795
- lines.push(` transition-timing-function: var(--ease-${spec.curve});`);
20788
+ lines.push(` transition-duration: var(--rafters-duration-${spec.durationTier});`);
20789
+ lines.push(` transition-timing-function: var(--rafters-ease-${spec.curve});`);
20796
20790
  if (spec.reducedMotion) {
20797
20791
  lines.push(" @media (prefers-reduced-motion: reduce) {");
20798
20792
  lines.push(` transition-property: ${spec.reducedMotion.properties.join(", ")};`);
@@ -20848,7 +20842,7 @@ function parseCellSpec(tokenName, raw) {
20848
20842
  `tailwind exporter: motion cell token "${tokenName}" has an unrecognized duration.kind ${JSON.stringify(kind ?? null)}. Known duration.kind values: period, tier.`
20849
20843
  );
20850
20844
  }
20851
- function generateMotionCellUtilities(motionTokens) {
20845
+ function generateMotionAnimationKeys(motionTokens) {
20852
20846
  const cellTokens = motionTokens.filter((t) => t.name.startsWith("motion-cell-"));
20853
20847
  if (cellTokens.length === 0) return "";
20854
20848
  const periodMembers = /* @__PURE__ */ new Set();
@@ -20856,38 +20850,38 @@ function generateMotionCellUtilities(motionTokens) {
20856
20850
  const parts = motionNamespaceParts(token.name);
20857
20851
  if (parts?.namespace === "period") periodMembers.add(parts.member);
20858
20852
  }
20859
- const lines = [
20860
- "/* Motion cells -- one utility per animated (component, part, transition) */"
20861
- ];
20853
+ const keys = /* @__PURE__ */ new Map();
20862
20854
  for (const token of cellTokens) {
20863
20855
  if (typeof token.value !== "string") continue;
20864
20856
  const spec = parseCellSpec(token.name, token.value);
20865
- lines.push(`@utility ${token.name.replace("motion-cell-", "animate-")} {`);
20866
20857
  if (spec === null) {
20867
- lines.push(` animation: ${token.value};`);
20868
- } else if (spec.duration.kind === "period") {
20858
+ keys.set(token.name.replace("motion-cell-", ""), token.value);
20859
+ continue;
20860
+ }
20861
+ if (spec.duration.kind === "period") {
20869
20862
  const { period } = spec.duration;
20870
20863
  if (!periodMembers.has(period)) {
20871
20864
  throw new Error(
20872
20865
  `tailwind exporter: motion cell token "${token.name}" references unknown period "${period}". Known periods: ${[...periodMembers].sort().join(", ")}.`
20873
20866
  );
20874
20867
  }
20875
- lines.push(` animation-name: ${spec.keyframe};`);
20876
- lines.push(` animation-duration: var(--rafters-period-${period});`);
20877
- lines.push(" animation-iteration-count: infinite;");
20878
- } else {
20879
- lines.push(` animation-name: ${spec.keyframe};`);
20880
- lines.push(` animation-duration: var(--rafters-duration-${spec.duration.tier});`);
20881
- lines.push(` animation-timing-function: var(--rafters-ease-${spec.curve});`);
20882
- }
20883
- if (token.reducedMotionAware !== false) {
20884
- lines.push(" @media (prefers-reduced-motion: reduce) {");
20885
- lines.push(" animation-duration: 0s;");
20886
- lines.push(" }");
20868
+ keys.set(
20869
+ `${spec.keyframe}-${period}`,
20870
+ `${spec.keyframe} var(--rafters-period-${period}) infinite`
20871
+ );
20872
+ continue;
20887
20873
  }
20888
- lines.push("}");
20874
+ const { tier } = spec.duration;
20875
+ keys.set(
20876
+ `${spec.keyframe}-${tier}-${spec.curve}`,
20877
+ `${spec.keyframe} var(--rafters-duration-${tier}) var(--rafters-ease-${spec.curve})`
20878
+ );
20889
20879
  }
20890
- return lines.join("\n");
20880
+ if (keys.size === 0) return "";
20881
+ return [
20882
+ " /* Motion assignments -- one key per distinct (shape, tier, curve) */",
20883
+ ...[...keys].map(([name, value]) => ` --animate-${name}: ${value};`)
20884
+ ].join("\n");
20891
20885
  }
20892
20886
  function generateMotionNamespaceVars(motionTokens) {
20893
20887
  const lines = [];
@@ -20903,35 +20897,45 @@ function generateMotionNamespaceVars(motionTokens) {
20903
20897
  ...lines
20904
20898
  ].join("\n");
20905
20899
  }
20900
+ function generateReducedMotionLaw(motionTokens) {
20901
+ const lines = [];
20902
+ for (const token of motionTokens) {
20903
+ const parts = motionNamespaceParts(token.name);
20904
+ if (!parts || !REDUCED_MOTION_ZEROED.has(parts.namespace)) continue;
20905
+ lines.push(` --${token.name}: 0ms;`);
20906
+ }
20907
+ if (lines.length === 0) return "";
20908
+ return [
20909
+ "/* Reduced motion is law: every duration and delay is zero, loops are not. */",
20910
+ "@media (prefers-reduced-motion: reduce) {",
20911
+ " :root {",
20912
+ ...lines,
20913
+ " }",
20914
+ "}"
20915
+ ].join("\n");
20916
+ }
20906
20917
  function generateMotionBridgeVars(motionTokens) {
20907
20918
  const lines = [];
20908
20919
  for (const token of motionTokens) {
20909
- if (token.name.startsWith("motion-duration-") && token.name !== "motion-duration-base") {
20910
- const key = token.name.replace("motion-duration-", "");
20911
- lines.push(` --duration-${key}: var(--rafters-duration-${key});`);
20912
- }
20913
- if (token.name.startsWith("motion-easing-")) {
20914
- const key = token.name.replace("motion-easing-", "");
20915
- lines.push(` --ease-${key}: var(--rafters-ease-${key});`);
20916
- }
20920
+ const parts = motionNamespaceParts(token.name);
20921
+ if (!parts) continue;
20922
+ const themePrefix = TAILWIND_THEME_PREFIX[parts.namespace];
20923
+ if (!themePrefix) continue;
20924
+ lines.push(` --${themePrefix}-${parts.member}: var(--${token.name});`);
20917
20925
  }
20918
20926
  return lines.join("\n");
20919
20927
  }
20920
20928
  function generateMotionNamespaceUtilities(motionTokens) {
20921
- const lines = ["/* The five motion namespaces -- one utility per member */"];
20929
+ const lines = ["/* Motion namespaces Tailwind has no theme key for */"];
20922
20930
  let emitted = 0;
20923
20931
  for (const token of motionTokens) {
20924
20932
  const parts = motionNamespaceParts(token.name);
20925
20933
  if (!parts) continue;
20934
+ if (TAILWIND_THEME_PREFIX[parts.namespace]) continue;
20926
20935
  const property = MOTION_NAMESPACE_PROPERTY[parts.namespace];
20927
20936
  emitted++;
20928
20937
  lines.push(`@utility ${parts.namespace}-${parts.member} {`);
20929
20938
  lines.push(` ${property}: var(--${token.name});`);
20930
- if (REDUCED_MOTION_ZEROED.has(parts.namespace)) {
20931
- lines.push(" @media (prefers-reduced-motion: reduce) {");
20932
- lines.push(` ${property}: 0ms;`);
20933
- lines.push(" }");
20934
- }
20935
20939
  lines.push("}");
20936
20940
  }
20937
20941
  return emitted === 0 ? "" : lines.join("\n");
@@ -21001,6 +21005,11 @@ function tokensToTailwind(tokens, options = {}, typographyOverrides = []) {
21001
21005
  if (keyframes) {
21002
21006
  sections.push(keyframes);
21003
21007
  }
21008
+ const reducedMotionLaw = generateReducedMotionLaw(groups.motion);
21009
+ if (reducedMotionLaw) {
21010
+ sections.push("");
21011
+ sections.push(reducedMotionLaw);
21012
+ }
21004
21013
  const typographyThemeInline = generateTypographyCompositeThemeInline(
21005
21014
  groups["typography-composite"]
21006
21015
  );
@@ -21028,11 +21037,6 @@ function tokensToTailwind(tokens, options = {}, typographyOverrides = []) {
21028
21037
  sections.push("");
21029
21038
  sections.push(motionUtilities);
21030
21039
  }
21031
- const cellUtilities = generateMotionCellUtilities(groups.motion);
21032
- if (cellUtilities) {
21033
- sections.push("");
21034
- sections.push(cellUtilities);
21035
- }
21036
21040
  const overrideCSS = generateTypographyOverrideCSS(typographyOverrides);
21037
21041
  if (overrideCSS) {
21038
21042
  sections.push("");
@@ -22294,6 +22298,28 @@ var DEFAULT_KEYFRAME_DEFINITIONS = {
22294
22298
  meaning: "Scale down while fading out",
22295
22299
  contexts: ["modal-exit", "popover-close"]
22296
22300
  },
22301
+ "grow-in": {
22302
+ // scaleY, not a uniform scale -- a bar growing from the baseline to its
22303
+ // full height is STRUCTURAL geometry (fixed, like the accordion
22304
+ // chevron's 180deg rotation), not a pop: no opacity, no other numeric.
22305
+ // The transform-origin anchoring the growth at the bottom (the
22306
+ // value-axis baseline) is the caller-set `transform-origin`
22307
+ // (bar-chart.behavior.ts's `transformOriginFor`), not this keyframe --
22308
+ // this declares only the scaleY 0 -> 1 extent.
22309
+ css: () => "from { transform: scaleY(0); } to { transform: scaleY(1); }",
22310
+ meaning: "Grow from zero to full height, anchored at the baseline (caller-set transform-origin)",
22311
+ contexts: ["bar-chart", "value-display"]
22312
+ },
22313
+ "grow-in-x": {
22314
+ // The horizontal-layout counterpart of grow-in: scaleX, not scaleY --
22315
+ // `layout: 'horizontal'` swaps computeBars' value axis from y to x
22316
+ // (bar-chart.behavior.ts), so the structural growth axis swaps with it.
22317
+ // Same rationale as grow-in: no opacity, no other numeric, transform-
22318
+ // origin is caller-set (transformOriginFor).
22319
+ css: () => "from { transform: scaleX(0); } to { transform: scaleX(1); }",
22320
+ meaning: "Grow from zero to full width, anchored at the baseline (caller-set transform-origin)",
22321
+ contexts: ["bar-chart", "value-display"]
22322
+ },
22297
22323
  spin: {
22298
22324
  css: () => "from { transform: rotate(0deg); } to { transform: rotate(360deg); }",
22299
22325
  meaning: "Continuous rotation",
@@ -22739,6 +22765,38 @@ var DEFAULT_MOTION_CELL_ANIMATIONS = {
22739
22765
  meaning: "The incoming tab panel as the selection moves: the calendar month-change moment on a panel instead of a grid, one tier quicker because a panel swap covers no distance.",
22740
22766
  contexts: ["tabs", "panel", "crossfade"]
22741
22767
  },
22768
+ "bar-chart-bar-enter": {
22769
+ keyframe: "grow-in",
22770
+ duration: { kind: "tier", tier: "normal" },
22771
+ curve: "enter",
22772
+ cell: { component: "bar-chart", part: "bar", transition: "enter" },
22773
+ meaning: "A bar arriving: grows from zero at the value-axis baseline (bar-chart.behavior.ts sets the transform-origin), on the arrival curve. Vertical layout, the default -- see bar-chart-bar-enter-x for the horizontal counterpart.",
22774
+ contexts: ["bar-chart", "chart", "value-display"]
22775
+ },
22776
+ "bar-chart-bar-enter-x": {
22777
+ keyframe: "grow-in-x",
22778
+ duration: { kind: "tier", tier: "normal" },
22779
+ curve: "enter",
22780
+ cell: { component: "bar-chart", part: "bar", transition: "enter-horizontal" },
22781
+ meaning: 'A bar arriving under layout: "horizontal": grows from zero at the value-axis baseline, now the left edge (bar-chart.behavior.ts sets the transform-origin), on the same arrival curve as the vertical bar-chart-bar-enter cell -- only the transform property (scaleX, not scaleY) changes with the swapped axis.',
22782
+ contexts: ["bar-chart", "chart", "value-display"]
22783
+ },
22784
+ "area-chart-area-enter": {
22785
+ keyframe: "fade-in",
22786
+ duration: { kind: "tier", tier: "moderate" },
22787
+ curve: "enter",
22788
+ cell: { component: "area-chart", part: "area", transition: "enter" },
22789
+ meaning: "A filled area arriving on mount: fade, not scale -- unlike bar-chart-bar-enter, a stacked area series has no single baseline edge to grow from (its baseline is the previous series own top curve, area-chart.behavior.ts computeAreas), so opacity is the one property every area shares whether overlaid or stacked.",
22790
+ contexts: ["area-chart", "chart", "value-display"]
22791
+ },
22792
+ "line-chart-line-enter": {
22793
+ keyframe: "fade-in",
22794
+ duration: { kind: "tier", tier: "moderate" },
22795
+ curve: "enter",
22796
+ cell: { component: "line-chart", part: "line", transition: "enter" },
22797
+ meaning: "A line series arriving on mount: fades in rather than snapping into place. The matrix assigns opacity over a stroke-dashoffset reveal for this cell -- a dashoffset keyframe needs a per-instance path-length value nothing here names, where a fade needs none.",
22798
+ contexts: ["line-chart", "chart", "value-display"]
22799
+ },
22742
22800
  // ------------------------------------------------------------- load / appearance
22743
22801
  "avatar-image-load": {
22744
22802
  keyframe: "fade-in",
@@ -29639,6 +29697,21 @@ async function installWithPackageManager(packageManager, dependencies, options)
29639
29697
 
29640
29698
  // src/utils/install-registry-deps.ts
29641
29699
  var RAFTERS_SCOPE_PREFIX = "@rafters/";
29700
+ var PLACEHOLDER_DEPENDENCY_NAMES = /* @__PURE__ */ new Set(["none", "n/a"]);
29701
+ function isPlaceholderDependencyName(name) {
29702
+ const normalized = name.trim().toLowerCase();
29703
+ return normalized === "" || PLACEHOLDER_DEPENDENCY_NAMES.has(normalized);
29704
+ }
29705
+ var PlaceholderDependencyError = class extends Error {
29706
+ constructor(itemName, dependency) {
29707
+ super(
29708
+ `Registry item "${itemName}" declares dependency "${dependency}", which is not a real npm package name -- it looks like a placeholder left in the manifest. Refusing to install it.`
29709
+ );
29710
+ this.itemName = itemName;
29711
+ this.dependency = dependency;
29712
+ this.name = "PlaceholderDependencyError";
29713
+ }
29714
+ };
29642
29715
  var REACT_RUNTIME_PACKAGES = /* @__PURE__ */ new Set(["react", "react-dom", "@types/react", "@types/react-dom"]);
29643
29716
  function parseDependency(dep) {
29644
29717
  const trimmed = dep.trim();
@@ -29685,6 +29758,16 @@ async function installRegistryDependencies(items, targetDir, options = {}) {
29685
29758
  devInstalled: [],
29686
29759
  failed: []
29687
29760
  };
29761
+ for (const item of items) {
29762
+ for (const file2 of item.files) {
29763
+ for (const dep of file2.dependencies) {
29764
+ const { name } = parseDependency(dep);
29765
+ if (isPlaceholderDependencyName(name)) {
29766
+ throw new PlaceholderDependencyError(item.name, dep);
29767
+ }
29768
+ }
29769
+ }
29770
+ }
29688
29771
  const allDeps = new Set(items.flatMap((item) => item.files.flatMap((file2) => file2.dependencies)));
29689
29772
  if (allDeps.size === 0) {
29690
29773
  return result;
@@ -30464,13 +30547,23 @@ async function add(componentArgs, options) {
30464
30547
  target
30465
30548
  });
30466
30549
  } catch (err) {
30467
- const message = err instanceof Error ? err.message : String(err);
30468
- log({
30469
- event: "add:deps:install-failed",
30470
- message: `Failed to process dependencies: ${message}`,
30471
- dependencies: [],
30472
- suggestion: "Check package.json and try installing dependencies manually."
30473
- });
30550
+ if (err instanceof PlaceholderDependencyError) {
30551
+ log({
30552
+ event: "add:deps:install-failed",
30553
+ message: err.message,
30554
+ dependencies: [],
30555
+ suggestion: "This is a registry defect, not a local problem -- do not install it manually."
30556
+ });
30557
+ process.exitCode = 1;
30558
+ } else {
30559
+ const message = err instanceof Error ? err.message : String(err);
30560
+ log({
30561
+ event: "add:deps:install-failed",
30562
+ message: `Failed to process dependencies: ${message}`,
30563
+ dependencies: [],
30564
+ suggestion: "Check package.json and try installing dependencies manually."
30565
+ });
30566
+ }
30474
30567
  }
30475
30568
  if (depsResult.installed.length > 0 || depsResult.skipped.length > 0) {
30476
30569
  log({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Design Intelligence CLI. Scaffold tokens, import existing shadcn/Tailwind v4 sources, add components, and serve an MCP server so AI agents read decisions instead of guessing.",
5
5
  "homepage": "https://rafters.studio",
6
6
  "license": "MIT",