synthesisui 0.16.147 → 0.16.148
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/commands/doctor.js +10 -4
- package/dist/doctor/apply-fix.js +20 -0
- package/dist/doctor/scan.js +13 -4
- package/dist/doctor/tokens.js +24 -5
- package/dist/index.js +7 -3
- package/dist/skill-init.js +25 -12
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -1271,12 +1271,18 @@ export async function doctor(opts) {
|
|
|
1271
1271
|
if (opts.fix) {
|
|
1272
1272
|
const read = await readerFor(root, d.findings.filter((f) => f.token).map((f) => f.file));
|
|
1273
1273
|
const { result, next } = planFix(d, read);
|
|
1274
|
-
|
|
1274
|
+
const writing = opts.write === true;
|
|
1275
|
+
if (writing)
|
|
1275
1276
|
await writeFix(root, next);
|
|
1276
|
-
console.log(section(
|
|
1277
|
-
for (const line of describeFix(result,
|
|
1277
|
+
console.log(section(writing ? "Fixed what had an answer" : "What --fix would do"));
|
|
1278
|
+
for (const line of describeFix(result, !writing))
|
|
1278
1279
|
console.log(body(line));
|
|
1279
|
-
if (!
|
|
1280
|
+
if (!writing && result.applied.length > 0) {
|
|
1281
|
+
console.log("");
|
|
1282
|
+
console.log(body("Nothing was written. When the list above is what you want:"));
|
|
1283
|
+
console.log(paint.blue(snippet(["npx synthesisui@latest doctor --fix --write"])));
|
|
1284
|
+
}
|
|
1285
|
+
if (writing && result.applied.length > 0) {
|
|
1280
1286
|
console.log("");
|
|
1281
1287
|
console.log(body(paint.dim("Read the diff before committing - it is your code, and this replaced literals with the tokens your own system declares.")));
|
|
1282
1288
|
}
|
package/dist/doctor/apply-fix.js
CHANGED
|
@@ -65,6 +65,23 @@ export function planFix(d, read) {
|
|
|
65
65
|
});
|
|
66
66
|
continue;
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* COINCIDÊNCIA NÃO SE APLICA - ver `crossFamily` em `scan.ts`.
|
|
70
|
+
*
|
|
71
|
+
* Um `2rem` que só existe como token de tipografia é uma resposta para um `font-size` e um erro
|
|
72
|
+
* de categoria para um `border-radius`. O `--fix` trocou `rounded-[2rem]` por
|
|
73
|
+
* `var(--ds-typography-scale-h1-font-size)` no repo do dono (05/08) - um canto que se move no dia
|
|
74
|
+
* em que alguém edita a escala de tipo. Medido lá: 14 de 129, e 0 de 76 na biblioteca dele.
|
|
75
|
+
*/
|
|
76
|
+
if (f.crossFamily) {
|
|
77
|
+
skipped.push({
|
|
78
|
+
file: f.file,
|
|
79
|
+
line: f.line,
|
|
80
|
+
literal: f.literal,
|
|
81
|
+
because: "cross-family",
|
|
82
|
+
});
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
68
85
|
if (!lines.has(f.file)) {
|
|
69
86
|
const raw = read(f.file);
|
|
70
87
|
if (raw === null) {
|
|
@@ -134,6 +151,7 @@ export async function readerFor(root, files) {
|
|
|
134
151
|
export function describeFix(result, dry) {
|
|
135
152
|
const { applied, skipped } = result;
|
|
136
153
|
const moved = skipped.filter((s) => s.because === "moved").length;
|
|
154
|
+
const coincidence = skipped.filter((s) => s.because === "cross-family").length;
|
|
137
155
|
const unread = skipped.filter((s) => s.because === "unreadable").length;
|
|
138
156
|
const decisions = skipped.filter((s) => s.because === "no-token").length;
|
|
139
157
|
const lines = [];
|
|
@@ -153,6 +171,8 @@ export function describeFix(result, dry) {
|
|
|
153
171
|
lines.push(` var(${token}) · ${n} time${n === 1 ? "" : "s"}`);
|
|
154
172
|
if (moved > 0)
|
|
155
173
|
lines.push(`${moved} line${moved === 1 ? "" : "s"} changed since the scan and ${moved === 1 ? "was" : "were"} left alone - run it again.`);
|
|
174
|
+
if (coincidence > 0)
|
|
175
|
+
lines.push(`${coincidence} left alone because the only token with that value belongs to another family - a length that happens to equal a font size is a coincidence, not an answer.`);
|
|
156
176
|
if (unread > 0)
|
|
157
177
|
lines.push(`${unread} file${unread === 1 ? "" : "s"} could not be read.`);
|
|
158
178
|
if (decisions > 0)
|
package/dist/doctor/scan.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Text in, findings out. No filesystem, no AST, no network - a diagnosis that
|
|
10
10
|
* takes eight seconds and needs a build step is a diagnosis nobody runs.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { tokenMatch } from "./tokens.js";
|
|
13
13
|
/**
|
|
14
14
|
* `next/og` renders JSX to a PNG on the server. There is no document, so there
|
|
15
15
|
* is no `var(--ds-*)` to read: every colour in such a file MUST be a literal.
|
|
@@ -335,13 +335,22 @@ function scanCore(file, source, table) {
|
|
|
335
335
|
if (seen.has(key))
|
|
336
336
|
return;
|
|
337
337
|
seen.add(key);
|
|
338
|
+
// The kind is already known here, and without passing it the lookup
|
|
339
|
+
// answers a `gap` with a radius token.
|
|
340
|
+
const match = tokenMatch(table, literal, kind);
|
|
338
341
|
findings.push({
|
|
339
342
|
kind,
|
|
340
343
|
line: at,
|
|
341
344
|
literal,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
+
token: match?.token ?? null,
|
|
346
|
+
/**
|
|
347
|
+
* A COINCIDÊNCIA VIAJA COM O ACHADO - ver `tokenMatch`.
|
|
348
|
+
*
|
|
349
|
+
* Sem esta marca, quem só tem `token: string` não consegue distinguir "o seu sistema nomeia
|
|
350
|
+
* este raio" de "existe um token de TIPOGRAFIA com o mesmo 2rem". A primeira é uma troca de
|
|
351
|
+
* uma linha; a segunda é um erro de categoria, e o `--fix` fez essa troca no repo do dono.
|
|
352
|
+
*/
|
|
353
|
+
...(match && !match.family ? { crossFamily: true } : {}),
|
|
345
354
|
excerpt: clip(line),
|
|
346
355
|
});
|
|
347
356
|
};
|
package/dist/doctor/tokens.js
CHANGED
|
@@ -445,17 +445,36 @@ const FAMILY = {
|
|
|
445
445
|
font: "--ds-typography-",
|
|
446
446
|
motion: "--ds-motion-",
|
|
447
447
|
};
|
|
448
|
-
|
|
448
|
+
/**
|
|
449
|
+
* O token que carrega este valor, e SE ELE É DA MESMA FAMÍLIA.
|
|
450
|
+
*
|
|
451
|
+
* A queda para o conjunto inteiro sempre existiu aqui, com um comentário admitindo que "deixa de ser
|
|
452
|
+
* recomendação". Como INFORMAÇÃO num relatório isso é honesto: um `2rem` que só existe como
|
|
453
|
+
* `--ds-typography-scale-h1-font-size` é um fato sobre o sistema deles.
|
|
454
|
+
*
|
|
455
|
+
* Como AÇÃO é um erro de categoria, e ele aconteceu: o `--fix` trocou `rounded-[2rem]` por
|
|
456
|
+
* `var(--ds-typography-scale-h1-font-size)` e `margin: 2rem` pela mesma coisa, no repo do dono
|
|
457
|
+
* (05/08). Um canto que se move no dia em que alguém edita a escala de tipo.
|
|
458
|
+
*
|
|
459
|
+
* Medido no `apps/web-dashboard` dele: 14 de 129 achados com token (11%) são coincidência entre
|
|
460
|
+
* famílias, e no `packages/ui` são 0 de 76 - então não é raro nem universal, é o tipo de coisa que
|
|
461
|
+
* só aparece num app grande.
|
|
462
|
+
*
|
|
463
|
+
* Então a função passa a dizer QUAL DOS DOIS é: quem informa usa os dois, quem escreve usa só
|
|
464
|
+
* `family`.
|
|
465
|
+
*/
|
|
466
|
+
export function tokenMatch(table, literal, kind) {
|
|
449
467
|
const hit = table.byValue.get(normalizeValue(literal));
|
|
450
468
|
if (!hit || hit.length === 0)
|
|
451
469
|
return null;
|
|
452
|
-
// Narrow to the family first, and only fall back to the whole set when the
|
|
453
|
-
// system has no token of that kind holding this value - a fallback is still
|
|
454
|
-
// better than silence, it just stops being a recommendation.
|
|
455
470
|
const prefix = kind ? FAMILY[kind] : undefined;
|
|
456
471
|
const family = prefix ? hit.filter((n) => n.startsWith(prefix)) : [];
|
|
457
472
|
const pool = family.length > 0 ? family : hit;
|
|
458
473
|
// Semantic roles name intent; primitives name a shelf. Prefer intent.
|
|
459
474
|
const semantic = pool.find((n) => n.includes("-semantic-"));
|
|
460
|
-
return semantic ?? pool[0];
|
|
475
|
+
return { token: semantic ?? pool[0], family: family.length > 0 };
|
|
476
|
+
}
|
|
477
|
+
/** O nome só, para quem já tratava um `string | null` - a forma antiga, preservada. */
|
|
478
|
+
export function tokenFor(table, literal, kind) {
|
|
479
|
+
return tokenMatch(table, literal, kind)?.token ?? null;
|
|
461
480
|
}
|
package/dist/index.js
CHANGED
|
@@ -60,8 +60,8 @@ Usage - governance (deterministic, FREE):
|
|
|
60
60
|
reusing the reading you already authored, plus the local
|
|
61
61
|
record - checks, fixes, open requests (--record-only skips
|
|
62
62
|
the measurement, --yes skips the overwrite question)
|
|
63
|
-
synthesisui doctor --fix [--
|
|
64
|
-
|
|
63
|
+
synthesisui doctor --fix [--write] show every hand-written value your system already names,
|
|
64
|
+
and the token for it. --write applies them in place
|
|
65
65
|
synthesisui status where this project stands: installed version, last check,
|
|
66
66
|
the ratchet, open requests - local first, no login needed
|
|
67
67
|
synthesisui ci [--write] the two steps that put drift in your PR - annotations on
|
|
@@ -273,7 +273,11 @@ async function main() {
|
|
|
273
273
|
: {}),
|
|
274
274
|
...(flags.baseline === true ? { compareBaseline: true } : {}),
|
|
275
275
|
...(flags.fix === true ? { fix: true } : {}),
|
|
276
|
-
|
|
276
|
+
/**
|
|
277
|
+
* `--fix` MOSTRA; `--fix --write` escreve. `--dry` continua aceito e é um no-op, porque um
|
|
278
|
+
* script escrito hoje não pode virar uma escrita amanhã por causa de uma flag que sumiu.
|
|
279
|
+
*/
|
|
280
|
+
...(flags.write === true ? { write: true } : {}),
|
|
277
281
|
...(flags["write-baseline"] === true ? { writeBaseline: true } : {}),
|
|
278
282
|
cli: CLI_VERSION,
|
|
279
283
|
});
|
package/dist/skill-init.js
CHANGED
|
@@ -84,26 +84,39 @@ Then wire it, once, with the command that is already idempotent:
|
|
|
84
84
|
npx synthesisui@latest connect
|
|
85
85
|
\`\`\`
|
|
86
86
|
|
|
87
|
-
## 2. AUTHORISATION - ONE QUESTION,
|
|
87
|
+
## 2. AUTHORISATION - ONE QUESTION, AND IT IS A SINGLE CHOICE
|
|
88
88
|
|
|
89
89
|
Ask it HERE, before the import, because it is the only way the end can be four lines instead of a
|
|
90
90
|
negotiation.
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
and
|
|
92
|
+
**ASK IT AS A SINGLE CHOICE, NEVER A MULTI-SELECT.** The first real run of this skill made the reason
|
|
93
|
+
obvious (dono, 05/08): a multi-select renders as three checkboxes plus a fourth, \`[ ] Type something\`,
|
|
94
|
+
that the question tool always appends - and free text next to three closed deterministic transforms
|
|
95
|
+
has nothing to act on. Whatever somebody types there matches no transform that exists, so the option
|
|
96
|
+
is either ignored or it lies. Three named options and the escape stays meaningful.
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
\`\`\`
|
|
99
|
+
May I apply the deterministic fixes after importing? Nothing here is AI - it is arithmetic,
|
|
100
|
+
and the version you have now stays frozen either way.
|
|
101
|
+
|
|
102
|
+
All three (recommended)
|
|
103
|
+
tokens & contrast - values that repeat become one named token, and text that fails
|
|
104
|
+
contrast gets its INK changed, never its background
|
|
105
|
+
spacing - lengths off your own scale converge onto the step you named
|
|
106
|
+
typography - sizes and weights land on your own scale
|
|
107
|
+
Let me pick
|
|
108
|
+
then, and only then, ask which ones - there the multi-select is the right shape,
|
|
109
|
+
because picking IS the question
|
|
110
|
+
None
|
|
111
|
+
the system arrives exactly as your code has it, and the fixes wait on a button
|
|
100
112
|
\`\`\`
|
|
101
113
|
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
Two things must be said in the same breath with the recommendation, because a default that edits is
|
|
115
|
+
only defensible with both:
|
|
104
116
|
|
|
105
|
-
- **
|
|
106
|
-
restoring it is one click.
|
|
117
|
+
- **The version they have now is frozen before anything is applied.** It becomes a published version,
|
|
118
|
+
it never moves, and restoring it is one click. Say the NUMBER the route answers with, never "v1":
|
|
119
|
+
a system publishes its baseline at birth, so an import writes into v2 and the freeze produces v3.
|
|
107
120
|
- **Value, never form.** Your axes, options, parts and layers are declarations - we do not touch
|
|
108
121
|
them. \`archetypeFidelity\` is excluded from this run on purpose: its fix once deleted the \`variant\`
|
|
109
122
|
and \`size\` a real \`Input\` declared (dono, 02/08).
|
package/package.json
CHANGED