synthesisui 0.16.47 → 0.16.49

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.
@@ -336,7 +336,18 @@ export async function takeCensus(root) {
336
336
  }
337
337
  }
338
338
  const d = diagnose(reports);
339
- const components = tallyToInventory(tally);
339
+ const inventory = tallyToInventory(tally);
340
+ // The verdict travels WITH the payload: the platform reads one reading rather
341
+ // than computing a second opinion from the same numbers, which is how two
342
+ // implementations of the same judgement start disagreeing.
343
+ const verdicts = new Map(crosswalk(inventory).map((r) => [
344
+ r.component.name,
345
+ { bucket: r.bucket, canonical: r.canonical, because: r.because },
346
+ ]));
347
+ const components = inventory.map((c) => ({
348
+ ...c,
349
+ ...(verdicts.get(c.name) ?? {}),
350
+ }));
340
351
  const pkgRaw = await readFile(join(root, "package.json"), "utf8").catch(() => null);
341
352
  let name = null;
342
353
  if (pkgRaw) {
@@ -477,6 +488,20 @@ function printCrosswalk(mine) {
477
488
  show(`Already in the catalogue (${exists.length})`, exists, 6);
478
489
  show(`The same decision, twice (${nearly.length})`, nearly, 6);
479
490
  show(`Only yours (${exclusive.length})`, exclusive, 8);
491
+ // Counted, never listed: four icons and a provider under "only yours" pad the
492
+ // one list worth reading with the least interesting thing in it.
493
+ const icons = of("icon").length;
494
+ const providers = of("provider").length;
495
+ if (icons > 0 || providers > 0) {
496
+ const bits = [
497
+ icons > 0 &&
498
+ `${icons} icon${icons === 1 ? "" : "s"} (a library, not recipes)`,
499
+ providers > 0 &&
500
+ `${providers} provider${providers === 1 ? "" : "s"} (no UI of their own)`,
501
+ ].filter(Boolean);
502
+ console.log(body(paint.faint(`Set aside: ${bits.join(" · ")}`)));
503
+ console.log("");
504
+ }
480
505
  console.log(body(paint.dim("Nothing was mapped. This is a reading - the adopting is a decision you make.")));
481
506
  }
482
507
  function printAgentContract() {
@@ -59,7 +59,19 @@ function looksLikeType(source, at) {
59
59
  * of copy wearing the shape of a variant axis. An axis is a closed set somebody
60
60
  * designed; a title is whatever that screen happens to say.
61
61
  */
62
- const NOT_A_DECISION = /^(class|className|style|key|ref|id|href|src|alt|type|name|value|placeholder|children|on[A-Z]|data-|aria-|title|label|text|heading|subtitle|caption|description|message|content|tooltip|hint|error|helper)$|^(class|className|on[A-Z]|data-|aria-)/;
62
+ const CONTENT_WORD = "class|className|style|key|ref|id|href|src|alt|type|name|value|placeholder|children|title|label|text|heading|subtitle|caption|description|message|content|tooltip|hint|error|helper";
63
+ /**
64
+ * Whole name, or the TAIL of a camelCase compound.
65
+ *
66
+ * `bodyClassName` and `infoContent` walked straight past a whole-name match
67
+ * (dono, 30/07), and `infoContent` brought four sentences of screen copy into
68
+ * what is supposed to be a list of variant axes. The tail is what says what a
69
+ * prop IS: `infoContent` is content, `bodyClassName` is a class name.
70
+ *
71
+ * Matching the tail rather than anywhere keeps `titleTone` - an axis whose name
72
+ * merely starts with a content word.
73
+ */
74
+ const NOT_A_DECISION = new RegExp(`^(?:${CONTENT_WORD})$|[a-z](?:${CONTENT_WORD.replace(/\b([a-z])/g, (_, c) => `[${c}${c.toUpperCase()}]`)})$|^(?:on[A-Z]|data-|aria-)`);
63
75
  /** `import X, { A, B as C } from "spec"` - who owns each local name. */
64
76
  const IMPORT = /import\s+(?:type\s+)?([\s\S]*?)\s+from\s+["']([^"']+)["']/g;
65
77
  function importedNames(source, internal = []) {
@@ -207,6 +207,26 @@ export function axisOverlap(a, b) {
207
207
  shared += 1;
208
208
  return shared / new Set([...va, ...vb]).size;
209
209
  }
210
+ /**
211
+ * Names that are not components in the sense this crosswalk means.
212
+ *
213
+ * An ICON is a glyph in a set, and a design system models it as a library
214
+ * rather than as a recipe with variant axes - so listing four of them under
215
+ * "only yours" pads the interesting list with the least interesting thing in
216
+ * it (dono, 30/07). A PROVIDER renders no UI at all.
217
+ *
218
+ * Both are reported as counts instead, which says more in one line than four
219
+ * lines of "nothing in the catalogue does this".
220
+ */
221
+ const IS_ICON = /(^Icon|Icon$)/;
222
+ const IS_PROVIDER = /(Provider|Context)$/;
223
+ export function classifyAside(name) {
224
+ if (IS_ICON.test(name))
225
+ return "icon";
226
+ if (IS_PROVIDER.test(name))
227
+ return "provider";
228
+ return null;
229
+ }
210
230
  /** Below this, two components share a value or two by accident. */
211
231
  export const TWIN_OVERLAP = 0.5;
212
232
  /**
@@ -254,6 +274,18 @@ export function crosswalk(components) {
254
274
  }
255
275
  return mine
256
276
  .map((component) => {
277
+ const aside = classifyAside(component.name);
278
+ if (aside) {
279
+ return {
280
+ component,
281
+ canonical: null,
282
+ twins: [],
283
+ bucket: aside,
284
+ because: aside === "icon"
285
+ ? "a glyph, which a system models as a library rather than a recipe"
286
+ : "renders no UI - infrastructure, not a component",
287
+ };
288
+ }
257
289
  const canonical = canonicalName(component.name);
258
290
  const twins = mine
259
291
  .filter((o) => o.name !== component.name)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.47",
3
+ "version": "0.16.49",
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": {