La différence entre un fichier de design et une bibliothèque de composants, c'est l'identité. Un fichier de design contient des formes. Une bibliothèque de composants contient des éléments nommés, réutilisables, avec des identifiants stables. components/inventory.json est la réponse de figmascope à la question : « quels éléments de ce design sont censés être des composants, et comment les instances se relient-elles à eux ? »

Le schéma

components/inventory.json est un tableau d'objets :

[
  {
    "id": "789:012",
    "name": "PrimaryButton",
    "type": "COMPONENT"
  },
  {
    "id": "789:013",
    "name": "SecondaryButton",
    "type": "COMPONENT"
  },
  {
    "id": "890:100",
    "name": "Button",
    "type": "COMPONENT_SET"
  },
  {
    "id": "890:101",
    "name": "Button/Primary/Default",
    "type": "COMPONENT"
  },
  {
    "id": "890:102",
    "name": "Button/Primary/Pressed",
    "type": "COMPONENT"
  }
]

Chaque entrée a trois champs :

C'est le schéma complet. Il est intentionnellement minimal.

Comment les instances se relient

Le lien entre les instances et l'inventaire se trouve dans l'IR par écran. Tout nœud qui est une INSTANCE d'un composant porte componentId et componentName :

// screens/home.json
{
  "kind": "stack",
  "id": "555:201",
  "componentId": "789:012",
  "componentName": "PrimaryButton",
  "axis": "horizontal",
  ...
}

Le componentId correspond à un id dans inventory.json. Le componentName correspond au name. Les deux champs sont présents pour que l'agent n'ait pas à charger inventory.json pour obtenir le nom — mais s'il a besoin de savoir que ce composant fait partie d'un COMPONENT_SET, il croise les références avec l'inventaire.

C'est ainsi qu'un agent de codage sait que le nœud bouton à l'écran n'est pas une mise en page bespoke à reproduire en détail — c'est une instance de PrimaryButton, et il doit appeler le composable PrimaryButton() existant plutôt que d'en générer un nouveau à partir des détails structurels de l'IR.

Sans identité de composant, un agent génère le même bouton de zéro sur chaque écran. Avec elle, l'agent appelle le composable existant et ignore entièrement la génération de code structurel. La différence est entre 40 blocs Row { Text(...) Surface { ... } } et 40 appels PrimaryButton(...).

Pourquoi c'est important pour la sécurité du refactoring

Les design systems sont refactorisés. Un bouton prend une nouvelle forme, un nouveau padding, une couleur différente. Si tout votre code généré était structurellement littéral, le refactoring implique de toucher chaque écran qui a un bouton. Si le code généré utilisait la référence au composable PrimaryButton, le refactoring se résume à un seul fichier.

L'inventaire rend cela possible en établissant le contrat au moment de la génération : « ce nœud n'est pas une mise en page personnalisée, c'est une instance d'un composant connu. » L'agent qui respecte ce contrat génère du code déjà structuré pour un refactoring au niveau des composants.

C'est l'avantage structurel principal par rapport aux passations basées sur des captures d'écran. Une capture d'écran d'un bouton, c'est des pixels. Elle n'a pas d'identité. Un agent travaillant depuis une capture d'écran génèrera toujours du code structurel pour le bouton sur chaque écran. Un agent travaillant depuis l'IR avec un lien d'inventaire peut reconnaître l'instance et utiliser la référence au composant à la place.

COMPONENT vs. COMPONENT_SET

Le système de composants de Figma a deux niveaux. Un COMPONENT est une définition unique. Un COMPONENT_SET est un conteneur de variantes — ce que Figma appelle des « variantes » (par exemple, un bouton avec des états Primary/Secondary/Destructive et Default/Hovered/Pressed).

En pratique, une instance sur un écran référencera toujours un COMPONENT (une variante spécifique), pas le COMPONENT_SET. Le COMPONENT_SET est là pour que l'agent connaisse la surface complète des variantes quand il a besoin d'implémenter la machine d'états du composant.

// Entrées d'inventaire pour un ensemble Button
{ "id": "890:100", "name": "Button", "type": "COMPONENT_SET" }
{ "id": "890:101", "name": "Button/Primary/Default", "type": "COMPONENT" }
{ "id": "890:102", "name": "Button/Primary/Pressed", "type": "COMPONENT" }
{ "id": "890:103", "name": "Button/Secondary/Default", "type": "COMPONENT" }

// L'instance dans l'IR de l'écran référence une variante spécifique
{ "componentId": "890:101", "componentName": "Button/Primary/Default" }

Un agent générant du code Compose pour cela sait : le composant est un Button avec un style Primary et un état Default. Il peut déduire que la signature de la fonction implique probablement des paramètres style: ButtonStyle et state: ButtonState, ou utiliser au minimum Button/Primary/Default comme référence sémantique pour un bouton primaire dans son état par défaut.

La limite de 300 entrées

figmascope limite inventory.json à 300 entrées. Les fichiers Figma à grande échelle — en particulier les design systems avec des bibliothèques de composants exhaustives — peuvent avoir des milliers de composants. Les inclure tous dans un bundle de contexte destiné à être envoyé à un LLM rembourrerait la fenêtre de contexte avec des définitions que l'agent n'utilisera pas pour les écrans en cours d'implémentation.

Quand la limite est atteinte, un champ _truncated apparaît dans l'inventaire :

[
  { "id": "...", "name": "...", "type": "COMPONENT" },
  ...
  { "_truncated": true, "totalCount": 847, "included": 300 }
]

Le totalCount vous indique combien de composants existent dans le fichier. Le champ included vous indique combien ont été inclus dans l'inventaire. L'ordre est celui de la première rencontre dans l'arbre de nœuds Figma, donc les composants référencés tôt dans le document (généralement les écrans principaux) ont plus de chances d'être inclus.

Si vous travaillez sur des écrans qui utilisent des composants définis tard dans le document et qu'ils ne sont pas dans l'inventaire, les nœuds IR de ces instances ont quand même componentId et componentName — l'information d'identité est préservée même quand le composant n'est pas listé dans l'inventaire. L'agent connaît le nom du composant depuis l'IR, même sans l'entrée d'inventaire.

Ce que l'inventaire n'inclut pas

L'inventaire est une liste d'identités, pas une spec d'implémentation. Il vous dit qu'un composant nommé PrimaryButton avec l'ID 789:012 existe. Il ne vous dit pas :

Ces lacunes sont intentionnelles dans la v0.4. L'inférence complète des props des composants depuis la structure IR est possible mais produirait des résultats peu fiables pour les composants avec une logique de variantes complexe. L'inventaire fournit une identité stable. Les détails d'implémentation viennent de votre base de code existante, à laquelle l'agent a également accès.

Le bon workflow : l'agent voit un componentId: "789:012", le recherche dans l'inventaire comme PrimaryButton, puis cherche dans votre base de code Kotlin le PrimaryButton pour comprendre la signature de la fonction réelle. L'inventaire est le pont entre le design et le code, pas un substitut au code. Vous pouvez exporter un inventaire depuis n'importe quel fichier Figma sur figmascope.dev.

L'identité des composants dans l'IR est ce qui distingue « générer du code depuis ce design » de « générer du code qui s'intègre dans une base de code existante ». Le premier est un jouet. Le second est le vrai travail.

Comparaison avec la passation basée uniquement sur des captures d'écran

Un agent travaillant depuis un PNG du même écran n'a pas d'identité de composant. Il voit un rectangle bleu arrondi avec du texte centré et génère :

Box(
    modifier = Modifier
        .background(Color(0xFF7F5CFE), RoundedCornerShape(24.dp))
        .padding(horizontal = 32.dp, vertical = 16.dp)
) {
    Text("Start", color = Color.White, fontWeight = FontWeight.SemiBold)
}

Un agent travaillant depuis l'IR avec l'inventaire voit componentId: "789:012", trouve PrimaryButton, trouve le composable existant dans la base de code, et génère :

PrimaryButton(
    label = stringResource(R.string.start),
    onClick = { /* TODO */ }
)

La deuxième sortie s'intègre à votre design system. La première crée de la divergence. L'inventaire est ce qui rend la deuxième sortie possible. Pour plus d'informations sur pourquoi les captures d'écran échouent comme artefacts de passation, voir Pourquoi les captures d'écran échouent.

L'IR par écran qui contient les références componentId est couvert en détail dans IR par écran — Stack, Overlay, Absolute, Leaf. La structure complète du bundle de contexte qui contient les deux artefacts est introduite via Anatomie de CONTEXT.md. Pour obtenir votre propre inventaire de composants, exécutez figmascope sur votre fichier Figma.