synthesisui 0.16.121 → 0.16.123
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 +13 -4
- package/dist/doctor/variant-read.js +29 -4
- package/dist/guide.js +19 -1
- package/package.json +1 -1
package/dist/anatomy-read.js
CHANGED
|
@@ -324,14 +324,23 @@ sketch) {
|
|
|
324
324
|
/**
|
|
325
325
|
* A MOTION ELEMENT IS THEIRS TO NAME TOO. `motion.div` is the element their panel
|
|
326
326
|
* opens with, and the props that make it open have no CSS to translate to - so the
|
|
327
|
-
* snippet emits their element and their props, verbatim.
|
|
328
|
-
*
|
|
329
|
-
*
|
|
327
|
+
* snippet emits their element and their props, verbatim.
|
|
328
|
+
*
|
|
329
|
+
* AND SO IS A GLYPH, which this gate kept out and the deriver did not - so the fix
|
|
330
|
+
* landed on the DERIVED path and missed the AUTHORED one, which is the path he
|
|
331
|
+
* runs. Measured on his own live system (test44, dono, 04/08): 35 of 37 icon nodes
|
|
332
|
+
* arrived with no package, across 17 of 32 components, and the snippet handed him
|
|
333
|
+
* `<span className="ds-collapsible-card-chevron">…</span>` where his file writes
|
|
334
|
+
* `<ChevronUp className="size-5 …" />`. Third time this seam has taken a field.
|
|
335
|
+
*
|
|
336
|
+
* Headless for the behaviour, motion for the animation, icon for the drawing - the
|
|
337
|
+
* three whose every pixel is in their own class list. An opaque library draws its
|
|
338
|
+
* own insides and is still named and stopped at.
|
|
330
339
|
*/
|
|
331
340
|
if (measured?.from &&
|
|
332
341
|
measured.tag &&
|
|
333
342
|
/^[A-Za-z]/.test(measured.tag) &&
|
|
334
|
-
(kind === "headless" || kind === "motion")) {
|
|
343
|
+
(kind === "headless" || kind === "motion" || kind === "icon")) {
|
|
335
344
|
node_.from = measured.from;
|
|
336
345
|
node_.refName = measured.tag;
|
|
337
346
|
}
|
|
@@ -508,14 +508,39 @@ export function readVariants(source) {
|
|
|
508
508
|
* Read separately from `cva` because a file can have both, and because this shape is
|
|
509
509
|
* what a codebase that never adopted a variant library uses everywhere.
|
|
510
510
|
*/
|
|
511
|
-
|
|
511
|
+
/**
|
|
512
|
+
* THE `!` HAS TO BE PART OF THE MATCH, or the two opposite branches of one `cn()` become
|
|
513
|
+
* the same condition.
|
|
514
|
+
*
|
|
515
|
+
* `!` is not in `[A-Za-z_$]`, so `!isLoading && "cursor-pointer"` matched starting at
|
|
516
|
+
* `isLoading` and the negation fell outside the match, in silence. Their collapsible card
|
|
517
|
+
* writes both branches:
|
|
518
|
+
*
|
|
519
|
+
* !isLoading && "cursor-pointer hover:bg-lightgray-200/50"
|
|
520
|
+
* isLoading && "cursor-not-allowed opacity-80"
|
|
521
|
+
*
|
|
522
|
+
* and both arrived as `{isLoading: "true"}` - so the same state carried `cursor: pointer`
|
|
523
|
+
* AND `cursor: not-allowed`, with whichever came last winning. A dead rule is bad; a rule
|
|
524
|
+
* that says the opposite of their code is worse (dono, 04/08). Measured on his library: 8
|
|
525
|
+
* inline conditions with a class list, 2 of them negated.
|
|
526
|
+
*/
|
|
527
|
+
const INLINE_COND = /(!?)\s*([A-Za-z_$][\w$.]*)\s*(?:===|==)\s*["']([^"']+)["']\s*&&\s*(["'`])((?:(?!\4).)*)\4|(!?)\s*([A-Za-z_$][\w$]*)\s*&&\s*(["'`])((?:(?!\8).)*)\8/g;
|
|
512
528
|
export function readInlineConditions(source) {
|
|
513
529
|
const layers = [];
|
|
514
530
|
INLINE_COND.lastIndex = 0;
|
|
515
531
|
for (const m of source.matchAll(INLINE_COND)) {
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
const raw = m[
|
|
532
|
+
const negated = (m[1] ?? m[6]) === "!";
|
|
533
|
+
const prop = (m[2] ?? m[7] ?? "").split(".").pop() ?? "";
|
|
534
|
+
const raw = m[5] ?? m[9] ?? "";
|
|
535
|
+
/**
|
|
536
|
+
* `!status === "loading"` is not a thing anybody writes, and negating a comparison we
|
|
537
|
+
* cannot invert would guess. A comparison is taken as written; only the bare boolean
|
|
538
|
+
* flips, and it flips to the option `false` - which is what `data-x="false"` means.
|
|
539
|
+
*/
|
|
540
|
+
const compared = m[3];
|
|
541
|
+
if (compared != null && negated)
|
|
542
|
+
continue;
|
|
543
|
+
const option = compared ?? (negated ? "false" : "true");
|
|
519
544
|
if (!prop || raw.includes("${"))
|
|
520
545
|
continue;
|
|
521
546
|
const classes = raw.split(/\s+/).filter(Boolean);
|
package/dist/guide.js
CHANGED
|
@@ -198,6 +198,16 @@ function componentEntry(cname, recipe) {
|
|
|
198
198
|
/** Its own laws, computed from its own usage - and nothing read them (dono, 04/08). */
|
|
199
199
|
const laws = (recipe.usage ?? []).map((law) => `\n - ${law}`).join("");
|
|
200
200
|
const lawsText = laws ? `\n it obeys:${laws}` : "";
|
|
201
|
+
/**
|
|
202
|
+
* WHAT IT DOES, beside what it obeys - and they are not the same reader.
|
|
203
|
+
*
|
|
204
|
+
* `usage` is law: short, imperative, checkable, injected into a Build Script. This is the
|
|
205
|
+
* briefing for somebody rebuilding the component in a project that has none of these prop
|
|
206
|
+
* names, which is the question the guide exists to answer (dono, 04/08). Measured on a
|
|
207
|
+
* real system: 23 of 35 components have something derived to say here.
|
|
208
|
+
*/
|
|
209
|
+
const does = (recipe.behaviour ?? []).map((line) => `\n - ${line}`).join("");
|
|
210
|
+
const doesText = does ? `\n what it does:${does}` : "";
|
|
201
211
|
const behaviour = behaviourLine(cname, recipe);
|
|
202
212
|
const behaviourText = behaviour ? `\n ${behaviour}` : "";
|
|
203
213
|
/**
|
|
@@ -240,7 +250,7 @@ function componentEntry(cname, recipe) {
|
|
|
240
250
|
const anatomyText = anatomy.length
|
|
241
251
|
? `\n anatomy, in this nesting:\n${anatomy.join("\n")}`
|
|
242
252
|
: "";
|
|
243
|
-
return `- **${cname}** (\`${cls}\`)${variantsText}\n ${recipe.description}${factsText}${defaultsText}${partsText}${statesText}${behaviourText}${contractText}${anatomyText}${lawsText}`;
|
|
253
|
+
return `- **${cname}** (\`${cls}\`)${variantsText}\n ${recipe.description}${factsText}${defaultsText}${partsText}${statesText}${behaviourText}${contractText}${anatomyText}${doesText}${lawsText}`;
|
|
244
254
|
}
|
|
245
255
|
/**
|
|
246
256
|
* Builds GUIDE.md - instructions *for the agent* on how to build components
|
|
@@ -402,6 +412,14 @@ It writes \`<componentsDir>/button/\` with \`button.tsx\` (+ colocated \`button.
|
|
|
402
412
|
The boundary: tokens are global (\`tokens.css\` + the \`data-ds\` root attribute, once per app);
|
|
403
413
|
everything a component owns lives in its own folder.
|
|
404
414
|
|
|
415
|
+
**A component's conditional classes are decided at the call site, not in the stylesheet.** A recipe
|
|
416
|
+
carries the look of a state as a rule keyed on a data attribute (\`[data-size="lg"]\`), and the caller
|
|
417
|
+
is what turns it on. \`synthesisui component\` emits this system's own \`cn()\` beside the component: it
|
|
418
|
+
resolves two utilities that set the SAME property in favour of the one the caller passed, which plain
|
|
419
|
+
Tailwind decides by emission order instead. Compose with it and an override wins; concatenate strings
|
|
420
|
+
and which one applies is a coin flip. Where a component needs an attribute nobody sets by default, its
|
|
421
|
+
own entry below says so.
|
|
422
|
+
|
|
405
423
|
---
|
|
406
424
|
`
|
|
407
425
|
: "";
|
package/package.json
CHANGED