Koobiq 圖示 Figma 審計 是做什麼的?
此技能會驗證 Koobiq 圖示 Figma 檔案中的圖示元件是否遵循庫的結構慣例,以便 figma:sync(@figma-export 管線)能產出乾淨、正確的 SVG。在 Figma 中發現問題遠比在匯出後才發現要便宜得多,因為匯出會默默轉換它所找到的任何內容 — 錯誤的圖層名稱、多餘的節點或偏離網格的座標,都會變成有問題的 SVG 且沒有任何錯誤訊息。
預設情況下是唯讀審計:檢查後,回報一份清晰的通過/失敗清單,並附帶具體、可操作的發現。僅在使用者明確要求您修復某些內容時,才修改 Figma 檔案(請參閱「提供修正」)。
先決條件
-
必須已連線 Figma MCP。此技能透過
get_metadata(結構概覽)、use_figma(透過 Plugin API 進行精確檢查)和get_screenshot(視覺合理性檢查)來驅動它。 -
在每次呼叫
use_figma之前,請載入figma-use技能 — 它記錄了 Plugin API 規則,並防止常見且難以除錯的失敗。 -
檔案金鑰和尺寸頁面是固定的(已針對實際文件驗證)。若要跳過頁面探索的往返,請直接使用其節點 ID 跳轉到某個頁面:
尺寸 頁面節點 ID 16 1320:1738724 42149:86032 42149:86248 42149:86164 42149:863檔案金鑰:
WvUarBj8tO256vML4PkBLl。每個頁面名稱就是尺寸後綴;名為foo_24的圖示位於頁面24上。文件: https://www.figma.com/design/WvUarBj8tO256vML4PkBLl/%F0%9F%9F%A5-koobiq-%C2%B7-icons?node-id=1320-17387- 此檔案金鑰是唯一真相來源 — 它與
config.mjs相符。packages/icons/README.md中的「繪製圖示」連結指向一個不同且錯誤的檔案 — 請忽略它,並信任上述金鑰。 - 未指定
nodeId的get_metadata僅列出第一個頁面 (ICONS,其中不包含任何圖示元件) — 請勿將其用於頁面探索;請改用上述的節點 ID。
- 此檔案金鑰是唯一真相來源 — 它與
如果無法存取檔案
如果 Figma 呼叫失敗 — 驗證/權限錯誤、「節點不存在」、空白/意外的檔案,或 MCP 無回應 — 請停止並向使用者說明,而不是進行猜測。告訴他們什麼失敗了,並要求他們執行以下其中一項:
- 提供不同的/正確的 Figma 連結(
config.mjs中的檔案金鑰可能已過期,或者他們貼上了另一個檔案或分支的連結),或 - 重新檢查儲存庫
.env中的FIGMA_TOKEN(figma-export/ MCP 存取權杖可能遺失、過期或被撤銷),或 - 確認他們的 Figma 帳戶可以存取該檔案(這是一個私人設計檔案 — 他們可能需要被邀請,或使用綁定到有權限帳戶的權杖)。
請勿基於假設憑空捏造審計結果;檢查只有在針對實際檔案時才有意義。
並行讀取與頁面上下文
Figma 讀取呼叫可以並行執行,但僅限於任務中第一次 Figma 編輯之前。在任何編輯開始後,請一次執行一個 Figma 呼叫。
切換頁面的並行讀取可能會互相干擾。在一次觀察到的執行中,針對頁面 24 的讀取回傳了頁面 16 的資料(相同的元件計數、錯誤的名稱),導致審計報告 24px 變體遺失。請保護每次切換頁面的讀取:
// 用於每次切換頁面的讀取。僅傳回屬於 PAGE_ID 的節點資料。
const PAGE_ID = "42149:860"; // 上表中頁面節點 ID
const NAMES = ["foo_24"]; // 要尋找的元件名稱
const page = await figma.getNodeByIdAsync(PAGE_ID);
await figma.setCurrentPageAsync(page);
if (figma.currentPage.id !== PAGE_ID) throw new Error("讀取前頁面上下文不符");
const pageOf = (n) => { let p = n; while (p && p.type !== "PAGE") p = p.parent; return p; };
const hits = figma.currentPage
.findAllWithCriteria({ types: ["COMPONENT", "COMPONENT_SET"] })
.filter((n) => NAMES.includes(n.name) && pageOf(n) && pageOf(n).id === PAGE_ID)
.map((n) => ({ name: n.name, id: n.id, w: n.width, h: n.height }));
if (figma.currentPage.id !== PAGE_ID) throw new Error("讀取期間頁面上下文已變更");
return { pageId: PAGE_ID, hits };
如果保護機制拋出錯誤,請丟棄該結果並重新執行讀取。任何「遺失」的主張(例如「沒有 24px 變體」)必須基於該頁面的一次受保護讀取,或基於節點 ID 的查詢。絕不要從拋出錯誤的讀取或您未檢查的頁面回報遺失。
步驟 1 — 解析目標
使用者可能會提供您以下任一項:
- 一個 Figma 節點 URL 或 ID(例如
node-id=44731-6643)→ 精確檢查該節點,然後也根據名稱查詢其同級尺寸變體以進行跨變體檢查。 - 一個圖示名稱(例如
floating-window或floating-window_16)→ 在圖示頁面中尋找所有尺寸變體並全部檢查。新圖示通常是未發布的,甚至可能尚未命名,因此如果名稱比對失敗,請列出相關頁面上的元件,並要求使用者指向該節點,或按節點 ID 檢查。
頁面名稱等於尺寸後綴。尺寸掃描: 檢查所有五個頁面(16 / 24 / 32 / 48 / 64)中每個請求的名稱,因為圖示可能存在於使用者未提及的尺寸中。使用先決條件表格中的頁面節點 ID,每個頁面一次 use_figma 呼叫,每個呼叫都帶有「並行讀取與頁面上下文」中的保護機制。僅在第一次 Figma 編輯之前並行執行它們。將結果報告為表格(name × size → node id or missing)。
步驟 2 — 檢查
執行此唯讀腳本(改編 targets/頁面 ID)。它會傳回以下規則所需的所有內容。搭配 get_screenshot 使用,以便您可以確認字形實際呈現。
// 貼入 use_figma(skillNames: "figma-use")。設定 PAGE_ID 和 NAMES。
// 頁面節點 ID:16=1320:17387 24=42149:860 32=42149:862 48=42149:861 64=42149:863
const PAGE_ID = "1320:17387"; // 頁面名稱即為尺寸,例如 "16"
const NAMES = ["floating-window_16"]; // 要在該頁面上審計的元件名稱
figma.skipInvisibleInstanceChildren = false; // false:規則 G 也必須看到隱藏圖層
const page = await figma.getNodeByIdAsync(PAGE_ID);
await figma.setCurrentPageAsync(page);
if (figma.currentPage.id !== PAGE_ID) throw new Error("讀取前頁面上下文不符");
const toHex = (c) => {
const h = (n) => Math.round(n * 255).toString(16).padStart(2, "0").toUpperCase();
return "#" + h(c.r) + h(c.g) + h(c.b);
};
const describeFill = (n) => {
if (!n.fills || n.fills === figma.mixed) return n.fills === figma.mixed ? "MIXED" : null;
return n.fills.filter((f) => f.visible !== false).map((f) => ({
type: f.type,
hex: f.type === "SOLID" ? toHex(f.color) : undefined,
opacity: f.opacity,
boundVar: f.boundVariables && f.boundVariables.color ? f.boundVariables.color.id : null,
}));
};
const pageOf = (n) => { let p = n; while (p && p.type !== "PAGE") p = p.parent; return p; };
const all = page.findAllWithCriteria({ types: ["COMPONENT", "COMPONENT_SET"] })
.filter((n) => pageOf(n) && pageOf(n).id === PAGE_ID);
const report = [];
for (const name of NAMES) {
const c = all.find((x) => x.name === name);
if (!c) { report.push({ name, found: false }); continue; }
report.push({
name: c.name, id: c.id, type: c.type,
width: c.width, height: c.height, x: c.x, y: c.y,
layoutMode: c.layoutMode,
clipsContent: c.clipsContent,
description: c.description,
children: c.children.map((ch) => ({
name: ch.name, type: ch.type,
w: ch.width, h: ch.height, x: ch.x, y: ch.y,
constraints: ch.constraints,
fills: describeFill(ch),
styles: { fill: ch.fillStyleId, stroke: ch.strokeStyleId, effect: ch.effectStyleId },
boundVariableFields: ch.boundVariables ? Object.keys(ch.boundVariables) : [],
})),
});
}
if (figma.currentPage.id !== PAGE_ID) throw new Error("讀取期間頁面上下文已變更");
return report;
為可靠驗證填色合約,也以相同方式讀取一個已知良好的同尺寸兄弟圖示(例如 list_16),並比較主要形狀的綁定變數鍵(規則 D)。名義上的合約十六進位值和實際上儲存在檔案中的值不同(請參閱規則 D),因此與真實相鄰圖示進行比較比比對字面十六進位值更可靠。
步驟 3 — 根據規則進行檢查
針對每個檢查項目,回報 ✅ 通過 / ❌ 失敗,並附上觀察到的值以及失敗時應有的值。
A. 節點類型與名稱
-
節點是一個
COMPONENT(或一個尺寸變體的COMPONENT_SET),而不是FRAME、GROUP或INSTANCE。只有元件才能匯出。 -
第一個字元 — 先檢查這個,它會改變名稱的含義:
- 以
.或_開頭 → 一個內部元件,故意不匯出。 匯出的filterComponent(在.figmaexportrc.mjs中)會丟棄任何符合^[._]的名稱。這是設計使然,不是錯誤 — 不要將其審計為損壞的圖示。僅在使用者期望此節點匯出時才提出(那麼修正方法是將其重新命名,不帶前綴的./_)。 - 以
-開頭 → 無效,不允許。 標記為失敗。 - 以數字開頭 → 允許但不鼓勵。 它仍然會匯出(例如
3-columns_32),但名稱最好以英文字母開頭 — 建議使用者重新命名。軟性建議,不是阻擋條件。 - 以英文小寫字母開頭 → 首選情況。
- 以
-
名稱格式 — 可匯出的圖示名稱必須符合此正規表達式:
^[a-z0-9]+(-[a-z0-9]+)*_(16|24|32|48|64)$僅限英文小寫字母和數字;單字以單一連字號連接(沒有
--,沒有前導/尾隨連字號);然後是恰好一個底線;然後是有效的尺寸後綴。無大寫、空格、西里爾字母/其他文字或其他標點符號。- ✅
floating-window_16、api_24、arrow-up-to-bracket_16(以及格式有效但數字開頭不鼓勵的3-columns_32) - ❌
Floating-Window_16(大寫)·плавающее-окно_16(非英文)·floating_window_16/icon__16(兩個底線)·floating--window_16(雙連字號)·floating-window-16(尺寸前用連字號代替底線)·-floating-window_16(前導連字號)·floating-window_20(尺寸不在允許集合中)·floating-window_16(尾隨空格)
- ✅
-
-o後綴 = 輪廓變體。 不含-o的名稱是填充變體。像cloud_24/cloud-o_24或cloud-arrow-up_24/cloud-arrow-up-o_24這樣的一對是一個圖示的兩種樣式。這是有效的,不是衝突或重複;不要因此要求重新命名。 -
數字後綴必須等於元件的實際尺寸(規則 B)。一個
..._16但實際上是 24×24 的是錯誤。
B. 幾何與佈局
- 無自動佈局:
layoutMode === "NONE"。自動佈局會重新排列子元素並損壞匯出。 - 固定且正方形:
width === height === size。尺寸必須是整數像素(像素網格對齊);分數元件尺寸表示圖示不是在網格上繪製的。 - 關閉裁切內容:
clipsContent === false。「裁切內容」切換開關必須關閉。對於圖示的每個尺寸變體,它必須相同(關閉)。 當它開啟時,任何超出框架邊界的圖稿都會在匯出中被靜默裁剪,產生一個被裁切或截斷的圖示。
C. 根結構(這是大多數實際錯誤隱藏的地方)
- 單色圖示:恰好一個根子節點 — 一個名為
shape的VECTOR。 - 雙色圖示:恰好兩個根子節點 —
shape(主要)和shape-2(次要)。沒有其他東西。 - 元件根部無其他節點:沒有雜散框架、群組、矩形、切片、重複向量或隱藏輔助圖層。每個多餘的節點最終都會出現在匯出的 SVG 中。
- 每個形狀的約束在兩個軸上都是
SCALE(horizontal: "SCALE"、vertical: "SCALE"),這樣圖稿會隨著框架縮放。- 嚴重性:警告(衛生),不是匯出阻擋條件。 約束控制元件在 Figma 中調整大小的方式,而非匯出的路徑資料。與其他批准的 Figma 編輯一起修正它們,因為實例和調整大小依賴於它們。
- 理想情況下,每個形狀都是一個單一扁平路徑(請參閱儲存庫 README「繪製圖示」步驟:輪廓、扁平、非零填充規則)。將多個子路徑或未扁平化的群組標記為警告。
D. 填色合約
合約(儲存庫 README「SVG 顏色區域指南」):
- 主要區域
shape→ 黑色#21222C。匯出時移除其fill,以便 SVG 繼承currentColor。 - 次要區域
shape-2→ 紅色#E21D03。匯出時將其重寫為fill="currentColor"。
實際情況:在生產檔案中,主要填色綁定到共享的 Icon/Contrast 變數,該變數解析為 #000819,不透明度為 0.85 — 而不是字面上的 #21222C。因此:
- 參考鍵:
91eb54e0eae961c540c994192c4eed18e4ea1825— 來自🟥 koobiq · components 19(集合鍵c78fab428cf2003c9aa53d86b132a2fa95acb2e2)的Icon/Contrast。 - 比較主要繪畫的綁定變數鍵與該參考值(
(await figma.variables.getVariableByIdAsync(boundId)).key),並檢查繪畫不透明度是否為 0.85。不要比較名稱:Icon/Contrast存在於多個函式庫和集合中。圖示中唯一允許的變數是這個,按鍵比對。具有不同鍵(來自另一個函式庫或集合)的同名變數是一個失敗,而不是警告。修正:重新綁定到來自相同函式庫集合的參考鍵變數。 - 次要
shape-2必須是純色字面#E21D03(匯出將其比對為字面十六進位值 —.figmaexportrc.mjs中的secondaryZoneColorHex),或按鍵綁定到來自🟥 koobiq · components 19的Icon/Error(鍵e736157dcefc2a3e016ec2ddd41a7dc8cf8a5bc6)。對於變數的情況,不檢查繪畫不透明度。任何其他填色都會失敗。 - 主要填色是純色字面顏色但與任何兄弟圖示不符,或次要填色不是
#E21D03,將會以完整的十六進位值匯出,破壞預期的currentColor行為。
E. 基礎版/輪廓版(-o)一致性
如果使用者同時檢查它們,或如果您透過名稱找到它們:
- 對於給定的名稱(例如
cloud),填充版 (cloud_24) 和輪廓版 (cloud-o_24) 必須存在於相同尺寸。 - 僅存在其中一個是常見的(一個圖示可能只有一個變體),但缺少預期的對應項應標記為警告。
F. 描述後設資料
- 元件的
.description欄位應填入 SVG 描述(通常在 Jira 票證或 Figma 原型中指定?)。目前不強制執行,但如果缺少則提供警告。
G. 隱藏圖層
- 檢查
figma.skipInvisibleInstanceChildren = false時可見的隱藏子節點。它們不應該存在。標記為警告。
步驟 4 — 回報
提供清晰的摘要表格,如下所示:
| 規則 | 狀態 | 觀察結果 | 預期 / 建議 |
|---|---|---|---|
| A. 類型 | ✅ | COMPONENT | COMPONENT |
| A. 名稱 | ❌ | Floating-Window_16 | floating-window_16 |
| B. 尺寸 | ✅ | 16×16 | 16×16 |
| C. 結構 | ❌ | 找到 3 個根子節點 | 1(單色)或 2(雙色) |
| D. 填色 | ❌ | 主要:#FF0000(無變數) | 綁定到 Icon/Contrast(鍵 ...) |
然後總結關鍵發現:哪些是阻擋條件,哪些是警告。
提供修正
如果使用者要求您修復一個或多個發現:
- 僅在使用者提出要求時才編輯。 不要假設他們想要修正。
- 解釋您將做什麼並請求繼續的許可。
- 使用一系列的
use_figma呼叫(在編輯之間不要並行)。 - 可安全編輯的內容:
- 重新命名元件(修正 A)
- 調整框架大小(修正 B)
- 刪除額外的根子節點(僅在使用者確認後,修正 C)
- 重新綁定變數(修正 D)
- 填入
.description(修正 F) - 刪除隱藏圖層(僅在使用者確認後,修正 G)
- 編輯後進行檢查:重新執行步驟 2 的檢查腳本並比較結果。
故障排除
- 「找不到名稱」:元件可能尚未發布。要求節點連結或列出相關頁面上的所有元件。
- 未預期的節點類型:使用者可能選擇了群組或框架。僅審計
COMPONENT或COMPONENT_SET。 use_figma錯誤:載入figma-use技能以取得最新 API 文件。- 跨尺寸不一致:如果
list_16在頁面 16 上但list_24不在頁面 24 上,請先檢查list_24是否在頁面 16 上(錯誤的頁面)— 使用受保護的讀取來確認。 - 如果審計運作完美,但使用者仍堅持匯出時 SVG 損壞:在
#icon-supportSlack 頻道中提出問題。