primocode 7.17.0 → 7.18.0

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.
Files changed (2) hide show
  1. package/bin/primocode.js +138 -30
  2. package/package.json +1 -1
package/bin/primocode.js CHANGED
@@ -119,12 +119,11 @@ function completarSlash(line) {
119
119
  // Régua com os níveis, o marcador ▲ embaixo do escolhido, "Faster" à
120
120
  // esquerda e "Smarter" à direita. ←/→ move, Enter confirma, Esc cancela.
121
121
 
122
- function desenharEffort(idx) {
122
+ // O layout sai em texto PLANO, com as marcas de onde pintar. Assim os dois
123
+ // pintores (o normal e o do ultracode animado) desenham exatamente a mesma
124
+ // régua — sem duas contas de posição para manter em sincronia.
125
+ function layoutEffort(idx) {
123
126
  const cols = Math.max(52, Math.min(process.stdout.columns || 100, 104));
124
- const regua = '▔'.repeat(cols);
125
- const linhas = [];
126
-
127
- // Cada nível ocupa uma coluna própria; o ultracode fica destacado no fim.
128
127
  const rotulos = effort.NOMES;
129
128
  const larguras = rotulos.map((n) => Math.max(n.length + 3, 9));
130
129
  const posicoes = [];
@@ -134,38 +133,130 @@ function desenharEffort(idx) {
134
133
  x += larguras[i];
135
134
  }
136
135
  const fimRegua = x;
137
-
138
- linhas.push(c.dim(regua));
139
- linhas.push(' ' + c.bold(c.white('Effort')));
140
- linhas.push('');
141
-
142
- // Faster ....▲.... Smarter — a trilha, com o marcador na posição atual.
143
136
  const marca = posicoes[idx];
137
+
144
138
  let trilha = '';
145
139
  for (let k = 23; k < fimRegua; k++) {
146
- if (k === marca) trilha += c.brand('▲');
147
- else if (k === fimRegua - 12) trilha += c.dim('┆'); // ultracode é um degrau à parte
148
- else trilha += c.dim(k < marca ? '─' : '─');
140
+ if (k === marca) trilha += '▲';
141
+ else if (k === fimRegua - 12) trilha += '┆'; // ultracode é um degrau à parte
142
+ else trilha += '─';
149
143
  }
150
- linhas.push(' ' + c.muted('Faster') + ' '.repeat(Math.max(1, fimRegua - 23 - 6 - 7)) + c.muted('Smarter'));
151
- linhas.push(' ' + trilha);
152
144
 
153
- // Os nomes, o escolhido aceso.
154
- let linhaNomes = ' '.repeat(23);
145
+ let nomes = ' '.repeat(23);
146
+ let deNome = 0, ateNome = 0;
155
147
  for (let i = 0; i < rotulos.length; i++) {
156
148
  const nome = rotulos[i];
157
- const espaco = larguras[i];
158
- const pad = Math.floor((espaco - nome.length) / 2);
159
- const texto = i === idx ? c.bold(c.brand(nome)) : c.muted(nome);
160
- linhaNomes += ' '.repeat(pad) + texto + ' '.repeat(espaco - nome.length - pad);
149
+ const pad = Math.floor((larguras[i] - nome.length) / 2);
150
+ if (i === idx) { deNome = nomes.length + pad; ateNome = deNome + nome.length; }
151
+ nomes += ' '.repeat(pad) + nome + ' '.repeat(larguras[i] - nome.length - pad);
161
152
  }
162
- linhas.push(linhaNomes);
163
153
 
164
154
  const nivel = effort.nivelDe(rotulos[idx]);
165
- linhas.push(' ' + c.dim(nivel.resumo));
166
- linhas.push('');
167
- linhas.push(' ' + c.muted('←/→ ajusta · Enter confirma · Esc cancela'));
168
- return linhas.join('\n');
155
+ const vaoExtremos = Math.max(1, fimRegua - 23 - 6 - 7);
156
+
157
+ return {
158
+ cols,
159
+ marca,
160
+ destaque: { de: deNome, ate: ateNome },
161
+ linhas: [
162
+ { tipo: 'regua', texto: '▔'.repeat(cols) },
163
+ { tipo: 'titulo', texto: ' Effort' },
164
+ { tipo: 'vazio', texto: '' },
165
+ { tipo: 'extremos', texto: ' '.repeat(23) + 'Faster' + ' '.repeat(vaoExtremos) + 'Smarter' },
166
+ { tipo: 'trilha', texto: ' '.repeat(23) + trilha },
167
+ { tipo: 'nomes', texto: nomes },
168
+ { tipo: 'resumo', texto: ' '.repeat(23) + nivel.resumo },
169
+ { tipo: 'vazio', texto: '' },
170
+ { tipo: 'ajuda', texto: ' ←/→ ajusta · Enter confirma · Esc cancela' },
171
+ ],
172
+ };
173
+ }
174
+
175
+ // ── O brilho do ultracode ────────────────────────────────────────────────
176
+ // No nível máximo o painel inteiro ganha um fundo roxo que ondula, como no
177
+ // Claude Code. São faixas verticais de roxo em brilhos diferentes; a onda
178
+ // anda para o lado a cada quadro, e o texto fica branco por cima.
179
+ //
180
+ // Roxos do cubo de 256 cores, ordenados pelo brilho REAL (luminância), do
181
+ // mais escuro ao mais claro — a onda escolhe um deles por faixa. Fora da
182
+ // lista ficaram os roxos escuros que puxam para o vinho (53-56): quebravam a
183
+ // família violeta. Só 256 cores (não truecolor): funciona em qualquer
184
+ // terminal, inclusive no Terminal.app do macOS.
185
+ const ROXOS = [57, 92, 93, 63, 98, 99, 105, 141];
186
+
187
+ function fundoRoxo(x, fase) {
188
+ // Duas ondas de períodos diferentes: o encontro delas dá o desenho
189
+ // irregular de faixas, em vez de listras repetidas e óbvias.
190
+ const faixa = Math.floor(x / 2);
191
+ const onda = Math.sin(faixa * 0.55 + fase) * 0.6 + Math.sin(faixa * 0.17 - fase * 0.7) * 0.4;
192
+ const i = Math.round((onda + 1) / 2 * (ROXOS.length - 1));
193
+ return ROXOS[Math.max(0, Math.min(ROXOS.length - 1, i))];
194
+ }
195
+
196
+ // Pinta a linha inteira (até a largura do painel) com a onda por baixo.
197
+ // `destaque` é o trecho que fica branco e forte (o nível escolhido); o resto
198
+ // da linha sai num lilás claro, para o escolhido saltar aos olhos.
199
+ function pintarLinhaUltra(texto, largura, fase, opts = {}) {
200
+ const base = opts.base || 231; // branco
201
+ const apagado = opts.apagado || base;
202
+ const destaque = opts.destaque;
203
+ let saida = '';
204
+ let corFundo = -1;
205
+ let corTexto = -1;
206
+ let forte = null;
207
+
208
+ for (let x = 0; x < largura; x++) {
209
+ const fundo = fundoRoxo(x, fase);
210
+ if (fundo !== corFundo) { saida += `\x1b[48;5;${fundo}m`; corFundo = fundo; }
211
+
212
+ const dentro = destaque && x >= destaque.de && x < destaque.ate;
213
+ const querForte = opts.negrito || dentro;
214
+ const querCor = dentro ? base : apagado;
215
+ if (querForte !== forte) { saida += querForte ? '\x1b[1m' : '\x1b[22m'; forte = querForte; }
216
+ if (querCor !== corTexto) { saida += `\x1b[38;5;${querCor}m`; corTexto = querCor; }
217
+
218
+ saida += texto[x] || ' ';
219
+ }
220
+ return saida + '\x1b[0m';
221
+ }
222
+
223
+ function desenharEffort(idx, fase) {
224
+ const L = layoutEffort(idx);
225
+ const ultra = effort.NOMES[idx] === 'ultracode';
226
+
227
+ if (ultra) {
228
+ // No ultracode o painel todo vira fundo animado. Cada linha é pintada
229
+ // com a MESMA onda, então as faixas ficam alinhadas de cima a baixo.
230
+ const LILAS = 189; // lilás bem claro, para o que não é o escolhido
231
+ return L.linhas.map((l) => {
232
+ if (l.tipo === 'titulo') return pintarLinhaUltra(l.texto, L.cols, fase, { negrito: true });
233
+ if (l.tipo === 'nomes') return pintarLinhaUltra(l.texto, L.cols, fase, { destaque: L.destaque, apagado: LILAS });
234
+ if (l.tipo === 'resumo' || l.tipo === 'extremos') return pintarLinhaUltra(l.texto, L.cols, fase, { base: LILAS, apagado: LILAS });
235
+ return pintarLinhaUltra(l.texto, L.cols, fase, {});
236
+ }).join('\n');
237
+ }
238
+
239
+ return L.linhas.map((l) => {
240
+ const t = l.texto;
241
+ switch (l.tipo) {
242
+ case 'regua': return c.dim(t);
243
+ case 'titulo': return c.bold(c.white(t));
244
+ case 'extremos': return c.muted(t);
245
+ case 'trilha': {
246
+ // O marcador aceso, o resto da trilha recuada.
247
+ const antes = t.slice(0, L.marca);
248
+ const depois = t.slice(L.marca + 1);
249
+ return c.dim(antes) + c.brand('▲') + c.dim(depois);
250
+ }
251
+ case 'nomes': {
252
+ const { de, ate } = L.destaque;
253
+ return c.muted(t.slice(0, de)) + c.bold(c.brand(t.slice(de, ate))) + c.muted(t.slice(ate));
254
+ }
255
+ case 'resumo': return c.dim(t);
256
+ case 'ajuda': return c.muted(t);
257
+ default: return t;
258
+ }
259
+ }).join('\n');
169
260
  }
170
261
 
171
262
  // Desenha, escuta as setas, e devolve o nome escolhido (ou null se cancelou).
@@ -177,21 +268,38 @@ function sliderEffort(atual) {
177
268
  }
178
269
  let idx = effort.indiceDe(atual);
179
270
  const ALTURA = 9; // linhas que o desenho ocupa — para redesenhar por cima
271
+ let fase = 0;
272
+ let anima = null;
180
273
 
181
274
  const pintar = (primeira) => {
182
275
  if (!primeira) process.stdout.write(`\x1b[${ALTURA}A\r`);
183
- process.stdout.write(desenharEffort(idx).split('\n').map((l) => '\x1b[2K' + l).join('\n') + '\n');
276
+ process.stdout.write(desenharEffort(idx, fase).split('\n').map((l) => '\x1b[2K' + l).join('\n') + '\n');
184
277
  };
278
+
279
+ // O fundo do ultracode ondula; sair dele apaga a animação na hora, para
280
+ // não redesenhar sozinho um painel que já está parado.
281
+ const cuidarDaAnimacao = () => {
282
+ const ultra = effort.NOMES[idx] === 'ultracode';
283
+ if (ultra && !anima) {
284
+ anima = setInterval(() => { fase += 0.32; pintar(false); }, 90);
285
+ anima.unref();
286
+ } else if (!ultra && anima) {
287
+ clearInterval(anima); anima = null;
288
+ }
289
+ };
290
+
185
291
  pintar(true);
292
+ cuidarDaAnimacao();
186
293
 
187
294
  const aoTeclar = (ch, key) => {
188
295
  if (!key) return;
189
- if (key.name === 'right' || key.name === 'l') { idx = Math.min(effort.NOMES.length - 1, idx + 1); pintar(false); return; }
190
- if (key.name === 'left' || key.name === 'h') { idx = Math.max(0, idx - 1); pintar(false); return; }
296
+ if (key.name === 'right' || key.name === 'l') { idx = Math.min(effort.NOMES.length - 1, idx + 1); cuidarDaAnimacao(); pintar(false); return; }
297
+ if (key.name === 'left' || key.name === 'h') { idx = Math.max(0, idx - 1); cuidarDaAnimacao(); pintar(false); return; }
191
298
  if (key.name === 'return' || key.name === 'enter') { encerrar(effort.NOMES[idx]); return; }
192
299
  if (key.name === 'escape' || (key.ctrl && key.name === 'c')) { encerrar(null); return; }
193
300
  };
194
301
  const encerrar = (valor) => {
302
+ if (anima) { clearInterval(anima); anima = null; }
195
303
  process.stdin.removeListener('keypress', aoTeclar);
196
304
  sliderAtivo = false;
197
305
  resolve(valor);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "7.17.0",
3
+ "version": "7.18.0",
4
4
  "description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
5
5
  "main": "bin/primocode.js",
6
6
  "bin": {