synthesisui 0.16.86 → 0.16.87

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.
@@ -194,6 +194,15 @@ root) {
194
194
  : [];
195
195
  const t = transcribe(classes, declared);
196
196
  parts[key] = { base: t.base, dark: t.dark, states: t.states };
197
+ /**
198
+ * A drop on a PART is still a drop. The root path already says these out loud;
199
+ * this one silently swallowed `p-spacing-sm` on a real menu popup, so the part
200
+ * arrived with no padding and the not-expressed file had to discover it from
201
+ * the outside (dono, 01/08).
202
+ */
203
+ for (const cls of t.unreadable) {
204
+ notes.push(`part \`${key}\`: \`${cls}\` starts like a design decision and reads as nothing - usually a typo in the source, and the element renders without it today`);
205
+ }
197
206
  node_.part = key;
198
207
  }
199
208
  const text = typeof node.text === "string" ? node.text.trim() : "";
@@ -799,6 +799,31 @@ export async function takeCensus(root, opts) {
799
799
  console.log(body(line));
800
800
  console.log(body(paint.faint("Do not go looking for any of this - it is measured, and a sweep would bring back a different slice than the next one.")));
801
801
  }
802
+ /**
803
+ * THE DROPS, SAID OUT LOUD. `p-spacing-sm` on a real menu popup read as nothing and
804
+ * the popup shipped with no padding, invisibly - "if the compiler drops them, the drop
805
+ * is invisible from here" was the not-expressed file calling this exact hole (dono,
806
+ * 01/08). More often than not the class is malformed in THEIR source, which makes this
807
+ * a defect report about their code, not an apology about ours.
808
+ */
809
+ const unreadableByComponent = Object.entries(looks)
810
+ .filter(([, look]) => (look.unreadable ?? []).length > 0)
811
+ .map(([component, look]) => ({
812
+ component,
813
+ classes: look.unreadable ?? [],
814
+ }));
815
+ if (unreadableByComponent.length > 0) {
816
+ const total = unreadableByComponent.reduce((n, u) => n + u.classes.length, 0);
817
+ console.log("");
818
+ console.log(section("Design classes that read as nothing"));
819
+ console.log(body(`${total} class${total === 1 ? "" : "es"} start like a design decision and resolve to nothing - usually a typo in the source, and the element renders without that decision today. Nothing was guessed on their behalf:`));
820
+ for (const u of unreadableByComponent.slice(0, 8)) {
821
+ console.log(body(paint.faint(` ${u.component}: ${u.classes.slice(0, 4).join(", ")}`)));
822
+ }
823
+ if (unreadableByComponent.length > 8) {
824
+ console.log(body(paint.faint(` and ${unreadableByComponent.length - 8} more components`)));
825
+ }
826
+ }
802
827
  const coverageLines = describeCoverage(coverage);
803
828
  if (coverageLines.length > 0) {
804
829
  console.log("");
@@ -1590,6 +1615,7 @@ async function resolveReadParts(census, root) {
1590
1615
  dark: {},
1591
1616
  states: {},
1592
1617
  skipped: [],
1618
+ unreadable: [],
1593
1619
  literals: [],
1594
1620
  fromToken: 0,
1595
1621
  fromLiteral: 0,
@@ -447,6 +447,22 @@ function resolveArbitrary(inner, declared) {
447
447
  return null;
448
448
  const v = /^var\(\s*(--[a-zA-Z0-9_-]+)\s*(?:,[^)]*)?\)$/.exec(raw);
449
449
  if (!v) {
450
+ /**
451
+ * `[--text-body-s]` IS the var() shorthand - Tailwind v3 defined it that way, so the
452
+ * author wrote a working thing once. v4 dropped the shorthand, which is why their
453
+ * inputs render at the inherited size today; the token they NAMED is still the
454
+ * intent, and a recipe is the rules to rebuild the component, not a photograph of
455
+ * the regression. Their own defect still gets reported - see `unreadable`.
456
+ *
457
+ * A bare `--name` they never declared resolves to nothing under either semantics,
458
+ * and carrying it would emit `font-size: --x`, which is invalid CSS.
459
+ */
460
+ if (raw.startsWith("--")) {
461
+ const name = raw.split(",")[0].trim();
462
+ return declared.has(name)
463
+ ? { value: tokenRefFor(name), token: name }
464
+ : null;
465
+ }
450
466
  // A literal in brackets is still a real value somebody typed.
451
467
  return /[<>{}@;]/.test(raw) ? null : { value: raw };
452
468
  }
@@ -540,12 +556,18 @@ export function readInlineStyle(source) {
540
556
  }
541
557
  /** Properties React leaves unitless, so a bare number is not pixels. */
542
558
  const UNITLESS = /^(opacity|zIndex|flex|flexGrow|flexShrink|order|lineHeight|fontWeight|zoom|columnCount|fillOpacity|strokeOpacity|animationIterationCount)$/;
559
+ /**
560
+ * A prefix this reader claims to understand. A class that starts with one of these and
561
+ * still reads as nothing is a failed DESIGN read, not layout plumbing.
562
+ */
563
+ const DESIGN_PREFIX = /^(?:p|px|py|pt|pr|pb|pl|m|mx|my|gap|rounded|text|font|bg|border|shadow|w|h|size|min-w|min-h|max-w|max-h|leading|tracking|opacity|fill|stroke)-\S/;
543
564
  export function transcribe(classes, declared) {
544
565
  const out = {
545
566
  base: {},
546
567
  states: {},
547
568
  dark: {},
548
569
  skipped: [],
570
+ unreadable: [],
549
571
  literals: [],
550
572
  fromToken: 0,
551
573
  fromLiteral: 0,
@@ -585,7 +607,16 @@ export function transcribe(classes, declared) {
585
607
  out.skipped.push(cls);
586
608
  continue;
587
609
  }
588
- if (!decl || !target)
610
+ if (!decl) {
611
+ // The prefix says design intent; the rest resolved to nothing. Malformed in their
612
+ // source more often than not - `p-spacing-sm` for `p-sm` - and a silent drop here
613
+ // is how a menu popup ships with no padding and nobody is told.
614
+ if (DESIGN_PREFIX.test(utility) && !out.unreadable.includes(cls)) {
615
+ out.unreadable.push(cls);
616
+ }
617
+ continue;
618
+ }
619
+ if (!target)
589
620
  continue;
590
621
  /**
591
622
  * EVERYTHING IT READS TRAVELS, literals included - as DATA.
@@ -543,6 +543,20 @@ extensions are configured at construction, not toggled later
543
543
  of their manifest and attaches it (\`"^2.1.0 in packages/ui"\`), so the rule does not go stale the
544
544
  day they upgrade and the information is not lost either.
545
545
 
546
+ ### If \`recipe_vocabulary\` is missing, the MCP registration is stale
547
+
548
+ A real run reported "recipe_vocabulary and validate_recipe do not exist on this server" and
549
+ sent every anatomy unvalidated - the repo's \`.mcp.json\` pinned \`synthesisui@0.16.78\` while
550
+ the import itself ran unpinned at latest: **two CLI versions in one repo, in one run** (dono,
551
+ 01/08). Before concluding a tool does not exist, check the registration:
552
+
553
+ \`\`\`json
554
+ "args": ["synthesisui@latest", "mcp"]
555
+ \`\`\`
556
+
557
+ Tell the person to fix the pin and restart the server. Do not work around a stale server by
558
+ skipping validation - unvalidated is how correct work arrives as nothing.
559
+
546
560
  ### Ask what a recipe can hold, BEFORE you write one
547
561
 
548
562
  \`\`\`
@@ -669,6 +683,26 @@ found them wastes a turn and risks contradicting the measurement:
669
683
  What is still yours: the anatomy tree, the part names, the frontiers, the roles, the
670
684
  concept, and the rules that are not about a class name.
671
685
 
686
+ ### A scale's INTENT is a rule, and their docs already wrote it
687
+
688
+ A three-step spacing scale with documented meaning - sm "close UI relationships", md
689
+ "standard internal padding", lg "layout gutters" - carries a law nothing else expresses:
690
+ **this system has exactly three steps, and a fourth is a mistake.** A real import read the
691
+ scale and lost the sentence (dono, 01/08).
692
+
693
+ When their docs (a \`Geometry.mdx\`, a design-tokens page, comments beside the \`@theme\`)
694
+ state what a step is FOR, send it as system-wide rules - \`applies: []\`, \`fact: true\`, the
695
+ doc as evidence:
696
+
697
+ \`\`\`
698
+ this system's spacing has exactly three steps - sm for close relationships inside a
699
+ control, md for standard internal padding, lg for layout gutters. A fourth step is a
700
+ mistake, not a gap (Geometry.mdx)
701
+ \`\`\`
702
+
703
+ \`applies: []\` is what makes it reach CLAUDE.md and every prompt after - the same route the
704
+ architecture rule travels.
705
+
672
706
  ### How much to send
673
707
 
674
708
  Send an anatomy for **every component with visible structure**, which is nearly all of them. A
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.86",
3
+ "version": "0.16.87",
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": {