synthesisui 0.16.231 → 0.16.233

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.
@@ -1,5 +1,5 @@
1
1
  import { deltaE, JND } from "./doctor/color-distance.js";
2
- import { nearestToken, normalizeValue, } from "./doctor/tokens.js";
2
+ import { familySays, nearestToken, normalizeValue, } from "./doctor/tokens.js";
3
3
  /** Onde cada tipo de valor mora na fundação. `color` é o único com dois segmentos. */
4
4
  const HOME = {
5
5
  color: "color",
@@ -18,23 +18,18 @@ const clean = (name) => name.replace(/^--/, "").toLowerCase();
18
18
  * para um achado de espaçamento, e o caminho que sai daí é `typography.h2` sobre uma margem - o mesmo
19
19
  * erro de categoria que o `crossFamily` foi criado para impedir do outro lado da esteira.
20
20
  *
21
- * A primeira palavra do nome dele é a pista, e é a mesma lista que o `CATEGORY` acima já usa. Quando
22
- * nenhum candidato concorda com a natureza do achado, COR ainda passa - um hex numa posição de cor
23
- * não tem ambiguidade de família - e comprimento não passa: sem concordância não há o que separasse
24
- * um token de tipo de um de espaçamento, e o valor volta sem caminho para a pessoa decidir.
21
+ * A pista são as palavras de família do nome dele - `familySays`, a MESMA tabela que o mapa de
22
+ * vocabulário usa para recusar uma sugestão de categoria trocada. Duas listas para a mesma pergunta é
23
+ * como duas telas passam a discordar sobre o mesmo `12px`.
24
+ *
25
+ * Quando nenhum candidato concorda com a natureza do achado, COR ainda passa - um hex numa posição de
26
+ * cor não tem ambiguidade de família - e comprimento não passa: sem concordância não há o que
27
+ * separasse um token de tipo de um de espaçamento, e o valor volta sem caminho para a pessoa decidir.
25
28
  */
26
- const KIND_WORDS = {
27
- color: ["color", "colour"],
28
- radius: ["radius", "rounded"],
29
- spacing: ["spacing", "space", "gap"],
30
- font: ["font", "type", "text"],
31
- motion: ["duration", "motion", "ease", "easing", "transition"],
32
- };
33
29
  function nameOf(kind, candidates) {
34
30
  if (!candidates || candidates.length === 0)
35
31
  return undefined;
36
- const head = (n) => clean(n).split("-")[0] ?? "";
37
- const agrees = candidates.find((n) => KIND_WORDS[kind].includes(head(n)));
32
+ const agrees = candidates.find((n) => familySays(kind, n));
38
33
  if (agrees)
39
34
  return agrees;
40
35
  return kind === "color" ? candidates[0] : undefined;
@@ -748,7 +748,7 @@ export async function doctor(opts) {
748
748
  const asideTotal = [...aside.values()].reduce((n, v) => n + v, 0);
749
749
  if (verbose) {
750
750
  for (const [reason, count] of aside) {
751
- console.log(body(`set aside: ${plural(count, "value")} in ${reason}`));
751
+ console.log(body(`out of the count: ${plural(count, "value")} in ${reason}`));
752
752
  }
753
753
  }
754
754
  else if (asideTotal > 0) {
@@ -1157,7 +1157,14 @@ export async function doctor(opts) {
1157
1157
  console.log(body("Add the name to the system, or use one it has. Do not leave it."));
1158
1158
  }
1159
1159
  if (d.findings.length > 0) {
1160
- say(section("Drift"));
1160
+ /**
1161
+ * "Drift" É A NOSSA PALAVRA para um valor fora do sistema, e era o TÍTULO de uma seção inteira.
1162
+ *
1163
+ * A rodada padrão perdeu o vocabulário nosso em 13/08 e o `--verbose` ficou - a guarda rodava
1164
+ * sobre a saída sem `--verbose`, que é o que a maioria lê. O que a seção lista é literal escrito
1165
+ * à mão, e é isso que o título passa a dizer.
1166
+ */
1167
+ say(section("Values written by hand"));
1161
1168
  const order = ["color", "radius", "spacing", "font"].filter((k) => d.counts[k] > 0);
1162
1169
  for (const kind of order) {
1163
1170
  say(body(`${d.counts[kind]} ${KIND_LABEL[kind]}`));
@@ -1,3 +1,4 @@
1
+ import { PHRASING_FORMS, } from "./types.js";
1
2
  export const DEFAULT_CONVENTION = {
2
3
  prefix: "ds-",
3
4
  partSeparator: "-",
@@ -106,11 +107,22 @@ function elementFor(name, recipe) {
106
107
  * UI and a hand-written disclosure, because it reads the SHAPE.
107
108
  */
108
109
  if (tag === "button") {
109
- const holdsBlock = (nodes) => nodes.some((n) => n.as === "slot" ||
110
- n.as === "button" ||
111
- n.as === "field" ||
112
- n.as === "component" ||
113
- n.as === "external" ||
110
+ /**
111
+ * A LISTA SAI DE `PHRASING_FORMS`, e a enumeração à mão estava incompleta - conserto de 14/08.
112
+ *
113
+ * Ela esquecia `heading`, `row` e `stack`, e o custo é a marcação que a lei nomeia logo acima: uma
114
+ * raiz `action` com um heading no topo saía como `<button><h3>…</h3></button>`. Reproduzido pelo
115
+ * próprio codegen:
116
+ *
117
+ * preview.kind = "action", parts = [{ as: "heading" }] -> ComponentProps<"button">
118
+ *
119
+ * E o ramo abaixo já sabia que heading importa - ele devolve `article` quando o topo tem um -,
120
+ * então era código morto: o gate nunca deixava chegar até ele.
121
+ *
122
+ * O complemento de phrasing é a pergunta certa, e não uma lista de formas de bloco: são três nomes
123
+ * de um lado contra oito do outro, e a lista curta é a que não esquece um.
124
+ */
125
+ const holdsBlock = (nodes) => nodes.some((n) => !PHRASING_FORMS.includes(n.as) ||
114
126
  (n.children != null && n.children.length > 0));
115
127
  const tree = recipe.preview?.parts;
116
128
  if (tree && tree.length > 0 && holdsBlock(tree)) {
@@ -1,4 +1,4 @@
1
- import { DS_FAMILY, normalizeValue } from "./tokens.js";
1
+ import { DS_FAMILY, familySays, normalizeValue, } from "./tokens.js";
2
2
  /**
3
3
  * COMO ELE CHAMA O VALOR - para a gente parar de renomear as variáveis dele.
4
4
  *
@@ -127,7 +127,28 @@ export function theirNames(ours, theirs) {
127
127
  const candidates = theirs.byValue.get(value);
128
128
  if (!candidates || candidates.length === 0)
129
129
  continue;
130
- out.set(key, pick(candidates, name, convention));
130
+ /**
131
+ * E A CATEGORIA DO NOME DELE TEM QUE FECHAR - senão o nosso nome fica, e ele está certo.
132
+ *
133
+ * O NOSSO prefixo carrega a família por construção; o dele não carrega nada garantido. Quando a
134
+ * FORMA do valor já decide (hex, `ms`, pilha de fontes) não há o que proteger e o nome dele
135
+ * entra. Quando o valor é um comprimento cru, `12px` pode ser `--text-caption` ou `--spacing-sm`
136
+ * no vocabulário dele, e escolher errado escreve um token de tipo num `border-radius`.
137
+ *
138
+ * Medido no repositório real em 14/08, antes desta linha: 290 sugestões com a categoria trocada,
139
+ * 66 delas gravadas por um `--fix --write`, e a pior na PRIMEIRA página do relatório -
140
+ * `0.25em → --radius-xs` em 116 arquivos. Ver `FAMILY_WORDS`.
141
+ *
142
+ * Recusar aqui não perde informação: sem alias, `nameToWrite` cai no NOSSO token, que é da
143
+ * família certa porque foi o prefixo dela que o trouxe a este laço.
144
+ */
145
+ const formDecides = Object.values(UNAMBIGUOUS).some((f) => f.test(value));
146
+ const usable = formDecides
147
+ ? candidates
148
+ : candidates.filter((n) => familySays(kind, n));
149
+ if (usable.length === 0)
150
+ continue;
151
+ out.set(key, pick(usable, name, convention));
131
152
  }
132
153
  }
133
154
  for (const [value, candidates] of theirs.byValue) {
@@ -513,6 +513,47 @@ export const DS_FAMILY = {
513
513
  font: "--ds-typography-",
514
514
  motion: "--ds-motion-",
515
515
  };
516
+ /**
517
+ * AS PALAVRAS COM QUE O MUNDO NOMEIA CADA FAMÍLIA - e é isto que impede um erro de categoria.
518
+ *
519
+ * O NOSSO prefixo carrega a família (`--ds-spacing-md` é espaçamento por construção). O DELE não
520
+ * carrega nada garantido, e nada verificava: `--text-caption` e `--spacing-sm` podem segurar o mesmo
521
+ * `12px`, e sugerir o primeiro para um `border-radius` é escrever um token de tipo numa posição de
522
+ * raio. É exatamente o erro que o `crossFamily` foi criado para impedir do NOSSO lado, e ele passou
523
+ * a acontecer pelo lado dele quando o mapa de vocabulário nasceu.
524
+ *
525
+ * Medido no repositório real em 14/08, antes desta trava:
526
+ *
527
+ * 2680 achados com nome dele
528
+ * 1635 categoria concorda
529
+ * 290 categoria DISCORDA <- sugestão errada na tela
530
+ * 66 dessas o `--fix --write` GRAVARIA (o resto é `em`, que já tem trava)
531
+ * 755 nome sem palavra de categoria - e são todos cor (748) e fonte (7)
532
+ *
533
+ * A pior estava na primeira página: `0.25em → --radius-xs` em 116 arquivos, na lista das mais
534
+ * repetidas. E `4px → --radius-xs` num padding é uma escrita silenciosa, sem `em` para barrá-la.
535
+ *
536
+ * `text` está na lista de tipo porque `--text-h2` é a forma dominante no mundo real, e `transition`
537
+ * na de motion pelo mesmo motivo.
538
+ */
539
+ export const FAMILY_WORDS = {
540
+ color: ["color", "colour"],
541
+ radius: ["radius", "rounded", "corner"],
542
+ spacing: ["spacing", "space", "gap", "inset"],
543
+ font: ["font", "type", "text"],
544
+ motion: ["duration", "motion", "ease", "easing", "transition"],
545
+ };
546
+ /**
547
+ * O NOME DELE DIZ QUE É DESTA FAMÍLIA - por QUALQUER segmento, não só pelo primeiro.
548
+ *
549
+ * `--dashboard-radius-lg` é um raio e o primeiro segmento é o namespace dele. Olhar só o começo
550
+ * recusaria um nome certo por causa de um prefixo de produto, que é a metade oposta do mesmo erro.
551
+ */
552
+ export const familySays = (kind, name) => {
553
+ const words = FAMILY_WORDS[kind] ?? [];
554
+ const segments = name.replace(/^--/, "").toLowerCase().split("-");
555
+ return segments.some((seg) => words.includes(seg));
556
+ };
516
557
  /**
517
558
  * O token que carrega este valor, e SE ELE É DA MESMA FAMÍLIA.
518
559
  *
@@ -71,7 +71,16 @@
71
71
  * a prop, e 104 das 207 condições sem expressão nenhuma. Um `upgrade` anterior a esta versão reescreve
72
72
  * os componentes dele com a perda intacta.
73
73
  */
74
- export const MATERIALISER_SINCE = "0.16.220";
74
+ /**
75
+ * 0.16.220 -> 0.16.233 em 14/08: o componente materializado muda de bytes. A regra de contenção HTML
76
+ * era enumerada à mão em `elementFor` e a enumeração esquecia `heading`, `row` e `stack` - uma raiz de
77
+ * papel `action` com um heading no topo saía como `<button><h3>…</h3></button>`, que é marcação
78
+ * inválida embarcada num copy-paste. Agora sai `<article>`.
79
+ *
80
+ * Zero ocorrências no sistema real medido (todos os headings dele estão aninhados, e o `children > 0`
81
+ * já os pegava), então para ele o `upgrade` é no-op. A marca é sobre o caso geral.
82
+ */
83
+ export const MATERIALISER_SINCE = "0.16.233";
75
84
  /**
76
85
  * A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
77
86
  *
@@ -123,7 +132,14 @@ export const MATERIALISER_SINCE = "0.16.220";
123
132
  * e diz que o sistema não nomeia a fonte que o css dela nomeia. Medido no repositório real: as trocas
124
133
  * com nome disponível foram de 1624 para 1631.
125
134
  */
126
- export const CHECKER_SINCE = "0.16.231";
135
+ /**
136
+ * 0.16.231 -> 0.16.232 em 14/08, e este é o pior que esta marca já carregou: o pinado não erra por
137
+ * omissão, ele erra o CONSELHO. O mapa de vocabulário escolhia entre os nomes DELE sem conferir a
138
+ * categoria, então um hook pinado antes disto olha um `border-radius: 12px` recém-escrito e manda usar
139
+ * `var(--text-caption)` - um token de tipo numa posição de raio. Medido no repositório real: 290
140
+ * sugestões com a categoria trocada, 66 delas graváveis por um `--fix --write`.
141
+ */
142
+ export const CHECKER_SINCE = "0.16.232";
127
143
  /**
128
144
  * A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
129
145
  *
package/dist/types.js CHANGED
@@ -3,4 +3,13 @@
3
3
  * import `@synthesisui-hub/ds-contracts` (it only consumes the endpoint JSON).
4
4
  * We type only what the CLI reads to generate GUIDE.md and the .lock.
5
5
  */
6
- export {};
6
+ /**
7
+ * AS TRÊS FORMAS QUE SÃO PHRASING CONTENT - e é a lista CURTA de propósito.
8
+ *
9
+ * `<button><Upload/>Upload</button>` é HTML válido: o modelo de conteúdo de um botão é phrasing, e um
10
+ * glifo e uma corrida de palavras são phrasing. Tudo o mais é bloco, e perguntar pelo complemento é o
11
+ * que impede a lista de esquecer um nome - foi assim que `heading`, `row` e `stack` ficaram fora da
12
+ * enumeração à mão do `elementFor` até 14/08, e uma raiz `action` com heading no topo saía como
13
+ * `<button><h3>…</h3></button>`.
14
+ */
15
+ export const PHRASING_FORMS = ["icon", "text", "image"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.231",
3
+ "version": "0.16.233",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {