@getparallel/parx 0.4.2 → 0.4.4
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/CHANGELOG.md +11 -0
- package/README.md +16 -5
- package/dist/actividad.js +131 -0
- package/dist/agente.js +35 -8
- package/dist/cli.js +53 -7
- package/dist/config.js +4 -0
- package/dist/herramientas.js +18 -4
- package/dist/marca.js +18 -0
- package/dist/textos.js +20 -10
- package/dist/tui.js +136 -40
- package/dist/ui.js +143 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ changes). Entries are generated by `npm run release` from the git history of `ap
|
|
|
6
6
|
|
|
7
7
|
<!-- Notes written under «## Unreleased» are used as the next entry instead of the git log. -->
|
|
8
8
|
|
|
9
|
+
## 0.4.4 (2026-10-11)
|
|
10
|
+
|
|
11
|
+
- parx always shows what it is doing: «Thinking…» from the first moment, then the real step as it happens («Reading src/app.ts…», «Searching for useAuth…», «Editing 2 files…», «Running tests…», «Writing the answer…»), with the time and the Actions. If the server takes a while, only honest phrases («Planning the steps…», «Looking at the results…», «Waiting for the server…»); the old brand phrases are gone. Same in `--lanes`, at 40 columns, with `NO_COLOR` and `--ascii`.
|
|
12
|
+
- The answer is written out word by word with a soft cursor, in 2 seconds at most, and ends exactly as before (markdown, code blocks, wrapping). Only the rows that change are repainted; scrolling up while it writes does not jump. Any key shows the rest at once; `/animation off` (saved) or `--no-animation` shows answers whole, and so do pipes, CI, `--lanes` and `/report`.
|
|
13
|
+
- `/new` starts truly fresh: the screen and the terminal scrollback are cleared in one go, the queue, the waiting lines and what the agent remembers are gone, and you see the welcome and the tips again, like when parx opens. Mode, model and sign-in stay. If a task is running, it asks «Stop and start new?» with buttons first.
|
|
14
|
+
- `/mouse off` is remembered in the next session too (it was saved but not read back).
|
|
15
|
+
|
|
16
|
+
## 0.4.3 (2026-10-11)
|
|
17
|
+
|
|
18
|
+
- Each permission mode has its own colour, from least to most freedom: Read only teal, Ask violet, Auto orange, Bypass red. It shows in the status row, the `/mode` menu, the `/mode` confirmation, `/help` and the `╱╱` input marker; bash mode keeps its amber.
|
|
19
|
+
|
|
9
20
|
## 0.4.2 (2026-10-11)
|
|
10
21
|
|
|
11
22
|
- Bash mode: type `!` first and the input turns amber with a `bash` label; Enter runs the command in the project folder, its output streams into the conversation with the exit code, and the agent sees it next turn. Read only refuses, «Careful» commands ask first, credentials stay out of the conversation, Esc or Ctrl+C stops it.
|
package/README.md
CHANGED
|
@@ -93,10 +93,19 @@ when it asks first:
|
|
|
93
93
|
## Inside a session
|
|
94
94
|
|
|
95
95
|
The input sits at the bottom from the start; the status lives in one place, the row under the rail:
|
|
96
|
-
the mode and the model at rest; while it works, what it is doing
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
the mode and the model at rest; while it works, it always says what it is doing right now, with a
|
|
97
|
+
small rail animation in violet, the time and the Actions so far. It is never silent: «Thinking…»
|
|
98
|
+
while Parallel thinks, then the real step as it happens («Reading src/app.ts…», «Searching for
|
|
99
|
+
useAuth…», «Editing 2 files…», «Running tests…», «Running git status…», «Writing the answer…»). If
|
|
100
|
+
the server takes a while, only what is really going on («Planning the steps…», «Looking at the
|
|
101
|
+
results…», «Waiting for the server…»). It works the same in `--lanes` (one row per lane), at 40
|
|
102
|
+
columns, with `NO_COLOR` and with `--ascii`.
|
|
103
|
+
|
|
104
|
+
**The answer is written out**, word by word, with a soft cursor, in 2 seconds at most (long answers
|
|
105
|
+
go faster). It is laid out whole first, so markdown and code blocks end exactly as if they had
|
|
106
|
+
appeared at once, and scrolling up while it writes does not move what you are reading. Any key shows
|
|
107
|
+
the rest at once. `/animation off` (saved) or `--no-animation` shows answers whole; without a
|
|
108
|
+
terminal (a pipe, CI), in `--lanes` and in `/report` they are always whole.
|
|
100
109
|
|
|
101
110
|
Type `/` for the commands; the menu filters as you type, ↑ ↓ move, Tab or Enter pick, Esc closes.
|
|
102
111
|
|
|
@@ -110,10 +119,12 @@ Type `/` for the commands; the menu filters as you type, ↑ ↓ move, Tab or En
|
|
|
110
119
|
| `/inbox` | what your missions are waiting for (approvals, proposed changes, your steps), as buttons; what you pick takes effect like on the web |
|
|
111
120
|
| `/report` | a plain-language summary of what was done, copied to the clipboard for an email or WhatsApp |
|
|
112
121
|
| `/teach` | class mode: explains each step as it codes, like a teacher |
|
|
113
|
-
| `/
|
|
122
|
+
| `/new` | a clean conversation, as if you had just opened parx: the screen, the terminal scrollback, the queue and what the agent remembers are cleared (mode, model and sign-in stay). If a task is running it asks «Stop and start new?» first |
|
|
123
|
+
| `/undo`, `/balance`, `/log`, `/help`, `/exit` | the usual |
|
|
114
124
|
| `/notify` on, off | desktop notifications |
|
|
115
125
|
| `/tips` on, off | the welcome tips |
|
|
116
126
|
| `/mouse` on, off | clicks and wheel inside parx (saved; `--no-mouse` for one session) |
|
|
127
|
+
| `/animation` on, off | answers written out as they arrive, or whole (saved; `--no-animation` for one session) |
|
|
117
128
|
|
|
118
129
|
**Bash mode:** start the input with `!` and it turns amber with a small `bash` label; Enter runs the
|
|
119
130
|
command in your shell, in the project folder, and its output streams into the conversation with the
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { capacidades, c } from "./marca.js";
|
|
2
|
+
import { t } from "./textos.js";
|
|
3
|
+
import { anchoDeCaracter, sinColor } from "./texto.js";
|
|
4
|
+
export function elipsis() {
|
|
5
|
+
return capacidades().unicode ? '…' : '...';
|
|
6
|
+
}
|
|
7
|
+
export function conElipsis(texto) {
|
|
8
|
+
const s = texto.trimEnd();
|
|
9
|
+
if (!s || /(…|\.\.\.|[.!?:])$/.test(s))
|
|
10
|
+
return s;
|
|
11
|
+
return `${s}${elipsis()}`;
|
|
12
|
+
}
|
|
13
|
+
const AL_EMPEZAR = [
|
|
14
|
+
[0, 'trabajo.pensando'],
|
|
15
|
+
[6_000, 'trabajo.planear'],
|
|
16
|
+
[15_000, 'trabajo.sigue'],
|
|
17
|
+
[35_000, 'trabajo.servidor'],
|
|
18
|
+
];
|
|
19
|
+
const TRAS_HERRAMIENTAS = [
|
|
20
|
+
[0, 'trabajo.pensando'],
|
|
21
|
+
[5_000, 'trabajo.resultados'],
|
|
22
|
+
[11_000, 'trabajo.siguiente'],
|
|
23
|
+
[20_000, 'trabajo.sigue'],
|
|
24
|
+
[40_000, 'trabajo.servidor'],
|
|
25
|
+
];
|
|
26
|
+
export function fraseDeEspera(ms, trasHerramientas = false) {
|
|
27
|
+
const tabla = trasHerramientas ? TRAS_HERRAMIENTAS : AL_EMPEZAR;
|
|
28
|
+
let clave = tabla[0][1];
|
|
29
|
+
for (const [desde, k] of tabla)
|
|
30
|
+
if (Math.max(0, ms) >= desde)
|
|
31
|
+
clave = k;
|
|
32
|
+
return conElipsis(t(clave));
|
|
33
|
+
}
|
|
34
|
+
export const CARACTERES_POR_SEGUNDO = 110;
|
|
35
|
+
export const MS_MAX_RESPUESTA = 2000;
|
|
36
|
+
export const MS_MAX_INTERMEDIO = 900;
|
|
37
|
+
export const MS_POR_FOTOGRAMA = 33;
|
|
38
|
+
export function duracionDeRevelado(caracteres, maxMs = MS_MAX_RESPUESTA) {
|
|
39
|
+
if (caracteres <= 0)
|
|
40
|
+
return 0;
|
|
41
|
+
return Math.round(Math.min(maxMs, Math.max(160, (caracteres / CARACTERES_POR_SEGUNDO) * 1000)));
|
|
42
|
+
}
|
|
43
|
+
export function caracteresVisibles(total, ms, duracion) {
|
|
44
|
+
if (duracion <= 0 || ms >= duracion)
|
|
45
|
+
return total;
|
|
46
|
+
return Math.max(0, Math.min(total, Math.floor((total * Math.max(0, ms)) / duracion)));
|
|
47
|
+
}
|
|
48
|
+
export function cursorEncendido(ms) {
|
|
49
|
+
return Math.floor(Math.max(0, ms) / 500) % 2 === 0;
|
|
50
|
+
}
|
|
51
|
+
export function simboloDeCursor() {
|
|
52
|
+
return c.violeta(capacidades().unicode ? '▍' : '_');
|
|
53
|
+
}
|
|
54
|
+
export function caracteresDe(lineas) {
|
|
55
|
+
let n = 0;
|
|
56
|
+
for (const l of lineas)
|
|
57
|
+
n += anchoVisible(l);
|
|
58
|
+
return n;
|
|
59
|
+
}
|
|
60
|
+
function anchoVisible(l) {
|
|
61
|
+
let n = 0;
|
|
62
|
+
for (const ch of sinColor(l))
|
|
63
|
+
n += anchoDeCaracter(ch);
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
const ESCAPE = /\x1b\[[0-9;?]*[ -/]*[@-~]/y;
|
|
67
|
+
export function cortarVisible(linea, n) {
|
|
68
|
+
if (n <= 0)
|
|
69
|
+
return '';
|
|
70
|
+
let out = '';
|
|
71
|
+
let w = 0;
|
|
72
|
+
let color = false;
|
|
73
|
+
let i = 0;
|
|
74
|
+
while (i < linea.length) {
|
|
75
|
+
if (linea[i] === '\x1b') {
|
|
76
|
+
ESCAPE.lastIndex = i;
|
|
77
|
+
const m = ESCAPE.exec(linea);
|
|
78
|
+
if (m) {
|
|
79
|
+
out += m[0];
|
|
80
|
+
color = true;
|
|
81
|
+
i += m[0].length;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const ch = String.fromCodePoint(linea.codePointAt(i));
|
|
86
|
+
const cw = anchoDeCaracter(ch);
|
|
87
|
+
if (w + cw > n)
|
|
88
|
+
return `${out}${color ? '\x1b[0m' : ''}`;
|
|
89
|
+
out += ch;
|
|
90
|
+
w += cw;
|
|
91
|
+
i += ch.length;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
export function revelarLineas(lineas, n, cursor, ancho = Infinity) {
|
|
96
|
+
const total = caracteresDe(lineas);
|
|
97
|
+
if (n >= total)
|
|
98
|
+
return cursor ? conCursor([...lineas], cursor, ancho) : [...lineas];
|
|
99
|
+
const out = [];
|
|
100
|
+
let quedan = Math.max(0, n);
|
|
101
|
+
for (const l of lineas) {
|
|
102
|
+
const w = anchoVisible(l);
|
|
103
|
+
if (quedan >= w) {
|
|
104
|
+
out.push(l);
|
|
105
|
+
quedan -= w;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const plano = Array.from(sinColor(l));
|
|
109
|
+
let cols = 0;
|
|
110
|
+
let k = 0;
|
|
111
|
+
while (k < plano.length && cols < quedan)
|
|
112
|
+
cols += anchoDeCaracter(plano[k++]);
|
|
113
|
+
while (k < plano.length && plano[k] !== ' ')
|
|
114
|
+
cols += anchoDeCaracter(plano[k++]);
|
|
115
|
+
const parte = cortarVisible(l, cols);
|
|
116
|
+
out.push(sinColor(parte).trim() ? parte : '');
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
while (out.length > 1 && out[out.length - 1] === '' && lineas[out.length - 1] !== '')
|
|
120
|
+
out.pop();
|
|
121
|
+
return cursor ? conCursor(out, cursor, ancho) : out;
|
|
122
|
+
}
|
|
123
|
+
function conCursor(lineas, cursor, ancho) {
|
|
124
|
+
if (!lineas.length)
|
|
125
|
+
return [cursor];
|
|
126
|
+
const i = lineas.length - 1;
|
|
127
|
+
const l = lineas[i];
|
|
128
|
+
if (anchoVisible(l) + 1 < ancho)
|
|
129
|
+
lineas[i] = `${l.replace(/\s+$/, '')}${anchoVisible(l) ? ' ' : ''}${cursor}`;
|
|
130
|
+
return lineas;
|
|
131
|
+
}
|
package/dist/agente.js
CHANGED
|
@@ -153,29 +153,31 @@ export async function hacerTarea(s, pedido, señal, imagenes = []) {
|
|
|
153
153
|
disponibles = r.acciones.disponibles;
|
|
154
154
|
pantalla.progreso(acciones);
|
|
155
155
|
registro.apuntar('turno', { tarea, vuelta, acciones });
|
|
156
|
-
const llamadas =
|
|
156
|
+
const llamadas = r.salida.filter((i) => i.type === 'function_call');
|
|
157
157
|
for (const item of r.salida) {
|
|
158
158
|
s.conversacion.push(item);
|
|
159
159
|
if (item.type === 'message') {
|
|
160
160
|
const texto = textoDeMensaje(item).trim();
|
|
161
161
|
if (texto) {
|
|
162
162
|
respuesta = texto;
|
|
163
|
+
pantalla.espera(t('hace.respuesta'), 'tendido', true);
|
|
163
164
|
pantalla.escribir();
|
|
164
|
-
pantalla.
|
|
165
|
+
await pantalla.textoAnimado(texto, { intermedio: llamadas.length > 0 });
|
|
165
166
|
pantalla.escribir();
|
|
167
|
+
pantalla.espera(null);
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
|
-
else if (item.type === 'function_call')
|
|
169
|
-
llamadas.push(item);
|
|
170
170
|
}
|
|
171
171
|
if (!llamadas.length)
|
|
172
172
|
break;
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
for (const ll of llamadas) {
|
|
173
|
+
const todas = llamadas.map((l) => ({ name: String(l.name ?? ''), arguments: String(l.arguments ?? '{}') }));
|
|
174
|
+
for (let i = 0; i < llamadas.length; i++) {
|
|
175
|
+
const ll = llamadas[i];
|
|
177
176
|
if (señal.aborted)
|
|
178
177
|
throw new ErrorDeParallel('interrumpido');
|
|
178
|
+
const etiqueta = etiquetaDeTanda(grupoDesde(todas, i));
|
|
179
|
+
if (etiqueta && empiezaGrupo(todas, i))
|
|
180
|
+
pantalla.espera(etiqueta.texto, etiqueta.anim, false);
|
|
179
181
|
const salida = await ejecutarHerramienta(String(ll.name ?? ''), String(ll.arguments ?? '{}'), s.ctx);
|
|
180
182
|
s.conversacion.push({ type: 'function_call_output', call_id: ll.call_id, output: salida });
|
|
181
183
|
}
|
|
@@ -209,6 +211,31 @@ export async function hacerTarea(s, pedido, señal, imagenes = []) {
|
|
|
209
211
|
pantalla.cierre(`${t('estado.hecho')}${sep}${c.gris(textoDeAcciones(acciones))}${sep}${c.gris(duracion(ms))}`);
|
|
210
212
|
return true;
|
|
211
213
|
}
|
|
214
|
+
function familia(nombre) {
|
|
215
|
+
if (nombre === 'write_file' || nombre === 'edit_file')
|
|
216
|
+
return 'cambio';
|
|
217
|
+
if (nombre === 'read_file' || nombre === 'search' || nombre === 'list_files')
|
|
218
|
+
return nombre;
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
export function grupoDesde(llamadas, i) {
|
|
222
|
+
const actual = llamadas[i];
|
|
223
|
+
if (!actual)
|
|
224
|
+
return [];
|
|
225
|
+
const f = familia(actual.name);
|
|
226
|
+
if (!f)
|
|
227
|
+
return [actual];
|
|
228
|
+
let j = i;
|
|
229
|
+
while (j < llamadas.length && familia(llamadas[j].name) === f)
|
|
230
|
+
j++;
|
|
231
|
+
return llamadas.slice(i, j);
|
|
232
|
+
}
|
|
233
|
+
export function empiezaGrupo(llamadas, i) {
|
|
234
|
+
if (i === 0)
|
|
235
|
+
return true;
|
|
236
|
+
const f = familia(llamadas[i].name);
|
|
237
|
+
return !f || f !== familia(llamadas[i - 1].name);
|
|
238
|
+
}
|
|
212
239
|
function cerrarLlamadasPendientes(items) {
|
|
213
240
|
const respondidas = new Set(items.filter((i) => i.type === 'function_call_output').map((i) => i.call_id));
|
|
214
241
|
for (const i of [...items]) {
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { basename, resolve } from 'node:path';
|
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
7
|
import { randomUUID } from 'node:crypto';
|
|
8
8
|
import { ALIAS_COMANDO, ALIAS_SI_NO, COMANDOS, t, USO } from "./textos.js";
|
|
9
|
-
import { c, fijarCapacidades, sep, signo, SIMBOLOS } from "./marca.js";
|
|
9
|
+
import { c, colorDeModo, fijarCapacidades, sep, signo, SIMBOLOS } from "./marca.js";
|
|
10
10
|
import { guardarAjustes, leerAjustes, migrarCarpetaVieja, VERSION } from "./config.js";
|
|
11
11
|
import { MODELO_POR_DEFECTO, MODELOS, modeloDe, nombreDeModelo } from "./modelos.js";
|
|
12
12
|
import { correoVisible, crearMision, ErrorDeParallel, leerBandeja, leerCuenta, quienEs, responderBandeja, verMision } from "./api.js";
|
|
@@ -43,6 +43,8 @@ function opciones() {
|
|
|
43
43
|
ascii: { type: 'boolean' },
|
|
44
44
|
mouse: { type: 'boolean' },
|
|
45
45
|
'no-mouse': { type: 'boolean' },
|
|
46
|
+
'no-animation': { type: 'boolean' },
|
|
47
|
+
animation: { type: 'boolean' },
|
|
46
48
|
version: { type: 'boolean', short: 'v' },
|
|
47
49
|
help: { type: 'boolean', short: 'h' },
|
|
48
50
|
browser: { type: 'boolean' },
|
|
@@ -85,8 +87,12 @@ export function modoDe(texto) {
|
|
|
85
87
|
return tabla[v] ?? null;
|
|
86
88
|
}
|
|
87
89
|
export const SIGUIENTE_MODO = { ask: 'auto', auto: 'read', read: 'ask', bypass: 'ask' };
|
|
90
|
+
export function modosEnColor() {
|
|
91
|
+
const nombres = ['read', 'ask', 'auto', 'bypass'].map((m) => colorDeModo(m)(nombreDeModo(m)));
|
|
92
|
+
return `${nombres.slice(0, 3).join(', ')} or ${nombres[3]}`;
|
|
93
|
+
}
|
|
88
94
|
function ayudaDeComandos() {
|
|
89
|
-
return bloqueDeTabla([['!', t('tip.bash')], ...COMANDOS.map(([k, d]) => [`/${k}`, d])], { pie: `${t('tui.atajos')}\n${t('tui.atajos_raton')}` });
|
|
95
|
+
return bloqueDeTabla([['!', t('tip.bash')], ...COMANDOS.map(([k, d]) => [`/${k}`, k === 'mode' ? modosEnColor() : d])], { pie: `${t('tui.atajos')}\n${t('tui.atajos_raton')}` });
|
|
90
96
|
}
|
|
91
97
|
async function cuentaOError(cred, pantalla) {
|
|
92
98
|
try {
|
|
@@ -258,10 +264,40 @@ async function principal() {
|
|
|
258
264
|
const quien = quienEs(cuenta);
|
|
259
265
|
let avisos = ajustes.avisos !== false;
|
|
260
266
|
const conRaton = !v['no-mouse'] && v.mouse !== false && (Boolean(v.mouse) || ajustes.raton !== false);
|
|
267
|
+
pantalla.animar = !v['no-animation'] && v.animation !== false && (Boolean(v.animation) || ajustes.animacion !== false);
|
|
261
268
|
let esperandoEnBandeja = null;
|
|
262
269
|
let control = new AbortController();
|
|
263
270
|
let ocupado = false;
|
|
264
271
|
let superficie = null;
|
|
272
|
+
let bienvenida;
|
|
273
|
+
let consejosDeEntrada = [];
|
|
274
|
+
const confirmarNuevaEnMarcha = async () => {
|
|
275
|
+
if (!superficie)
|
|
276
|
+
return;
|
|
277
|
+
const i = await superficie.elegir(t('nueva.parar'), [{ etiqueta: t('nueva.si'), peligro: true }, { etiqueta: t('nueva.no') }], { detalle: t('nueva.parar_detalle'), elegida: 1 });
|
|
278
|
+
if (i !== 0)
|
|
279
|
+
return;
|
|
280
|
+
superficie.nuevaTrasParar();
|
|
281
|
+
if (ocupado)
|
|
282
|
+
control.abort();
|
|
283
|
+
};
|
|
284
|
+
const empezarDeNuevo = () => {
|
|
285
|
+
sesion.conversacion = [];
|
|
286
|
+
sesion.diario = [];
|
|
287
|
+
sesion.id = randomUUID();
|
|
288
|
+
sesion.ultimaTarea = undefined;
|
|
289
|
+
sesion.ctx.hechos = { ficheros: [], comandos: [] };
|
|
290
|
+
sesion.ctx.permitidos.clear();
|
|
291
|
+
sesion.ctx.deshacer.length = 0;
|
|
292
|
+
if (sesion.ctx.lecturas)
|
|
293
|
+
sesion.ctx.lecturas.length = 0;
|
|
294
|
+
registro.apuntar('nueva', {});
|
|
295
|
+
pantalla.reiniciar();
|
|
296
|
+
if (superficie)
|
|
297
|
+
superficie.reiniciar(bienvenida, consejosDeEntrada);
|
|
298
|
+
else
|
|
299
|
+
pantalla.escribir(t('repl.nueva'));
|
|
300
|
+
};
|
|
265
301
|
let ctrlC = 0;
|
|
266
302
|
const totalDeLaSesion = () => sesion.total.tareas ? c.gris(t('estado.total', { acciones: acciones(sesion.total.acciones), tiempo: duracion(sesion.total.ms) })) : '';
|
|
267
303
|
const despedida = () => [totalDeLaSesion(), c.gris(t('repl.adios'))].filter(Boolean).join('\n ');
|
|
@@ -393,6 +429,7 @@ async function principal() {
|
|
|
393
429
|
saldo: saldo(),
|
|
394
430
|
saldoCorto: saldoCorto(),
|
|
395
431
|
peligro: sesion.ctx.modo === 'bypass',
|
|
432
|
+
clave: sesion.ctx.modo,
|
|
396
433
|
extra: sesion.ensenar ? 'Teach' : undefined,
|
|
397
434
|
esperando: esperandoEnBandeja ?? 0,
|
|
398
435
|
});
|
|
@@ -414,6 +451,7 @@ async function principal() {
|
|
|
414
451
|
if (avisos && superficie?.enfocada === false)
|
|
415
452
|
avisarEnElEscritorio({ titulo: 'parx', texto: t('aviso.te_necesita', { pregunta: texto }) });
|
|
416
453
|
},
|
|
454
|
+
alNuevaEnMarcha: () => void confirmarNuevaEnMarcha(),
|
|
417
455
|
});
|
|
418
456
|
const s = superficie;
|
|
419
457
|
process.on('exit', () => s.restaurarDeUrgencia());
|
|
@@ -432,7 +470,8 @@ async function principal() {
|
|
|
432
470
|
const elegidos = elegirConsejos(leerAjustes().consejos);
|
|
433
471
|
if (elegidos.ids.length)
|
|
434
472
|
guardarAjustes({ consejos: elegidos.estado });
|
|
435
|
-
|
|
473
|
+
consejosDeEntrada = conCarriles || quiereBypass ? [] : elegidos.ids;
|
|
474
|
+
bienvenida = bloqueDeBienvenida({
|
|
436
475
|
version: VERSION,
|
|
437
476
|
quien: quien.includes('@') ? quien : (quien.split(/\s+/)[0] ?? ''),
|
|
438
477
|
carpeta: rutaCorta(raiz, 60),
|
|
@@ -442,7 +481,8 @@ async function principal() {
|
|
|
442
481
|
return nueva ? t('update.disponible', { version: nueva }) : undefined;
|
|
443
482
|
})(),
|
|
444
483
|
peligro: () => sesion.ctx.modo === 'bypass',
|
|
445
|
-
})
|
|
484
|
+
});
|
|
485
|
+
superficie.iniciar(bienvenida, consejosDeEntrada);
|
|
446
486
|
if (quiereBypass)
|
|
447
487
|
await confirmarBypass();
|
|
448
488
|
const mirarBandeja = async () => {
|
|
@@ -662,7 +702,7 @@ async function principal() {
|
|
|
662
702
|
guardarAjustes({ modo: m });
|
|
663
703
|
registro.apuntar('modo', { modo: m });
|
|
664
704
|
}
|
|
665
|
-
pantalla.escribir(t('modo.ahora', { modo: nombreDeModo(sesion.ctx.modo), desc: t(`modo.${sesion.ctx.modo}.desc`) }));
|
|
705
|
+
pantalla.escribir(t('modo.ahora', { modo: colorDeModo(sesion.ctx.modo)(c.negrita(nombreDeModo(sesion.ctx.modo))), desc: t(`modo.${sesion.ctx.modo}.desc`) }));
|
|
666
706
|
superficie?.pintar();
|
|
667
707
|
break;
|
|
668
708
|
}
|
|
@@ -814,9 +854,15 @@ async function principal() {
|
|
|
814
854
|
break;
|
|
815
855
|
}
|
|
816
856
|
case 'new':
|
|
817
|
-
|
|
818
|
-
|
|
857
|
+
empezarDeNuevo();
|
|
858
|
+
break;
|
|
859
|
+
case 'animation': {
|
|
860
|
+
const si = ALIAS_SI_NO[arg] ?? (pantalla.animar ? 'no' : 'si');
|
|
861
|
+
pantalla.animar = si === 'si';
|
|
862
|
+
guardarAjustes({ animacion: pantalla.animar });
|
|
863
|
+
pantalla.escribir(pantalla.animar ? t('animacion.on') : t('animacion.off'));
|
|
819
864
|
break;
|
|
865
|
+
}
|
|
820
866
|
case 'undo': {
|
|
821
867
|
const r2 = deshacerUltimo(sesion.ctx);
|
|
822
868
|
pantalla.escribir(r2 ? t('repl.deshecho', { ruta: r2 }) : t('repl.nada_que_deshacer'));
|
package/dist/config.js
CHANGED
|
@@ -92,6 +92,10 @@ export function leerAjustes(dir = carpetaDeConfiguracion()) {
|
|
|
92
92
|
out.modo = MODO_VIEJO[a.modo];
|
|
93
93
|
if (typeof a.avisos === 'boolean')
|
|
94
94
|
out.avisos = a.avisos;
|
|
95
|
+
if (typeof a.raton === 'boolean')
|
|
96
|
+
out.raton = a.raton;
|
|
97
|
+
if (typeof a.animacion === 'boolean')
|
|
98
|
+
out.animacion = a.animacion;
|
|
95
99
|
const k = a.consejos;
|
|
96
100
|
if (k && typeof k === 'object') {
|
|
97
101
|
const n = (x) => (typeof x === 'number' && Number.isFinite(x) && x >= 0 ? Math.floor(x) : 0);
|
package/dist/herramientas.js
CHANGED
|
@@ -3,6 +3,7 @@ import { dirname, join, relative } from 'node:path';
|
|
|
3
3
|
import { contarCambios, diffDeLineas } from "./diff.js";
|
|
4
4
|
import { cuantos, t } from "./textos.js";
|
|
5
5
|
import { c } from "./marca.js";
|
|
6
|
+
import { elipsis } from "./actividad.js";
|
|
6
7
|
import { estaDentro, familiaDeComando, nivelDeComando, resolverRuta, rutaReal } from "./seguridad.js";
|
|
7
8
|
import { ejecutarComando, recortar } from "./shell.js";
|
|
8
9
|
const nulable = (tipo, descripcion) => ({ type: [tipo, 'null'], description: descripcion });
|
|
@@ -211,7 +212,15 @@ export function etiquetaDeComando(comando) {
|
|
|
211
212
|
return t('hace.instalar');
|
|
212
213
|
const primera = c1.split(/\s*(?:&&|\|\||;|\|)\s*/)[0] ?? c1;
|
|
213
214
|
const corto = primera.split(/\s+/).filter((p) => !/^[A-Z_]+=/.test(p)).slice(0, 3).join(' ');
|
|
214
|
-
return t('hace.ejecutar', { comando: corto.length > 28 ? `${corto.slice(0, 27)}
|
|
215
|
+
return t('hace.ejecutar', { comando: corto.length > 28 ? `${corto.slice(0, 27)}${elipsis()}` : corto });
|
|
216
|
+
}
|
|
217
|
+
export function rutaParaLaBarra(ruta) {
|
|
218
|
+
const r = ruta.trim().replace(/^\.\/(?=.)/, '') || '.';
|
|
219
|
+
if (r.length <= 30)
|
|
220
|
+
return r;
|
|
221
|
+
const partes = r.split(/[\\/]/).filter(Boolean);
|
|
222
|
+
const cola = partes.slice(-2).join('/');
|
|
223
|
+
return cola.length <= 30 ? `${elipsis()}/${cola}` : `${elipsis()}/${partes[partes.length - 1]}`;
|
|
215
224
|
}
|
|
216
225
|
export function etiquetaDeTanda(llamadas) {
|
|
217
226
|
if (!llamadas.length)
|
|
@@ -225,7 +234,7 @@ export function etiquetaDeTanda(llamadas) {
|
|
|
225
234
|
}
|
|
226
235
|
});
|
|
227
236
|
const nombres = llamadas.map((l) => l.name);
|
|
228
|
-
const base = (p) => String(p ?? '.')
|
|
237
|
+
const base = (p) => rutaParaLaBarra(String(p ?? '.'));
|
|
229
238
|
const cmd = nombres.indexOf('run_command');
|
|
230
239
|
if (cmd !== -1)
|
|
231
240
|
return { texto: etiquetaDeComando(String(args[cmd].command ?? '')), anim: 'tren' };
|
|
@@ -244,9 +253,14 @@ export function etiquetaDeTanda(llamadas) {
|
|
|
244
253
|
const b = nombres.indexOf('search');
|
|
245
254
|
if (b !== -1) {
|
|
246
255
|
const patron = String(args[b].pattern ?? '');
|
|
247
|
-
|
|
256
|
+
if (!patron.trim())
|
|
257
|
+
return { texto: t('hace.buscar_ficheros'), anim: 'lectura' };
|
|
258
|
+
return { texto: t('hace.buscar', { patron: patron.length > 24 ? `${patron.slice(0, 23)}${elipsis()}` : patron }), anim: 'lectura' };
|
|
248
259
|
}
|
|
249
|
-
|
|
260
|
+
const l = nombres.indexOf('list_files');
|
|
261
|
+
if (l !== -1)
|
|
262
|
+
return { texto: t('hace.listar', { ruta: base(args[l].path ?? '.') }), anim: 'lectura' };
|
|
263
|
+
return { texto: t('hace.generico'), anim: 'ruta' };
|
|
250
264
|
}
|
|
251
265
|
export async function ejecutarHerramienta(nombre, argumentos, ctx) {
|
|
252
266
|
let a;
|
package/dist/marca.js
CHANGED
|
@@ -40,6 +40,10 @@ const PALETA = {
|
|
|
40
40
|
verdeDiff: [80, 200, 120],
|
|
41
41
|
rojoDiff: [240, 100, 100],
|
|
42
42
|
peligro: [255, 69, 58],
|
|
43
|
+
modoRead: [29, 161, 181],
|
|
44
|
+
modoAsk: [139, 112, 255],
|
|
45
|
+
modoAuto: [242, 107, 29],
|
|
46
|
+
bash: [245, 166, 35],
|
|
43
47
|
};
|
|
44
48
|
const ANSI256 = {
|
|
45
49
|
violeta: 99,
|
|
@@ -52,6 +56,10 @@ const ANSI256 = {
|
|
|
52
56
|
verdeDiff: 78,
|
|
53
57
|
rojoDiff: 203,
|
|
54
58
|
peligro: 196,
|
|
59
|
+
modoRead: 37,
|
|
60
|
+
modoAsk: 99,
|
|
61
|
+
modoAuto: 202,
|
|
62
|
+
bash: 214,
|
|
55
63
|
};
|
|
56
64
|
const ANSI16 = {
|
|
57
65
|
violeta: 35,
|
|
@@ -64,6 +72,10 @@ const ANSI16 = {
|
|
|
64
72
|
verdeDiff: 32,
|
|
65
73
|
rojoDiff: 31,
|
|
66
74
|
peligro: 91,
|
|
75
|
+
modoRead: 36,
|
|
76
|
+
modoAsk: 35,
|
|
77
|
+
modoAuto: 33,
|
|
78
|
+
bash: 93,
|
|
67
79
|
};
|
|
68
80
|
function pintar(nombre, texto) {
|
|
69
81
|
const { color } = caps;
|
|
@@ -88,10 +100,16 @@ export const c = {
|
|
|
88
100
|
mas: (t) => pintar('verdeDiff', t),
|
|
89
101
|
menos: (t) => pintar('rojoDiff', t),
|
|
90
102
|
peligro: (t) => pintar('peligro', t),
|
|
103
|
+
bash: (t) => pintar('bash', t),
|
|
91
104
|
negrita: (t) => (caps.color ? `\x1b[1m${t}\x1b[22m` : t),
|
|
92
105
|
tenue: (t) => (caps.color ? `\x1b[2m${t}\x1b[22m` : t),
|
|
93
106
|
boton: (t, peligro = false) => fondo(peligro ? 'peligro' : 'violeta', t),
|
|
94
107
|
};
|
|
108
|
+
const TINTA_DE_MODO = { read: 'modoRead', ask: 'modoAsk', auto: 'modoAuto', bypass: 'peligro' };
|
|
109
|
+
export function colorDeModo(modo) {
|
|
110
|
+
const nombre = TINTA_DE_MODO[modo];
|
|
111
|
+
return (t) => pintar(nombre, t);
|
|
112
|
+
}
|
|
95
113
|
function fondo(nombre, texto) {
|
|
96
114
|
const { color } = caps;
|
|
97
115
|
if (color === 0)
|
package/dist/textos.js
CHANGED
|
@@ -19,7 +19,7 @@ export const TEXTOS = {
|
|
|
19
19
|
'tui.si_ediciones': 'Yes, allow all edits',
|
|
20
20
|
'tui.si_ediciones_corto': 'All edits',
|
|
21
21
|
'tui.una_vez': 'Yes, this once',
|
|
22
|
-
'tui.atajos': 'Shift+Tab mode · ↑ ↓ history · ! runs a shell command · Esc stops a task · Ctrl+C twice exits · Ctrl+L redraws',
|
|
22
|
+
'tui.atajos': 'Shift+Tab mode · ↑ ↓ history · ! runs a shell command · Esc stops a task · any key shows the whole answer · Ctrl+C twice exits · Ctrl+L redraws',
|
|
23
23
|
'tui.atajos_raton': 'Click any option or button · wheel or PgUp/PgDn scrolls · End jumps to the bottom · Shift+drag selects text (Option in iTerm2, Fn in Terminal) · /mouse off gives the mouse back to the terminal',
|
|
24
24
|
'tui.al_fondo': 'Jump to bottom',
|
|
25
25
|
'tui.nuevas': '{n} new',
|
|
@@ -76,21 +76,22 @@ export const TEXTOS = {
|
|
|
76
76
|
'carpeta.lista': 'Allowed folders: {lista}',
|
|
77
77
|
'carpeta.ninguna': 'Only the project folder. Add one with /folder <path>.',
|
|
78
78
|
'carpeta.no_existe': 'That folder does not exist: {ruta}',
|
|
79
|
-
'trabajo.
|
|
80
|
-
'trabajo.
|
|
81
|
-
'trabajo.
|
|
82
|
-
'trabajo.
|
|
83
|
-
'trabajo.
|
|
84
|
-
'trabajo.
|
|
85
|
-
'
|
|
86
|
-
'
|
|
79
|
+
'trabajo.pensando': 'Thinking',
|
|
80
|
+
'trabajo.planear': 'Planning the steps',
|
|
81
|
+
'trabajo.resultados': 'Looking at the results',
|
|
82
|
+
'trabajo.siguiente': 'Deciding the next step',
|
|
83
|
+
'trabajo.sigue': 'Still thinking',
|
|
84
|
+
'trabajo.servidor': 'Waiting for the server',
|
|
85
|
+
'hace.respuesta': 'Writing the answer',
|
|
86
|
+
'hace.generico': 'Working',
|
|
87
|
+
'hace.buscar_ficheros': 'Searching files',
|
|
87
88
|
'hace.leer': 'Reading {n} files',
|
|
88
89
|
'hace.leer_uno': 'Reading {ruta}',
|
|
89
90
|
'hace.listar': 'Looking around {ruta}',
|
|
90
91
|
'hace.buscar': 'Searching for {patron}',
|
|
91
92
|
'hace.escribir': 'Writing {ruta}',
|
|
92
93
|
'hace.editar': 'Editing {ruta}',
|
|
93
|
-
'hace.cambios': '
|
|
94
|
+
'hace.cambios': 'Editing {n} files',
|
|
94
95
|
'hace.tests': 'Running tests',
|
|
95
96
|
'hace.build': 'Building',
|
|
96
97
|
'hace.instalar': 'Installing',
|
|
@@ -181,6 +182,12 @@ export const TEXTOS = {
|
|
|
181
182
|
'repl.adios': 'See you.',
|
|
182
183
|
'repl.registro': 'Log of this session: {ruta}',
|
|
183
184
|
'repl.nueva': 'New conversation.',
|
|
185
|
+
'nueva.parar': 'Stop and start new?',
|
|
186
|
+
'nueva.parar_detalle': 'parx is working. This stops it and clears the conversation.',
|
|
187
|
+
'nueva.si': 'Stop and start new',
|
|
188
|
+
'nueva.no': 'Keep working',
|
|
189
|
+
'animacion.on': 'Answers are written out as they arrive. Any key shows the rest at once.',
|
|
190
|
+
'animacion.off': 'Answers appear whole, at once. /animation on to turn it back on.',
|
|
184
191
|
'repl.desconocido': 'Unknown command. Type /help.',
|
|
185
192
|
'repl.deshecho': 'Undone: {ruta}',
|
|
186
193
|
'repl.nada_que_deshacer': 'Nothing to undo.',
|
|
@@ -261,6 +268,7 @@ export const COMANDOS = [
|
|
|
261
268
|
['notify', 'Desktop notifications: on or off'],
|
|
262
269
|
['tips', 'Tips when it opens: on or off'],
|
|
263
270
|
['mouse', 'Clicks and wheel in parx: on or off'],
|
|
271
|
+
['animation', 'Answers written out as they arrive: on or off'],
|
|
264
272
|
['log', 'Where the log of what it does is kept'],
|
|
265
273
|
['exit', 'Leave parx'],
|
|
266
274
|
];
|
|
@@ -273,6 +281,7 @@ export const ALIAS_COMANDO = {
|
|
|
273
281
|
mission: 'mission', mision: 'mission', misión: 'mission',
|
|
274
282
|
inbox: 'inbox', bandeja: 'inbox', approvals: 'inbox', waiting: 'inbox',
|
|
275
283
|
mouse: 'mouse', raton: 'mouse', ratón: 'mouse',
|
|
284
|
+
animation: 'animation', animations: 'animation', animacion: 'animation', animación: 'animation',
|
|
276
285
|
report: 'report', informe: 'report',
|
|
277
286
|
teach: 'teach', class: 'teach', clase: 'teach',
|
|
278
287
|
undo: 'undo', deshacer: 'undo',
|
|
@@ -304,6 +313,7 @@ export const USO = [
|
|
|
304
313
|
' --dir <folder> the project folder (default: current)',
|
|
305
314
|
' --ascii plain characters only (also NO_COLOR for no color)',
|
|
306
315
|
' --no-mouse leave the mouse to the terminal (no clicks or wheel in parx)',
|
|
316
|
+
' --no-animation show answers whole, without writing them out',
|
|
307
317
|
' --version, --help',
|
|
308
318
|
'',
|
|
309
319
|
'Sign in:',
|
package/dist/tui.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { animacion, c, capacidades, sep, cabezaDePar, caraDePar, carril, fijarCapacidades, signo, ANCHO_DE_PAR, SIMBOLOS } from "./marca.js";
|
|
1
|
+
import { animacion, c, capacidades, colorDeModo, sep, cabezaDePar, caraDePar, carril, fijarCapacidades, signo, ANCHO_DE_PAR, SIMBOLOS } from "./marca.js";
|
|
2
2
|
import { anchoDe, envolver, puntos, recortar, rellenar } from "./texto.js";
|
|
3
3
|
import { ALIAS_COMANDO, COMANDOS, t as tt } from "./textos.js";
|
|
4
4
|
import { MODELOS, ORDEN_DE_MODELOS } from "./modelos.js";
|
|
5
5
|
import { consejo, ordenDeConsejos } from "./consejos.js";
|
|
6
6
|
export { anchoDe } from "./texto.js";
|
|
7
|
+
import { conElipsis, fraseDeEspera } from "./actividad.js";
|
|
7
8
|
export const MARGEN = 2;
|
|
8
9
|
export function anchoUtil(ancho) {
|
|
9
10
|
return Math.max(10, ancho - MARGEN * 2);
|
|
@@ -138,13 +139,13 @@ export function bloqueDeCierre(texto, marca) {
|
|
|
138
139
|
export function bloqueDeBash(comando) {
|
|
139
140
|
return (ancho) => {
|
|
140
141
|
const util = anchoUtil(ancho);
|
|
141
|
-
return ['', ...envolver(comando, util - 2).map((l, i) => `${m}${i === 0 ? c.
|
|
142
|
+
return ['', ...envolver(comando, util - 2).map((l, i) => `${m}${i === 0 ? c.bash(c.negrita('!')) : ' '} ${l}`)];
|
|
142
143
|
};
|
|
143
144
|
}
|
|
144
145
|
export function bloqueDeSalidaDeBash(lineas) {
|
|
145
146
|
return (ancho) => {
|
|
146
147
|
const util = anchoUtil(ancho);
|
|
147
|
-
const raya = c.tenue(c.
|
|
148
|
+
const raya = c.tenue(c.bash(capacidades().unicode ? '│' : '|'));
|
|
148
149
|
return lineas.map((l) => `${m}${raya} ${recortar(l.replace(/\t/g, ' '), util - 2)}`);
|
|
149
150
|
};
|
|
150
151
|
}
|
|
@@ -152,7 +153,7 @@ export function bloqueDeFinDeBash(bien, texto) {
|
|
|
152
153
|
return (ancho) => {
|
|
153
154
|
const u = capacidades().unicode;
|
|
154
155
|
const marca = bien ? c.ok(u ? '✓' : 'v') : c.error(u ? '✗' : 'x');
|
|
155
|
-
return [`${m}${c.tenue(c.
|
|
156
|
+
return [`${m}${c.tenue(c.bash(u ? '╰' : '`'))} ${marca} ${recortar(c.gris(texto), anchoUtil(ancho) - 4)}`];
|
|
156
157
|
};
|
|
157
158
|
}
|
|
158
159
|
export function trocearTeclas(datos, estado) {
|
|
@@ -287,9 +288,9 @@ function teclaDeRaton(b, x, y, soltar, botonX10) {
|
|
|
287
288
|
}
|
|
288
289
|
export const RATON_SI = '\x1b[?1000h\x1b[?1002h\x1b[?1003h\x1b[?1006h';
|
|
289
290
|
export const RATON_NO = '\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?1006l';
|
|
290
|
-
const CON_VALOR = new Set(['mode', 'model', 'tips', 'notify', 'teach', 'mouse']);
|
|
291
|
+
const CON_VALOR = new Set(['mode', 'model', 'tips', 'notify', 'teach', 'mouse', 'animation']);
|
|
291
292
|
const CON_TEXTO = new Set(['folder']);
|
|
292
|
-
const SE_MANDAN_SOLOS = new Set(['teach', 'notify', 'tips', 'folder', 'mouse']);
|
|
293
|
+
const SE_MANDAN_SOLOS = new Set(['teach', 'notify', 'tips', 'folder', 'mouse', 'animation']);
|
|
293
294
|
const MODOS_DEL_MENU = ['ask', 'auto', 'read', 'bypass'];
|
|
294
295
|
export function opcionesDelMenu(valor) {
|
|
295
296
|
if (!valor.startsWith('/') || valor.includes('\n'))
|
|
@@ -321,12 +322,12 @@ export function opcionesDelMenu(valor) {
|
|
|
321
322
|
: cmd === 'model'
|
|
322
323
|
? ORDEN_DE_MODELOS.map((k) => [k, `${MODELOS[k].nombre} ${MODELOS[k].version}: ${MODELOS[k].descripcion}`])
|
|
323
324
|
: [
|
|
324
|
-
['on', cmd === 'teach' ? 'Explain each step as it codes' : cmd === 'notify' ? 'Notify me when it finishes or needs me' : cmd === 'mouse' ? 'Click options, scroll with the wheel' : 'Show them when parx opens'],
|
|
325
|
-
['off', cmd === 'teach' ? 'Back to normal' : cmd === 'notify' ? 'No desktop notifications' : cmd === 'mouse' ? 'The terminal selects and scrolls as usual' : 'Don’t show them again'],
|
|
325
|
+
['on', cmd === 'teach' ? 'Explain each step as it codes' : cmd === 'notify' ? 'Notify me when it finishes or needs me' : cmd === 'mouse' ? 'Click options, scroll with the wheel' : cmd === 'animation' ? 'Write answers out as they arrive' : 'Show them when parx opens'],
|
|
326
|
+
['off', cmd === 'teach' ? 'Back to normal' : cmd === 'notify' ? 'No desktop notifications' : cmd === 'mouse' ? 'The terminal selects and scrolls as usual' : cmd === 'animation' ? 'Show answers whole, at once' : 'Don’t show them again'],
|
|
326
327
|
];
|
|
327
328
|
return valores
|
|
328
329
|
.filter(([v]) => v.startsWith(q))
|
|
329
|
-
.map(([v, d]) => ({ nombre: v, descripcion: d, completar: `${base}${v}`, enviar: true, ...(cmd === 'mode'
|
|
330
|
+
.map(([v, d]) => ({ nombre: v, descripcion: d, completar: `${base}${v}`, enviar: true, ...(cmd === 'mode' ? { modo: v, ...(v === 'bypass' ? { peligro: true } : {}) } : {}) }));
|
|
330
331
|
}
|
|
331
332
|
export function componerBarra(e, util) {
|
|
332
333
|
return componerBarraConZonas(e, util).texto;
|
|
@@ -334,7 +335,8 @@ export function componerBarra(e, util) {
|
|
|
334
335
|
export function componerBarraConZonas(e, util) {
|
|
335
336
|
const u = capacidades().unicode;
|
|
336
337
|
const punto = c.gris(sep());
|
|
337
|
-
const
|
|
338
|
+
const tinta = e.clave ? colorDeModo(e.clave) : e.peligro ? c.peligro : c.gris;
|
|
339
|
+
const modo = e.peligro ? tinta(c.negrita(`${u ? '▲' : '!'} ${e.modo}`)) : tinta(e.modo);
|
|
338
340
|
const extra = e.extra ? `${punto}${c.marca(e.extra)}` : '';
|
|
339
341
|
const n = e.esperando ?? 0;
|
|
340
342
|
const espera = n ? c.aviso(`${u ? '●' : '*'} ${tt('bandeja.esperando', { n })}`) : '';
|
|
@@ -379,17 +381,11 @@ export function duracion(ms) {
|
|
|
379
381
|
export function acciones(n) {
|
|
380
382
|
return n === 1 ? tt('estado.accion') : tt('estado.acciones', { n: n.toLocaleString('en-US') });
|
|
381
383
|
}
|
|
382
|
-
|
|
383
|
-
export const MS_POR_FRASE = 2800;
|
|
384
|
-
export function fraseDeEspera(ms, semilla = 0) {
|
|
385
|
-
const vuelta = Math.floor(Math.max(0, ms) / MS_POR_FRASE);
|
|
386
|
-
if (vuelta === 0)
|
|
387
|
-
return `${tt(FRASES[0])}…`;
|
|
388
|
-
return `${tt(FRASES[1 + ((semilla + vuelta - 1) % (FRASES.length - 1))])}…`;
|
|
389
|
-
}
|
|
384
|
+
export { fraseDeEspera } from "./actividad.js";
|
|
390
385
|
export function ponerTextoDeTrabajo(w, texto, anim = 'ruta', vivo = false, ahora = Date.now()) {
|
|
391
386
|
if (texto) {
|
|
392
387
|
w.texto = texto;
|
|
388
|
+
w.trasHerramientas = true;
|
|
393
389
|
w.anim = anim;
|
|
394
390
|
w.vivo = vivo;
|
|
395
391
|
w.literalHasta = ahora + 1800;
|
|
@@ -400,12 +396,12 @@ export function ponerTextoDeTrabajo(w, texto, anim = 'ruta', vivo = false, ahora
|
|
|
400
396
|
}
|
|
401
397
|
}
|
|
402
398
|
export function nuevoTrabajo(ahora = Date.now()) {
|
|
403
|
-
return { texto: null, literalHasta: 0, vivo: false, anim: 'ruta', desde: ahora, esperaDesde: ahora, paso: 0, acciones: null,
|
|
399
|
+
return { texto: null, literalHasta: 0, vivo: false, anim: 'ruta', desde: ahora, esperaDesde: ahora, paso: 0, acciones: null, trasHerramientas: false };
|
|
404
400
|
}
|
|
405
401
|
export function textoDeTrabajo(w, ahora) {
|
|
406
402
|
if (w.texto && (w.vivo || ahora < w.literalHasta))
|
|
407
|
-
return { texto: w.texto, anim: w.anim };
|
|
408
|
-
return { texto: fraseDeEspera(ahora - w.esperaDesde, w.
|
|
403
|
+
return { texto: conElipsis(w.texto), anim: w.anim };
|
|
404
|
+
return { texto: fraseDeEspera(ahora - w.esperaDesde, w.trasHerramientas), anim: 'ruta' };
|
|
409
405
|
}
|
|
410
406
|
export function filaDeTrabajo(w, ahora, ancho = Infinity) {
|
|
411
407
|
const { texto, anim } = textoDeTrabajo(w, ahora);
|
|
@@ -515,8 +511,8 @@ export function componerCarril(d, ancho, alto) {
|
|
|
515
511
|
const anchoEscribir = util - (conPar ? huecoPar + 1 : 0);
|
|
516
512
|
{
|
|
517
513
|
const libre = Math.max(4, anchoEscribir - signoW);
|
|
518
|
-
const color = d.trabajo || d.pregunta ? c.gris : c.claro;
|
|
519
|
-
const marca = bash ? c.
|
|
514
|
+
const color = d.trabajo || d.pregunta ? c.gris : d.estado.clave ? colorDeModo(d.estado.clave) : c.claro;
|
|
515
|
+
const marca = bash ? c.bash(c.negrita('!')) : color(signo());
|
|
520
516
|
const unidades = bash ? d.unidades.slice(1) : d.unidades;
|
|
521
517
|
const enCursor = bash ? Math.max(0, d.cursor - 1) : d.cursor;
|
|
522
518
|
if (!unidades.length) {
|
|
@@ -534,7 +530,7 @@ export function componerCarril(d, ancho, alto) {
|
|
|
534
530
|
}
|
|
535
531
|
if (conPar)
|
|
536
532
|
filaEscribir += ` ${cabezaDePar()} `;
|
|
537
|
-
const tinta = (t) => (bash ? c.
|
|
533
|
+
const tinta = (t) => (bash ? c.bash(t) : c.violeta(t));
|
|
538
534
|
const linea = conPar ? `${tinta(carril(util - ANCHO_DE_PAR - 2))}${caraDePar(d.gesto)}${tinta(carril(2))}` : tinta(carril(util));
|
|
539
535
|
let barra;
|
|
540
536
|
let zonasBarra = [];
|
|
@@ -553,7 +549,7 @@ export function componerCarril(d, ancho, alto) {
|
|
|
553
549
|
barra = izquierdaYDerecha(c.aviso(recortar(d.aviso, util)), '', util);
|
|
554
550
|
}
|
|
555
551
|
else if (bash) {
|
|
556
|
-
const etiqueta = c.
|
|
552
|
+
const etiqueta = c.bash(c.negrita(tt('bash.etiqueta')));
|
|
557
553
|
const largo = `${etiqueta}${c.gris(`${sep()}${tt('bash.intro')}`)}`;
|
|
558
554
|
barra = izquierdaYDerecha(anchoDe(largo) + anchoDe(d.estado.saldoCorto ?? '') + 2 <= util ? largo : etiqueta, c.gris(d.estado.saldoCorto ?? ''), util);
|
|
559
555
|
}
|
|
@@ -641,7 +637,8 @@ export function componerCarril(d, ancho, alto) {
|
|
|
641
637
|
const sel = i === d.menu.elegida;
|
|
642
638
|
const sobre = d.sobre === `menu:${i}`;
|
|
643
639
|
zonasEncima.push({ fila: k, desde: MARGEN, hasta: MARGEN + util, id: `menu:${i}` });
|
|
644
|
-
const
|
|
640
|
+
const tinta = o.modo ? colorDeModo(o.modo) : o.peligro ? c.peligro : c.claro;
|
|
641
|
+
const marca = sel ? tinta(u ? '▌' : '>') : ' ';
|
|
645
642
|
const resto = Math.max(0, util - 2 - anchoNombre);
|
|
646
643
|
const desc = recortar(o.descripcion, resto);
|
|
647
644
|
if (o.peligro) {
|
|
@@ -649,6 +646,8 @@ export function componerCarril(d, ancho, alto) {
|
|
|
649
646
|
return `${m}${rellenar(`${marca} ${sel || sobre ? c.negrita(c.peligro(nombre)) : c.peligro(nombre)}${sel ? c.peligro(desc) : c.tenue(c.peligro(desc))}`, util)}`;
|
|
650
647
|
}
|
|
651
648
|
const nombre = rellenar(recortar(o.nombre, anchoNombre - 1), anchoNombre);
|
|
649
|
+
if (o.modo)
|
|
650
|
+
return `${m}${rellenar(`${marca} ${sel || sobre ? c.negrita(tinta(nombre)) : tinta(nombre)}${sel || sobre ? desc : c.tenue(c.gris(desc))}`, util)}`;
|
|
652
651
|
return `${m}${rellenar(`${marca} ${sel ? c.negrita(c.marca(nombre)) : sobre ? c.marca(nombre) : c.gris(nombre)}${sel || sobre ? desc : c.tenue(c.gris(desc))}`, util)}`;
|
|
653
652
|
});
|
|
654
653
|
}
|
|
@@ -708,6 +707,9 @@ export function opcionesDePregunta(tipo) {
|
|
|
708
707
|
return { opciones: [tt('tui.si'), tt('tui.siempre'), tt('tui.no')], cortas: [tt('tui.si'), tt('tui.siempre_corto'), tt('tui.no')], valores: ['si', 'siempre', 'no'], elegida: 0 };
|
|
709
708
|
}
|
|
710
709
|
}
|
|
710
|
+
export function esNueva(v) {
|
|
711
|
+
return ALIAS_COMANDO[/^\/(\S+)$/.exec(v.trim())?.[1]?.toLowerCase() ?? ''] === 'new';
|
|
712
|
+
}
|
|
711
713
|
const MAX_LINEAS = 4000;
|
|
712
714
|
const MAX_BLOQUES = 1500;
|
|
713
715
|
const PEGADO_LARGO = 280;
|
|
@@ -1041,6 +1043,98 @@ export class Superficie {
|
|
|
1041
1043
|
this.escribir(s);
|
|
1042
1044
|
this.pintar();
|
|
1043
1045
|
}
|
|
1046
|
+
refrescarUltimo() {
|
|
1047
|
+
const b = this.ultimoBloque();
|
|
1048
|
+
if (!b)
|
|
1049
|
+
return;
|
|
1050
|
+
const k = this.ultimoAlto;
|
|
1051
|
+
if (this.lineas.length < k)
|
|
1052
|
+
return;
|
|
1053
|
+
const nuevas = sinBlancoDoble(b(this.ancho), this.ultimoTrasBlanco);
|
|
1054
|
+
const base = this.lineas.length - k;
|
|
1055
|
+
const viejas = this.lineas.slice(base);
|
|
1056
|
+
if (nuevas.length === k && nuevas.every((l, i) => l === viejas[i]))
|
|
1057
|
+
return;
|
|
1058
|
+
if (nuevas.length < k) {
|
|
1059
|
+
this.bloques[this.bloques.length - 1] = b;
|
|
1060
|
+
this.recomponer();
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
this.lineas.splice(base, k, ...nuevas);
|
|
1064
|
+
this.ultimoAlto = nuevas.length;
|
|
1065
|
+
if (!this.activa) {
|
|
1066
|
+
this.volcadas = this.lineas.length;
|
|
1067
|
+
this.recortarMemoria();
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
const subido = this.subidas > 0 || this.volcadas < base + k;
|
|
1071
|
+
const n = this.carrilPintadas || componerCarril(this.datos(), this.ancho, this.alto).carril.length;
|
|
1072
|
+
if (subido) {
|
|
1073
|
+
const desde = this.desplazadas(n) - this.subidas;
|
|
1074
|
+
const cambiaVisible = viejas.some((l, i) => base + i < this.volcadas && base + i >= desde && nuevas[i] !== l);
|
|
1075
|
+
if (cambiaVisible)
|
|
1076
|
+
this.vistaSucia = true;
|
|
1077
|
+
this.recortarMemoria();
|
|
1078
|
+
if (cambiaVisible || nuevas.length !== k)
|
|
1079
|
+
this.pintar();
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const top = this.filaDelCarril(n);
|
|
1083
|
+
const tapadas = top - this.encimaPintadas;
|
|
1084
|
+
let s = '';
|
|
1085
|
+
for (let i = 0; i < k; i++) {
|
|
1086
|
+
if (nuevas[i] === viejas[i])
|
|
1087
|
+
continue;
|
|
1088
|
+
const fila = base + i - this.desplazadas(n) + 1;
|
|
1089
|
+
if (fila >= 1 && fila < tapadas)
|
|
1090
|
+
s += `\x1b[${fila};1H\x1b[2K${nuevas[i]}`;
|
|
1091
|
+
}
|
|
1092
|
+
if (s)
|
|
1093
|
+
this.escribir(`\x1b[?2026h\x1b7${s}\x1b8\x1b[?2026l`);
|
|
1094
|
+
if (nuevas.length > k) {
|
|
1095
|
+
this.escribir(this.destapar());
|
|
1096
|
+
this.volcar();
|
|
1097
|
+
this.pintar();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
reiniciar(bienvenida, consejos = []) {
|
|
1101
|
+
this.bloques = [];
|
|
1102
|
+
this.lineas = [];
|
|
1103
|
+
this.volcadas = 0;
|
|
1104
|
+
this.subidas = 0;
|
|
1105
|
+
this.vistaSucia = false;
|
|
1106
|
+
this.cola = [];
|
|
1107
|
+
this.aviso = null;
|
|
1108
|
+
this.ultimoAlto = 0;
|
|
1109
|
+
this.ultimoTrasBlanco = false;
|
|
1110
|
+
this.encimaPintadas = 0;
|
|
1111
|
+
this.sobre = null;
|
|
1112
|
+
this.pulsado = null;
|
|
1113
|
+
this.ctrlC = 0;
|
|
1114
|
+
this.alRevelar = null;
|
|
1115
|
+
this.ponerBienvenida(bienvenida, consejos);
|
|
1116
|
+
this.recomponer();
|
|
1117
|
+
}
|
|
1118
|
+
ponerBienvenida(bienvenida, consejos = []) {
|
|
1119
|
+
if (bienvenida)
|
|
1120
|
+
this.bloques.push(bienvenida);
|
|
1121
|
+
this.consejos = [];
|
|
1122
|
+
this.consejosVisibles = false;
|
|
1123
|
+
if (consejos.length) {
|
|
1124
|
+
const ocupado = (bienvenida ? bienvenida(this.ancho).length : 0) + componerCarril(this.datos(), this.ancho, this.alto).carril.length;
|
|
1125
|
+
let n = consejos.length;
|
|
1126
|
+
while (n > 0 && ocupado + bloqueDeConsejos(() => consejos.slice(0, n))(this.ancho).length > this.alto)
|
|
1127
|
+
n--;
|
|
1128
|
+
this.consejos = consejos.slice(0, n);
|
|
1129
|
+
this.consejosVisibles = n > 0;
|
|
1130
|
+
if (n > 0)
|
|
1131
|
+
this.bloques.push(bloqueDeConsejos(() => (this.consejosVisibles ? this.consejos : [])));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
alRevelar = null;
|
|
1135
|
+
revelando(fn) {
|
|
1136
|
+
this.alRevelar = fn;
|
|
1137
|
+
}
|
|
1044
1138
|
recomponer(alEmpezar = false) {
|
|
1045
1139
|
if (!this.activa)
|
|
1046
1140
|
return;
|
|
@@ -1094,6 +1188,10 @@ export class Superficie {
|
|
|
1094
1188
|
this.volcar();
|
|
1095
1189
|
this.pintar();
|
|
1096
1190
|
}
|
|
1191
|
+
nuevaTrasParar() {
|
|
1192
|
+
this.cola = ['/new'];
|
|
1193
|
+
this.mostrarAviso(null);
|
|
1194
|
+
}
|
|
1097
1195
|
get _vista() {
|
|
1098
1196
|
return { subidas: this.subidas, pendientes: this.lineas.length - this.volcadas };
|
|
1099
1197
|
}
|
|
@@ -1171,18 +1269,7 @@ export class Superficie {
|
|
|
1171
1269
|
if (this.activa)
|
|
1172
1270
|
return;
|
|
1173
1271
|
this.activa = true;
|
|
1174
|
-
|
|
1175
|
-
this.bloques.push(bienvenida);
|
|
1176
|
-
if (consejos.length) {
|
|
1177
|
-
const ocupado = (bienvenida ? bienvenida(this.ancho).length : 0) + componerCarril(this.datos(), this.ancho, this.alto).carril.length;
|
|
1178
|
-
let n = consejos.length;
|
|
1179
|
-
while (n > 0 && ocupado + bloqueDeConsejos(() => consejos.slice(0, n))(this.ancho).length > this.alto)
|
|
1180
|
-
n--;
|
|
1181
|
-
this.consejos = consejos.slice(0, n);
|
|
1182
|
-
this.consejosVisibles = n > 0;
|
|
1183
|
-
if (n > 0)
|
|
1184
|
-
this.bloques.push(bloqueDeConsejos(() => (this.consejosVisibles ? this.consejos : [])));
|
|
1185
|
-
}
|
|
1272
|
+
this.ponerBienvenida(bienvenida, consejos);
|
|
1186
1273
|
this.escribir(this.modosSi());
|
|
1187
1274
|
try {
|
|
1188
1275
|
this.o.entrada.setRawMode?.(true);
|
|
@@ -1317,7 +1404,7 @@ export class Superficie {
|
|
|
1317
1404
|
const r = enCola;
|
|
1318
1405
|
this.ejemplo = null;
|
|
1319
1406
|
this.mostrarAviso(this.cola.length ? tt('tui.en_cola_n', { n: this.cola.length }) : null);
|
|
1320
|
-
if (r.trim() && r.trim() !== '!')
|
|
1407
|
+
if (r.trim() && r.trim() !== '!' && !esNueva(r.trim()))
|
|
1321
1408
|
this.anadir(this.bloqueDeEnvio(r));
|
|
1322
1409
|
return Promise.resolve(this.expandir(r));
|
|
1323
1410
|
}
|
|
@@ -1475,6 +1562,10 @@ export class Superficie {
|
|
|
1475
1562
|
this.posHistorial = this.historial.length;
|
|
1476
1563
|
this.irAlFondo();
|
|
1477
1564
|
if (this.trabajo || !this.resolverLectura) {
|
|
1565
|
+
if (this.trabajo && this.o.alNuevaEnMarcha && esNueva(v)) {
|
|
1566
|
+
this.o.alNuevaEnMarcha();
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1478
1569
|
if (v) {
|
|
1479
1570
|
this.cola.push(crudo);
|
|
1480
1571
|
this.mostrarAviso(this.cola.length > 1 ? tt('tui.en_cola_n', { n: this.cola.length }) : tt('tui.en_cola'));
|
|
@@ -1483,7 +1574,7 @@ export class Superficie {
|
|
|
1483
1574
|
}
|
|
1484
1575
|
const r = this.resolverLectura;
|
|
1485
1576
|
this.resolverLectura = null;
|
|
1486
|
-
if (v && v !== '!')
|
|
1577
|
+
if (v && v !== '!' && !esNueva(v))
|
|
1487
1578
|
this.anadir(this.bloqueDeEnvio(crudo));
|
|
1488
1579
|
else
|
|
1489
1580
|
this.pintar();
|
|
@@ -1503,6 +1594,11 @@ export class Superficie {
|
|
|
1503
1594
|
}
|
|
1504
1595
|
if (k.t === 'raton')
|
|
1505
1596
|
return this.alRaton(k);
|
|
1597
|
+
if (this.alRevelar && k.t !== 'pagina_arriba' && k.t !== 'pagina_abajo' && k.t !== 'fondo') {
|
|
1598
|
+
const f = this.alRevelar;
|
|
1599
|
+
this.alRevelar = null;
|
|
1600
|
+
f();
|
|
1601
|
+
}
|
|
1506
1602
|
if (k.t === 'pagina_arriba' || k.t === 'pagina_abajo')
|
|
1507
1603
|
return this.desplazar((k.t === 'pagina_arriba' ? 1 : -1) * Math.max(1, this.filasDeVista() - 2));
|
|
1508
1604
|
if (k.t === 'fondo')
|
package/dist/ui.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { animacion, c, capacidades, SIMBOLOS } from "./marca.js";
|
|
2
2
|
import { cuantos, t } from "./textos.js";
|
|
3
3
|
import { trozos } from "./diff.js";
|
|
4
|
+
import { caracteresDe, caracteresVisibles, cursorEncendido, duracionDeRevelado, MS_MAX_INTERMEDIO, MS_MAX_RESPUESTA, MS_POR_FOTOGRAMA, revelarLineas, simboloDeCursor } from "./actividad.js";
|
|
4
5
|
import { anchoDe, envolver, recortar, recortarMedio, rellenar, sinColor } from "./texto.js";
|
|
5
6
|
import { anchoUtil, bloqueDeCierre, bloqueDeLinea, bloqueDeTexto, filaDeTrabajo, MARGEN, nuevoTrabajo, opcionesDePregunta, ponerTextoDeTrabajo, } from "./tui.js";
|
|
6
7
|
export function leerRespuesta(texto, tipo = 'normal') {
|
|
@@ -89,6 +90,8 @@ export class Pantalla {
|
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
92
|
bloque(b, grupo = 'otro') {
|
|
93
|
+
if (this.revelado)
|
|
94
|
+
this.terminarRevelado();
|
|
92
95
|
const bc = this.conCarril(this.conAire(b, grupo));
|
|
93
96
|
if (this.superficie) {
|
|
94
97
|
this.superficie.anadir(bc);
|
|
@@ -290,42 +293,147 @@ export class Pantalla {
|
|
|
290
293
|
}
|
|
291
294
|
}
|
|
292
295
|
texto(md) {
|
|
293
|
-
|
|
294
|
-
this.bloque((
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
296
|
+
this.terminarRevelado();
|
|
297
|
+
this.bloque(bloqueDeMarkdown(md), 'texto');
|
|
298
|
+
}
|
|
299
|
+
animar = true;
|
|
300
|
+
revelado = null;
|
|
301
|
+
terminarRevelado() {
|
|
302
|
+
const f = this.revelado;
|
|
303
|
+
this.revelado = null;
|
|
304
|
+
f?.();
|
|
305
|
+
}
|
|
306
|
+
textoAnimado(md, o = {}) {
|
|
307
|
+
this.terminarRevelado();
|
|
308
|
+
const conSuperficie = this.superficie !== null;
|
|
309
|
+
if (!this.animar || this.carril || !this.tty || (!conSuperficie && !process.stdout.isTTY)) {
|
|
310
|
+
this.texto(md);
|
|
311
|
+
return Promise.resolve();
|
|
312
|
+
}
|
|
313
|
+
const completo = bloqueDeMarkdown(md);
|
|
314
|
+
const maxMs = o.intermedio ? MS_MAX_INTERMEDIO : MS_MAX_RESPUESTA;
|
|
315
|
+
const estado = { inicio: Date.now(), fin: false, ahora: Date.now() };
|
|
316
|
+
const total = caracteresDe(completo(this.superficie?.ancho ?? Math.max(20, process.stdout.columns || 80)));
|
|
317
|
+
const duracion = duracionDeRevelado(total, maxMs);
|
|
318
|
+
const parcial = (ancho) => {
|
|
319
|
+
const lineas = completo(ancho);
|
|
320
|
+
if (estado.fin)
|
|
321
|
+
return lineas;
|
|
322
|
+
const ms = estado.ahora - estado.inicio;
|
|
323
|
+
const n = caracteresVisibles(caracteresDe(lineas), ms, duracion);
|
|
324
|
+
return revelarLineas(lineas, n, cursorEncendido(ms) ? simboloDeCursor() : ' ', anchoUtil(ancho) + MARGEN * 2);
|
|
325
|
+
};
|
|
326
|
+
return new Promise((resolver) => {
|
|
327
|
+
let reloj = null;
|
|
328
|
+
const acabar = () => {
|
|
329
|
+
if (estado.fin)
|
|
330
|
+
return;
|
|
331
|
+
estado.fin = true;
|
|
332
|
+
if (reloj)
|
|
333
|
+
clearInterval(reloj);
|
|
334
|
+
if (this.revelado === acabar)
|
|
335
|
+
this.revelado = null;
|
|
336
|
+
this.superficie?.revelando(null);
|
|
337
|
+
if (conSuperficie)
|
|
338
|
+
this.superficie.refrescarUltimo();
|
|
339
|
+
else
|
|
340
|
+
this.revelarEnLinea(completo, Infinity);
|
|
341
|
+
resolver();
|
|
342
|
+
};
|
|
343
|
+
if (conSuperficie) {
|
|
344
|
+
this.bloque(parcial, 'texto');
|
|
345
|
+
this.superficie.revelando(acabar);
|
|
326
346
|
}
|
|
327
|
-
|
|
328
|
-
|
|
347
|
+
else {
|
|
348
|
+
this.pausarLinea();
|
|
349
|
+
this.ultimoGrupo = 'texto';
|
|
350
|
+
this.lineaEnCurso = { escritas: 0, parcial: '' };
|
|
351
|
+
this.revelarEnLinea(completo, 0);
|
|
352
|
+
}
|
|
353
|
+
this.revelado = acabar;
|
|
354
|
+
const fotograma = () => {
|
|
355
|
+
estado.ahora = Date.now();
|
|
356
|
+
if (estado.ahora - estado.inicio >= duracion)
|
|
357
|
+
return acabar();
|
|
358
|
+
if (conSuperficie)
|
|
359
|
+
this.superficie.refrescarUltimo();
|
|
360
|
+
else
|
|
361
|
+
this.revelarEnLinea(completo, caracteresVisibles(total, estado.ahora - estado.inicio, duracion), cursorEncendido(estado.ahora - estado.inicio));
|
|
362
|
+
};
|
|
363
|
+
reloj = setInterval(fotograma, MS_POR_FOTOGRAMA);
|
|
364
|
+
reloj.unref?.();
|
|
365
|
+
});
|
|
329
366
|
}
|
|
367
|
+
lineaEnCurso = null;
|
|
368
|
+
revelarEnLinea(completo, n, cursor = false) {
|
|
369
|
+
const l = this.lineaEnCurso;
|
|
370
|
+
if (!l)
|
|
371
|
+
return;
|
|
372
|
+
const ancho = Math.max(20, process.stdout.columns || capacidades().ancho || 80);
|
|
373
|
+
const lineas = completo(ancho);
|
|
374
|
+
const vistas = revelarLineas(lineas, n, null);
|
|
375
|
+
let s = '';
|
|
376
|
+
while (l.escritas < vistas.length - 1 || (n >= caracteresDe(lineas) && l.escritas < lineas.length)) {
|
|
377
|
+
s += `\r\x1b[2K${lineas[l.escritas]}\n`;
|
|
378
|
+
l.escritas++;
|
|
379
|
+
l.parcial = '';
|
|
380
|
+
}
|
|
381
|
+
if (n < caracteresDe(lineas)) {
|
|
382
|
+
const parcial = vistas[l.escritas] ?? '';
|
|
383
|
+
s += `\r\x1b[2K${parcial}${cursor ? `${anchoDe(parcial) ? ' ' : ''}${simboloDeCursor()}` : ''}`;
|
|
384
|
+
l.parcial = parcial;
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
this.lineaEnCurso = null;
|
|
388
|
+
if (lineas.length)
|
|
389
|
+
this.enBlanco = lineas[lineas.length - 1].trim() === '';
|
|
390
|
+
}
|
|
391
|
+
if (s)
|
|
392
|
+
process.stdout.write(s);
|
|
393
|
+
}
|
|
394
|
+
reiniciar() {
|
|
395
|
+
this.terminarRevelado();
|
|
396
|
+
this.ultimoGrupo = null;
|
|
397
|
+
this.enBlanco = true;
|
|
398
|
+
this.lecturas = null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
export function bloqueDeMarkdown(md) {
|
|
402
|
+
const lineas = md.replace(/\r/g, '').split('\n');
|
|
403
|
+
return (ancho) => {
|
|
404
|
+
const util = anchoUtil(ancho);
|
|
405
|
+
const pre = ' '.repeat(MARGEN);
|
|
406
|
+
const out = [];
|
|
407
|
+
let enBloque = false;
|
|
408
|
+
for (const linea of lineas) {
|
|
409
|
+
if (/^```/.test(linea.trim())) {
|
|
410
|
+
enBloque = !enBloque;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (enBloque) {
|
|
414
|
+
out.push(`${pre} ${c.marca(recortar(linea.replace(/\t/g, ' '), util - 2))}`);
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const titulo = /^#{1,6}\s+(.*)$/.exec(linea);
|
|
418
|
+
if (titulo) {
|
|
419
|
+
for (const l of envolver(c.negrita(titulo[1]), util))
|
|
420
|
+
out.push(`${pre}${l}`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
let l = linea;
|
|
424
|
+
let sangria = '';
|
|
425
|
+
const lista = /^(\s*)([-*]|\d+[.)])\s+/.exec(l);
|
|
426
|
+
if (lista) {
|
|
427
|
+
const marca = /\d/.test(lista[2]) ? lista[2] : SIMBOLOS.punto === '●' ? '·' : '-';
|
|
428
|
+
l = `${lista[1]}${c.claro(marca)} ${l.slice(lista[0].length)}`;
|
|
429
|
+
sangria = ' '.repeat(anchoDe(lista[1]) + anchoDe(marca) + 1);
|
|
430
|
+
}
|
|
431
|
+
l = l.replace(/\*\*([^*]+)\*\*/g, (_m, x) => c.negrita(x));
|
|
432
|
+
l = l.replace(/`([^`]+)`/g, (_m, x) => c.marca(x));
|
|
433
|
+
for (const p of envolver(l, util, sangria))
|
|
434
|
+
out.push(`${pre}${p}`);
|
|
435
|
+
}
|
|
436
|
+
return out;
|
|
437
|
+
};
|
|
330
438
|
}
|
|
331
439
|
export { bloqueDeLinea };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getparallel/parx",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "parx: the coding agent of Parallel in your terminal. Reads and edits your project, runs commands and tests, and does coding tasks end to end. Mac, Windows and Linux.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"parx",
|