synthesisui 0.16.91 → 0.16.92

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.
@@ -18,6 +18,7 @@ import { describeConvention, describeRemainder, detectConventions, } from "../do
18
18
  import { diagnose, scanSource } from "../doctor/scan.js";
19
19
  import { parseSchemeBlocks } from "../doctor/scheme-blocks.js";
20
20
  import { describeSignals, emptySignals, finishSignals, readSignalsInto, } from "../doctor/signals.js";
21
+ import { sketchOf } from "../doctor/sketch.js";
21
22
  import { buildTable } from "../doctor/tokens.js";
22
23
  import { readInlineStyle, rootClasses, rootTag, transcribe, } from "../doctor/transcribe.js";
23
24
  import { transcribeVariants } from "../doctor/variant-read.js";
@@ -472,8 +473,16 @@ export async function takeCensus(root, opts) {
472
473
  Object.keys(parts).length +
473
474
  layers.length;
474
475
  const tag = rootTag(src);
476
+ /**
477
+ * THE SKETCH travels with the look, so the reading phase never opens this
478
+ * file again: naming parts is a sentence over data the census already holds,
479
+ * and a file read whose content is here is a pipeline optimization failure -
480
+ * the owner's rule, adopted verbatim (dono, 01/08).
481
+ */
482
+ const sketch = sketchOf(src);
475
483
  if (size > 0 || tag) {
476
484
  looks[found[0].name] = {
485
+ ...(sketch.length > 0 ? { sketch } : {}),
477
486
  ...t,
478
487
  base,
479
488
  states,
@@ -267,7 +267,7 @@ function looksLikeTypeArg(source, at) {
267
267
  }
268
268
  return false;
269
269
  }
270
- function scanTags(source) {
270
+ export function scanTags(source) {
271
271
  const out = [];
272
272
  for (let i = 0; i < source.length; i++) {
273
273
  if (source[i] !== "<")
@@ -0,0 +1,68 @@
1
+ /**
2
+ * THE ANATOMY SKETCH - the component's own markup, carried by the census so nobody
3
+ * reads the file twice.
4
+ *
5
+ * The owner's directive, verbatim in intent (dono, 01/08): the census is the ONLY
6
+ * source of truth about the project's tree; iterative per-layer sweeps are forbidden;
7
+ * and any file read whose content the census already carries is a pipeline
8
+ * optimization failure. The reading phase was the last consumer of raw files - it
9
+ * opened every component to see its JSX before naming parts, which is most of the
10
+ * tokens and most of the minutes of a run.
11
+ *
12
+ * The sketch is everything that reading extracted, minus the one thing that is
13
+ * genuinely a judgment: the NAMES. Tag, class string, depth, text, and the
14
+ * capitalised frontiers, per element, in document order. A reader holding this names
15
+ * `trigger`/`status-dot`/`panel` without opening anything - and naming is a sentence,
16
+ * not a sweep.
17
+ *
18
+ * GENERALIZED BY CONSTRUCTION: it reads JSX shape, not folder layout, so it works the
19
+ * same in an atomic library, a feature-foldered app, or a single flat directory.
20
+ */
21
+ import { scanTags } from "./css-modules.js";
22
+ /** Past this a component is a page in disguise, and the gate already said no. */
23
+ const MAX_NODES = 60;
24
+ /** `className="..."` or className={"..."} / {`...`} with no interpolation. */
25
+ const CLASS_ATTR = /className=\{?["'`]([^"'`{}$]*)["'`]\}?/;
26
+ /**
27
+ * The sketch of one component file.
28
+ *
29
+ * Depth comes from the same tag scanner the CSS-module reader trusts (brace-counting,
30
+ * quote-aware, type-argument-proof). Text is captured only when it is a short literal
31
+ * sitting directly between an open and its close - dynamic children are the caller's,
32
+ * and the slot form already says so.
33
+ */
34
+ export function sketchOf(source) {
35
+ const events = scanTags(source);
36
+ const out = [];
37
+ let depth = 0;
38
+ for (let i = 0; i < events.length; i++) {
39
+ const event = events[i];
40
+ if (event.kind === "close") {
41
+ depth = Math.max(0, depth - 1);
42
+ continue;
43
+ }
44
+ if (out.length >= MAX_NODES)
45
+ break;
46
+ const node = { tag: event.tag, depth };
47
+ const cls = CLASS_ATTR.exec(event.body);
48
+ if (cls?.[1].trim())
49
+ node.classes = cls[1].trim();
50
+ // Literal text: the slice between this open and the next event, when it is prose.
51
+ const next = events[i + 1];
52
+ if (next) {
53
+ const between = source
54
+ .slice(event.at + event.tag.length, next.at)
55
+ .replace(/^[^>]*>/, "")
56
+ .trim();
57
+ if (between &&
58
+ between.length <= 60 &&
59
+ !between.includes("{") &&
60
+ !between.includes("<")) {
61
+ node.text = between;
62
+ }
63
+ }
64
+ out.push(node);
65
+ depth += 1;
66
+ }
67
+ return out;
68
+ }
@@ -610,6 +610,20 @@ export function readUtility(utility, declared) {
610
610
  if (size)
611
611
  return { property: axisProp, value: size };
612
612
  }
613
+ /**
614
+ * THEIR OWN SPACING STEP IS A LIVE UTILITY IN v4: `gap-sm` compiles to
615
+ * `var(--spacing-sm)` the moment the theme declares it. A real Label uses exactly
616
+ * that, and the reading filed it under "known typos in their source" - the typo
617
+ * was ours (test15, 01/08). Their name first, the numeric formula second.
618
+ */
619
+ const own = `--spacing-${sizeRest}`;
620
+ if (declared.has(own)) {
621
+ return {
622
+ property: spaceProp,
623
+ value: `{spacing.${sizeRest}}`,
624
+ token: own,
625
+ };
626
+ }
613
627
  const size = SPACING[sizeRest] ?? numericSpacing(sizeRest);
614
628
  if (size)
615
629
  return { property: spaceProp, value: size };
@@ -669,6 +669,41 @@ what shows while it loads a skeleton, a blur, or the surface colour
669
669
  what shows when it is missing a missing asset must never look broken
670
670
  \`\`\`
671
671
 
672
+ ### The census is the ONLY source of truth - four universal rules
673
+
674
+ These hold in ANY project, whatever its folders, and they exist because a run spent most
675
+ of its tokens re-reading what the census already carried (dono, 01/08):
676
+
677
+ 1. **No iterative sweeps by layer or folder.** Never walk atoms/, then molecules/, then
678
+ organisms/ reading files - the census walked everything once.
679
+ 2. **Batch over the census's own data.** Everything you consume comes out of
680
+ \`_synthesisui/census.json\` - the looks, the sketch, the signals, the coverage.
681
+ 3. **Metadata is one pass, already done.** Theme, fonts, page, libraries, architecture
682
+ arrive counted. You confirm in a sentence; you never re-derive.
683
+ 4. **A file read whose content the census carries is a PIPELINE FAILURE.** Say so in
684
+ not-expressed.md when you catch yourself - that report is how the census grows.
685
+
686
+ ### Name the parts from the SKETCH - the file is already in the census
687
+
688
+ Every component's look carries \`sketch\`: its own markup as data - tag, class string,
689
+ depth and text per element, in document order. That is everything you used to open the
690
+ file for, minus the one thing that is genuinely yours: the NAMES.
691
+
692
+ \`\`\`json
693
+ "sketch": [
694
+ { "tag": "div", "depth": 0, "classes": "rounded-md border ..." },
695
+ { "tag": "button", "depth": 1, "classes": "flex w-full ..." },
696
+ { "tag": "span", "depth": 2, "classes": "size-2 rounded-full bg-success-500" },
697
+ { "tag": "span", "depth": 2, "text": "Status" },
698
+ { "tag": "ChevronDownIcon", "depth": 2 }
699
+ ]
700
+ \`\`\`
701
+
702
+ Read that and write the anatomy: depth 1 is \`trigger\`, the dot is \`status-dot\`, the
703
+ capitalised tag is a frontier. **Do not open the component file** - if the sketch is
704
+ missing or visibly truncated (60-node cap), say so in not-expressed.md and only then
705
+ read, because that gap is the census's to close.
706
+
672
707
  ### What the CLI now reads for you, so do not spend a turn on it
673
708
 
674
709
  **The rule under all of this: you are given EVIDENCE and asked to DECIDE.** You are never
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.91",
3
+ "version": "0.16.92",
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": {