chocolatito-code 1.6.10 → 1.6.12
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/README.md +3 -1
- package/dist/agent/huecos.d.ts +24 -0
- package/dist/agent/huecos.js +34 -0
- package/dist/agent/loop.js +49 -3
- package/dist/ui/spinner.d.ts +6 -0
- package/dist/ui/spinner.js +8 -0
- package/extension/manifest.json +1 -1
- package/kits/diseno/skills/carruseles-y-flyers/SKILL.md +100 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -262,7 +262,9 @@ mételo en un kit y tu equipo entero trabaja así.
|
|
|
262
262
|
Viene uno hecho, y no es de programación: `kits/diseno` trae lotes de imágenes con
|
|
263
263
|
ImageMagick, las medidas de todas las redes (con las zonas seguras de historias, que es
|
|
264
264
|
lo que nadie recuerda), y variantes de una misma pieza desde un CSV —cuarenta
|
|
265
|
-
certificados con su nombre, sin una sola errata—.
|
|
265
|
+
certificados con su nombre, sin una sola errata—. Y carruseles: partir una panorámica en
|
|
266
|
+
diapositivas que deslizan como una sola imagen, numeradas con ceros para que se suban en
|
|
267
|
+
orden (`slide-1, slide-10, slide-2` es el error que aparece con el carrusel ya publicado).
|
|
266
268
|
|
|
267
269
|
```bash
|
|
268
270
|
/kit instalar ./kits/diseno
|
package/dist/agent/huecos.d.ts
CHANGED
|
@@ -56,3 +56,27 @@ export declare function liberarHuecos(): void;
|
|
|
56
56
|
* que hacer nada: es lo unico que se arregla solo con esperar.
|
|
57
57
|
*/
|
|
58
58
|
export declare function mensajeDeEspera(segundos: number): string;
|
|
59
|
+
/**
|
|
60
|
+
* Espera a que se libere el hueco, en vez de devolverle la pared al usuario.
|
|
61
|
+
*
|
|
62
|
+
* DE DONDE SALE
|
|
63
|
+
*
|
|
64
|
+
* La misma sesion, un paso mas alla. Ya no se gastaba una peticion por intento
|
|
65
|
+
* -eso lo arreglo lo de arriba-, pero el usuario seguia atrapado:
|
|
66
|
+
*
|
|
67
|
+
* > sigue
|
|
68
|
+
* ⏸ Faltan 144s.
|
|
69
|
+
* > sigoe
|
|
70
|
+
* ⏸ Faltan 133s.
|
|
71
|
+
*
|
|
72
|
+
* Tecleando contra una pared que el programa sabia medir. Si sabemos que faltan
|
|
73
|
+
* 133 segundos, esperarlos es trabajo del programa, no del que paga. Se avisa,
|
|
74
|
+
* se cuenta hacia atras y se sigue solo; con Esc se deja.
|
|
75
|
+
*
|
|
76
|
+
* Vuelve `true` si ya hay hueco y `false` si el usuario corto.
|
|
77
|
+
*/
|
|
78
|
+
export declare function esperarAlHueco(opciones: {
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
alPasarUnSegundo?: (quedan: number) => void;
|
|
81
|
+
dormir?: (ms: number) => Promise<void>;
|
|
82
|
+
}): Promise<boolean>;
|
package/dist/agent/huecos.js
CHANGED
|
@@ -79,3 +79,37 @@ export function mensajeDeEspera(segundos) {
|
|
|
79
79
|
"No llamo al motor porque la respuesta seria la misma y cada intento puede alargar la espera. " +
|
|
80
80
|
"No hace falta que hagas nada: el hueco se libera solo.");
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Espera a que se libere el hueco, en vez de devolverle la pared al usuario.
|
|
84
|
+
*
|
|
85
|
+
* DE DONDE SALE
|
|
86
|
+
*
|
|
87
|
+
* La misma sesion, un paso mas alla. Ya no se gastaba una peticion por intento
|
|
88
|
+
* -eso lo arreglo lo de arriba-, pero el usuario seguia atrapado:
|
|
89
|
+
*
|
|
90
|
+
* > sigue
|
|
91
|
+
* ⏸ Faltan 144s.
|
|
92
|
+
* > sigoe
|
|
93
|
+
* ⏸ Faltan 133s.
|
|
94
|
+
*
|
|
95
|
+
* Tecleando contra una pared que el programa sabia medir. Si sabemos que faltan
|
|
96
|
+
* 133 segundos, esperarlos es trabajo del programa, no del que paga. Se avisa,
|
|
97
|
+
* se cuenta hacia atras y se sigue solo; con Esc se deja.
|
|
98
|
+
*
|
|
99
|
+
* Vuelve `true` si ya hay hueco y `false` si el usuario corto.
|
|
100
|
+
*/
|
|
101
|
+
export async function esperarAlHueco(opciones) {
|
|
102
|
+
const { signal, alPasarUnSegundo } = opciones;
|
|
103
|
+
const dormir = opciones.dormir || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
104
|
+
// Se relee en cada vuelta en vez de contar desde un numero fijo: si algo
|
|
105
|
+
// libera los huecos antes -otra respuesta del motor-, se sale en el acto.
|
|
106
|
+
let quedan = segundosDeEspera();
|
|
107
|
+
while (quedan > 0) {
|
|
108
|
+
if (signal?.aborted)
|
|
109
|
+
return false;
|
|
110
|
+
alPasarUnSegundo?.(quedan);
|
|
111
|
+
await dormir(1000);
|
|
112
|
+
quedan = segundosDeEspera();
|
|
113
|
+
}
|
|
114
|
+
return !signal?.aborted;
|
|
115
|
+
}
|
package/dist/agent/loop.js
CHANGED
|
@@ -8,7 +8,7 @@ import { CONTEXT_WINDOW, MAX_OUTPUT_TOKENS, COMPACT_THRESHOLD_TOKENS, } from "..
|
|
|
8
8
|
import { withRetry, isContextOverflow, esDemasiadasEnCurso } from "./retry.js";
|
|
9
9
|
import { canRunInParallel } from "../tools/safety.js";
|
|
10
10
|
import { createEngineClient, explicarErrorDelMotor, motivoDeParadaDelError, } from "../config/engine.js";
|
|
11
|
-
import { anotarHuecosOcupados, liberarHuecos, mensajeDeEspera, segundosDeEspera } from "./huecos.js";
|
|
11
|
+
import { anotarHuecosOcupados, esperarAlHueco, liberarHuecos, mensajeDeEspera, segundosDeEspera, } from "./huecos.js";
|
|
12
12
|
import { MarkdownStream } from "../ui/markdownStream.js";
|
|
13
13
|
import { checkQuota, recordUsage, mensajeDeCorte, quotaBadge } from "../config/quota.js";
|
|
14
14
|
import { ReasoningStream } from "../ui/reasoningStream.js";
|
|
@@ -461,9 +461,43 @@ export class AgentLoop {
|
|
|
461
461
|
// llamada es justo la que puede refrescar la reserva y alargar la espera.
|
|
462
462
|
const espera = segundosDeEspera();
|
|
463
463
|
if (espera > 0) {
|
|
464
|
+
// ESPERAR ES TRABAJO DEL PROGRAMA, NO DEL QUE PAGA
|
|
465
|
+
//
|
|
466
|
+
// Antes esto devolvia el aviso y cortaba el turno. El usuario escribia
|
|
467
|
+
// "sigue", se llevaba "faltan 144s", escribia otra vez y se llevaba
|
|
468
|
+
// "faltan 133s": tecleando contra una pared que el programa sabia medir.
|
|
469
|
+
// Si sabemos cuanto falta, se espera y se sigue solo. Con Esc se deja.
|
|
464
470
|
const aviso = mensajeDeEspera(espera);
|
|
465
|
-
|
|
466
|
-
|
|
471
|
+
// SOLO SE ESPERA SI HAY ALGUIEN DELANTE.
|
|
472
|
+
//
|
|
473
|
+
// Esperar tres minutos es una pausa cuando se ve la cuenta atras y se
|
|
474
|
+
// puede cortar con Esc. Sin terminal -CI, `chocolatito "haz X"`, una
|
|
475
|
+
// tuberia- es un cuelgue mudo de tres minutos que nadie puede parar, y
|
|
476
|
+
// ademas ahi no hay nadie a quien ahorrarle el teclear. Se dice y se
|
|
477
|
+
// corta, como antes.
|
|
478
|
+
if (!process.stdin.isTTY) {
|
|
479
|
+
salida.linea(chalk.yellow(`\n⏸ ${aviso}\n`));
|
|
480
|
+
return parada("ocupado", aviso);
|
|
481
|
+
}
|
|
482
|
+
salida.linea(chalk.yellow(`\n⏸ ${aviso}`));
|
|
483
|
+
salida.linea(chalk.gray(" Espero y sigo solo cuando se libere. Esc para dejarlo.\n"));
|
|
484
|
+
const vigilante = new InterruptWatcher();
|
|
485
|
+
const senal = vigilante.start();
|
|
486
|
+
const spinnerEspera = new DynamicSpinner();
|
|
487
|
+
spinnerEspera.start();
|
|
488
|
+
const hayHueco = await esperarAlHueco({
|
|
489
|
+
signal: senal,
|
|
490
|
+
alPasarUnSegundo: (quedan) => {
|
|
491
|
+
spinnerEspera.setPrefijo(`Esperando hueco de la licencia (quedan ${quedan}s)`);
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
spinnerEspera.stop();
|
|
495
|
+
vigilante.stop();
|
|
496
|
+
if (!hayHueco) {
|
|
497
|
+
salida.linea(chalk.yellow("\n⏸ Interrumpido. Dime como seguir."));
|
|
498
|
+
return parada("interrumpido");
|
|
499
|
+
}
|
|
500
|
+
salida.linea(chalk.green(" ✔ Hueco libre. Sigo.\n"));
|
|
467
501
|
}
|
|
468
502
|
// Cuota ANTES de gastar. Si no queda margen, se dice y no se llama al motor:
|
|
469
503
|
// avisar despues de haber gastado no sirve de nada.
|
|
@@ -742,6 +776,18 @@ export class AgentLoop {
|
|
|
742
776
|
// porque la conexion estaba bien.
|
|
743
777
|
if (!llegoElFinal) {
|
|
744
778
|
watcher.stop();
|
|
779
|
+
// SE CIERRA LA PETICION MUERTA, Y ESTO NO ES LIMPIEZA: ES EL HUECO.
|
|
780
|
+
//
|
|
781
|
+
// El cuerpo se corto, pero por lo que respecta al proxy la peticion
|
|
782
|
+
// sigue en curso y sigue ocupando uno de los cuatro huecos de la
|
|
783
|
+
// licencia hasta que ALGO la cierre. Por eso el mensaje del proxy dice
|
|
784
|
+
// "si acabas de interrumpir una, el hueco se libera solo en 206s": son
|
|
785
|
+
// tres minutos y medio de servicio perdido por una conexion que ya
|
|
786
|
+
// estaba muerta de este lado. Abortando aqui se suelta en el momento.
|
|
787
|
+
try {
|
|
788
|
+
stream?.controller?.abort();
|
|
789
|
+
}
|
|
790
|
+
catch { }
|
|
745
791
|
// Lo que llego se guarda, pero MARCADO. Sin la marca, en el turno
|
|
746
792
|
// siguiente el modelo lee su propia frase a medias como algo que decidio
|
|
747
793
|
// dejar asi, y responde a un texto que nadie escribio.
|
package/dist/ui/spinner.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export declare class DynamicSpinner {
|
|
|
7
7
|
private prefix;
|
|
8
8
|
tokenCount: number;
|
|
9
9
|
constructor(prefix?: string);
|
|
10
|
+
/**
|
|
11
|
+
* Cambia el texto mientras gira. Hace falta para las esperas con cuenta
|
|
12
|
+
* atras: el prefijo se fijaba al arrancar y el numero de segundos que faltan
|
|
13
|
+
* cambia cada vuelta.
|
|
14
|
+
*/
|
|
15
|
+
setPrefijo(prefijo: string): void;
|
|
10
16
|
start(initialPrefix?: string): void;
|
|
11
17
|
private tick;
|
|
12
18
|
/**
|
package/dist/ui/spinner.js
CHANGED
|
@@ -42,6 +42,14 @@ export class DynamicSpinner {
|
|
|
42
42
|
constructor(prefix = "") {
|
|
43
43
|
this.prefix = prefix;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Cambia el texto mientras gira. Hace falta para las esperas con cuenta
|
|
47
|
+
* atras: el prefijo se fijaba al arrancar y el numero de segundos que faltan
|
|
48
|
+
* cambia cada vuelta.
|
|
49
|
+
*/
|
|
50
|
+
setPrefijo(prefijo) {
|
|
51
|
+
this.prefix = prefijo;
|
|
52
|
+
}
|
|
45
53
|
start(initialPrefix) {
|
|
46
54
|
if (this.isRunning)
|
|
47
55
|
return;
|
package/extension/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"minimum_chrome_version": "102",
|
|
4
4
|
"name": "Chocolatito Code",
|
|
5
|
-
"version": "1.6.
|
|
5
|
+
"version": "1.6.12",
|
|
6
6
|
"description": "Deja que Chocolatito Code trabaje dentro de tu Chrome, en segundo plano y con tus sesiones ya iniciadas.",
|
|
7
7
|
"permissions": [
|
|
8
8
|
"tabs",
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: carruseles-y-flyers
|
|
3
|
+
description: Usar para carruseles de Instagram o LinkedIn y para flyers comerciales — partir una panorámica en diapositivas, sacar el mismo flyer en varias versiones (precio, sucursal, fecha, idioma), numerar para que suban en orden y preparar el envío al cliente.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Carruseles y flyers
|
|
7
|
+
|
|
8
|
+
El trabajo repetido de una pieza comercial: **el diseñador hace una buena, y aquí
|
|
9
|
+
salen las otras treinta y nueve y todos los formatos.** Nada de esto decide la
|
|
10
|
+
composición ni elige la tipografía — eso es suyo.
|
|
11
|
+
|
|
12
|
+
## Medidas
|
|
13
|
+
|
|
14
|
+
| | |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| Carrusel Instagram | 1080 × 1350 (4:5, ocupa más pantalla que el cuadrado) |
|
|
17
|
+
| Carrusel LinkedIn | 1080 × 1350, se sube como **PDF** de varias páginas |
|
|
18
|
+
| Máximo de diapositivas | 20 en Instagram; 10 es lo que la gente aguanta |
|
|
19
|
+
| Flyer digital | 1080 × 1350 feed · 1080 × 1920 historia |
|
|
20
|
+
| Flyer imprenta A5 | 148 × 210 mm a 300 ppp = 1748 × 2480 px, con 3 mm de sangre |
|
|
21
|
+
|
|
22
|
+
## Partir una panorámica en diapositivas, sin costuras
|
|
23
|
+
|
|
24
|
+
El carrusel que se desliza como una sola imagen. La panorámica tiene que medir
|
|
25
|
+
`1080 × n` de ancho exacto: para 5 diapositivas, **5400 × 1350**.
|
|
26
|
+
|
|
27
|
+
Con ImageMagick, de una vez:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
magick pano.png -crop 1080x1350 +repage slide-%02d.png
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Sin ImageMagick, con ffmpeg (comprobado: salen las 5 a 1080×1350 clavadas):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
for i in 0 1 2 3 4; do
|
|
37
|
+
ffmpeg -y -i pano.png -vf "crop=1080:1350:$((i*1080)):0" "slide-$(printf %02d $((i+1))).png"
|
|
38
|
+
done
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Antes de cortar, comprueba el ancho.** Si la panorámica no es múltiplo exacto de
|
|
42
|
+
1080, la última diapositiva sale corta y se nota: reescala o recorta primero, y
|
|
43
|
+
dilo.
|
|
44
|
+
|
|
45
|
+
## Los ceros de la numeración no son estética
|
|
46
|
+
|
|
47
|
+
`slide-1, slide-2 … slide-10` se sube en el orden `1, 10, 2, 3…` porque el
|
|
48
|
+
selector de archivos ordena por texto. **Siempre `%02d`:** `slide-01`,
|
|
49
|
+
`slide-02`. Este es el error que aparece con el carrusel ya publicado.
|
|
50
|
+
|
|
51
|
+
## El mismo flyer en varias versiones
|
|
52
|
+
|
|
53
|
+
Precio distinto, sucursal distinta, fecha distinta, idioma distinto. Es el mismo
|
|
54
|
+
método que `variantes-de-pieza`: una tabla CSV con lo que cambia, una plantilla
|
|
55
|
+
SVG con `{{PRECIO}}`, `{{SUCURSAL}}`, y una pieza por fila.
|
|
56
|
+
|
|
57
|
+
Dos cosas que rompen el lote comercial en particular:
|
|
58
|
+
|
|
59
|
+
- **El precio largo.** `$9` y `$1.299.990` no ocupan lo mismo. Mira el más largo
|
|
60
|
+
del CSV antes de lanzar, y ajusta el tamaño de letra a ese.
|
|
61
|
+
- **La fecha.** Escríbela en el CSV tal y como tiene que verse. No la formatees
|
|
62
|
+
tú: "5 de marzo", "05/03" y "Marzo 5" no son intercambiables en una pieza.
|
|
63
|
+
|
|
64
|
+
## Colores que se apagan al subir
|
|
65
|
+
|
|
66
|
+
Instagram convierte todo a **sRGB**. Si la pieza sale en Adobe RGB o P3, los
|
|
67
|
+
colores se apagan al publicar y el cliente lo nota en el rojo de su logo:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
magick pieza.png -colorspace sRGB -quality 90 pieza-feed.jpg
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Para imprenta es al revés: **no toques el color y no recomprimas**. Si el cliente
|
|
74
|
+
pide CMYK, avisa de que una conversión sin el perfil de la imprenta cambia el
|
|
75
|
+
tono, y que el archivo bueno sale del programa de maquetación, no de aquí.
|
|
76
|
+
|
|
77
|
+
## Zona segura del carrusel
|
|
78
|
+
|
|
79
|
+
Los puntitos del paso de página y la flecha de "siguiente" se comen los bordes:
|
|
80
|
+
deja **60 px libres abajo** y unos 40 a los lados. La primera diapositiva es la
|
|
81
|
+
que decide si alguien desliza, y la última es la llamada a la acción — si el CSV
|
|
82
|
+
no las trae aparte, pregunta en vez de repetir la plantilla.
|
|
83
|
+
|
|
84
|
+
## Revisar y entregar
|
|
85
|
+
|
|
86
|
+
Todo de un vistazo antes de mandarlo:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
magick montage slide-*.png -tile 5x -geometry 300x300+8+8 revision.jpg
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Carrusel de LinkedIn (va en PDF, una página por diapositiva):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
magick slide-*.png -quality 95 carrusel.pdf
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Y el envío: una carpeta por formato, nombres sin acentos ni espacios, y un ZIP.
|
|
99
|
+
Al terminar di cuántos archivos salieron y cuánto pesa el ZIP — si pasa de 25 MB
|
|
100
|
+
no entra en un correo, y más vale saberlo antes de mandarlo.
|
package/package.json
CHANGED