@atezer/figma-mcp-bridge 1.9.6 → 1.9.8
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.
- package/CHANGELOG.md +78 -0
- package/dist/core/blocking-tracker.d.ts +68 -0
- package/dist/core/blocking-tracker.d.ts.map +1 -0
- package/dist/core/blocking-tracker.js +152 -0
- package/dist/core/blocking-tracker.js.map +1 -0
- package/dist/core/bootstrap-injector.d.ts +50 -0
- package/dist/core/bootstrap-injector.d.ts.map +1 -0
- package/dist/core/bootstrap-injector.js +134 -0
- package/dist/core/bootstrap-injector.js.map +1 -0
- package/dist/core/embedded-skills.d.ts +17 -0
- package/dist/core/embedded-skills.d.ts.map +1 -0
- package/dist/core/embedded-skills.js +817 -0
- package/dist/core/embedded-skills.js.map +1 -0
- package/dist/core/plugin-bridge-connector.d.ts +15 -0
- package/dist/core/plugin-bridge-connector.d.ts.map +1 -1
- package/dist/core/plugin-bridge-connector.js +17 -0
- package/dist/core/plugin-bridge-connector.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/local-plugin-only.d.ts.map +1 -1
- package/dist/local-plugin-only.js +141 -2
- package/dist/local-plugin-only.js.map +1 -1
- package/f-mcp-plugin/code.js +206 -1
- package/f-mcp-plugin/ui.html +20 -1
- package/package.json +3 -2
- package/skills/fmcp-intent-router/SKILL.md +23 -9
- package/skills/fmcp-screen-orchestrator/SKILL.md +26 -0
package/f-mcp-plugin/code.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
// v1.8.0+: Plugin version reported in WebSocket "ready" handshake.
|
|
8
8
|
// Keep in sync with package.json and src/core/version.ts.
|
|
9
|
-
var FMCP_PLUGIN_VERSION = '1.9.
|
|
9
|
+
var FMCP_PLUGIN_VERSION = '1.9.8';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* v1.9.6: Post-execute scan — figma_execute sonrasında oluşturulan node'ları
|
|
@@ -2939,6 +2939,211 @@ figma.ui.onmessage = async (msg) => {
|
|
|
2939
2939
|
}
|
|
2940
2940
|
}
|
|
2941
2941
|
|
|
2942
|
+
// ============================================================================
|
|
2943
|
+
// CREATE_MINI_DS (v1.9.7) — Boş dosyada minimal Design System kur.
|
|
2944
|
+
// 2-phase batched: Phase 1 = Variable Collections + Variables + Text Styles
|
|
2945
|
+
// Phase 2 = Components (Button/Input/Card)
|
|
2946
|
+
// Her phase ayrı promise chain, timeout 30s içinde tamamlanır.
|
|
2947
|
+
// ============================================================================
|
|
2948
|
+
else if (msg.type === 'CREATE_MINI_DS') {
|
|
2949
|
+
try {
|
|
2950
|
+
console.log('🌉 [F-MCP v1.9.7] CREATE_MINI_DS starting...');
|
|
2951
|
+
|
|
2952
|
+
var primaryColor = msg.primaryColor || '#1464FF';
|
|
2953
|
+
var fontFamily = msg.fontFamily || 'Inter';
|
|
2954
|
+
var dsName = msg.name || 'Mini DS';
|
|
2955
|
+
var includeComponents = msg.includeComponents !== false;
|
|
2956
|
+
|
|
2957
|
+
// ---------- Hex → RGB ----------
|
|
2958
|
+
function hexToRgb(hex) {
|
|
2959
|
+
var h = hex.replace('#', '');
|
|
2960
|
+
if (h.length === 3) h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
|
|
2961
|
+
var r = parseInt(h.slice(0,2), 16) / 255;
|
|
2962
|
+
var g = parseInt(h.slice(2,4), 16) / 255;
|
|
2963
|
+
var b = parseInt(h.slice(4,6), 16) / 255;
|
|
2964
|
+
return { r: r, g: g, b: b };
|
|
2965
|
+
}
|
|
2966
|
+
function mix(a, b, t) { return { r: a.r*(1-t)+b.r*t, g: a.g*(1-t)+b.g*t, b: a.b*(1-t)+b.b*t }; }
|
|
2967
|
+
|
|
2968
|
+
var primaryRgb = hexToRgb(primaryColor);
|
|
2969
|
+
var black = { r: 0, g: 0, b: 0 };
|
|
2970
|
+
var white = { r: 1, g: 1, b: 1 };
|
|
2971
|
+
|
|
2972
|
+
// ---------- PHASE 1: Variable Collections + Variables + Text Styles ----------
|
|
2973
|
+
var colorCollection = figma.variables.createVariableCollection(dsName + ' — Colors');
|
|
2974
|
+
var modeId = colorCollection.defaultModeId;
|
|
2975
|
+
var colorVars = {};
|
|
2976
|
+
|
|
2977
|
+
function addColorVar(name, rgb) {
|
|
2978
|
+
var v = figma.variables.createVariable(name, colorCollection, 'COLOR');
|
|
2979
|
+
v.setValueForMode(modeId, rgb);
|
|
2980
|
+
colorVars[name] = v;
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
addColorVar('primary/default', primaryRgb);
|
|
2984
|
+
addColorVar('primary/hover', mix(primaryRgb, black, 0.1));
|
|
2985
|
+
addColorVar('primary/disabled', mix(primaryRgb, white, 0.5));
|
|
2986
|
+
addColorVar('bg/level-0', white);
|
|
2987
|
+
addColorVar('bg/level-1', { r: 0.97, g: 0.97, b: 0.98 });
|
|
2988
|
+
addColorVar('bg/level-2', { r: 0.93, g: 0.94, b: 0.96 });
|
|
2989
|
+
addColorVar('text/primary', { r: 0.1, g: 0.1, b: 0.1 });
|
|
2990
|
+
addColorVar('text/secondary', { r: 0.4, g: 0.4, b: 0.45 });
|
|
2991
|
+
addColorVar('text/on-primary', white);
|
|
2992
|
+
addColorVar('success/default', { r: 0.18, g: 0.73, b: 0.39 });
|
|
2993
|
+
addColorVar('error/default', { r: 0.91, g: 0.22, b: 0.27 });
|
|
2994
|
+
addColorVar('warning/default', { r: 0.98, g: 0.69, b: 0.13 });
|
|
2995
|
+
|
|
2996
|
+
var sizingCollection = figma.variables.createVariableCollection(dsName + ' — Sizing');
|
|
2997
|
+
var sizingMode = sizingCollection.defaultModeId;
|
|
2998
|
+
var sizingVars = {};
|
|
2999
|
+
function addSizingVar(name, value) {
|
|
3000
|
+
var v = figma.variables.createVariable(name, sizingCollection, 'FLOAT');
|
|
3001
|
+
v.setValueForMode(sizingMode, value);
|
|
3002
|
+
sizingVars[name] = v;
|
|
3003
|
+
}
|
|
3004
|
+
addSizingVar('spacing/xs', 4);
|
|
3005
|
+
addSizingVar('spacing/sm', 8);
|
|
3006
|
+
addSizingVar('spacing/md', 16);
|
|
3007
|
+
addSizingVar('spacing/lg', 24);
|
|
3008
|
+
addSizingVar('spacing/xl', 32);
|
|
3009
|
+
addSizingVar('radius/sm', 4);
|
|
3010
|
+
addSizingVar('radius/md', 8);
|
|
3011
|
+
addSizingVar('radius/lg', 16);
|
|
3012
|
+
|
|
3013
|
+
// Text Styles (font loading required)
|
|
3014
|
+
await figma.loadFontAsync({ family: fontFamily, style: 'Semi Bold' }).catch(function() {});
|
|
3015
|
+
await figma.loadFontAsync({ family: fontFamily, style: 'Regular' }).catch(function() {});
|
|
3016
|
+
|
|
3017
|
+
function createTextStyle(name, fontSize, lineHeight, fontStyle) {
|
|
3018
|
+
var ts = figma.createTextStyle();
|
|
3019
|
+
ts.name = name;
|
|
3020
|
+
try {
|
|
3021
|
+
ts.fontName = { family: fontFamily, style: fontStyle };
|
|
3022
|
+
ts.fontSize = fontSize;
|
|
3023
|
+
ts.lineHeight = { unit: 'PIXELS', value: lineHeight };
|
|
3024
|
+
} catch (e) {
|
|
3025
|
+
console.warn('TextStyle ' + name + ' font set failed:', e.message);
|
|
3026
|
+
}
|
|
3027
|
+
return ts;
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
var titleStyle = createTextStyle(dsName + '/Title/Large', 24, 32, 'Semi Bold');
|
|
3031
|
+
var bodyStyle = createTextStyle(dsName + '/Body/Default', 14, 20, 'Regular');
|
|
3032
|
+
var captionStyle = createTextStyle(dsName + '/Caption/Small', 12, 16, 'Regular');
|
|
3033
|
+
|
|
3034
|
+
console.log('🌉 [F-MCP v1.9.7] Phase 1 done — variables + text styles created');
|
|
3035
|
+
|
|
3036
|
+
// ---------- PHASE 2: Components ----------
|
|
3037
|
+
var componentIds = {};
|
|
3038
|
+
if (includeComponents) {
|
|
3039
|
+
// Button component
|
|
3040
|
+
var buttonFrame = figma.createFrame();
|
|
3041
|
+
buttonFrame.name = dsName + '/Button/Primary';
|
|
3042
|
+
buttonFrame.layoutMode = 'HORIZONTAL';
|
|
3043
|
+
buttonFrame.primaryAxisSizingMode = 'AUTO';
|
|
3044
|
+
buttonFrame.counterAxisSizingMode = 'AUTO';
|
|
3045
|
+
buttonFrame.primaryAxisAlignItems = 'CENTER';
|
|
3046
|
+
buttonFrame.counterAxisAlignItems = 'CENTER';
|
|
3047
|
+
buttonFrame.setBoundVariable('paddingLeft', sizingVars['spacing/md']);
|
|
3048
|
+
buttonFrame.setBoundVariable('paddingRight', sizingVars['spacing/md']);
|
|
3049
|
+
buttonFrame.setBoundVariable('paddingTop', sizingVars['spacing/sm']);
|
|
3050
|
+
buttonFrame.setBoundVariable('paddingBottom', sizingVars['spacing/sm']);
|
|
3051
|
+
buttonFrame.setBoundVariable('cornerRadius', sizingVars['radius/md']);
|
|
3052
|
+
buttonFrame.fills = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: primaryRgb }, 'color', colorVars['primary/default'])];
|
|
3053
|
+
|
|
3054
|
+
var buttonText = figma.createText();
|
|
3055
|
+
try {
|
|
3056
|
+
buttonText.fontName = { family: fontFamily, style: 'Semi Bold' };
|
|
3057
|
+
buttonText.characters = 'Button';
|
|
3058
|
+
await buttonText.setTextStyleIdAsync(bodyStyle.id).catch(function() {});
|
|
3059
|
+
buttonText.fills = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }, 'color', colorVars['text/on-primary'])];
|
|
3060
|
+
} catch (e) { console.warn('Button text fail:', e.message); }
|
|
3061
|
+
buttonFrame.appendChild(buttonText);
|
|
3062
|
+
var buttonComp = figma.createComponentFromNode(buttonFrame);
|
|
3063
|
+
componentIds.button = buttonComp.id;
|
|
3064
|
+
|
|
3065
|
+
// Input component
|
|
3066
|
+
var inputFrame = figma.createFrame();
|
|
3067
|
+
inputFrame.name = dsName + '/Input/Default';
|
|
3068
|
+
inputFrame.resize(240, 40);
|
|
3069
|
+
inputFrame.layoutMode = 'HORIZONTAL';
|
|
3070
|
+
inputFrame.primaryAxisAlignItems = 'CENTER';
|
|
3071
|
+
inputFrame.setBoundVariable('paddingLeft', sizingVars['spacing/md']);
|
|
3072
|
+
inputFrame.setBoundVariable('paddingRight', sizingVars['spacing/md']);
|
|
3073
|
+
inputFrame.setBoundVariable('cornerRadius', sizingVars['radius/md']);
|
|
3074
|
+
inputFrame.fills = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 0.97, g: 0.97, b: 0.98 } }, 'color', colorVars['bg/level-1'])];
|
|
3075
|
+
inputFrame.strokes = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 0.9, g: 0.9, b: 0.92 } }, 'color', colorVars['bg/level-2'])];
|
|
3076
|
+
inputFrame.strokeWeight = 1;
|
|
3077
|
+
|
|
3078
|
+
var placeholder = figma.createText();
|
|
3079
|
+
try {
|
|
3080
|
+
placeholder.fontName = { family: fontFamily, style: 'Regular' };
|
|
3081
|
+
placeholder.characters = 'Placeholder';
|
|
3082
|
+
await placeholder.setTextStyleIdAsync(bodyStyle.id).catch(function() {});
|
|
3083
|
+
placeholder.fills = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 0.4, g: 0.4, b: 0.45 } }, 'color', colorVars['text/secondary'])];
|
|
3084
|
+
} catch (e) {}
|
|
3085
|
+
inputFrame.appendChild(placeholder);
|
|
3086
|
+
var inputComp = figma.createComponentFromNode(inputFrame);
|
|
3087
|
+
componentIds.input = inputComp.id;
|
|
3088
|
+
|
|
3089
|
+
// Card component
|
|
3090
|
+
var cardFrame = figma.createFrame();
|
|
3091
|
+
cardFrame.name = dsName + '/Card/Default';
|
|
3092
|
+
cardFrame.layoutMode = 'VERTICAL';
|
|
3093
|
+
cardFrame.primaryAxisSizingMode = 'AUTO';
|
|
3094
|
+
cardFrame.counterAxisSizingMode = 'AUTO';
|
|
3095
|
+
cardFrame.setBoundVariable('paddingLeft', sizingVars['spacing/lg']);
|
|
3096
|
+
cardFrame.setBoundVariable('paddingRight', sizingVars['spacing/lg']);
|
|
3097
|
+
cardFrame.setBoundVariable('paddingTop', sizingVars['spacing/lg']);
|
|
3098
|
+
cardFrame.setBoundVariable('paddingBottom', sizingVars['spacing/lg']);
|
|
3099
|
+
cardFrame.setBoundVariable('itemSpacing', sizingVars['spacing/sm']);
|
|
3100
|
+
cardFrame.setBoundVariable('cornerRadius', sizingVars['radius/lg']);
|
|
3101
|
+
cardFrame.fills = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }, 'color', colorVars['bg/level-0'])];
|
|
3102
|
+
cardFrame.strokes = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 0.93, g: 0.94, b: 0.96 } }, 'color', colorVars['bg/level-2'])];
|
|
3103
|
+
cardFrame.strokeWeight = 1;
|
|
3104
|
+
|
|
3105
|
+
var cardTitle = figma.createText();
|
|
3106
|
+
try {
|
|
3107
|
+
cardTitle.fontName = { family: fontFamily, style: 'Semi Bold' };
|
|
3108
|
+
cardTitle.characters = 'Card title';
|
|
3109
|
+
await cardTitle.setTextStyleIdAsync(titleStyle.id).catch(function() {});
|
|
3110
|
+
cardTitle.fills = [figma.variables.setBoundVariableForPaint({ type: 'SOLID', color: { r: 0.1, g: 0.1, b: 0.1 } }, 'color', colorVars['text/primary'])];
|
|
3111
|
+
} catch (e) {}
|
|
3112
|
+
cardFrame.appendChild(cardTitle);
|
|
3113
|
+
var cardComp = figma.createComponentFromNode(cardFrame);
|
|
3114
|
+
componentIds.card = cardComp.id;
|
|
3115
|
+
|
|
3116
|
+
console.log('🌉 [F-MCP v1.9.7] Phase 2 done — 3 components created');
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
figma.ui.postMessage({
|
|
3120
|
+
type: 'CREATE_MINI_DS_RESULT',
|
|
3121
|
+
requestId: msg.requestId,
|
|
3122
|
+
success: true,
|
|
3123
|
+
dsName: dsName,
|
|
3124
|
+
variableCollectionIds: { colors: colorCollection.id, sizing: sizingCollection.id },
|
|
3125
|
+
textStyleIds: { title: titleStyle.id, body: bodyStyle.id, caption: captionStyle.id },
|
|
3126
|
+
componentIds: componentIds,
|
|
3127
|
+
summary: {
|
|
3128
|
+
colorVariableCount: Object.keys(colorVars).length,
|
|
3129
|
+
sizingVariableCount: Object.keys(sizingVars).length,
|
|
3130
|
+
textStyleCount: 3,
|
|
3131
|
+
componentCount: Object.keys(componentIds).length,
|
|
3132
|
+
},
|
|
3133
|
+
hint: "v1.9.7 Mini DS hazir. Ekran kurmak icin figma_execute kullan: importVariableByKeyAsync + importComponentByKeyAsync ile key'leri al, instance + setBoundVariable yaparak olustur.",
|
|
3134
|
+
});
|
|
3135
|
+
} catch (error) {
|
|
3136
|
+
var errorMsg = error && error.message ? error.message : String(error);
|
|
3137
|
+
console.error('🌉 [F-MCP v1.9.7] CREATE_MINI_DS error:', errorMsg);
|
|
3138
|
+
figma.ui.postMessage({
|
|
3139
|
+
type: 'CREATE_MINI_DS_RESULT',
|
|
3140
|
+
requestId: msg.requestId,
|
|
3141
|
+
success: false,
|
|
3142
|
+
error: errorMsg,
|
|
3143
|
+
});
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
|
|
2942
3147
|
// ============================================================================
|
|
2943
3148
|
// DELETE_NODE - Delete a node
|
|
2944
3149
|
// ============================================================================
|
package/f-mcp-plugin/ui.html
CHANGED
|
@@ -424,7 +424,7 @@
|
|
|
424
424
|
window.__figmaFileName = null;
|
|
425
425
|
// v1.8.0+: Plugin version reported in WebSocket "ready" handshake.
|
|
426
426
|
// Keep in sync with package.json, src/core/version.ts, f-mcp-plugin/code.js.
|
|
427
|
-
var FMCP_PLUGIN_VERSION = '1.9.
|
|
427
|
+
var FMCP_PLUGIN_VERSION = '1.9.8';
|
|
428
428
|
|
|
429
429
|
// v1.9.2+ Startup log — Figma plugin iframe'de yeni kod yüklendiğini doğrulamak için.
|
|
430
430
|
// Console'da bu log görünüyorsa plugin güncel. Görünmüyorsa iframe eski kopyayı tutuyor
|
|
@@ -778,11 +778,28 @@
|
|
|
778
778
|
if (options.format) params.format = options.format; // PNG, JPG
|
|
779
779
|
if (options.scale != null) params.scale = options.scale; // 1, 2, 4, etc.
|
|
780
780
|
if (options.jpegQuality != null) params.jpegQuality = options.jpegQuality; // 30-100 (v1.8.0)
|
|
781
|
+
// v1.9.5 method selection params
|
|
782
|
+
if (options.returnMode) params.returnMode = options.returnMode;
|
|
783
|
+
if (options.regionStrategy) params.regionStrategy = options.regionStrategy;
|
|
784
|
+
if (options.maxRegions != null) params.maxRegions = options.maxRegions;
|
|
785
|
+
if (options.sliceHeight != null) params.sliceHeight = options.sliceHeight;
|
|
786
|
+
if (options.requestedSlices) params.requestedSlices = options.requestedSlices;
|
|
781
787
|
}
|
|
782
788
|
return window.sendPluginCommand('CAPTURE_SCREENSHOT', params, 30000)
|
|
783
789
|
.catch(function(err) { return { success: false, error: err.message || String(err) }; });
|
|
784
790
|
};
|
|
785
791
|
|
|
792
|
+
// v1.9.7: Create Mini DS — variables + text styles + components in one call
|
|
793
|
+
window.createMiniDs = (params) => {
|
|
794
|
+
return window.sendPluginCommand('CREATE_MINI_DS', {
|
|
795
|
+
primaryColor: params.primaryColor || '#1464FF',
|
|
796
|
+
fontFamily: params.fontFamily || 'Inter',
|
|
797
|
+
name: params.name || 'Mini DS',
|
|
798
|
+
includeComponents: params.includeComponents !== false,
|
|
799
|
+
}, 45000) // 45s — font loading + 3 component creation takes time
|
|
800
|
+
.catch(function(err) { return { success: false, error: err.message || String(err) }; });
|
|
801
|
+
};
|
|
802
|
+
|
|
786
803
|
// Batch export nodes as SVG/PNG/JPG/PDF with base64
|
|
787
804
|
window.batchExportNodes = (nodeIds, format, scale, svgOutlineText, svgIncludeId, svgSimplifyStroke) => {
|
|
788
805
|
return window.sendPluginCommand('BATCH_EXPORT_NODES', {
|
|
@@ -1856,6 +1873,7 @@
|
|
|
1856
1873
|
if (method === 'refreshVariables') return window.refreshVariables();
|
|
1857
1874
|
if (method === 'getLocalComponents') return window.getLocalComponents(params.currentPageOnly, params.limit);
|
|
1858
1875
|
if (method === 'searchLibraryAssets') return window.searchLibraryAssets(params.query, params.assetTypes, params.limit, params.currentPageOnly);
|
|
1876
|
+
if (method === 'getCodeConnectHints') return window.getCodeConnectHints(params.nodeIds, params.scanCurrentPage, params.maxNodes);
|
|
1859
1877
|
if (method === 'instantiateComponent') return window.instantiateComponent(params.componentKey, params.options);
|
|
1860
1878
|
if (method === 'setNodeDescription') return window.setNodeDescription(params.nodeId, params.description, params.descriptionMarkdown);
|
|
1861
1879
|
if (method === 'captureScreenshot') return window.captureScreenshot(params.nodeId, params.options);
|
|
@@ -1881,6 +1899,7 @@
|
|
|
1881
1899
|
if (method === 'setNodeCornerRadius') return window.setNodeCornerRadius(params.nodeId, params.radius);
|
|
1882
1900
|
if (method === 'cloneScreenToDevice') return window.cloneScreenToDevice(params);
|
|
1883
1901
|
if (method === 'validateScreen') return window.validateScreen(params);
|
|
1902
|
+
if (method === 'createMiniDs') return window.createMiniDs(params);
|
|
1884
1903
|
if (method === 'cloneNode') return window.cloneNode(params.nodeId);
|
|
1885
1904
|
if (method === 'deleteNode') return window.deleteNode(params.nodeId);
|
|
1886
1905
|
if (method === 'renameNode') return window.renameNode(params.nodeId, params.newName);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atezer/figma-mcp-bridge",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.8",
|
|
4
4
|
"description": "F-MCP ATezer: MCP server and Figma plugin bridge for Claude/Cursor. No REST token required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/local-plugin-only.js",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"LICENSE"
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
|
-
"prepublishOnly": "npm run build && npm run validate:fmcp-skills",
|
|
30
|
+
"prepublishOnly": "npm run generate:embedded-skills && npm run build && npm run validate:fmcp-skills",
|
|
31
|
+
"generate:embedded-skills": "node scripts/generate-embedded-skills.mjs",
|
|
31
32
|
"dev": "node dist/local-plugin-only.js",
|
|
32
33
|
"build:run": "npm run build && node dist/local-plugin-only.js",
|
|
33
34
|
"build": "tsc -p tsconfig.local.json",
|
|
@@ -28,18 +28,32 @@ Bu SKILL bu üç soruyu **upstream** çözer. Claude intent'i netleştirmeden hi
|
|
|
28
28
|
|
|
29
29
|
Kullanıcı F-MCP ile ilgili herhangi bir talep yaptığında Claude bu 9 adımı SIRAYLA uygular. Hiçbir adım atlanmaz.
|
|
30
30
|
|
|
31
|
-
### 🚨 Adım 0 — DS GATE (v1.9.
|
|
31
|
+
### 🚨 Adım 0 — DS GATE + BLANK FILE CHECK (v1.9.7+ MUTLAK İLK KAPI)
|
|
32
32
|
|
|
33
33
|
**Herhangi bir intent analysis veya figma_* tool çağrısından ÖNCE:**
|
|
34
34
|
|
|
35
|
-
1.
|
|
36
|
-
2. `
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
1. `figma_get_status` çağır — plugin bağlı mı, response'ta `_bootstrap` varsa direktifleri OKU (v1.9.7)
|
|
36
|
+
2. `.claude/design-systems/active-ds.md` dosyasını oku
|
|
37
|
+
3. `Status:` alanını kontrol et:
|
|
38
|
+
- **`✅ Aktif`** → DS net, Blank File Sub-Check'e geç (madde 5)
|
|
39
|
+
- **`❌ Henüz seçilmedi`** VEYA dosya yok → kullanıcıya DS sorusu sor (madde 4)
|
|
40
|
+
4. **DS Sorusu (klasik):** "Aktif bir design system belirlenmemiş. Hangi DS ile ilerlemek istersiniz? (SUI / Material / kendi library)"
|
|
41
|
+
|
|
42
|
+
5. **BLANK FILE SUB-CHECK (v1.9.7, ZORUNLU):** `figma_get_design_system_summary` çağır.
|
|
43
|
+
- `components === 0 && componentSets === 0 && variableCollections.length === 0` ise **BOŞ DOSYA** tespit edildi.
|
|
44
|
+
- `_nextStep: "BLANK_FILE_DIALOG_REQUIRED"` response'ta görünüyorsa, **kullanıcıya 4 seçenek sun** (AskUserQuestion tek call, 4 option):
|
|
45
|
+
```
|
|
46
|
+
Q: "Bu dosyada henüz Design System yok. Nasıl ilerleyelim?"
|
|
47
|
+
(a) Team library import — "Hangi library? SUI, Material 3, iOS HIG, veya kendi library'niz?"
|
|
48
|
+
(b) Mini DS kur otomatik — figma_create_mini_ds tool'u çağrılır (12 color + 8 sizing + 3 text style + Button/Input/Card)
|
|
49
|
+
(c) Referans DS kopyala — "Material 3 template / iOS HIG template"
|
|
50
|
+
(d) DS'siz ilerle — linter tolerant mode, hardcoded değerler kabul (explicit acceptance)
|
|
51
|
+
```
|
|
52
|
+
- **Seçim yapılmadan `figma_execute createFrame` YASAK.** Claude ham createFrame denerse plugin `_DESIGN_SYSTEM_VIOLATIONS_BLOCKING` flag'i döndürür, retry zorunlu olur.
|
|
53
|
+
- Kullanıcı "(b)" derse Claude `figma_create_mini_ds({ primaryColor, fontFamily, name })` tek tool çağrısı yapar, sonra ekran üretimine geçer.
|
|
54
|
+
- Kullanıcı "(a)" derse Claude `figma_get_library_variables(libraryName)` ile listeleme yapar, seçenek sunar.
|
|
55
|
+
|
|
56
|
+
6. Seçim sonrası `active-ds.md`'yi güncelle (`Status: ✅ Aktif`, `Library Name: <isim>`), sonra Adım 1'e geç.
|
|
43
57
|
|
|
44
58
|
**Neden bu kadar katı:**
|
|
45
59
|
- DS belirsizken `figma_search_assets`, `figma_get_file_data`, `figma_get_library_variables` çağırmak = kullanıcının istemediği library'leri enumere etmek = **token israfı + UX bozulması**
|
|
@@ -144,6 +144,32 @@ Benchmark/görselden DEĞER alma YASAK. Sadece NİYET: layout yönü, hiyerarşi
|
|
|
144
144
|
2. **Content** — DS instance yerleşimi → screenshot → onay
|
|
145
145
|
3. **Polish** — spacing, states, edge cases → son screenshot → audit
|
|
146
146
|
|
|
147
|
+
### v1.9.7 Anti-Suppression Kuralı (MUTLAK — HARD enforcement)
|
|
148
|
+
|
|
149
|
+
BLOCKING response gördüğünde YASAK ifadeler:
|
|
150
|
+
- "bu projede geçerli değil"
|
|
151
|
+
- "dosyada DS yok, bu uyarılar önemsiz"
|
|
152
|
+
- "şimdilik skip edelim"
|
|
153
|
+
- "sonra düzeltiriz"
|
|
154
|
+
- "yine de devam ediyorum"
|
|
155
|
+
|
|
156
|
+
**Server seviyesi enforcement (Katman 3):** Claude BLOCKING flag gördükten sonra **aynı nodeId** üzerinde ikinci `figma_execute` mutation yaparsa server **HARD_ERROR** döndürür — tool fail eder, response değil error. Claude bunu skip edemez.
|
|
157
|
+
|
|
158
|
+
**Override escape hatch:** Sadece kullanıcı onayı ile `// FORCE_OVERRIDE` comment'i kod başına ekleyerek bypass edilebilir.
|
|
159
|
+
|
|
160
|
+
**ZORUNLU davranış BLOCKING gördüğünde:**
|
|
161
|
+
1. BLOCKING'i OKU — hangi nodeId, hangi kategori? (_postExecuteViolations.violations)
|
|
162
|
+
2. Kök nedeni analiz et:
|
|
163
|
+
- UNBOUND_FILL → setBoundVariableForPaint eksik
|
|
164
|
+
- UNBOUND_PADDING → setBoundVariable eksik
|
|
165
|
+
- UNBOUND_TEXTSTYLE → setTextStyleIdAsync eksik
|
|
166
|
+
- NO_INSTANCE_USAGE → DS component yok, import edilmeli
|
|
167
|
+
3. Kullanıcıya sor SADECE şu koşullarda:
|
|
168
|
+
- DS gerçekten yok → Adım 0 Blank File sub-check'e dön (4 option)
|
|
169
|
+
- Override gerekli → "BLOCKING'i geçici bypass etmek istiyor musun?"
|
|
170
|
+
4. Kullanıcı override isterse `// FORCE_OVERRIDE` comment ile kod tekrar çalıştır
|
|
171
|
+
5. Aksi takdirde kodu DÜZELT ve retry
|
|
172
|
+
|
|
147
173
|
### v1.9.5 Discovery Budget Rule (SERT)
|
|
148
174
|
|
|
149
175
|
- **Maks 3 discovery çağrısı** (figma_get_*, figma_search_*, figma_execute read-only) sonra plan sun.
|