Community程式設計與開發github.com

Koobiq 圖示 Figma 審計

驗證 Koobiq 圖示檔案中的 Figma 圖示元件是否組織正確,以便在匯出前符合圖示庫的結構慣例。

Koobiq 圖示 Figma 審計 是什麼?

這項技能會對 Koobiq Figma 圖示檔案中的圖示元件執行唯讀審計,確保其在匯出前符合庫的結構慣例。它會檢查節點類型、名稱格式、尺寸、佈局、根形狀結構(`shape` / `shape-2`)、縮放約束、填色合約以及描述後設資料,並驗證同一圖示的不同尺寸變體之間是否一致。此技能可搭配任何 Figma MCP 使用,並利用 `use_figma` (Plugin API)、`get_metadata` 和 `get_screenshot` 進行精確檢查。它會產生一份帶有具體、可操作說明的通過/失敗清單,並引導使用者根據經過驗證的庫鍵 (`WvUarBj8tO256vML4PkBLl`) 進行修正,從而防止匯出時產生有問題的 SVG。

相容平台~Claude Code~Codex CLI~Cursor
Part ofMobile
npx skills add https://github.com/rmnturov/koobiq-icons-figma-audit-skill/tree/HEAD

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

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
    161320:17387
    2442149:860
    3242149:862
    4842149:861
    6442149: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. 類型✅COMPONENTCOMPONENT
A. 名稱❌Floating-Window_16floating-window_16
B. 尺寸✅16×1616×16
C. 結構❌找到 3 個根子節點1(單色)或 2(雙色)
D. 填色❌主要:#FF0000(無變數)綁定到 Icon/Contrast(鍵 ...)

然後總結關鍵發現:哪些是阻擋條件,哪些是警告。

提供修正

如果使用者要求您修復一個或多個發現:

  1. 僅在使用者提出要求時才編輯。 不要假設他們想要修正。
  2. 解釋您將做什麼並請求繼續的許可。
  3. 使用一系列的 use_figma 呼叫(在編輯之間不要並行)。
  4. 可安全編輯的內容:
    • 重新命名元件(修正 A)
    • 調整框架大小(修正 B)
    • 刪除額外的根子節點(僅在使用者確認後,修正 C)
    • 重新綁定變數(修正 D)
    • 填入 .description(修正 F)
    • 刪除隱藏圖層(僅在使用者確認後,修正 G)
  5. 編輯後進行檢查:重新執行步驟 2 的檢查腳本並比較結果。

故障排除

  • 「找不到名稱」:元件可能尚未發布。要求節點連結或列出相關頁面上的所有元件。
  • 未預期的節點類型:使用者可能選擇了群組或框架。僅審計 COMPONENT 或 COMPONENT_SET。
  • use_figma 錯誤:載入 figma-use 技能以取得最新 API 文件。
  • 跨尺寸不一致:如果 list_16 在頁面 16 上但 list_24 不在頁面 24 上,請先檢查 list_24 是否在頁面 16 上(錯誤的頁面)— 使用受保護的讀取來確認。
  • 如果審計運作完美,但使用者仍堅持匯出時 SVG 損壞:在 #icon-support Slack 頻道中提出問題。

相關技能