sloth-d2c-mcp 1.0.4-beta75 → 1.0.4-beta76
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/dist/build/core/prompt-builder.js +45 -20
- package/dist/build/core/sampling.js +5 -23
- package/dist/build/index.js +159 -26
- package/dist/build/server.js +218 -131
- package/dist/build/socket-client.js +163 -0
- package/dist/build/socket-server.js +233 -0
- package/dist/build/utils/extract.js +11 -9
- package/dist/build/utils/file-manager.js +80 -11
- package/dist/build/utils/tj.js +139 -0
- package/dist/build/utils/utils.js +5 -5
- package/dist/interceptor-web/dist/build-report.json +7 -7
- package/dist/interceptor-web/dist/detail.html +1 -1
- package/dist/interceptor-web/dist/index.html +1 -1
- package/package.json +5 -4
- package/dist/build/component-mapping/adapter-manager.js +0 -45
- package/dist/build/component-mapping/adapters/base-adapter.js +0 -137
- package/dist/build/component-mapping/adapters/ios-adapter.js +0 -697
- package/dist/build/component-mapping/adapters/web-adapter.js +0 -536
- package/dist/build/component-mapping/index.js +0 -32
- package/dist/build/component-mapping/storage.js +0 -142
- package/dist/build/component-mapping/types.js +0 -4
|
@@ -49,12 +49,14 @@ export function buildUserPromptText(userPrompt) {
|
|
|
49
49
|
return '\n## 用户提示词\n以下是用户针对该代码的优化提出的补充说明和要求,请在优化代码时特别关注这些指导并尽可能实现:\n' + userPrompt;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* 构建组件映射提示词(包含组件映射和组件上下文和组件标记)
|
|
53
53
|
* @param componentMappings 组件映射数组
|
|
54
54
|
* @param componentContexts 所有组的组件上下文数组
|
|
55
|
+
* @param markedComponents 标记的组件数组
|
|
55
56
|
* @returns 组件映射提示词
|
|
56
57
|
*/
|
|
57
|
-
export function buildComponentMappingPrompt(componentMappings, componentContexts = []) {
|
|
58
|
+
export function buildComponentMappingPrompt(componentMappings, componentContexts = [], markedComponents = []) {
|
|
59
|
+
let prompt = '';
|
|
58
60
|
// 收集所有组件:组件映射 + 组件上下文(按名称去重)
|
|
59
61
|
const allComponents = new Map();
|
|
60
62
|
// 添加组件映射的组件
|
|
@@ -69,23 +71,46 @@ export function buildComponentMappingPrompt(componentMappings, componentContexts
|
|
|
69
71
|
}
|
|
70
72
|
});
|
|
71
73
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
// 构建可用组件提示词
|
|
75
|
+
if (allComponents.size > 0) {
|
|
76
|
+
prompt += '\n\n## 可用组件\n\n以下是项目中可用的组件,请在最终代码中使用这些组件:\n\n';
|
|
77
|
+
allComponents.forEach((component) => {
|
|
78
|
+
const propsInfo = component.props && component.props.length > 0
|
|
79
|
+
? component.props.map((p) => `${p.name}${p.required ? ' (必需)' : ' (可选)'}: ${p.type || 'any'}`).join(', ')
|
|
80
|
+
: '无';
|
|
81
|
+
const libInfo = component.lib ? ` (来自 ${component.lib})` : ' (项目组件)';
|
|
82
|
+
const descInfo = component.description ? `\n- **描述**: ${component.description}` : '';
|
|
83
|
+
prompt += `### ${component.name}${libInfo}\n`;
|
|
84
|
+
prompt += `- **文件路径**: ${component.path}\n`;
|
|
85
|
+
prompt += `- **导入方式**: ${component.import}\n`;
|
|
86
|
+
prompt += `- **Props**: ${propsInfo}${descInfo}\n\n`;
|
|
87
|
+
});
|
|
88
|
+
prompt +=
|
|
89
|
+
'**重要**:在最终写入代码时,请根据设计稿需求合理传递 props 参数。请按照项目路径规范合理引入组件。\n';
|
|
90
|
+
}
|
|
91
|
+
// 构建标记组件提示词
|
|
92
|
+
if (markedComponents.length > 0) {
|
|
93
|
+
prompt += '\n\n## 组件标记任务\n\n';
|
|
94
|
+
prompt += '以下组件已被用户标记为可复用组件,**请在写入代码后调用 `mark_components` 工具保存这些组件信息**:\n\n';
|
|
95
|
+
prompt += '| 组件名 | signature |\n';
|
|
96
|
+
prompt += '|--------|-----------|';
|
|
97
|
+
markedComponents.forEach((comp) => {
|
|
98
|
+
prompt += `\n| ${comp.name} | ${comp.signature} |`;
|
|
99
|
+
});
|
|
100
|
+
prompt += '\n\n调用示例:\n';
|
|
101
|
+
prompt += '```json\n';
|
|
102
|
+
prompt += JSON.stringify({
|
|
103
|
+
components: markedComponents.map((comp) => ({
|
|
104
|
+
name: comp.name,
|
|
105
|
+
path: '按实际写入路径填写',
|
|
106
|
+
signature: comp.signature,
|
|
107
|
+
import: '按实际写入路径填写导入语句',
|
|
108
|
+
description: '组件的功能描述',
|
|
109
|
+
})),
|
|
110
|
+
}, null, 2);
|
|
111
|
+
prompt += '\n```\n\n';
|
|
112
|
+
prompt += '**重要**:组件标记后可在后续转码中自动匹配复用\n';
|
|
74
113
|
}
|
|
75
|
-
let prompt = '\n\n## 可用组件\n\n以下是项目中可用的组件,请在最终代码中使用这些组件:\n\n';
|
|
76
|
-
allComponents.forEach((component) => {
|
|
77
|
-
const propsInfo = component.props && component.props.length > 0
|
|
78
|
-
? component.props.map((p) => `${p.name}${p.required ? ' (必需)' : ' (可选)'}: ${p.type || 'any'}`).join(', ')
|
|
79
|
-
: '无';
|
|
80
|
-
const libInfo = component.lib ? ` (来自 ${component.lib})` : ' (项目组件)';
|
|
81
|
-
const descInfo = component.description ? `\n- **描述**: ${component.description}` : '';
|
|
82
|
-
prompt += `### ${component.name}${libInfo}\n`;
|
|
83
|
-
prompt += `- **文件路径**: ${component.path}\n`;
|
|
84
|
-
prompt += `- **导入方式**: ${component.import}\n`;
|
|
85
|
-
prompt += `- **Props**: ${propsInfo}${descInfo}\n\n`;
|
|
86
|
-
});
|
|
87
|
-
prompt +=
|
|
88
|
-
'**重要**:在最终写入代码时,请根据设计稿需求合理传递 props 参数。请按照项目路径规范合理引入组件。\n';
|
|
89
114
|
return prompt;
|
|
90
115
|
}
|
|
91
116
|
/**
|
|
@@ -100,8 +125,8 @@ export function buildPlaceholderPrompt(placeholders) {
|
|
|
100
125
|
let prompt = '\n\n## 子组件占位符\n\n**重要**:以下占位符代表已实现的子组件,请转换格式但不要重新实现其内部逻辑。\n\n';
|
|
101
126
|
placeholders.forEach((placeholder) => {
|
|
102
127
|
const kebabName = placeholder.name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
103
|
-
prompt += `- \`<${kebabName}
|
|
128
|
+
prompt += `- \`<${kebabName}>\`\n`;
|
|
104
129
|
});
|
|
105
|
-
prompt += '\n将 HTML
|
|
130
|
+
prompt += '\n将 HTML 标签转换为真实组件调用,但不需要添加对应的 import 语句,保持其在布局中的位置。\n';
|
|
106
131
|
return prompt;
|
|
107
132
|
}
|
|
@@ -135,7 +135,7 @@ export function injectChildPlaceholders(nodeList, childResults, nestingInfo, par
|
|
|
135
135
|
* @returns 采样结果
|
|
136
136
|
*/
|
|
137
137
|
export async function sampleSingleGroup(group, nodeList, config) {
|
|
138
|
-
const { d2cNodeList, imageMap, convertConfig, chunkPrompt, mcpServer, componentMappings, codeSnippets, componentContexts } = config;
|
|
138
|
+
const { d2cNodeList, imageMap, convertConfig, frameworkPrompt, chunkPrompt, mcpServer, componentMappings, codeSnippets, componentContexts } = config;
|
|
139
139
|
Logger.log(`开始采样组 ${group.groupIndex}, 元素数量: ${group.elements.length}, nodeList 长度: ${nodeList.length}`);
|
|
140
140
|
// 检查组件映射
|
|
141
141
|
const mappingResult = handleComponentMapping(group, componentMappings);
|
|
@@ -184,7 +184,8 @@ export async function sampleSingleGroup(group, nodeList, config) {
|
|
|
184
184
|
role: 'user',
|
|
185
185
|
content: {
|
|
186
186
|
type: 'text',
|
|
187
|
-
text:
|
|
187
|
+
text: frameworkPrompt +
|
|
188
|
+
chunkPrompt.replace(/{FRAMEWORK}/g, convertConfig.convertSetting?.framework || '') +
|
|
188
189
|
userPromptText +
|
|
189
190
|
componentContextPrompt +
|
|
190
191
|
placeholderPrompt,
|
|
@@ -203,18 +204,7 @@ export async function sampleSingleGroup(group, nodeList, config) {
|
|
|
203
204
|
const { code: chunkCode, componentName: sampledName } = extractCodeAndComponents(text)[0];
|
|
204
205
|
Logger.log(`组 ${group.groupIndex} 采样成功,组件名称: ${sampledName}, 代码长度: ${chunkCode.length}`);
|
|
205
206
|
group.name = sampledName;
|
|
206
|
-
|
|
207
|
-
let finalCode = chunkCode;
|
|
208
|
-
if (group.marked && group.screenshot?.hash) {
|
|
209
|
-
const document = `/**
|
|
210
|
-
* ${sampledName} 组件
|
|
211
|
-
* signature: ${group.screenshot.hash}
|
|
212
|
-
*/
|
|
213
|
-
`;
|
|
214
|
-
finalCode = document + chunkCode;
|
|
215
|
-
Logger.log(`组 ${group.groupIndex} 已标记,注入 signature: ${group.screenshot.hash}, 组件名: ${sampledName}`);
|
|
216
|
-
}
|
|
217
|
-
codeSnippets.push(finalCode);
|
|
207
|
+
codeSnippets.push(chunkCode);
|
|
218
208
|
return {
|
|
219
209
|
code: chunkCode,
|
|
220
210
|
componentName: sampledName,
|
|
@@ -224,15 +214,7 @@ export async function sampleSingleGroup(group, nodeList, config) {
|
|
|
224
214
|
}
|
|
225
215
|
catch (e) {
|
|
226
216
|
Logger.log(`组 ${group.groupIndex} 调用采样出错:`, e);
|
|
227
|
-
|
|
228
|
-
// 如果分组被标记且有截图,注入 signature 注释
|
|
229
|
-
if (group.marked && group.screenshot?.hash) {
|
|
230
|
-
const document = `/**
|
|
231
|
-
* signature: ${group.screenshot.hash}
|
|
232
|
-
*/
|
|
233
|
-
`;
|
|
234
|
-
fallbackCode = document + fallbackCode;
|
|
235
|
-
}
|
|
217
|
+
const fallbackCode = userPromptText + componentContextPrompt + placeholderPrompt + `\n // Group${group.groupIndex + 1} \n` + codeWithCustomName;
|
|
236
218
|
codeSnippets.push(fallbackCode);
|
|
237
219
|
Logger.log(`组 ${group.groupIndex} 使用降级代码,长度: ${fallbackCode.length}`);
|
|
238
220
|
return {
|
package/dist/build/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { ConfigManager, defaultConfigData, } from './config-manager/index.js';
|
|
|
7
7
|
import { cleanup as cleanupTauri } from './interceptor/client.js';
|
|
8
8
|
import { cleanup as cleanupVSCode, getUserInputFromVSCode, isVSCodeAvailable } from './interceptor/vscode.js';
|
|
9
9
|
import { cleanup as cleanupWeb, getUserInput } from './interceptor/web.js';
|
|
10
|
-
import { loadConfig, startHttpServer, stopHttpServer } from './server.js';
|
|
10
|
+
import { loadConfig, startHttpServer, stopHttpServer, generateComponentId, stopSocketServer } from './server.js';
|
|
11
11
|
import { FileManager } from './utils/file-manager.js';
|
|
12
12
|
import { updateImageMapIfNeeded } from './utils/update.js';
|
|
13
13
|
import { Logger } from './utils/logger.js';
|
|
@@ -20,6 +20,32 @@ import { promises as fs } from 'fs';
|
|
|
20
20
|
import * as path from 'path';
|
|
21
21
|
import { fileURLToPath } from 'url';
|
|
22
22
|
import { extractCodeAndComponents } from './utils/extract.js';
|
|
23
|
+
import { trackToolCall } from './utils/tj.js';
|
|
24
|
+
/**
|
|
25
|
+
* 启动http & socket监听
|
|
26
|
+
* @param init 是否是初始化,初始化时需要连接stdio transport
|
|
27
|
+
*/
|
|
28
|
+
export async function startListening(init = false) {
|
|
29
|
+
const isStdioMode = process.env.NODE_ENV === 'cli' || process.argv.includes('--stdio');
|
|
30
|
+
const configManager = new ConfigManager('d2c-mcp');
|
|
31
|
+
const port = await getAvailablePort();
|
|
32
|
+
if (isStdioMode) {
|
|
33
|
+
Logger.log(`Stdio模式启动`);
|
|
34
|
+
await loadConfig(mcpServer, configManager);
|
|
35
|
+
if (init) {
|
|
36
|
+
const transport = new StdioServerTransport();
|
|
37
|
+
await mcpServer.connect(transport);
|
|
38
|
+
}
|
|
39
|
+
// 启动 HTTP 服务器 - 仅启用web服务
|
|
40
|
+
await startHttpServer(port, mcpServer, configManager, false);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// 启动 HTTP 服务器 - 这是核心功能,不依赖 VSCode 日志
|
|
44
|
+
await startHttpServer(port, mcpServer, configManager, true);
|
|
45
|
+
}
|
|
46
|
+
// 服务器启动成功后再尝试使用 Logger(包含 VSCode 日志)
|
|
47
|
+
Logger.log(`Server started successfully on port ${port}!`);
|
|
48
|
+
}
|
|
23
49
|
export class D2CMcpServer extends McpServer {
|
|
24
50
|
figmaApiKey = '';
|
|
25
51
|
baseURL = '';
|
|
@@ -53,6 +79,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
53
79
|
local: z.boolean().optional().describe('Whether to use local data cache, default is false'),
|
|
54
80
|
}, async (args) => {
|
|
55
81
|
try {
|
|
82
|
+
trackToolCall('d2c_figma');
|
|
56
83
|
Logger.log(`收到工具调用参数:`, JSON.stringify(args, null, 2));
|
|
57
84
|
const { fileKey, nodeId, depth, local } = args;
|
|
58
85
|
let config = await configManager.load();
|
|
@@ -71,6 +98,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
71
98
|
catch (error) {
|
|
72
99
|
Logger.log('获取根目录时出错:', error);
|
|
73
100
|
}
|
|
101
|
+
await startListening();
|
|
74
102
|
// 没有配置figmaApiKey,无法预览,直接唤起配置页面
|
|
75
103
|
if (!config.mcp?.figmaApiKey) {
|
|
76
104
|
hasLaunchWebview = true;
|
|
@@ -154,6 +182,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
154
182
|
else {
|
|
155
183
|
configDataString = await getUserInput({ fileKey: fileKey, nodeId: nodeId });
|
|
156
184
|
}
|
|
185
|
+
console.log('收到网页提交数据', configDataString);
|
|
157
186
|
if (!configDataString || configDataString.trim() === '') {
|
|
158
187
|
throw new Error('未提供有效的转码配置');
|
|
159
188
|
}
|
|
@@ -203,7 +232,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
203
232
|
const savedPromptSetting = await fileManager.loadPromptSetting(fileKey, nodeId);
|
|
204
233
|
const convertConfig = config.fileConfigs?.[fileKey] || defaultConfigData;
|
|
205
234
|
// 获取提示词,优先使用用户保存的提示词,否则使用默认提示词
|
|
206
|
-
const frameworkPrompt = savedPromptSetting?.frameworkGuidePrompt;
|
|
235
|
+
const frameworkPrompt = savedPromptSetting?.enableFrameworkGuide ? savedPromptSetting?.frameworkGuidePrompt : '';
|
|
207
236
|
const chunkPrompt = savedPromptSetting?.chunkOptimizePrompt || chunkOptimizeCodePrompt;
|
|
208
237
|
const aggregationPrompt = savedPromptSetting?.aggregationOptimizePrompt || aggregationOptimizeCodePrompt;
|
|
209
238
|
const finalPrompt = savedPromptSetting?.finalOptimizePrompt || finalOptimizeCodePrompt;
|
|
@@ -230,7 +259,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
230
259
|
isSupportSampling = await processSampling(groupsData, samplingConfig);
|
|
231
260
|
// 采样完成后,更新组件名并保存到文件
|
|
232
261
|
try {
|
|
233
|
-
const groupsDataToSave = groupsData.map(group => {
|
|
262
|
+
const groupsDataToSave = groupsData.map((group) => {
|
|
234
263
|
// 将提取的组件名更新到 componentName(仅标记的分组)
|
|
235
264
|
if (group.marked && group.name) {
|
|
236
265
|
group.componentName = group.name;
|
|
@@ -241,7 +270,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
241
270
|
return groupWithoutNodeList;
|
|
242
271
|
});
|
|
243
272
|
await fileManager.saveGroupsData(fileKey, nodeId, groupsDataToSave);
|
|
244
|
-
Logger.log(`已保存更新后的 groupsData,包含 ${groupsData.filter(g => g.marked).length} 个标记的组件`);
|
|
273
|
+
Logger.log(`已保存更新后的 groupsData,包含 ${groupsData.filter((g) => g.marked).length} 个标记的组件`);
|
|
245
274
|
}
|
|
246
275
|
catch (error) {
|
|
247
276
|
Logger.error(`保存 groupsData 失败:`, error);
|
|
@@ -322,7 +351,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
322
351
|
role: 'user',
|
|
323
352
|
content: {
|
|
324
353
|
type: 'text',
|
|
325
|
-
text: aggregationPrompt.replace(/{FRAMEWORK}/g, convertConfig.convertSetting?.framework || '') + placeholderPrompt,
|
|
354
|
+
text: frameworkPrompt + aggregationPrompt.replace(/{FRAMEWORK}/g, convertConfig.convertSetting?.framework || '') + placeholderPrompt,
|
|
326
355
|
},
|
|
327
356
|
},
|
|
328
357
|
{
|
|
@@ -352,8 +381,21 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
352
381
|
Logger.log('处理完成,生成的代码片段数量:', codeSnippets.length);
|
|
353
382
|
Logger.log('已映射的组件数量:', componentMappings.length);
|
|
354
383
|
Logger.log('组件上下文数量:', componentContexts.length);
|
|
355
|
-
//
|
|
356
|
-
const
|
|
384
|
+
// 收集标记的组件(用于提示 LLM 调用 mark_components)
|
|
385
|
+
const markedComponents = [];
|
|
386
|
+
if (groupsData && groupsData.length > 0) {
|
|
387
|
+
groupsData.forEach((group) => {
|
|
388
|
+
if (group.marked && group.screenshot?.hash && group.name) {
|
|
389
|
+
markedComponents.push({
|
|
390
|
+
name: group.name,
|
|
391
|
+
signature: group.screenshot.hash,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
Logger.log(`收集到 ${markedComponents.length} 个标记的组件`);
|
|
396
|
+
}
|
|
397
|
+
// 构建组件映射提示词(包含组件映射、组件上下文和标记组件)
|
|
398
|
+
const componentMappingPrompt = buildComponentMappingPrompt(componentMappings, componentContexts, markedComponents);
|
|
357
399
|
// 使用提示词(已包含默认值)
|
|
358
400
|
return {
|
|
359
401
|
content: [
|
|
@@ -387,6 +429,112 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
387
429
|
};
|
|
388
430
|
}
|
|
389
431
|
});
|
|
432
|
+
// 标记并保存组件到 components.json
|
|
433
|
+
mcpServer.tool('mark_components', 'Mark and save components to project components.json for future reuse and matching', {
|
|
434
|
+
components: z
|
|
435
|
+
.array(z.object({
|
|
436
|
+
name: z.string().describe('Component name in PascalCase'),
|
|
437
|
+
path: z.string().describe('Component file path relative to project root'),
|
|
438
|
+
signature: z.string().describe('Component signature (SHA256 hash) for matching'),
|
|
439
|
+
type: z.enum(['button', 'input', 'card', 'text', 'image', 'container', 'list', 'custom']).optional(),
|
|
440
|
+
description: z.string().optional(),
|
|
441
|
+
import: z.string().optional().describe('Import statement for the component'),
|
|
442
|
+
props: z
|
|
443
|
+
.array(z.object({
|
|
444
|
+
name: z.string(),
|
|
445
|
+
type: z.string().optional(),
|
|
446
|
+
required: z.boolean().optional(),
|
|
447
|
+
description: z.string().optional(),
|
|
448
|
+
}))
|
|
449
|
+
.optional(),
|
|
450
|
+
}))
|
|
451
|
+
.describe('Array of components to mark and save'),
|
|
452
|
+
}, async (args) => {
|
|
453
|
+
try {
|
|
454
|
+
const { components } = args;
|
|
455
|
+
// 1. 设置 workspaceRoot
|
|
456
|
+
let root = './';
|
|
457
|
+
try {
|
|
458
|
+
const rootRes = await mcpServer.server.listRoots();
|
|
459
|
+
Logger.log('获取根目录:', rootRes);
|
|
460
|
+
root = rootRes.roots[0]?.uri?.slice(7) || './';
|
|
461
|
+
// 设置 fileManager 的工作目录根路径
|
|
462
|
+
fileManager.setWorkspaceRoot(root);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
Logger.log('获取根目录时出错:', error);
|
|
466
|
+
}
|
|
467
|
+
// 2. 加载现有组件
|
|
468
|
+
const existingComponents = await fileManager.loadComponentsDatabase();
|
|
469
|
+
// 3. 构建 signature 索引(用于去重)
|
|
470
|
+
const signatureIndex = new Map();
|
|
471
|
+
existingComponents.forEach((comp, index) => {
|
|
472
|
+
if (comp.signature) {
|
|
473
|
+
signatureIndex.set(comp.signature, index);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
// 4. 处理新组件(合并逻辑)
|
|
477
|
+
const now = new Date().toISOString();
|
|
478
|
+
let addedCount = 0;
|
|
479
|
+
let updatedCount = 0;
|
|
480
|
+
for (const comp of components) {
|
|
481
|
+
const existingIndex = signatureIndex.get(comp.signature);
|
|
482
|
+
const storedComponent = {
|
|
483
|
+
id: generateComponentId(),
|
|
484
|
+
name: comp.name,
|
|
485
|
+
type: comp.type || 'custom',
|
|
486
|
+
path: comp.path,
|
|
487
|
+
import: comp.import || `import { ${comp.name} } from '${comp.path}'`,
|
|
488
|
+
props: (comp.props || []).map((p) => ({
|
|
489
|
+
name: p.name,
|
|
490
|
+
type: p.type || 'any',
|
|
491
|
+
required: p.required || false,
|
|
492
|
+
description: p.description,
|
|
493
|
+
})),
|
|
494
|
+
description: comp.description,
|
|
495
|
+
signature: comp.signature,
|
|
496
|
+
};
|
|
497
|
+
if (existingIndex !== undefined) {
|
|
498
|
+
// 更新已存在的组件(保留原始导入时间)
|
|
499
|
+
const originalImportedAt = existingComponents[existingIndex].importedAt;
|
|
500
|
+
existingComponents[existingIndex] = {
|
|
501
|
+
...storedComponent,
|
|
502
|
+
importedAt: originalImportedAt,
|
|
503
|
+
};
|
|
504
|
+
updatedCount++;
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// 添加新组件
|
|
508
|
+
existingComponents.push(storedComponent);
|
|
509
|
+
signatureIndex.set(comp.signature, existingComponents.length - 1);
|
|
510
|
+
addedCount++;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// 5. 保存到文件
|
|
514
|
+
await fileManager.saveComponentsDatabase(existingComponents);
|
|
515
|
+
// 6. 返回结果
|
|
516
|
+
Logger.log(`mark_components: 新增 ${addedCount} 个,更新 ${updatedCount} 个,共 ${existingComponents.length} 个组件`);
|
|
517
|
+
return {
|
|
518
|
+
content: [
|
|
519
|
+
{
|
|
520
|
+
type: 'text',
|
|
521
|
+
text: `✅ 组件标记完成:新增 ${addedCount} 个,更新 ${updatedCount} 个,共 ${existingComponents.length} 个组件`,
|
|
522
|
+
},
|
|
523
|
+
],
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
Logger.error('mark_components 处理失败:', error);
|
|
528
|
+
return {
|
|
529
|
+
content: [
|
|
530
|
+
{
|
|
531
|
+
type: 'text',
|
|
532
|
+
text: `❌ 标记组件失败: ${error instanceof Error ? error.message : String(error)}`,
|
|
533
|
+
},
|
|
534
|
+
],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
});
|
|
390
538
|
// 启动 HTTP 服务器
|
|
391
539
|
async function main() {
|
|
392
540
|
// 检查是否是版本命令
|
|
@@ -760,7 +908,7 @@ async function main() {
|
|
|
760
908
|
success: true,
|
|
761
909
|
framework: frameworkName,
|
|
762
910
|
configPath: frameworkPath,
|
|
763
|
-
created: !configExists
|
|
911
|
+
created: !configExists,
|
|
764
912
|
}, null, 2));
|
|
765
913
|
}
|
|
766
914
|
else {
|
|
@@ -778,7 +926,7 @@ async function main() {
|
|
|
778
926
|
if (isJsonOutput) {
|
|
779
927
|
console.log(JSON.stringify({
|
|
780
928
|
success: false,
|
|
781
|
-
error: error.toString()
|
|
929
|
+
error: error.toString(),
|
|
782
930
|
}, null, 2));
|
|
783
931
|
}
|
|
784
932
|
else {
|
|
@@ -791,24 +939,8 @@ async function main() {
|
|
|
791
939
|
try {
|
|
792
940
|
// 先输出基本启动信息到控制台,确保即使 VSCode 日志失败也能看到启动过程
|
|
793
941
|
console.log(`[${new Date().toISOString()}] Starting Figma transcoding interceptor MCP server...`);
|
|
794
|
-
const port = await getAvailablePort();
|
|
795
942
|
// Create config manager instance to pass to server
|
|
796
|
-
|
|
797
|
-
const isStdioMode = process.env.NODE_ENV === 'cli' || process.argv.includes('--stdio');
|
|
798
|
-
if (isStdioMode) {
|
|
799
|
-
Logger.log(`Stdio模式启动`);
|
|
800
|
-
await loadConfig(mcpServer, configManager);
|
|
801
|
-
const transport = new StdioServerTransport();
|
|
802
|
-
await mcpServer.connect(transport);
|
|
803
|
-
// 启动 HTTP 服务器 - 仅启用web服务
|
|
804
|
-
await startHttpServer(port, mcpServer, configManager, false);
|
|
805
|
-
}
|
|
806
|
-
else {
|
|
807
|
-
// 启动 HTTP 服务器 - 这是核心功能,不依赖 VSCode 日志
|
|
808
|
-
await startHttpServer(port, mcpServer, configManager, true);
|
|
809
|
-
}
|
|
810
|
-
// 服务器启动成功后再尝试使用 Logger(包含 VSCode 日志)
|
|
811
|
-
Logger.log(`Server started successfully on port ${port}!`);
|
|
943
|
+
await startListening(true);
|
|
812
944
|
}
|
|
813
945
|
catch (error) {
|
|
814
946
|
// 确保错误信息能够输出,即使 VSCode 日志服务不可用
|
|
@@ -852,6 +984,7 @@ process.on('SIGTERM', async () => {
|
|
|
852
984
|
catch (error) {
|
|
853
985
|
// 忽略日志错误,确保清理过程继续
|
|
854
986
|
}
|
|
987
|
+
await stopSocketServer();
|
|
855
988
|
cleanupTauri();
|
|
856
989
|
cleanupWeb();
|
|
857
990
|
cleanupVSCode();
|