synthesisui 0.16.185 → 0.16.187

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.
@@ -137,6 +137,28 @@ async function rewireIfBehind(root, cli) {
137
137
  console.log(`↻ the check after every write moved from ${pinned} to ${cli}`);
138
138
  console.log(" Reopen your editor session - hooks are read at startup.");
139
139
  }
140
+ /**
141
+ * O RECIBO DO PRÓPRIO UPGRADE - e há UM lugar que o emite, para os dois caminhos de escrita.
142
+ *
143
+ * Um upgrade que entrega arquivos novos e depois pede para alguém lembrar de conferir deixa a pessoa
144
+ * sem saber se funcionou (dono, 01/08). Então a checagem roda daqui, sobre a mesma raiz, e o
145
+ * relatório dela é o recibo.
146
+ *
147
+ * ELE VIROU UMA FUNÇÃO SÓ em 08/08 porque a duplicata custou caro: a chamada morava no fim do
148
+ * caminho de troca de VERSÃO, e o caminho de rematerialização - o comum, o que o alerta do terminal
149
+ * aciona - saía antes por um `return`. Os dois números do alerta fechavam e a medição continuava
150
+ * parada. Um spec textual passava, porque a chamada existia; ela é que não era alcançada.
151
+ *
152
+ * NUNCA `strict`. Um achado é informação, não falha do upgrade, e sair com erro por causa do drift da
153
+ * própria pessoa transformaria uma instalação bem-sucedida num terminal vermelho.
154
+ */
155
+ async function checkAfterWriting(root, version) {
156
+ console.log(section(`Checking the app against ${version}`));
157
+ await doctor({ dir: root }).catch((err) => {
158
+ /** Um doctor que não roda não é um upgrade que falhou: os artefatos já estão em disco e certos. */
159
+ console.log(body(`The check could not run (${err instanceof Error ? err.message : "unknown"}). Run \`npx synthesisui doctor\` when you can.`));
160
+ });
161
+ }
140
162
  async function theOnlyInstalled(root) {
141
163
  const dsDir = join(root, "_synthesisui", "ds");
142
164
  const entries = await readdir(dsDir, { withFileTypes: true }).catch(() => []);
@@ -188,6 +210,17 @@ export async function upgrade(asked, opts) {
188
210
  ...(opts.cli ? { cli: opts.cli } : {}),
189
211
  });
190
212
  await rewireIfBehind(root, opts.cli);
213
+ /**
214
+ * E ESTE CAMINHO MEDE TAMBÉM - ele não media, e é justamente o que o alerta do terminal manda
215
+ * rodar (dono, 08/08).
216
+ *
217
+ * A rematerialização é o caso comum agora: a versão do DS não mudou, mudou o LEITOR. Só que
218
+ * ela saía por este `return` antes de chegar ao doctor lá embaixo, então a pessoa via os dois
219
+ * números do alerta fecharem e a medição continuar sendo a de dias atrás. Na máquina do dono o
220
+ * `sync` seguinte respondeu `6 checks sent, 0 new` - o comando disse que atualizou e nada foi
221
+ * medido.
222
+ */
223
+ await checkAfterWriting(root, `v${installed}`);
191
224
  await reportWhatIsLeft(root, opts.cli ? { cli: opts.cli } : {});
192
225
  return;
193
226
  }
@@ -326,26 +359,7 @@ export async function upgrade(asked, opts) {
326
359
  console.log("");
327
360
  console.log(body(`⚠ Could not regenerate: ${failed.join(", ")} (removed/renamed in v${latest.version}?)`));
328
361
  }
329
- /**
330
- * THE CHECK RUNS ITSELF - closing the loop the owner asked for end to end
331
- * (dono, 01/08).
332
- *
333
- * An upgrade that lands a v2 whose whole point is "these literals are tokens
334
- * now" and then says "ask your agent to migrate" leaves the person with no
335
- * way to know whether it worked. The doctor is the answer and it is one
336
- * command, so it is not somebody's job to remember: it runs here, over the
337
- * same root, and its report IS the receipt.
338
- *
339
- * Never strict. A finding is information, not a failure of the upgrade, and
340
- * exiting non-zero on somebody's own drift would turn a successful install
341
- * into a red terminal.
342
- */
343
- console.log(section("Checking the app against v" + latest.version));
344
- await doctor({ dir: root }).catch((err) => {
345
- // A doctor that cannot run is not an upgrade that failed - the artifacts
346
- // are already on disk and correct.
347
- console.log(body(`The check could not run (${err instanceof Error ? err.message : "unknown"}). Run \`npx synthesisui doctor\` when you can.`));
348
- });
362
+ await checkAfterWriting(root, `v${latest.version}`);
349
363
  console.log(section("Migrate the app"));
350
364
  console.log(body(`The migration brief is at _synthesisui/ds/${slug}/UPGRADE.md`));
351
365
  console.log("");
package/dist/progress.js CHANGED
@@ -14,6 +14,19 @@
14
14
  * writes a carriage return into a transcript.
15
15
  */
16
16
  const BAR_WIDTH = 24;
17
+ /**
18
+ * O INTERVALO MÍNIMO ENTRE DOIS QUADROS - e ele existe por causa do CLIPBOARD, não da velocidade.
19
+ *
20
+ * A linha se redesenha com `\r`, então na tela ela é UMA linha e a leitura é ótima. Copiar é outra
21
+ * história: o `\r` não sobrevive à seleção, então cada repintura vira um pedaço do texto colado. Em
22
+ * 08/08 o dono tentou copiar a saída de um `sync` e levou junto 2 172 quadros - um por arquivo de
23
+ * evidência -, o que enterrou o relatório que ele queria mostrar (dono, 08/08).
24
+ *
25
+ * 80ms é o teto do que um olho aproveita: acima de ~12 quadros por segundo a animação já lê como
26
+ * contínua, e o resto é trabalho que só o clipboard vê. No caso dele isso troca 2 172 quadros por
27
+ * algumas dezenas, sem tirar um único frame que alguém fosse perceber.
28
+ */
29
+ const MIN_FRAME_MS = 80;
17
30
  /** Every this-many items, the non-TTY path prints one line. Enough to see movement in
18
31
  * a piped log without turning it into the log. */
19
32
  const MILESTONE_EVERY = 25;
@@ -49,9 +62,12 @@ function tail(label, width) {
49
62
  export function startProgress(title, opts) {
50
63
  const write = opts?.write ?? ((s) => process.stdout.write(s));
51
64
  const tty = opts?.isTty ?? Boolean(process.stdout.isTTY);
65
+ const now = opts?.now ?? (() => Date.now());
52
66
  let total = opts?.total ?? 0;
53
67
  let done = 0;
54
68
  let last = "";
69
+ /** `-Infinity` para o PRIMEIRO passo pintar sempre: silêncio no começo lê como travado. */
70
+ let painted = Number.NEGATIVE_INFINITY;
55
71
  const paint = () => {
56
72
  const count = total > 0 ? `${done} of ${total}` : String(done);
57
73
  const line = ` ${bar(done, total)} ${count} ${tail(last, 40)}`;
@@ -64,6 +80,14 @@ export function startProgress(title, opts) {
64
80
  done += 1;
65
81
  last = label;
66
82
  if (tty) {
83
+ /**
84
+ * O ÚLTIMO SEMPRE PINTA, mesmo dentro da janela: parar num "2 100 de 2 172" e pular para o
85
+ * resumo faria a barra parecer que desistiu antes do fim.
86
+ */
87
+ const at = now();
88
+ if (at - painted < MIN_FRAME_MS && !(total > 0 && done === total))
89
+ return;
90
+ painted = at;
67
91
  paint();
68
92
  return;
69
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.185",
3
+ "version": "0.16.187",
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": {