agentleFS
atezeratezer/FMCP/skills/fmcp-screen-recipes/SKILL.md

fmcp-screen-recipes

Skill10 starsChanged 29 days agoMITView on GitHub

Fast path cookbook — standart ekran tipleri (login/payment/profile/list/detail/form/onboarding/dashboard/settings) için 5 mega-adımlı recipe. Max 15 op/execute, cache-first discovery, her adımda Türkçe micro-report.

The file

Files
SKILL.md

FMCP Screen Recipes — Fast Path Cookbook

Kullanım (v3.1+): fmcp-screen-orchestrator Adım 0.5 Fast Path 5/5 match olduğunda çağrılır. Bağımsız giriş noktası değil. Adım 1.6 (Text Style Resolution) canonical kopyası burada — orchestrator buraya referans verir, kod duplicate etmez.

Ne Zaman Kullanılır

Fast Path DEVREYE GİRER (hepsi TRUE olmalı):

  • ✅ Tek ekran üretimi
  • ✅ Standart ekran tipi: 9 recipe'ten biri match ediyor
  • ✅ DS tanımlı: active-ds.md Status: ✅ Aktif
  • ✅ Platform belli
  • ✅ Custom animation / prototype YOK

Devreye GİRMEZ: Multi-screen flow, custom layout, animation, explicit generate-figma-screen talebi, DS GATE geçilmemişse.


5 Mega-Adımlı Akış

Max 15 op/execute. Her mega-adım sonrası tek satır Türkçe micro-report.

M1: Pre-Flight Discovery + Token + Text Style  (1 execute, ~15 op)
M2: Frame + Structure + Modes                   (1 execute, ~12-14 op)
M3: Component Placement (toplu, 3-4/execute)    (2-3 execute, ~12-15 op each)
     ↳ Her M3 execute sonrası: figma_scan_ds_compliance(threshold=85) inline gate
M4: Dark Variant                                (1 execute, ~4 op)
M5: Validate + Final Report                     (ZORUNLU: figma_scan_ds_compliance(detailed) + figma_validate_screen)

Toplam execute: ~6-8 + 2-3 scan call. Hedef süre: ~10 dk. v1.9.4: Scan call'ları hızlıdır (~150ms), execute bütçesini yemezler.

3 MUTLAK KURAL

KURAL 1 — Fill Bind Zorunlu (frame + text dahil):

const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
const bound = figma.variables.setBoundVariableForPaint(paint, 'color', dsColorVar);
node.fills = [bound];

Fill panel'de variable icon 🎨 GÖRÜNMELI. Hardcoded hex YASAK. Frame fill + text fill + primitive dahil TÜM node'lar.

KURAL 2 — Variant Seçim: DEFAULT Koru:

  • setProperties ile SADECE recipe'de explicit belirtilen property'leri set et
  • Diğer TÜM property'leri DEFAULT bırak
  • Product → main (default), Boolean kontroller → recipe'de explicit yoksa DEFAULT

KURAL 3 — Token Bind, Alias Resolve ETME: setBoundVariable(property, importedVariable) ile bind et. Figma runtime alias chain'i otomatik çözer. valuesByMode okuma, alias traversal YASAK (timeout riski).

Mega-Adım Mapping

Aşağıdaki eski adımlar REFERANS amaçlıdır. AYRI AYRI execute ETME — mega-adım tablosunu takip et:

  • M1: Adım 1 (validation) + 1.5 (discovery) + 1.6 (text style) → TEK execute
  • M2: Adım 2 (frame) + 4b (mode) + 5.5 (content body) → TEK execute
  • M3: Adım 6 (discovery) + 7 (placement) → 2-3 execute
  • M4: Adım 8 (dark) → TEK execute
  • M5: Adım 9 (validate) → 1-2 validate call

Adım 1 — Pre-Flight Check

Hiçbir figma_execute çağırma. Doğrula: active-ds.md ✅, screen_type geçerli, platform + device_preset geçerli, variants ≥1.

Micro-report: ✅ Pre-flight: screen_type=<X>, platform=<Y>, device=<Z>, variants=<V>

Adım 1.5 — Unified Pre-Flight Discovery

Cache-First (v3.0+): Önce .claude/design-systems/<active-ds>/tokens.md oku (<active-ds> = active-ds.md'den Library Name'in slug hali — ❖ Ana-DS → ana-ds, Material → material, vb.). Cache varsa ve <7 gün → token discovery ATLA, cache'ten kullan. Yoksa aşağıdaki execute'ları çalıştır, sonra cache'i güncelle.

Token name matching: DS nested path formatı (örn. "Spacing/spacing-100"). endsWith match kullan:

vars.find(v => v.name.endsWith("/" + suffix) || v.name === suffix)

Execute 1 — Collection & Mode Discovery (7 op)

const colls = await figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync();
function findColl(keywords) {
  return colls.find(c => {
    const n = c.name.toLowerCase().trim();
    return keywords.some(kw => n.includes(kw));
  });
}
const sizeColl = findColl(["semantic size", "semantic sizes", "size"]);
const colorsColl = findColl(["semantic color", "theme color"]);
const result = { availableColls: colls.map(c => ({name: c.name, key: c.key})), spacingTokenKeys: {}, collectionInfo: { colors: null, size: null }, surfaceKey: null };

if (sizeColl) {
  const sizeVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(sizeColl.key);
  const suffixes = ["spacing-none","spacing-050","spacing-075","spacing-100","spacing-125","spacing-150","spacing-200"];
  for (const s of suffixes) {
    const f = sizeVars.find(v => v.name.endsWith("/"+s) || v.name === s);
    if (f) result.spacingTokenKeys[s] = f.key;
  }
  if (sizeVars.length > 0) {
    const first = await figma.variables.importVariableByKeyAsync(sizeVars[0].key);
    const coll = await figma.variables.getVariableCollectionByIdAsync(first.variableCollectionId);
    result.collectionInfo.size = { collId: coll.id, modes: coll.modes.map(m => ({name: m.name, modeId: m.modeId})) };
  }
}
if (colorsColl) {
  const colorsVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(colorsColl.key);
  const bgVar = colorsVars.find(v => v.name.toLowerCase().includes("background") && v.name.toLowerCase().includes("level-0"));
  if (bgVar) result.surfaceKey = bgVar.key;
  if (colorsVars.length > 0) {
    const first = await figma.variables.importVariableByKeyAsync(colorsVars[0].key);
    const coll = await figma.variables.getVariableCollectionByIdAsync(first.variableCollectionId);
    result.collectionInfo.colors = { collId: coll.id, modes: coll.modes.map(m => ({name: m.name, modeId: m.modeId})) };
  }
}
return result;

Execute 2 — Critical Token Import (7 op)

const tokenKeyMap = {};
const spacingKeys = { /* Execute 1'den gelen spacingTokenKeys */ };
for (const [suffix, key] of Object.entries(spacingKeys)) {
  try {
    const imported = await figma.variables.importVariableByKeyAsync(key);
    tokenKeyMap[suffix] = imported.id;
  } catch(e) {}
}
return { tokenKeyMap };

Micro-report: ✅ Pre-Flight Discovery: <N> collection, <M> token imported, surface=<found/missing>

Adım 1.6 — Text Style Resolution (v2.0+ — fontFamilies + seed fallback)

Dosyadaki mevcut text style'ları tara, role mapping + font family üret. importStyleByKeyAsync ÇAĞIRMA — direkt setTextStyleIdAsync(roleMap[role].id) kullan.

// Adım A: Sayfa boşsa ilk componentKey ile seed instance düşür (v2.0+ Page 4 senaryosu)
// Caller `seedComponentKey` değişkenini opsiyonel olarak tanımlar (componentKey cache'ten).
// Tanımsızsa sessizce skip edilir — styleMap boş dönebilir, fallback chain devreye girer.
let seedInst = null;
if (figma.currentPage.findAll(n => n.type === "TEXT").length === 0) {
  const _seedKey = (typeof seedComponentKey !== "undefined") ? seedComponentKey : null;
  if (_seedKey) {
    try {
      const seedComp = await figma.importComponentByKeyAsync(_seedKey);
      seedInst = seedComp.createInstance();
      figma.currentPage.appendChild(seedInst);
    } catch(e) { /* fallback: keep going, styleMap may be empty */ }
  }
}

// Adım B: findAll + getStyleByIdAsync
const allTexts = figma.currentPage.findAll(n => n.type === "TEXT");
const uniqueStyleIds = new Set();
for (const t of allTexts) { if (t.textStyleId && typeof t.textStyleId === 'string') uniqueStyleIds.add(t.textStyleId); }

const styleMap = {};
const fontFamilySet = new Set();
for (const id of uniqueStyleIds) {
  try {
    const style = await figma.getStyleByIdAsync(id);
    if (style) {
      styleMap[style.id] = { id: style.id, name: style.name, fontSize: style.fontSize || null, fontName: style.fontName || null };
      if (style.fontName?.family) fontFamilySet.add(style.fontName.family);  // v2.0+
    }
  } catch(e) {}
}

const roleKeywords = {
  display: ["display","hero","amount","title-xl"], title: ["section-title","title","heading"],
  subtitle: ["subtitle","body-semibold","body-bold"], body: ["body-medium","body-regular","body"],
  caption: ["small","caption","footnote"], button: ["button"]
};
function findStyle(kws) { for (const kw of kws) { const m = Object.values(styleMap).find(s => (s.name||"").toLowerCase().includes(kw.toLowerCase())); if (m) return m; } return null; }
const roleMap = {};
for (const [role, kws] of Object.entries(roleKeywords)) { const m = findStyle(kws); if (m) roleMap[role] = { id: m.id, name: m.name, fontSize: m.fontSize }; }
if (!roleMap.display) {
  const sorted = Object.values(styleMap).filter(s => s.fontSize).sort((a,b) => b.fontSize - a.fontSize);
  if (sorted.length > 0) roleMap.display = { id: sorted[0].id, name: sorted[0].name, fontSize: sorted[0].fontSize };
}

// Adım C: Seed temizle (ekran dışına düşürürdü, kaldır)
if (seedInst) { try { seedInst.remove(); } catch(e) {} }

return {
  totalStyles: Object.keys(styleMap).length,
  styleMap,
  roleMap,
  fontFamilies: [...fontFamilySet],  // v2.0+ — loadFontAsync için
};

Micro-report: ✅ Text Style: <N> style, <M> role eşleşti, font: <family>

Kritik: fontFamilies[0] (DS'inizin gerçek font ailesi) sonraki tüm loadFontAsync({ family }) çağrılarında kullanılır. Hardcoded "Inter" YASAK — figma-canvas-ops Rule 8b-pre.


Adım 2 — Wrapper Frame + Background (Edge-to-Edge)

Ana frame: device preset boyutu, auto-layout VERTICAL, padding=0, gap=0, background DS variable'a bağlı.

Edge-to-Edge yapı:

Ana Frame (padding:0, gap:0, VERTICAL)
├── NavigationTopBar (FILL — edge-to-edge)
├── Content Body (FILL both, padding:spacing-100, gap:spacing-075)
│   └── Recipe component'leri
└── BottomNavBar (FILL — edge-to-edge, varsa)
const frame = figma.createFrame();
frame.name = "<Screen Name> — <Device Preset>";
frame.resize(<width>, <height>);
frame.layoutMode = "VERTICAL";
frame.primaryAxisSizingMode = "FIXED";
frame.counterAxisSizingMode = "FIXED";
frame.paddingTop = 0; frame.paddingBottom = 0; frame.paddingLeft = 0; frame.paddingRight = 0;
frame.itemSpacing = 0;
if (surfaceKey) {
  const bgVar = await figma.variables.importVariableByKeyAsync(surfaceKey);
  const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
  frame.fills = [figma.variables.setBoundVariableForPaint(paint, 'color', bgVar)];
}
return { frameId: frame.id };

Micro-report: ✅ Frame: <device> (<w>×<h>), edge-to-edge, background bound

Adım 3 — ⏭️ DEVRE DIŞI

Breakpoint bind frame boyutunu bozuyor (375 vs 402). Device preset boyutu korunur.

Adım 4 — Theme + Size Mode Setup

Collection/mode bilgisi Adım 1.5'ten gelir (ayrı execute yok).

Adım 4b — Mode apply:

const frame = await figma.getNodeByIdAsync(frameId);
if (collectionInfo.colors) {
  const coll = await figma.variables.getVariableCollectionByIdAsync(collectionInfo.colors.collId);
  const lightMode = collectionInfo.colors.modes.find(m => m.name.toLowerCase().includes("light"));
  if (lightMode) frame.setExplicitVariableModeForCollection(coll, lightMode.modeId);
}
if (collectionInfo.size) {
  const coll = await figma.variables.getVariableCollectionByIdAsync(collectionInfo.size.collId);
  const kws = { mobile:["mobil","mobile"], tablet:["tablet"], desktop:["web","desktop"], web:["web","desktop"] };
  const sizeMode = collectionInfo.size.modes.find(m => (kws[platform]||["mobil"]).some(k => m.name.toLowerCase().includes(k)));
  if (sizeMode) frame.setExplicitVariableModeForCollection(coll, sizeMode.modeId);
}
return { modesApplied: true };

Micro-report: ✅ Theme: Light, Size: <modeName>

Adım 5.5 — Content Body Wrapper

const parentFrame = await figma.getNodeByIdAsync(frameId);
const contentBody = figma.createFrame();
contentBody.name = "Content Body";
contentBody.layoutMode = "VERTICAL";
parentFrame.appendChild(contentBody);  // ÖNCE (Rule 11)
contentBody.layoutSizingHorizontal = "FILL";  // SONRA
contentBody.layoutSizingVertical = "FILL";
const paddingVar = await figma.variables.importVariableByKeyAsync(spacing100Key);
const gapVar = await figma.variables.importVariableByKeyAsync(spacing075Key);
contentBody.setBoundVariable("paddingLeft", paddingVar);
contentBody.setBoundVariable("paddingRight", paddingVar);
contentBody.setBoundVariable("paddingTop", paddingVar);
contentBody.setBoundVariable("paddingBottom", paddingVar);
contentBody.setBoundVariable("itemSpacing", gapVar);
contentBody.fills = [];
return { contentBodyId: contentBody.id };

Micro-report: ✅ Content Body: FILL both, padding=spacing-100, gap=spacing-075

Adım 6 — Component Discovery

Cache-First (v3.0+): Önce .claude/design-systems/<active-ds>/components.md oku (DS adı active-ds.md'den). Cache varsa → figma_search_assets ATLA, direkt importComponentByKeyAsync kullan. Yoksa: figma_search_assets(query="<keywords>") + Rule 24 fallback.

Micro-report: ✅ Component keşfi: <N> bulundu, <M> eksik

Adım 7 — Recipe Component Placement

3-4 component TEK execute'ta. Parent routing: edge-to-edge (NavigationTopBar, BottomNavBar) → Ana Frame, diğer her şey → Content Body.

const edgeNames = /^(navigation|nav|top|bottom|status|tabbar|tab_bar)/i;
const parentId = edgeNames.test(spec.name) ? frameId : contentBodyId;

Text Style Binding: await textNode.setTextStyleIdAsync(roleMap[role].id) — DİREKT bağla, importStyleByKeyAsync ÇAĞIRMA, fontSize set ETME.

Text rolü mapping: display→Amount/Hero, title→Section Header, subtitle→Card Title, body→Body text, caption→Small/Info.

Adım 8 — Dark Variant

const lightFrame = await figma.getNodeByIdAsync(lightFrameId);
const darkFrame = lightFrame.clone();
figma.currentPage.appendChild(darkFrame);
darkFrame.x = lightFrame.x + lightFrame.width + 80;
darkFrame.name = lightFrame.name + " — Dark";
const coll = await figma.variables.getVariableCollectionByIdAsync(semColorsCollId);
darkFrame.setExplicitVariableModeForCollection(coll, darkModeId);
return { darkFrameId: darkFrame.id };

Adım 9 — Validation + Final Report (v1.9.4 Güçlendirildi)

ZORUNLU gate — atlama YASAK. Her ekran için iki aşamalı doğrulama:

9a. Inline scan (M3/M4 sonrası, isteğe bağlı retry):

figma_scan_ds_compliance(nodeId=frameId, threshold=85)
  • passed: false veya coverage.paddings.pct < 90 veya coverage.radius.pct < 90 veya coverage.textStyle.pct < 90 → M3/M4 retry zorunlu.
  • samples.hardcodedHex döndüyse → o node'lardaki fill'ler için setBoundVariableForPaint çağrısı eksik, derhal düzelt.
  • samples.hardcodedFontSize döndüyse → o text'ler için setTextStyleIdAsync(roleMap[role].id) çağrısı eksik.
  • samples.primitiveFrames döndüyse → o frame'ler için Ana-DS'de component var mı ara (figma_search_assets); varsa instance'a dönüştür.
  • overflow.overflowPx > 0 ve clipsContent: true → içerik kesiliyor, kullanıcıya "scroll mu, body genişletme mi?" sor.

9b. Final validate (her frame için, ZORUNLU):

figma_validate_screen(frameId, minScore=80)
  • Skor <80 → 3 retry; 3× fail → kullanıcıya generate-figma-screen skill'ine fallback öner.

Son Rapor (ZORUNLU alanlar):

  • Screen type, DS, device, variants
  • Frame ID'leri (light/dark)
  • Score: validate + scan skorları birlikte (örn. "validate: 87, scan: 89")
  • Coverage yüzdeleri: fills/paddings/radius/itemSpacing/textStyle/textColor (scan response'tan)
  • Kullanılan library instance listesi + primitive fallback listesi (varsa)
  • Token binding sayıları
  • Overflow durumu (body kesiliyor mu?)
  • Toplam execute sayısı, süre
  • Hardcoded hex/fontSize sample'ları (varsa — kullanıcıya gösterilir, "bilinçli skip" değil "acil düzeltilecek")

Device Presets Lookup Table

Mobile

DeviceWHKeywords
iPhone 17402874mobile, iphone, ios
iPhone 16 & 17 Pro402874iphone pro
iPhone 16393852iphone 16
iPhone 16 Pro Max440956pro max
iPhone 16 Plus430932iphone plus
iPhone Air420912iphone air
iPhone 13 & 14390844iphone 13, iphone 14
Android Compact412917android
Android Medium700840android tablet

Default: mobile → iPhone 17, android → Android Compact.

Tablet

DeviceWH
iPad Pro 11"8341194
iPad Pro 12.9"10241366

Default: tablet → iPad Pro 11".

Desktop / Web

DeviceWH
Desktop1440900
Desktop HD19201080
MacBook Pro 14"1512982
MacBook Pro 16"17281117

Default: desktop/web → Desktop 1440×900.


9 Screen Type Recipes

Her recipe = component listesi + yerleşim sırası + search keyword'leri + setProperties.

Recipe 1: Login

Trigger: login, giriş, oturum aç, sign in

  1. AppBar — ["navigation top", "appbar"] setProperties: { "Subtitle": false, "Right Controls": false }
  2. Logo — ["logo", "brand"]
  3. Welcome Text (H1) — ["heading", "display"]
  4. Subtitle Text — ["body medium", "text body"]
  5. Email Input — ["text field", "input", "email"]
  6. Password Input — ["password", "text field password"]
  7. Primary Button ("Giriş Yap") — ["button primary"] setProperties: { "Value": "Giriş Yap" }
  8. Forgot Password Link — ["link", "text button"]
  9. Divider — ["divider"]
  10. Register Link — ["text button", "link"]

Recipe 2: Payment

Trigger: ödeme, payment, checkout, satın al

  1. NavigationTopBar — ["navigation top", "appbar"] setProperties: { "Title Text": "Ödeme", "Right Controls": false, "Subtitle": false, "Product": "main" }
  2. Amount Display — ["display large", "hero text"]
  3. Currency Label — ["body small", "caption"]
  4. Section Header — ["section header", "subtitle"]
  5. Payment Method Cards (×3) — ["card payment", "list item"]
  6. Add New Method Button — ["button secondary"]
  7. Divider — ["divider"]
  8. CTA Button — ["button primary large"] setProperties: { "Value": "Ödemeyi Tamamla" }
  9. Security Info — ["text small", "caption"]

🔀 Content Override (reference_benchmark varsa — v1.9.9+ G20):

reference_benchmark input dolu ise (görsel upload veya Figma node URL):

  1. inspiration-intake skill'i çağır. structural_intent JSON üret:
    {
      "sections": [
        {"role": "amount", "text_hints": ["2.124,90 TL"]},
        {"role": "segmented_tabs", "text_hints": ["Tek Çekim", "3 Taksit"]},
        {"role": "method_card", "count": 2, "text_hints": [...]}
      ]
    }
  2. Recipe 2 bileşen listesine override uygula (sadece text + count):
    • Amount Display text → sections[amount].text_hints[0]
    • Payment Method Cards count → sections[method_card].count (varsayılan 3, referansta 2 olabilir)
    • Method Card text slot → text_hints map (örn. "Tek Çekim" / "3 Taksit")
    • Security Info → referansta varsa extract, yoksa default

⚠️ Override sadece TEXT + COUNT. Color/spacing/radius/font hep DS tokens'a bound kalır (K5 Token-Only Enforcement). Referansın hex değerleri, px piksel ölçüleri KULLANILMAZ.

Recipe 3: Profile

Trigger: profil, profile, hesap, account

  1. AppBar — ["navigation top"] setProperties: { "Title Text": "Profilim" }
  2. Avatar — ["avatar large", "profile picture"]
  3. User Name Text — ["heading", "display medium"]
  4. User Email Text — ["body", "text secondary"]
  5. Divider — ["divider"]
  6. Menu List Items (×4) — ["list item", "menu row"]
  7. Destructive Button ("Çıkış Yap") — ["button destructive"] setProperties: { "Value": "Çıkış Yap" }

Recipe 4: List

Trigger: liste, list, arama, search, katalog

  1. AppBar — ["navigation top"] setProperties: { "Title Text": "Arama" }
  2. Search Bar — ["search", "search bar"]
  3. Filter Chips Row — ["chip", "filter chip"]
  4. Card List Items (×N) — ["card", "list card"]
  5. Pagination — ["pagination"]
  6. FAB (opsiyonel) — ["fab", "floating button"]

Recipe 5: Detail

Trigger: detay, detail, ürün detay, product

  1. AppBar — ["navigation top"] setProperties: { "Right Controls": true }
  2. Hero Image — ["image large", "hero"]
  3. Title Text — ["heading large", "display"]
  4. Price + Rating Row — ["price", "rating"]
  5. Description Text — ["body", "text body"]
  6. Stats Section — ["stat", "info row"]
  7. CTA Button — ["button primary"] setProperties: { "Value": "Sepete Ekle" }

Recipe 6: Form

Trigger: form, başvuru, kayıt, application

  1. AppBar — ["navigation top"] setProperties: { "Title Text": "Başvuru" }
  2. Progress Indicator — ["stepper", "progress"]
  3. Field Group 1 (3-4 input) — ["text field", "input"]
  4. Field Group 2 — ["select", "dropdown", "date picker"]
  5. Checkbox / Terms — ["checkbox", "agreement"]
  6. Submit Button — ["button primary"] setProperties: { "Value": "Gönder" }

Recipe 7: Onboarding

Trigger: onboarding, tanıtım, karşılama, welcome

  1. Hero Image — ["illustration", "image hero"]
  2. Title Text — ["display", "heading large"]
  3. Subtitle Text — ["body", "text secondary"]
  4. Pagination Dots — ["dots", "page indicator"]
  5. Primary Button — ["button primary"] setProperties: { "Value": "Başla" }
  6. Skip Link — ["text button", "link"]

Recipe 8: Dashboard

Trigger: dashboard, özet, summary, panel

  1. AppBar — ["navigation top"] setProperties: { "Title Text": "Özet" }
  2. Stats Cards (×4) — ["stat card", "metric card"]
  3. Chart — ["chart", "graph"]
  4. Section Header — ["section header"]
  5. Activity List Items (×N) — ["list item", "activity row"]

Recipe 9: Settings

Trigger: ayarlar, settings, tercihler

  1. AppBar — ["navigation top"] setProperties: { "Title Text": "Ayarlar" }
  2. Section Headers — ["section header"]
  3. Toggle Rows — ["list item toggle", "setting row"]
  4. Info Row — ["list item", "nav row"]
  5. Destructive Button — ["button destructive"] setProperties: { "Value": "Hesabı Sil" }

Primitive Fallback Pattern

Component bulunamazsa token-bound primitive ile inşa et. Tüm visual properties variable'a bağlı olmalı:

const card = figma.createFrame();
card.name = "... (primitive fallback)";
card.layoutMode = "HORIZONTAL";
card.setBoundVariable("cornerRadius", radiusSmVar);
card.setBoundVariable("paddingLeft", spacingMdVar);
// ... diğer padding/gap bind'ları
const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
card.fills = [figma.variables.setBoundVariableForPaint(paint, 'color', surfaceVar)];
parent.appendChild(card);  // ÖNCE
card.layoutSizingHorizontal = "FILL";  // SONRA (Rule 11)

Chunking Kuralları

  • Max 15 atomic op / figma_execute
  • 3-4 component TEK execute'ta yerleştir
  • Timeout: 15000 ms yeterli (15 op ≈ 200-300ms)
  • Her component için AYRI execute YASAK

Error Recovery

HataAksiyon
DS GATE yokFast Path iptal, orchestrator'a dön
Device bulunamadıDefault kullan (iPhone 17 / Desktop 1440)
Colors collection yokLight-only, dark skip
Component 0 sonuçPrimitive fallback
Execute timeoutOp sayısını azalt, böl
Style import failRule 23 try-catch, roleMap fallback
3× validate failKullanıcıya generate-figma-screen öner

Rule 24 — Component Fallback Chain (v1.9.9+ / v3.1+)

figma_search_assets library components boş dönerse (variables var ama component instance yok) sırayla şu yolu takip et:

24.0 — Live Enumerate (v3.3+ — İLK SIRA):

  • DS Library Registry'de libraryFileKey kayıtlıysa:
    • figma_enumerate_published_components(libraryFileKey, filter?) → componentKey listesi (REST; dosya açıksa figma_enumerate_library_components tercih et)
    • figma_get_library_variables(query?) → variableKey listesi (teamLibrary — hedef dosyada çalışır)
  • Başarılıysa 24.1-24.4 atlanır → direkt importComponentByKeyAsync(key) / importComponentSetByKeyAsync(key).
  • Registry boş veya REST token yoksa 24.1'e düş.

24.1 — REST API enumerate:

  • Response'ta _restFallbackHint varsa oku.
  • FIGMA_REST_TOKEN env var set ise: figma_rest_api(endpoint="/v1/files/<LIBRARY_FILE_KEY>/components") çağır.
  • Dönen componentKey'leri importComponentByKeyAsync ile kullan.

24.2 — Cache-populated keys (user-local):

  • ~/.claude/data/fcm-ds/<file-key>/components.md oku.
  • Yaygın rol → componentKey mapping varsa direkt importComponentByKeyAsync.
  • Cache yok/stale ise Step 3.1 Cache Populate (bkz. generate-figma-screen) otomatik tetikler.

24.3 — Token-bound primitive fallback (Rule 3 — MEŞRU):

  • Yukarıdakiler fail ise: createFrame + her fill/padding/radius/textStyle için setBoundVariable / setBoundVariableForPaint / setTextStyleIdAsync.
  • Sayma kuralı: totalBindings >= createFrameCount → code-warnings.ts ADVISORY döner (BLOCKING değil).
  • Hardcoded renk/spacing/font YASAK — sadece bound primitives.

24.4 — Full stop:

  • 24.1-24.3 hepsi fail ise kullanıcıya: "Library subscribe yok + REST token yok + cache boş. Manuel component sürüklemen gerek."

⚠️ figma_search_assets boş yanıt ≠ fallback yasağı. Fallback chain Rule 3 (fmcp-screen-orchestrator) ile uyumlu ve server-level enforcement tarafından ADVISORY olarak kabul edilir.


Known Limitations

  1. Ana-DS dışında test edilmedi — başka DS için isim farkı olabilir
  2. Dark mode validate ayrı ölçülmez — manuel göz kontrolü gerekli
  3. 9 recipe sabit — yeni tip → generate-figma-screen
  4. Multi-screen ve prototype/interactions YOK
  5. Component search false match riski — setProperties ile doğrula

Skill References

  • skills/fmcp-screen-orchestrator/SKILL.md — Fast Path tetiklenme
  • skills/figma-canvas-ops/SKILL.md — Rule 5a CHUNKING + Rule 22 Async + Rule 23 Style zorunlu
  • .claude/design-systems/active-ds.md — DS GATE state
  • .claude/design-systems/<active-ds>/tokens.md — Token cache (DS adı active-ds.md'den dinamik)
  • .claude/design-systems/<active-ds>/components.md — Component cache

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.