synthesisui 0.16.90 → 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.
- package/dist/commands/import.js +9 -0
- package/dist/doctor/css-modules.js +1 -1
- package/dist/doctor/sketch.js +68 -0
- package/dist/doctor/transcribe.js +229 -3
- package/dist/skill-import.js +35 -0
- package/package.json +1 -1
package/dist/commands/import.js
CHANGED
|
@@ -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,
|
|
@@ -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
|
+
}
|
|
@@ -121,10 +121,114 @@ const RADIUS = {
|
|
|
121
121
|
/** Tailwind's own colour table, for the values a project uses without declaring.
|
|
122
122
|
* Only the ones that actually turn up: white, black, and the neutral ramp a
|
|
123
123
|
* real project reached for 106 times. */
|
|
124
|
+
/**
|
|
125
|
+
* THE SIXTY DROPS WERE MINE, NOT THEIRS. test14's not-expressed listed ~60 classes that
|
|
126
|
+
* "start like a design decision and read as nothing" - and enumerating them found that
|
|
127
|
+
* every family is real, valid Tailwind these tables never held: `w-full`, `text-center`,
|
|
128
|
+
* `leading-tight`, `tracking-wide`, `shadow-sm` (with the token DECLARED), `gap-x-6`,
|
|
129
|
+
* `rounded-t-lg`, `border-none` (dono, 01/08). The unreadable report did its job; this is
|
|
130
|
+
* the coverage it bought.
|
|
131
|
+
*/
|
|
132
|
+
/** Tailwind's spacing formula: N x 0.25rem, halves included - `size-4.5` is 1.125rem,
|
|
133
|
+
* and a table of steps is always one step short (test14). */
|
|
134
|
+
function numericSpacing(step) {
|
|
135
|
+
if (!/^\d+(\.\d+)?$/.test(step))
|
|
136
|
+
return null;
|
|
137
|
+
const n = Number(step);
|
|
138
|
+
return n === 0 ? "0" : `${n * 0.25}rem`;
|
|
139
|
+
}
|
|
140
|
+
const SIZE_KEYWORD = {
|
|
141
|
+
full: "100%",
|
|
142
|
+
auto: "auto",
|
|
143
|
+
min: "min-content",
|
|
144
|
+
max: "max-content",
|
|
145
|
+
fit: "fit-content",
|
|
146
|
+
px: "1px",
|
|
147
|
+
"0": "0",
|
|
148
|
+
};
|
|
149
|
+
/** Axis-dependent: `w-screen` is 100vw and `h-screen` is 100vh. */
|
|
150
|
+
const SCREEN = {
|
|
151
|
+
w: "100vw",
|
|
152
|
+
h: "100vh",
|
|
153
|
+
"min-w": "100vw",
|
|
154
|
+
"min-h": "100vh",
|
|
155
|
+
"max-w": "100vw",
|
|
156
|
+
"max-h": "100vh",
|
|
157
|
+
};
|
|
158
|
+
const TEXT_ALIGN = new Set([
|
|
159
|
+
"left",
|
|
160
|
+
"center",
|
|
161
|
+
"right",
|
|
162
|
+
"justify",
|
|
163
|
+
"start",
|
|
164
|
+
"end",
|
|
165
|
+
]);
|
|
166
|
+
const LEADING = {
|
|
167
|
+
none: "1",
|
|
168
|
+
tight: "1.25",
|
|
169
|
+
snug: "1.375",
|
|
170
|
+
normal: "1.5",
|
|
171
|
+
relaxed: "1.625",
|
|
172
|
+
loose: "2",
|
|
173
|
+
};
|
|
174
|
+
const TRACKING = {
|
|
175
|
+
tighter: "-0.05em",
|
|
176
|
+
tight: "-0.025em",
|
|
177
|
+
normal: "0",
|
|
178
|
+
wide: "0.025em",
|
|
179
|
+
wider: "0.05em",
|
|
180
|
+
widest: "0.1em",
|
|
181
|
+
};
|
|
182
|
+
/** Tailwind's own shadows, for when a project uses them undeclared. A declared
|
|
183
|
+
* `--shadow-x` always wins and travels as the ref. */
|
|
184
|
+
const TAILWIND_SHADOW = {
|
|
185
|
+
sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
186
|
+
md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
|
|
187
|
+
lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
|
|
188
|
+
xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
|
|
189
|
+
"2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
|
|
190
|
+
inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",
|
|
191
|
+
none: "none",
|
|
192
|
+
};
|
|
193
|
+
const BORDER_STYLE = new Set([
|
|
194
|
+
"solid",
|
|
195
|
+
"dashed",
|
|
196
|
+
"dotted",
|
|
197
|
+
"double",
|
|
198
|
+
"hidden",
|
|
199
|
+
"none",
|
|
200
|
+
]);
|
|
201
|
+
/** `rounded-t-lg` → the two top corners: the side names half the corners each. */
|
|
202
|
+
const RADIUS_SIDE = {
|
|
203
|
+
t: ["borderTopLeftRadius", "borderTopRightRadius"],
|
|
204
|
+
b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
|
|
205
|
+
l: ["borderTopLeftRadius", "borderBottomLeftRadius"],
|
|
206
|
+
r: ["borderTopRightRadius", "borderBottomRightRadius"],
|
|
207
|
+
tl: ["borderTopLeftRadius"],
|
|
208
|
+
tr: ["borderTopRightRadius"],
|
|
209
|
+
bl: ["borderBottomLeftRadius"],
|
|
210
|
+
br: ["borderBottomRightRadius"],
|
|
211
|
+
};
|
|
124
212
|
const TAILWIND_COLOR = {
|
|
125
213
|
white: "#ffffff",
|
|
126
214
|
black: "#000000",
|
|
127
215
|
transparent: "transparent",
|
|
216
|
+
// zinc and blue, because a real Select is styled entirely in them and every
|
|
217
|
+
// reference read as nothing (test14, 01/08). Tailwind's own values, verbatim.
|
|
218
|
+
"zinc-50": "#fafafa",
|
|
219
|
+
"zinc-100": "#f4f4f5",
|
|
220
|
+
"zinc-200": "#e4e4e7",
|
|
221
|
+
"zinc-300": "#d4d4d8",
|
|
222
|
+
"zinc-400": "#a1a1aa",
|
|
223
|
+
"zinc-500": "#71717a",
|
|
224
|
+
"zinc-600": "#52525b",
|
|
225
|
+
"zinc-700": "#3f3f46",
|
|
226
|
+
"zinc-800": "#27272a",
|
|
227
|
+
"zinc-900": "#18181b",
|
|
228
|
+
"zinc-950": "#09090b",
|
|
229
|
+
"blue-400": "#60a5fa",
|
|
230
|
+
"blue-500": "#3b82f6",
|
|
231
|
+
"blue-600": "#2563eb",
|
|
128
232
|
"neutral-50": "#fafafa",
|
|
129
233
|
"neutral-100": "#f5f5f5",
|
|
130
234
|
"neutral-200": "#e5e5e5",
|
|
@@ -340,6 +444,18 @@ export function readUtility(utility, declared) {
|
|
|
340
444
|
return null;
|
|
341
445
|
const prefix = core.slice(0, dash);
|
|
342
446
|
const rest = core.slice(dash + 1);
|
|
447
|
+
/**
|
|
448
|
+
* `border-none` is a style and `border-collapse` a table's own model - neither a
|
|
449
|
+
* width nor a colour. BEFORE the colour branch, because `border` is a colour prefix
|
|
450
|
+
* and that branch returns null for a name it does not know (test14).
|
|
451
|
+
*/
|
|
452
|
+
if (prefix === "border") {
|
|
453
|
+
if (BORDER_STYLE.has(rest))
|
|
454
|
+
return { property: "borderStyle", value: rest };
|
|
455
|
+
if (rest === "collapse" || rest === "separate") {
|
|
456
|
+
return { property: "borderCollapse", value: rest };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
343
459
|
const colorProp = COLOR_PROPERTY[prefix];
|
|
344
460
|
if (colorProp) {
|
|
345
461
|
/**
|
|
@@ -381,6 +497,10 @@ export function readUtility(utility, declared) {
|
|
|
381
497
|
return null;
|
|
382
498
|
}
|
|
383
499
|
if (prefix === "text") {
|
|
500
|
+
// `text-center` is an alignment, not a colour and not a size - and it read as
|
|
501
|
+
// nothing on a real table's cells (test14).
|
|
502
|
+
if (TEXT_ALIGN.has(rest))
|
|
503
|
+
return { property: "textAlign", value: rest };
|
|
384
504
|
/**
|
|
385
505
|
* A SIZE TRAVELS AS A LITERAL, and that is not a shortcut.
|
|
386
506
|
*
|
|
@@ -406,6 +526,10 @@ export function readUtility(utility, declared) {
|
|
|
406
526
|
if (prefix === "font") {
|
|
407
527
|
// A family (`font-sans`) and a weight (`font-bold`) share this prefix, and
|
|
408
528
|
// the weight table is what tells them apart.
|
|
529
|
+
// `font-regular` is this library's own spelling of 400 - non-standard Tailwind,
|
|
530
|
+
// real in their files, and dropped whole (test14).
|
|
531
|
+
if (rest === "regular")
|
|
532
|
+
return { property: "fontWeight", value: "400" };
|
|
409
533
|
if (FONT_WEIGHT[rest]) {
|
|
410
534
|
/**
|
|
411
535
|
* ALSO A LITERAL, for a reason worth writing down: a weight ref is a valid
|
|
@@ -439,11 +563,100 @@ export function readUtility(utility, declared) {
|
|
|
439
563
|
if (prefix === "opacity" && /^\d{1,3}$/.test(rest)) {
|
|
440
564
|
return { property: "opacity", value: String(Number(rest) / 100) };
|
|
441
565
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
566
|
+
/**
|
|
567
|
+
* `shadow-sm` with `--shadow-sm` DECLARED read as nothing - there was no shadow branch
|
|
568
|
+
* at all, and elevations on parts vanished across a whole real library (test14).
|
|
569
|
+
*/
|
|
570
|
+
if (prefix === "shadow") {
|
|
571
|
+
const own = `--shadow-${rest}`;
|
|
572
|
+
if (declared.has(own)) {
|
|
573
|
+
return { property: "boxShadow", value: `{shadow.${rest}}`, token: own };
|
|
574
|
+
}
|
|
575
|
+
if (TAILWIND_SHADOW[rest]) {
|
|
576
|
+
return { property: "boxShadow", value: TAILWIND_SHADOW[rest] };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// `leading-tight`, `tracking-wide` - the typography refinements, fixed scales.
|
|
580
|
+
if (prefix === "leading") {
|
|
581
|
+
if (LEADING[rest])
|
|
582
|
+
return { property: "lineHeight", value: LEADING[rest] };
|
|
583
|
+
if (/^\d+(\.\d+)?$/.test(rest)) {
|
|
584
|
+
return { property: "lineHeight", value: `${Number(rest) * 0.25}rem` };
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (prefix === "tracking" && TRACKING[rest]) {
|
|
588
|
+
return { property: "letterSpacing", value: TRACKING[rest] };
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* `min-w-0` splits at the FIRST dash into prefix `min`, which no table holds - the
|
|
592
|
+
* `min-w`/`max-h` keys were dead on arrival. Recombine the axis before looking up.
|
|
593
|
+
*/
|
|
594
|
+
let sizePrefix = prefix;
|
|
595
|
+
let sizeRest = rest;
|
|
596
|
+
if (prefix === "min" || prefix === "max") {
|
|
597
|
+
const axis = /^(w|h)-(.+)$/.exec(rest);
|
|
598
|
+
if (axis) {
|
|
599
|
+
sizePrefix = `${prefix}-${axis[1]}`;
|
|
600
|
+
sizeRest = axis[2];
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const spaceProp = SPACING_PROPERTY[sizePrefix];
|
|
604
|
+
if (spaceProp) {
|
|
605
|
+
/** `gap-x-6` / `gap-y-2` - the two halves of gap, which one prefix cannot say. */
|
|
606
|
+
if (sizePrefix === "gap" && /^[xy]-/.test(sizeRest)) {
|
|
607
|
+
const axisProp = sizeRest.startsWith("x-") ? "columnGap" : "rowGap";
|
|
608
|
+
const step = sizeRest.slice(2);
|
|
609
|
+
const size = SPACING[step] ?? numericSpacing(step);
|
|
610
|
+
if (size)
|
|
611
|
+
return { property: axisProp, value: size };
|
|
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
|
+
}
|
|
627
|
+
const size = SPACING[sizeRest] ?? numericSpacing(sizeRest);
|
|
628
|
+
if (size)
|
|
629
|
+
return { property: spaceProp, value: size };
|
|
630
|
+
/** `w-full`, `h-screen`, `min-w-0` - the keyword sizes, axis-aware. */
|
|
631
|
+
if (sizeRest === "screen" && SCREEN[sizePrefix]) {
|
|
632
|
+
return { property: spaceProp, value: SCREEN[sizePrefix] };
|
|
633
|
+
}
|
|
634
|
+
if (SIZE_KEYWORD[sizeRest] &&
|
|
635
|
+
/^(w|h|min-w|min-h|max-w|max-h|size)$/.test(sizePrefix)) {
|
|
636
|
+
return { property: spaceProp, value: SIZE_KEYWORD[sizeRest] };
|
|
637
|
+
}
|
|
445
638
|
}
|
|
446
639
|
if (prefix === "rounded") {
|
|
640
|
+
/**
|
|
641
|
+
* `rounded-t-lg` - one side, two corners. The side names which half; the scale is
|
|
642
|
+
* the same one the whole-box read already resolves, their token first.
|
|
643
|
+
*/
|
|
644
|
+
const side = /^(tl|tr|bl|br|t|b|l|r)-(.+)$/.exec(rest);
|
|
645
|
+
if (side && RADIUS_SIDE[side[1]]) {
|
|
646
|
+
const own = `--radius-${side[2]}`;
|
|
647
|
+
const value = declared.has(own)
|
|
648
|
+
? `{radius.${side[2]}}`
|
|
649
|
+
: (RADIUS[side[2]] ?? null);
|
|
650
|
+
if (value) {
|
|
651
|
+
// One utility, two corners: returned as the first and the caller's write
|
|
652
|
+
// handles pairs the same way `size-N` is handled - see the transcribe loop.
|
|
653
|
+
return {
|
|
654
|
+
property: RADIUS_SIDE[side[1]].join("+"),
|
|
655
|
+
value,
|
|
656
|
+
...(declared.has(own) ? { token: own } : {}),
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
}
|
|
447
660
|
const own = `--radius-${rest}`;
|
|
448
661
|
if (declared.has(own)) {
|
|
449
662
|
return {
|
|
@@ -828,6 +1041,19 @@ export function transcribe(classes, declared) {
|
|
|
828
1041
|
out.fromLiteral += 1;
|
|
829
1042
|
continue;
|
|
830
1043
|
}
|
|
1044
|
+
// `rounded-t-lg` names two corners: the reader returns them joined with `+`, and
|
|
1045
|
+
// the write splits - the same shape the `size` expansion above uses.
|
|
1046
|
+
if (decl.property.includes("+")) {
|
|
1047
|
+
for (const property of decl.property.split("+")) {
|
|
1048
|
+
if (target[property] == null)
|
|
1049
|
+
target[property] = decl.value;
|
|
1050
|
+
}
|
|
1051
|
+
if (decl.token)
|
|
1052
|
+
out.fromToken += 1;
|
|
1053
|
+
else
|
|
1054
|
+
out.fromLiteral += 1;
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
831
1057
|
if (target[decl.property] != null)
|
|
832
1058
|
continue;
|
|
833
1059
|
target[decl.property] = decl.value;
|
package/dist/skill-import.js
CHANGED
|
@@ -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