synthesisui 0.16.305 → 0.16.306
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/agent-wiring.js +11 -9
- package/dist/commands/align.js +17 -1
- package/dist/commands/import.js +12 -2
- package/dist/doctor/fragments.js +6 -2
- package/dist/doctor/transcribe.js +11 -1
- package/dist/doctor/variant-read.js +199 -2
- package/dist/install-marks.js +26 -2
- package/package.json +1 -1
package/dist/agent-wiring.js
CHANGED
|
@@ -37,20 +37,22 @@ export async function hookCommand(root, version) {
|
|
|
37
37
|
: `npx synthesisui@${version} hook`;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* COMO A SESSÃO CHAMA O CLI,
|
|
40
|
+
* COMO A SESSÃO CHAMA O CLI - pinado, como o hook de escrita, e por um número.
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* Este já foi `@latest`, com a tese de que um `align` pinado não conhece verificação escrita depois
|
|
43
|
+
* dele. A tese perdeu para a medição de 25/08: `@latest` NUNCA cacheia - o npx faz round-trip no
|
|
44
|
+
* registro em TODA abertura de sessão (2,92s, sempre), enquanto a versão fixada cacheia (0,32s
|
|
45
|
+
* morno). O trabalho do align em si custa 50ms; o resto era o preço de manter a tese.
|
|
46
46
|
*
|
|
47
|
-
*
|
|
47
|
+
* E a tese tinha resposta: dizer o que está atrasado - inclusive o próprio CLI - continua sendo o
|
|
48
|
+
* trabalho do align, só que agora o fato vem do SERVIDOR (`cli` no `?meta=1`, ver `versionBehind`),
|
|
49
|
+
* que sabe o que está publicado sem que este binário precise se reinstalar para perguntar.
|
|
48
50
|
*/
|
|
49
|
-
export async function alignCommand(root) {
|
|
51
|
+
export async function alignCommand(root, version) {
|
|
50
52
|
const local = join(root, "node_modules", ".bin", "synthesisui");
|
|
51
53
|
return (await exists(local))
|
|
52
54
|
? "npx --no-install synthesisui align"
|
|
53
|
-
:
|
|
55
|
+
: `npx synthesisui@${version} align`;
|
|
54
56
|
}
|
|
55
57
|
/**
|
|
56
58
|
* Returns the command that IS in the file, not the one we would have written.
|
|
@@ -234,7 +236,7 @@ export async function wireAgent(root, version, want) {
|
|
|
234
236
|
* logado" ou "este sistema não sabe de onde foi medido".
|
|
235
237
|
*/
|
|
236
238
|
session: want.hook
|
|
237
|
-
? await wireSessionStart(root, await alignCommand(root))
|
|
239
|
+
? await wireSessionStart(root, await alignCommand(root, version))
|
|
238
240
|
: "skipped",
|
|
239
241
|
mcp: want.mcp ? await wireMcp(root, version) : "skipped",
|
|
240
242
|
};
|
package/dist/commands/align.js
CHANGED
|
@@ -459,6 +459,20 @@ export async function versionBehind(root, opts = {}) {
|
|
|
459
459
|
says: `the rules that govern "${lock.slug}" changed since this repo materialized them - your agent reads the copy on disk, so it is still following the previous set.`,
|
|
460
460
|
run: "npx synthesisui upgrade",
|
|
461
461
|
};
|
|
462
|
+
/**
|
|
463
|
+
* ESTE BINÁRIO FICOU PARA TRÁS - a linha que devolve ao align o trabalho que o `@latest` fazia.
|
|
464
|
+
*
|
|
465
|
+
* A abertura de sessão roda uma versão PINADA desde 25/08 (o `@latest` custava 2,92s de registro
|
|
466
|
+
* em toda sessão, medido), então nada aqui descobre sozinho que existe CLI mais novo - o número
|
|
467
|
+
* vem do servidor, que consulta o npm e cala quando não sabe. Vem POR ÚLTIMO de propósito: um
|
|
468
|
+
* fato sobre o sistema dele vale mais que uma novidade sobre a nossa ferramenta, e esta linha só
|
|
469
|
+
* aparece quando todo o resto está em dia.
|
|
470
|
+
*/
|
|
471
|
+
if (opts.cli && body.cli && isOlderCli(opts.cli, body.cli))
|
|
472
|
+
return {
|
|
473
|
+
says: `this session's opening check runs CLI ${opts.cli} and ${body.cli} is what installs today - the check is pinned on purpose, so it will not move by itself.`,
|
|
474
|
+
run: "npx synthesisui@latest connect",
|
|
475
|
+
};
|
|
462
476
|
return null;
|
|
463
477
|
}
|
|
464
478
|
/**
|
|
@@ -550,7 +564,9 @@ export async function misalignments(root, opts = {}) {
|
|
|
550
564
|
...(opts.cli ? { cli: opts.cli } : {}),
|
|
551
565
|
}).catch(() => []);
|
|
552
566
|
/** A única linha que custa rede, e ela some inteira quando não há rede. */
|
|
553
|
-
const remote = await versionBehind(root
|
|
567
|
+
const remote = await versionBehind(root, {
|
|
568
|
+
...(opts.cli ? { cli: opts.cli } : {}),
|
|
569
|
+
}).catch(() => null);
|
|
554
570
|
if (remote)
|
|
555
571
|
items.push(remote);
|
|
556
572
|
return items;
|
package/dist/commands/import.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
-
import { basename, dirname, join, relative } from "node:path";
|
|
2
|
+
import { basename, dirname, join, relative, sep } from "node:path";
|
|
3
3
|
import { anatomyFromSketch } from "../anatomy-from-sketch.js";
|
|
4
4
|
import { applyAnatomyPatch, hasEdits, } from "../anatomy-patch.js";
|
|
5
5
|
import { resolveAnatomy, resolveFlatParts, safePartName, } from "../anatomy-read.js";
|
|
@@ -402,8 +402,18 @@ export async function takeCensus(root, opts) {
|
|
|
402
402
|
* Os caminhos que o leitor REALMENTE olhou são o escopo mais os de uso: `--scope packages/ui
|
|
403
403
|
* --usage apps/web` lê os dois, então nenhum dos dois é "fora". Sem `scopeLabel` a função devolve
|
|
404
404
|
* `null` por conta, porque aí ele apontou para a raiz.
|
|
405
|
+
*
|
|
406
|
+
* A CAMINHADA PARTE DA RAIZ DO REPO, não da raiz escopada. `root` aqui JÁ é
|
|
407
|
+
* `<repo>/<scopeLabel>` (o chamador faz `join(root, one)`), então medir a partir dele compara
|
|
408
|
+
* `src/Button.tsx` com `packages/ui/…` - nada casa, e os 121 arquivos que a esteira acabou de LER
|
|
409
|
+
* eram reportados como "fora do que você apontou - nada foi lido lá". Disparava SEMPRE com
|
|
410
|
+
* `--scope`, e o primeiro relatório do cliente abria com uma acusação falsa sobre o repositório
|
|
411
|
+
* dele (medido em 25/08: 121 contra os 14 reais).
|
|
405
412
|
*/
|
|
406
|
-
const
|
|
413
|
+
const repoRoot = scopeLabel && root.endsWith(join(sep, ...scopeLabel.split("/")))
|
|
414
|
+
? root.slice(0, root.length - scopeLabel.length - 1)
|
|
415
|
+
: root;
|
|
416
|
+
const outside = await outsideScope(repoRoot, [
|
|
407
417
|
...(scopeLabel ? [scopeLabel] : []),
|
|
408
418
|
...(opts?.usage ?? []).map((u) => u.label),
|
|
409
419
|
]).catch(() => null);
|
package/dist/doctor/fragments.js
CHANGED
|
@@ -45,8 +45,12 @@ const STYLED_OPEN = /\bstyled(?:\.[a-zA-Z][\w]*|\([^)]{1,60}\))\s*(?:<[^>]{0,120
|
|
|
45
45
|
const PIECES = /([^{};]*)([{};]|$)/g;
|
|
46
46
|
/** Uma declaração `prop: value`, já sem o terminador. */
|
|
47
47
|
const DECLARATION = /^(-{0,2}[a-zA-Z][\w-]*)\s*:\s*([^;{}]{1,200})$/;
|
|
48
|
-
/**
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Uma classe que só um literal pode ter: começa com letra minúscula - ou com o `!` de important,
|
|
50
|
+
* que é como o Tailwind v3 escreve `!text-ink-900`. Sem o `!`, um className inteiro de important
|
|
51
|
+
* nem entrava no denominador: não era lido E não era "não lido" (25/08).
|
|
52
|
+
*/
|
|
53
|
+
const LOOKS_LIKE_CLASS = /^[!a-z[]/;
|
|
50
54
|
/**
|
|
51
55
|
* `${styles.card}` dentro de um template: uma classe de CSS module, cujo valor mora no arquivo de
|
|
52
56
|
* estilo - que a esteira lê. É a mesma regra que `CLASS_RUNTIME` já aplica a `className={styles.x}`.
|
|
@@ -669,7 +669,17 @@ function withAlpha(d, alpha) {
|
|
|
669
669
|
}
|
|
670
670
|
/** `w-1/2`, `basis-2/3` - a fraction is a width, and the `/` is not an alpha. */
|
|
671
671
|
const FRACTION = /^(w|h|basis|max-w|max-h|min-w|min-h|top|right|bottom|left|inset)-(\d+)\/(\d+)$/;
|
|
672
|
-
export function readUtility(
|
|
672
|
+
export function readUtility(raw, declared) {
|
|
673
|
+
/**
|
|
674
|
+
* O `!` DE IMPORTANT SAI ANTES DE TUDO - `!text-ink-900` (v3) e `text-ink-900!` (v4).
|
|
675
|
+
*
|
|
676
|
+
* O important é como ele força o cascade DENTRO do app dele; a decisão de design é o valor, e
|
|
677
|
+
* era ela que morria: `!text-ink-900` chegava aqui inteiro, nenhum prefixo casava, e a camada
|
|
678
|
+
* sumia sem uma linha - com `--color-ink-900` declarado no CSS dele. 48 valores num censo real,
|
|
679
|
+
* 20 e 4 nas duas populações congeladas (25/08). Numa receita não há cascade para forçar, então
|
|
680
|
+
* despir o `!` não perde decisão nenhuma.
|
|
681
|
+
*/
|
|
682
|
+
const utility = raw.replace(/^!/, "").replace(/!$/, "");
|
|
673
683
|
/**
|
|
674
684
|
* BEFORE THE ALPHA SPLIT, because the two share a slash and only one of them is a
|
|
675
685
|
* colour. `w-1/2` was reaching the size reader as `w-1` and coming back `0.25rem` -
|
|
@@ -270,6 +270,67 @@ export function literalClasses(fragment) {
|
|
|
270
270
|
}
|
|
271
271
|
return out;
|
|
272
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* OS TRECHOS DO ARQUIVO QUE CONSOMEM CLASSE - `className={…}` (e qualquer `*ClassName=`),
|
|
275
|
+
* e as chamadas que produzem classe (`cn`, `clsx`, `cva`, `tv`, …).
|
|
276
|
+
*
|
|
277
|
+
* Existe porque os três leitores de condição liam o arquivo INTEIRO como se toda string fosse
|
|
278
|
+
* classe: 531 de 1119 entradas de `rawLayers.classes` num censo real eram literais de ternário e
|
|
279
|
+
* de record que nunca foram classe - `gray`, `True`, `Find`, `✅`, `h1` - e o gradiente do Aurora
|
|
280
|
+
* deles chegava partido em `0%,rgba(…)` porque um VALOR de CSS foi partido no espaço como se
|
|
281
|
+
* fosse lista de classes (medido em 25/08). A forma sozinha não decide (`gray` e `hidden` têm a
|
|
282
|
+
* mesma forma); o consumo decide - a mesma doutrina que `looksLikeClassList` já escreveu.
|
|
283
|
+
*/
|
|
284
|
+
const CLASS_ATTR = /(?:^|[^A-Za-z])[A-Za-z]*[Cc]lassName=\{/g;
|
|
285
|
+
const CLASS_CALL = /\b(?:cn|clsx|classNames|cx|twMerge|twJoin|classcat|cva|tv)\(/g;
|
|
286
|
+
export function classSpans(source) {
|
|
287
|
+
const spans = [];
|
|
288
|
+
for (const re of [CLASS_ATTR, CLASS_CALL]) {
|
|
289
|
+
re.lastIndex = 0;
|
|
290
|
+
for (const m of source.matchAll(re)) {
|
|
291
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
292
|
+
const end = endOf(source, open);
|
|
293
|
+
if (end !== -1)
|
|
294
|
+
spans.push([open, end]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return spans;
|
|
298
|
+
}
|
|
299
|
+
function inSpan(spans, index) {
|
|
300
|
+
return spans.some(([open, end]) => index > open && index < end);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* O RECORD É CONSUMIDO COMO CLASSE? - direto (`styles[size]` dentro de um contexto de classe) ou
|
|
304
|
+
* com um pulo (`const config = statusConfig[status]` e depois `config.dotClass` num `cn()`),
|
|
305
|
+
* inclusive desestruturado (`const { fontSize } = sizeConfig[size]`).
|
|
306
|
+
*
|
|
307
|
+
* Um pulo só, de propósito: seguir a cadeia inteira seria reimplementar um resolvedor de escopo, e
|
|
308
|
+
* os 8 de 8 casos medidos no repo real (dono, 06/08) cabem em um.
|
|
309
|
+
*/
|
|
310
|
+
function consumedAsClass(source, spans, symbol) {
|
|
311
|
+
for (const m of source.matchAll(new RegExp(`\\b${symbol}\\s*\\[`, "g"))) {
|
|
312
|
+
const at = m.index ?? 0;
|
|
313
|
+
if (inSpan(spans, at))
|
|
314
|
+
return true;
|
|
315
|
+
const before = source.slice(Math.max(0, at - 120), at);
|
|
316
|
+
const alias = /([A-Za-z_$][\w$]*)\s*=\s*$/.exec(before)?.[1];
|
|
317
|
+
const destructured = /\{([^{}]*)\}\s*=\s*$/.exec(before)?.[1];
|
|
318
|
+
const names = destructured
|
|
319
|
+
? destructured.split(",").map((s) => s.split(":")[0].trim())
|
|
320
|
+
: alias
|
|
321
|
+
? [alias]
|
|
322
|
+
: [];
|
|
323
|
+
for (const name of names) {
|
|
324
|
+
if (!name || !/^[A-Za-z_$][\w$]*$/.test(name))
|
|
325
|
+
continue;
|
|
326
|
+
for (const use of source.matchAll(new RegExp(`\\b${name}\\b`, "g"))) {
|
|
327
|
+
if (inSpan(spans, use.index ?? 0))
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
273
334
|
/**
|
|
274
335
|
* Line and block comments removed, quotes respected.
|
|
275
336
|
*
|
|
@@ -600,8 +661,15 @@ function enclosingCallClasses(source, at) {
|
|
|
600
661
|
}
|
|
601
662
|
export function readInlineConditions(source) {
|
|
602
663
|
const layers = [];
|
|
664
|
+
const spans = classSpans(source);
|
|
603
665
|
INLINE_COND.lastIndex = 0;
|
|
604
666
|
for (const m of source.matchAll(INLINE_COND)) {
|
|
667
|
+
/**
|
|
668
|
+
* `cond && "string"` fora de um contexto de classe é quase sempre CONTEÚDO de JSX
|
|
669
|
+
* (`{loading && "Finding..."}`), e entrava como classe - o mesmo portão dos ternários.
|
|
670
|
+
*/
|
|
671
|
+
if (!inSpan(spans, m.index ?? 0) && !looksLikeClassList(m[5] ?? m[9] ?? ""))
|
|
672
|
+
continue;
|
|
605
673
|
const negated = (m[1] ?? m[6]) === "!";
|
|
606
674
|
const prop = (m[2] ?? m[7] ?? "").split(".").pop() ?? "";
|
|
607
675
|
const raw = m[5] ?? m[9] ?? "";
|
|
@@ -824,6 +892,7 @@ unattributed) {
|
|
|
824
892
|
axisOf.set(m[1], m[2]);
|
|
825
893
|
}
|
|
826
894
|
const layers = [];
|
|
895
|
+
const spans = classSpans(clean);
|
|
827
896
|
LOOKUP_RECORD.lastIndex = 0;
|
|
828
897
|
for (const m of clean.matchAll(LOOKUP_RECORD)) {
|
|
829
898
|
const symbol = m[1];
|
|
@@ -840,6 +909,16 @@ unattributed) {
|
|
|
840
909
|
const options = entries.filter((e) => literalClasses(e.value).length > 0);
|
|
841
910
|
if (options.length < 2)
|
|
842
911
|
continue;
|
|
912
|
+
/**
|
|
913
|
+
* SÓ É ESTILO O QUE UM CONTEXTO DE CLASSE CONSOME - ou o que tem a forma inequívoca de uma
|
|
914
|
+
* lista de classes. Um record indexado é a forma de MUITAS coisas: `TIER_LABELS[tier]` guarda
|
|
915
|
+
* conteúdo, `AURORA_PALETTES[palette]` guarda um gradiente que vai para `style=`, e ambos
|
|
916
|
+
* entravam aqui como classe - 531 de 1119 entradas do censo real eram isso (25/08). O consumo
|
|
917
|
+
* era a única prova, e a mesma regra já valia para os campos de uma tabela por elemento.
|
|
918
|
+
*/
|
|
919
|
+
if (!consumedAsClass(clean, spans, symbol) &&
|
|
920
|
+
!options.some((o) => looksLikeClassList(o.value)))
|
|
921
|
+
continue;
|
|
843
922
|
if (axesOut) {
|
|
844
923
|
const names = options.map((o) => o.key);
|
|
845
924
|
axesOut[axis] = [...new Set([...(axesOut[axis] ?? []), ...names])];
|
|
@@ -916,8 +995,26 @@ unattributed) {
|
|
|
916
995
|
const CLASS_TERNARY = /([!A-Za-z_$][\w$.!]*)\s*(?:===\s*["']([^"']+)["']\s*)?\?\s*["']([^"']*)["']\s*:\s*["']([^"']*)["']/g;
|
|
917
996
|
export function readClassTernaries(source) {
|
|
918
997
|
const layers = [];
|
|
998
|
+
const clean = stripComments(source);
|
|
999
|
+
const spans = classSpans(clean);
|
|
919
1000
|
CLASS_TERNARY.lastIndex = 0;
|
|
920
|
-
for (const m of
|
|
1001
|
+
for (const m of clean.matchAll(CLASS_TERNARY)) {
|
|
1002
|
+
/**
|
|
1003
|
+
* UM TERNÁRIO ENTRE DUAS STRINGS É A FORMA DE QUALQUER COISA - `loading ? "Finding..." :
|
|
1004
|
+
* "Find"` é o rótulo de um botão, `large ? "h1" : "h2"` é uma tag. Fora de um contexto de
|
|
1005
|
+
* classe, só a forma inequívoca de lista de classes prova estilo; dentro, o consumo prova.
|
|
1006
|
+
* Sem este portão, os dois exemplos acima entravam no censo como classe (25/08).
|
|
1007
|
+
*/
|
|
1008
|
+
const at = m.index ?? 0;
|
|
1009
|
+
if (!inSpan(spans, at)) {
|
|
1010
|
+
const before = clean.slice(Math.max(0, at - 120), at);
|
|
1011
|
+
const alias = /([A-Za-z_$][\w$]*)\s*=\s*$/.exec(before)?.[1];
|
|
1012
|
+
const aliased = alias &&
|
|
1013
|
+
/^[A-Za-z_$][\w$]*$/.test(alias) &&
|
|
1014
|
+
[...clean.matchAll(new RegExp(`\\b${alias}\\b`, "g"))].some((u) => inSpan(spans, u.index ?? 0));
|
|
1015
|
+
if (!aliased && !looksLikeClassList(m[3]) && !looksLikeClassList(m[4]))
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
921
1018
|
const negated = m[1].startsWith("!");
|
|
922
1019
|
const prop = m[1].replace(/^!/, "").split(".").pop() ?? "";
|
|
923
1020
|
if (!prop)
|
|
@@ -941,6 +1038,100 @@ export function readClassTernaries(source) {
|
|
|
941
1038
|
}
|
|
942
1039
|
return layers;
|
|
943
1040
|
}
|
|
1041
|
+
/** O valor quando ele é UMA string literal inteira - `null` para objeto, template com `${}`, etc. */
|
|
1042
|
+
function singleStringValue(value) {
|
|
1043
|
+
const trimmed = value.trim();
|
|
1044
|
+
if (!/^["'`]/.test(trimmed))
|
|
1045
|
+
return null;
|
|
1046
|
+
const closed = endOfString(trimmed);
|
|
1047
|
+
if (closed === -1 || trimmed.slice(closed).trim())
|
|
1048
|
+
return null;
|
|
1049
|
+
const inner = trimmed.slice(1, closed - 1);
|
|
1050
|
+
if (trimmed[0] === "`" && inner.includes("${"))
|
|
1051
|
+
return null;
|
|
1052
|
+
return inner;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* UM RECORD CONSUMIDO POR `style=` É ESTILO POR PROVA, NÃO POR FORMA - e o valor viaja INTEIRO.
|
|
1056
|
+
*
|
|
1057
|
+
* const AURORA_PALETTES: Record<AuroraPalette, string> = {
|
|
1058
|
+
* default: "linear-gradient(var(--aurora-angle,125deg),rgba(79,70,229,0.18) 0%,…)",
|
|
1059
|
+
* cool: "linear-gradient(…)",
|
|
1060
|
+
* };
|
|
1061
|
+
* style={{ backgroundImage: AURORA_PALETTES[palette], … }} // ou style={mergedStyle}
|
|
1062
|
+
*
|
|
1063
|
+
* O leitor de classes partia esse gradiente no espaço e o censo carregava `0%,rgba(192,132,252,0.18)`
|
|
1064
|
+
* como se fosse classe - 46 valores num censo real (25/08). A propriedade é a que ELE escreveu no
|
|
1065
|
+
* objeto de style (`backgroundImage`), o valor é o que ELE declarou no record, e o eixo é a prop que
|
|
1066
|
+
* indexa - transcrever não é inventar. Um nível de indireção é seguido (`const mergedStyle = {…}` e
|
|
1067
|
+
* `style={mergedStyle}`), que é como o componente real escreve.
|
|
1068
|
+
*/
|
|
1069
|
+
export function readStyleRecords(source, axesOut) {
|
|
1070
|
+
const clean = stripComments(source);
|
|
1071
|
+
const axisOf = new Map();
|
|
1072
|
+
RECORD_INDEX.lastIndex = 0;
|
|
1073
|
+
for (const m of clean.matchAll(RECORD_INDEX)) {
|
|
1074
|
+
if (!axisOf.has(m[1]))
|
|
1075
|
+
axisOf.set(m[1], m[2]);
|
|
1076
|
+
}
|
|
1077
|
+
if (axisOf.size === 0)
|
|
1078
|
+
return [];
|
|
1079
|
+
// symbol → option → o valor inteiro, para os records cujos valores são strings.
|
|
1080
|
+
const records = new Map();
|
|
1081
|
+
LOOKUP_RECORD.lastIndex = 0;
|
|
1082
|
+
for (const m of clean.matchAll(LOOKUP_RECORD)) {
|
|
1083
|
+
if (!axisOf.has(m[1]))
|
|
1084
|
+
continue;
|
|
1085
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
1086
|
+
const end = endOf(clean, open);
|
|
1087
|
+
if (end === -1)
|
|
1088
|
+
continue;
|
|
1089
|
+
const options = topLevelEntries(clean.slice(open + 1, end - 1))
|
|
1090
|
+
.map((e) => ({ key: e.key, value: singleStringValue(e.value) }))
|
|
1091
|
+
.filter((e) => e.value != null);
|
|
1092
|
+
if (options.length >= 2)
|
|
1093
|
+
records.set(m[1], options);
|
|
1094
|
+
}
|
|
1095
|
+
if (records.size === 0)
|
|
1096
|
+
return [];
|
|
1097
|
+
// Os corpos que ALIMENTAM um style=: o objeto inline, e o const que um style={nome} aponta.
|
|
1098
|
+
const bodies = [];
|
|
1099
|
+
for (const m of clean.matchAll(/style=\{\{/g)) {
|
|
1100
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
1101
|
+
const end = endOf(clean, open);
|
|
1102
|
+
if (end !== -1)
|
|
1103
|
+
bodies.push(clean.slice(open + 1, end - 1));
|
|
1104
|
+
}
|
|
1105
|
+
for (const m of clean.matchAll(/style=\{\s*([A-Za-z_$][\w$]*)\s*\}/g)) {
|
|
1106
|
+
const named = new RegExp(`(?:const|let)\\s+${m[1]}\\b[^=]*=\\s*\\{`, "g").exec(clean);
|
|
1107
|
+
if (!named)
|
|
1108
|
+
continue;
|
|
1109
|
+
const open = named.index + named[0].length - 1;
|
|
1110
|
+
const end = endOf(clean, open);
|
|
1111
|
+
if (end !== -1)
|
|
1112
|
+
bodies.push(clean.slice(open + 1, end - 1));
|
|
1113
|
+
}
|
|
1114
|
+
const layers = [];
|
|
1115
|
+
for (const body of bodies) {
|
|
1116
|
+
for (const m of body.matchAll(/([A-Za-z_$][\w$]*)\s*:\s*([A-Za-z_$][\w$]*)\s*\[/g)) {
|
|
1117
|
+
const property = m[1];
|
|
1118
|
+
const options = records.get(m[2]);
|
|
1119
|
+
const axis = axisOf.get(m[2]);
|
|
1120
|
+
if (!options || !axis)
|
|
1121
|
+
continue;
|
|
1122
|
+
if (axesOut)
|
|
1123
|
+
axesOut[axis] = [
|
|
1124
|
+
...new Set([...(axesOut[axis] ?? []), ...options.map((o) => o.key)]),
|
|
1125
|
+
];
|
|
1126
|
+
for (const option of options)
|
|
1127
|
+
layers.push({
|
|
1128
|
+
when: { variant: { [axis]: option.key } },
|
|
1129
|
+
style: { [property]: option.value },
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return layers;
|
|
1134
|
+
}
|
|
944
1135
|
/**
|
|
945
1136
|
* A LOOKUP INDEXED BY A TERNARY, which is how the same record serves a boolean.
|
|
946
1137
|
*
|
|
@@ -977,6 +1168,12 @@ globals) {
|
|
|
977
1168
|
...readLookupRecords(source, fromRecords, unslotted),
|
|
978
1169
|
...readClassTernaries(source),
|
|
979
1170
|
];
|
|
1171
|
+
/**
|
|
1172
|
+
* O record que um `style=` consome - valores prontos, sem tabela de escala no meio.
|
|
1173
|
+
* Entram direto nas camadas finais (e registram o eixo), nunca em `raw`: `raw` é
|
|
1174
|
+
* matéria-prima de reinterpretação por classes, e estes já são o valor final.
|
|
1175
|
+
*/
|
|
1176
|
+
const styleRecords = readStyleRecords(source, fromRecords);
|
|
980
1177
|
const axes = {};
|
|
981
1178
|
const defaults = {};
|
|
982
1179
|
const allLayers = [];
|
|
@@ -1087,7 +1284,7 @@ globals) {
|
|
|
1087
1284
|
reads,
|
|
1088
1285
|
axes,
|
|
1089
1286
|
defaults,
|
|
1090
|
-
layers,
|
|
1287
|
+
layers: [...layers, ...styleRecords],
|
|
1091
1288
|
raw: allLayers,
|
|
1092
1289
|
base: withGlobal(transcribe(baseClasses, declared), baseClasses, globals),
|
|
1093
1290
|
unslotted,
|
package/dist/install-marks.js
CHANGED
|
@@ -128,7 +128,16 @@
|
|
|
128
128
|
* é sempre o bump deste PR - nunca o número que o `package.json` já carrega, porque alguém pode
|
|
129
129
|
* publicar no meio.
|
|
130
130
|
*/
|
|
131
|
-
|
|
131
|
+
/**
|
|
132
|
+
* 0.16.305 -> 0.16.306 em 25/08, e o SIM é sobre a FIAÇÃO que o `upgrade` regrava: o hook de
|
|
133
|
+
* `SessionStart` passa a ser pinado (`npx synthesisui@<versão> align`) em vez de `@latest`. O
|
|
134
|
+
* `@latest` nunca cacheia - round-trip no registro em TODA abertura de sessão, 2,92s medidos, contra
|
|
135
|
+
* 0,32s da versão fixada - e o trabalho do align em si custa 50ms. Um `upgrade` anterior deixa o
|
|
136
|
+
* `.claude/settings.json` pagando 2,9s por sessão; quem roda o upgrade novo recebe bytes diferentes
|
|
137
|
+
* ali, que é exatamente o que esta marca existe para dizer. Quem avisa versão nova passa a ser o
|
|
138
|
+
* próprio align, com o `cli` que o servidor manda no `?meta=1`.
|
|
139
|
+
*/
|
|
140
|
+
export const MATERIALISER_SINCE = "0.16.306";
|
|
132
141
|
/**
|
|
133
142
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
134
143
|
*
|
|
@@ -264,7 +273,22 @@ export const CHECKER_SINCE = "0.16.250";
|
|
|
264
273
|
*
|
|
265
274
|
* É exatamente o que esta marca existe para dizer: o `align` avisa que vale remedir.
|
|
266
275
|
*/
|
|
267
|
-
|
|
276
|
+
/**
|
|
277
|
+
* 0.16.305 -> 0.16.306 em 25/08: três leituras que devolvem estilo do cliente, medidas antes de
|
|
278
|
+
* escrever (itens 8, 9 e 10 do diagnóstico de 25/08):
|
|
279
|
+
*
|
|
280
|
+
* 1. O `!` de important deixa de matar o utilitário: `!text-ink-900` com `--color-ink-900`
|
|
281
|
+
* declarado agora resolve (48 valores no censo real; e o ledger passou a CONTAR um className
|
|
282
|
+
* de important, que antes nem entrava no denominador).
|
|
283
|
+
* 2. Um record consumido por `style=` viaja como o VALOR que ele é: o gradiente do Aurora chega
|
|
284
|
+
* inteiro em `layers`, em vez de partido em `0%,rgba(…)` como classe (46 valores).
|
|
285
|
+
* 3. Só é classe o que um contexto de classe consome: literais de ternário e de record de
|
|
286
|
+
* conteúdo (`Finding...`, `h1`, `✅`) saem de `rawLayers.classes` - eram 531 de 1119 entradas
|
|
287
|
+
* (47%) no censo real, poluindo numerador e denominador de toda métrica em cima.
|
|
288
|
+
*
|
|
289
|
+
* Um censo medido antes carrega a poluição e a perda; quem remede ganha os três de uma vez.
|
|
290
|
+
*/
|
|
291
|
+
export const READER_SINCE = "0.16.306";
|
|
268
292
|
/**
|
|
269
293
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
270
294
|
*
|
package/package.json
CHANGED