rafters 0.0.71 → 0.0.72

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.
package/dist/index.js CHANGED
@@ -437,7 +437,7 @@ function generateThemeInlineBlock(semanticTokens) {
437
437
  function generateRootBlock(semanticTokens, darkMode = "class") {
438
438
  const semanticMappings = getSemanticMappingsFromTokens(semanticTokens);
439
439
  const lines = [];
440
- lines.push(":root {");
440
+ lines.push(":root, :host {");
441
441
  for (const [name, mapping] of Object.entries(semanticMappings)) {
442
442
  lines.push(` --rafters-${name}: var(--color-${mapping.light});`);
443
443
  }
@@ -750,6 +750,37 @@ function generateTypographyCompositeUtilities(compositeTokens) {
750
750
  }
751
751
  return lines.join("\n");
752
752
  }
753
+ function generateMotionUtilities(motionTokens) {
754
+ const semanticTokens = motionTokens.filter((t) => t.name.startsWith("motion-semantic-"));
755
+ if (semanticTokens.length === 0) {
756
+ return "";
757
+ }
758
+ const lines = ["/* Semantic motion utilities -- transition longhand per token */"];
759
+ for (const token of semanticTokens) {
760
+ if (typeof token.value !== "string") continue;
761
+ let spec;
762
+ try {
763
+ spec = JSON.parse(token.value);
764
+ } catch {
765
+ continue;
766
+ }
767
+ const className = token.name.replace("motion-semantic-", "motion-");
768
+ lines.push(`@utility ${className} {`);
769
+ lines.push(` transition-property: ${spec.properties.join(", ")};`);
770
+ lines.push(` transition-duration: var(--duration-${spec.durationTier});`);
771
+ lines.push(` transition-timing-function: var(--ease-${spec.curve});`);
772
+ if (spec.reducedMotion) {
773
+ lines.push(" @media (prefers-reduced-motion: reduce) {");
774
+ lines.push(` transition-property: ${spec.reducedMotion.properties.join(", ")};`);
775
+ if (typeof spec.reducedMotion.ms === "number") {
776
+ lines.push(` transition-duration: ${spec.reducedMotion.ms}ms;`);
777
+ }
778
+ lines.push(" }");
779
+ }
780
+ lines.push("}");
781
+ }
782
+ return lines.join("\n");
783
+ }
753
784
  function overridePropertyToUtility(property, value) {
754
785
  switch (property) {
755
786
  case "fontFamily":
@@ -825,6 +856,11 @@ function tokensToTailwind(tokens, options = {}, typographyOverrides = []) {
825
856
  sections.push("");
826
857
  sections.push(depthUtilities);
827
858
  }
859
+ const motionUtilities = generateMotionUtilities(groups.motion);
860
+ if (motionUtilities) {
861
+ sections.push("");
862
+ sections.push(motionUtilities);
863
+ }
828
864
  const overrideCSS = generateTypographyOverrideCSS(typographyOverrides);
829
865
  if (overrideCSS) {
830
866
  sections.push("");
@@ -910,6 +946,13 @@ function escapeStringValue(value) {
910
946
  }
911
947
  return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
912
948
  }
949
+ var VALID_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
950
+ function namespaceKey(namespace) {
951
+ return VALID_IDENTIFIER.test(namespace) ? namespace : `'${namespace}'`;
952
+ }
953
+ function namespaceAccess(namespace) {
954
+ return VALID_IDENTIFIER.test(namespace) ? `tokens.${namespace}` : `tokens['${namespace}']`;
955
+ }
913
956
  function namespaceToTypeName(namespace) {
914
957
  return namespace.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
915
958
  }
@@ -930,7 +973,7 @@ function generateTokensObject(tokensByNamespace, includeJSDoc) {
930
973
  const namespaces = Array.from(tokensByNamespace.keys()).sort();
931
974
  for (const namespace of namespaces) {
932
975
  const tokens = tokensByNamespace.get(namespace) || [];
933
- lines.push(` ${namespace}: {`);
976
+ lines.push(` ${namespaceKey(namespace)}: {`);
934
977
  for (const token of tokens) {
935
978
  const value = tokenValueToTS(token);
936
979
  if (includeJSDoc && token.semanticMeaning) {
@@ -949,7 +992,7 @@ function generateTypeAliases(namespaces) {
949
992
  lines.push("");
950
993
  for (const namespace of namespaces) {
951
994
  const typeName = `${namespaceToTypeName(namespace)}Token`;
952
- lines.push(`export type ${typeName} = keyof typeof tokens.${namespace};`);
995
+ lines.push(`export type ${typeName} = keyof (typeof ${namespaceAccess(namespace)});`);
953
996
  }
954
997
  if (namespaces.length > 0) {
955
998
  lines.push("");
@@ -1124,15 +1167,21 @@ var DEPTH_LEVELS = [
1124
1167
  "tooltip",
1125
1168
  "overlay"
1126
1169
  ];
1127
- var MOTION_DURATION_SCALE = ["instant", "fast", "normal", "slow", "slower"];
1170
+ var MOTION_DURATION_SCALE = [
1171
+ "instant",
1172
+ "micro",
1173
+ "fast",
1174
+ "moderate",
1175
+ "normal",
1176
+ "slow"
1177
+ ];
1128
1178
  var EASING_CURVES = [
1179
+ "standard",
1180
+ "enter",
1181
+ "exit",
1129
1182
  "linear",
1130
- "ease-in",
1131
- "ease-out",
1132
- "ease-in-out",
1133
- "productive",
1134
- "expressive",
1135
- "spring"
1183
+ "spring-smooth",
1184
+ "spring-snappy"
1136
1185
  ];
1137
1186
  var BREAKPOINT_SCALE = ["sm", "md", "lg", "xl", "2xl"];
1138
1187
 
@@ -21442,6 +21491,13 @@ var ExampleSchema = external_exports.object({
21442
21491
  description: external_exports.string().optional()
21443
21492
  });
21444
21493
  var ColorIntelligenceSchema = external_exports.object({
21494
+ // Designer-facing evocative handle ("Aged Terracotta", "Solar Citrine") from
21495
+ // the intelligence generator. Language, not identity: the deterministic
21496
+ // `name` on ColorValue is the stable, parseable address; `label` is the
21497
+ // voice. Optional because pre-label intelligence persists in Vectorize and
21498
+ // token files. Backported from platform's generateColorIntelligence, which
21499
+ // requires it on generation.
21500
+ label: external_exports.string().optional(),
21445
21501
  reasoning: external_exports.string(),
21446
21502
  emotionalImpact: external_exports.string(),
21447
21503
  culturalContext: external_exports.string(),
@@ -21684,7 +21740,7 @@ var TokenSchema = external_exports.object({
21684
21740
  motionIntent: external_exports.enum(["enter", "exit", "emphasis", "transition"]).optional(),
21685
21741
  easingCurve: external_exports.tuple([external_exports.number(), external_exports.number(), external_exports.number(), external_exports.number()]).optional(),
21686
21742
  // cubicBezier [x1, y1, x2, y2]
21687
- easingName: external_exports.enum(["linear", "ease-in", "ease-out", "ease-in-out", "productive", "expressive", "spring"]).optional(),
21743
+ easingName: external_exports.enum(["standard", "enter", "exit", "linear", "spring-smooth", "spring-snappy"]).optional(),
21688
21744
  delayMs: external_exports.number().optional(),
21689
21745
  // Delay before animation starts
21690
21746
  // Keyframe tokens (CSS @keyframes content / animation step definitions)
@@ -22301,78 +22357,216 @@ var DEFAULT_SHADOW_DEFINITIONS = {
22301
22357
  };
22302
22358
  var DEFAULT_DURATION_DEFINITIONS = {
22303
22359
  instant: {
22304
- step: "instant",
22305
- meaning: "Instant - no animation",
22306
- contexts: ["disabled-motion", "prefers-reduced-motion"],
22360
+ ms: 0,
22361
+ band: "",
22362
+ meaning: "No perceptible transition. Cursor changes, text selection, badge counts. Below all perception -- there is nothing to track, so nothing is communicated.",
22363
+ contexts: ["disabled-motion", "prefers-reduced-motion", "cursor", "badge-count"],
22364
+ motionIntent: "transition"
22365
+ },
22366
+ micro: {
22367
+ ms: 100,
22368
+ band: "at the instantaneous threshold (Nielsen 0.1s)",
22369
+ meaning: "Immediate but visible. Focus rings and press feedback. At the instantaneous threshold -- acknowledgment that input landed, not communication of a change.",
22370
+ contexts: ["focus", "press", "micro-feedback"],
22307
22371
  motionIntent: "transition"
22308
22372
  },
22309
22373
  fast: {
22310
- step: -1,
22311
- meaning: "Fast - micro-interactions, hover states",
22312
- contexts: ["hover", "focus", "active", "micro-feedback"],
22374
+ ms: 150,
22375
+ band: "below the communicative window",
22376
+ meaning: "Hover states. The cursor is already there, so the response must match its speed. Below the communicative window -- acknowledgment, not communication.",
22377
+ contexts: ["hover", "micro-feedback"],
22313
22378
  motionIntent: "transition"
22314
22379
  },
22315
- normal: {
22316
- step: 0,
22317
- meaning: "Normal - standard UI transitions",
22318
- contexts: ["buttons", "toggles", "state-changes"],
22380
+ moderate: {
22381
+ ms: 250,
22382
+ band: "communicative (~200-300ms)",
22383
+ meaning: "Dropdowns, tab switches, small reveals. The communicative window: fast enough to feel responsive, slow enough for the eye to track a trajectory and build a spatial model.",
22384
+ contexts: ["dropdowns", "tab-switches", "small-reveals"],
22319
22385
  motionIntent: "transition"
22320
22386
  },
22321
- slow: {
22322
- step: 1,
22323
- meaning: "Slow - enter/exit animations",
22324
- contexts: ["modals", "dialogs", "panels", "enter-exit"],
22387
+ normal: {
22388
+ ms: 350,
22389
+ band: "communicative, larger movement",
22390
+ meaning: "The workhorse -- modal entrances, toggles, standard state transitions. The communicative window for larger movement.",
22391
+ contexts: ["modals", "toggles", "state-changes"],
22325
22392
  motionIntent: "enter"
22326
22393
  },
22327
- slower: {
22328
- step: 2,
22329
- meaning: "Slower - emphasis, large element transitions",
22330
- contexts: ["page-transitions", "hero-animations", "emphasis"],
22331
- motionIntent: "emphasis"
22394
+ slow: {
22395
+ ms: 500,
22396
+ band: "at the sluggish boundary",
22397
+ meaning: "Sheets, page transitions, large spatial movement where the user needs orientation. At the sluggish boundary -- the ceiling for anything but full-screen spatial transitions.",
22398
+ contexts: ["sheets", "page-transitions", "large-spatial-movement"],
22399
+ motionIntent: "enter"
22332
22400
  }
22333
22401
  };
22334
22402
  var DEFAULT_EASING_DEFINITIONS = {
22403
+ standard: {
22404
+ curve: [0.25, 0.1, 0.25, 1],
22405
+ meaning: "Precision -- arrives exactly where it should, engineered rather than thrown. Decelerates into its final position. General-purpose state transitions and hover.",
22406
+ contexts: ["state-changes", "hover", "general-purpose"],
22407
+ css: "cubic-bezier(0.25, 0.1, 0.25, 1)"
22408
+ },
22409
+ enter: {
22410
+ curve: [0, 0, 0.2, 1],
22411
+ meaning: "Arrival, welcome, settling into place. The fast start communicates responsiveness; the slow finish communicates care. Anything entering the viewport. Paired with `exit` as a deliberately asymmetric couple. (Dragicevic 2011: slow-in/slow-out outperforms constant speed for tracking.)",
22412
+ contexts: ["enter-animations", "elements-appearing"],
22413
+ css: "cubic-bezier(0, 0, 0.2, 1)"
22414
+ },
22415
+ exit: {
22416
+ curve: [0.4, 0, 1, 1],
22417
+ meaning: "Withdrawal, giving space. The brief hesitation acknowledges the user; the fast departure avoids lingering. Anything leaving the viewport. Paired with `enter` as a deliberately asymmetric couple -- exits are faster than entrances by design.",
22418
+ contexts: ["exit-animations", "elements-leaving"],
22419
+ css: "cubic-bezier(0.4, 0, 1, 1)"
22420
+ },
22335
22421
  linear: {
22336
22422
  curve: [0, 0, 1, 1],
22337
- meaning: "Linear - constant speed, mechanical feel",
22338
- contexts: ["progress-bars", "loading-spinners", "opacity-fades"],
22423
+ meaning: "Mechanical, procedural, without personality -- a process, not a gesture. Progress and loading, where the system is working rather than interacting. Never for interactive spatial transitions.",
22424
+ contexts: ["progress-bars", "loading-spinners", "opacity-fades", "focus-ring-fade"],
22339
22425
  css: "linear"
22340
22426
  },
22341
- "ease-in": {
22342
- curve: [0.42, 0, 1, 1],
22343
- meaning: "Ease in - starts slow, accelerates (exiting)",
22344
- contexts: ["exit-animations", "elements-leaving"],
22345
- css: "cubic-bezier(0.42, 0, 1, 1)"
22427
+ "spring-smooth": {
22428
+ curve: [0.2, 0.9, 0.3, 1],
22429
+ meaning: "Alive, coming physically to rest -- a critically-damped spring with no overshoot. Page transitions, sheets, large tracked spatial movement. (Johansson 1973 / Pratt 2010: animate motion is perceived as alive and captures attention.)",
22430
+ contexts: ["page-transitions", "sheets", "large-spatial-movement"],
22431
+ css: "cubic-bezier(0.2, 0.9, 0.3, 1)"
22346
22432
  },
22347
- "ease-out": {
22348
- curve: [0, 0, 0.58, 1],
22349
- meaning: "Ease out - starts fast, decelerates (entering)",
22350
- contexts: ["enter-animations", "elements-appearing"],
22351
- css: "cubic-bezier(0, 0, 0.58, 1)"
22352
- },
22353
- "ease-in-out": {
22354
- curve: [0.42, 0, 0.58, 1],
22355
- meaning: "Ease in-out - symmetric acceleration/deceleration",
22356
- contexts: ["state-changes", "transforms", "general-purpose"],
22357
- css: "cubic-bezier(0.42, 0, 0.58, 1)"
22358
- },
22359
- productive: {
22360
- curve: [0.2, 0, 0.38, 0.9],
22361
- meaning: "Productive - quick, efficient, minimal overshoot",
22362
- contexts: ["work-ui", "data-displays", "business-apps"],
22363
- css: "cubic-bezier(0.2, 0, 0.38, 0.9)"
22364
- },
22365
- expressive: {
22366
- curve: [0.4, 0.14, 0.3, 1],
22367
- meaning: "Expressive - dramatic, attention-grabbing",
22368
- contexts: ["marketing", "onboarding", "celebrations", "emphasis"],
22369
- css: "cubic-bezier(0.4, 0.14, 0.3, 1)"
22370
- },
22371
- spring: {
22372
- curve: [0.175, 0.885, 0.32, 1.275],
22373
- meaning: "Spring - bouncy overshoot for playful feel",
22374
- contexts: ["buttons", "icons", "playful-ui", "celebrations"],
22375
- css: "cubic-bezier(0.175, 0.885, 0.32, 1.275)"
22433
+ "spring-snappy": {
22434
+ curve: [0.2, 0.8, 0.2, 1],
22435
+ meaning: "Alive, tight, following input closely -- a tighter spring with less settle and no overshoot. Toggles, presses, interactions that track input.",
22436
+ contexts: ["toggles", "presses", "input-tracking"],
22437
+ css: "cubic-bezier(0.2, 0.8, 0.2, 1)"
22438
+ }
22439
+ };
22440
+ var DEFAULT_MOTION_SEMANTIC_MAPPINGS = {
22441
+ hover: {
22442
+ properties: ["color", "background-color", "border-color"],
22443
+ durationTier: "fast",
22444
+ curve: "standard",
22445
+ reducedMotion: null,
22446
+ category: "interaction",
22447
+ sizeReasoning: "Colour only, no movement -- the cursor is already on target, so the response matches its speed at the fast tier.",
22448
+ meaning: "Hover-state colour transition. Acknowledges pointer presence.",
22449
+ contexts: ["hover", "links", "interactive-surfaces"]
22450
+ },
22451
+ focus: {
22452
+ properties: ["box-shadow", "outline-color"],
22453
+ durationTier: "micro",
22454
+ curve: "linear",
22455
+ reducedMotion: null,
22456
+ category: "interaction",
22457
+ sizeReasoning: "A ring appearing, not moving -- micro tier at linear velocity reads as the system marking focus, not a gesture. (Tailwind ring is box-shadow.)",
22458
+ meaning: "Focus-ring transition. Marks the focused element.",
22459
+ contexts: ["focus-visible", "keyboard-navigation"]
22460
+ },
22461
+ press: {
22462
+ properties: ["transform", "color", "background-color"],
22463
+ durationTier: "micro",
22464
+ curve: "spring-snappy",
22465
+ reducedMotion: { properties: ["color", "background-color"] },
22466
+ category: "interaction",
22467
+ sizeReasoning: "The fastest, tightest feedback -- micro tier with a snappy spring follows the finger. Under reduced motion the transform drops; the colour change survives.",
22468
+ meaning: "Press/active feedback. Confirms the input was received.",
22469
+ contexts: ["press", "active", "buttons"]
22470
+ },
22471
+ toggle: {
22472
+ properties: ["color", "background-color", "transform"],
22473
+ durationTier: "moderate",
22474
+ curve: "spring-snappy",
22475
+ reducedMotion: { properties: ["color", "background-color"] },
22476
+ category: "interaction",
22477
+ sizeReasoning: "A thumb travelling a track is a small, tracked movement -- moderate tier, snappy spring. Reduced motion drops the transform to a colour cross-fade.",
22478
+ meaning: "Toggle/switch state change. Shows the new state.",
22479
+ contexts: ["switch", "toggle", "checkbox"]
22480
+ },
22481
+ "dropdown-in": {
22482
+ properties: ["opacity", "transform"],
22483
+ durationTier: "moderate",
22484
+ curve: "enter",
22485
+ reducedMotion: { properties: ["opacity"], ms: 100 },
22486
+ category: "enter",
22487
+ sizeReasoning: "A dropdown is small and travels a short distance -- moderate tier, one step below the modal, with the arrival curve.",
22488
+ meaning: "Dropdown/menu entrance.",
22489
+ contexts: ["dropdown", "menu", "select", "popover"]
22490
+ },
22491
+ "dropdown-out": {
22492
+ properties: ["opacity", "transform"],
22493
+ durationTier: "fast",
22494
+ curve: "exit",
22495
+ reducedMotion: { properties: ["opacity"], ms: 100 },
22496
+ category: "exit",
22497
+ sizeReasoning: "The exit of a small element -- fast tier (shorter than its moderate entrance) with the departure curve. The user already chose to dismiss it.",
22498
+ meaning: "Dropdown/menu exit.",
22499
+ contexts: ["dropdown", "menu", "select", "popover"]
22500
+ },
22501
+ "modal-in": {
22502
+ properties: ["opacity", "transform"],
22503
+ durationTier: "normal",
22504
+ curve: "enter",
22505
+ reducedMotion: { properties: ["opacity"], ms: 150 },
22506
+ category: "enter",
22507
+ sizeReasoning: "A modal is larger and travels farther than a dropdown -- normal tier, one step up, with the arrival curve. Size and distance produce the longer duration.",
22508
+ meaning: "Modal/dialog entrance.",
22509
+ contexts: ["modal", "dialog", "alert-dialog"]
22510
+ },
22511
+ "modal-out": {
22512
+ properties: ["opacity", "transform"],
22513
+ durationTier: "moderate",
22514
+ curve: "exit",
22515
+ reducedMotion: { properties: ["opacity"], ms: 150 },
22516
+ category: "exit",
22517
+ sizeReasoning: "The modal exit -- moderate tier (shorter than its normal entrance) with the departure curve.",
22518
+ meaning: "Modal/dialog exit.",
22519
+ contexts: ["modal", "dialog", "alert-dialog"]
22520
+ },
22521
+ "sheet-in": {
22522
+ properties: ["transform"],
22523
+ durationTier: "slow",
22524
+ curve: "spring-smooth",
22525
+ reducedMotion: { properties: ["opacity"], ms: 250 },
22526
+ category: "enter",
22527
+ sizeReasoning: "A sheet is the largest spatial movement -- slow tier with the physical settle of a smooth spring, because the user must track it into place. Reduced motion becomes a cross-fade.",
22528
+ meaning: "Sheet/drawer entrance.",
22529
+ contexts: ["sheet", "drawer", "side-panel"]
22530
+ },
22531
+ "sheet-out": {
22532
+ properties: ["transform"],
22533
+ durationTier: "normal",
22534
+ curve: "exit",
22535
+ reducedMotion: { properties: ["opacity"], ms: 250 },
22536
+ category: "exit",
22537
+ sizeReasoning: "The sheet exit -- normal tier (shorter than its slow entrance) with the departure curve.",
22538
+ meaning: "Sheet/drawer exit.",
22539
+ contexts: ["sheet", "drawer", "side-panel"]
22540
+ },
22541
+ expand: {
22542
+ properties: ["grid-template-rows", "opacity"],
22543
+ durationTier: "normal",
22544
+ curve: "enter",
22545
+ reducedMotion: { properties: ["opacity"] },
22546
+ category: "enter",
22547
+ sizeReasoning: "Content unfolding to its natural height -- normal tier with the arrival curve. Transitions grid-template-rows (0fr->1fr), the transitionable stand-in for height:auto. Reduced motion snaps the rows and fades opacity.",
22548
+ meaning: "Expand/reveal collapsible content (accordion, disclosure).",
22549
+ contexts: ["accordion", "collapsible", "disclosure"]
22550
+ },
22551
+ collapse: {
22552
+ properties: ["grid-template-rows", "opacity"],
22553
+ durationTier: "moderate",
22554
+ curve: "exit",
22555
+ reducedMotion: { properties: ["opacity"] },
22556
+ category: "exit",
22557
+ sizeReasoning: "Content folding away -- moderate tier (shorter than its normal expansion) with the departure curve. Reduced motion snaps the rows.",
22558
+ meaning: "Collapse/hide collapsible content (accordion, disclosure).",
22559
+ contexts: ["accordion", "collapsible", "disclosure"]
22560
+ },
22561
+ page: {
22562
+ properties: ["opacity", "transform"],
22563
+ durationTier: "slow",
22564
+ curve: "spring-smooth",
22565
+ reducedMotion: { properties: ["opacity"], ms: 200 },
22566
+ category: "enter",
22567
+ sizeReasoning: "A whole-view transition -- slow tier with the physical settle of a smooth spring, because the user reorients across the largest possible distance.",
22568
+ meaning: "Page/route transition.",
22569
+ contexts: ["page-transition", "route-change", "view-switch"]
22376
22570
  }
22377
22571
  };
22378
22572
  var DEFAULT_DELAY_DEFINITIONS = {
@@ -24997,7 +25191,14 @@ function tryParseUnit(cssValue, registry2 = DEFAULT_UNITS) {
24997
25191
  }
24998
25192
 
24999
25193
  // ../design-tokens/src/generators/motion.ts
25000
- function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25194
+ var LEGACY_EASING_REMAP = {
25195
+ linear: "linear",
25196
+ "ease-out": "enter",
25197
+ "ease-in": "exit",
25198
+ "ease-in-out": "standard",
25199
+ spring: "spring-snappy"
25200
+ };
25201
+ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, semanticMappings) {
25001
25202
  const tokens = [];
25002
25203
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25003
25204
  const { baseTransitionDuration, progressionRatio } = config2;
@@ -25009,52 +25210,44 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25009
25210
  value: `${baseTransitionDuration}ms`,
25010
25211
  category: "motion",
25011
25212
  namespace: "motion",
25012
- semanticMeaning: "Base transition duration - all motion timing derives from this",
25013
- usageContext: ["calculation-reference"],
25213
+ semanticMeaning: "Legacy base transition duration. The perceptual duration scale (motion-duration-*) no longer derives from this; retained only as a reference value and delay-progression base.",
25214
+ usageContext: ["calculation-reference", "delay-base"],
25014
25215
  progressionSystem: progressionRatio,
25015
- description: `Base duration (${baseTransitionDuration}ms). Motion uses ${progressionRatio} progression (ratio ${ratioVal}).`,
25216
+ description: `Base duration (${baseTransitionDuration}ms). Delay tokens use ${progressionRatio} progression (ratio ${ratioVal}); duration tiers are perceptually derived literals.`,
25016
25217
  generatedAt: timestamp,
25017
25218
  containerQueryAware: false,
25018
25219
  reducedMotionAware: true,
25019
25220
  userOverride: null,
25020
25221
  usagePatterns: {
25021
- do: ["Reference as the calculation base"],
25022
- never: ["Change without understanding full motion scale impact"]
25222
+ do: ["Reference as the delay-progression base"],
25223
+ never: ["Assume the perceptual duration tiers derive from this"]
25023
25224
  }
25024
25225
  });
25025
25226
  for (const scale2 of MOTION_DURATION_SCALE) {
25026
25227
  const def = durationDefs[scale2];
25027
25228
  if (!def) continue;
25028
25229
  const scaleIndex = MOTION_DURATION_SCALE.indexOf(scale2);
25029
- let durationMs;
25030
- let mathRelationship;
25031
- if (def.step === "instant") {
25032
- durationMs = 0;
25033
- mathRelationship = "0";
25034
- } else {
25035
- durationMs = Math.round(computeStep(baseTransitionDuration, def.step));
25036
- mathRelationship = def.step === 0 ? `${baseTransitionDuration}ms (base)` : `${baseTransitionDuration} \xD7 ${ratioVal}^${def.step}`;
25037
- }
25230
+ const durationMs = def.ms;
25231
+ const bandNote = def.band ? ` Band: ${def.band}.` : "";
25038
25232
  tokens.push({
25039
25233
  name: `motion-duration-${scale2}`,
25040
- value: durationMs === 0 ? "0ms" : `${durationMs}ms`,
25234
+ value: `${durationMs}ms`,
25041
25235
  category: "motion",
25042
25236
  namespace: "motion",
25043
- semanticMeaning: def.meaning,
25237
+ semanticMeaning: `${def.meaning}${bandNote}`,
25044
25238
  usageContext: def.contexts,
25045
25239
  scalePosition: scaleIndex,
25046
- progressionSystem: progressionRatio,
25047
25240
  motionIntent: def.motionIntent,
25048
25241
  motionDuration: durationMs,
25049
- mathRelationship,
25050
- dependsOn: def.step === "instant" ? [] : ["motion-duration-base"],
25051
- description: `Duration ${scale2}: ${durationMs}ms. ${def.meaning}`,
25242
+ mathRelationship: def.band ? `${durationMs}ms (perceptual: ${def.band})` : `${durationMs}ms`,
25243
+ dependsOn: [],
25244
+ description: `Duration ${scale2}: ${durationMs}ms.${bandNote} ${def.meaning}`,
25052
25245
  generatedAt: timestamp,
25053
25246
  containerQueryAware: false,
25054
25247
  reducedMotionAware: true,
25055
25248
  userOverride: null,
25056
25249
  usagePatterns: {
25057
- do: scale2 === "instant" ? ["Use for prefers-reduced-motion", "Use for disabled animations"] : scale2 === "fast" ? ["Use for hover/focus states", "Use for micro-interactions"] : scale2 === "normal" ? ["Use for most UI transitions", "Use for state changes"] : ["Use for enter/exit animations", "Use for emphasis"],
25250
+ do: scale2 === "instant" ? ["Use for prefers-reduced-motion", "Use for disabled animations"] : scale2 === "micro" || scale2 === "fast" ? ["Use for acknowledgment feedback (hover, focus, press)"] : ["Use for communicative transitions where the user must track the change"],
25058
25251
  never: ["Ignore prefers-reduced-motion", "Use slow animations for frequent actions"]
25059
25252
  }
25060
25253
  });
@@ -25077,10 +25270,10 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25077
25270
  reducedMotionAware: true,
25078
25271
  userOverride: null,
25079
25272
  usagePatterns: {
25080
- do: curve === "linear" ? ["Use for opacity fades", "Use for progress indicators"] : curve === "ease-out" ? ["Use for entering elements", "Use for appearing content"] : curve === "ease-in" ? ["Use for exiting elements", "Use for disappearing content"] : ["Use for general transitions", "Match context to curve feel"],
25273
+ do: curve === "linear" ? ["Use for progress and loading, where the system is working not interacting"] : curve === "enter" ? ["Use for anything entering the viewport"] : curve === "exit" ? ["Use for anything leaving the viewport"] : ["Match the curve register to the interaction feel"],
25081
25274
  never: [
25082
- "Use ease-in for entering (feels sluggish)",
25083
- "Use ease-out for exiting (feels abrupt)"
25275
+ "Use exit for entering (arrives reluctantly)",
25276
+ "Use linear for interactive spatial transitions (reads as mechanical)"
25084
25277
  ]
25085
25278
  }
25086
25279
  });
@@ -25417,13 +25610,13 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25417
25610
  } else {
25418
25611
  const durationDef = durationDefs[anim.duration];
25419
25612
  if (!durationDef) continue;
25420
- const durationMs = durationDef.step === "instant" ? 0 : Math.round(computeStep(baseTransitionDuration, durationDef.step));
25421
- durationValue = `${durationMs}ms`;
25613
+ durationValue = `${durationDef.ms}ms`;
25422
25614
  durationRef = `var(--motion-duration-${anim.duration})`;
25423
25615
  }
25424
- const easingDef = easingDefs[anim.easing];
25616
+ const easingKey = LEGACY_EASING_REMAP[anim.easing] ?? anim.easing;
25617
+ const easingDef = easingDefs[easingKey];
25425
25618
  if (!easingDef) continue;
25426
- const easingRef = `var(--motion-easing-${anim.easing})`;
25619
+ const easingRef = `var(--motion-easing-${easingKey})`;
25427
25620
  const iterations = anim.iterations || "";
25428
25621
  const animValue = iterations ? `${anim.keyframe} ${durationRef} ${easingRef} ${iterations}` : `${anim.keyframe} ${durationRef} ${easingRef}`;
25429
25622
  tokens.push({
@@ -25441,7 +25634,7 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25441
25634
  dependsOn: [
25442
25635
  `motion-keyframe-${anim.keyframe}`,
25443
25636
  ...anim.duration.endsWith("s") || anim.duration.endsWith("ms") ? [] : [`motion-duration-${anim.duration}`],
25444
- `motion-easing-${anim.easing}`
25637
+ `motion-easing-${easingKey}`
25445
25638
  ],
25446
25639
  description: `Animation ${anim.name}: ${anim.meaning}`,
25447
25640
  generatedAt: timestamp,
@@ -25489,14 +25682,10 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25489
25682
  ];
25490
25683
  for (const comp of composites2) {
25491
25684
  const durationDef = durationDefs[comp.duration];
25492
- const easingDef = easingDefs[comp.easing];
25685
+ const easingKey = LEGACY_EASING_REMAP[comp.easing] ?? comp.easing;
25686
+ const easingDef = easingDefs[easingKey];
25493
25687
  if (!durationDef || !easingDef) continue;
25494
- let durationMs;
25495
- if (durationDef.step === "instant") {
25496
- durationMs = 0;
25497
- } else {
25498
- durationMs = Math.round(computeStep(baseTransitionDuration, durationDef.step));
25499
- }
25688
+ const durationMs = durationDef.ms;
25500
25689
  tokens.push({
25501
25690
  name: comp.name,
25502
25691
  value: `${durationMs}ms ${easingDef.css}`,
@@ -25506,27 +25695,54 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs) {
25506
25695
  usageContext: comp.contexts,
25507
25696
  motionDuration: durationMs,
25508
25697
  easingCurve: easingDef.curve,
25509
- easingName: comp.easing,
25510
- dependsOn: [`motion-duration-${comp.duration}`, `motion-easing-${comp.easing}`],
25511
- description: `${comp.meaning}. Combines ${comp.duration} duration with ${comp.easing} easing.`,
25698
+ easingName: easingKey,
25699
+ dependsOn: [`motion-duration-${comp.duration}`, `motion-easing-${easingKey}`],
25700
+ description: `${comp.meaning}. Combines ${comp.duration} duration with ${easingKey} easing.`,
25512
25701
  generatedAt: timestamp,
25513
25702
  containerQueryAware: false,
25514
25703
  reducedMotionAware: true,
25515
25704
  userOverride: null
25516
25705
  });
25517
25706
  }
25707
+ for (const [name, mapping] of Object.entries(semanticMappings)) {
25708
+ tokens.push({
25709
+ name: `motion-semantic-${name}`,
25710
+ value: JSON.stringify({
25711
+ properties: mapping.properties,
25712
+ durationTier: mapping.durationTier,
25713
+ curve: mapping.curve,
25714
+ reducedMotion: mapping.reducedMotion
25715
+ }),
25716
+ category: "motion",
25717
+ namespace: "motion",
25718
+ semanticMeaning: `${mapping.meaning} ${mapping.sizeReasoning}`,
25719
+ usageContext: mapping.contexts,
25720
+ motionIntent: mapping.category === "enter" ? "enter" : mapping.category === "exit" ? "exit" : "transition",
25721
+ generateUtilityClass: true,
25722
+ dependsOn: [`motion-duration-${mapping.durationTier}`, `motion-easing-${mapping.curve}`],
25723
+ description: `Semantic motion motion-${name}: ${mapping.properties.join(", ")} over ${mapping.durationTier} with ${mapping.curve}. ${mapping.sizeReasoning}`,
25724
+ generatedAt: timestamp,
25725
+ containerQueryAware: false,
25726
+ reducedMotionAware: true,
25727
+ userOverride: null,
25728
+ usagePatterns: {
25729
+ do: [`Apply class motion-${name} and let a state/presence change trigger the transition`],
25730
+ never: ["Hardcode a raw numeric duration in place of this token"]
25731
+ }
25732
+ });
25733
+ }
25518
25734
  tokens.push({
25519
25735
  name: "motion-progression",
25520
25736
  value: JSON.stringify({
25521
25737
  ratio: progressionRatio,
25522
25738
  ratioValue: ratioVal,
25523
25739
  baseDuration: baseTransitionDuration,
25524
- note: "Motion timing uses step-based progression (base * ratio^step) for unified feel"
25740
+ note: "Duration tiers are perceptually derived literals (docs/MOTION.md); delay tokens use the workspace progression ratio from the base duration."
25525
25741
  }),
25526
25742
  category: "motion",
25527
25743
  namespace: "motion",
25528
- semanticMeaning: "Metadata about the motion progression system",
25529
- description: `Motion uses ${progressionRatio} progression from ${baseTransitionDuration}ms base.`,
25744
+ semanticMeaning: "Metadata about the motion system",
25745
+ description: `Duration tiers are perceptual literals; delays use ${progressionRatio} progression from ${baseTransitionDuration}ms base.`,
25530
25746
  generatedAt: timestamp,
25531
25747
  containerQueryAware: false,
25532
25748
  userOverride: null
@@ -26436,7 +26652,8 @@ function createGeneratorDefs(colorPaletteBases) {
26436
26652
  config2,
26437
26653
  DEFAULT_DURATION_DEFINITIONS,
26438
26654
  DEFAULT_EASING_DEFINITIONS,
26439
- DEFAULT_DELAY_DEFINITIONS
26655
+ DEFAULT_DELAY_DEFINITIONS,
26656
+ DEFAULT_MOTION_SEMANTIC_MAPPINGS
26440
26657
  )
26441
26658
  },
26442
26659
  {
@@ -27444,7 +27661,7 @@ var RegistryFileSchema = external_exports.object({
27444
27661
  devDependencies: external_exports.array(external_exports.string()).default([])
27445
27662
  // e.g., ["vitest"] - from @devDependencies JSDoc
27446
27663
  });
27447
- var RegistryItemTypeSchema = external_exports.enum(["ui", "primitive", "composite", "rule"]);
27664
+ var RegistryItemTypeSchema = external_exports.enum(["ui", "primitive", "composite", "rule", "substrate"]);
27448
27665
  var RegistryItemIntelligenceSchema = external_exports.object({
27449
27666
  cognitiveLoad: external_exports.number().optional(),
27450
27667
  attentionEconomics: external_exports.string().optional(),
@@ -27472,7 +27689,8 @@ var RegistryIndexSchema = external_exports.object({
27472
27689
  components: external_exports.array(external_exports.string()),
27473
27690
  primitives: external_exports.array(external_exports.string()),
27474
27691
  composites: external_exports.array(external_exports.string()).default([]),
27475
- rules: external_exports.array(external_exports.string()).default([])
27692
+ rules: external_exports.array(external_exports.string()).default([]),
27693
+ substrate: external_exports.array(external_exports.string()).default([])
27476
27694
  });
27477
27695
 
27478
27696
  // src/registry/client.ts
@@ -27481,7 +27699,8 @@ var REGISTRY_SOURCE = {
27481
27699
  ui: { folder: "components", label: "Component" },
27482
27700
  primitive: { folder: "primitives", label: "Primitive" },
27483
27701
  composite: { folder: "composites", label: "Composite" },
27484
- rule: { folder: "rules", label: "Rule" }
27702
+ rule: { folder: "rules", label: "Rule" },
27703
+ substrate: { folder: "substrate", label: "Substrate" }
27485
27704
  };
27486
27705
  var FETCH_ORDER = Object.keys(REGISTRY_SOURCE);
27487
27706
  var RegistryClient = class {
@@ -27517,7 +27736,8 @@ var RegistryClient = class {
27517
27736
  ui: fetchFrom("ui"),
27518
27737
  primitive: fetchFrom("primitive"),
27519
27738
  composite: fetchFrom("composite"),
27520
- rule: fetchFrom("rule")
27739
+ rule: fetchFrom("rule"),
27740
+ substrate: fetchFrom("substrate")
27521
27741
  };
27522
27742
  this.fetchComponent = this.fetchByType.ui;
27523
27743
  this.fetchPrimitive = this.fetchByType.primitive;
@@ -28488,6 +28708,7 @@ function isSharedFile(path) {
28488
28708
  }
28489
28709
  return false;
28490
28710
  }
28711
+ var REACT_FALLBACK_TARGETS = /* @__PURE__ */ new Set(["astro", "vue", "svelte"]);
28491
28712
  function selectFilesForFramework(files, target) {
28492
28713
  const preferredExt = targetToExtension(target);
28493
28714
  const shared = files.filter((f) => isSharedFile(f.path));
@@ -28495,12 +28716,15 @@ function selectFilesForFramework(files, target) {
28495
28716
  if (matched.length > 0) {
28496
28717
  return { files: [...matched, ...shared], fallback: false };
28497
28718
  }
28498
- if (target !== "react") {
28719
+ if (REACT_FALLBACK_TARGETS.has(target)) {
28499
28720
  const fallbackFiles = files.filter((f) => f.path.endsWith(".tsx"));
28500
28721
  if (fallbackFiles.length > 0) {
28501
28722
  return { files: [...fallbackFiles, ...shared], fallback: true };
28502
28723
  }
28503
28724
  }
28725
+ if (target !== "react" && !REACT_FALLBACK_TARGETS.has(target)) {
28726
+ return { files: shared, fallback: false };
28727
+ }
28504
28728
  return { files, fallback: false };
28505
28729
  }
28506
28730
  var TARGET_GATED_COMPOSITE_FILES = [
@@ -28515,12 +28739,13 @@ function selectCompositeFiles(files, target) {
28515
28739
  var FOLDER_NAMES = /* @__PURE__ */ new Set(["composites"]);
28516
28740
  function isAlreadyInstalled(config2, item) {
28517
28741
  if (!config2?.installed) return false;
28518
- const { components, primitives, composites: composites2, rules } = config2.installed;
28742
+ const { components, primitives, composites: composites2, rules, substrate } = config2.installed;
28519
28743
  const bucketByType = {
28520
28744
  ui: components,
28521
28745
  primitive: primitives,
28522
28746
  composite: composites2 ?? [],
28523
- rule: rules ?? []
28747
+ rule: rules ?? [],
28748
+ substrate: substrate ?? []
28524
28749
  };
28525
28750
  return bucketByType[item.type].includes(item.name);
28526
28751
  }
@@ -28539,11 +28764,13 @@ function trackInstalled(config2, items) {
28539
28764
  const installed = config2.installed;
28540
28765
  if (!installed.composites) installed.composites = [];
28541
28766
  if (!installed.rules) installed.rules = [];
28767
+ if (!installed.substrate) installed.substrate = [];
28542
28768
  const bucketByType = {
28543
28769
  ui: installed.components,
28544
28770
  primitive: installed.primitives,
28545
28771
  composite: installed.composites,
28546
- rule: installed.rules
28772
+ rule: installed.rules,
28773
+ substrate: installed.substrate
28547
28774
  };
28548
28775
  for (const item of items) {
28549
28776
  const bucket = bucketByType[item.type];
@@ -28553,11 +28780,12 @@ function trackInstalled(config2, items) {
28553
28780
  installed.primitives.sort();
28554
28781
  installed.composites.sort();
28555
28782
  installed.rules.sort();
28783
+ installed.substrate.sort();
28556
28784
  }
28557
28785
  function rootFor(field, cwd, fallback) {
28558
28786
  return field === void 0 ? fallback : resolveRoot(field, cwd, fallback);
28559
28787
  }
28560
- function transformPath(registryPath, config2, cwd) {
28788
+ function transformPath(registryPath, config2, cwd = process.cwd()) {
28561
28789
  if (!config2) return registryPath;
28562
28790
  const replacements = [
28563
28791
  ["components/ui/", config2.componentsPath, "components/ui"],
@@ -28572,10 +28800,16 @@ function transformPath(registryPath, config2, cwd) {
28572
28800
  }
28573
28801
  return registryPath;
28574
28802
  }
28803
+ function substrateProjectPath(registryPath, config2, cwd = process.cwd()) {
28804
+ const componentsResolved = rootFor(config2?.componentsPath, cwd, "components/ui");
28805
+ const sourceRoot = componentsResolved.replace(/\/?components\/ui$/, "");
28806
+ return sourceRoot ? join7(sourceRoot, registryPath) : registryPath;
28807
+ }
28575
28808
  function fileExists(cwd, relativePath) {
28576
28809
  return existsSync3(join7(cwd, relativePath));
28577
28810
  }
28578
- function transformFileContent(content, config2, fileType = "component", cwd = process.cwd()) {
28811
+ function transformFileContent(content, config2, fileType = "component", cwd = process.cwd(), opts = {}) {
28812
+ const { substrateKinds = [], installPath } = opts;
28579
28813
  let transformed = content;
28580
28814
  const componentsPath = rootFor(config2?.componentsPath, cwd, "components/ui");
28581
28815
  const primitivesPath = rootFor(config2?.primitivesPath, cwd, "lib/primitives");
@@ -28590,26 +28824,23 @@ function transformFileContent(content, config2, fileType = "component", cwd = pr
28590
28824
  /from\s+['"]\.\.\/primitives\/([^'"]+)['"]/g,
28591
28825
  `from '@/${aliasPrimitives}/$1'`
28592
28826
  );
28593
- const aliasSibling = fileType === "primitive" ? aliasPrimitives : aliasComponents;
28827
+ const substrateOwnDir = fileType === "substrate" && installPath ? stripSourceRoot(dirname(installPath)) : null;
28828
+ const aliasSibling = fileType === "primitive" ? aliasPrimitives : substrateOwnDir ?? aliasComponents;
28594
28829
  transformed = transformed.replace(/from\s+['"]\.\/([^'"]+)['"]/g, `from '@/${aliasSibling}/$1'`);
28595
- const aliasLib = stripSourceRoot(dirname(primitivesPath));
28596
- transformed = transformed.replace(
28597
- /from\s+['"]\.\.\/lib\/([^'"]+)['"]/g,
28598
- `from '@/${aliasLib}/$1'`
28599
- );
28600
- const componentsMatch = aliasComponents.match(/^(.*)components\/ui$/);
28601
- const aliasHooks = componentsMatch ? `${componentsMatch[1]}hooks`.replace(/^\//, "") : "hooks";
28602
- transformed = transformed.replace(
28603
- /from\s+['"]\.\.\/hooks\/([^'"]+)['"]/g,
28604
- `from '@/${aliasHooks}/$1'`
28605
- );
28830
+ if (substrateKinds.length > 0) {
28831
+ const kindAlternation = substrateKinds.join("|");
28832
+ transformed = transformed.replace(
28833
+ new RegExp(`from\\s+['"](?:\\.\\./){1,2}(${kindAlternation})/([^'"]+)['"]`, "g"),
28834
+ "from '@/$1/$2'"
28835
+ );
28836
+ }
28606
28837
  transformed = transformed.replace(
28607
- /from\s+['"]\.\.\/(?!lib\/|hooks\/)([^'"]+)['"]/g,
28838
+ /from\s+['"]\.\.\/([^'"]+)['"]/g,
28608
28839
  `from '@/${aliasComponents}/$1'`
28609
28840
  );
28610
28841
  return transformed;
28611
28842
  }
28612
- async function installItem(cwd, item, options, config2) {
28843
+ async function installItem(cwd, item, options, config2, substrateKinds = []) {
28613
28844
  const installedFiles = [];
28614
28845
  let skipped = false;
28615
28846
  let filesToInstall = item.files;
@@ -28625,11 +28856,17 @@ async function installItem(cwd, item, options, config2) {
28625
28856
  message: `No ${targetToExtension(target)} version available for ${item.name}. Installing React version.`
28626
28857
  });
28627
28858
  }
28859
+ const hasComponentFile = selection.files.some((f) => !isSharedFile(f.path));
28860
+ if (!hasComponentFile) {
28861
+ throw new Error(
28862
+ `No ${targetToExtension(target)} version available for "${item.name}" and no compatible fallback exists.`
28863
+ );
28864
+ }
28628
28865
  } else if (item.type === "composite") {
28629
28866
  filesToInstall = selectCompositeFiles(item.files, getComponentTarget(config2));
28630
28867
  }
28631
28868
  for (const file2 of filesToInstall) {
28632
- const projectPath2 = transformPath(file2.path, config2, cwd);
28869
+ const projectPath2 = item.type === "substrate" ? substrateProjectPath(file2.path, config2, cwd) : transformPath(file2.path, config2, cwd);
28633
28870
  const targetPath = join7(cwd, projectPath2);
28634
28871
  if (fileExists(cwd, projectPath2)) {
28635
28872
  if (!options.overwrite) {
@@ -28644,8 +28881,11 @@ async function installItem(cwd, item, options, config2) {
28644
28881
  }
28645
28882
  }
28646
28883
  await mkdir2(dirname(targetPath), { recursive: true });
28647
- const fileType = item.type === "primitive" ? "primitive" : "component";
28648
- const transformedContent = transformFileContent(file2.content, config2, fileType, cwd);
28884
+ const fileType = item.type === "primitive" ? "primitive" : item.type === "substrate" ? "substrate" : "component";
28885
+ const transformedContent = transformFileContent(file2.content, config2, fileType, cwd, {
28886
+ substrateKinds,
28887
+ installPath: file2.path
28888
+ });
28649
28889
  await writeFile2(targetPath, transformedContent, "utf-8");
28650
28890
  installedFiles.push(projectPath2);
28651
28891
  }
@@ -28758,6 +28998,11 @@ async function add(componentArgs, options) {
28758
28998
  return;
28759
28999
  }
28760
29000
  }
29001
+ const substrateKinds = [
29002
+ ...new Set(
29003
+ allItems.filter((item) => item.type === "substrate").map((item) => item.files[0]?.path.split("/")[0]).filter((segment) => Boolean(segment))
29004
+ )
29005
+ ];
28761
29006
  const installed = [];
28762
29007
  const skipped = [];
28763
29008
  const installedItems = [];
@@ -28781,7 +29026,7 @@ async function add(componentArgs, options) {
28781
29026
  });
28782
29027
  }
28783
29028
  try {
28784
- const result = await installItem(cwd, item, options, config2);
29029
+ const result = await installItem(cwd, item, options, config2, substrateKinds);
28785
29030
  if (result.installed) {
28786
29031
  installed.push(item.name);
28787
29032
  installedItems.push(item);
@@ -19,13 +19,19 @@ declare const RegistryFileSchema: z.ZodObject<{
19
19
  }, z.core.$strip>;
20
20
  type RegistryFile = z.infer<typeof RegistryFileSchema>;
21
21
  /**
22
- * Item type in registry
22
+ * Item type in registry.
23
+ * `substrate` is the behavior-layer runtime (the score contract, compose
24
+ * slices, reactive hooks -- everything under ui/src outside components /
25
+ * primitives / composites). It is a copy-in shared dependency resolved like a
26
+ * primitive; the specific dir it installs into (`@/lib`, `@/hooks`, ...) is
27
+ * carried in the item's file path, so the type never grows per kind.
23
28
  */
24
29
  declare const RegistryItemTypeSchema: z.ZodEnum<{
25
30
  ui: "ui";
26
31
  primitive: "primitive";
27
32
  composite: "composite";
28
33
  rule: "rule";
34
+ substrate: "substrate";
29
35
  }>;
30
36
  type RegistryItemType = z.infer<typeof RegistryItemTypeSchema>;
31
37
  /**
@@ -59,6 +65,7 @@ declare const RegistryItemSchema: z.ZodObject<{
59
65
  primitive: "primitive";
60
66
  composite: "composite";
61
67
  rule: "rule";
68
+ substrate: "substrate";
62
69
  }>;
63
70
  description: z.ZodOptional<z.ZodString>;
64
71
  primitives: z.ZodArray<z.ZodString>;
@@ -93,6 +100,7 @@ declare const RegistryIndexSchema: z.ZodObject<{
93
100
  primitives: z.ZodArray<z.ZodString>;
94
101
  composites: z.ZodDefault<z.ZodArray<z.ZodString>>;
95
102
  rules: z.ZodDefault<z.ZodArray<z.ZodString>>;
103
+ substrate: z.ZodDefault<z.ZodArray<z.ZodString>>;
96
104
  }, z.core.$strip>;
97
105
  type RegistryIndex = z.infer<typeof RegistryIndexSchema>;
98
106
 
@@ -8,7 +8,7 @@ var RegistryFileSchema = z.object({
8
8
  devDependencies: z.array(z.string()).default([])
9
9
  // e.g., ["vitest"] - from @devDependencies JSDoc
10
10
  });
11
- var RegistryItemTypeSchema = z.enum(["ui", "primitive", "composite", "rule"]);
11
+ var RegistryItemTypeSchema = z.enum(["ui", "primitive", "composite", "rule", "substrate"]);
12
12
  var RegistryItemIntelligenceSchema = z.object({
13
13
  cognitiveLoad: z.number().optional(),
14
14
  attentionEconomics: z.string().optional(),
@@ -36,7 +36,8 @@ var RegistryIndexSchema = z.object({
36
36
  components: z.array(z.string()),
37
37
  primitives: z.array(z.string()),
38
38
  composites: z.array(z.string()).default([]),
39
- rules: z.array(z.string()).default([])
39
+ rules: z.array(z.string()).default([]),
40
+ substrate: z.array(z.string()).default([])
40
41
  });
41
42
  export {
42
43
  RegistryFileSchema,
package/package.json CHANGED
@@ -1,13 +1,22 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.71",
3
+ "version": "0.0.72",
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",
7
- "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/rafters-studio/rafters.git",
10
+ "directory": "packages/cli"
11
+ },
8
12
  "bin": {
9
13
  "rafters": "./dist/index.js"
10
14
  },
15
+ "files": [
16
+ "dist",
17
+ "LICENSE"
18
+ ],
19
+ "type": "module",
11
20
  "exports": {
12
21
  ".": "./dist/index.js",
13
22
  "./registry/types": {
@@ -15,10 +24,9 @@
15
24
  "import": "./dist/registry/types.js"
16
25
  }
17
26
  },
18
- "files": [
19
- "dist",
20
- "LICENSE"
21
- ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
22
30
  "scripts": {
23
31
  "build": "tsup",
24
32
  "dev": "tsup --watch",
@@ -57,13 +65,5 @@
57
65
  "typescript": "catalog:",
58
66
  "vitest": "catalog:",
59
67
  "zocker": "catalog:"
60
- },
61
- "repository": {
62
- "type": "git",
63
- "url": "git+https://github.com/rafters-studio/rafters.git",
64
- "directory": "packages/cli"
65
- },
66
- "publishConfig": {
67
- "access": "public"
68
68
  }
69
69
  }