Drzewo węzłów Figmy jest bogate, ale hałaśliwe. Frame, Group, Component, Instance, Text, Rectangle, Ellipse, Vector — każdy ma dziesiątki opcjonalnych pól, z których część ze sobą konfliktuje. Agent pracujący bezpośrednio na surowej odpowiedzi API Figmy traci budżet poznawczy na rozumienie schematu zamiast pisania kodu.
figmascope normalizuje drzewo do czterech rodzajów węzłów: stack, overlay, absolute i leaf. Każdy węzeł w każdym pliku screens/*.json jest jednym z tych czterech. Nic innego.
Cztery rodzaje
stack
Stack to węzeł z Figma Auto Layout — layoutMode: "VERTICAL" lub layoutMode: "HORIZONTAL". Jego dzieci są pozycjonowane przepływem wzdłuż osi. Przerwy i padding są explicite.
{
"kind": "stack",
"id": "123:456",
"name": "ContentColumn",
"axis": "vertical",
"gap": 16,
"paddingTop": 24,
"paddingBottom": 24,
"paddingLeft": 20,
"paddingRight": 20,
"primaryAxisAlignItems": "SPACE_BETWEEN",
"counterAxisAlignItems": "CENTER",
"width": 390,
"height": 844,
"fills": [{ "type": "SOLID", "color": "#1a1a2e" }],
"children": [ ... ]
}
Mapuje na Jetpack Compose jako Column (oś pionowa) lub Row (oś pozioma). Pola primaryAxisAlignItems i counterAxisAlignItems mapują odpowiednio na Arrangement i Alignment. gap staje się Arrangement.spacedBy().
overlay
Overlay to węzeł, którego dzieci mają absolutne pozycje wewnątrz niego. Figma reprezentuje to jako Frame z layoutMode: "NONE", gdzie dzieci mają współrzędne absoluteBoundingBox. Rodzaj overlay przechwytuje to bez wymagania od agenta rozumowania na temat surowych współrzędnych w celu zrozumienia struktury.
{
"kind": "overlay",
"id": "123:789",
"name": "CardOverlay",
"width": 358,
"height": 200,
"fills": [{ "type": "SOLID", "color": "#ffffff" }],
"children": [
{
"kind": "absolute",
"offset": { "x": 16, "y": 16 },
"child": { ... }
}
]
}
Mapuje na Compose jako Box. Dzieci są pozycjonowane za pomocą Modifier.offset() lub Modifier.align() zależnie od tego, jak ich pozycje odnoszą się do granic rodzica.
absolute
Węzeł absolute to opakowanie niosące jedno dziecko w konkretnym ofsecie (x, y) wewnątrz overlay rodzica. To cienki węzeł strukturalny — istnieje, by zachować relację przestrzenną z Figmy bez mieszania pozycji z zawartością.
{
"kind": "absolute",
"offset": { "x": 24, "y": 140 },
"child": {
"kind": "leaf",
"name": "BadgeLabel",
"text": "NEW",
"stringRef": "badge.new",
...
}
}
W Compose to zazwyczaj dziecko Box z Modifier.offset(x.dp, y.dp). Wartości offsetu są kandydatami do podstawienia tokenem, jeśli tokeny odstępów istnieją w zakresie.
leaf
Leaf to węzeł terminalny — bez dzieci. Ma stylowanie (wypełnienia, obrysy, promień rogu) i opcjonalnie zawartość tekstową. Liście tekstowe mają pole text (dosłowny ciąg) i pole stringRef (klucz zasobu i18n z strings.json).
{
"kind": "leaf",
"id": "123:101",
"name": "SpeedLabel",
"text": "Speed Test",
"stringRef": "speed.test",
"fontSize": 18,
"fontWeight": 600,
"fills": [{ "type": "SOLID", "color": "#ffffff" }],
"width": 160,
"height": 28
}
Mapuje na Compose jako composable Text() dla zawartości tekstowej lub jako Surface, Box lub Image dla nieniestowych liści zależnie od typu wypełnienia.
Cztery rodzaje mapują jeden do jednego na kompozycyjny model każdego frameworka UI. Stacki to układy przepływu. Overlaye to kontenery z pozycjonowaniem z. Absoluty niosą metadane przestrzenne. Liście to zawartość. To wszystko. Taksonomia węzłów Figmy ma ponad 12 typów — IR redukuje to do czterech.
Jak określany jest rodzaj
Logika klasyfikacji:
- Jeśli
layoutModeto"VERTICAL"lub"HORIZONTAL"→stack - Jeśli
layoutModeto"NONE"i węzeł ma dzieci →overlay(dzieci owinięte jakoabsolute) - Jeśli węzeł jest bezpośrednim dzieckiem overlay i niesie pozycję →
absolute - Jeśli węzeł nie ma dzieci i nie jest opakowaniem absolute →
leaf
Ostrzeżenie layout-mode-none-inferred w _meta.json odpala się, gdy Frame z layoutMode: "NONE" ma dzieci z nietrywialnie nakładającymi się bounding boxami. figmascope traktuje to jako overlay, ale odnotowuje inferencję, ponieważ niektóre ramki layoutMode: "NONE" w praktyce są po prostu kontenerami dla jednego dziecka (zerowe nakładanie) i mogłyby być uproszczone. Ostrzeżenie pozwala agentowi zdecydować, jak to obsłużyć.
absoluteBoundingBox — dlaczego jest zachowane
Każdy węzeł w IR zachowuje swój absoluteBoundingBox z Figmy, nawet gdy rodzic jest stackiem (gdzie absolutne pozycje są teoretycznie nieistotne dla układu):
{
"kind": "stack",
"absoluteBoundingBox": { "x": 0, "y": 88, "width": 390, "height": 756 },
...
}
Jest to tam dla agentów, które muszą rozumować o relacjach przestrzennych między węzłami, które nie mają bezpośredniej relacji rodzic-dziecko. Nakładanie dwóch elementów z różnych gałęzi drzewa wymaga znajomości ich absolutnych pozycji, nie tylko relative offsetów. Układ współrzędnych to canvas Figmy — początek w lewym górnym rogu, y zwiększa się w dół.
Pomaga też z porównaniem do PNG. _meta.json zawiera pole pngCount, a wyeksportowane PNG są nazwane według sluga ekranu. Jeśli porównujesz IR ze zrzutem ekranu, absoluteBoundingBox pozwala zlokalizować konkretne węzły wewnątrz obrazu. Aby zobaczyć to na własnym projekcie, wyeksportuj pakiet z figmascope.dev.
Wypełnienia i obrysy na rodzajach kontenera
Częsty punkt zamieszania: węzły stack i overlay mogą mieć wypełnienia, nie tylko liście. Kolumna z kolorem tła to nadal stack — ma po prostu też tablicę fills. figmascope to zachowuje:
{
"kind": "stack",
"axis": "vertical",
"fills": [{ "type": "SOLID", "color": "#f6f2ea" }],
"cornerRadius": 12,
...
}
W Compose to zazwyczaj Column wewnątrz Surface lub Card z kolorem wypełnienia i promieniem rogu zastosowanym do kontenera. IR nie zwija wypełnienia do dzieci — zachowuje je na węźle, gdzie Figma je ustawiła.
Wypełnienia gradientowe na rodzajach kontenera emitują ostrzeżenie background-gradient-not-supported:<name>. Węzeł jest nadal obecny w IR z zachowanymi innymi polami. Agent powinien traktować wypełnienie jako TODO i albo przybliżyć je kolorem jednolitym, albo wygenerować niestandardowe wywołanie rysowania, zgodnie z notatkami zakresu CONTEXT.md.
Relacja stringRef + text
Węzły liści tekstowych niosą oba:
text: dosłowna wartość ciągu z Figmy (np."Speed Test")stringRef: klucz zasobu zstrings.json(np."speed.test")
Jeśli zawartość węzła tekstowego została usunięta z strings.json z powodu kolizji lub filtra (tylko numeryczne, puste, za krótkie), leaf nadal ma text, ale stringRef będzie nieobecny. Agent powinien w tym przypadku cofnąć się do dosłownego. Zobacz strings.json dla pełnej logiki obsługi kolizji.
Gdy oba są obecne, ograniczenie CONTEXT.md mówi, by używać stringRef. Pole text jest tam dla rozumowania agenta (żeby wiedział, co mówi ciąg bez otwierania strings.json) i jako fallback.
Instancje komponentów w IR
Gdy węzeł Figmy jest INSTANCE komponentu, węzeł IR niesie dwa dodatkowe pola:
{
"kind": "stack",
"componentId": "789:012",
"componentName": "PrimaryButton",
...
}
To łączy z components/inventory.json. Agent wie, że ten węzeł to instancja PrimaryButton, a nie bespoke układ, i powinien odwołać się do istniejącego komponentu zamiast generować duplikat kodu. Pełne omówienie jest w Component Inventory.
Realny przykład ekranu
Uproszczony, ale strukturalnie dokładny przykład ekranu z nagłówkiem, kolumną zawartości i pływającym przyciskiem:
{
"screen": "home",
"root": {
"kind": "overlay",
"name": "HomeScreen",
"width": 390,
"height": 844,
"fills": [{ "type": "SOLID", "color": "#0d0d1a" }],
"children": [
{
"kind": "absolute",
"offset": { "x": 0, "y": 0 },
"child": {
"kind": "stack",
"name": "ContentArea",
"axis": "vertical",
"gap": 24,
"paddingTop": 56,
"paddingLeft": 20,
"paddingRight": 20,
"children": [
{
"kind": "leaf",
"name": "Title",
"text": "Speed Test",
"stringRef": "speed.test",
"fontSize": 28,
"fontWeight": 700
}
]
}
},
{
"kind": "absolute",
"offset": { "x": 111, "y": 752 },
"child": {
"kind": "stack",
"name": "StartButton",
"componentId": "321:654",
"componentName": "PrimaryButton",
"axis": "horizontal",
"gap": 8,
"paddingTop": 16,
"paddingBottom": 16,
"paddingLeft": 32,
"paddingRight": 32,
"cornerRadius": 24,
"fills": [{ "type": "SOLID", "color": "#7f5cfe" }]
}
}
]
}
}
Z tego agent może poprawnie wygenerować ekran Compose Box z absolutnie pozycjonowaną Column dla zawartości i absolutnie pozycjonowanym komponentem PrimaryButton na dole. Każda decyzja układu jest wyprowadzalna z IR bez zgadywania.
Jak tokeny stosują się do wartości odstępów i kolorów w tej strukturze, zobacz tokens.json Wyjaśniony. Dla pełnego workflow agenta używającego tego IR z Cursor lub Claude Code, zobacz Jetpack Compose z Figmy. Wypróbuj samodzielnie wklejając URL Figma do figmascope.