synthesisui 0.16.190 → 0.16.192

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/index.js CHANGED
@@ -643,6 +643,33 @@ async function recordRun(command, started, ok, error, bytes) {
643
643
  const root = resolve(typeof parseFlags(process.argv.slice(2)).flags.dir === "string"
644
644
  ? parseFlags(process.argv.slice(2)).flags.dir
645
645
  : process.cwd());
646
+ /**
647
+ * ONDE, DENTRO DO NOSSO CÓDIGO, e é o que separa uma queda consertável de uma inútil.
648
+ *
649
+ * `Cannot read properties of undefined` sem lugar nenhum não se conserta daqui - a única saída
650
+ * seria pedir para a pessoa reproduzir, que é exatamente o que não pode acontecer (dono, 09/08:
651
+ * *"não quero pedir para o usuário rodar alguma coisa porque a gente não pegou"*).
652
+ *
653
+ * O primeiro quadro da pilha que aponta para o NOSSO pacote resolve isso, e ele não é dado dele:
654
+ * `index.js:1234` é o nosso bundle. O diretório - que é o cache de npx na máquina dele - fica de
655
+ * fora, e um quadro que não seja nosso é descartado em vez de viajar.
656
+ */
657
+ const frame = (() => {
658
+ const stack = error instanceof Error ? (error.stack ?? "") : "";
659
+ for (const line of stack.split("\n").slice(1)) {
660
+ const at = /\(?([^()\s]+\.js):(\d+):(\d+)\)?\s*$/.exec(line.trim());
661
+ if (!at)
662
+ continue;
663
+ const file = at[1].split("/").pop() ?? "";
664
+ /** Só o nosso bundle e os nossos módulos: um quadro do Node ou de outro pacote não é nosso. */
665
+ if (!file || /node:/.test(at[1]))
666
+ continue;
667
+ if (!at[1].includes("synthesisui") && !at[1].endsWith("/dist/index.js"))
668
+ continue;
669
+ return `${file}:${at[2]}`;
670
+ }
671
+ return "";
672
+ })();
646
673
  const message = scrub(error instanceof Error ? error.message : error ? String(error) : "");
647
674
  await appendEvent(root, {
648
675
  kind: "run",
@@ -653,6 +680,7 @@ async function recordRun(command, started, ok, error, bytes) {
653
680
  /** Só a primeira linha, cortada: uma stack carrega caminhos do projeto de alguém. */
654
681
  ...(message ? { error: message.split("\n")[0].slice(0, 200) } : {}),
655
682
  ...(typeof bytes === "number" && bytes > 0 ? { bytes } : {}),
683
+ ...(frame ? { where: frame } : {}),
656
684
  });
657
685
  }
658
686
  catch {
package/dist/progress.js CHANGED
@@ -12,6 +12,9 @@
12
12
  * skill runs the CLI with no TTY and pipes the output, so it gets a plain milestone
13
13
  * line every so often. Neither has to know about the other, and the non-TTY path never
14
14
  * writes a carriage return into a transcript.
15
+ *
16
+ * E OS DOIS ESCREVEM EM `stderr`, porque progresso não é resultado: quem redireciona a saída fica com
17
+ * o relatório limpo, e quem está na tela continua vendo tudo.
15
18
  */
16
19
  const BAR_WIDTH = 24;
17
20
  /**
@@ -22,11 +25,11 @@ const BAR_WIDTH = 24;
22
25
  * 08/08 o dono tentou copiar a saída de um `sync` e levou junto 2 172 quadros - um por arquivo de
23
26
  * evidência -, o que enterrou o relatório que ele queria mostrar (dono, 08/08).
24
27
  *
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
+ * 250ms, e o número subiu de 80ms em 09/08 por uma razão de LEITURA e não só de clipboard: a linha
29
+ * mostra o arquivo sendo lido, e a 12 quadros por segundo o nome vira borrão. A quatro por segundo dá
30
+ * para de fato ler o que está passando - fica melhor na tela E três vezes menor no texto colado.
28
31
  */
29
- const MIN_FRAME_MS = 80;
32
+ const MIN_FRAME_MS = 250;
30
33
  /** Every this-many items, the non-TTY path prints one line. Enough to see movement in
31
34
  * a piped log without turning it into the log. */
32
35
  const MILESTONE_EVERY = 25;
@@ -60,7 +63,20 @@ function tail(label, width) {
60
63
  return `…${label.slice(-(width - 1))}`;
61
64
  }
62
65
  export function startProgress(title, opts) {
63
- const write = opts?.write ?? ((s) => process.stdout.write(s));
66
+ /**
67
+ * O PROGRESSO VAI PARA `stderr`, como o banner de fase ao lado - e isto é o que torna a saída
68
+ * copiável com garantia, e não com esperança.
69
+ *
70
+ * Uma barra é PROGRESSO, não resultado. O banner de fase já seguia essa regra desde 01/08 pelo
71
+ * mesmo motivo; a barra tinha ficado no `stdout` por descuido. Com ela em `stderr`, `sync > arquivo`
72
+ * e `sync | pbcopy` entregam o relatório limpo sem depender de o terminal honrar `\x1b[2K` - e na
73
+ * tela nada muda, porque o terminal mostra os dois.
74
+ *
75
+ * O dono viu isso duas vezes: na primeira eram 2 172 quadros no clipboard, e na segunda, já com o
76
+ * limite de tempo, ainda sobravam mais de cem. Depender do comportamento do terminal de cada pessoa
77
+ * é depender do que a gente não controla.
78
+ */
79
+ const write = opts?.write ?? ((s) => process.stderr.write(s));
64
80
  const tty = opts?.isTty ?? Boolean(process.stdout.isTTY);
65
81
  const now = opts?.now ?? (() => Date.now());
66
82
  let total = opts?.total ?? 0;
@@ -68,12 +84,22 @@ export function startProgress(title, opts) {
68
84
  let last = "";
69
85
  /** `-Infinity` para o PRIMEIRO passo pintar sempre: silêncio no começo lê como travado. */
70
86
  let painted = Number.NEGATIVE_INFINITY;
87
+ /**
88
+ * APAGAR A LINHA, e não pintar espaços por cima dela.
89
+ *
90
+ * A versão anterior voltava o cursor com `\r` e escrevia 80 caracteres, sobrando espaços para cobrir
91
+ * o quadro mais longo. Na tela funciona; no clipboard não: o terminal do dono guardava cada escrita
92
+ * como conteúdo NOVO, então colar trazia os quadros um atrás do outro separados por corridas de
93
+ * espaço - foi exatamente o que ele mostrou depois do primeiro conserto (dono, 09/08).
94
+ *
95
+ * `\x1b[2K` é a instrução explícita de LIMPAR A LINHA. Um terminal que a honra substitui a linha no
96
+ * próprio buffer em vez de acrescentar, e aí a seleção pega uma linha só. E como não há mais nada a
97
+ * cobrir, o preenchimento some junto: menos 65 bytes por quadro, e nenhuma corrida de espaço no
98
+ * texto colado.
99
+ */
71
100
  const paint = () => {
72
101
  const count = total > 0 ? `${done} of ${total}` : String(done);
73
- const line = ` ${bar(done, total)} ${count} ${tail(last, 40)}`;
74
- // Pad to the previous width so a shorter label cannot leave the tail of a
75
- // longer one behind on the line.
76
- write(`\r${line.padEnd(80)}`);
102
+ write(`\x1b[2K\r ${bar(done, total)} ${count} ${tail(last, 40)}`);
77
103
  };
78
104
  return {
79
105
  step(label) {
@@ -103,7 +129,7 @@ export function startProgress(title, opts) {
103
129
  },
104
130
  done(summary) {
105
131
  if (tty)
106
- write(`\r${" ".repeat(80)}\r`);
132
+ write("\x1b[2K\r");
107
133
  if (summary)
108
134
  write(` ${summary}\n`);
109
135
  },
@@ -55,6 +55,7 @@ export async function repoStateOf(projectRoot, slug, cli) {
55
55
  ...(typeof e.ms === "number" ? { ms: Math.round(e.ms) } : {}),
56
56
  ...(e.error ? { error: String(e.error).slice(0, 200) } : {}),
57
57
  ...(typeof e.bytes === "number" ? { bytes: Math.round(e.bytes) } : {}),
58
+ ...(e.where ? { where: String(e.where).slice(0, 60) } : {}),
58
59
  }));
59
60
  const doctor = events
60
61
  .filter((e) => e.kind === "doctor" && e.at)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.190",
3
+ "version": "0.16.192",
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": {