# Inkscape

Un modèle pilote Inkscape 1.4 par le Hub pour composer une carte de visite sur deux calques. Inkscape est la seule application ici qui n'a rien à démarrer : le Hub lance un processus Python pour chaque script. Cette page donne le texte, le script, les réponses du Hub et la capture.

*Un dessin fait par un script et ouvert dans Inkscape : c'est un SVG ordinaire, dont les nœuds XML se modifient à la main.*

## Faits

- Version essayée : 1.4
- Mode : `cli`

## Activer Inkscape

Il n'y a ni pont ni fenêtre. Le Hub cherche `/usr/bin/python3`, `/usr/bin/inkscape`, et les extensions d'Inkscape dans `/usr/share/inkscape/extensions`. Toute la préparation tient en une entrée dans `~/.config/libremodular-mcp/config.toml`, après `libremodular-mcp init` :

```toml
[apps.inkscape]
enabled = true
```

Sur une Debian 13 vierge, `import inkex` demande aussi `python3-tinycss2`, que le paquet `inkscape` n'installe pas : `sudo apt install inkscape python3-tinycss2`.

`get_state` sur `hub` a listé Inkscape comme connecté en mode `cli`, avec la version qu'Inkscape déclarait :

**Réponse du Hub**

```json
{
  "app": "hub",
  "app_version": "0.1.0",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:13.608Z",
  "connected": true,
  "content_origin": "hub",
  "screenshot": null,
  "state": {
    "modules": [
      {
        "app_version": "1.4",
        "bridge_version": null,
        "connected": true,
        "mode": "cli",
        "name": "inkscape"
      }
    ]
  },
  "warnings": []
}
```


## Texte donné au modèle

```text
In Inkscape, lay out a name card, A6 landscape (148 by 105 mm). Dark blue background (#1c385c), a copper circle (#c25438) in the top-left corner, the name "Alice Martin" in white and large, a thin amber line (#f2b344) under it, then the role "Sound engineer" in light grey and smaller. Put the background shapes on a layer named "Background" and the text on a layer named "Text".
```


Avant d'écrire, le modèle a appelé `get_state` sur `inkscape`. Le `state` était vide et un avertissement disait `no document yet: the working document is created by the first script`.

## Script écrit

Un modèle, Claude Sonnet 5.5, a écrit ce script à partir du guide et de cet état. Ce n'est pas un script tiré des tests du projet. Deux lignes viennent du guide : le script retire d'abord les enfants de la racine, parce que le document est conservé d'un script au suivant, et il règle `cx`, `cy` et `r` du cercle un par un, parce que, comme le guide l'avertit, les deux arguments de `inkex.Circle.new` ne font pas un centre.

**Script écrit**

```python
import json
import os

import inkex

document = inkex.load_svg(os.environ["LMM_DOCUMENT"])
svg = document.getroot()
for child in list(svg):
    svg.remove(child)
svg.set("width", "148mm")
svg.set("height", "105mm")
svg.set("viewBox", "0 0 148 105")

background = svg.add(inkex.Layer.new("Background"))
panel = inkex.Rectangle.new(0, 0, 148, 105)
panel.style = {"fill": "#1c385c"}
background.add(panel)
disc = inkex.Circle()
disc.set("cx", "20")
disc.set("cy", "20")
disc.set("r", "9")
disc.style = {"fill": "#c25438"}
background.add(disc)
rule = inkex.Rectangle.new(20, 62, 108, 0.8)
rule.style = {"fill": "#f2b344"}
background.add(rule)

texts = svg.add(inkex.Layer.new("Text"))
for content, y, size, colour, weight in [("Alice Martin", 56, 12, "#ffffff", "bold"), ("Sound engineer", 76, 6, "#cfd4da", "normal")]:
    line = inkex.TextElement()
    line.text = content
    line.set("x", "20")
    line.set("y", str(y))
    line.style = {"fill": colour, "font-size": f"{size}px", "font-family": "sans-serif", "font-weight": weight}
    texts.add(line)

document.write(os.environ["LMM_DOCUMENT"])
print("LMM_RESULT: " + json.dumps({"layers": [layer.label for layer in svg.descendants().filter(inkex.Layer)], "elements": len(list(svg.descendants()))}))
```


## La réponse du Hub

**Réponse du Hub**

```json
{
  "app": "inkscape",
  "duration_ms": 151,
  "error": null,
  "result": {
    "elements": 8,
    "layers": [
      "Background",
      "Text"
    ]
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}
```


Le script a duré 151 ms : un processus Python a démarré et s'est terminé. Le script a imprimé son `result` sur la dernière ligne, après `LMM_RESULT: `, ce qui est la façon dont un script Inkscape renvoie une valeur. `get_state` a ensuite décrit le document de travail et renvoyé une capture :

**Réponse du Hub**

```json
{
  "app": "inkscape",
  "app_version": "1.4",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:14.185Z",
  "connected": true,
  "content_origin": "document",
  "screenshot": "$XDG_RUNTIME_DIR/libremodular-mcp/captures/inkscape-20261021T124713832Z.png",
  "state": {
    "element_count": 8,
    "height": "105mm",
    "layers": [
      {
        "id": null,
        "label": "Background"
      },
      {
        "id": null,
        "label": "Text"
      }
    ],
    "width": "148mm"
  },
  "warnings": []
}
```


## L'image produite

La capture est le document de travail exporté par `inkscape` lui-même, de 559 par 397 pixels pour une carte de 148 par 105 mm. Le texte est dessiné dans la police `sans-serif` de cette machine ; une autre machine le dessine dans la sienne. Le pixel en haut à gauche de la capture vaut `#1C385C` et celui qui est dans le cercle vaut `#C25438`.

![Une carte bleu foncé avec un cercle cuivre en haut à gauche, le nom Alice Martin en blanc, un trait ambre et la fonction Sound engineer en gris.](https://libremodular.davalan.fr/images/applications/inkscape-name-card.png)

*La capture du document de travail après le script, telle que le Hub l'a exportée.*


> **Limite**
>
> Non essayé : une fenêtre Inkscape ouverte en même temps que le Hub, et un document plus grand que celui-ci.

[Guide de référence dans le dépôt](https://github.com/RonanDavalan/LibreModular-MCP/blob/main/docs/modules/inkscape.md)

