synthesisui 0.16.79 → 0.16.81
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/anatomy-read.js +15 -1
- package/dist/commands/import.js +338 -16
- package/dist/commands/mcp.js +9 -0
- package/dist/component-codegen.js +11 -0
- package/dist/doctor/architecture.js +134 -0
- package/dist/doctor/call-sites.js +171 -0
- package/dist/doctor/component-gate.js +235 -0
- package/dist/doctor/components-scan.js +70 -2
- package/dist/doctor/crosswalk.js +59 -2
- package/dist/doctor/definitions-scan.js +32 -0
- package/dist/doctor/transcribe.js +116 -0
- package/dist/doctor/variant-read.js +584 -0
- package/dist/index.js +35 -5
- package/dist/progress.js +71 -0
- package/dist/skill-import.js +200 -11
- package/package.json +1 -1
package/dist/progress.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHAT IS HAPPENING RIGHT NOW, and how much is left.
|
|
3
|
+
*
|
|
4
|
+
* "While the design system import is running it would be good to have an animated
|
|
5
|
+
* progress and underneath the current component and a (10 of 1235)" (dono, 01/08).
|
|
6
|
+
*
|
|
7
|
+
* A census over a real monorepo reads 2797 files and the terminal said nothing for
|
|
8
|
+
* most of it. Twenty silent minutes is indistinguishable from twenty broken ones, and
|
|
9
|
+
* that is the difference this makes: not speed, but the ability to wait.
|
|
10
|
+
*
|
|
11
|
+
* TWO AUDIENCES, ONE API. A person at a terminal gets a bar that redraws in place; the
|
|
12
|
+
* skill runs the CLI with no TTY and pipes the output, so it gets a plain milestone
|
|
13
|
+
* line every so often. Neither has to know about the other, and the non-TTY path never
|
|
14
|
+
* writes a carriage return into a transcript.
|
|
15
|
+
*/
|
|
16
|
+
const BAR_WIDTH = 24;
|
|
17
|
+
/** Every this-many items, the non-TTY path prints one line. Enough to see movement in
|
|
18
|
+
* a piped log without turning it into the log. */
|
|
19
|
+
const MILESTONE_EVERY = 25;
|
|
20
|
+
/** A bar that reads as a bar at any width. Two glyphs, no colour: this line is
|
|
21
|
+
* overwritten dozens of times a second and anything fancier flickers. */
|
|
22
|
+
function bar(done, total) {
|
|
23
|
+
const ratio = total > 0 ? Math.min(1, done / total) : 0;
|
|
24
|
+
const filled = Math.round(ratio * BAR_WIDTH);
|
|
25
|
+
return `${"█".repeat(filled)}${"░".repeat(BAR_WIDTH - filled)}`;
|
|
26
|
+
}
|
|
27
|
+
/** Truncated from the LEFT, because the end of a path is the part that identifies it. */
|
|
28
|
+
function tail(label, width) {
|
|
29
|
+
if (label.length <= width)
|
|
30
|
+
return label;
|
|
31
|
+
return `…${label.slice(-(width - 1))}`;
|
|
32
|
+
}
|
|
33
|
+
export function startProgress(title, opts) {
|
|
34
|
+
const write = opts?.write ?? ((s) => process.stdout.write(s));
|
|
35
|
+
const tty = opts?.isTty ?? Boolean(process.stdout.isTTY);
|
|
36
|
+
let total = opts?.total ?? 0;
|
|
37
|
+
let done = 0;
|
|
38
|
+
let last = "";
|
|
39
|
+
const paint = () => {
|
|
40
|
+
const count = total > 0 ? `${done} of ${total}` : String(done);
|
|
41
|
+
const line = ` ${bar(done, total)} ${count} ${tail(last, 40)}`;
|
|
42
|
+
// Pad to the previous width so a shorter label cannot leave the tail of a
|
|
43
|
+
// longer one behind on the line.
|
|
44
|
+
write(`\r${line.padEnd(80)}`);
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
step(label) {
|
|
48
|
+
done += 1;
|
|
49
|
+
last = label;
|
|
50
|
+
if (tty) {
|
|
51
|
+
paint();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// NO CARRIAGE RETURNS IN A TRANSCRIPT. A milestone line every so often, plus
|
|
55
|
+
// the first one so the reader knows the phase started at all.
|
|
56
|
+
if (done === 1 || done % MILESTONE_EVERY === 0) {
|
|
57
|
+
const count = total > 0 ? `${done} of ${total}` : `${done}`;
|
|
58
|
+
write(` ${title}: ${count} - ${label}\n`);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
total(n) {
|
|
62
|
+
total = n;
|
|
63
|
+
},
|
|
64
|
+
done(summary) {
|
|
65
|
+
if (tty)
|
|
66
|
+
write(`\r${" ".repeat(80)}\r`);
|
|
67
|
+
if (summary)
|
|
68
|
+
write(` ${summary}\n`);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
package/dist/skill-import.js
CHANGED
|
@@ -105,22 +105,42 @@ This root holds several projects
|
|
|
105
105
|
is the average of all of them. That is a fine DIAGNOSIS and a poor system: a
|
|
106
106
|
light app and a dark one average into a palette that is neither.
|
|
107
107
|
|
|
108
|
-
synthesisui import --scope packages/ui
|
|
108
|
+
synthesisui import --scope packages/ui --usage apps/web-dashboard
|
|
109
109
|
\`\`\`
|
|
110
110
|
|
|
111
|
-
Then measure again, narrowed
|
|
111
|
+
Then measure again, narrowed - and **name both roles**:
|
|
112
112
|
|
|
113
113
|
\`\`\`
|
|
114
|
-
npx synthesisui import --dry --scope packages/ui
|
|
114
|
+
npx synthesisui import --dry --scope packages/ui --usage apps/web-dashboard
|
|
115
115
|
\`\`\`
|
|
116
116
|
|
|
117
|
+
### The two roles, and why one flag could not do both
|
|
118
|
+
|
|
119
|
+
\`\`\`
|
|
120
|
+
--scope the SYSTEM tokens, components, the way they name a class ONE folder
|
|
121
|
+
--usage the EVIDENCE how often, which values get picked, the laws as many as they have
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
\`--usage\` is repeatable and never contributes a token, a scale or a component. That split is
|
|
125
|
+
not tidiness, it is the two mistakes this pipeline exists to avoid, and each one has been made:
|
|
126
|
+
|
|
117
127
|
**Do not import the average.** Three apps measured together produce a palette that belongs to
|
|
118
128
|
none of them, and it will look plausible - a real monorepo reported 36% coverage across 2797
|
|
119
129
|
files at the root and 47% across 114 in its shared package. The second number is the system;
|
|
120
|
-
the first is a diagnosis.
|
|
130
|
+
the first is a diagnosis. Their scale is a DECLARATION; an app's average is a set of choices
|
|
131
|
+
made from one.
|
|
132
|
+
|
|
133
|
+
**Do not import a library alone either.** A law needs three files that agree, and a library has
|
|
134
|
+
ONE file per component - so measured by itself, a real one came back with \`usage\` empty on all
|
|
135
|
+
23 components and not a single observed law. Not because nothing is settled: because the
|
|
136
|
+
agreeing happens in the app (dono, 01/08). If they have apps, name them.
|
|
137
|
+
|
|
138
|
+
Point \`--usage\` at every app that consumes the system. A component that lives in an app and
|
|
139
|
+
not in the system is reported and left out - the system is one place, and a union of two
|
|
140
|
+
codebases is not a system.
|
|
121
141
|
|
|
122
|
-
|
|
123
|
-
and
|
|
142
|
+
The census still lands at \`<root>/_synthesisui/census.json\` and records both what it measured
|
|
143
|
+
and where the evidence came from, so you always send the same path and there is never a second
|
|
124
144
|
file to pick between. Nothing is sent by \`--dry\`. Read the file.
|
|
125
145
|
|
|
126
146
|
### 2. Read what arithmetic cannot
|
|
@@ -398,7 +418,7 @@ shell and its own parts inside.
|
|
|
398
418
|
\`"root": "BaseDialog.Root"\`, \`"root": "Popover.Root"\`
|
|
399
419
|
- **a plain tag** - leave it out. \`<div>\`, \`<td>\`, \`<button>\` are not frontiers.
|
|
400
420
|
|
|
401
|
-
### The
|
|
421
|
+
### The ten forms
|
|
402
422
|
|
|
403
423
|
\`as\` says what a node IS, and the renderer draws that. Nothing else is accepted:
|
|
404
424
|
|
|
@@ -413,10 +433,47 @@ row arranges its children ACROSS
|
|
|
413
433
|
stack arranges its children DOWN
|
|
414
434
|
component their component → needs "ref": the name as their code spells it
|
|
415
435
|
external a library → needs "from": the package name
|
|
436
|
+
slot the CALLER's content → takes "expects": what goes there, in their words
|
|
416
437
|
\`\`\`
|
|
417
438
|
|
|
418
439
|
Only \`row\` and \`stack\` take \`children\`. Everything else is a leaf.
|
|
419
440
|
|
|
441
|
+
### \`{children}\` is a node, not an absence
|
|
442
|
+
|
|
443
|
+
Four things decide what a component looks like on screen, and only the first is in its own source:
|
|
444
|
+
|
|
445
|
+
\`\`\`
|
|
446
|
+
its own classes you are reading them
|
|
447
|
+
what the caller passes <tr>{children}</tr> → slot
|
|
448
|
+
its runtime state value={62} drives the width → a part with no static size
|
|
449
|
+
a library drawing it <EditorContent> → external
|
|
450
|
+
\`\`\`
|
|
451
|
+
|
|
452
|
+
A component whose content is not its own had nothing to send, so it sent nothing and previewed as
|
|
453
|
+
a blank box. A real \`DataTableRow\` is sixteen lines - a \`<tr>\` carrying a border, a hover and
|
|
454
|
+
\`{children}\` - and it came back empty, which is not wrong so much as unreadable (dono, 01/08).
|
|
455
|
+
|
|
456
|
+
So send the slot. \`expects\` is what the caller is supposed to put there, in the words their own
|
|
457
|
+
code uses - read the prop type, the JSDoc, or a call site:
|
|
458
|
+
|
|
459
|
+
\`\`\`json
|
|
460
|
+
{ "as": "slot", "name": "row", "classes": "border-b hover:bg-ocean-50/30", "expects": "the cells" }
|
|
461
|
+
\`\`\`
|
|
462
|
+
|
|
463
|
+
It keeps its \`name\` and \`classes\` like any other node - the border and the hover are real and
|
|
464
|
+
belong on that element. What it does not do is invent content: the preview says "the cells go
|
|
465
|
+
here", which is exactly what the file says.
|
|
466
|
+
|
|
467
|
+
Good \`expects\`: \`"the cells"\`, \`"one row per item"\`, \`"the form"\`, \`"the page body"\`. Leave it
|
|
468
|
+
out rather than writing \`"children"\` - the field is for what a person would call it.
|
|
469
|
+
|
|
470
|
+
**A part can carry its own variants and states**, not only a flat class string. Send
|
|
471
|
+
\`classes\` for the resting look and let the prefixes travel inside it - \`hover:\`,
|
|
472
|
+
\`focus-visible:\`, \`disabled:\`, \`group-hover:\`, \`md:\`, \`data-[state=open]:\` are all read
|
|
473
|
+
and land where they belong. \`group-hover:\` in particular now has somewhere to go: it
|
|
474
|
+
means "when the component around me is hovered", which is what a real table row is built
|
|
475
|
+
on and what nothing could express before.
|
|
476
|
+
|
|
420
477
|
**\`name\` is the part name**, and it is what carries the styles - flat, lowercase, kebab. Never
|
|
421
478
|
nested: \`"actions.generate"\` compiles to two CSS classes and is invalid, so name it
|
|
422
479
|
\`"generate-action"\`. Name what it IS - \`label\`, \`value\`, \`delta\`, \`cover\`, \`toolbar\` - because the
|
|
@@ -440,10 +497,22 @@ of. It previews as a block carrying its name, and it becomes a **rule** - \`Arti
|
|
|
440
497
|
That edge is a **fact, not a habit**: you saw it in the definition, so it is true once and for
|
|
441
498
|
all, and it arrives active without waiting for a third sighting.
|
|
442
499
|
|
|
443
|
-
|
|
500
|
+
Three things that are NOT edges, and sending them as such would be wrong:
|
|
444
501
|
|
|
445
502
|
- \`motion.div\`, \`Dialog.Root\`, \`Radio.Item\` - a library's namespace, not a component of theirs
|
|
446
503
|
- an HTML element with a capital in a variable name
|
|
504
|
+
- **a name the file binds itself and does not export.** Capitalisation is React's rule for "not an
|
|
505
|
+
html tag", which is a different question from "is this a component of the system". Both of these
|
|
506
|
+
arrived as components of a real library and neither is one (dono, 01/08):
|
|
507
|
+
|
|
508
|
+
\`\`\`tsx
|
|
509
|
+
function Text({ as: Component = "p" }) // a prop rename - the polymorphic idiom
|
|
510
|
+
const CustomLegend = () => (…) // a closure inside LineChart
|
|
511
|
+
\`\`\`
|
|
512
|
+
|
|
513
|
+
The test is reachability: a component of the system is imported from somewhere or exported to
|
|
514
|
+
somewhere. A name that only lives inside one function body is that function's private wiring,
|
|
515
|
+
and giving it a recipe invents a component nobody can import.
|
|
447
516
|
|
|
448
517
|
If the component came from a package, it is \`external\`, not \`component\`.
|
|
449
518
|
|
|
@@ -468,6 +537,72 @@ extensions are configured at construction, not toggled later
|
|
|
468
537
|
of their manifest and attaches it (\`"^2.1.0 in packages/ui"\`), so the rule does not go stale the
|
|
469
538
|
day they upgrade and the information is not lost either.
|
|
470
539
|
|
|
540
|
+
### The floor: what a preview needs before it can draw anything
|
|
541
|
+
|
|
542
|
+
The owner asked this directly - "to render the button, what do I need to have?" - and the
|
|
543
|
+
honest answer per kind is short. **Work the list for the component's kind and say which
|
|
544
|
+
ones their code answers.** A reader holding this knows that a button read with no hover
|
|
545
|
+
means the hover has not been found yet, and goes back for it.
|
|
546
|
+
|
|
547
|
+
\`\`\`
|
|
548
|
+
action the resting fill and the text colour on it · padding and radius ·
|
|
549
|
+
text size and weight · hover, focus-visible AND disabled - three looks,
|
|
550
|
+
not one · gap and alignment if it can carry an icon
|
|
551
|
+
field the fill of the typing area and the text colour in it · the edge at rest
|
|
552
|
+
and the edge when focused · padding and radius · the placeholder colour
|
|
553
|
+
(a different decision) · the invalid look if their code has one
|
|
554
|
+
control the off look and the on look, which IS the component · the size · the
|
|
555
|
+
focus ring, since a control is reached by keyboard · the disabled look
|
|
556
|
+
surface the fill, and the edge or the elevation that separates it from the page ·
|
|
557
|
+
radius and inner padding · the gap between what it holds · whether it
|
|
558
|
+
changes at a breakpoint
|
|
559
|
+
pill the fill and text colour, per status if it has statuses · radius and
|
|
560
|
+
horizontal padding · the text size
|
|
561
|
+
indicator the colour, or the two if it has a track and a fill · the size or ratio ·
|
|
562
|
+
WHAT DRIVES IT - which prop moves the number
|
|
563
|
+
text the colour · the size and line height, or a note that both are inherited ·
|
|
564
|
+
the weight and family if they differ from body
|
|
565
|
+
\`\`\`
|
|
566
|
+
|
|
567
|
+
**A component genuinely has none of something, and saying so is a real answer.** This is a
|
|
568
|
+
list to work through, not a gate: nothing is blocked for failing it, and inventing a
|
|
569
|
+
background to satisfy it would be the exact opposite of the point.
|
|
570
|
+
|
|
571
|
+
There is one hard line under all of it: a recipe that declares NO colour, NO edge and NO
|
|
572
|
+
size cannot be told apart from the page. The platform says so rather than printing a
|
|
573
|
+
score, and it follows the root first - a component whose surface belongs to the \`Card\` it
|
|
574
|
+
returns is complete.
|
|
575
|
+
|
|
576
|
+
### Media is not a style question
|
|
577
|
+
|
|
578
|
+
An \`image\` node draws a grey region and carries nothing about how to use it, so an agent
|
|
579
|
+
composing with it invents the answer. Send these as \`rules\` on that component:
|
|
580
|
+
|
|
581
|
+
\`\`\`
|
|
582
|
+
a ratio or an explicit size a region with neither collapses before the image loads,
|
|
583
|
+
and the page jumps when it arrives
|
|
584
|
+
an object-fit cover crops and contain letterboxes; which one is right
|
|
585
|
+
is about the content, not a default
|
|
586
|
+
a corner radius an image inside a rounded surface has to say whether it
|
|
587
|
+
follows the curve
|
|
588
|
+
what shows while it loads a skeleton, a blur, or the surface colour
|
|
589
|
+
what shows when it is missing a missing asset must never look broken
|
|
590
|
+
\`\`\`
|
|
591
|
+
|
|
592
|
+
### What the CLI now reads for you, so do not spend a turn on it
|
|
593
|
+
|
|
594
|
+
Four things are read deterministically from the source. Reporting them back as if you
|
|
595
|
+
found them wastes a turn and risks contradicting the measurement:
|
|
596
|
+
|
|
597
|
+
- **variant and state styles** out of \`cva\`, \`tv\` and \`cond === "x" && "classes"\` -
|
|
598
|
+
including a hover PER variant, compound variants, and \`[var(--their-token)]\`
|
|
599
|
+
- **whether a component forwards the rest of its props**, and which it names
|
|
600
|
+
- **which components appear inside which** at their call sites, as pairs
|
|
601
|
+
- **the folder architecture**, and it will ask you which one has priority
|
|
602
|
+
|
|
603
|
+
What is still yours: the anatomy tree, the part names, the frontiers, the roles, the
|
|
604
|
+
concept, and the rules that are not about a class name.
|
|
605
|
+
|
|
471
606
|
### How much to send
|
|
472
607
|
|
|
473
608
|
Send an anatomy for **every component with visible structure**, which is nearly all of them. A
|
|
@@ -497,7 +632,7 @@ spec view has no nesting to draw.
|
|
|
497
632
|
|
|
498
633
|
### 3. Walk them through the decisions, one at a time
|
|
499
634
|
|
|
500
|
-
|
|
635
|
+
Six decisions are theirs. **Ask them as separate questions with selectable options** - use your
|
|
501
636
|
question tool, one call per decision, so they pick instead of reading a wall and composing a
|
|
502
637
|
reply. A single block containing everything is a report, and a report gets read, not answered.
|
|
503
638
|
|
|
@@ -581,6 +716,56 @@ canvas #222326 darkgray-500 - what DashboardLayout paints on <main>
|
|
|
581
716
|
Say it and let them object. Offering five near-blacks to pick between is asking someone to
|
|
582
717
|
re-decide something their code already decided.
|
|
583
718
|
|
|
719
|
+
**3e-bis. Our kit, or theirs alone** - ask this ONLY when they already have a component library,
|
|
720
|
+
which is when \`--scope\` pointed at one and the CLI said so. An app with no components of its own
|
|
721
|
+
has nothing to weigh and should never see the question.
|
|
722
|
+
|
|
723
|
+
\`\`\`
|
|
724
|
+
Every system born here comes with 45 components of ours - button, card, input,
|
|
725
|
+
table, the rest of a working toolkit. You already have 37.
|
|
726
|
+
|
|
727
|
+
add ours to yours (recommended) 68 components, and the 23 governed by your
|
|
728
|
+
own rules are the 23 you wrote
|
|
729
|
+
mine alone 37 components, all of them yours. Our
|
|
730
|
+
layouts that need a component you do not
|
|
731
|
+
have stay out too. Add any of ours later
|
|
732
|
+
from the library, one at a time
|
|
733
|
+
\`\`\`
|
|
734
|
+
|
|
735
|
+
Send it as \`"standards": "none"\` in the reading when they choose the second, and leave the field
|
|
736
|
+
out when they choose the first. **Recommend adding ours**, and say why in one line: a component of
|
|
737
|
+
ours costs nothing until something composes it, and having a \`table\` the day somebody needs a
|
|
738
|
+
table is worth more than a shorter list.
|
|
739
|
+
|
|
740
|
+
Two things to be straight about, because both are true and neither is obvious:
|
|
741
|
+
|
|
742
|
+
- **the foundations come from the seed either way.** Their palette, their scales, their type - all
|
|
743
|
+
theirs - but the seven type slots, the easing and the neutral floor arrive from ours, because
|
|
744
|
+
the alternative to a type scale is no components at all rather than their components.
|
|
745
|
+
- **our layouts are written in our recipes.** A landing that composes \`button\`, \`card\` and
|
|
746
|
+
\`badge\` needs those to exist. Choosing *mine alone* keeps the layouts whose recipes they
|
|
747
|
+
happen to have and drops the rest - a layout pointing at a recipe nobody has renders as holes.
|
|
748
|
+
|
|
749
|
+
**3e-ter. Which architecture has priority** - ask only when the CLI reported more than
|
|
750
|
+
one, which it does under "How this project is organised".
|
|
751
|
+
|
|
752
|
+
A project having two shapes is not a project with a mistake: a library organised
|
|
753
|
+
atomically inside a monorepo whose apps are organised by feature is two true answers.
|
|
754
|
+
Say that before you ask, or the question reads as an accusation.
|
|
755
|
+
|
|
756
|
+
\`\`\`
|
|
757
|
+
Two shapes, and neither is wrong:
|
|
758
|
+
|
|
759
|
+
atomic under packages/ui (recommended) 114 files - atoms/molecules/organisms,
|
|
760
|
+
and this is where the system comes from
|
|
761
|
+
feature under apps/web-dashboard 900 files - features/, where the app
|
|
762
|
+
consumes it
|
|
763
|
+
\`\`\`
|
|
764
|
+
|
|
765
|
+
The one they pick becomes a rule that reaches \`CLAUDE.md\`, so **every prompt after this
|
|
766
|
+
knows where a new component goes** - not what the folders are called, but which rung or
|
|
767
|
+
which feature a new file belongs to. Recommend the one the system came from.
|
|
768
|
+
|
|
584
769
|
**3f. Fonts** - do not ask at all. Report and move on:
|
|
585
770
|
|
|
586
771
|
\`\`\`
|
|
@@ -595,7 +780,7 @@ way. If it declares one, there is nothing to decide.
|
|
|
595
780
|
carries the weight - \`ocean-500\` tells someone more than a square would. When they run
|
|
596
781
|
\`npx synthesisui import\` themselves in a terminal, the CLI paints them.
|
|
597
782
|
|
|
598
|
-
Then, and only after all
|
|
783
|
+
Then, and only after all of them are answered:
|
|
599
784
|
|
|
600
785
|
\`\`\`
|
|
601
786
|
npx synthesisui import --census _synthesisui/census.json --name "<the name>" [--registry <from step 0>]
|
|
@@ -603,7 +788,7 @@ npx synthesisui import --census _synthesisui/census.json --name "<the name>" [--
|
|
|
603
788
|
|
|
604
789
|
**This is the one real write.** Everything before it is on their disk and costs nothing to redo.
|
|
605
790
|
|
|
606
|
-
Pass every answer explicitly - \`--name\`, \`--scope\` if they changed
|
|
791
|
+
Pass every answer explicitly - \`--name\`, \`--scope\` and \`--usage\` if they changed either, and the reading rewritten
|
|
607
792
|
with their primary and their theme. The CLI cannot ask anything when you are the one running it:
|
|
608
793
|
it has no terminal, so its own prompts are skipped by design. **Whatever you did not ask, nobody
|
|
609
794
|
asked.** Skip 3a and the first time they learn where their system came from is when they open
|
|
@@ -627,6 +812,10 @@ proposal. Say all of this in your own words - do not just print the link:
|
|
|
627
812
|
which half was deliberate: nothing was invented, so a component whose classes named no token
|
|
628
813
|
arrives with structure and no colour. The platform shows that as *Not written yet*, not as a
|
|
629
814
|
zero.
|
|
815
|
+
- **their own ladder**, if their repo has one. Components under \`atoms/\`, \`molecules/\` and
|
|
816
|
+
\`organisms/\` arrive grouped that way in the vitrine, read off the folder rather than guessed
|
|
817
|
+
from a name. You do not send this and you should not try to: a project that does not organise
|
|
818
|
+
itself this way gets one honest group instead of three invented ones.
|
|
630
819
|
- **the shape of each component**, if you sent one: its parts nested as they nest in their code,
|
|
631
820
|
the components of theirs it composes, and the libraries it needs. Each edge is also a rule now,
|
|
632
821
|
which is what makes it survive being looked at once.
|
package/package.json
CHANGED