xonecode 0.17.1 → 0.19.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/apps/web/dist/assets/{index-BRVuyPBf.js → index-BvPUqjUm.js} +72 -72
- package/apps/web/dist/assets/{index-DLfF2uEK.css → index-CGEXMJBx.css} +1 -1
- package/apps/web/dist/assets/{php-B5LpMCn-.js → php-Dk28VW45.js} +1 -1
- package/apps/web/dist/assets/{ruby-Clm-qfwM.js → ruby-CPzEcmdp.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/config/iconosEnRed.js +47 -0
- package/dist/agent/config/iconosEnRed.js.map +1 -0
- package/dist/agent/dispositivos/decodificarImagen.js +62 -0
- package/dist/agent/dispositivos/decodificarImagen.js.map +1 -0
- package/dist/agent/grafo/buscarIcono.js +92 -0
- package/dist/agent/grafo/buscarIcono.js.map +1 -0
- package/dist/agent/grafo/compararCapturas.js +103 -0
- package/dist/agent/grafo/compararCapturas.js.map +1 -0
- package/dist/agent/grafo/criticaVisual.js +23 -3
- package/dist/agent/grafo/criticaVisual.js.map +1 -1
- package/dist/agent/grafo/diferenciaDeCapturas.js +88 -0
- package/dist/agent/grafo/diferenciaDeCapturas.js.map +1 -0
- package/dist/agent/grafo/generarFondoSvg.js +83 -0
- package/dist/agent/grafo/generarFondoSvg.js.map +1 -0
- package/dist/agent/grafo/lectorDeReferencias.js +19 -0
- package/dist/agent/grafo/lectorDeReferencias.js.map +1 -0
- package/dist/agent/grafo/xoneAgent.js +8 -1
- package/dist/agent/grafo/xoneAgent.js.map +1 -1
- package/dist/agent/motores/trueforge/eventosTrueforge.js +11 -1
- package/dist/agent/motores/trueforge/eventosTrueforge.js.map +1 -1
- package/dist/agent/motores/trueforge/modeloLangchain.js +5 -0
- package/dist/agent/motores/trueforge/modeloLangchain.js.map +1 -1
- package/dist/agent/motores/trueforge/sesionTrueforge.js +67 -6
- package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
- package/dist/agent/subagentes/agentesEnDisco.js +26 -2
- package/dist/agent/subagentes/agentesEnDisco.js.map +1 -1
- package/dist/agent/turno/diagnosticoDeTools.js +32 -3
- package/dist/agent/turno/diagnosticoDeTools.js.map +1 -1
- package/dist/agent/turno/informeDeTraza.js +54 -4
- package/dist/agent/turno/informeDeTraza.js.map +1 -1
- package/dist/agent/turno/registroDeFallos.js +6 -1
- package/dist/agent/turno/registroDeFallos.js.map +1 -1
- package/dist/agent/turno/resumenDeTool.js +9 -0
- package/dist/agent/turno/resumenDeTool.js.map +1 -1
- package/dist/agent/turno/turnoReal.js +8 -2
- package/dist/agent/turno/turnoReal.js.map +1 -1
- package/dist/cli/run.js +11 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/core/agentes.js +29 -1
- package/dist/core/agentes.js.map +1 -1
- package/dist/core/analisisDeSesion.js +198 -0
- package/dist/core/analisisDeSesion.js.map +1 -0
- package/dist/core/compararCapturas.js +339 -0
- package/dist/core/compararCapturas.js.map +1 -0
- package/dist/core/fallos.js.map +1 -1
- package/dist/core/fondosSvg.js +223 -0
- package/dist/core/fondosSvg.js.map +1 -0
- package/dist/core/iconos.js +135 -0
- package/dist/core/iconos.js.map +1 -0
- package/dist/core/modoDeEscritura.js +22 -0
- package/dist/core/modoDeEscritura.js.map +1 -1
- package/dist/core/notify.js +8 -0
- package/dist/core/notify.js.map +1 -1
- package/dist/core/nucleoXone.js +111 -0
- package/dist/core/nucleoXone.js.map +1 -0
- package/dist/core/paqueteDeSoporte.js +118 -0
- package/dist/core/paqueteDeSoporte.js.map +1 -0
- package/dist/core/ports.js +31 -0
- package/dist/core/ports.js.map +1 -1
- package/dist/core/referenciasDeImagen.js +55 -0
- package/dist/core/referenciasDeImagen.js.map +1 -0
- package/dist/web/servidor/arranque.js +110 -0
- package/dist/web/servidor/arranque.js.map +1 -1
- package/dist/web/servidor/sesiones.js +8 -0
- package/dist/web/servidor/sesiones.js.map +1 -1
- package/dist/web/servidor/soporte.js +571 -0
- package/dist/web/servidor/soporte.js.map +1 -0
- package/dist/web/servidor/transporte.js.map +1 -1
- package/package.json +4 -1
- package/skills/artifacts-builder/reference/estilo.md +3 -6
- package/skills/xone-development/SKILL.md +1 -5
- package/skills/xone-development/references/css/propiedades-y-herencia.md +2 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +15 -3
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +2 -0
- package/skills/xone-hotswap/SKILL.md +10 -0
- package/skills/xone-hotswap/references/comandos.md +14 -0
- package/skills/xone-hotswap/scripts/xone-recargar-android +269 -0
- package/skills/xone-plan-builder/SKILL.md +8 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +8 -0
- package/skills/xone-project-generator/SKILL.md +1 -1
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +1 -0
package/apps/web/dist/index.html
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<!-- El icono oficial, servido por Vite desde `iconos/`. Es el mismo fichero que la
|
|
8
8
|
marca de la barra superior: dos copias del logo acaban siendo dos logos. -->
|
|
9
9
|
<link rel="icon" type="image/png" href="./iconos/xonecode.png" />
|
|
10
|
-
<script type="module" crossorigin src="./assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
10
|
+
<script type="module" crossorigin src="./assets/index-BvPUqjUm.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CGEXMJBx.css">
|
|
12
12
|
</head>
|
|
13
13
|
<!-- Sin el atributo aquí: el oscuro es SOLO del splash (el registro de entorno y
|
|
14
14
|
modelo), no de la consola entera — `App.tsx` lo pone y lo quita él mismo, en el
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { idsDeBusqueda, motivoDeSvgInaceptable, TOPE_DE_ICONOS_MS, urlDeBusqueda, urlDeSvg, } from "../../core/iconos.js";
|
|
2
|
+
/**
|
|
3
|
+
* Iconify, de verdad (IXCODE-18). Los mismos datos que usa `better-icons`, pero llamando a la API
|
|
4
|
+
* directamente: el binario no lo puede ejecutar ningún especialista, y una dependencia global
|
|
5
|
+
* que instalar es un fallo del entorno que no merece la pena.
|
|
6
|
+
*
|
|
7
|
+
* Un fallo lleva el CÓDIGO HTTP o la palabra «red», nunca la URL ni el cuerpo remoto (mismo trato
|
|
8
|
+
* que `ErrorCatalogoModelos`). `fetch` entra por parámetro para probarlo sin conexión.
|
|
9
|
+
*/
|
|
10
|
+
export function iconosEnRed(llamar = fetch, topeMs = TOPE_DE_ICONOS_MS) {
|
|
11
|
+
const pedir = async (url) => {
|
|
12
|
+
let respuesta;
|
|
13
|
+
try {
|
|
14
|
+
respuesta = await llamar(url, { signal: AbortSignal.timeout(topeMs) });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error("no hay red o Iconify no responde");
|
|
18
|
+
}
|
|
19
|
+
if (!respuesta.ok)
|
|
20
|
+
throw new Error(`Iconify contestó ${respuesta.status}`);
|
|
21
|
+
return respuesta;
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
async buscar(consulta, opciones) {
|
|
25
|
+
const respuesta = await pedir(urlDeBusqueda(consulta, opciones.limite, opciones.prefijo));
|
|
26
|
+
let cuerpo;
|
|
27
|
+
try {
|
|
28
|
+
cuerpo = await respuesta.json();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new Error("respuesta incompatible de Iconify");
|
|
32
|
+
}
|
|
33
|
+
// Iconify NO respeta límites pequeños (medido: pidiendo 3 contestó con limit 32), así que se
|
|
34
|
+
// recorta aquí: el doble ya lo hacía, y un puerto real que no coincida con su doble miente.
|
|
35
|
+
return idsDeBusqueda(cuerpo).slice(0, opciones.limite);
|
|
36
|
+
},
|
|
37
|
+
async svg(id, opciones) {
|
|
38
|
+
const respuesta = await pedir(urlDeSvg(id, opciones.color, opciones.tamano));
|
|
39
|
+
const svg = await respuesta.text();
|
|
40
|
+
const motivo = motivoDeSvgInaceptable(svg);
|
|
41
|
+
if (motivo !== undefined)
|
|
42
|
+
throw new Error(motivo);
|
|
43
|
+
return svg;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=iconosEnRed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iconosEnRed.js","sourceRoot":"","sources":["../../../src/agent/config/iconosEnRed.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,aAAa,EAAE,QAAQ,GAClF,MAAM,sBAAsB,CAAC;AAG9B;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,SAAuB,KAAK,EAC5B,SAAiB,iBAAiB;IAElC,MAAM,KAAK,GAAG,KAAK,EAAE,GAAW,EAAqB,EAAE;QACrD,IAAI,SAAmB,CAAC;QACxB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3E,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ;YAC7B,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1F,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YAClC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;YACD,6FAA6F;YAC7F,4FAA4F;YAC5F,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ;YACpB,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7E,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;YAClD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import jpeg from "jpeg-js";
|
|
2
|
+
import { PNG } from "pngjs";
|
|
3
|
+
/**
|
|
4
|
+
* De un PNG o un JPEG a píxeles RGBA, para `core/compararCapturas.ts` (que no importa librerías).
|
|
5
|
+
*
|
|
6
|
+
* **Se comprueban las dimensiones ANTES de descomprimir.** Un PNG de unos cientos de bytes puede
|
|
7
|
+
* declarar 60.000×60.000 y agotar la memoria al abrirlo (una bomba de descompresión, la misma
|
|
8
|
+
* clase que el `.zip` de una skill), así que se lee la cabecera —los primeros bytes— y se rechaza
|
|
9
|
+
* lo que pase de `MAX_LADO`/`MAX_PIXELES` sin decodificar nada. Una captura de móvil va de
|
|
10
|
+
* 1080×2400 a 1440×3200 (~4,6 millones), lejos del tope.
|
|
11
|
+
*
|
|
12
|
+
* **Lanza con un mensaje sin ruta ni contenido**: quien lo llama (la tool) lo devuelve como texto.
|
|
13
|
+
*/
|
|
14
|
+
export const MAX_LADO = 8192;
|
|
15
|
+
export const MAX_PIXELES = 40_000_000;
|
|
16
|
+
export const MAX_BYTES = 30 * 1024 * 1024;
|
|
17
|
+
const FIRMA_PNG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
18
|
+
function dimensionesPng(b) {
|
|
19
|
+
// Firma (8) + longitud del trozo (4) + «IHDR» (4) + ancho (4) + alto (4).
|
|
20
|
+
if (b.length < 24 || b.toString("ascii", 12, 16) !== "IHDR")
|
|
21
|
+
throw new Error("PNG con la cabecera dañada");
|
|
22
|
+
return { ancho: b.readUInt32BE(16), alto: b.readUInt32BE(20) };
|
|
23
|
+
}
|
|
24
|
+
function dimensionesJpeg(b) {
|
|
25
|
+
// Se recorren los marcadores hasta el de tamaño (SOF0–SOF15, salvo DHT/JPG/DAC).
|
|
26
|
+
let i = 2;
|
|
27
|
+
while (i + 9 < b.length) {
|
|
28
|
+
if (b[i] !== 0xff)
|
|
29
|
+
throw new Error("JPEG con la cabecera dañada");
|
|
30
|
+
const marcador = b[i + 1];
|
|
31
|
+
if (marcador >= 0xc0 && marcador <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marcador)) {
|
|
32
|
+
return { alto: b.readUInt16BE(i + 5), ancho: b.readUInt16BE(i + 7) };
|
|
33
|
+
}
|
|
34
|
+
i += 2 + b.readUInt16BE(i + 2);
|
|
35
|
+
}
|
|
36
|
+
throw new Error("JPEG sin tamaño declarado");
|
|
37
|
+
}
|
|
38
|
+
export function decodificarImagen(bytes) {
|
|
39
|
+
if (bytes.length > MAX_BYTES)
|
|
40
|
+
throw new Error("la imagen es demasiado grande");
|
|
41
|
+
const esPng = bytes.length >= 8 && bytes.subarray(0, 8).equals(FIRMA_PNG);
|
|
42
|
+
const esJpeg = bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff;
|
|
43
|
+
if (!esPng && !esJpeg)
|
|
44
|
+
throw new Error("no es un PNG ni un JPEG");
|
|
45
|
+
const { ancho, alto } = esPng ? dimensionesPng(bytes) : dimensionesJpeg(bytes);
|
|
46
|
+
if (ancho < 1 || alto < 1 || ancho > MAX_LADO || alto > MAX_LADO || ancho * alto > MAX_PIXELES) {
|
|
47
|
+
throw new Error(`la imagen declara ${ancho}×${alto} píxeles, por encima del tope`);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
if (esPng) {
|
|
51
|
+
const png = PNG.sync.read(bytes);
|
|
52
|
+
return { ancho: png.width, alto: png.height, datos: png.data };
|
|
53
|
+
}
|
|
54
|
+
const j = jpeg.decode(bytes, { useTArray: true, formatAsRGBA: true, maxResolutionInMP: 40 });
|
|
55
|
+
return { ancho: j.width, alto: j.height, datos: j.data };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// El mensaje de la librería puede llevar trozos del fichero: solo se dice que no se pudo.
|
|
59
|
+
throw new Error("la imagen está dañada y no se pudo decodificar");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=decodificarImagen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decodificarImagen.js","sourceRoot":"","sources":["../../../src/agent/dispositivos/decodificarImagen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { tool } from "@langchain/core/tools";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { colorParaIconify, LIMITES_DE_BUSQUEDA, LIMITES_DE_TAMANO, motivoDeColorInaceptable, motivoDeIdInaceptable, motivoDePrefijoInaceptable, NOMBRE_BUSCAR_ICONO, nombreDeFicheroDeIcono, recibeBuscarIcono, rutaDeIcono, } from "../../core/iconos.js";
|
|
4
|
+
export { NOMBRE_BUSCAR_ICONO, recibeBuscarIcono };
|
|
5
|
+
/**
|
|
6
|
+
* Buscar un icono y traer su SVG, para quien no tiene los assets (IXCODE-18).
|
|
7
|
+
*
|
|
8
|
+
* ## Es de LECTURA, y no escribe nada
|
|
9
|
+
*
|
|
10
|
+
* Un SVG es texto: lo escribe `write_file`, con su diff y su aprobación de siempre. Una tool de
|
|
11
|
+
* red que además escribiera sería un segundo camino al proyecto que habría que cerrar a mano
|
|
12
|
+
* (`incorporar_adjunto` existe porque un PNG NO sobrevive a `write(content: string)`; un SVG sí).
|
|
13
|
+
* **Contrapartida declarada**: el modelo reteclea el `path` del SVG al escribirlo. Son unos
|
|
14
|
+
* cientos de caracteres y el diff lo enseña, pero un carácter mal copiado sale en el fichero.
|
|
15
|
+
*
|
|
16
|
+
* ## Los rechazos se DEVUELVEN, nunca se lanzan
|
|
17
|
+
*
|
|
18
|
+
* Una excepción se lleva el turno y el agente no reintenta. Sin red, la tool lo DICE y no
|
|
19
|
+
* devuelve ningún SVG: la alternativa, que el modelo dibuje uno a mano, es justo lo que esto
|
|
20
|
+
* existe para evitar. De un fallo de red solo viaja su mensaje ya saneado (`iconosEnRed.ts`).
|
|
21
|
+
*
|
|
22
|
+
* El color y la altura son OBLIGATORIOS en `obtener` (motivo en `core/iconos.ts`).
|
|
23
|
+
*/
|
|
24
|
+
const ESQUEMA = z.object({
|
|
25
|
+
operacion: z.enum(["buscar", "obtener"]).describe("buscar: lista ids por texto. obtener: devuelve el SVG de un id."),
|
|
26
|
+
consulta: z.string().min(1).max(64).optional().describe("Solo en «buscar». En inglés rinde más (home, arrow, trash)."),
|
|
27
|
+
prefijo: z.string().max(32).optional().describe("Solo en «buscar»: limita a una colección (lucide, mdi, tabler…)."),
|
|
28
|
+
limite: z.coerce.number().int().min(1).max(LIMITES_DE_BUSQUEDA.maximo).optional()
|
|
29
|
+
.describe(`Solo en «buscar». Por omisión ${LIMITES_DE_BUSQUEDA.porOmision}.`),
|
|
30
|
+
id: z.string().max(80).optional().describe("Solo en «obtener»: prefijo:nombre, tal como sale de «buscar»."),
|
|
31
|
+
color: z.string().max(32).optional().describe("Obligatorio en «obtener»: #RRGGBB, o #FFRRGGBB de XOne si es opaco (#1a73e8)."),
|
|
32
|
+
tamano: z.coerce.number().int().min(LIMITES_DE_TAMANO.minimo).max(LIMITES_DE_TAMANO.maximo).optional()
|
|
33
|
+
.describe(`Solo en «obtener»: altura en píxeles. Por omisión ${LIMITES_DE_TAMANO.porOmision}.`),
|
|
34
|
+
});
|
|
35
|
+
export function crearBuscarIcono(iconos) {
|
|
36
|
+
return tool(async (entrada) => {
|
|
37
|
+
try {
|
|
38
|
+
if (entrada.operacion === "buscar")
|
|
39
|
+
return await buscar(iconos, entrada);
|
|
40
|
+
return await obtener(iconos, entrada);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const causa = error instanceof Error ? error.message : "fallo desconocido";
|
|
44
|
+
return `No se pudo completar: ${causa}. No dibujes el icono a mano: dilo, o usa un asset del proyecto.`;
|
|
45
|
+
}
|
|
46
|
+
}, {
|
|
47
|
+
name: NOMBRE_BUSCAR_ICONO,
|
|
48
|
+
description: "Busca iconos en Iconify (más de 200 colecciones) y devuelve el SVG de uno, para botones, " +
|
|
49
|
+
"menús y pestañas cuando el proyecto no trae los assets. Dos pasos: «buscar» con un texto y " +
|
|
50
|
+
"«obtener» con un id de la lista, un color hexadecimal y una altura. Solo LEE: el SVG lo " +
|
|
51
|
+
"escribes tú con write_file en la ruta que te indica. XOne pinta SVG nativo y busca el " +
|
|
52
|
+
'fichero en icons/: se usa con img="ic_x.svg" (o type="IMG" path="ic_x.svg"), nombre a secas ' +
|
|
53
|
+
'y nunca con type="WEB". Sin red te lo dice.',
|
|
54
|
+
schema: ESQUEMA,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function buscar(iconos, e) {
|
|
58
|
+
if (e.consulta === undefined)
|
|
59
|
+
return "«buscar» necesita `consulta`.";
|
|
60
|
+
if (e.prefijo !== undefined) {
|
|
61
|
+
const motivo = motivoDePrefijoInaceptable(e.prefijo);
|
|
62
|
+
if (motivo !== undefined)
|
|
63
|
+
return motivo;
|
|
64
|
+
}
|
|
65
|
+
const limite = e.limite ?? LIMITES_DE_BUSQUEDA.porOmision;
|
|
66
|
+
const ids = await iconos.buscar(e.consulta, { limite, ...(e.prefijo === undefined ? {} : { prefijo: e.prefijo }) });
|
|
67
|
+
if (ids.length === 0)
|
|
68
|
+
return `Ningún icono para «${e.consulta}». Prueba otro texto, en inglés, o sin prefijo.`;
|
|
69
|
+
return [
|
|
70
|
+
`${ids.length} iconos para «${e.consulta}» (elige UNA colección para toda la app, así se ven a juego):`,
|
|
71
|
+
...ids,
|
|
72
|
+
"Siguiente paso: buscar_icono con operacion «obtener», el id, un color hexadecimal y la altura.",
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
async function obtener(iconos, e) {
|
|
76
|
+
if (e.id === undefined)
|
|
77
|
+
return "«obtener» necesita `id`.";
|
|
78
|
+
if (e.color === undefined) {
|
|
79
|
+
return "«obtener» necesita `color` en hexadecimal (#1a73e8): XOne no documenta currentColor y un icono sin tinte válido sale negro sin avisar.";
|
|
80
|
+
}
|
|
81
|
+
const problema = motivoDeIdInaceptable(e.id) ?? motivoDeColorInaceptable(e.color);
|
|
82
|
+
if (problema !== undefined)
|
|
83
|
+
return problema;
|
|
84
|
+
const tamano = e.tamano ?? LIMITES_DE_TAMANO.porOmision;
|
|
85
|
+
const color = colorParaIconify(e.color);
|
|
86
|
+
const svg = await iconos.svg(e.id, { color, tamano });
|
|
87
|
+
return [
|
|
88
|
+
`SVG de ${e.id} (${e.color}, ${tamano}px). Escríbelo TAL CUAL con write_file en ${rutaDeIcono(e.id)} y refiérelo con el nombre a secas: img="${nombreDeFicheroDeIcono(e.id)}" (XOne lo busca en icons/).`,
|
|
89
|
+
svg,
|
|
90
|
+
].join("\n");
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=buscarIcono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buscarIcono.js","sourceRoot":"","sources":["../../../src/agent/grafo/buscarIcono.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { tool } from "@langchain/core/tools";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { esRutaDeArtefacto, mimeDeArtefacto, nombreDeArtefacto, rutaRelativaDeArtefacto, } from "../../core/artefactos.js";
|
|
4
|
+
import { compararPantallas, informeDeComparacion } from "../../core/compararCapturas.js";
|
|
5
|
+
import { imagenReferida } from "../../core/referenciasDeImagen.js";
|
|
6
|
+
import { decodificarImagen } from "../dispositivos/decodificarImagen.js";
|
|
7
|
+
export const NOMBRE_COMPARAR_CAPTURAS = "comparar_capturas";
|
|
8
|
+
/**
|
|
9
|
+
* `comparar_capturas`: MEDIR si una pantalla tiene la estructura de una maqueta (IXCODE-18).
|
|
10
|
+
*
|
|
11
|
+
* **Es la otra mitad de `xone_critica_visual`, no su sustituta.** El crítico tiene visión y
|
|
12
|
+
* opina —forma, texto cortado, estilo— pero es un modelo: se equivoca, y un agente puede
|
|
13
|
+
* descartarlo como «no fiable» (pasó, con el crítico acertado). Esto no opina: **mide**. Dónde
|
|
14
|
+
* hay contenido, hasta dónde llega, qué franjas difieren de la referencia. Determinista, sin
|
|
15
|
+
* modelo y sin necesitar visión (`core/compararCapturas.ts` tiene el porqué y los límites).
|
|
16
|
+
*
|
|
17
|
+
* Va con el orquestador, como el crítico, y con las MISMAS guardas: las dos rutas bajo
|
|
18
|
+
* `/artefactos/` (lista BLANCA de forma) y se comprueban las dos ANTES de abrir ninguna. Solo
|
|
19
|
+
* lee de la carpeta de artefactos de la sesión, no toca el aparato y no escribe nada.
|
|
20
|
+
*
|
|
21
|
+
* **Todo rechazo se DEVUELVE como texto**: una excepción se lleva el turno.
|
|
22
|
+
*/
|
|
23
|
+
const Entrada = z.object({
|
|
24
|
+
referencia: z
|
|
25
|
+
.string()
|
|
26
|
+
.describe("La ruta virtual de la MAQUETA o diseño con el que se compara: bajo /artefactos/, bajo /adjuntos/ (lo que " +
|
|
27
|
+
"adjuntó la persona) o un PNG/JPEG del proyecto (/diseno/screen.png)"),
|
|
28
|
+
captura: z
|
|
29
|
+
.string()
|
|
30
|
+
.describe("La ruta virtual, bajo /artefactos/, de la captura de la pantalla real (mejor la nativa y COMPLETA)"),
|
|
31
|
+
});
|
|
32
|
+
function comprobar(ruta, que) {
|
|
33
|
+
if (!esRutaDeArtefacto(ruta)) {
|
|
34
|
+
return `«${ruta}» no es ${que} de esta sesión. Solo puedo comparar lo que hay bajo /artefactos/.`;
|
|
35
|
+
}
|
|
36
|
+
const mime = mimeDeArtefacto(nombreDeArtefacto(ruta));
|
|
37
|
+
if (mime !== "image/png" && mime !== "image/jpeg") {
|
|
38
|
+
return `«${nombreDeArtefacto(ruta)}» no es un PNG ni un JPEG, así que no hay nada que medir.`;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Comprueba la ruta de la referencia; con lector propio vale también /adjuntos/ y el proyecto. */
|
|
43
|
+
function comprobarReferencia(ruta, deps) {
|
|
44
|
+
if (deps.leerReferencia === undefined)
|
|
45
|
+
return comprobar(ruta, "la referencia");
|
|
46
|
+
const r = imagenReferida(ruta);
|
|
47
|
+
return typeof r === "string" ? r : undefined;
|
|
48
|
+
}
|
|
49
|
+
async function abrir(ruta, deps, comoReferencia = false) {
|
|
50
|
+
const nombre = nombreDeArtefacto(ruta);
|
|
51
|
+
let bytes;
|
|
52
|
+
try {
|
|
53
|
+
const imagen = comoReferencia && deps.leerReferencia !== undefined ? imagenReferida(ruta) : undefined;
|
|
54
|
+
bytes =
|
|
55
|
+
typeof imagen === "object" && deps.leerReferencia !== undefined
|
|
56
|
+
? await deps.leerReferencia(imagen)
|
|
57
|
+
: await deps.leerArtefacto(rutaRelativaDeArtefacto(ruta));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// El mensaje de un error de Node lleva la RUTA ABSOLUTA y esto va al modelo: solo su `code`.
|
|
61
|
+
const codigo = error.code;
|
|
62
|
+
return `No pude abrir «${nombre}» (${typeof codigo === "string" ? codigo : "error de lectura"}).`;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
return decodificarImagen(bytes);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
// Estos mensajes son NUESTROS (`decodificarImagen.ts`): sin ruta y sin trozos del fichero.
|
|
69
|
+
return `No pude usar «${nombre}»: ${error instanceof Error ? error.message : "no se pudo decodificar"}.`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function crearCompararCapturas(deps) {
|
|
73
|
+
return tool(async (entrada) => {
|
|
74
|
+
// Las DOS comprobadas antes de abrir ninguna: una ruta mal escrita no puede descubrirse
|
|
75
|
+
// después de haber leído la otra.
|
|
76
|
+
const problema = comprobarReferencia(entrada.referencia, deps) ?? comprobar(entrada.captura, "una captura");
|
|
77
|
+
if (problema !== undefined)
|
|
78
|
+
return problema;
|
|
79
|
+
const ref = await abrir(entrada.referencia, deps, true);
|
|
80
|
+
if (typeof ref === "string")
|
|
81
|
+
return ref;
|
|
82
|
+
const cap = await abrir(entrada.captura, deps);
|
|
83
|
+
if (typeof cap === "string")
|
|
84
|
+
return cap;
|
|
85
|
+
try {
|
|
86
|
+
return informeDeComparacion(compararPantallas(ref, cap)).join("\n");
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return `No se pudo medir: ${error instanceof Error ? error.message : "fallo desconocido"}.`;
|
|
90
|
+
}
|
|
91
|
+
}, {
|
|
92
|
+
name: NOMBRE_COMPARAR_CAPTURAS,
|
|
93
|
+
description: "MIDE si una captura de pantalla tiene la misma estructura que una maqueta (la de /diseno/, /adjuntos/ o " +
|
|
94
|
+
"/artefactos/), con números y sin " +
|
|
95
|
+
"opinar: qué franjas del alto y del ancho tienen contenido en cada una y hasta dónde llega. Caza " +
|
|
96
|
+
"lo grande —una banda vacía, un teclado que ocupa un tercio— y es determinista. NO ve texto " +
|
|
97
|
+
"cortado, estilo ni colores: para eso está xone_critica_visual. Úsala cuando el encargo traiga un " +
|
|
98
|
+
"diseño, con la captura nativa completa, y antes de dar la pantalla por parecida. La captura va bajo " +
|
|
99
|
+
"/artefactos/.",
|
|
100
|
+
schema: Entrada,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=compararCapturas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compararCapturas.js","sourceRoot":"","sources":["../../../src/agent/grafo/compararCapturas.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { tool } from "@langchain/core/tools";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { esRutaDeArtefacto, mimeDeArtefacto, nombreDeArtefacto, rutaRelativaDeArtefacto, } from "../../core/artefactos.js";
|
|
4
|
+
import { imagenReferida } from "../../core/referenciasDeImagen.js";
|
|
4
5
|
import { juzgarPantalla, TOPE_DE_PETICIONES, } from "../dispositivos/juezVisual.js";
|
|
5
6
|
/**
|
|
6
7
|
* `xone_critica_visual`: enseñarle una captura al crítico y traer su veredicto.
|
|
@@ -46,7 +47,7 @@ const Entrada = z.object({
|
|
|
46
47
|
referencia: z
|
|
47
48
|
.string()
|
|
48
49
|
.optional()
|
|
49
|
-
.describe("OPCIONAL. La ruta virtual
|
|
50
|
+
.describe("OPCIONAL. La ruta virtual de la MAQUETA o diseño de referencia: bajo /artefactos/, bajo /adjuntos/ o un PNG/JPEG del proyecto (/diseno/screen.png). " +
|
|
50
51
|
"Pásala SIEMPRE que el encargo traiga un diseño: con ella comparo la captura contra " +
|
|
51
52
|
"la maqueta (forma, tamaños, colocación). SIN ella solo busco roturas y NO digo nada " +
|
|
52
53
|
"sobre si se parece al diseño."),
|
|
@@ -97,6 +98,17 @@ async function leer(ruta, mime, deps) {
|
|
|
97
98
|
return `No pude abrir «${nombreDeArtefacto(ruta)}» (${error instanceof Error ? error.name : "error"}).`;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
101
|
+
/** Como `leer`, para una referencia que puede salir de /adjuntos/ o del proyecto. Nunca lanza. */
|
|
102
|
+
async function leerReferida(imagen, mime, leerReferencia) {
|
|
103
|
+
try {
|
|
104
|
+
return { base64: (await leerReferencia(imagen)).toString("base64"), mime };
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// El mensaje de un error de Node lleva la ruta absoluta y esto va al modelo: solo su `code`.
|
|
108
|
+
const codigo = error.code;
|
|
109
|
+
return `No pude abrir «${imagen.nombre}» (${typeof codigo === "string" ? codigo : "error"}).`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
100
112
|
/**
|
|
101
113
|
* **Fail-closed: una referencia que se pidió y no se pudo usar es un NO.**
|
|
102
114
|
*
|
|
@@ -121,9 +133,15 @@ export function crearCriticaVisual(deps) {
|
|
|
121
133
|
const deLaCaptura = veredictoDeImagen(entrada.captura, "una captura");
|
|
122
134
|
if (typeof deLaCaptura === "string")
|
|
123
135
|
return deLaCaptura;
|
|
136
|
+
// Con lector propio la referencia puede venir de /adjuntos/ y del proyecto; sin él, solo de artefactos.
|
|
137
|
+
const referida = entrada.referencia !== undefined && deps.leerReferencia !== undefined ? imagenReferida(entrada.referencia) : undefined;
|
|
138
|
+
if (typeof referida === "string")
|
|
139
|
+
return sinJuzgar(referida);
|
|
124
140
|
const deLaReferencia = entrada.referencia === undefined
|
|
125
141
|
? undefined
|
|
126
|
-
:
|
|
142
|
+
: referida !== undefined
|
|
143
|
+
? { mime: referida.nombre.toLowerCase().endsWith(".png") ? "image/png" : "image/jpeg" }
|
|
144
|
+
: veredictoDeImagen(entrada.referencia, "la referencia");
|
|
127
145
|
if (typeof deLaReferencia === "string")
|
|
128
146
|
return sinJuzgar(deLaReferencia);
|
|
129
147
|
const abierta = await leer(entrada.captura, deLaCaptura.mime, deps);
|
|
@@ -131,7 +149,9 @@ export function crearCriticaVisual(deps) {
|
|
|
131
149
|
return abierta;
|
|
132
150
|
let referencia;
|
|
133
151
|
if (entrada.referencia !== undefined && deLaReferencia !== undefined) {
|
|
134
|
-
const abiertaRef =
|
|
152
|
+
const abiertaRef = referida !== undefined && deps.leerReferencia !== undefined
|
|
153
|
+
? await leerReferida(referida, deLaReferencia.mime, deps.leerReferencia)
|
|
154
|
+
: await leer(entrada.referencia, deLaReferencia.mime, deps);
|
|
135
155
|
/**
|
|
136
156
|
* **Fail-closed: una referencia que se pidió y no se pudo abrir es un NO.**
|
|
137
157
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"criticaVisual.js","sourceRoot":"","sources":["../../../src/agent/grafo/criticaVisual.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,cAAc,EACd,kBAAkB,GAGnB,MAAM,+BAA+B,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,CAAC,qFAAqF,CAAC;IAClG,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CAAC,8EAA8E,CAAC;IAC3F;;;;;;;OAOG;IACH,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,
|
|
1
|
+
{"version":3,"file":"criticaVisual.js","sourceRoot":"","sources":["../../../src/agent/grafo/criticaVisual.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { tool } from "@langchain/core/tools";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { esRutaDeArtefacto, mimeDeArtefacto, nombreDeArtefacto, rutaRelativaDeArtefacto, } from "../../core/artefactos.js";
|
|
4
|
+
import { informeDeCambio, medirCambio } from "../../core/compararCapturas.js";
|
|
5
|
+
import { decodificarImagen } from "../dispositivos/decodificarImagen.js";
|
|
6
|
+
export const NOMBRE_DIFERENCIA_DE_CAPTURAS = "diferencia_de_capturas";
|
|
7
|
+
/**
|
|
8
|
+
* `diferencia_de_capturas`: ¿CAMBIÓ una zona de la pantalla entre dos capturas? (IXCODE-18)
|
|
9
|
+
*
|
|
10
|
+
* Es lo que el conductor escribía a mano en cada pasada de la calculadora: 46 scripts de Python con
|
|
11
|
+
* PIL y 31 `md5`. Y un `md5` no contesta la pregunta buena: uno igual dice que TODA la imagen es
|
|
12
|
+
* igual, y uno distinto puede ser un cursor parpadeando. Aquí se pide la zona que debía cambiar.
|
|
13
|
+
*
|
|
14
|
+
* **Va al conductor y no al orquestador**: mide las capturas que el conductor acaba de tomar, para
|
|
15
|
+
* saber si su toque tuvo efecto — es comprobar una ACCIÓN, no juzgar una pantalla. El juicio
|
|
16
|
+
* sigue siendo del crítico (`xone_critica_visual`) y de `comparar_capturas`.
|
|
17
|
+
*
|
|
18
|
+
* Mismas guardas que las otras dos tools de capturas: las dos rutas bajo `/artefactos/` (lista
|
|
19
|
+
* BLANCA de forma), comprobadas las dos ANTES de abrir ninguna. Solo lee, no escribe nada, y todo
|
|
20
|
+
* rechazo se DEVUELVE como texto.
|
|
21
|
+
*/
|
|
22
|
+
const Region = z.object({
|
|
23
|
+
x: z.number().describe("Esquina izquierda, de 0 a 1 (fracción del ancho de la imagen)"),
|
|
24
|
+
y: z.number().describe("Esquina de arriba, de 0 a 1 (fracción del alto de la imagen)"),
|
|
25
|
+
ancho: z.number().describe("Ancho de la zona, fracción del ancho de la imagen"),
|
|
26
|
+
alto: z.number().describe("Alto de la zona, fracción del alto de la imagen"),
|
|
27
|
+
});
|
|
28
|
+
const Entrada = z.object({
|
|
29
|
+
antes: z.string().describe("Ruta virtual, bajo /artefactos/, de la captura de ANTES"),
|
|
30
|
+
despues: z.string().describe("Ruta virtual, bajo /artefactos/, de la captura de DESPUÉS (del mismo aparato: mismo tamaño)"),
|
|
31
|
+
zona: Region.optional().describe("La zona que debía cambiar, en fracciones de la imagen. Sin ella se mide la imagen entera"),
|
|
32
|
+
});
|
|
33
|
+
function comprobar(ruta, que) {
|
|
34
|
+
if (!esRutaDeArtefacto(ruta)) {
|
|
35
|
+
return `«${ruta}» no es ${que} de esta sesión. Solo puedo medir lo que hay bajo /artefactos/.`;
|
|
36
|
+
}
|
|
37
|
+
const mime = mimeDeArtefacto(nombreDeArtefacto(ruta));
|
|
38
|
+
if (mime !== "image/png" && mime !== "image/jpeg") {
|
|
39
|
+
return `«${nombreDeArtefacto(ruta)}» no es un PNG ni un JPEG, así que no hay nada que medir.`;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
async function abrir(ruta, deps) {
|
|
44
|
+
const nombre = nombreDeArtefacto(ruta);
|
|
45
|
+
let bytes;
|
|
46
|
+
try {
|
|
47
|
+
bytes = await deps.leerArtefacto(rutaRelativaDeArtefacto(ruta));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
// El mensaje de un error de Node lleva la RUTA ABSOLUTA y esto va al modelo: solo su `code`.
|
|
51
|
+
const codigo = error.code;
|
|
52
|
+
return `No pude abrir «${nombre}» (${typeof codigo === "string" ? codigo : "error de lectura"}).`;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return decodificarImagen(bytes);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return `No pude usar «${nombre}»: ${error instanceof Error ? error.message : "no se pudo decodificar"}.`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function crearDiferenciaDeCapturas(deps) {
|
|
62
|
+
return tool(async (entrada) => {
|
|
63
|
+
const problema = comprobar(entrada.antes, "la captura de antes") ?? comprobar(entrada.despues, "la captura de después");
|
|
64
|
+
if (problema !== undefined)
|
|
65
|
+
return problema;
|
|
66
|
+
const a = await abrir(entrada.antes, deps);
|
|
67
|
+
if (typeof a === "string")
|
|
68
|
+
return a;
|
|
69
|
+
const d = await abrir(entrada.despues, deps);
|
|
70
|
+
if (typeof d === "string")
|
|
71
|
+
return d;
|
|
72
|
+
try {
|
|
73
|
+
return informeDeCambio(medirCambio(a, d, entrada.zona)).join("\n");
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return `No se pudo medir: ${error instanceof Error ? error.message : "fallo desconocido"}.`;
|
|
77
|
+
}
|
|
78
|
+
}, {
|
|
79
|
+
name: NOMBRE_DIFERENCIA_DE_CAPTURAS,
|
|
80
|
+
description: "Dice si una ZONA de la pantalla CAMBIÓ entre dos capturas de la misma app, con el porcentaje de píxeles " +
|
|
81
|
+
"distintos y dónde. Úsala tras un toque para comprobar que lo pintado cambió, en vez de comparar con md5 o " +
|
|
82
|
+
"escribir un script: un md5 igual solo dice que TODA la imagen es igual. La zona va en fracciones de la imagen " +
|
|
83
|
+
"(0 a 1). Si dice IGUAL no lo des por «captura cacheada»: toma otra a los 500 ms. getText no vale para esto, " +
|
|
84
|
+
"porque devuelve el valor del campo y no lo pintado. Las dos rutas van bajo /artefactos/.",
|
|
85
|
+
schema: Entrada,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=diferenciaDeCapturas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diferenciaDeCapturas.js","sourceRoot":"","sources":["../../../src/agent/grafo/diferenciaDeCapturas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { tool } from "@langchain/core/tools";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { ANCHO_DE_PANTALLA_DE_REFERENCIA, generarFondo, INTENSIDADES, medidaEnPixeles, motivoDeNombreDeFondoInaceptable, motivoDePeticionInaceptable, nombreDeFicheroDeFondo, NOMBRE_GENERAR_FONDO, PIXELES_POR_P, rutaDeFondo, TIPOS_DE_FONDO, } from "../../core/fondosSvg.js";
|
|
4
|
+
import { recibeBuscarIcono } from "./buscarIcono.js";
|
|
5
|
+
export { NOMBRE_GENERAR_FONDO };
|
|
6
|
+
/**
|
|
7
|
+
* Generar el fondo SVG de un frame o un botón (degradado, sombra, resplandor), para lo que XOne
|
|
8
|
+
* no trae propio (IXCODE-18). **Pura y sin red**: no hay puerto, siempre está montada donde se
|
|
9
|
+
* reparte. Las plantillas, y por qué no llevan ningún filtro, están en `core/fondosSvg.ts`.
|
|
10
|
+
*
|
|
11
|
+
* **Solo lee**, como `buscar_icono`: el SVG lo escribe `write_file` con su aprobación. Y va a los
|
|
12
|
+
* MISMOS especialistas (`recibeBuscarIcono`, la regla de dato de quien escribe el proyecto), en
|
|
13
|
+
* los dos motores. Los rechazos se DEVUELVEN como texto, nunca se lanzan.
|
|
14
|
+
*/
|
|
15
|
+
export const recibeGenerarFondo = recibeBuscarIcono;
|
|
16
|
+
const ESQUEMA = z.object({
|
|
17
|
+
tipo: z.enum(TIPOS_DE_FONDO).describe("degradado-lineal | degradado-radial | sombra (tarjeta con sombra suave) | glow (resplandor)"),
|
|
18
|
+
nombre: z.string().min(1).max(40).describe("Nombre del fichero: minúsculas, dígitos y _ (sale bg_<nombre>.svg)"),
|
|
19
|
+
ancho: z.union([z.number(), z.string().max(16)]).describe('Ancho del control TAL COMO lo declaras en el XML: "90%", "300p", "120px" o un número (píxeles)'),
|
|
20
|
+
alto: z.union([z.number(), z.string().max(16)]).describe('Alto del control tal como lo declaras: "60p", "120px" o un número (píxeles). El % no vale en el alto'),
|
|
21
|
+
colores: z.array(z.string().max(32)).min(1).max(4).describe("Colores COMO EN XOne: #RRGGBB o #AARRGGBB (alfa PRIMERO, no el #RRGGBBAA de HTML). 2 a 4 en los degradados; 1 en sombra (la tarjeta) y en glow (la luz)"),
|
|
22
|
+
angulo: z.coerce.number().min(0).max(360).optional().describe("Solo degradado-lineal: 0 = izquierda a derecha, 90 = arriba abajo (por omisión)"),
|
|
23
|
+
radio: z.coerce.number().min(0).max(50).optional().describe("Esquina como % del alto: 0 recta, 25 por omisión, 50 píldora"),
|
|
24
|
+
intensidad: z.enum(INTENSIDADES).optional().describe("Solo sombra: suave | media | fuerte"),
|
|
25
|
+
fondo: z.string().max(32).optional().describe("Solo glow: color de detrás, como en XOne; sin él, transparente"),
|
|
26
|
+
});
|
|
27
|
+
export function crearGenerarFondoSvg() {
|
|
28
|
+
return tool(async (e) => {
|
|
29
|
+
try {
|
|
30
|
+
const nombreMal = motivoDeNombreDeFondoInaceptable(e.nombre);
|
|
31
|
+
if (nombreMal !== undefined)
|
|
32
|
+
return nombreMal;
|
|
33
|
+
const ancho = medidaEnPixeles(e.ancho, "ancho");
|
|
34
|
+
const alto = medidaEnPixeles(e.alto, "alto");
|
|
35
|
+
if ("motivo" in ancho)
|
|
36
|
+
return ancho.motivo;
|
|
37
|
+
if ("motivo" in alto)
|
|
38
|
+
return alto.motivo;
|
|
39
|
+
const estimada = [e.ancho, e.alto].some((v) => typeof v === "string" && /[%p]$/i.test(v.trim()) && !/px$/i.test(v.trim()));
|
|
40
|
+
const peticion = {
|
|
41
|
+
tipo: e.tipo,
|
|
42
|
+
ancho: ancho.px,
|
|
43
|
+
alto: alto.px,
|
|
44
|
+
colores: e.colores,
|
|
45
|
+
...(e.angulo === undefined ? {} : { angulo: e.angulo }),
|
|
46
|
+
...(e.radio === undefined ? {} : { radio: e.radio }),
|
|
47
|
+
...(e.intensidad === undefined ? {} : { intensidad: e.intensidad }),
|
|
48
|
+
...(e.fondo === undefined ? {} : { fondo: e.fondo }),
|
|
49
|
+
};
|
|
50
|
+
const motivo = motivoDePeticionInaceptable(peticion);
|
|
51
|
+
if (motivo !== undefined)
|
|
52
|
+
return motivo;
|
|
53
|
+
const { svg, anchoDelViewBox } = generarFondo(peticion);
|
|
54
|
+
const fichero = nombreDeFicheroDeFondo(e.nombre);
|
|
55
|
+
return [
|
|
56
|
+
`Fondo ${e.tipo} de proporción ${anchoDelViewBox}:100 (la de un control de ${e.ancho}×${e.alto}). ` +
|
|
57
|
+
`Escríbelo TAL CUAL con write_file en ${rutaDeFondo(e.nombre)}. En un FRAME o GRUPO se usa con ` +
|
|
58
|
+
`imgbk="${fichero}"; en un BOTÓN (type="B") con img="${fichero}" (y imgsel para el pulsado): en un botón ` +
|
|
59
|
+
`imgbk se ignora (medido). La esquina va DIBUJADA en el SVG: border-corner-radius no recorta el fondo. ` +
|
|
60
|
+
`Si el control cambia de proporción, la esquina se deforma: genera otro para ese control.` +
|
|
61
|
+
(estimada
|
|
62
|
+
? ` Las medidas en % y p son una ESTIMACIÓN sobre un aparato de ${ANCHO_DE_PANTALLA_DE_REFERENCIA} px de ancho ` +
|
|
63
|
+
`(1 p ≈ ${PIXELES_POR_P} px): en otro dispositivo la proporción real puede variar un poco.`
|
|
64
|
+
: ""),
|
|
65
|
+
svg,
|
|
66
|
+
].join("\n");
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
return `No se pudo generar: ${error instanceof Error ? error.message : "fallo desconocido"}.`;
|
|
70
|
+
}
|
|
71
|
+
}, {
|
|
72
|
+
name: NOMBRE_GENERAR_FONDO,
|
|
73
|
+
description: "Genera el SVG de fondo de un frame o botón: degradado lineal o radial, tarjeta con sombra o " +
|
|
74
|
+
"resplandor. Los colores van como en XOne (#RRGGBB o #AARRGGBB) y se convierten a SVG. XOne no " +
|
|
75
|
+
"tiene degradados ni desenfoques propios y ignora en silencio los filtros SVG, " +
|
|
76
|
+
"así que esto los dibuja sin filtros. Pasa el ancho y el alto DEL CONTROL que lo lleva: la esquina " +
|
|
77
|
+
"va dibujada y solo sale bien con su proporción. Solo genera: el SVG lo escribes tú con write_file " +
|
|
78
|
+
'en la ruta que te indica. En un frame va con imgbk="bg_x.svg"; en un botón (type="B") con img="bg_x.svg", ' +
|
|
79
|
+
'porque imgbk se ignora en los botones. Nombre a secas, nunca type="WEB".',
|
|
80
|
+
schema: ESQUEMA,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=generarFondoSvg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generarFondoSvg.js","sourceRoot":"","sources":["../../../src/agent/grafo/generarFondoSvg.ts"],"names":[],"mappings":"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"}
|