vatts 1.2.0-test.5 → 1.2.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.
@@ -1,20 +1,10 @@
1
1
  /**
2
- * Um "handle" para uma linha dinâmica. As instâncias desta classe
3
- * são retornadas por `Console.dynamicLine()` e usadas para controlar
4
- * o conteúdo da linha.
2
+ * Handle para linhas dinâmicas.
5
3
  */
6
4
  export declare class DynamicLine {
7
5
  private readonly _id;
8
6
  constructor(initialContent: string);
9
- /**
10
- * Atualiza o conteúdo da linha no console.
11
- * @param newContent O novo texto a ser exibido.
12
- */
13
7
  update(newContent: string): void;
14
- /**
15
- * Finaliza a linha, opcionalmente com um texto final, e a torna estática.
16
- * @param finalContent O texto final a ser exibido.
17
- */
18
8
  end(finalContent: string): void;
19
9
  }
20
10
  export declare enum Colors {
@@ -58,13 +48,16 @@ export default class Console {
58
48
  private static redrawDynamicLines;
59
49
  private static writeStatic;
60
50
  private static formatLog;
61
- private static registerDynamicLine;
62
- private static updateDynamicLine;
63
- private static endDynamicLine;
51
+ /**
52
+ * Menu de seleção interativo usando setas do teclado.
53
+ * @param options Objeto no formato { "valor_retornado": "Label Exibida" }
54
+ */
55
+ static selection<T = string>(question: string, options: Record<string, T>): Promise<string>;
64
56
  static error(...args: any[]): void;
65
57
  static warn(...args: any[]): void;
66
58
  static info(...args: any[]): void;
67
59
  static success(...args: any[]): void;
60
+ static default_log(...args: any[]): void;
68
61
  static debug(...args: any[]): void;
69
62
  static logCustomLevel(levelName: string, without?: boolean, color?: Colors, ...args: any[]): void;
70
63
  static logWithout(level: Levels, colors?: Colors, ...args: any[]): void;
@@ -76,4 +69,7 @@ export default class Console {
76
69
  Value: any;
77
70
  }>): void;
78
71
  static dynamicLine(initialContent: string): DynamicLine;
72
+ private static registerDynamicLine;
73
+ private static updateDynamicLine;
74
+ private static endDynamicLine;
79
75
  }
@@ -22,28 +22,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.Levels = exports.Colors = exports.DynamicLine = void 0;
23
23
  const node_readline_1 = __importDefault(require("node:readline"));
24
24
  /**
25
- * Um "handle" para uma linha dinâmica. As instâncias desta classe
26
- * são retornadas por `Console.dynamicLine()` e usadas para controlar
27
- * o conteúdo da linha.
25
+ * Handle para linhas dinâmicas.
28
26
  */
29
27
  class DynamicLine {
30
- // A ID é usada internamente pela classe Console para rastrear esta linha.
31
28
  _id = Symbol();
32
29
  constructor(initialContent) {
33
- // Registra esta nova linha na classe Console para que ela seja renderizada.
34
30
  Console['registerDynamicLine'](this._id, initialContent);
35
31
  }
36
- /**
37
- * Atualiza o conteúdo da linha no console.
38
- * @param newContent O novo texto a ser exibido.
39
- */
40
32
  update(newContent) {
41
33
  Console['updateDynamicLine'](this._id, newContent);
42
34
  }
43
- /**
44
- * Finaliza a linha, opcionalmente com um texto final, e a torna estática.
45
- * @param finalContent O texto final a ser exibido.
46
- */
47
35
  end(finalContent) {
48
36
  Console['endDynamicLine'](this._id, finalContent);
49
37
  }
@@ -87,27 +75,21 @@ var Levels;
87
75
  Levels["SUCCESS"] = "SUCCESS";
88
76
  })(Levels || (exports.Levels = Levels = {}));
89
77
  class Console {
90
- // Armazena o estado de todas as linhas dinâmicas ativas
91
78
  static activeLines = [];
92
- // Quantas linhas foram efetivamente renderizadas na última operação.
93
79
  static lastRenderedLines = 0;
94
- // --- MÉTODOS PRIVADOS PARA GERENCIAR A RENDERIZAÇÃO ---
80
+ // --- RENDERIZAÇÃO ---
95
81
  static redrawDynamicLines() {
96
82
  const stream = process.stdout;
97
83
  if (this.lastRenderedLines > 0) {
98
84
  try {
99
85
  node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
100
86
  }
101
- catch (_e) {
102
- // Em terminais estranhos a movimentação pode falhar — ignoramos.
103
- }
87
+ catch { }
104
88
  }
105
89
  node_readline_1.default.cursorTo(stream, 0);
106
90
  node_readline_1.default.clearScreenDown(stream);
107
91
  if (this.activeLines.length > 0) {
108
- // ATUALIZADO: Aplica o formato de log (Timestamp + Style) nas linhas dinâmicas
109
- // Usamos um nível pseudo 'WAIT' para indicar processo em andamento
110
- stream.write(this.activeLines.map(l => this.formatLog('WAIT', l.content, Colors.FgRed)).join('\n') + '\n');
92
+ stream.write(this.activeLines.map(l => this.formatLog('WAIT', l.content)).join('\n') + '\n');
111
93
  }
112
94
  this.lastRenderedLines = this.activeLines.length;
113
95
  }
@@ -117,105 +99,136 @@ class Console {
117
99
  try {
118
100
  node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
119
101
  }
120
- catch (_e) { }
102
+ catch { }
121
103
  node_readline_1.default.cursorTo(stream, 0);
122
104
  node_readline_1.default.clearScreenDown(stream);
123
105
  }
124
- // MODIFICAÇÃO PRINCIPAL:
125
- // Substituímos stream.write por console.log aqui.
126
- // O console.log é interceptado pelos debuggers (VSCode, etc), o stream.write não.
127
- // Removemos a quebra de linha final (\n$) pois o console.log já adiciona uma automaticamente.
128
106
  console.log(content.replace(/\n$/, ''));
129
107
  if (this.activeLines.length > 0) {
130
- // ATUALIZADO: Garante que ao redesenhar após um log estático, o formato se mantém
131
- stream.write(this.activeLines.map(l => this.formatLog('WAIT', l.content, Colors.FgRed)).join('\n') + '\n');
108
+ stream.write(this.activeLines.map(l => this.formatLog('WAIT', l.content)).join('\n') + '\n');
132
109
  this.lastRenderedLines = this.activeLines.length;
133
110
  }
134
111
  else {
135
112
  this.lastRenderedLines = 0;
136
113
  }
137
114
  }
138
- // --- HELPER DE FORMATAÇÃO CENTRALIZADO ---
139
115
  static formatLog(level, message, color) {
140
116
  let icon = '•';
141
117
  let baseColor = Colors.FgWhite;
142
118
  switch (level) {
143
- // ✕ : Multiplication X (Matemático, sempre texto)
144
119
  case Levels.ERROR:
145
120
  icon = '✕';
146
121
  baseColor = Colors.FgRed;
147
122
  break;
148
- // ⚠ : Muitas vezes vira emoji. O triângulo ▲ é mais seguro e fica bonito colorido
149
- // Alternativa: '‼'
150
123
  case Levels.WARN:
151
124
  icon = '▲';
152
125
  baseColor = Colors.FgYellow;
153
126
  break;
154
- // ℹ : Vira emoji. O '𝐢' é um "i" matemático em negrito (Math Bold Small I)
155
- // Ele mantém a cor que você definir e parece muito um ícone.
156
127
  case Levels.INFO:
157
- icon = '𝐢';
158
- baseColor = Colors.FgRed; // ALTERADO: Agora é vermelho (o Bright é aplicado abaixo)
128
+ icon = '';
129
+ baseColor = Colors.FgCyan;
159
130
  break;
160
- // ✔ : Às vezes vira emoji verde. O '✓' simples costuma obedecer a cor.
161
- // Se der erro, use '√' (raiz quadrada)
162
131
  case Levels.SUCCESS:
163
132
  icon = '✓';
164
133
  baseColor = Colors.FgGreen;
165
134
  break;
166
- // ⚙ : Vira emoji cinza. Use '›' ou '»' ou '⌗' para debug
167
135
  case Levels.DEBUG:
168
136
  icon = '›';
169
- baseColor = Colors.FgMagenta;
137
+ baseColor = Colors.FgGray;
170
138
  break;
171
- // ⟳ : Esse costuma funcionar, mas se virar emoji, use '∞' ou '…'
172
139
  case 'WAIT':
173
- icon = '';
174
- baseColor = Colors.FgRed;
140
+ icon = '';
141
+ baseColor = Colors.FgCyan;
175
142
  break;
176
143
  default:
177
144
  icon = '•';
178
145
  baseColor = color || Colors.FgWhite;
179
146
  break;
180
147
  }
181
- if (color) {
182
- baseColor = color;
183
- }
184
148
  const gray = Colors.FgGray;
185
149
  const bold = Colors.Bright;
186
150
  const reset = Colors.Reset;
187
- const now = new Date();
188
- const time = now.toLocaleTimeString('pt-BR', { hour12: false });
189
- // Retorna a string formatada SEM quebra de linha (quem chama decide onde por)
190
- // O Colors.Bright + baseColor garante que será Bright Red
191
- return ` ${gray}${time}${reset} ${Colors.Bright + baseColor}${icon} ${bold}${level}${reset} ${message}`;
151
+ const time = new Date().toLocaleTimeString('pt-BR', { hour12: false });
152
+ const levelStr = level === 'WAIT' ? '' : ` ${bold}${level}${reset}`;
153
+ return ` ${gray}${time}${reset} ${baseColor}${icon}${levelStr}${reset} ${message}`;
192
154
  }
193
- // --- MÉTODOS CHAMADOS PELA CLASSE DynamicLine ---
194
- static registerDynamicLine(id, content) {
195
- this.activeLines.push({ id, content });
196
- this.redrawDynamicLines();
197
- }
198
- static updateDynamicLine(id, newContent) {
199
- const line = this.activeLines.find(l => l.id === id);
200
- if (line) {
201
- line.content = newContent;
202
- this.redrawDynamicLines();
203
- }
204
- }
205
- static endDynamicLine(id, finalContent) {
206
- const lineIndex = this.activeLines.findIndex(l => l.id === id);
207
- if (lineIndex > -1) {
208
- this.activeLines.splice(lineIndex, 1);
209
- // ATUALIZADO: Formata a mensagem final como INFO (ou SUCCESS implícito)
210
- // para manter consistência visual com o resto dos logs.
211
- this.writeStatic(this.formatLog(Levels.INFO, finalContent) + '\n');
155
+ // --- INTERATIVIDADE (SELECTION) ---
156
+ /**
157
+ * Menu de seleção interativo usando setas do teclado.
158
+ * @param options Objeto no formato { "valor_retornado": "Label Exibida" }
159
+ */
160
+ static async selection(question, options) {
161
+ const entries = Object.entries(options);
162
+ let currentIndex = 0;
163
+ const stream = process.stdout;
164
+ let firstRender = true;
165
+ if (this.lastRenderedLines > 0) {
166
+ node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
167
+ node_readline_1.default.cursorTo(stream, 0);
168
+ node_readline_1.default.clearScreenDown(stream);
212
169
  }
170
+ stream.write('\x1b[?25l'); // Hide cursor
171
+ const render = () => {
172
+ // Se não for a primeira vez, sobe as linhas do menu anterior para sobrescrever
173
+ if (!firstRender) {
174
+ node_readline_1.default.moveCursor(stream, 0, -(entries.length + 1));
175
+ }
176
+ node_readline_1.default.cursorTo(stream, 0);
177
+ node_readline_1.default.clearScreenDown(stream);
178
+ stream.write(` ${Colors.FgCyan}?${Colors.Reset} ${Colors.Bright}${question}${Colors.Reset}\n`);
179
+ entries.forEach(([key, label], i) => {
180
+ const isSelected = i === currentIndex;
181
+ const prefix = isSelected ? `${Colors.FgCyan}❯${Colors.Reset}` : ' ';
182
+ const text = isSelected ? `${Colors.FgCyan}${Colors.Bright}${label}${Colors.Reset}` : `${Colors.FgGray}${label}${Colors.Reset}`;
183
+ stream.write(` ${prefix} ${text}\n`);
184
+ });
185
+ firstRender = false;
186
+ };
187
+ render();
188
+ return new Promise((resolve) => {
189
+ const handleKey = (_chunk, key) => {
190
+ if (!key)
191
+ return;
192
+ if (key.name === 'up') {
193
+ currentIndex = (currentIndex - 1 + entries.length) % entries.length;
194
+ render();
195
+ }
196
+ else if (key.name === 'down') {
197
+ currentIndex = (currentIndex + 1) % entries.length;
198
+ render();
199
+ }
200
+ else if (key.name === 'return') {
201
+ process.stdin.removeListener('keypress', handleKey);
202
+ if (process.stdin.isTTY)
203
+ process.stdin.setRawMode(false);
204
+ process.stdin.pause();
205
+ stream.write('\x1b[?25h'); // Show cursor
206
+ // Limpa o menu final antes de escrever o log estático
207
+ node_readline_1.default.moveCursor(stream, 0, -(entries.length + 1));
208
+ node_readline_1.default.cursorTo(stream, 0);
209
+ node_readline_1.default.clearScreenDown(stream);
210
+ const [selectedKey, selectedLabel] = entries[currentIndex];
211
+ this.writeStatic(` ${Colors.FgCyan}✓${Colors.Reset} ${Colors.Bright}${question}${Colors.Reset} ${Colors.FgGray}›${Colors.Reset} ${selectedLabel}`);
212
+ resolve(selectedKey);
213
+ }
214
+ else if (key.ctrl && key.name === 'c') {
215
+ stream.write('\x1b[?25h');
216
+ process.exit();
217
+ }
218
+ };
219
+ node_readline_1.default.emitKeypressEvents(process.stdin);
220
+ if (process.stdin.isTTY)
221
+ process.stdin.setRawMode(true);
222
+ process.stdin.resume();
223
+ process.stdin.on('keypress', handleKey);
224
+ });
213
225
  }
214
- // --- MÉTODOS DE LOG PÚBLICOS ---
226
+ // --- MÉTODOS PÚBLICOS ---
215
227
  static error(...args) { this.log(Levels.ERROR, null, ...args); }
216
228
  static warn(...args) { this.log(Levels.WARN, null, ...args); }
217
229
  static info(...args) { this.log(Levels.INFO, null, ...args); }
218
230
  static success(...args) { this.log(Levels.SUCCESS, null, ...args); }
231
+ static default_log(...args) { this.log(Levels.INFO, null, ...args); }
219
232
  static debug(...args) { this.log(Levels.DEBUG, null, ...args); }
220
233
  static logCustomLevel(levelName, without = true, color, ...args) {
221
234
  if (without) {
@@ -232,50 +245,29 @@ class Console {
232
245
  let output = "";
233
246
  for (const arg of args) {
234
247
  let msg = (arg instanceof Error) ? arg.stack : (typeof arg === 'string') ? arg : JSON.stringify(arg, null, 2);
235
- if (msg) {
236
- // ATUALIZADO: Usa o helper formatLog
248
+ if (msg)
237
249
  output += this.formatLog(level, msg, colors) + '\n';
238
- }
239
250
  }
240
- // Remove a última quebra de linha porque writeStatic já garante uma
241
251
  this.writeStatic(output.replace(/\n$/, ''));
242
252
  }
243
- // --- OUTROS MÉTODOS ---
244
253
  static async ask(question, defaultValue) {
245
- const stream = process.stdout;
246
- if (this.lastRenderedLines > 0) {
247
- try {
248
- node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
249
- }
250
- catch (_e) { }
251
- node_readline_1.default.cursorTo(stream, 0);
252
- node_readline_1.default.clearScreenDown(stream);
253
- }
254
- const readlineInterface = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
255
- const defaultPart = defaultValue ? ` (${defaultValue})` : '';
256
- const prompt = ` ${Colors.FgRed}?${Colors.Reset} ${question}${Colors.FgGray}${defaultPart}${Colors.Reset} \n ${Colors.FgRed}➜${Colors.Reset} `;
254
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
255
+ const defaultPart = defaultValue ? ` ${Colors.FgGray}(${defaultValue})${Colors.Reset}` : '';
256
+ const prompt = ` ${Colors.FgCyan}?${Colors.Reset} ${Colors.Bright}${question}${Colors.Reset}${defaultPart}\n ${Colors.FgCyan}❯${Colors.Reset} `;
257
257
  return new Promise(resolve => {
258
- readlineInterface.question(prompt, ans => {
259
- readlineInterface.close();
258
+ rl.question(prompt, ans => {
259
+ rl.close();
260
260
  const value = ans.trim();
261
- this.redrawDynamicLines();
262
261
  resolve(value === '' && defaultValue !== undefined ? defaultValue : value);
263
262
  });
264
263
  });
265
264
  }
266
265
  static async confirm(message, defaultYes = false) {
267
266
  const suffix = defaultYes ? 'Y/n' : 'y/N';
268
- while (true) {
269
- const ans = (await this.ask(`${message} ${Colors.FgGray}[${suffix}]${Colors.Reset}`)).toLowerCase();
270
- if (ans === '')
271
- return defaultYes;
272
- if (['y', 'yes', 's', 'sim'].includes(ans))
273
- return true;
274
- if (['n', 'no', 'nao', 'não'].includes(ans))
275
- return false;
276
- // ATUALIZADO: Formato consistente
277
- this.writeStatic(` ${Colors.FgRed}✖ Opção inválida.${Colors.Reset}`);
278
- }
267
+ const ans = (await this.ask(`${message} ${Colors.FgGray}[${suffix}]${Colors.Reset}`)).toLowerCase();
268
+ if (ans === '')
269
+ return defaultYes;
270
+ return ['y', 'yes', 's', 'sim'].includes(ans);
279
271
  }
280
272
  static table(data) {
281
273
  let rows;
@@ -293,7 +285,7 @@ class Console {
293
285
  const mid = `├${h_line}┼${v_line}┤`;
294
286
  const bottom = `└${h_line}┴${v_line}┘`;
295
287
  let output = top + '\n';
296
- output += `│ ${Colors.Bright}${Colors.FgGreen}${'Field'.padEnd(fieldLen)}${Colors.Reset} │ ${Colors.Bright}${Colors.FgGreen}${'Value'.padEnd(valueLen)}${Colors.Reset} │\n`;
288
+ output += `│ ${Colors.Bright}${Colors.FgCyan}${'Field'.padEnd(fieldLen)}${Colors.Reset} │ ${Colors.Bright}${Colors.FgCyan}${'Value'.padEnd(valueLen)}${Colors.Reset} │\n`;
297
289
  output += mid + '\n';
298
290
  for (const row of rows) {
299
291
  output += `│ ${row.Field.padEnd(fieldLen)} │ ${row.Value.padEnd(valueLen)} │\n`;
@@ -304,5 +296,23 @@ class Console {
304
296
  static dynamicLine(initialContent) {
305
297
  return new DynamicLine(initialContent);
306
298
  }
299
+ static registerDynamicLine(id, content) {
300
+ this.activeLines.push({ id, content });
301
+ this.redrawDynamicLines();
302
+ }
303
+ static updateDynamicLine(id, newContent) {
304
+ const line = this.activeLines.find(l => l.id === id);
305
+ if (line) {
306
+ line.content = newContent;
307
+ this.redrawDynamicLines();
308
+ }
309
+ }
310
+ static endDynamicLine(id, finalContent) {
311
+ const index = this.activeLines.findIndex(l => l.id === id);
312
+ if (index > -1) {
313
+ this.activeLines.splice(index, 1);
314
+ this.writeStatic(this.formatLog(Levels.SUCCESS, finalContent));
315
+ }
316
+ }
307
317
  }
308
318
  exports.default = Console;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vatts",
3
- "version": "1.2.0-test.5",
3
+ "version": "1.2.0",
4
4
  "description": "Vatts.js is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "itsmuzin",