# Krita

Un modelo maneja Krita 5.2.9 a través del Hub para pintar una postal en dos capas y guardarla como archivo .kra. El primer script funcionó. Esta página da el texto, el script, las respuestas del Hub y la captura, y explica cómo se inició el puente: no desde Scripter, que no se probó.

*Palabras en varios idiomas pasan sobre un paisaje pintado en Krita; el movimiento lo renderiza el Hub en Blender.*

## Datos

- Versión probada: 5.2.9
- Modo: `tcp`

## Iniciar Krita con su puente

La [guía](https://github.com/RonanDavalan/LibreModular-MCP/blob/main/docs/modules/krita.md) hace que el operador ejecute el archivo del puente desde Scripter. Esta ejecución no usó ese gesto. Inició Krita en una pantalla X virtual, en un perfil nuevo, con una extensión de arranque cuyo `__init__.py` entero ejecuta el archivo del puente:

```sh
xvfb-run -a krita --nosplash
```

```python
exec(open('/usr/share/libremodular-mcp/bridges/krita_bridge.py', encoding='utf-8').read())
```

La codificación no es opcional. El Python incorporado de Krita lee los archivos con la codificación de la configuración regional con la que se inició, y el archivo del puente contiene caracteres con acento: en la configuración regional `C`, un comando sin `encoding='utf-8'` se detuvo con `UnicodeDecodeError`, como registra la [página de compatibilidad](https://libremodular.davalan.fr/es/compatibility/index.md#krita). La ejecución usó el puerto 9312 en `config.toml` y un token nuevo. El puerto empezó a escuchar 10,4 s después del inicio, y `get_state` sobre `hub` enumeró Krita como conectado:

**Respuesta del Hub**

```json
{
  "app": "hub",
  "app_version": "0.1.0",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:48.951Z",
  "connected": true,
  "content_origin": "hub",
  "screenshot": null,
  "state": {
    "modules": [
      {
        "app_version": "5.2.9",
        "bridge_version": "1",
        "connected": true,
        "mode": "tcp",
        "name": "krita"
      }
    ]
  },
  "warnings": []
}
```


## Texto dado al modelo

```text
In Krita, create a document named "Postcard", 600 by 400 pixels. On a paint layer named "Sky", paint a vertical gradient from dark blue (#1c385c) at the top to teal (#29a196) at the bottom. On a second paint layer named "Hill", paint a copper (#c25438) hill: the upper half of an ellipse centred on the bottom edge, 520 pixels wide and 240 pixels high. Save the document as postcard.kra in your working directory.
```


Antes de escribir, el modelo llamó a `get_state` sobre `krita`: `"document": null`. El documento aún no existe, así que el script tiene que crearlo y abrirlo en una ventana antes de poder pintar.

## Script escrito

Un modelo, Claude Sonnet 5.5, escribió este script a partir de la guía y de ese estado. No es un script tomado de las pruebas del proyecto. El script escribe los bytes de los píxeles en el orden azul, verde, rojo, alfa; los colores de la captura de más abajo muestran que es el orden que Krita espera para este documento. El script activa el modo por lotes antes de `saveAs`; lo que ocurre sin él no se probó.

**Script escrito**

```python
import os

krita = Krita.instance()
document = krita.createDocument(600, 400, "Postcard", "RGBA", "U8", "", 72.0)
krita.activeWindow().addView(document)
root = document.rootNode()

def lerp(a, b, t):
    return round(a + (b - a) * t)

top, bottom = (0x1c, 0x38, 0x5c), (0x29, 0xa1, 0x96)
sky_pixels = bytearray()
for y in range(400):
    t = y / 399
    r, g, b = (lerp(top[i], bottom[i], t) for i in range(3))
    sky_pixels += bytes((b, g, r, 255)) * 600
sky = document.createNode("Sky", "paintlayer")
root.addChildNode(sky, None)
sky.setPixelData(bytes(sky_pixels), 0, 0, 600, 400)

hill_pixels = bytearray()
for y in range(400):
    for x in range(600):
        inside = ((x - 300) / 260) ** 2 + ((y - 400) / 120) ** 2 <= 1
        hill_pixels += bytes((0x38, 0x54, 0xC2, 255)) if inside else bytes((0, 0, 0, 0))
hill = document.createNode("Hill", "paintlayer")
root.addChildNode(hill, None)
hill.setPixelData(bytes(hill_pixels), 0, 0, 600, 400)

document.refreshProjection()
document.setBatchmode(True)
path = os.path.join(LMM_WORK_DIR, "postcard.kra")
saved = document.saveAs(path)
result = {"saved": saved, "kra_bytes": os.path.getsize(path), "layers": [node.name() for node in document.topLevelNodes()]}
```


## Respuesta del Hub

**Respuesta del Hub**

```json
{
  "app": "krita",
  "duration_ms": 1060,
  "error": null,
  "result": {
    "kra_bytes": 110725,
    "layers": [
      "Background",
      "Sky",
      "Hill"
    ],
    "saved": true
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}
```


El script se ejecutó en 1,06 s. El documento tiene tres capas, porque un documento nuevo de Krita empieza con una capa `Background` que el script no tocó, bajo `Sky` y `Hill`. `get_state` describió entonces el documento y devolvió una captura de su proyección:

**Respuesta del Hub**

```json
{
  "app": "krita",
  "app_version": "5.2.9",
  "bridge_version": "1",
  "captured_at": "2026-10-21T12:47:50.526Z",
  "connected": true,
  "content_origin": "document",
  "screenshot": "$XDG_RUNTIME_DIR/libremodular-mcp/captures/krita-20261021T124750502Z.png",
  "state": {
    "document": {
      "active_layer": "{727e9cd9-e6e1-423c-ade7-1bf29d6d5289}",
      "color_depth": "U8",
      "color_model": "RGBA",
      "color_profile": "sRGB-elle-V2-srgbtrc.icc",
      "height": 400,
      "layers": [
        {
          "id": "{69f5540d-2916-4771-8df7-1fcad46b677f}",
          "name": "Background",
          "opacity": 255,
          "type": "paintlayer",
          "visible": true
        },
        {
          "id": "{ed307be8-5a93-4b16-8739-7508916ea687}",
          "name": "Sky",
          "opacity": 255,
          "type": "paintlayer",
          "visible": true
        },
        {
          "id": "{727e9cd9-e6e1-423c-ade7-1bf29d6d5289}",
          "name": "Hill",
          "opacity": 255,
          "type": "paintlayer",
          "visible": true
        }
      ],
      "name": "Postcard",
      "width": 600
    }
  },
  "warnings": []
}
```


## La imagen producida

La captura es la imagen fusionada que muestra Krita, de 600 por 400 píxeles. Los píxeles que se leen en ella son `#1C385C` arriba, `#29A196` abajo y `#C25438` dentro de la colina, los tres colores que pedía el texto. El archivo `.kra` que guardó el script pesa 110 725 bytes.

![Un degradado vertical de azul oscuro a verde azulado con una semielipse de cobre que sube desde el borde inferior.](https://libremodular.davalan.fr/images/applications/krita-postcard.png)

*La captura del documento Postcard tras el script, tal como la exportó el Hub.*


> **Límite**
>
> No se probó: iniciar el puente desde Scripter (la ruta de menú, las variables de entorno que le llegan, cuánto vive una vez cerrado Scripter), varios documentos abiertos a la vez y un documento con un grupo de capas.

[Guía de referencia en el repositorio](https://github.com/RonanDavalan/LibreModular-MCP/blob/main/docs/modules/krita.md)

