Communitygithub.com

iggypcnfsky/figma-bridge

Pilotez un fichier Figma via le pont Figma Bridge local : lisez la sélection, construisez des cadres, du texte et des formes, liez des variables, placez des images, exportez en PNG/SVG.

Qu'est-ce que figma-bridge ?

Cette compétence agit comme un pont local entre un agent IA et Figma, contournant l'absence d'API publique d'écriture en utilisant un serveur HTTP local et un plugin Figma. Elle permet de lire et d'écrire dans un fichier Figma : sélectionner des nœuds, créer des cadres, du texte et des formes, appliquer des variables de design, placer des images importées ou générées, et exporter en PNG ou SVG. L'agent exécute une série de commandes structurées (ops) via un CLI ou des requêtes HTTP, en respectant un cycle strict : s'orienter (statut du fichier), lire (sélection, nœud), planifier (parent, taille, ordre), écrire (lot d'ops) et vérifier (exporter et inspecter visuellement). Les préconditions critiques incluent la vérification que le serveur figma-bridge tourne sur localhost:3847 et que le plugin Figma Bridge est actif dans le fichier cible ; l'agent ne doit pas improviser en leur absence et doit demander à l'utilisateur de les démarrer. La compétence privilégie la lecture avant l'écriture, le respect des dispositions existantes, et l'utilisation de lots atomiques (un lot = une pensée) pour éviter les dépendances entre ops. Elle s'intègre également avec img-forge pour générer des visuels en dehors de Figma, puis les placer sans exposer de clé API.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/iggypcnfsky/figma-bridge/tree/HEAD/.agents/skills/figma-bridge

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Figma Bridge

Figma ne dispose pas d'API publique d'écriture. Cette compétence utilise un pont local : des opérations nommées via HTTP, exécutées dans Figma par un plugin, via l'API Plugin Figma. Pas d'OAuth, pas de jetons.

Dépôt : https://github.com/iggypcnfsky/figma-bridge — AGENTS.md, docs/ops-reference.md.

Préconditions — à vérifier avant toute action

figma est dans le PATH, donc ne cherchez jamais le dépôt dans le système de fichiers :

figma doctor        # dépôt, serveur, fenêtres connectées, img-forge + clé FAL en une commande
figma clients       # id, fichier, page de chaque fenêtre Figma connectée
figma rules         # contraintes du projet : type floor, grille, taille de cadre, nommage de version

Lisez figma rules avant de concevoir. Il contient les limites non évidentes — par ex. « Poppins 20/25 est le minimum » — qu'aucune lecture du canevas ne vous révélera. Considérez "confirmed": false comme une hypothèse à vérifier avec l'utilisateur, et {} comme « demandez, n'inventez pas ».

Si la commande est manquante, utilisez node "$(figma where)/server.mjs" seulement après avoir demandé ; ne faites pas de find du dépôt et ne devinez pas les chemins.

  • pluginConnected: false → arrêtez. Demandez à l'utilisateur d'exécuter Plugins → Development → Figma Bridge dans la fenêtre du fichier qu'il souhaite modifier. Réessayer ne sert à rien.
  • 503 de /run signifie la même chose. 504 PLUGIN_GONE signifie qu'ils ont fermé la fenêtre du plugin.
  • 409 signifie que plusieurs fenêtres Figma sont connectées et qu'aucune n'a été choisie. Sélectionnez le fichier nommé par l'utilisateur (--file <NOM>) ou un identifiant client (--client <ID>) ; ne réessayez jamais aveuglément, sinon vous risquez de modifier le mauvais fichier.

La boucle

  1. Orientez-vous. ./bin/figma status → nom du fichier, page courante, sélection, toutes les pages.
  2. Lisez. selection 2, node <id> 2, find {"name":"Header","allPages":true}. Ne devinez jamais un nodeId — les identifiants Figma ressemblent à 4022:2 et ne sont pas dérivables.
  3. Planifiez. Déterminez le parent, la taille, l'ordre. Les nouveaux nœuds vont sur la page courante sauf si parentId est fourni.
  4. Écrivez. ./bin/figma run '[{...},{...}]'.
  5. Vérifiez. ./bin/figma export <id> out/check.png PNG 2 et regardez réellement l'image.

Commandes

./bin/figma op '{"op":"status"}'
./bin/figma run '[{"op":"createFrame","name":"Card","width":320,"height":200,"fill":"#0B0B0F",
                   "props":{"cornerRadius":16,"layoutMode":"VERTICAL","paddingTop":24,"paddingLeft":24,"itemSpacing":12}}]'
./bin/figma run '[{"op":"createText","parentId":"12:34","characters":"Title","fontSize":24,"color":"#FFFFFF"}]'
./bin/figma op '{"op":"varFill","nodeId":"12:34","variableName":"color/bg/surface"}'
./bin/figma export 12:34 out/card.png PNG 2
./bin/figma image 12:34 out/cover.png FILL      # fichier image local → remplissage (pas de cloud)

Ops : ping status pages variables styles fonts inventory · selection node find · createFrame createRect createEllipse createText createPage createInstance · update clone reparent remove goto group ungroup select · grids setGrids align distribute effects · applyStyle varFill · export imageFill · exec. Paramètres complets : docs/ops-reference.md.

Raccourcis utiles pour le travail de design :

  • grid signifie GRILLE : quand l'utilisateur dit « mettre une grille », il veut la grille de mise en page carrée, pas des colonnes : {"op":"setGrids","nodeId":"…","grid":true} (cellules de 40 px, 40 % par défaut) ou "grid":40. Utilisez columns/rows seulement s'ils parlent de colonnes, d'une grille à 12 colonnes, ou donnent des valeurs de gouttière/marge. Ne simulez jamais une grille avec des rectangles ; applyStyle avec kind:"grid" applique un style de grille enregistré.
  • lignes d'éléments : construisez-les, puis {"op":"align","nodeIds":[…],"mode":"TOP"} et {"op":"distribute","nodeIds":[…],"axis":"HORIZONTAL","gap":24}.
  • profondeur : {"op":"effects","nodeId":"…","shadow":{"y":8,"blur":24,"color":"#00000033"}} ou {"op":"effects","nodeId":"…","blur":{"type":"BACKGROUND","radius":20}}.
  • dégradés : {"op":"gradient","nodeId":"…","kind":"linear","angle":135,"stops":[[0,"#8B76FF"],[1,"#0E0E12"]]} — l'angle 0 est de gauche à droite, 90 de haut en bas. Ne simulez jamais un dégradé avec des rectangles empilés.
  • système de design : createInstance avec un id COMPONENT, puis instanceProps pour les variantes (lisez d'abord les noms) et instanceText pour changer le texte sans détacher ; applyStyle pour les styles. Ne redessinez pas ce que la bibliothèque fournit déjà et ne détachez pas une instance pour « corriger » son texte.
  • vérifications d'espacement : {"op":"measure","a":"1:2","b":"1:3"} retourne les écarts par côté et la distance centrale ; utilisez-le plutôt que d'exporter une capture d'écran pour juger de l'alignement.
  • styles : saveStyle crée ou met à jour un style text / paint / effect / grid par nom, et applyStyle l'applique. Créez le jeton une fois, appliquez-le partout.
  • responsive : sizing définit HUG/FILL/FIXED par axe plus min/max ; constraints couvre les enfants non-auto-layout (STRETCH/MIN/MAX/CENTER/SCALE). Lisez les deux depuis node avant de les modifier — sizing retourne skipped avec la raison de Figma quand une propriété ne s'applique pas.

Images

Besoin d'illustrations ? Générez-les avec le CLI img-forge frère (https://github.com/iggypcnfsky/img-forge), puis placez-les — la génération reste en dehors de Figma pour que la clé API n'entre jamais dans le code du plugin et qu'un rendu lent ne bloque pas le canevas :

cd ../img-forge
./img gen --prompt "fond dégradé abstrait, violet à noir" --preset 16:9@2k --out out/cover.png
./img edit --prompt "transforme-le en nuit avec accents néon" --ref out/cover.png   # ou modifier un actif existant
cd ../figma-bridge
./bin/figma image 12:34 ../img-forge/out/cover.png FILL

FILL recadre sur le nœud, FIT ajoute des boîtes aux lettres, TILE répète, CROP respecte le recadrage existant. Pour modifier ce que l'utilisateur a sélectionné, exportez-le d'abord (./bin/figma export <nodeId> out/sel.png PNG 2) et passez ce fichier comme --ref.

La fenêtre du plugin dispose également d'un panneau Générer / Modifier pour les humains (modèle, préréglages de taille, coût, insérer). Ne pilotez pas le panneau par programmation — utilisez le CLI ; il expose plus (masque, variantes, simulation). Les préréglages enregistrés dans ce panneau vivent dans figma-bridge/presets.json.

Construire sur une ébauche existante

Une ébauche qui « n'est pas finie » encode quand même des décisions : ce qui est à côté de quoi, ce qui est groupé, l'ordre des éléments, le rythme d'espacement. Lisez-la avant de concevoir, puis conservez-la.

  1. {"op":"inventory"} — les cadres, leurs tailles et les statistiques typographiques.
  2. {"op":"structure","nodeId":"<cadre>"} — enfants regroupés en lignes avec espacements et un readingOrder ; c'est le groupement choisi par l'auteur. {"op":"node",…,"depth":2} donne le même arbre avec plus de profondeur.
  3. Notez le motif (une marge gauche qui se répète, un titre qui se trouve toujours au-dessus de trois colonnes) et conservez-le. Modifiez ce que la demande nécessite, laissez le reste là où il est.
  4. Signalez les écarts que vous avez faits — « déplacé le pied de page vers le haut de 40 px », pas seulement « amélioré la mise en page ».

Ne normalisez jamais une ébauche silencieusement. Si la cohérence nécessite de déplacer des éléments que l'utilisateur a placés, dites-le et demandez ; une diapositive soignée qui a perdu leurs groupements est une régression, pas une amélioration.

Règles

  • Lire avant d'écrire. Minimum deux appels : inspecter, puis modifier.
  • Un lot = une pensée. Une opération ne peut pas référencer le résultat d'une opération précédente, donc si vous avez besoin de l'id du nouveau nœud, divisez en deux appels.
  • Respectez la disposition héritée : l'ordre des frères, la proximité et le groupement sont une intention. Ne réorganisez pas une ébauche pour la rendre plus nette sauf demande ; demandez d'abord quand la cohérence déplacerait leurs éléments.
  • Ne touchez jamais aux calques existants de l'utilisateur (update, remove, reparent) sans instruction explicite. Préfixez vos propres créations : Bridge / ….
  • Une refonte se place juste à côté de l'original, aussi proche que possible. Si on vous demande de refaire une diapositive/écran/section : dupliquez-la, appelez {"op":"placeNear","nodeId":"<nouvel id>","move":true,"gap":80,"name":"Bridge / v2 — <nom>"} pour que la copie atterrisse sur le premier côté libre au lieu d'une coordonnée devinée, et laissez l'original intact. Si l'outil signale free: false, demandez où elle doit aller. Remplacez les éléments sur place seulement quand l'utilisateur le demande explicitement.
  • goto modifie la vue de l'utilisateur — seulement sur demande.
  • exec est un dernier recours. Cela fonctionne (le bac à sable de Figma autorise new Function) et contourne toute validation de paramètres. Retournez uniquement des valeurs sérialisables en JSON.
  • Nettoyez les artefacts de test, ou demandez s'il faut les conserver.

Pièges

  • Utilisez localhost, jamais 127.0.0.1 : le manifeste du plugin n'autorise que http://localhost:3847 (devAllowedDomains) ; Figma rejette les IP. Le serveur écoute à la fois sur 127.0.0.1 et ::1.
  • figma.currentPage est la page ouverte dans cette fenêtre Figma — pas un pointeur sauvegardé. Changer d'onglet modifie ce que vous voyez à l'appel suivant.
  • Les positions sont relatives au parent, et un parentId omis signifie la page courante.
  • Les questions typographiques (polices, tailles, nombres) se font en un appel : {"op":"fonts"} ; ajoutez "allPages":true pour tout le fichier. Ne parcourez pas les cadres avec votre propre script.
  • Un aperçu du jeu de diapositives se fait aussi en un appel : {"op":"inventory"} retourne chaque cadre avec sa géométrie, minFontSize, polices, nombre de grilles et remplissages — assez pour choisir « les cadres 11–19 » ou trouver un texte en dessous d'une taille minimale.
  • N'indexez jamais un champ que la référence ne liste pas. summary contient font, fontSize, lineHeight et letterSpacing pour TEXT ; tout le reste vient de exec avec le nom réel de l'API. Les styles mixtes sont la chaîne "MIXED", donc protégez avec .get() et des valeurs de repli.
  • Un rectangle ne peut pas avoir d'enfants ; utilisez un cadre quand du texte ou des icônes doivent être à l'intérieur d'une forme.
  • Pas de variables locales → variables retourne collections: [] et varFill échoue. Vérifiez d'abord.
  • Le texte a besoin d'une police : createText la charge via loadFontAsync ; si la famille est manquante, utilisez Inter.
  • export plafonne à 8 Mo — réduisez scale au lieu de réessayer.
  • Lire les enfants d'une page non courante nécessite loadAllPagesAsync ; le pont réessaie une fois automatiquement, mais préférez find avec allPages: true.
  • Après avoir modifié les fichiers du plugin (code.js, ui.html), l'utilisateur doit relancer le plugin ; les modifications de manifest.json nécessitent une réimportation.

Skills associés