synthesisui 0.16.78 → 0.16.80
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 +63 -8
- package/dist/commands/component.js +38 -1
- package/dist/commands/import.js +211 -14
- package/dist/commands/mcp.js +128 -0
- package/dist/component-codegen.js +52 -15
- 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/index.js +35 -5
- package/dist/project-facts.js +58 -2
- package/dist/skill-import.js +215 -17
- package/package.json +1 -1
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.
|
|
121
132
|
|
|
122
|
-
|
|
123
|
-
|
|
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.
|
|
141
|
+
|
|
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
|
|
@@ -133,6 +153,7 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
133
153
|
"themes": { "default": "dark", "has": ["dark"] },
|
|
134
154
|
"roles": { "canvas": "#050505", "foreground": "#f9fafb", "primary": "#4A90E2" },
|
|
135
155
|
"fonts": { "display": "Inter", "body": "Inter" },
|
|
156
|
+
"typeRoles": { "base": "body-m", "display": "h1", "xs": "caption" },
|
|
136
157
|
"concept": "one paragraph on what this product is",
|
|
137
158
|
"rules": [
|
|
138
159
|
{
|
|
@@ -149,6 +170,13 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
149
170
|
"kind": "limit",
|
|
150
171
|
"files": 1,
|
|
151
172
|
"evidence": "one dashboard page; may just be how it happened"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"text": "TextEditor's editable region is <EditorContent> - never render children into it directly",
|
|
176
|
+
"applies": ["TextEditor"],
|
|
177
|
+
"kind": "implementation",
|
|
178
|
+
"fact": true,
|
|
179
|
+
"evidence": "read in TextEditor itself: useEditor() feeds a single <EditorContent>"
|
|
152
180
|
}
|
|
153
181
|
],
|
|
154
182
|
"components": {
|
|
@@ -167,6 +195,7 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
167
195
|
]
|
|
168
196
|
},
|
|
169
197
|
"ArticleCard": {
|
|
198
|
+
"root": "Card",
|
|
170
199
|
"anatomy": [
|
|
171
200
|
{ "as": "image", "name": "cover", "classes": "aspect-video w-full rounded-t-lg" },
|
|
172
201
|
{ "as": "heading", "name": "title", "classes": "text-lg font-semibold" },
|
|
@@ -184,7 +213,12 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
184
213
|
},
|
|
185
214
|
"TextEditor": {
|
|
186
215
|
"anatomy": [
|
|
187
|
-
{
|
|
216
|
+
{
|
|
217
|
+
"as": "row",
|
|
218
|
+
"name": "toolbar",
|
|
219
|
+
"classes": "flex gap-1 border-b p-2",
|
|
220
|
+
"children": [{ "as": "component", "ref": "ToolbarButton" }]
|
|
221
|
+
},
|
|
188
222
|
{ "as": "external", "from": "@tiptap/react" }
|
|
189
223
|
]
|
|
190
224
|
}
|
|
@@ -221,6 +255,26 @@ about which one is the page. Read a screen and see.
|
|
|
221
255
|
**\`fonts\` and \`concept\`** - the voice, and one paragraph on what this product is. The concept
|
|
222
256
|
feeds every recommendation downstream, so a real one beats a generic one by a wide margin.
|
|
223
257
|
|
|
258
|
+
**\`typeRoles\` - which of THEIR type steps plays each of our seven slots.**
|
|
259
|
+
|
|
260
|
+
Their scale arrives under their own names now - \`h1\`, \`body-m\`, \`caption\`, \`overline\` - read
|
|
261
|
+
straight off their \`--text-*\` declarations. What arithmetic cannot know is which of those steps
|
|
262
|
+
is *body copy* and which is *the display size*, and our own components ask for it by slot:
|
|
263
|
+
|
|
264
|
+
\`\`\`json
|
|
265
|
+
"typeRoles": {
|
|
266
|
+
"xs": "caption", "sm": "body-s", "base": "body-m", "lg": "body-l",
|
|
267
|
+
"xl": "h3", "2xl": "h2", "display": "h1"
|
|
268
|
+
}
|
|
269
|
+
\`\`\`
|
|
270
|
+
|
|
271
|
+
Leave it out and we pick by size, which is usually right and occasionally silly - a project whose
|
|
272
|
+
\`overline\` is tiny and whose \`caption\` is tinier gets them the wrong way round. One line from you
|
|
273
|
+
fixes it, and a person can correct it later in the studio either way.
|
|
274
|
+
|
|
275
|
+
**Do not rename their steps to match ours.** \`h1\` stays \`h1\`. The whole point is that editing
|
|
276
|
+
\`body-m\` in the studio moves the text in their app.
|
|
277
|
+
|
|
224
278
|
**\`rules\` - how this company BUILDS, which is half of what they actually made.**
|
|
225
279
|
|
|
226
280
|
A design system that arrives as tokens and recipes is only the vocabulary. The other half is the
|
|
@@ -258,12 +312,39 @@ Use it only when the rule genuinely depends on the environment. Most rules do no
|
|
|
258
312
|
a Sidebar loose"* is true about their design regardless of framework, and pinning it to \`next\`
|
|
259
313
|
would quietly drop it the day they add a second app.
|
|
260
314
|
|
|
261
|
-
**Report \`files\` honestly - it decides whether
|
|
262
|
-
habit and the rule arrives active; one file is a coincidence and it arrives as a candidate,
|
|
315
|
+
**Report \`files\` honestly - it decides whether an OBSERVED rule governs.** Three or more files
|
|
316
|
+
is a habit and the rule arrives active; one file is a coincidence and it arrives as a candidate,
|
|
263
317
|
inactive, waiting for the person to promote it. You do not make that call; you report the
|
|
264
318
|
evidence and a threshold makes it. So a pattern you saw once should say \`"files": 1\` even when
|
|
265
319
|
you are confident - being wrong about a law is worse than being slow about one.
|
|
266
320
|
|
|
321
|
+
**\`fact: true\` - for a rule you read in the DEFINITION, where counting is the wrong question.**
|
|
322
|
+
|
|
323
|
+
Three of these were reported as \`files: 1\` and arrived inactive, which was correct arithmetic on
|
|
324
|
+
the wrong kind of claim (dono, 01/08):
|
|
325
|
+
|
|
326
|
+
\`\`\`
|
|
327
|
+
the editable region is <EditorContent>
|
|
328
|
+
toolbar actions go through editor.chain().focus()
|
|
329
|
+
extensions are configured at construction
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
None of those is a coincidence waiting for a second sighting. They are how the component IS
|
|
333
|
+
built, read off its own source, and they are true the moment somebody wrote it. Counting how many
|
|
334
|
+
files agree would leave every construction law in the codebase inactive forever.
|
|
335
|
+
|
|
336
|
+
So the test is **where you read it**, not how sure you feel:
|
|
337
|
+
|
|
338
|
+
\`\`\`
|
|
339
|
+
fact: true you read it inside the component's own definition - its imports, its
|
|
340
|
+
JSX, how its state is wired. True by construction.
|
|
341
|
+
files: N you inferred it from how the component is USED across the project.
|
|
342
|
+
An observation, and the count is what makes it a habit.
|
|
343
|
+
\`\`\`
|
|
344
|
+
|
|
345
|
+
A rule can carry \`fact\` OR \`files\`, never both. If you find yourself wanting both, it is an
|
|
346
|
+
observation - use \`files\`.
|
|
347
|
+
|
|
267
348
|
**Write \`evidence\` as what you actually saw.** "All three app shells wrap them; none renders
|
|
268
349
|
them loose" lets somebody disagree with a fact. "Best practice" lets them disagree only with
|
|
269
350
|
you.
|
|
@@ -302,7 +383,42 @@ an EXTERNAL a third-party library we do not have it and never will → bl
|
|
|
302
383
|
component or a library, stop there, and record the edge. That is why no parameter tells you how
|
|
303
384
|
deep to go: a \`Divider\` is one node deep and a dashboard shell is five, and both are complete.
|
|
304
385
|
|
|
305
|
-
|
|
386
|
+
**You decide the depth, and nothing downstream caps it.** The platform follows a
|
|
387
|
+
\`component\` edge into that component's own anatomy, and then into ITS edges, as far as the chain
|
|
388
|
+
goes - \`Chat → Message → TypingIndicator\` renders all three. So a frontier is not a dead end you
|
|
389
|
+
are apologising for; it is how the chain gets walked. Record the edge and stop, and the whole
|
|
390
|
+
depth appears anyway.
|
|
391
|
+
|
|
392
|
+
### The ROOT is a frontier too
|
|
393
|
+
|
|
394
|
+
\`\`\`json
|
|
395
|
+
"components": {
|
|
396
|
+
"ArticleCard": {
|
|
397
|
+
"root": "Card",
|
|
398
|
+
"anatomy": [ … ]
|
|
399
|
+
},
|
|
400
|
+
"Modal": {
|
|
401
|
+
"root": "BaseDialog.Root",
|
|
402
|
+
"anatomy": [ … ]
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
\`\`\`
|
|
406
|
+
|
|
407
|
+
**Say what the component RETURNS when it is not a plain tag.** Measured on a real library
|
|
408
|
+
(dono, 01/08): 14 of 23 components return one of their own - \`ArticleCard\` returns a \`<Card>\`,
|
|
409
|
+
\`Button\` and \`Text\` return a \`<Component>\` - and 17 of 23 have no style of their own at all,
|
|
410
|
+
because the surface belongs to the root.
|
|
411
|
+
|
|
412
|
+
Without \`root\`, an \`ArticleCard\` previewed as floating text: the background, the border, the
|
|
413
|
+
radius and the padding had nowhere to come from. With it, the card wears \`Card\`'s recipe as its
|
|
414
|
+
shell and its own parts inside.
|
|
415
|
+
|
|
416
|
+
- **their component** - the name, as their code spells it: \`"root": "Card"\`
|
|
417
|
+
- **a library** - the dotted namespace, verbatim: \`"root": "Radio.Root"\`,
|
|
418
|
+
\`"root": "BaseDialog.Root"\`, \`"root": "Popover.Root"\`
|
|
419
|
+
- **a plain tag** - leave it out. \`<div>\`, \`<td>\`, \`<button>\` are not frontiers.
|
|
420
|
+
|
|
421
|
+
### The ten forms
|
|
306
422
|
|
|
307
423
|
\`as\` says what a node IS, and the renderer draws that. Nothing else is accepted:
|
|
308
424
|
|
|
@@ -317,10 +433,40 @@ row arranges its children ACROSS
|
|
|
317
433
|
stack arranges its children DOWN
|
|
318
434
|
component their component → needs "ref": the name as their code spells it
|
|
319
435
|
external a library → needs "from": the package name
|
|
436
|
+
slot the CALLER's content → takes "expects": what goes there, in their words
|
|
320
437
|
\`\`\`
|
|
321
438
|
|
|
322
439
|
Only \`row\` and \`stack\` take \`children\`. Everything else is a leaf.
|
|
323
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
|
+
|
|
324
470
|
**\`name\` is the part name**, and it is what carries the styles - flat, lowercase, kebab. Never
|
|
325
471
|
nested: \`"actions.generate"\` compiles to two CSS classes and is invalid, so name it
|
|
326
472
|
\`"generate-action"\`. Name what it IS - \`label\`, \`value\`, \`delta\`, \`cover\`, \`toolbar\` - because the
|
|
@@ -344,10 +490,22 @@ of. It previews as a block carrying its name, and it becomes a **rule** - \`Arti
|
|
|
344
490
|
That edge is a **fact, not a habit**: you saw it in the definition, so it is true once and for
|
|
345
491
|
all, and it arrives active without waiting for a third sighting.
|
|
346
492
|
|
|
347
|
-
|
|
493
|
+
Three things that are NOT edges, and sending them as such would be wrong:
|
|
348
494
|
|
|
349
495
|
- \`motion.div\`, \`Dialog.Root\`, \`Radio.Item\` - a library's namespace, not a component of theirs
|
|
350
496
|
- an HTML element with a capital in a variable name
|
|
497
|
+
- **a name the file binds itself and does not export.** Capitalisation is React's rule for "not an
|
|
498
|
+
html tag", which is a different question from "is this a component of the system". Both of these
|
|
499
|
+
arrived as components of a real library and neither is one (dono, 01/08):
|
|
500
|
+
|
|
501
|
+
\`\`\`tsx
|
|
502
|
+
function Text({ as: Component = "p" }) // a prop rename - the polymorphic idiom
|
|
503
|
+
const CustomLegend = () => (…) // a closure inside LineChart
|
|
504
|
+
\`\`\`
|
|
505
|
+
|
|
506
|
+
The test is reachability: a component of the system is imported from somewhere or exported to
|
|
507
|
+
somewhere. A name that only lives inside one function body is that function's private wiring,
|
|
508
|
+
and giving it a recipe invents a component nobody can import.
|
|
351
509
|
|
|
352
510
|
If the component came from a package, it is \`external\`, not \`component\`.
|
|
353
511
|
|
|
@@ -379,9 +537,15 @@ Send an anatomy for **every component with visible structure**, which is nearly
|
|
|
379
537
|
\`CircularProgress\` has a track and a fill. Sending none is right only for something genuinely
|
|
380
538
|
undivided - a \`Divider\`, a \`Spacer\`.
|
|
381
539
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
540
|
+
**There is no node budget, and the one I gave you was wrong.** "Three to six nodes is a
|
|
541
|
+
recognisable component" cost a real \`ArticleCard\` most of itself: it has a carousel with arrows,
|
|
542
|
+
three image buttons, a source chip, a refresh, a rich body, a source line and a "Similar
|
|
543
|
+
published content" region with two selects and two buttons - about fourteen regions - and the
|
|
544
|
+
reading came back with six because the guidance said so (dono, 01/08).
|
|
545
|
+
|
|
546
|
+
**One node per region a person can point at.** If they can say "that part", it is a node. What
|
|
547
|
+
you still collapse is a wrapper whose only job is \`flex\` - fold it into the \`row\` it already is,
|
|
548
|
+
because that is not a region, it is plumbing.
|
|
385
549
|
|
|
386
550
|
The cost of sending none is not neutral. A component with no anatomy previews as a grey box with
|
|
387
551
|
a sentence in it, or - if its kind is \`indicator\` - as a small blank shape. A component with a
|
|
@@ -395,7 +559,7 @@ spec view has no nesting to draw.
|
|
|
395
559
|
|
|
396
560
|
### 3. Walk them through the decisions, one at a time
|
|
397
561
|
|
|
398
|
-
|
|
562
|
+
Five decisions are theirs. **Ask them as separate questions with selectable options** - use your
|
|
399
563
|
question tool, one call per decision, so they pick instead of reading a wall and composing a
|
|
400
564
|
reply. A single block containing everything is a report, and a report gets read, not answered.
|
|
401
565
|
|
|
@@ -479,6 +643,36 @@ canvas #222326 darkgray-500 - what DashboardLayout paints on <main>
|
|
|
479
643
|
Say it and let them object. Offering five near-blacks to pick between is asking someone to
|
|
480
644
|
re-decide something their code already decided.
|
|
481
645
|
|
|
646
|
+
**3e-bis. Our kit, or theirs alone** - ask this ONLY when they already have a component library,
|
|
647
|
+
which is when \`--scope\` pointed at one and the CLI said so. An app with no components of its own
|
|
648
|
+
has nothing to weigh and should never see the question.
|
|
649
|
+
|
|
650
|
+
\`\`\`
|
|
651
|
+
Every system born here comes with 45 components of ours - button, card, input,
|
|
652
|
+
table, the rest of a working toolkit. You already have 37.
|
|
653
|
+
|
|
654
|
+
add ours to yours (recommended) 68 components, and the 23 governed by your
|
|
655
|
+
own rules are the 23 you wrote
|
|
656
|
+
mine alone 37 components, all of them yours. Our
|
|
657
|
+
layouts that need a component you do not
|
|
658
|
+
have stay out too. Add any of ours later
|
|
659
|
+
from the library, one at a time
|
|
660
|
+
\`\`\`
|
|
661
|
+
|
|
662
|
+
Send it as \`"standards": "none"\` in the reading when they choose the second, and leave the field
|
|
663
|
+
out when they choose the first. **Recommend adding ours**, and say why in one line: a component of
|
|
664
|
+
ours costs nothing until something composes it, and having a \`table\` the day somebody needs a
|
|
665
|
+
table is worth more than a shorter list.
|
|
666
|
+
|
|
667
|
+
Two things to be straight about, because both are true and neither is obvious:
|
|
668
|
+
|
|
669
|
+
- **the foundations come from the seed either way.** Their palette, their scales, their type - all
|
|
670
|
+
theirs - but the seven type slots, the easing and the neutral floor arrive from ours, because
|
|
671
|
+
the alternative to a type scale is no components at all rather than their components.
|
|
672
|
+
- **our layouts are written in our recipes.** A landing that composes \`button\`, \`card\` and
|
|
673
|
+
\`badge\` needs those to exist. Choosing *mine alone* keeps the layouts whose recipes they
|
|
674
|
+
happen to have and drops the rest - a layout pointing at a recipe nobody has renders as holes.
|
|
675
|
+
|
|
482
676
|
**3f. Fonts** - do not ask at all. Report and move on:
|
|
483
677
|
|
|
484
678
|
\`\`\`
|
|
@@ -493,7 +687,7 @@ way. If it declares one, there is nothing to decide.
|
|
|
493
687
|
carries the weight - \`ocean-500\` tells someone more than a square would. When they run
|
|
494
688
|
\`npx synthesisui import\` themselves in a terminal, the CLI paints them.
|
|
495
689
|
|
|
496
|
-
Then, and only after all
|
|
690
|
+
Then, and only after all of them are answered:
|
|
497
691
|
|
|
498
692
|
\`\`\`
|
|
499
693
|
npx synthesisui import --census _synthesisui/census.json --name "<the name>" [--registry <from step 0>]
|
|
@@ -501,7 +695,7 @@ npx synthesisui import --census _synthesisui/census.json --name "<the name>" [--
|
|
|
501
695
|
|
|
502
696
|
**This is the one real write.** Everything before it is on their disk and costs nothing to redo.
|
|
503
697
|
|
|
504
|
-
Pass every answer explicitly - \`--name\`, \`--scope\` if they changed
|
|
698
|
+
Pass every answer explicitly - \`--name\`, \`--scope\` and \`--usage\` if they changed either, and the reading rewritten
|
|
505
699
|
with their primary and their theme. The CLI cannot ask anything when you are the one running it:
|
|
506
700
|
it has no terminal, so its own prompts are skipped by design. **Whatever you did not ask, nobody
|
|
507
701
|
asked.** Skip 3a and the first time they learn where their system came from is when they open
|
|
@@ -525,6 +719,10 @@ proposal. Say all of this in your own words - do not just print the link:
|
|
|
525
719
|
which half was deliberate: nothing was invented, so a component whose classes named no token
|
|
526
720
|
arrives with structure and no colour. The platform shows that as *Not written yet*, not as a
|
|
527
721
|
zero.
|
|
722
|
+
- **their own ladder**, if their repo has one. Components under \`atoms/\`, \`molecules/\` and
|
|
723
|
+
\`organisms/\` arrive grouped that way in the vitrine, read off the folder rather than guessed
|
|
724
|
+
from a name. You do not send this and you should not try to: a project that does not organise
|
|
725
|
+
itself this way gets one honest group instead of three invented ones.
|
|
528
726
|
- **the shape of each component**, if you sent one: its parts nested as they nest in their code,
|
|
529
727
|
the components of theirs it composes, and the libraries it needs. Each edge is also a rule now,
|
|
530
728
|
which is what makes it survive being looked at once.
|
package/package.json
CHANGED