@atezer/figma-mcp-bridge 1.7.30 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +408 -0
  2. package/README.md +8 -8
  3. package/agents/_orchestrator-protocol.md +185 -0
  4. package/agents/ds-auditor.md +73 -22
  5. package/agents/screen-builder.md +60 -22
  6. package/agents/token-syncer.md +63 -19
  7. package/dist/core/code-warnings.d.ts +38 -0
  8. package/dist/core/code-warnings.d.ts.map +1 -0
  9. package/dist/core/code-warnings.js +191 -0
  10. package/dist/core/code-warnings.js.map +1 -0
  11. package/dist/core/device-presets.d.ts +49 -0
  12. package/dist/core/device-presets.d.ts.map +1 -0
  13. package/dist/core/device-presets.js +141 -0
  14. package/dist/core/device-presets.js.map +1 -0
  15. package/dist/core/instructions.d.ts +4 -2
  16. package/dist/core/instructions.d.ts.map +1 -1
  17. package/dist/core/instructions.js +239 -29
  18. package/dist/core/instructions.js.map +1 -1
  19. package/dist/core/plugin-bridge-connector.d.ts +26 -0
  20. package/dist/core/plugin-bridge-connector.d.ts.map +1 -1
  21. package/dist/core/plugin-bridge-connector.js +18 -2
  22. package/dist/core/plugin-bridge-connector.js.map +1 -1
  23. package/dist/core/plugin-bridge-server.d.ts +16 -0
  24. package/dist/core/plugin-bridge-server.d.ts.map +1 -1
  25. package/dist/core/plugin-bridge-server.js +83 -1
  26. package/dist/core/plugin-bridge-server.js.map +1 -1
  27. package/dist/core/response-guard.d.ts +23 -0
  28. package/dist/core/response-guard.d.ts.map +1 -1
  29. package/dist/core/response-guard.js +113 -0
  30. package/dist/core/response-guard.js.map +1 -1
  31. package/dist/core/version.d.ts +1 -1
  32. package/dist/core/version.d.ts.map +1 -1
  33. package/dist/core/version.js +1 -1
  34. package/dist/core/version.js.map +1 -1
  35. package/dist/local-plugin-only.d.ts.map +1 -1
  36. package/dist/local-plugin-only.js +334 -101
  37. package/dist/local-plugin-only.js.map +1 -1
  38. package/f-mcp-plugin/code.js +514 -29
  39. package/f-mcp-plugin/ui.html +90 -14
  40. package/package.json +1 -1
  41. package/skills/SKILL_INDEX.md +13 -1
  42. package/skills/apply-figma-design-system/SKILL.md +37 -0
  43. package/skills/audit-figma-design-system/SKILL.md +38 -0
  44. package/skills/code-design-mapper/SKILL.md +37 -0
  45. package/skills/design-token-pipeline/SKILL.md +44 -0
  46. package/skills/figma-canvas-ops/SKILL.md +200 -243
  47. package/skills/fmcp-ds-audit-orchestrator/SKILL.md +205 -0
  48. package/skills/fmcp-intent-router/SKILL.md +574 -0
  49. package/skills/fmcp-screen-orchestrator/SKILL.md +166 -0
  50. package/skills/fmcp-screen-recipes/SKILL.md +528 -0
  51. package/skills/fmcp-token-sync-orchestrator/SKILL.md +198 -0
  52. package/skills/generate-figma-library/SKILL.md +38 -0
  53. package/skills/generate-figma-screen/SKILL.md +360 -6
  54. package/skills/implement-design/SKILL.md +32 -0
  55. package/skills/inspiration-intake/SKILL.md +220 -0
  56. package/skills/visual-qa-compare/SKILL.md +33 -0
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: fmcp-screen-orchestrator
3
+ description: DS-compliant Figma ekran üretimi için platform-agnostic orkestratör skill. DS GATE, Fast Path routing, intake mode, error recovery.
4
+ metadata:
5
+ mcp-server: user-figma-mcp-bridge
6
+ version: 3.0.0
7
+ priority: 95
8
+ phase: orchestrator
9
+ personas:
10
+ - designer
11
+ - uidev
12
+ token_budget: condensed-first
13
+ required_inputs:
14
+ - name: intake_mode
15
+ type: "enum: text_only | figma_benchmark | image_uploaded | image_url | no_idea"
16
+ - name: task_description
17
+ type: string
18
+ - name: source_ref
19
+ type: "string | null"
20
+ ---
21
+
22
+ # FMCP Screen Orchestrator
23
+
24
+ ## Essentials
25
+
26
+ ### Ortak Protokol
27
+
28
+ 1. **Skill Registry** açık — tahmin yasak, sezgisel Read() yasak
29
+ 2. **Intent Routing** — belirsiz → Read fmcp-intent-router
30
+ 3. **Cheap-First** — `depth=1`, `verbosity="summary"`, screenshot sadece onay kapısında, ≤5 execute
31
+ 4. **Cache-First** — `.claude/design-systems/<lib>/` cache'i API'den ÖNCE oku
32
+ 5. **Onay Kapıları** — approach / destructive / evolution / 3. audit fail
33
+ 6. **Self-Audit** — `figma_validate_screen(nodeId, minScore=80)` ZORUNLU
34
+ 7. **Skill Evolution** — iki aşamalı onay + `# DRAFT — PENDING APPROVAL` banner
35
+ 8. **Türkçe Raporlama** — metrik bloğu zorunlu
36
+
37
+ ### Skill Registry
38
+
39
+ | Skill | Trigger | Common case? |
40
+ |---|---|---|
41
+ | `fmcp-intent-router` | Belirsiz intent | Sadece belirsizlikte |
42
+ | `inspiration-intake` | image/figma_benchmark | Sadece bu modlarda |
43
+ | `generate-figma-screen` | Net screen creation | HER ZAMAN (ana motor) |
44
+ | `figma-canvas-ops` | Her figma_execute öncesi | HER ZAMAN (pre-flight) |
45
+ | `fmcp-screen-recipes` | Fast Path match | Sadece Fast Path'te |
46
+ | `apply-figma-design-system` | Mevcut ekranı DS'ye hizala | Sadece remediation |
47
+
48
+ ### Adım 0 — DS GATE (MUTLAK İLK KAPI)
49
+
50
+ ```
51
+ 1. Read(".claude/design-systems/active-ds.md")
52
+ 2. ✅ Aktif → Library Name not al, devam
53
+ ❌ Seçilmedi veya dosya yok → DUR, kullanıcıya "Hangi DS?" sor, hiçbir figma_* çağırma
54
+ ```
55
+
56
+ DS belirsizken `figma_get_file_data`, `figma_search_assets` çağırmak YASAK. İstisnalar: `figma_get_status()`, filesystem read, Claude vision.
57
+
58
+ ### Adım 0.5 — Fast Path Check
59
+
60
+ 5 koşulun HEPSİ TRUE → `fmcp-screen-recipes` Read, recipe uygula:
61
+ 1. Tek ekran (multi-screen değil)
62
+ 2. Standart tip (login/payment/profile/list/detail/form/onboarding/dashboard/settings)
63
+ 3. DS aktif
64
+ 4. Platform belli (explicit keyword VEYA önceki intent'ten saved). Yoksa DUR, kullanıcıya sor. Default varsayım YASAK.
65
+ 5. Animation/prototype YOK
66
+
67
+ Biri bile FALSE → Karar Akışı'na geç. Recipe kırılırsa da fallback.
68
+
69
+ ### Karar Akışı
70
+
71
+ ```
72
+ 1. Intent net mi? EVET → sub-skill / HAYIR → Read fmcp-intent-router
73
+ 2. image/figma_benchmark → Read inspiration-intake → structural_intent JSON
74
+ 3. figma_execute yazacak mı? EVET → Read figma-canvas-ops
75
+ 4. Ana motor → Read generate-figma-screen
76
+ 5. build-from-scratch vs clone-to-device
77
+ ```
78
+
79
+ ### DS Fallback Chain
80
+
81
+ 1. **DS component instance** — en çok tercih
82
+ 2. **DS primitive variant** — yakın variant + setProperties
83
+ 3. **Token-bound primitive** — createFrame + tüm fill/padding/radius variable'a bağlı → meşru, ihlal DEĞİL
84
+ 4. **Hardcoded shape** → GERÇEK İHLAL, DUR, kullanıcıya bildir
85
+
86
+ ### Resmi Figma MCP Yasağı
87
+
88
+ `Figma:*` prefix'li tool'lar (mcp.figma.com) ASLA çağrılmaz. Sadece `figma_*` (figma-mcp-bridge) kullan. `figma_search_assets` boş → Rule 24 fallback (manuel instance scan), `Figma:search_design_system`'e düşME.
89
+
90
+ ### Filesystem MCP
91
+
92
+ Skill Read için: `mcp__fmcp-filesystem__read_text_file(path="<absolute_path>/skills/<skill>/SKILL.md")`. Claude Desktop'ta prompt'ta absolute path belirtilmeli.
93
+
94
+ ### Self-Audit Gate + Verification
95
+
96
+ ```
97
+ figma_validate_screen(nodeId=<wrapper_id>, minScore=80)
98
+ ```
99
+ - `<80` → SEVERE violation'ları oku, düzelt, yeniden validate
100
+ - 3 deneme fail → kullanıcıdan rebuild onayı al
101
+
102
+ Teslim öncesi kontrol: validate ≥80, ham shape yok, tüm değerler token'a bağlı, auto-layout eksiksiz, Türkçe rapor + metrikler.
103
+
104
+ ### Rapor Formatı
105
+
106
+ ```markdown
107
+ ## 🎨 Ekran Üretimi — <ekran_adı>
108
+ **Mod:** <intake_mode> | **DS:** <active-ds> | **Yaklaşım:** <approach>
109
+ ### Sonuç
110
+ <Figma node link>
111
+ 📊 Metrikler: skill'ler, API çağrı, cache hit/miss, execute sayısı, validate score
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Advanced — Only Load If Needed
117
+
118
+ Bu bölüm: karmaşık intake, 3. audit fail, skill evolution, non-obvious error durumlarında okunur.
119
+
120
+ ### Inspiration Handoff Contract
121
+
122
+ `inspiration-intake` → `structural_intent_json` üretir (layout_direction, sections, hierarchy_notes, spacing_intent). `generate-figma-screen` ise `reference_benchmark` olarak nodeId bekler. Handoff prompt-level'da yapılır:
123
+ 1. structural_intent_json'u orchestrator context'te tut
124
+ 2. generate-figma-screen'e: `reference_benchmark: "none"`, structural hints'i prompt context olarak ekle
125
+ 3. Hints'ten sadece layout/hiyerarşi al, DEĞER ASLA (renk/font/spacing DS'den gelir)
126
+
127
+ ### Intake Mode Router
128
+
129
+ ```
130
+ text_only → (belirsizse intent-router) → generate-figma-screen
131
+ figma_benchmark → inspiration-intake → generate-figma-screen (build-from-scratch)
132
+ image_uploaded → inspiration-intake (vision) → generate-figma-screen
133
+ image_url → inspiration-intake → WebFetch dener → başarısızsa kullanıcıdan upload iste
134
+ no_idea → Kullanıcıya sor: ekran türü, kitle, içerik blokları, estetik → generate-figma-screen
135
+ ```
136
+
137
+ ### Inspiration Only Rule
138
+
139
+ Benchmark/görselden DEĞER alma YASAK. Sadece NİYET: layout yönü, hiyerarşi, bölüm sırası, spacing intent. Tüm değerler DS'ten.
140
+
141
+ ### Step-by-Step Mode (≤5 execute)
142
+
143
+ 1. **Skeleton** — wrapper + section frame'leri → screenshot → onay
144
+ 2. **Content** — DS instance yerleşimi → screenshot → onay
145
+ 3. **Polish** — spacing, states, edge cases → son screenshot → audit
146
+
147
+ ### Error Recovery
148
+
149
+ | Hata | Aksiyon |
150
+ |---|---|
151
+ | Plugin kopması | `figma_get_status()` kontrol, gelmezse bildir |
152
+ | Tool timeout | Scope daralt, 1 retry, sonra raporla |
153
+ | SEVERE violation | Oku, düzelt, audit döngüsüne sok |
154
+ | Truncated response | Küçük scope ile yeniden iste |
155
+ | 3. validate fail | Kullanıcıya raporla, rebuild öner |
156
+
157
+ ### Skill Evolution
158
+
159
+ 1. Gap onayı: kullanıcıya "yeni skill mi, mevcut edit mi?" sor
160
+ 2. İçerik onayı: yeni skill `# DRAFT — PENDING APPROVAL` banner ile, edit ise unified diff göster, onay → uygula
161
+
162
+ ### Platform Notes
163
+
164
+ - **Claude Code:** Sub-agent isolation, cache: `.claude/design-systems/`
165
+ - **Cursor:** `.cursor/rules/` referans, sub-agent yok, `.cursor/mcp.json` gerekli
166
+ - **Claude Desktop:** Project knowledge'a yükle, ilk prompt'ta explicit referans ver
@@ -0,0 +1,528 @@
1
+ ---
2
+ name: fmcp-screen-recipes
3
+ description: 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.
4
+ metadata:
5
+ mcp-server: user-figma-mcp-bridge
6
+ version: 3.0.0
7
+ priority: 96
8
+ phase: fast-path
9
+ personas:
10
+ - designer
11
+ - uidev
12
+ token_budget: condensed-first
13
+ required_inputs:
14
+ - name: screen_type
15
+ type: "enum: login | payment | profile | list | detail | form | onboarding | dashboard | settings"
16
+ - name: platform
17
+ type: "enum: mobile | tablet | desktop | web"
18
+ - name: device_preset
19
+ type: string
20
+ - name: variants
21
+ type: "array: [light] | [light, dark]"
22
+ - name: active_ds
23
+ type: string
24
+ outputs:
25
+ - name: light_frame_id
26
+ type: string
27
+ - name: dark_frame_id
28
+ type: "string | null"
29
+ - name: validate_scores
30
+ type: "{ light: number, dark: number | null }"
31
+ ---
32
+
33
+ # FMCP Screen Recipes — Fast Path Cookbook
34
+
35
+ ## Ne Zaman Kullanılır
36
+
37
+ **Fast Path DEVREYE GİRER** (hepsi TRUE olmalı):
38
+ - ✅ Tek ekran üretimi
39
+ - ✅ Standart ekran tipi: 9 recipe'ten biri match ediyor
40
+ - ✅ DS tanımlı: `active-ds.md` Status: ✅ Aktif
41
+ - ✅ Platform belli
42
+ - ✅ Custom animation / prototype YOK
43
+
44
+ **Devreye GİRMEZ:** Multi-screen flow, custom layout, animation, explicit generate-figma-screen talebi, DS GATE geçilmemişse.
45
+
46
+ ---
47
+
48
+ ## 5 Mega-Adımlı Akış
49
+
50
+ Max **15 op/execute**. Her mega-adım sonrası tek satır Türkçe micro-report.
51
+
52
+ ```
53
+ M1: Pre-Flight Discovery + Token + Text Style (1 execute, ~15 op)
54
+ M2: Frame + Structure + Modes (1 execute, ~12-14 op)
55
+ M3: Component Placement (toplu, 3-4/execute) (2-3 execute, ~12-15 op each)
56
+ M4: Dark Variant (1 execute, ~4 op)
57
+ M5: Validate + Final Report (1-2 validate çağrısı)
58
+ ```
59
+
60
+ **Toplam execute:** ~6-8. **Hedef süre:** ~10 dk.
61
+
62
+ ### 3 MUTLAK KURAL
63
+
64
+ **KURAL 1 — Fill Bind Zorunlu (frame + text dahil):**
65
+ ```js
66
+ const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
67
+ const bound = figma.variables.setBoundVariableForPaint(paint, 'color', dsColorVar);
68
+ node.fills = [bound];
69
+ ```
70
+ Fill panel'de variable icon 🎨 GÖRÜNMELI. Hardcoded hex YASAK. Frame fill + text fill + primitive dahil TÜM node'lar.
71
+
72
+ **KURAL 2 — Variant Seçim: DEFAULT Koru:**
73
+ - `setProperties` ile SADECE recipe'de explicit belirtilen property'leri set et
74
+ - Diğer TÜM property'leri DEFAULT bırak
75
+ - `Product` → main (default), Boolean kontroller → recipe'de explicit yoksa DEFAULT
76
+
77
+ **KURAL 3 — Token Bind, Alias Resolve ETME:**
78
+ `setBoundVariable(property, importedVariable)` ile bind et. Figma runtime alias chain'i otomatik çözer. `valuesByMode` okuma, alias traversal YASAK (timeout riski).
79
+
80
+ ### Mega-Adım Mapping
81
+
82
+ Aşağıdaki eski adımlar REFERANS amaçlıdır. AYRI AYRI execute ETME — mega-adım tablosunu takip et:
83
+ - **M1:** Adım 1 (validation) + 1.5 (discovery) + 1.6 (text style) → TEK execute
84
+ - **M2:** Adım 2 (frame) + 4b (mode) + 5.5 (content body) → TEK execute
85
+ - **M3:** Adım 6 (discovery) + 7 (placement) → 2-3 execute
86
+ - **M4:** Adım 8 (dark) → TEK execute
87
+ - **M5:** Adım 9 (validate) → 1-2 validate call
88
+
89
+ ---
90
+
91
+ ### Adım 1 — Pre-Flight Check
92
+
93
+ Hiçbir figma_execute çağırma. Doğrula: active-ds.md ✅, screen_type geçerli, platform + device_preset geçerli, variants ≥1.
94
+
95
+ **Micro-report:** `✅ Pre-flight: screen_type=<X>, platform=<Y>, device=<Z>, variants=<V>`
96
+
97
+ ### Adım 1.5 — Unified Pre-Flight Discovery
98
+
99
+ **Cache-First (v3.0+):** Önce `.claude/design-systems/sui/tokens.md` oku. 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.
100
+
101
+ Token name matching: SUI nested path formatı (`"Spacing/spacing-100"`). `endsWith` match kullan:
102
+ ```js
103
+ vars.find(v => v.name.endsWith("/" + suffix) || v.name === suffix)
104
+ ```
105
+
106
+ #### Execute 1 — Collection & Mode Discovery (7 op)
107
+
108
+ ```js
109
+ const colls = await figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync();
110
+ function findColl(keywords) {
111
+ return colls.find(c => {
112
+ const n = c.name.toLowerCase().trim();
113
+ return keywords.some(kw => n.includes(kw));
114
+ });
115
+ }
116
+ const sizeColl = findColl(["semantic size", "semantic sizes", "size"]);
117
+ const colorsColl = findColl(["semantic color", "s theme"]);
118
+ const result = { availableColls: colls.map(c => ({name: c.name, key: c.key})), spacingTokenKeys: {}, collectionInfo: { colors: null, size: null }, surfaceKey: null };
119
+
120
+ if (sizeColl) {
121
+ const sizeVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(sizeColl.key);
122
+ const suffixes = ["spacing-none","spacing-050","spacing-075","spacing-100","spacing-125","spacing-150","spacing-200"];
123
+ for (const s of suffixes) {
124
+ const f = sizeVars.find(v => v.name.endsWith("/"+s) || v.name === s);
125
+ if (f) result.spacingTokenKeys[s] = f.key;
126
+ }
127
+ if (sizeVars.length > 0) {
128
+ const first = await figma.variables.importVariableByKeyAsync(sizeVars[0].key);
129
+ const coll = await figma.variables.getVariableCollectionByIdAsync(first.variableCollectionId);
130
+ result.collectionInfo.size = { collId: coll.id, modes: coll.modes.map(m => ({name: m.name, modeId: m.modeId})) };
131
+ }
132
+ }
133
+ if (colorsColl) {
134
+ const colorsVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(colorsColl.key);
135
+ const bgVar = colorsVars.find(v => v.name.toLowerCase().includes("background") && v.name.toLowerCase().includes("level-0"));
136
+ if (bgVar) result.surfaceKey = bgVar.key;
137
+ if (colorsVars.length > 0) {
138
+ const first = await figma.variables.importVariableByKeyAsync(colorsVars[0].key);
139
+ const coll = await figma.variables.getVariableCollectionByIdAsync(first.variableCollectionId);
140
+ result.collectionInfo.colors = { collId: coll.id, modes: coll.modes.map(m => ({name: m.name, modeId: m.modeId})) };
141
+ }
142
+ }
143
+ return result;
144
+ ```
145
+
146
+ #### Execute 2 — Critical Token Import (7 op)
147
+
148
+ ```js
149
+ const tokenKeyMap = {};
150
+ const spacingKeys = { /* Execute 1'den gelen spacingTokenKeys */ };
151
+ for (const [suffix, key] of Object.entries(spacingKeys)) {
152
+ try {
153
+ const imported = await figma.variables.importVariableByKeyAsync(key);
154
+ tokenKeyMap[suffix] = imported.id;
155
+ } catch(e) {}
156
+ }
157
+ return { tokenKeyMap };
158
+ ```
159
+
160
+ **Micro-report:** `✅ Pre-Flight Discovery: <N> collection, <M> token imported, surface=<found/missing>`
161
+
162
+ ### Adım 1.6 — Text Style Resolution
163
+
164
+ Dosyadaki mevcut text style'ları tara, role mapping üret. `importStyleByKeyAsync` ÇAĞIRMA — direkt `setTextStyleIdAsync(roleMap[role].id)` kullan.
165
+
166
+ ```js
167
+ const allTexts = figma.currentPage.findAll(n => n.type === "TEXT");
168
+ const uniqueStyleIds = new Set();
169
+ for (const t of allTexts) { if (t.textStyleId && typeof t.textStyleId === 'string') uniqueStyleIds.add(t.textStyleId); }
170
+
171
+ const styleMap = {};
172
+ for (const id of uniqueStyleIds) {
173
+ try { const style = await figma.getStyleByIdAsync(id); if (style) styleMap[style.id] = { id: style.id, name: style.name, fontSize: style.fontSize || null }; } catch(e) {}
174
+ }
175
+
176
+ const roleKeywords = {
177
+ display: ["display","hero","amount","title-xl"], title: ["section-title","title","heading"],
178
+ subtitle: ["subtitle","body-semibold","body-bold"], body: ["body-medium","body-regular","body"],
179
+ caption: ["small","caption","footnote"], button: ["button"]
180
+ };
181
+ 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; }
182
+ const roleMap = {};
183
+ 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 }; }
184
+ if (!roleMap.display) {
185
+ const sorted = Object.values(styleMap).filter(s => s.fontSize).sort((a,b) => b.fontSize - a.fontSize);
186
+ if (sorted.length > 0) roleMap.display = { id: sorted[0].id, name: sorted[0].name, fontSize: sorted[0].fontSize };
187
+ }
188
+ return { totalStyles: Object.keys(styleMap).length, styleMap, roleMap };
189
+ ```
190
+
191
+ **Micro-report:** `✅ Text Style: <N> style, <M> role eşleşti`
192
+
193
+ ---
194
+
195
+ ### Adım 2 — Wrapper Frame + Background (Edge-to-Edge)
196
+
197
+ Ana frame: device preset boyutu, auto-layout VERTICAL, padding=0, gap=0, background DS variable'a bağlı.
198
+
199
+ **Edge-to-Edge yapı:**
200
+ ```
201
+ Ana Frame (padding:0, gap:0, VERTICAL)
202
+ ├── NavigationTopBar (FILL — edge-to-edge)
203
+ ├── Content Body (FILL both, padding:spacing-100, gap:spacing-075)
204
+ │ └── Recipe component'leri
205
+ └── BottomNavBar (FILL — edge-to-edge, varsa)
206
+ ```
207
+
208
+ ```js
209
+ const frame = figma.createFrame();
210
+ frame.name = "<Screen Name> — <Device Preset>";
211
+ frame.resize(<width>, <height>);
212
+ frame.layoutMode = "VERTICAL";
213
+ frame.primaryAxisSizingMode = "FIXED";
214
+ frame.counterAxisSizingMode = "FIXED";
215
+ frame.paddingTop = 0; frame.paddingBottom = 0; frame.paddingLeft = 0; frame.paddingRight = 0;
216
+ frame.itemSpacing = 0;
217
+ if (surfaceKey) {
218
+ const bgVar = await figma.variables.importVariableByKeyAsync(surfaceKey);
219
+ const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
220
+ frame.fills = [figma.variables.setBoundVariableForPaint(paint, 'color', bgVar)];
221
+ }
222
+ return { frameId: frame.id };
223
+ ```
224
+
225
+ **Micro-report:** `✅ Frame: <device> (<w>×<h>), edge-to-edge, background bound`
226
+
227
+ ### Adım 3 — ⏭️ DEVRE DIŞI
228
+
229
+ Breakpoint bind frame boyutunu bozuyor (375 vs 402). Device preset boyutu korunur.
230
+
231
+ ### Adım 4 — Theme + Size Mode Setup
232
+
233
+ Collection/mode bilgisi Adım 1.5'ten gelir (ayrı execute yok).
234
+
235
+ **Adım 4b — Mode apply:**
236
+ ```js
237
+ const frame = await figma.getNodeByIdAsync(frameId);
238
+ if (collectionInfo.colors) {
239
+ const coll = await figma.variables.getVariableCollectionByIdAsync(collectionInfo.colors.collId);
240
+ const lightMode = collectionInfo.colors.modes.find(m => m.name.toLowerCase().includes("light"));
241
+ if (lightMode) frame.setExplicitVariableModeForCollection(coll, lightMode.modeId);
242
+ }
243
+ if (collectionInfo.size) {
244
+ const coll = await figma.variables.getVariableCollectionByIdAsync(collectionInfo.size.collId);
245
+ const kws = { mobile:["mobil","mobile"], tablet:["tablet"], desktop:["web","desktop"], web:["web","desktop"] };
246
+ const sizeMode = collectionInfo.size.modes.find(m => (kws[platform]||["mobil"]).some(k => m.name.toLowerCase().includes(k)));
247
+ if (sizeMode) frame.setExplicitVariableModeForCollection(coll, sizeMode.modeId);
248
+ }
249
+ return { modesApplied: true };
250
+ ```
251
+
252
+ **Micro-report:** `✅ Theme: Light, Size: <modeName>`
253
+
254
+ ### Adım 5.5 — Content Body Wrapper
255
+
256
+ ```js
257
+ const parentFrame = await figma.getNodeByIdAsync(frameId);
258
+ const contentBody = figma.createFrame();
259
+ contentBody.name = "Content Body";
260
+ contentBody.layoutMode = "VERTICAL";
261
+ parentFrame.appendChild(contentBody); // ÖNCE (Rule 11)
262
+ contentBody.layoutSizingHorizontal = "FILL"; // SONRA
263
+ contentBody.layoutSizingVertical = "FILL";
264
+ const paddingVar = await figma.variables.importVariableByKeyAsync(spacing100Key);
265
+ const gapVar = await figma.variables.importVariableByKeyAsync(spacing075Key);
266
+ contentBody.setBoundVariable("paddingLeft", paddingVar);
267
+ contentBody.setBoundVariable("paddingRight", paddingVar);
268
+ contentBody.setBoundVariable("paddingTop", paddingVar);
269
+ contentBody.setBoundVariable("paddingBottom", paddingVar);
270
+ contentBody.setBoundVariable("itemSpacing", gapVar);
271
+ contentBody.fills = [];
272
+ return { contentBodyId: contentBody.id };
273
+ ```
274
+
275
+ **Micro-report:** `✅ Content Body: FILL both, padding=spacing-100, gap=spacing-075`
276
+
277
+ ### Adım 6 — Component Discovery
278
+
279
+ **Cache-First (v3.0+):** Önce `.claude/design-systems/sui/components.md` oku. Cache varsa → `figma_search_assets` ATLA, direkt `importComponentByKeyAsync` kullan. Yoksa: `figma_search_assets(query="<keywords>")` + Rule 24 fallback.
280
+
281
+ **Micro-report:** `✅ Component keşfi: <N> bulundu, <M> eksik`
282
+
283
+ ### Adım 7 — Recipe Component Placement
284
+
285
+ 3-4 component TEK execute'ta. Parent routing: edge-to-edge (NavigationTopBar, BottomNavBar) → Ana Frame, diğer her şey → Content Body.
286
+
287
+ ```
288
+ const edgeNames = /^(navigation|nav|top|bottom|status|tabbar|tab_bar)/i;
289
+ const parentId = edgeNames.test(spec.name) ? frameId : contentBodyId;
290
+ ```
291
+
292
+ **Text Style Binding:** `await textNode.setTextStyleIdAsync(roleMap[role].id)` — DİREKT bağla, `importStyleByKeyAsync` ÇAĞIRMA, `fontSize` set ETME.
293
+
294
+ **Text rolü mapping:** display→Amount/Hero, title→Section Header, subtitle→Card Title, body→Body text, caption→Small/Info.
295
+
296
+ ### Adım 8 — Dark Variant
297
+
298
+ ```js
299
+ const lightFrame = await figma.getNodeByIdAsync(lightFrameId);
300
+ const darkFrame = lightFrame.clone();
301
+ figma.currentPage.appendChild(darkFrame);
302
+ darkFrame.x = lightFrame.x + lightFrame.width + 80;
303
+ darkFrame.name = lightFrame.name + " — Dark";
304
+ const coll = await figma.variables.getVariableCollectionByIdAsync(semColorsCollId);
305
+ darkFrame.setExplicitVariableModeForCollection(coll, darkModeId);
306
+ return { darkFrameId: darkFrame.id };
307
+ ```
308
+
309
+ ### Adım 9 — Validation + Final Report
310
+
311
+ `figma_validate_screen(frameId, minScore=80)` her frame için. 3 deneme fail → kullanıcıya sor.
312
+
313
+ **Son Rapor:** Screen type, DS, device, variants, frame ID'leri, validate skorları, kullanılan component listesi, primitive fallback listesi, token binding sayıları, toplam execute, süre.
314
+
315
+ ---
316
+
317
+ ## Device Presets Lookup Table
318
+
319
+ ### Mobile
320
+
321
+ | Device | W | H | Keywords |
322
+ |---|---|---|---|
323
+ | iPhone 17 | 402 | 874 | mobile, iphone, ios |
324
+ | iPhone 16 & 17 Pro | 402 | 874 | iphone pro |
325
+ | iPhone 16 | 393 | 852 | iphone 16 |
326
+ | iPhone 16 Pro Max | 440 | 956 | pro max |
327
+ | iPhone 16 Plus | 430 | 932 | iphone plus |
328
+ | iPhone Air | 420 | 912 | iphone air |
329
+ | iPhone 13 & 14 | 390 | 844 | iphone 13, iphone 14 |
330
+ | Android Compact | 412 | 917 | android |
331
+ | Android Medium | 700 | 840 | android tablet |
332
+
333
+ **Default:** mobile → iPhone 17, android → Android Compact.
334
+
335
+ ### Tablet
336
+
337
+ | Device | W | H |
338
+ |---|---|---|
339
+ | iPad Pro 11" | 834 | 1194 |
340
+ | iPad Pro 12.9" | 1024 | 1366 |
341
+
342
+ **Default:** tablet → iPad Pro 11".
343
+
344
+ ### Desktop / Web
345
+
346
+ | Device | W | H |
347
+ |---|---|---|
348
+ | Desktop | 1440 | 900 |
349
+ | Desktop HD | 1920 | 1080 |
350
+ | MacBook Pro 14" | 1512 | 982 |
351
+ | MacBook Pro 16" | 1728 | 1117 |
352
+
353
+ **Default:** desktop/web → Desktop 1440×900.
354
+
355
+ ---
356
+
357
+ ## 9 Screen Type Recipes
358
+
359
+ Her recipe = component listesi + yerleşim sırası + search keyword'leri + setProperties.
360
+
361
+ ### Recipe 1: Login
362
+ **Trigger:** login, giriş, oturum aç, sign in
363
+
364
+ 1. **AppBar** — `["navigation top", "appbar"]`
365
+ **setProperties:** `{ "Subtitle": false, "Right Controls": false }`
366
+ 2. **Logo** — `["logo", "brand"]`
367
+ 3. **Welcome Text** (H1) — `["heading", "display"]`
368
+ 4. **Subtitle Text** — `["body medium", "text body"]`
369
+ 5. **Email Input** — `["text field", "input", "email"]`
370
+ 6. **Password Input** — `["password", "text field password"]`
371
+ 7. **Primary Button** ("Giriş Yap") — `["button primary"]`
372
+ **setProperties:** `{ "Value": "Giriş Yap" }`
373
+ 8. **Forgot Password Link** — `["link", "text button"]`
374
+ 9. **Divider** — `["divider"]`
375
+ 10. **Register Link** — `["text button", "link"]`
376
+
377
+ ### Recipe 2: Payment
378
+ **Trigger:** ödeme, payment, checkout, satın al
379
+
380
+ 1. **NavigationTopBar** — `["navigation top", "appbar"]`
381
+ **setProperties:** `{ "Title Text": "Ödeme", "Right Controls": false, "Subtitle": false, "Product": "main" }`
382
+ 2. **Amount Display** — `["display large", "hero text"]`
383
+ 3. **Currency Label** — `["body small", "caption"]`
384
+ 4. **Section Header** — `["section header", "subtitle"]`
385
+ 5. **Payment Method Cards** (×3) — `["card payment", "list item"]`
386
+ 6. **Add New Method Button** — `["button secondary"]`
387
+ 7. **Divider** — `["divider"]`
388
+ 8. **CTA Button** — `["button primary large"]`
389
+ **setProperties:** `{ "Value": "Ödemeyi Tamamla" }`
390
+ 9. **Security Info** — `["text small", "caption"]`
391
+
392
+ ### Recipe 3: Profile
393
+ **Trigger:** profil, profile, hesap, account
394
+
395
+ 1. **AppBar** — `["navigation top"]`
396
+ **setProperties:** `{ "Title Text": "Profilim" }`
397
+ 2. **Avatar** — `["avatar large", "profile picture"]`
398
+ 3. **User Name Text** — `["heading", "display medium"]`
399
+ 4. **User Email Text** — `["body", "text secondary"]`
400
+ 5. **Divider** — `["divider"]`
401
+ 6. **Menu List Items** (×4) — `["list item", "menu row"]`
402
+ 7. **Destructive Button** ("Çıkış Yap") — `["button destructive"]`
403
+ **setProperties:** `{ "Value": "Çıkış Yap" }`
404
+
405
+ ### Recipe 4: List
406
+ **Trigger:** liste, list, arama, search, katalog
407
+
408
+ 1. **AppBar** — `["navigation top"]`
409
+ **setProperties:** `{ "Title Text": "Arama" }`
410
+ 2. **Search Bar** — `["search", "search bar"]`
411
+ 3. **Filter Chips Row** — `["chip", "filter chip"]`
412
+ 4. **Card List Items** (×N) — `["card", "list card"]`
413
+ 5. **Pagination** — `["pagination"]`
414
+ 6. **FAB** (opsiyonel) — `["fab", "floating button"]`
415
+
416
+ ### Recipe 5: Detail
417
+ **Trigger:** detay, detail, ürün detay, product
418
+
419
+ 1. **AppBar** — `["navigation top"]`
420
+ **setProperties:** `{ "Right Controls": true }`
421
+ 2. **Hero Image** — `["image large", "hero"]`
422
+ 3. **Title Text** — `["heading large", "display"]`
423
+ 4. **Price + Rating Row** — `["price", "rating"]`
424
+ 5. **Description Text** — `["body", "text body"]`
425
+ 6. **Stats Section** — `["stat", "info row"]`
426
+ 7. **CTA Button** — `["button primary"]`
427
+ **setProperties:** `{ "Value": "Sepete Ekle" }`
428
+
429
+ ### Recipe 6: Form
430
+ **Trigger:** form, başvuru, kayıt, application
431
+
432
+ 1. **AppBar** — `["navigation top"]`
433
+ **setProperties:** `{ "Title Text": "Başvuru" }`
434
+ 2. **Progress Indicator** — `["stepper", "progress"]`
435
+ 3. **Field Group 1** (3-4 input) — `["text field", "input"]`
436
+ 4. **Field Group 2** — `["select", "dropdown", "date picker"]`
437
+ 5. **Checkbox / Terms** — `["checkbox", "agreement"]`
438
+ 6. **Submit Button** — `["button primary"]`
439
+ **setProperties:** `{ "Value": "Gönder" }`
440
+
441
+ ### Recipe 7: Onboarding
442
+ **Trigger:** onboarding, tanıtım, karşılama, welcome
443
+
444
+ 1. **Hero Image** — `["illustration", "image hero"]`
445
+ 2. **Title Text** — `["display", "heading large"]`
446
+ 3. **Subtitle Text** — `["body", "text secondary"]`
447
+ 4. **Pagination Dots** — `["dots", "page indicator"]`
448
+ 5. **Primary Button** — `["button primary"]`
449
+ **setProperties:** `{ "Value": "Başla" }`
450
+ 6. **Skip Link** — `["text button", "link"]`
451
+
452
+ ### Recipe 8: Dashboard
453
+ **Trigger:** dashboard, özet, summary, panel
454
+
455
+ 1. **AppBar** — `["navigation top"]`
456
+ **setProperties:** `{ "Title Text": "Özet" }`
457
+ 2. **Stats Cards** (×4) — `["stat card", "metric card"]`
458
+ 3. **Chart** — `["chart", "graph"]`
459
+ 4. **Section Header** — `["section header"]`
460
+ 5. **Activity List Items** (×N) — `["list item", "activity row"]`
461
+
462
+ ### Recipe 9: Settings
463
+ **Trigger:** ayarlar, settings, tercihler
464
+
465
+ 1. **AppBar** — `["navigation top"]`
466
+ **setProperties:** `{ "Title Text": "Ayarlar" }`
467
+ 2. **Section Headers** — `["section header"]`
468
+ 3. **Toggle Rows** — `["list item toggle", "setting row"]`
469
+ 4. **Info Row** — `["list item", "nav row"]`
470
+ 5. **Destructive Button** — `["button destructive"]`
471
+ **setProperties:** `{ "Value": "Hesabı Sil" }`
472
+
473
+ ---
474
+
475
+ ## Primitive Fallback Pattern
476
+
477
+ Component bulunamazsa token-bound primitive ile inşa et. Tüm visual properties variable'a bağlı olmalı:
478
+ ```js
479
+ const card = figma.createFrame();
480
+ card.name = "... (primitive fallback)";
481
+ card.layoutMode = "HORIZONTAL";
482
+ card.setBoundVariable("cornerRadius", radiusSmVar);
483
+ card.setBoundVariable("paddingLeft", spacingMdVar);
484
+ // ... diğer padding/gap bind'ları
485
+ const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
486
+ card.fills = [figma.variables.setBoundVariableForPaint(paint, 'color', surfaceVar)];
487
+ parent.appendChild(card); // ÖNCE
488
+ card.layoutSizingHorizontal = "FILL"; // SONRA (Rule 11)
489
+ ```
490
+
491
+ ---
492
+
493
+ ## Chunking Kuralları
494
+
495
+ - Max **15 atomic op / figma_execute**
496
+ - **3-4 component TEK execute'ta** yerleştir
497
+ - Timeout: `15000` ms yeterli (15 op ≈ 200-300ms)
498
+ - Her component için AYRI execute YASAK
499
+
500
+ ## Error Recovery
501
+
502
+ | Hata | Aksiyon |
503
+ |---|---|
504
+ | DS GATE yok | Fast Path iptal, orchestrator'a dön |
505
+ | Device bulunamadı | Default kullan (iPhone 17 / Desktop 1440) |
506
+ | Colors collection yok | Light-only, dark skip |
507
+ | Component 0 sonuç | Primitive fallback |
508
+ | Execute timeout | Op sayısını azalt, böl |
509
+ | Style import fail | Rule 23 try-catch, roleMap fallback |
510
+ | 3× validate fail | Kullanıcıya generate-figma-screen öner |
511
+
512
+ ## Known Limitations
513
+
514
+ 1. SUI dışında test edilmedi — başka DS için isim farkı olabilir
515
+ 2. Dark mode validate ayrı ölçülmez — manuel göz kontrolü gerekli
516
+ 3. 9 recipe sabit — yeni tip → generate-figma-screen
517
+ 4. Multi-screen ve prototype/interactions YOK
518
+ 5. Component search false match riski — setProperties ile doğrula
519
+
520
+ ---
521
+
522
+ ## Skill References
523
+
524
+ - `skills/fmcp-screen-orchestrator/SKILL.md` — Fast Path tetiklenme
525
+ - `skills/figma-canvas-ops/SKILL.md` — Rule 5a CHUNKING + Rule 22 Async + Rule 23 Style zorunlu
526
+ - `.claude/design-systems/active-ds.md` — DS GATE state
527
+ - `.claude/design-systems/sui/tokens.md` — Token cache
528
+ - `.claude/design-systems/sui/components.md` — Component cache