@getparallel/parx 0.5.0 → 0.7.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.
package/dist/firmas.js ADDED
@@ -0,0 +1,55 @@
1
+ import { createHash, createVerify } from 'node:crypto';
2
+ export const CLAVES_DE_NPM = [
3
+ {
4
+ keyid: 'SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U',
5
+ key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==',
6
+ },
7
+ ];
8
+ const REGISTRO_DE_NPM = 'https://registry.npmjs.org';
9
+ export function keyidDe(spkiBase64) {
10
+ return `SHA256:${createHash('sha256').update(Buffer.from(spkiBase64, 'base64')).digest('base64').replace(/=+$/, '')}`;
11
+ }
12
+ export function clavesDeConfianza(env = process.env) {
13
+ const claves = [...CLAVES_DE_NPM];
14
+ const registro = (env.PARX_REGISTRY ?? '').replace(/\/+$/, '');
15
+ if (registro && registro !== REGISTRO_DE_NPM && env.PARX_CLAVES_DE_FIRMA) {
16
+ for (const k of env.PARX_CLAVES_DE_FIRMA.split(',').map((x) => x.trim()).filter(Boolean))
17
+ claves.push({ keyid: keyidDe(k), key: k });
18
+ }
19
+ return claves;
20
+ }
21
+ export function mensajeFirmado(nombre, version, integrity) {
22
+ return `${nombre}@${version}:${integrity}`;
23
+ }
24
+ export function firmaValida(d, claves = CLAVES_DE_NPM) {
25
+ if (!Array.isArray(d.firmas) || !d.firmas.length)
26
+ return false;
27
+ if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(d.integrity))
28
+ return false;
29
+ const mensaje = mensajeFirmado(d.nombre, d.version, d.integrity);
30
+ for (const f of d.firmas) {
31
+ if (!f || typeof f !== 'object')
32
+ continue;
33
+ const { keyid, sig } = f;
34
+ if (typeof keyid !== 'string' || typeof sig !== 'string')
35
+ continue;
36
+ const clave = claves.find((k) => k.keyid === keyid);
37
+ if (!clave)
38
+ continue;
39
+ try {
40
+ const v = createVerify('SHA256');
41
+ v.update(mensaje);
42
+ if (v.verify(`-----BEGIN PUBLIC KEY-----\n${clave.key}\n-----END PUBLIC KEY-----`, sig, 'base64'))
43
+ return true;
44
+ }
45
+ catch {
46
+ }
47
+ }
48
+ return false;
49
+ }
50
+ export function integridadDe(contenido) {
51
+ return `sha512-${createHash('sha512').update(contenido).digest('base64')}`;
52
+ }
53
+ export function integridadCoincide(contenido, integrity) {
54
+ return integridadDe(contenido) === integrity;
55
+ }
package/dist/marca.js CHANGED
@@ -125,27 +125,64 @@ export function logo() {
125
125
  const tonos = [c.marca, c.claro, c.claro, c.violeta];
126
126
  return filas.map((f, i) => tonos[i](f));
127
127
  }
128
- export const ANCHO_DE_PAR = 6;
129
- export function cabezaDePar() {
130
- return caps.unicode ? c.claro('▗▄▄▄▄▖') : c.claro(' .--. ');
128
+ export const ANCHO_DE_PAR = 8;
129
+ function miniPar(gesto) {
130
+ const ojo = gesto === 'error' ? 'R' : 'E';
131
+ const fila1 = ['.', '.', 'B', 'B', '.', 'B', 'B', '.'];
132
+ if (gesto === 'pregunta') {
133
+ fila1.splice(0, 8, '.', '.', ojo, ojo, '.', ojo, ojo, '.');
134
+ }
135
+ else if (gesto !== 'parpadeo') {
136
+ const izquierda = gesto === 1 || gesto === 2;
137
+ const a = izquierda ? 2 : 3;
138
+ fila1[a] = ojo;
139
+ fila1[a + 3] = ojo;
140
+ }
141
+ return [
142
+ ['.', '.', '.', 'B', 'B', '.', 'B', 'B'],
143
+ fila1,
144
+ ['.', 'B', 'B', '.', 'B', 'B', '.', '.'],
145
+ ['B', 'B', '.', 'B', 'B', '.', '.', '.'],
146
+ ];
131
147
  }
132
- export function caraDePar(gesto = 'quieto') {
133
- const u = caps.unicode;
134
- const ojos = gesto === 'parpadeo'
135
- ? u ? ' ── ' : ' -- '
136
- : gesto === 'pregunta'
137
- ? u ? ' •• ' : ' oo '
138
- : gesto === 'error'
139
- ? u ? ' ×× ' : ' xx '
140
- : typeof gesto === 'number'
141
- ?
142
- (u ? ['╱╱ ', ' ╱╱ ', ' ╱╱', ' ╱╱ '] : ['// ', ' // ', ' //', ' // '])[gesto]
143
- : u ? ' ╱╱ ' : ' // ';
144
- if (caps.color) {
145
- const fondo = caps.color === 3 ? '\x1b[48;2;139;112;255m' : caps.color === 2 ? '\x1b[48;5;141m' : '\x1b[45m';
146
- return `${fondo}\x1b[1;97m ${ojos} \x1b[22;39;49m`;
148
+ const RGB_DE_PAR = { B: [139, 112, 255], E: [246, 243, 255], R: [255, 107, 107] };
149
+ const C256_DE_PAR = { B: 141, E: 255, R: 203 };
150
+ function filaDePar(arriba, abajo) {
151
+ let out = '';
152
+ for (let x = 0; x < ANCHO_DE_PAR; x++) {
153
+ const a = arriba[x];
154
+ const b = abajo[x];
155
+ if (!caps.unicode || !caps.color) {
156
+ const hay = a !== '.' || b !== '.';
157
+ const ojo = a === 'E' || a === 'R' || b === 'E' || b === 'R';
158
+ out += ojo ? 'o' : hay ? (caps.unicode ? '█' : '/') : ' ';
159
+ continue;
160
+ }
161
+ if (a === '.' && b === '.') {
162
+ out += ' ';
163
+ continue;
164
+ }
165
+ const fg = a === '.' ? null : a;
166
+ const bg = b === '.' ? null : b;
167
+ const color = (p, fondo) => caps.color === 3 ? `\x1b[${fondo ? 48 : 38};2;${RGB_DE_PAR[p].join(';')}m` : caps.color === 2 ? `\x1b[${fondo ? 48 : 38};5;${C256_DE_PAR[p]}m` : p === 'B' ? `\x1b[${fondo ? 45 : 35}m` : p === 'R' ? `\x1b[${fondo ? 41 : 31}m` : `\x1b[${fondo ? 47 : 97}m`;
168
+ if (fg && bg && fg === bg)
169
+ out += `${color(fg, false)}█\x1b[39m`;
170
+ else if (fg && bg)
171
+ out += `${color(fg, false)}${color(bg, true)}▀\x1b[39;49m`;
172
+ else if (fg)
173
+ out += `${color(fg, false)}▀\x1b[39m`;
174
+ else
175
+ out += `${color(bg, false)}▄\x1b[39m`;
147
176
  }
148
- return u ? `▐${ojos}▌` : `[${ojos}]`;
177
+ return out;
178
+ }
179
+ export function cabezaDePar(gesto = 'quieto') {
180
+ const p = miniPar(gesto);
181
+ return filaDePar(p[0], p[1]);
182
+ }
183
+ export function caraDePar(gesto = 'quieto') {
184
+ const p = miniPar(gesto);
185
+ return filaDePar(p[2], p[3]);
149
186
  }
150
187
  export const ANCHO_DE_ANIMACION = 8;
151
188
  export function animacion(tipo, paso) {
package/dist/mision.js CHANGED
@@ -1,12 +1,13 @@
1
1
  export const MAX_OBJETIVO = 600;
2
- export function objetivoDeMision(meta, diario) {
2
+ export function objetivoDeMision(meta, diario, vista = null) {
3
3
  const ultima = [...diario].reverse().find((d) => d.pedido.trim());
4
4
  const que = (meta.trim() || ultima?.pedido.trim() || '').replace(/\s+/g, ' ');
5
5
  if (que.length < 4)
6
6
  return null;
7
7
  const ficheros = [...new Set(diario.flatMap((d) => d.ficheros))].slice(0, 6);
8
- const contexto = ultima
9
- ? ` Context from the terminal (parx): ${ficheros.length ? `changed ${ficheros.join(', ')}. ` : ''}${ultima.respuesta ? `Last result: ${ultima.respuesta.replace(/\s+/g, ' ')}` : ''}`
8
+ const enlace = vista ? `preview of the website (temporary link, valid 24 hours): ${vista}. ` : '';
9
+ const contexto = ultima || vista
10
+ ? ` Context from the terminal (parx): ${enlace}${ficheros.length ? `changed ${ficheros.join(', ')}. ` : ''}${ultima?.respuesta ? `Last result: ${ultima.respuesta.replace(/\s+/g, ' ')}` : ''}`
10
11
  : '';
11
12
  const base = que.length > MAX_OBJETIVO ? `${que.slice(0, MAX_OBJETIVO - 1)}…` : que;
12
13
  const sitio = MAX_OBJETIVO - base.length;
package/dist/par.js ADDED
@@ -0,0 +1,355 @@
1
+ export const LADO = 16;
2
+ export const TONOS = {
3
+ 300: { cuerpo: '#ae9bff', sombra: '#8b70ff', detalle: '#ddd3ff' },
4
+ 400: { cuerpo: '#8b70ff', sombra: '#6d4aff', detalle: '#ae9bff' },
5
+ 500: { cuerpo: '#7b5cff', sombra: '#5a36e6', detalle: '#ae9bff' },
6
+ 600: { cuerpo: '#6d4aff', sombra: '#5a36e6', detalle: '#ae9bff' },
7
+ 700: { cuerpo: '#5a36e6', sombra: '#4626ba', detalle: '#8b70ff' },
8
+ };
9
+ export const ORDEN_DE_TONOS = [600, 400, 700, 500, 300];
10
+ export const TINTA = '#15131f';
11
+ export const CLARO = '#d3cde8';
12
+ export const ORO = '#f2b632';
13
+ function huella(s) {
14
+ let h = 2166136261;
15
+ for (let i = 0; i < s.length; i++) {
16
+ h ^= s.charCodeAt(i);
17
+ h = Math.imul(h, 16777619);
18
+ }
19
+ return h >>> 0;
20
+ }
21
+ export function tonoDe(semilla) {
22
+ if (!semilla)
23
+ return 600;
24
+ return ORDEN_DE_TONOS[huella(semilla) % ORDEN_DE_TONOS.length];
25
+ }
26
+ export function tonosDelEquipo(semillas) {
27
+ const usados = new Set();
28
+ return semillas.map((s) => {
29
+ let t = tonoDe(s);
30
+ if (usados.size < ORDEN_DE_TONOS.length) {
31
+ let i = ORDEN_DE_TONOS.indexOf(t);
32
+ while (usados.has(t)) {
33
+ i = (i + 1) % ORDEN_DE_TONOS.length;
34
+ t = ORDEN_DE_TONOS[i];
35
+ }
36
+ }
37
+ usados.add(t);
38
+ return t;
39
+ });
40
+ }
41
+ export function colorDe(c, tono) {
42
+ const t = TONOS[tono];
43
+ switch (c) {
44
+ case 'B':
45
+ return t.cuerpo;
46
+ case 'D':
47
+ return t.sombra;
48
+ case 'A':
49
+ return t.detalle;
50
+ case 'E':
51
+ return TINTA;
52
+ case 'W':
53
+ return CLARO;
54
+ case 'Y':
55
+ return ORO;
56
+ default:
57
+ return null;
58
+ }
59
+ }
60
+ const INICIO = [7, 11];
61
+ const ARRIBA = 2;
62
+ const FILAS = 12;
63
+ const FILA_OJO = ARRIBA + 3;
64
+ function pon(m, x, y, c) {
65
+ if (x >= 0 && x < LADO && y >= 0 && y < LADO)
66
+ m[y][x] = c;
67
+ }
68
+ export function matrizDe(c, accesorio) {
69
+ const m = Array.from({ length: LADO }, () => Array(LADO).fill('.'));
70
+ const ojos = Array.isArray(c.ojos) ? c.ojos : [c.ojos ?? 'abierto', c.ojos ?? 'abierto'];
71
+ [c.izq, c.der].forEach((b, k) => {
72
+ const dx = b?.dx ?? 0;
73
+ const dy = b?.dy ?? 0;
74
+ const estira = b?.estira ?? 0;
75
+ const inicio = INICIO[k];
76
+ const xDe = (y) => inicio - Math.floor((y - ARRIBA) / 2) + dx;
77
+ for (let y = ARRIBA - estira; y <= ARRIBA + FILAS; y++) {
78
+ const color = y === ARRIBA + FILAS ? 'D' : 'B';
79
+ for (let i = 0; i < 3; i++)
80
+ pon(m, xDe(y) + i, y + dy, color);
81
+ }
82
+ const filaOjo = FILA_OJO - estira;
83
+ const fy = filaOjo + dy;
84
+ const cx = xDe(filaOjo) + 1;
85
+ const ojo = ojos[k];
86
+ switch (ojo) {
87
+ case 'abierto':
88
+ pon(m, cx, fy, 'E');
89
+ break;
90
+ case 'cerrado':
91
+ pon(m, cx - 1, fy, 'D');
92
+ pon(m, cx, fy, 'D');
93
+ pon(m, cx + 1, fy, 'D');
94
+ break;
95
+ case 'feliz':
96
+ pon(m, cx, fy - 1, 'E');
97
+ pon(m, cx - 1, fy, 'E');
98
+ pon(m, cx + 1, fy, 'E');
99
+ break;
100
+ case 'grande':
101
+ pon(m, cx, fy - 1, 'E');
102
+ pon(m, cx, fy, 'E');
103
+ break;
104
+ case 'x':
105
+ pon(m, cx - 1, fy - 1, 'E');
106
+ pon(m, cx + 1, fy - 1, 'E');
107
+ pon(m, cx, fy, 'E');
108
+ pon(m, cx - 1, fy + 1, 'E');
109
+ pon(m, cx + 1, fy + 1, 'E');
110
+ break;
111
+ case 'abajo':
112
+ pon(m, cx, fy + 1, 'E');
113
+ break;
114
+ case 'arriba':
115
+ pon(m, cx, fy - 1, 'E');
116
+ break;
117
+ case 'izquierda':
118
+ pon(m, cx - 1, fy, 'E');
119
+ break;
120
+ case 'derecha':
121
+ pon(m, cx + 1, fy, 'E');
122
+ break;
123
+ }
124
+ });
125
+ if (accesorio) {
126
+ for (const [x, y, ch] of PIEZAS[accesorio]) {
127
+ const b = x <= 9 ? c.izq : c.der;
128
+ pon(m, x + (b?.dx ?? 0), y + (b?.dy ?? 0) - (b?.estira ?? 0), ch);
129
+ }
130
+ }
131
+ for (const p of c.extra ?? [])
132
+ pon(m, p[0], p[1], p[2] ?? 'A');
133
+ return m.map((f) => f.join(''));
134
+ }
135
+ const FILAS_DE_ACCESORIOS = {
136
+ gorra: { 0: '...........DDD..', 1: '...........DDDDD' },
137
+ copa: { 0: '.......EAE......', 1: '......EEEEE.....' },
138
+ astronauta: { 0: '........WWWWW...', 1: '......WW.....WW.', 2: '......W.......W.', 3: '......W.......W.' },
139
+ corona: { 0: '...........Y.Y..', 1: '...........YYY..' },
140
+ gafas_sol: { 4: '......WEE.WEE...', 5: '......EEEEEEE...' },
141
+ gafas: { 4: '......YYY.YYY...', 5: '......Y.YYY.Y...', 6: '......YYY.YYY...' },
142
+ bigote: { 7: '.......EEE......' },
143
+ pajarita: { 9: '.....EEAEE......' },
144
+ idea: { 0: '.......YYY......', 1: '......A.Y.A.....' },
145
+ gato: { 0: '...........B.B..', 1: '...........BBB..' },
146
+ fiesta: { 0: '........Y.......', 1: '.......YEY......' },
147
+ auriculares: { 0: '............E...', 1: '...........E.E..', 3: '..........E....E', 4: '..........E....E' },
148
+ cocinero: { 0: '......WWWWW.....', 1: '.......WWW......' },
149
+ pirata: { 4: '......EEE.......', 5: '......EEE.......' },
150
+ bufanda: { 10: '...YYY.YYY......', 11: '........YY......' },
151
+ antena: { 0: '............Y...', 1: '............D...' },
152
+ sombrilla: { 0: '.......DDD......', 1: '......DDDDD.....' },
153
+ nube: { 0: '.......WW.......', 1: '......WWWW......', 2: '.....A..........', 3: '....A...........', 4: '.....A..........' },
154
+ halo: { 0: '.......YYY......' },
155
+ pelo: { 0: '......B.B.B.....', 1: '......BBBBB.....' },
156
+ };
157
+ export const LISTA_DE_ACCESORIOS = Object.keys(FILAS_DE_ACCESORIOS);
158
+ const PIEZAS = Object.fromEntries(LISTA_DE_ACCESORIOS.map((id) => [
159
+ id,
160
+ Object.entries(FILAS_DE_ACCESORIOS[id]).flatMap(([y, fila]) => [...fila].flatMap((ch, x) => (ch === '.' ? [] : [[x, Number(y), ch]]))),
161
+ ]));
162
+ export function accesorioDe(semilla) {
163
+ if (!semilla)
164
+ return null;
165
+ return LISTA_DE_ACCESORIOS[huella(`${semilla}·accesorio`) % LISTA_DE_ACCESORIOS.length];
166
+ }
167
+ export function accesoriosDelEquipo(semillas) {
168
+ const usados = new Set();
169
+ return semillas.map((s) => {
170
+ let a = accesorioDe(s || 'parallel');
171
+ if (usados.size < LISTA_DE_ACCESORIOS.length) {
172
+ let i = LISTA_DE_ACCESORIOS.indexOf(a);
173
+ while (usados.has(a)) {
174
+ i = (i + 1) % LISTA_DE_ACCESORIOS.length;
175
+ a = LISTA_DE_ACCESORIOS[i];
176
+ }
177
+ }
178
+ usados.add(a);
179
+ return a;
180
+ });
181
+ }
182
+ const P = (puntos, dx = 0, dy = 0, c = 'A') => puntos.map(([x, y]) => [x + dx, y + dy, c]);
183
+ const INTERROGACION = [[1, 0], [2, 0], [3, 1], [2, 2], [2, 4]];
184
+ const EXCLAMACION = [[2, 0], [2, 1], [2, 2], [2, 4]];
185
+ const TIC = [[0, 2], [1, 3], [2, 2], [3, 1], [4, 0]];
186
+ const CORAZON = [[1, 0], [3, 0], [0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [1, 2], [2, 2], [3, 2], [2, 3]];
187
+ const ZETA = [[0, 0], [1, 0], [2, 0], [1, 1], [0, 2], [1, 2], [2, 2]];
188
+ const NOTA = [[1, 0], [2, 0], [1, 1], [1, 2], [0, 3], [1, 3]];
189
+ export const ANIMACIONES = {
190
+ reposo: [{ ms: 1500 }, { ojos: 'cerrado', ms: 120 }, { ms: 1800 }, { ojos: 'derecha', ms: 700 }, { ms: 500 }, { izq: { dy: 1 }, der: { dy: 1 }, ms: 400 }, { ms: 900 }],
191
+ saludo: [
192
+ { ojos: 'feliz', ms: 200 },
193
+ { ojos: 'feliz', der: { dx: 1, dy: -1 }, ms: 180 },
194
+ { ojos: 'feliz', ms: 180 },
195
+ { ojos: 'feliz', der: { dx: 1, dy: -1 }, ms: 180 },
196
+ { ojos: 'feliz', ms: 600 },
197
+ ],
198
+ pensando: [
199
+ { ojos: 'arriba', ms: 350 },
200
+ { ojos: 'arriba', extra: P([[2, 1]]), ms: 350 },
201
+ { ojos: 'arriba', extra: P([[2, 1], [4, 1]]), ms: 350 },
202
+ { ojos: 'arriba', extra: P([[2, 1], [4, 1], [6, 0]]), ms: 600 },
203
+ { ojos: 'cerrado', extra: P([[2, 1], [4, 1], [6, 0]]), ms: 120 },
204
+ ],
205
+ trabajando: [
206
+ { izq: { dy: -1 }, extra: P([[1, 9], [15, 3]]), ms: 170 },
207
+ { der: { dy: -1 }, extra: P([[0, 7], [15, 6]]), ms: 170 },
208
+ ],
209
+ escribiendo: [
210
+ { ojos: 'abajo', extra: P([[3, 15]]), ms: 220 },
211
+ { ojos: 'abajo', extra: P([[3, 15], [5, 15]]), ms: 220 },
212
+ { ojos: 'abajo', extra: P([[3, 15], [5, 15], [7, 15]]), ms: 220 },
213
+ { ojos: 'abajo', ms: 220 },
214
+ ],
215
+ buscando: [{ ojos: 'izquierda', ms: 380 }, { ms: 200 }, { ojos: 'derecha', ms: 380 }, { ms: 200 }],
216
+ esperando: [{ ms: 450 }, { extra: P(INTERROGACION, 1, 1), ms: 650 }, { extra: P(INTERROGACION, 1, 1), ojos: 'cerrado', ms: 120 }, { extra: P(INTERROGACION, 1, 1), ms: 650 }],
217
+ hecho: [
218
+ { ms: 200 },
219
+ { izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', ms: 140 },
220
+ { izq: { dy: -2 }, der: { dy: -2 }, ojos: 'feliz', extra: P(TIC, 1, 1), ms: 240 },
221
+ { izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', extra: P(TIC, 1, 1), ms: 140 },
222
+ { ojos: 'feliz', extra: P(TIC, 1, 1), ms: 900 },
223
+ ],
224
+ error: [
225
+ { ojos: 'x', izq: { dx: -1 }, der: { dx: -1 }, ms: 90 },
226
+ { ojos: 'x', izq: { dx: 1 }, der: { dx: 1 }, ms: 90 },
227
+ { ojos: 'x', izq: { dx: -1 }, der: { dx: -1 }, ms: 90 },
228
+ { ojos: 'x', izq: { dx: 1 }, der: { dx: 1 }, ms: 90 },
229
+ { ojos: 'x', ms: 900 },
230
+ ],
231
+ dormido: [
232
+ { ojos: 'cerrado', extra: P(ZETA, 1, 6), ms: 600 },
233
+ { ojos: 'cerrado', izq: { dy: 1 }, der: { dy: 1 }, extra: P(ZETA, 1, 3), ms: 600 },
234
+ { ojos: 'cerrado', extra: P(ZETA, 2, 0), ms: 600 },
235
+ { ojos: 'cerrado', izq: { dy: 1 }, der: { dy: 1 }, ms: 600 },
236
+ ],
237
+ estirarse: [
238
+ { ms: 300 },
239
+ { izq: { estira: 1 }, der: { estira: 1 }, ojos: 'cerrado', ms: 220 },
240
+ { izq: { estira: 2 }, der: { estira: 2 }, ojos: 'cerrado', ms: 500 },
241
+ { izq: { estira: 1 }, der: { estira: 1 }, ojos: 'cerrado', ms: 200 },
242
+ { ojos: 'feliz', ms: 600 },
243
+ ],
244
+ salto: [
245
+ { izq: { dy: 1 }, der: { dy: 1 }, ms: 160 },
246
+ { izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', ms: 120 },
247
+ { izq: { dy: -2 }, der: { dy: -2 }, ojos: 'feliz', ms: 220 },
248
+ { izq: { dy: -1 }, der: { dy: -1 }, ojos: 'feliz', ms: 120 },
249
+ { izq: { dy: 1 }, der: { dy: 1 }, ms: 140 },
250
+ { ms: 500 },
251
+ ],
252
+ baile: [
253
+ { izq: { dy: -1, dx: -1 }, der: { dx: -1 }, ojos: 'feliz', extra: P(NOTA, 0, 1), ms: 220 },
254
+ { izq: { dx: 1 }, der: { dy: -1, dx: 1 }, ojos: 'feliz', ms: 220 },
255
+ { izq: { dy: -1, dx: -1 }, der: { dx: -1 }, ojos: 'feliz', ms: 220 },
256
+ { izq: { dx: 1 }, der: { dy: -1, dx: 1 }, ojos: 'feliz', extra: P(NOTA, 0, 0), ms: 220 },
257
+ ],
258
+ guino: [{ ms: 600 }, { ojos: ['abierto', 'cerrado'], ms: 260 }, { ojos: 'feliz', ms: 700 }],
259
+ corazon: [
260
+ { ojos: 'feliz', extra: P(CORAZON, 0, 3), ms: 260 },
261
+ { ojos: 'feliz', extra: P(CORAZON, 0, 2), ms: 260 },
262
+ { ojos: 'feliz', extra: P(CORAZON, 0, 1), ms: 260 },
263
+ { ojos: 'feliz', extra: P(CORAZON, 0, 0), ms: 700 },
264
+ ],
265
+ fiesta: [
266
+ { ojos: 'feliz', izq: { dy: -1 }, der: { dy: -1 }, extra: P([[1, 2], [4, 0], [15, 1], [14, 9], [0, 11]]), ms: 200 },
267
+ { ojos: 'feliz', extra: P([[2, 4], [5, 1], [14, 3], [15, 11], [1, 13]]), ms: 200 },
268
+ { ojos: 'feliz', izq: { dy: -1 }, der: { dy: -1 }, extra: P([[0, 6], [3, 0], [15, 5], [13, 13], [2, 15]]), ms: 200 },
269
+ { ojos: 'feliz', extra: P([[1, 8], [6, 0], [14, 7], [15, 14], [0, 1]]), ms: 200 },
270
+ ],
271
+ musica: [
272
+ { ojos: 'cerrado', izq: { dx: -1 }, der: { dx: -1 }, extra: P(NOTA, 1, 5), ms: 380 },
273
+ { ojos: 'cerrado', extra: P(NOTA, 1, 3), ms: 380 },
274
+ { ojos: 'cerrado', izq: { dx: 1 }, der: { dx: 1 }, extra: P(NOTA, 2, 1), ms: 380 },
275
+ { ojos: 'cerrado', extra: P(NOTA, 2, 0), ms: 380 },
276
+ ],
277
+ mirar: [{ ojos: 'izquierda', ms: 600 }, { ms: 250 }, { ojos: 'derecha', ms: 600 }, { ojos: 'arriba', ms: 500 }, { ms: 500 }],
278
+ sorpresa: [{ ms: 300 }, { ojos: 'grande', izq: { dy: -1 }, der: { dy: -1 }, extra: P(EXCLAMACION, 1, 1), ms: 200 }, { ojos: 'grande', extra: P(EXCLAMACION, 1, 1), ms: 900 }],
279
+ asomarse: [
280
+ { izq: { dy: 12 }, der: { dy: 12 }, ms: 500 },
281
+ { izq: { dy: 8 }, der: { dy: 8 }, ms: 140 },
282
+ { izq: { dy: 5 }, der: { dy: 5 }, ms: 140 },
283
+ { izq: { dy: 3 }, der: { dy: 3 }, ms: 900 },
284
+ { izq: { dy: 3 }, der: { dy: 3 }, ojos: 'cerrado', ms: 120 },
285
+ { izq: { dy: 3 }, der: { dy: 3 }, ojos: 'derecha', ms: 700 },
286
+ { izq: { dy: 6 }, der: { dy: 6 }, ms: 140 },
287
+ { izq: { dy: 12 }, der: { dy: 12 }, ms: 700 },
288
+ ],
289
+ };
290
+ export const LISTA_DE_ANIMACIONES = Object.keys(ANIMACIONES);
291
+ export const DEL_TRABAJO = ['saludo', 'pensando', 'trabajando', 'escribiendo', 'buscando', 'esperando', 'hecho', 'error'];
292
+ export function animacionDelEstado(estado) {
293
+ switch (estado) {
294
+ case 'trabajando':
295
+ return 'trabajando';
296
+ case 'espera':
297
+ return 'esperando';
298
+ case 'parado':
299
+ return 'error';
300
+ case 'hecho':
301
+ return 'hecho';
302
+ default:
303
+ return 'reposo';
304
+ }
305
+ }
306
+ const cache = new Map();
307
+ export function fotogramasDe(a, accesorio) {
308
+ const clave = `${a}|${accesorio ?? ''}`;
309
+ let f = cache.get(clave);
310
+ if (!f) {
311
+ f = ANIMACIONES[a].map((c) => ({ matriz: matrizDe(c, accesorio), ms: c.ms }));
312
+ cache.set(clave, f);
313
+ }
314
+ return f;
315
+ }
316
+ export function duracionDe(a) {
317
+ return fotogramasDe(a).reduce((s, f) => s + f.ms, 0);
318
+ }
319
+ export function fotogramaEn(a, t, accesorio) {
320
+ const fs = fotogramasDe(a, accesorio);
321
+ const total = duracionDe(a);
322
+ let r = ((t % total) + total) % total;
323
+ for (const f of fs) {
324
+ if (r < f.ms)
325
+ return f;
326
+ r -= f.ms;
327
+ }
328
+ return fs[fs.length - 1];
329
+ }
330
+ export function rectangulosDe(matriz, tono) {
331
+ const out = [];
332
+ matriz.forEach((fila, y) => {
333
+ let x = 0;
334
+ while (x < LADO) {
335
+ const c = fila[x];
336
+ const color = colorDe(c, tono);
337
+ if (!color) {
338
+ x++;
339
+ continue;
340
+ }
341
+ let fin = x + 1;
342
+ while (fin < LADO && fila[fin] === c)
343
+ fin++;
344
+ out.push({ x, y, ancho: fin - x, color });
345
+ x = fin;
346
+ }
347
+ });
348
+ return out;
349
+ }
350
+ export function svgDe(matriz, tono, px = 64) {
351
+ const r = rectangulosDe(matriz, tono)
352
+ .map((q) => `<rect x="${q.x}" y="${q.y}" width="${q.ancho}" height="1" fill="${q.color}"/>`)
353
+ .join('');
354
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${px}" height="${px}" viewBox="0 0 16 16" shape-rendering="crispEdges">${r}</svg>`;
355
+ }