@imenam/mcp-design 1.0.4 → 1.0.6

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.
Files changed (47) hide show
  1. package/README.md +61 -8
  2. package/dist/shared/icons/icon-data.d.ts +2 -0
  3. package/dist/shared/icons/icon-data.js +5 -0
  4. package/dist/shared/icons/icon-keywords.d.ts +1 -0
  5. package/dist/shared/icons/icon-keywords.js +4 -0
  6. package/dist/shared/icons/index.d.ts +47 -0
  7. package/dist/shared/icons/index.js +107 -0
  8. package/dist/shared/icons/search.d.ts +31 -0
  9. package/dist/shared/icons/search.js +104 -0
  10. package/dist/shared/instances.d.ts +39 -0
  11. package/dist/shared/instances.js +94 -0
  12. package/dist/shared/node-defaults.d.ts +2 -0
  13. package/dist/shared/node-defaults.js +32 -4
  14. package/dist/shared/node-fields.d.ts +2 -0
  15. package/dist/shared/node-fields.js +17 -4
  16. package/dist/shared/render.d.ts +31 -1
  17. package/dist/shared/render.js +65 -6
  18. package/dist/shared/schemas/design.schema.d.ts +19 -1
  19. package/dist/shared/schemas/design.schema.js +13 -1
  20. package/dist/src/core/clone.d.ts +23 -0
  21. package/dist/src/core/clone.js +34 -0
  22. package/dist/src/core/diff.js +2 -2
  23. package/dist/src/core/export.js +63 -3
  24. package/dist/src/core/hierarchy.d.ts +7 -0
  25. package/dist/src/core/hierarchy.js +75 -5
  26. package/dist/src/core/node-factory.js +55 -5
  27. package/dist/src/core/screenshot.d.ts +12 -0
  28. package/dist/src/core/screenshot.js +17 -6
  29. package/dist/src/index.js +97 -7
  30. package/dist/src/tools/add-nodes.js +7 -1
  31. package/dist/src/tools/delete-nodes.js +10 -1
  32. package/dist/src/tools/detach-instances.d.ts +14 -0
  33. package/dist/src/tools/detach-instances.js +61 -0
  34. package/dist/src/tools/duplicate-nodes.js +20 -28
  35. package/dist/src/tools/search-icons.d.ts +12 -0
  36. package/dist/src/tools/search-icons.js +87 -0
  37. package/dist/src/tools/ungroup-nodes.js +8 -0
  38. package/dist/src/version.d.ts +1 -1
  39. package/dist/src/version.js +1 -1
  40. package/gui/dist/assets/index-B-q92Pfu.js +10 -0
  41. package/gui/dist/assets/index-PQqxUQLx.css +1 -0
  42. package/gui/dist/assets/search-C-KcHHKM.js +1 -0
  43. package/gui/dist/index.html +2 -2
  44. package/package.json +4 -2
  45. package/scripts/generate-icons.js +85 -0
  46. package/gui/dist/assets/index-C6fXmzfu.css +0 -1
  47. package/gui/dist/assets/index-Cmo-X8Zs.js +0 -10
package/README.md CHANGED
@@ -85,8 +85,9 @@ Un design est un simple fichier JSON dans `<data>/designs/<id>.json` — lisible
85
85
  | `delete_nodes` | Supprime des éléments par identifiant. Supprimer un groupe emporte son contenu. |
86
86
  | `group_nodes` | Enveloppe des éléments dans un nouveau groupe, sans rien déplacer à l'écran. |
87
87
  | `ungroup_nodes` | Dissout des groupes : leur contenu remonte d'un niveau, toujours sans bouger. |
88
+ | `detach_instances` | Rompt le lien d'une instance avec son composant : son contenu devient des éléments ordinaires. |
88
89
 
89
- Cinq types d'éléments : `rect`, `ellipse`, `line`, `text` et `group`. Seul `type` est obligatoire ; tout le reste tombe sur des valeurs par défaut visibles, donc `{ "type": "rect" }` suffit à créer quelque chose qu'on voit à l'écran.
90
+ Sept types d'éléments : `rect`, `ellipse`, `line`, `text`, `icon`, `group` et `instance`. Seul `type` est obligatoire — sauf pour `icon`, qui a besoin du nom de l'icône ; tout le reste tombe sur des valeurs par défaut visibles, donc `{ "type": "rect" }` suffit à créer quelque chose qu'on voit à l'écran.
90
91
 
91
92
  **Champs communs** — `name`, `parentId`, `x`, `y`, `width`, `height` (en pixels, origine en haut à gauche), `rotation` (degrés, autour du centre de la boîte), `opacity` (0–1).
92
93
 
@@ -98,7 +99,35 @@ Cinq types d'éléments : `rect`, `ellipse`, `line`, `text` et `group`. Seul `ty
98
99
  | `ellipse` | `fill`, `stroke`, `strokeWidth` |
99
100
  | `line` | `stroke`, `strokeWidth` — le trait va de `(x, y)` à `(x + width, y + height)`, donc les deltas négatifs encodent la direction |
100
101
  | `text` | `text`, `color`, `fontSize`, `fontFamily`, `fontWeight`, `fontStyle`, `textAlign`, `lineHeight` (en px) |
102
+ | `icon` | `icon` (obligatoire — le nom du dessin), `fill`, `stroke`, `strokeWidth` |
101
103
  | `group` | aucun — c'est un pur conteneur |
104
+ | `instance` | `componentId` (obligatoire — le groupe reproduit) ; rien d'autre, son apparence vient de ce groupe |
105
+
106
+ ### Icônes
107
+
108
+ Un agent qui n'a que des formes à sa disposition finit par approximer une icône avec trois ellipses et deux traits : le rendu est médiocre, et la maquette relue plus tard ne dit plus ce que ce tas de formes représentait. Le type `icon` répond exactement à ça — **1791 icônes [Lucide](https://lucide.dev) (licence ISC) sont embarquées** dans le paquet.
109
+
110
+ ```json
111
+ { "type": "icon", "icon": "shopping-cart", "x": 24, "y": 18, "width": 24, "height": 24, "stroke": "#1a1a18" }
112
+ ```
113
+
114
+ Le nœud stocke le **nom**, jamais le tracé : le JSON reste lisible — `shopping-cart` dit à l'agent qui le relit ce que la forme représente —, un nœud `icon` pèse moins qu'un `rect`, et changer d'icône est un `update_nodes` d'un seul champ. Le SVG est résolu au rendu depuis [`shared/icons/`](shared/icons/), donc le canvas, l'export et la capture dessinent rigoureusement la même chose.
115
+
116
+ | Outil | Effet |
117
+ |---|---|
118
+ | `search_icons` | Cherche une icône par description (`query`), en anglais et par synonymes — « basket » sort `shopping-cart`. Renvoie les noms **et** une planche de vignettes, sauf si `preview: false`. |
119
+
120
+ Les icônes les plus courantes sont listées directement dans la description d'`add_nodes` (`CORE_ICONS` dans [`shared/icons/index.ts`](shared/icons/index.ts)) : habiller une interface ordinaire ne demande aucun aller-retour. `search_icons` sert à la traîne longue. Un nom refusé repart avec des suggestions issues des synonymes, ce qui rattrape aussi bien une faute qu'un renommage de la bibliothèque (`home` → `house`).
121
+
122
+ Trois règles à connaître :
123
+
124
+ - **`strokeWidth` est la seule exception à la règle du pixel** : il s'exprime dans la grille 24 × 24 de l'icône (2 = graisse normale, 1.5 = fine, 2.5 = grasse). Une icône de 96 px avec `strokeWidth: 2` dessine un trait de 8 px à l'écran — le trait suit la taille, il n'y a rien à compenser.
125
+ - **La boîte est carrée** : `width` et `height` sont ramenés au plus petit des deux, et le tracé est mis à l'échelle uniformément puis centré. Jamais déformé.
126
+ - **Un nom inconnu ne casse rien** : l'icône est remplacée par un carré en pointillés avec un `?`. Le même SVG servant au canvas, à l'export et à la capture, une exception y serait fatale partout à la fois — un design ouvert après une mise à jour de la bibliothèque doit rester ouvrable, et montrer *où* est le problème.
127
+
128
+ Côté GUI, l'outil « Icône » de la barre d'outils (raccourci `I`) ouvre un sélecteur cherchable, et l'inspecteur permet de remplacer l'icône d'un élément existant. L'index de synonymes y est chargé à la demande, pour ne pas peser sur le premier affichage du canvas.
129
+
130
+ La bibliothèque est figée dans le dépôt par `npm run generate-icons` ([`scripts/generate-icons.js`](scripts/generate-icons.js)) : ni le serveur publié ni le bundle de la GUI ne dépendent de Lucide à l'exécution, et la géométrie d'un design ne peut pas changer au gré d'un `npm install`.
102
131
 
103
132
  ### Hiérarchie
104
133
 
@@ -117,6 +146,29 @@ Le texte n'est **jamais re-wrappé automatiquement** : seuls les `\n` explicites
117
146
 
118
147
  `lineHeight` est une **distance en pixels entre deux lignes de base**, pas un multiplicateur de `fontSize` : avec `fontSize: 64`, on écrit `76`, pas `1.2`. C'est l'unité que les agents emploient spontanément, et la seule qui se lise dans la même échelle que le reste. Une valeur sous 4 est donc refusée à l'écriture avec un message explicite, et les fichiers écrits avant ce changement sont convertis à la lecture (`ratio x fontSize`) par [`src/core/migrate.ts`](src/core/migrate.ts). Champ omis = `fontSize x 1.35`.
119
148
 
149
+ ### Groupes réutilisables
150
+
151
+ Quand un même bloc revient plusieurs fois — une carte, une ligne de liste, un bouton —, il ne se recopie pas : il se **construit une fois** en groupe, puis s'**instancie**. Une `instance` ne stocke aucun contenu, seulement l'identifiant du groupe qu'elle reproduit ; le sous-arbre est reconstitué au rendu. Éditer le groupe met donc à jour **toutes** ses instances d'un coup, et un bloc répété reste une seule chose à lire et à modifier. C'est le fonctionnement d'un composant principal dans Figma.
152
+
153
+ ```jsonc
154
+ // 1. le composant : un groupe ordinaire
155
+ { "type": "group", "id": "card", "name": "Carte" }
156
+ // 2. ses copies liées
157
+ { "type": "instance", "componentId": "card", "x": 320, "y": 80 }
158
+ { "type": "instance", "componentId": "card", "x": 640, "y": 80 }
159
+ ```
160
+
161
+ Règles, volontairement peu nombreuses :
162
+
163
+ - **Rien ne se surcharge dans une instance** — c'est ce qui garantit qu'elles restent identiques. Sa position, sa rotation et son opacité lui appartiennent ; tout le reste vient du composant. Une copie qui doit diverger devient un second groupe, ou se détache (`detach_instances`).
164
+ - La **taille** d'une instance est déduite de son composant, comme celle d'un groupe.
165
+ - Changer `componentId` **échange** l'instance de composant (le *swap* de Figma).
166
+ - Les instances s'**imbriquent** (une instance de carte contenant une instance de bouton), mais un composant ne peut pas se contenir lui-même : le cas est refusé à l'écriture.
167
+ - Un groupe encore instancié ne peut être ni supprimé ni dissous — il faut d'abord détacher ou supprimer ses instances.
168
+ - Détacher une instance ne détache **pas** les instances imbriquées dans son composant : elles restent liées.
169
+
170
+ Côté agent, le gain se lit dans `get_changes` : une retouche du composant apparaît comme **un** changement, pas comme N copies modifiées.
171
+
120
172
  ### Ordre d'empilement
121
173
 
122
174
  L'index dans la liste **est** l'ordre d'empilement : le dernier élément d'une fratrie est celui du dessus. `add_nodes` empile au-dessus, et `reorder_nodes` déplace ensuite ce qui existe déjà.
@@ -170,11 +222,11 @@ mark_synced { design_id: "landing" } ← acquittement
170
222
 
171
223
  Trois colonnes : la liste des planches, le canvas, l'inspecteur et les calques.
172
224
 
173
- - **Outils** — sélection, rectangle, ellipse, ligne, texte. Glisser pour dimensionner, cliquer pour poser à la taille par défaut.
225
+ - **Outils** — sélection, rectangle, ellipse, ligne, texte, icône. Glisser pour dimensionner, cliquer pour poser à la taille par défaut. L'outil icône ouvre d'abord son sélecteur : poser une icône suppose d'avoir choisi laquelle.
174
226
  - **Édition** — déplacement au glisser, 8 poignées de redimensionnement (2 extrémités pour une ligne), sélection multiple au rectangle d'encadrement ou au Maj+clic.
175
227
  - **Groupes** — `Ctrl+G` groupe la sélection, `Ctrl+Maj+G` la dissout. Un clic dans le canvas sélectionne le **groupe le plus haut** ; `Alt+clic` vise l'élément exact à l'intérieur. Déplacer un groupe déplace tout son contenu.
176
228
  - **Texte** — double-clic pour éditer en place, avec la même métrique qu'au rendu final.
177
- - **Inspecteur** — position, taille, rotation, opacité, couleurs, épaisseur, arrondi, contenu, taille et graisse de police, interligne (en px), alignement. Pour un groupe, la taille est affichée en lecture seule puisqu'elle suit son contenu.
229
+ - **Inspecteur** — position, taille, rotation, opacité, couleurs, épaisseur, arrondi, contenu, taille et graisse de police, interligne (en px), alignement, choix de l'icône. Pour un groupe, la taille est affichée en lecture seule puisqu'elle suit son contenu ; pour une icône, un seul champ de taille, puisqu'elle est carrée.
178
230
  - **Calques** — arbre repliable, ordre d'empilement, sélection, montée/descente d'un cran.
179
231
 
180
232
  ### Naviguer dans la planche
@@ -183,7 +235,7 @@ Trois colonnes : la liste des planches, le canvas, l'inspecteur et les calques.
183
235
  - **Déplacement** : molette seule, ou **barre espace maintenue + glisser**. Tant que la barre espace est enfoncée, aucun geste ne peut déplacer un élément, même en partant de l'un d'eux. Le clic molette fait la même chose.
184
236
  - Le sélecteur de zoom de la barre d'outils reste disponible pour revenir à une valeur ronde.
185
237
 
186
- Raccourcis : `V` `R` `O` `L` `T` (outils), `Ctrl+Z` / `Ctrl+Y` (annuler/rétablir), `Ctrl+G` / `Ctrl+Maj+G` (grouper / dissoudre), `Ctrl+D` (dupliquer), `Ctrl+A` (tout sélectionner), `Suppr`, flèches (déplacer d'1 px, 10 px avec `Maj`), `Échap`.
238
+ Raccourcis : `V` `R` `O` `L` `T` `I` (outils), `Ctrl+Z` / `Ctrl+Y` (annuler/rétablir), `Ctrl+G` / `Ctrl+Maj+G` (grouper / dissoudre), `Ctrl+D` (dupliquer), `Ctrl+A` (tout sélectionner), `Suppr`, flèches (déplacer d'1 px, 10 px avec `Maj`), `Échap`.
187
239
 
188
240
  La sauvegarde est automatique et différée. Une modification faite par l'agent pendant que la GUI est ouverte y apparaît en direct, via SSE ; une modification faite dans la GUI est enregistrée comme venant de l'utilisateur, et c'est ce que `get_changes` restitue.
189
241
 
@@ -200,9 +252,9 @@ Deux processus, conformément au standard de l'écosystème (`@imenam/mcp-gui-in
200
252
 
201
253
  Les deux communiquent par IPC. Le worker se termine proprement quand le master disparaît (canal IPC fermé, PID parent absent) et quand une autre instance est déjà enregistrée (HTTP 409), ce qui évite les GUI orphelines.
202
254
 
203
- `shared/render.ts` contient le rendu SVG d'un élément, `shared/tree.ts` la résolution de la hiérarchie en coordonnées document, et `shared/node-defaults.ts` les valeurs par défaut. Les trois sont importés **à la fois** par l'exporteur côté serveur et par le canvas côté navigateur : ce que l'utilisateur voit est exactement ce que `export_design` — et donc `screenshot_design`, qui rasterise ce même SVG via resvg — produit.
255
+ `shared/render.ts` contient le rendu SVG d'un élément, `shared/tree.ts` la résolution de la hiérarchie en coordonnées document, `shared/instances.ts` l'expansion des instances en groupes, `shared/node-defaults.ts` les valeurs par défaut et `shared/icons/` la bibliothèque d'icônes. Tous sont importés **à la fois** par l'exporteur côté serveur et par le canvas côté navigateur : ce que l'utilisateur voit est exactement ce que `export_design` — et donc `screenshot_design`, qui rasterise ce même SVG via resvg — produit.
204
256
 
205
- Les invariants de hiérarchie — parent existant et de type `group`, absence de cycle, taille des groupes recalculée, ordre préfixe — sont appliqués dans `DesignStore.mutate`, donc sur **tout** chemin d'écriture : outil MCP comme sauvegarde depuis la GUI.
257
+ Les invariants de hiérarchie — parent existant et de type `group`, absence de cycle (y compris dans le graphe composant/instance), taille des groupes et des instances recalculée, ordre préfixe — sont appliqués dans `DesignStore.mutate`, donc sur **tout** chemin d'écriture : outil MCP comme sauvegarde depuis la GUI.
206
258
 
207
259
  **Limite assumée de cette version** : un groupe se déplace, se pivote et change d'opacité, mais ne se redimensionne pas — mettre son contenu à l'échelle demanderait de propager un facteur dans tout le sous-arbre, ce qui dépasse le cadre d'un premier jet sobre.
208
260
 
@@ -212,8 +264,9 @@ Les invariants de hiérarchie — parent existant et de type `group`, absence de
212
264
 
213
265
  ```bash
214
266
  npm install
215
- npm run build # backend (tsc) + GUI (vite)
216
- npm test # suite complète
267
+ npm run build # backend (tsc) + GUI (vite)
268
+ npm test # suite complète
269
+ npm run generate-icons # refige shared/icons/ après une mise à jour de lucide-static
217
270
  ```
218
271
 
219
272
  La suite comprend :