Sự khác biệt giữa một file thiết kế và một thư viện component là danh tính. Một file thiết kế có các hình dạng. Một thư viện component có các phần được đặt tên, có thể tái sử dụng với các định danh ổn định. components/inventory.json là câu trả lời của figmascope cho: "những phần nào trong thiết kế này được thiết kế để là các component, và cách các instance kết nối trở lại với chúng?"
Schema
components/inventory.json là một mảng các đối tượng:
[
{
"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"
}
]
Mỗi mục có ba trường:
id: ID node Figma, ổn định qua các phiên cho cùng một file.name: Tên component từ Figma. Đối với các variant trong COMPONENT_SET, Figma sử dụng ký hiệu gạch chéo:Button/Primary/Default.type: Là"COMPONENT"(một định nghĩa component đơn) hoặc"COMPONENT_SET"(một nhóm variant).
Đó là schema đầy đủ. Nó cố ý tối giản.
Cách các instance liên kết trở lại
Liên kết từ các instance đến inventory nằm trong per-screen IR. Bất kỳ node nào là INSTANCE của một component đều mang componentId và componentName:
// screens/home.json
{
"kind": "stack",
"id": "555:201",
"componentId": "789:012",
"componentName": "PrimaryButton",
"axis": "horizontal",
...
}
componentId khớp với một id trong inventory.json. componentName khớp với name. Cả hai trường đều có mặt để agent không phải tải inventory.json để lấy tên — nhưng nếu nó cần biết component này là một phần của COMPONENT_SET, nó tham chiếu chéo inventory.
Đây là cách một coding agent biết rằng node button trên màn hình không phải là layout tùy chỉnh cần tái tạo chi tiết — đó là một instance của PrimaryButton, và nó nên gọi composable PrimaryButton() hiện có thay vì tạo ra một cái mới từ các chi tiết cấu trúc của IR.
Không có danh tính component, một agent tạo ra cùng một button từ đầu trên mỗi màn hình. Với nó, agent gọi composable hiện có và bỏ qua hoàn toàn việc tạo code cấu trúc. Sự khác biệt là liệu bạn có được 40 khối
Row { Text(...) Surface { ... } }hay 40 lời gọiPrimaryButton(...).
Tại sao điều này quan trọng cho an toàn tái cấu trúc
Các design system được tái cấu trúc. Một button nhận được hình dạng mới, padding mới, màu sắc khác. Nếu tất cả code được tạo ra của bạn là cấu trúc literal, thì tái cấu trúc có nghĩa là đụng đến mỗi màn hình có button. Nếu code được tạo ra sử dụng tham chiếu composable PrimaryButton, tái cấu trúc là một file.
Inventory làm điều này có thể bằng cách thiết lập hợp đồng tại thời điểm tạo ra: "node này không phải là layout tùy chỉnh, đó là một instance của một component đã biết." Agent tôn trọng hợp đồng này tạo ra code đã được cấu trúc cho tái cấu trúc ở cấp component.
Đây là ưu điểm cấu trúc chính so với handoff chỉ dùng screenshot. Một ảnh chụp màn hình của một button là các pixel. Nó không có danh tính. Một agent làm việc từ screenshot sẽ tạo code cấu trúc cho button trên mỗi màn hình, luôn luôn. Một agent làm việc từ IR với liên kết inventory có thể nhận ra instance và sử dụng tham chiếu component thay thế.
COMPONENT vs. COMPONENT_SET
Hệ thống component của Figma có hai cấp độ. Một COMPONENT là một định nghĩa đơn. Một COMPONENT_SET là một container cho các variant — điều Figma gọi là "variants" (ví dụ: một button với trạng thái Primary/Secondary/Destructive và Default/Hovered/Pressed).
Trên thực tế, một instance trong một màn hình sẽ luôn tham chiếu đến một COMPONENT (một variant cụ thể), không phải COMPONENT_SET. COMPONENT_SET ở đó để agent biết bề mặt variant đầy đủ khi nó cần triển khai state machine của component.
// Inventory entries for a Button set
{ "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" }
// Instance in screen IR references a specific variant
{ "componentId": "890:101", "componentName": "Button/Primary/Default" }
Một agent tạo code Compose cho điều này biết: component là một Button với kiểu Primary và trạng thái Default. Nó có thể suy ra chữ ký hàm có thể liên quan đến các tham số style: ButtonStyle và state: ButtonState, hoặc ít nhất sử dụng Button/Primary/Default như tham chiếu ngữ nghĩa cho một primary button ở trạng thái mặc định của nó.
Giới hạn 300 mục
figmascope giới hạn inventory.json ở 300 mục. Các file Figma ở quy mô lớn — đặc biệt là các design system với thư viện component đầy đủ — có thể có hàng nghìn component. Bao gồm tất cả chúng trong một context bundle được thiết kế để gửi đến LLM sẽ đệm thêm cửa sổ context với các định nghĩa mà agent sẽ không sử dụng cho các màn hình đang được triển khai.
Khi giới hạn bị vượt qua, một trường _truncated xuất hiện trong inventory:
[
{ "id": "...", "name": "...", "type": "COMPONENT" },
...
{ "_truncated": true, "totalCount": 847, "included": 300 }
]
totalCount cho bạn biết bao nhiêu component tồn tại trong file. included cho bạn biết bao nhiêu đã vào inventory. Thứ tự là theo lần đầu gặp trong cây node Figma, vì vậy các component được tham chiếu sớm trong tài liệu (thường là các màn hình chính) có nhiều khả năng được bao gồm hơn.
Nếu bạn đang làm việc trên các màn hình sử dụng các component được định nghĩa muộn trong tài liệu và chúng không có trong inventory, các node IR cho các instance đó vẫn có componentId và componentName — thông tin danh tính được bảo tồn ngay cả khi component không được liệt kê trong inventory. Agent biết tên của component từ IR, ngay cả không có mục inventory.
Inventory không bao gồm những gì
Inventory là danh sách danh tính, không phải thông số triển khai. Nó cho bạn biết một component tên PrimaryButton với ID 789:012 tồn tại. Nó không cho bạn biết:
- Component chấp nhận những props nào
- Nó có những trạng thái nào
- Component được cấu trúc nội bộ như thế nào (đó là trong các node IR là INSTANCE)
- File Figma nguồn của component là gì (nếu nó từ thư viện được liên kết)
Những khoảng trống này là có chủ đích trong v0.4. Suy luận prop component đầy đủ từ cấu trúc IR là có thể nhưng sẽ tạo ra kết quả không đáng tin cậy cho các component với logic variant phức tạp. Inventory cung cấp danh tính ổn định. Chi tiết triển khai đến từ codebase hiện có của bạn, mà agent cũng có quyền truy cập.
Quy trình làm việc đúng: agent thấy một componentId: "789:012", tra cứu nó trong inventory là PrimaryButton, sau đó tìm kiếm codebase Kotlin của bạn cho PrimaryButton để hiểu chữ ký hàm thực tế. Inventory là cầu nối giữa thiết kế và code, không phải thay thế cho code. Bạn có thể xuất inventory từ bất kỳ file Figma nào trên figmascope.dev.
Danh tính component trong IR là điều phân tách "tạo code từ thiết kế này" với "tạo code phù hợp với codebase hiện có." Cái trước là đồ chơi. Cái sau mới là công việc thực.
So sánh với handoff chỉ dùng screenshot
Một agent làm việc từ PNG của cùng một màn hình không có danh tính component. Nó thấy một hình chữ nhật bo tròn màu xanh với văn bản căn giữa và tạo ra:
Box(
modifier = Modifier
.background(Color(0xFF7F5CFE), RoundedCornerShape(24.dp))
.padding(horizontal = 32.dp, vertical = 16.dp)
) {
Text("Start", color = Color.White, fontWeight = FontWeight.SemiBold)
}
Một agent làm việc từ IR với inventory thấy componentId: "789:012", tra cứu PrimaryButton, tìm composable hiện có trong codebase, và tạo ra:
PrimaryButton(
label = stringResource(R.string.start),
onClick = { /* TODO */ }
)
Output thứ hai tích hợp với design system của bạn. Output đầu tiên tạo ra sự phân kỳ. Inventory là điều làm cho output thứ hai có thể. Để biết thêm về lý do tại sao screenshot thất bại như các artifact handoff, xem Tại sao Screenshot thất bại.
Per-screen IR chứa các tham chiếu componentId được đề cập chi tiết trong Per-Screen IR — Stack, Overlay, Absolute, Leaf. Cấu trúc context bundle đầy đủ chứa cả hai artifact được giới thiệu qua Giải phẫu CONTEXT.md. Để lấy component inventory của riêng bạn, chạy figmascope trên file Figma của bạn.