synthesisui 0.16.98 → 0.16.100

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.
@@ -305,7 +305,24 @@ sketch) {
305
305
  "")
306
306
  : "";
307
307
  const paints = rootPainted ? { classes: rootPainted } : {};
308
- if (rootRaw && /^[A-Z]/.test(rootRaw)) {
308
+ /**
309
+ * A LIBRARY ROOT WHOSE NAME IS NOT DOTTED.
310
+ *
311
+ * Three components return a Base UI primitive imported under a single word -
312
+ * `Pill` and `SelectionCard` return `Toggle`, `RadioCardGroup` returns
313
+ * `RadioGroup` - and `"root": "Toggle"` would point at THEIR own Toggle (a
314
+ * switch), inventing a relation that does not exist. There was no way to say
315
+ * "a library component with a bare name", so `root` was left out on all three
316
+ * (not-expressed.md, dono, 01/08).
317
+ *
318
+ * `from` says it: the PACKAGE disambiguates what the name cannot. It wins over
319
+ * the name, because a caller that sends both is telling us where it came from.
320
+ */
321
+ const rootFrom = String(rootObj?.from ?? "").trim();
322
+ if (rootFrom) {
323
+ rootOut = { from: rootFrom, ...paints };
324
+ }
325
+ else if (rootRaw && /^[A-Z]/.test(rootRaw)) {
309
326
  if (rootRaw.includes(".")) {
310
327
  rootOut = { from: rootRaw, ...paints };
311
328
  }
@@ -437,6 +437,25 @@ async function resolveAtNodes(root, entries) {
437
437
  ],
438
438
  }
439
439
  : {}),
440
+ /**
441
+ * THE PARTS, which is where a provider-rooted component keeps
442
+ * EVERYTHING.
443
+ *
444
+ * `Tooltip` returns `<BaseTooltip.Provider>` - a context provider that
445
+ * renders no element and carries no style - and its whole look (fill,
446
+ * radius, padding, shadow) is on the popup PART. The merge carried
447
+ * `base` and `layers` and stopped there, so the validator judged a
448
+ * component with no declarations anywhere and said BELOW THE FLOOR
449
+ * about one that draws fine (not-expressed.md, dono, 01/08).
450
+ */
451
+ ...(look?.parts && Object.keys(look.parts).length > 0
452
+ ? {
453
+ parts: {
454
+ ...look.parts,
455
+ ...(recipe0.parts ?? {}),
456
+ },
457
+ }
458
+ : {}),
440
459
  /**
441
460
  * THE ROOT TAG TRAVELS, so the validator can name the KIND.
442
461
  *
@@ -82,8 +82,24 @@ function staticCallClasses(body) {
82
82
  argStart = i + 1;
83
83
  }
84
84
  }
85
+ /**
86
+ * A COMMENT BEFORE THE STRING IS STILL THE STRING.
87
+ *
88
+ * Real code annotates these lists - `// Light mode`, `// Shadow with subtle
89
+ * brand tint` - and the argument then reads as `// Light mode\n"bg-darkgray-800
90
+ * …"`, which is not a bare literal, so it was dropped. The Tooltip kept only
91
+ * its last two arguments (the two with no comment above them) and lost its
92
+ * fill, radius, padding and shadow - and it only came to light because the
93
+ * floor check flagged it (not-expressed.md, dono, 01/08). Measured: SIX of 36
94
+ * components in that library annotate a `cn()` this way.
95
+ */
85
96
  const classes = args
86
- .map((a) => a.trim())
97
+ .map((a) => a
98
+ // block comments first, so a `/* … */` spanning the line cannot leave
99
+ // a stray `*/` behind for the line rule to keep
100
+ .replace(/\/\*[\s\S]*?\*\//g, "")
101
+ .replace(/(^|\n)\s*\/\/[^\n]*/g, "$1")
102
+ .trim())
87
103
  .filter((a) => /^(["'`]).*\1$/.test(a) && !a.includes("${"))
88
104
  .map((a) => a.slice(1, -1).trim())
89
105
  .filter(Boolean);
@@ -241,6 +241,15 @@ const TAILWIND_COLOR = {
241
241
  white: "#ffffff",
242
242
  black: "#000000",
243
243
  transparent: "transparent",
244
+ /**
245
+ * KEYWORDS ARE VALUES. `bg-current` is `background-color: currentColor` and
246
+ * `text-inherit` is `color: inherit` - real CSS, no palette needed, and they
247
+ * were being dropped as unreadable. Nine utilities across the owner's library,
248
+ * and `bg-current` is how a spinner takes its colour from its parent - drop it
249
+ * and the spinner is invisible (not-expressed.md, dono, 01/08).
250
+ */
251
+ current: "currentColor",
252
+ inherit: "inherit",
244
253
  // zinc and blue, because a real Select is styled entirely in them and every
245
254
  // reference read as nothing (test14, 01/08). Tailwind's own values, verbatim.
246
255
  "zinc-50": "#fafafa",
@@ -351,6 +360,9 @@ const STATE_SPELLING = {
351
360
  first: "first",
352
361
  last: "last",
353
362
  empty: "empty",
363
+ /** Base UI's enter/exit faces - `data-[starting-style]:opacity-0`. */
364
+ "starting-style": "startingStyle",
365
+ "ending-style": "endingStyle",
354
366
  };
355
367
  const DATA_STATE = /^data-\[(?:state=)?([a-z-]+)\]$/;
356
368
  /**
@@ -837,6 +849,33 @@ function refFor(name) {
837
849
  return `{color.${name}}`;
838
850
  return `{color.${m[1]}.${m[2]}}`;
839
851
  }
852
+ /** Their breakpoint names, as Tailwind ships them. A project that renames these
853
+ * declares its own, and the census carries that - this is the default ladder. */
854
+ const BREAKPOINT = /^(sm|md|lg|xl|2xl|min-\[[^\]]+\]|max-\[[^\]]+\])$/;
855
+ /**
856
+ * The layer for one set of conditions, created once and reused.
857
+ *
858
+ * Reused rather than appended per utility, because `dark:hover:bg-x` and
859
+ * `dark:hover:border-y` are ONE conditional look with two declarations - two
860
+ * layers with identical conditions would compile to two identical rules.
861
+ */
862
+ function layerFor(out, when, at) {
863
+ out.layers ??= [];
864
+ const same = out.layers.find((l) => (l.at ?? null) === (at ?? null) &&
865
+ (l.when?.state ?? null) === (when.state ?? null) &&
866
+ (l.when?.within ?? null) === (when.within ?? null) &&
867
+ (l.when?.scheme ?? null) === (when.scheme ?? null) &&
868
+ l.when?.variant == null);
869
+ if (same)
870
+ return same;
871
+ const created = {
872
+ ...(Object.keys(when).length > 0 ? { when } : {}),
873
+ ...(at ? { at } : {}),
874
+ style: {},
875
+ };
876
+ out.layers.push(created);
877
+ return created;
878
+ }
840
879
  /**
841
880
  * Read every class in a component's root element and route each declaration by
842
881
  * its modifier.
@@ -1005,20 +1044,63 @@ export function transcribe(classes, declared) {
1005
1044
  if (rest.length === 0) {
1006
1045
  target = isDark ? out.dark : out.base;
1007
1046
  }
1008
- else if (rest.length === 1 && !isDark) {
1009
- const raw = STATE_SPELLING[rest[0]] != null
1010
- ? rest[0]
1011
- : (DATA_STATE.exec(rest[0])?.[1] ?? null);
1012
- const state = raw ? (STATE_SPELLING[raw] ?? null) : null;
1013
- if (state)
1014
- target = out.states[state] ?? (out.states[state] = {});
1015
- else
1016
- unslotted = true;
1017
- }
1018
1047
  else {
1019
- // A state under `dark:` is the dark scheme's version of that state, and a
1020
- // recipe has no slot for it.
1021
- unslotted = true;
1048
+ /**
1049
+ * EVERY MODIFIER GETS A SLOT, and the contract has had one all along.
1050
+ *
1051
+ * This used to route a bare state and drop everything else, on a comment
1052
+ * that said "a recipe has no slot for it" - true when it was written and
1053
+ * false since the transcription grew `layers`, which hold `scheme`,
1054
+ * `state`, `within` and `at` TOGETHER. Measured on the owner's library:
1055
+ * 29 real decisions dropped on that stale line, most of them a `dark:`
1056
+ * over a state (not-expressed.md, dono, 01/08).
1057
+ *
1058
+ * A recipe that reconstructs the component must carry them, so the router
1059
+ * reads the whole modifier list and only gives up on a modifier that
1060
+ * genuinely has no home - and then it SAYS so.
1061
+ */
1062
+ const when = {};
1063
+ let at;
1064
+ for (const mod of rest) {
1065
+ /** `group-hover:`, `group-data-[checked]:` - the PARENT's state. */
1066
+ const group = /^group-(.+)$/.exec(mod);
1067
+ const bare = group ? group[1] : mod;
1068
+ const raw = STATE_SPELLING[bare] != null
1069
+ ? bare
1070
+ : (DATA_STATE.exec(bare)?.[1] ?? null);
1071
+ const state = raw ? (STATE_SPELLING[raw] ?? null) : null;
1072
+ if (state) {
1073
+ if (group)
1074
+ when.within = state;
1075
+ else
1076
+ when.state = state;
1077
+ continue;
1078
+ }
1079
+ /** `md:`, `lg:` - a breakpoint of theirs, by their own name. */
1080
+ if (BREAKPOINT.test(mod) && !at) {
1081
+ at = mod;
1082
+ continue;
1083
+ }
1084
+ unslotted = true;
1085
+ }
1086
+ if (!unslotted) {
1087
+ if (isDark)
1088
+ when.scheme = "dark";
1089
+ // A bare single state with no scheme keeps its own slot: `states` is what
1090
+ // the compiler and every lens already read, and moving it to a layer
1091
+ // would be a shape change nothing asked for.
1092
+ if (when.state &&
1093
+ !when.within &&
1094
+ !when.scheme &&
1095
+ !at &&
1096
+ rest.length === 1) {
1097
+ target = out.states[when.state] ?? (out.states[when.state] = {});
1098
+ }
1099
+ else {
1100
+ const layer = layerFor(out, when, at);
1101
+ target = layer.style;
1102
+ }
1103
+ }
1022
1104
  }
1023
1105
  const decl = readUtility(utility, declared);
1024
1106
  if (unslotted) {
@@ -622,6 +622,17 @@ depth appears anyway.
622
622
  }
623
623
  \`\`\`
624
624
 
625
+ **A library root whose name is not dotted needs its PACKAGE.** \`Pill\` returns Base UI's
626
+ \`Toggle\`, and \`"root": "Toggle"\` would point at THEIR own Toggle - a switch - inventing a
627
+ relation that does not exist. Send where it came from instead:
628
+
629
+ \`\`\`json
630
+ "Pill": { "root": { "from": "@base-ui/react/toggle" }, "anatomy": [ … ] }
631
+ \`\`\`
632
+
633
+ \`Radio.Root\` needs none of this: the dot already says it is somebody's namespace. Leaving the
634
+ root out was the old workaround and it cost the surface its name (not-expressed.md, dono, 01/08).
635
+
625
636
  **The root takes what the PARENT paints on it.** When the returned root carries an override at
626
637
  the call site - \`MetricCard\` returns \`<Card className="flex-1 dark:bg-darkgray-300">\` - send the
627
638
  object form and point \`at\` the sketch node: \`{ "name": "Card", "at": 1 }\`. The string form still
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.98",
3
+ "version": "0.16.100",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {