sloth-d2c-mcp 1.0.4-beta91 → 1.0.4-beta93
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 +50 -1
- package/dist/build/core/sampling.js +13 -8
- package/dist/build/index.js +8 -4
- package/dist/build/server.js +228 -72
- package/dist/build/utils/file-manager.js +62 -11
- package/dist/build/utils/prompt-parser.js +46 -0
- package/dist/interceptor-web/dist/build-report.json +5 -5
- package/dist/interceptor-web/dist/detail.html +1 -1
- package/dist/interceptor-web/dist/index.html +1 -1
- package/package.json +3 -3
|
@@ -14,8 +14,12 @@ export function buildComponentContext(group) {
|
|
|
14
14
|
group.componentContext.forEach((comp) => {
|
|
15
15
|
componentMap.set(comp.name, comp);
|
|
16
16
|
});
|
|
17
|
-
// 替换 @组件名
|
|
17
|
+
// 替换 @组件名 为组件详细信息(只替换组件,不替换 Markdown 文件)
|
|
18
18
|
processedUserPrompt = processedUserPrompt.replace(mentionRegex, (match, display, id) => {
|
|
19
|
+
// 如果 id 包含路径分隔符,说明是文件,保持原样
|
|
20
|
+
if (id.includes('/') || id.includes('\\')) {
|
|
21
|
+
return match;
|
|
22
|
+
}
|
|
19
23
|
const component = componentMap.get(id || display);
|
|
20
24
|
if (component) {
|
|
21
25
|
const libInfo = component.lib ? ` (${component.lib})` : '';
|
|
@@ -37,6 +41,51 @@ export function buildComponentContext(group) {
|
|
|
37
41
|
}
|
|
38
42
|
return { processedUserPrompt, componentContextPrompt };
|
|
39
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* 处理 Markdown 文件上下文:解析 @文件名 并构建文档上下文提示词
|
|
46
|
+
* @param group 分组数据
|
|
47
|
+
* @returns 处理后的用户提示词和 Markdown 上下文提示词
|
|
48
|
+
*/
|
|
49
|
+
export function buildMarkdownContext(group) {
|
|
50
|
+
let processedUserPrompt = group.userPrompt || '';
|
|
51
|
+
let markdownContextPrompt = '';
|
|
52
|
+
if (group.markdownContext && Array.isArray(group.markdownContext) && group.markdownContext.length > 0) {
|
|
53
|
+
// 解析 userPrompt 中的 @文件名(react-mentions 格式:@[文件名](文件路径))
|
|
54
|
+
const mentionRegex = /@\[([^\]]+)\]\(([^)]+)\)/g;
|
|
55
|
+
const markdownMap = new Map();
|
|
56
|
+
// 构建 Markdown 文件映射表
|
|
57
|
+
group.markdownContext.forEach((file) => {
|
|
58
|
+
markdownMap.set(file.path, file);
|
|
59
|
+
});
|
|
60
|
+
// 替换 @文件名 为文件详细信息(只替换文件,不替换组件)
|
|
61
|
+
processedUserPrompt = processedUserPrompt.replace(mentionRegex, (match, _display, id) => {
|
|
62
|
+
// 如果 id 不包含路径分隔符,说明是组件名,保持原样
|
|
63
|
+
if (!id.includes('/') && !id.includes('\\')) {
|
|
64
|
+
return match;
|
|
65
|
+
}
|
|
66
|
+
const markdownFile = markdownMap.get(id);
|
|
67
|
+
if (markdownFile) {
|
|
68
|
+
return `[文档: ${markdownFile.name}]`;
|
|
69
|
+
}
|
|
70
|
+
return match; // 如果找不到文件,保持原样
|
|
71
|
+
});
|
|
72
|
+
// 构建 Markdown 上下文说明
|
|
73
|
+
markdownContextPrompt = '\n\n## 参考文档\n\n以下是用户提供的参考文档,请在代码生成时参考这些文档中的规范、示例和最佳实践:\n\n';
|
|
74
|
+
group.markdownContext.forEach((file) => {
|
|
75
|
+
markdownContextPrompt += `### 📄 ${file.name}\n\n`;
|
|
76
|
+
if (file.content) {
|
|
77
|
+
markdownContextPrompt += '```markdown\n';
|
|
78
|
+
markdownContextPrompt += file.content;
|
|
79
|
+
markdownContextPrompt += '\n```\n\n';
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
markdownContextPrompt += `**文件路径**: ${file.path}\n\n`;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
markdownContextPrompt += '**重要**:请仔细阅读上述参考文档,遵循文档中的编码规范、API 使用方式和最佳实践,确保生成的代码符合项目要求。\n';
|
|
86
|
+
}
|
|
87
|
+
return { processedUserPrompt, markdownContextPrompt };
|
|
88
|
+
}
|
|
40
89
|
/**
|
|
41
90
|
* 构建用户提示词文本
|
|
42
91
|
* @param userPrompt 用户提示词
|
|
@@ -2,7 +2,7 @@ import { getBoundingBox, getCode } from 'sloth-d2c-node/convert';
|
|
|
2
2
|
import { Logger } from '../utils/logger.js';
|
|
3
3
|
import { resetNodeListPosition, replaceImageSrc } from '../utils/utils.js';
|
|
4
4
|
import { extractCodeAndComponents } from '../utils/extract.js';
|
|
5
|
-
import { buildComponentContext, buildUserPromptText, buildPlaceholderPrompt } from './prompt-builder.js';
|
|
5
|
+
import { buildComponentContext, buildMarkdownContext, buildUserPromptText, buildPlaceholderPrompt } from './prompt-builder.js';
|
|
6
6
|
/**
|
|
7
7
|
* 构建嵌套结构映射
|
|
8
8
|
* @param groupsData 分组数据数组
|
|
@@ -46,9 +46,10 @@ export function buildNestingStructure(groupsData) {
|
|
|
46
46
|
* @param componentMappings 组件映射数组
|
|
47
47
|
* @param imageMap 图片映射
|
|
48
48
|
* @param convertConfig 转换配置
|
|
49
|
+
* @param isLocalMode 是否为 local 模式
|
|
49
50
|
* @returns 采样结果或 null
|
|
50
51
|
*/
|
|
51
|
-
export async function handleComponentMapping(group, nodeList, componentMappings, imageMap, convertConfig) {
|
|
52
|
+
export async function handleComponentMapping(group, nodeList, componentMappings, imageMap, convertConfig, isLocalMode) {
|
|
52
53
|
if (group.componentMapping) {
|
|
53
54
|
const componentName = group.componentMapping.name || `Group${group.groupIndex + 1}`;
|
|
54
55
|
group.name = componentName;
|
|
@@ -57,6 +58,7 @@ export async function handleComponentMapping(group, nodeList, componentMappings,
|
|
|
57
58
|
const code = await getCode({
|
|
58
59
|
d2cNodeList: resetNodeList,
|
|
59
60
|
config: convertConfig,
|
|
61
|
+
options: { isLocalMode },
|
|
60
62
|
});
|
|
61
63
|
const replacedCode = replaceImageSrc(code, imageMap);
|
|
62
64
|
componentMappings.push({
|
|
@@ -147,10 +149,10 @@ export function injectChildPlaceholders(nodeList, childResults, nestingInfo, par
|
|
|
147
149
|
* @returns 采样结果
|
|
148
150
|
*/
|
|
149
151
|
export async function sampleSingleGroup(group, nodeList, config) {
|
|
150
|
-
const { d2cNodeList, imageMap, convertConfig, frameworkPrompt, chunkPrompt, mcpServer, componentMappings, codeSnippets, componentContexts, isSupportSampling } = config;
|
|
152
|
+
const { d2cNodeList, imageMap, convertConfig, frameworkPrompt, chunkPrompt, mcpServer, componentMappings, codeSnippets, componentContexts, isSupportSampling, isLocalMode } = config;
|
|
151
153
|
Logger.log(`开始采样组 ${group.groupIndex}, 元素数量: ${group.elements.length}, nodeList 长度: ${nodeList.length}`);
|
|
152
154
|
// 检查组件映射
|
|
153
|
-
const mappingResult = await handleComponentMapping(group, nodeList, componentMappings, imageMap, convertConfig);
|
|
155
|
+
const mappingResult = await handleComponentMapping(group, nodeList, componentMappings, imageMap, convertConfig, isLocalMode);
|
|
154
156
|
if (mappingResult) {
|
|
155
157
|
Logger.log(`组 ${group.groupIndex} 已映射组件,跳过采样: ${mappingResult.componentName}`);
|
|
156
158
|
return mappingResult;
|
|
@@ -162,14 +164,16 @@ export async function sampleSingleGroup(group, nodeList, config) {
|
|
|
162
164
|
const code = await getCode({
|
|
163
165
|
d2cNodeList: resetNodeList,
|
|
164
166
|
config: convertConfig,
|
|
167
|
+
options: { isLocalMode },
|
|
165
168
|
});
|
|
166
169
|
Logger.log(`组 ${group.groupIndex} 代码生成完成,代码长度: ${code.length}`);
|
|
167
170
|
// 使用 imageMap 的 path 替换 code 中对应的 src
|
|
168
171
|
const replacedCode = replaceImageSrc(code, imageMap);
|
|
169
172
|
const componentName = `Group${group.groupIndex + 1}`;
|
|
170
173
|
const codeWithCustomName = replacedCode.replace(/\bApp\b/g, componentName);
|
|
171
|
-
//
|
|
172
|
-
const { processedUserPrompt, componentContextPrompt } = buildComponentContext(group);
|
|
174
|
+
// 处理组件上下文和 Markdown 上下文
|
|
175
|
+
const { processedUserPrompt: promptAfterComponent, componentContextPrompt } = buildComponentContext(group);
|
|
176
|
+
const { processedUserPrompt, markdownContextPrompt } = buildMarkdownContext({ ...group, userPrompt: promptAfterComponent });
|
|
173
177
|
const userPromptText = buildUserPromptText(processedUserPrompt);
|
|
174
178
|
// 收集组件上下文信息
|
|
175
179
|
if (group.componentContext && Array.isArray(group.componentContext) && group.componentContext.length > 0) {
|
|
@@ -186,8 +190,8 @@ export async function sampleSingleGroup(group, nodeList, config) {
|
|
|
186
190
|
.map((node) => ({
|
|
187
191
|
name: node.name || 'Unknown',
|
|
188
192
|
}));
|
|
189
|
-
const placeholderPrompt = buildPlaceholderPrompt(placeholders);
|
|
190
|
-
Logger.log(`组 ${group.groupIndex} 开始调用 AI 采样,代码长度: ${codeWithCustomName.length}, 是否有用户提示: ${!!group.userPrompt}, 是否有组件上下文: ${!!(group.componentContext && group.componentContext.length > 0)}, 占位符数量: ${placeholders.length}`);
|
|
193
|
+
const placeholderPrompt = buildPlaceholderPrompt(placeholders, isSupportSampling);
|
|
194
|
+
Logger.log(`组 ${group.groupIndex} 开始调用 AI 采样,代码长度: ${codeWithCustomName.length}, 是否有用户提示: ${!!group.userPrompt}, 是否有组件上下文: ${!!(group.componentContext && group.componentContext.length > 0)}, 是否有 Markdown 上下文: ${!!(group.markdownContext && group.markdownContext.length > 0)}, 占位符数量: ${placeholders.length}`);
|
|
191
195
|
// 执行采样
|
|
192
196
|
try {
|
|
193
197
|
if (!isSupportSampling)
|
|
@@ -202,6 +206,7 @@ export async function sampleSingleGroup(group, nodeList, config) {
|
|
|
202
206
|
chunkPrompt.replace(/{FRAMEWORK}/g, convertConfig.convertSetting?.framework || '') +
|
|
203
207
|
userPromptText +
|
|
204
208
|
componentContextPrompt +
|
|
209
|
+
markdownContextPrompt +
|
|
205
210
|
placeholderPrompt,
|
|
206
211
|
},
|
|
207
212
|
},
|
package/dist/build/index.js
CHANGED
|
@@ -189,11 +189,13 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
189
189
|
await fileManager.saveFile(fileKey, nodeId, 'imageMap.json', flatted.stringify(imageMap));
|
|
190
190
|
// 保存figma绝对定位代码
|
|
191
191
|
await fileManager.saveAbsoluteHtml(fileKey, nodeId, absoluteHtml);
|
|
192
|
+
await fileManager.saveAbsoluteHtml(fileKey, nodeId, absoluteHtml, true);
|
|
192
193
|
}
|
|
193
194
|
else {
|
|
194
195
|
d2cNodeList = flatted.parse(await fileManager.loadFile(fileKey, nodeId, 'nodeList.json'));
|
|
195
196
|
imageMap = flatted.parse(await fileManager.loadFile(fileKey, nodeId, 'imageMap.json'));
|
|
196
197
|
absoluteHtml = await fileManager.loadAbsoluteHtml(fileKey, nodeId);
|
|
198
|
+
await fileManager.saveAbsoluteHtml(fileKey, nodeId, absoluteHtml, true);
|
|
197
199
|
console.log('cache load data');
|
|
198
200
|
}
|
|
199
201
|
console.log('d2cNodeList', d2cNodeList);
|
|
@@ -290,7 +292,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
290
292
|
const savedPromptSetting = await fileManager.loadPromptSetting(fileKey, nodeId);
|
|
291
293
|
const convertConfig = config.fileConfigs?.[fileKey] || defaultConfigData;
|
|
292
294
|
// 获取提示词,优先使用用户保存的提示词,否则使用默认提示词
|
|
293
|
-
const frameworkPrompt = savedPromptSetting?.
|
|
295
|
+
const frameworkPrompt = savedPromptSetting?.frameworkGuidePrompt || '';
|
|
294
296
|
const chunkPrompt = savedPromptSetting?.chunkOptimizePrompt || chunkOptimizeCodePrompt;
|
|
295
297
|
const aggregationPrompt = savedPromptSetting?.aggregationOptimizePrompt || aggregationOptimizeCodePrompt;
|
|
296
298
|
const finalPrompt = savedPromptSetting?.finalOptimizePrompt || finalOptimizeCodePrompt;
|
|
@@ -314,6 +316,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
314
316
|
codeSnippets,
|
|
315
317
|
componentContexts,
|
|
316
318
|
isSupportSampling,
|
|
319
|
+
isLocalMode: local,
|
|
317
320
|
};
|
|
318
321
|
// 深度遍历采样:针对每个分组生成 AI 相对布局树并转码
|
|
319
322
|
if (groupsData && groupsData?.length > 0) {
|
|
@@ -400,6 +403,7 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
400
403
|
const code = await getCode({
|
|
401
404
|
d2cNodeList: ungroupedNodeList,
|
|
402
405
|
config: convertConfig,
|
|
406
|
+
options: { isLocalMode: local },
|
|
403
407
|
});
|
|
404
408
|
// 使用 imageMap 的 path 替换 code 中对应的 src
|
|
405
409
|
const replacedCode = replaceImageSrc(code, imageMap);
|
|
@@ -429,18 +433,18 @@ mcpServer.tool('d2c_figma', 'Convert Figma Design File to Code', {
|
|
|
429
433
|
}, {
|
|
430
434
|
timeout: 5 * 60 * 1000,
|
|
431
435
|
});
|
|
432
|
-
Logger.log('采样成功 (分组外元素)', text);
|
|
436
|
+
Logger.log('采样成功 (分组外元素)', text, '采样请求代码', replacedCode, '采样提示词', frameworkPrompt + aggregationPrompt.replace(/{FRAMEWORK}/g, convertConfig.convertSetting?.framework || '') + placeholderPrompt);
|
|
433
437
|
const { code: pageCode } = extractCodeAndComponents(text)[0];
|
|
434
438
|
codeSnippets.unshift(pageCode);
|
|
435
439
|
}
|
|
436
440
|
catch (e) {
|
|
437
441
|
Logger.log('调用分组外元素采样出错,降级到传统模式', e);
|
|
438
|
-
codeSnippets.unshift(
|
|
442
|
+
codeSnippets.unshift(replacedCode);
|
|
439
443
|
}
|
|
440
444
|
}
|
|
441
445
|
const onSamplingComplete = await pluginManager.executeHook('onSamplingComplete', {
|
|
442
446
|
codeSnippets,
|
|
443
|
-
imageMap
|
|
447
|
+
imageMap,
|
|
444
448
|
});
|
|
445
449
|
codeSnippets = onSamplingComplete?.codeSnippets || codeSnippets;
|
|
446
450
|
imageMap = onSamplingComplete?.imageMap || imageMap;
|
package/dist/build/server.js
CHANGED
|
@@ -18,8 +18,10 @@ import multer from 'multer';
|
|
|
18
18
|
import { ImageMatcher } from './utils/image-matcher.js';
|
|
19
19
|
import { initOpenCV } from './utils/opencv-loader.js';
|
|
20
20
|
import { extractJson } from './utils/extract.js';
|
|
21
|
-
import { componentAnalysisPrompt, componentAnalysisPromptVue } from 'sloth-d2c-node/convert';
|
|
21
|
+
import { componentAnalysisPrompt, componentAnalysisPromptVue, chunkOptimizeCodePromptKuikly, aggregationOptimizeCodePromptKuikly, finalOptimizeCodePromptKuikly, chunkOptimizeCodePrompt, aggregationOptimizeCodePrompt, finalOptimizeCodePrompt, chunkOptimizeCodePromptVue, aggregationOptimizeCodePromptVue, finalOptimizeCodePromptVue, frameworkPromptKuikly, noSamplingAggregationPrompt, noSamplingAggregationPromptVue, noSamplingAggregationPromptKuikly, chunkOptimizeCodePromptIosOc, aggregationOptimizeCodePromptIosOc, finalOptimizeCodePromptIosOc, noSamplingAggregationPromptIosOc, chunkOptimizeCodePromptIosSwift, aggregationOptimizeCodePromptIosSwift, finalOptimizeCodePromptIosSwift, noSamplingAggregationPromptIosSwift, } from 'sloth-d2c-node/convert';
|
|
22
22
|
import { SocketServer } from './socket-server.js';
|
|
23
|
+
import { getParam } from './utils/utils.js';
|
|
24
|
+
import { listWorkspacePlugins } from './plugin/loader.js';
|
|
23
25
|
/**
|
|
24
26
|
* 生成组件 ID(4位随机字符串)
|
|
25
27
|
*/
|
|
@@ -30,10 +32,6 @@ const upload = multer({
|
|
|
30
32
|
storage,
|
|
31
33
|
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB
|
|
32
34
|
});
|
|
33
|
-
// 导入默认提示词
|
|
34
|
-
import { chunkOptimizeCodePrompt, aggregationOptimizeCodePrompt, finalOptimizeCodePrompt, chunkOptimizeCodePromptVue, aggregationOptimizeCodePromptVue, finalOptimizeCodePromptVue, noSamplingAggregationPrompt, noSamplingAggregationPromptVue, } from 'sloth-d2c-node/convert';
|
|
35
|
-
import { getParam } from './utils/utils.js';
|
|
36
|
-
import { listWorkspacePlugins } from './plugin/loader.js';
|
|
37
35
|
// 保存 HTTP 服务器实例
|
|
38
36
|
export let httpServer = null;
|
|
39
37
|
// 保存 Socket 服务器实例
|
|
@@ -77,24 +75,26 @@ export async function loadConfig(mcpServer, configManagerInstance) {
|
|
|
77
75
|
Logger.warn(`读取配置文件失败: ${error}`);
|
|
78
76
|
}
|
|
79
77
|
}
|
|
78
|
+
// 默认框架列表
|
|
79
|
+
const defaultFrameworks = [
|
|
80
|
+
{ value: 'react', label: 'React' },
|
|
81
|
+
{ value: 'vue', label: 'Vue' },
|
|
82
|
+
{ value: 'ios-oc', label: 'iOS OC' },
|
|
83
|
+
{ value: 'ios-swift', label: 'iOS Swift' },
|
|
84
|
+
{ value: 'kuikly', label: 'Kuikly' },
|
|
85
|
+
];
|
|
80
86
|
// 初始化默认框架配置
|
|
81
87
|
if (!config.frameworks || config.frameworks.length === 0) {
|
|
82
88
|
Logger.log('初始化默认框架配置...');
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
if (!config.frameworks.find((fw) => fw.value === 'react') || !config.frameworks.find((fw) => fw.value === 'vue')) {
|
|
96
|
-
const newFrameworks = config.frameworks.filter((fw) => fw.value !== 'react' || fw.value !== 'vue');
|
|
97
|
-
config.frameworks = [{ value: 'react', label: 'React' }, { value: 'vue', label: 'Vue' }, ...newFrameworks];
|
|
89
|
+
config.frameworks = [...defaultFrameworks];
|
|
90
|
+
await configManager.save(config);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// 检查是否缺少默认框架,补充缺失的
|
|
94
|
+
const existingValues = config.frameworks.map((fw) => fw.value);
|
|
95
|
+
const missingFrameworks = defaultFrameworks.filter((fw) => !existingValues.includes(fw.value));
|
|
96
|
+
if (missingFrameworks.length > 0) {
|
|
97
|
+
config.frameworks = [...config.frameworks, ...missingFrameworks];
|
|
98
98
|
await configManager.save(config);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -135,6 +135,82 @@ export async function loadConfig(mcpServer, configManagerInstance) {
|
|
|
135
135
|
...(vueConfig || {}),
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
|
+
// 检查 ios-oc.json 是否存在,如果不存在则创建
|
|
139
|
+
const iosOcConfigExists = await configManager.frameworkConfigExists('ios-oc');
|
|
140
|
+
if (!iosOcConfigExists) {
|
|
141
|
+
await configManager.saveFrameworkConfig('ios-oc', {
|
|
142
|
+
chunkOptimizePrompt: chunkOptimizeCodePromptIosOc,
|
|
143
|
+
aggregationOptimizePrompt: aggregationOptimizeCodePromptIosOc,
|
|
144
|
+
finalOptimizePrompt: finalOptimizeCodePromptIosOc,
|
|
145
|
+
componentAnalysisPrompt: '',
|
|
146
|
+
noSamplingAggregationPrompt: noSamplingAggregationPromptIosOc,
|
|
147
|
+
});
|
|
148
|
+
const iosOcConfigPath = configManager.getFrameworkConfigPath('ios-oc');
|
|
149
|
+
Logger.log(`已创建默认 ios-oc 配置文件: ${iosOcConfigPath}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const iosOcConfig = await configManager.loadFrameworkConfig('ios-oc');
|
|
153
|
+
await configManager.saveFrameworkConfig('ios-oc', {
|
|
154
|
+
chunkOptimizePrompt: chunkOptimizeCodePromptIosOc,
|
|
155
|
+
aggregationOptimizePrompt: aggregationOptimizeCodePromptIosOc,
|
|
156
|
+
finalOptimizePrompt: finalOptimizeCodePromptIosOc,
|
|
157
|
+
componentAnalysisPrompt: '',
|
|
158
|
+
noSamplingAggregationPrompt: noSamplingAggregationPromptIosOc,
|
|
159
|
+
...(iosOcConfig || {}),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// 检查 ios-swift.json 是否存在,如果不存在则创建
|
|
163
|
+
const iosswiftConfigExists = await configManager.frameworkConfigExists('ios-swift');
|
|
164
|
+
if (!iosswiftConfigExists) {
|
|
165
|
+
await configManager.saveFrameworkConfig('ios-swift', {
|
|
166
|
+
chunkOptimizePrompt: chunkOptimizeCodePromptIosSwift,
|
|
167
|
+
aggregationOptimizePrompt: aggregationOptimizeCodePromptIosSwift,
|
|
168
|
+
finalOptimizePrompt: finalOptimizeCodePromptIosSwift,
|
|
169
|
+
componentAnalysisPrompt: '',
|
|
170
|
+
noSamplingAggregationPrompt: noSamplingAggregationPromptIosSwift,
|
|
171
|
+
});
|
|
172
|
+
const iosswiftConfigPath = configManager.getFrameworkConfigPath('ios-swift');
|
|
173
|
+
Logger.log(`已创建默认 ios-swift 配置文件: ${iosswiftConfigPath}`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const iosSwiftConfig = await configManager.loadFrameworkConfig('ios-swift');
|
|
177
|
+
await configManager.saveFrameworkConfig('ios-swift', {
|
|
178
|
+
chunkOptimizePrompt: chunkOptimizeCodePromptIosSwift,
|
|
179
|
+
aggregationOptimizePrompt: aggregationOptimizeCodePromptIosSwift,
|
|
180
|
+
finalOptimizePrompt: finalOptimizeCodePromptIosSwift,
|
|
181
|
+
componentAnalysisPrompt: '',
|
|
182
|
+
noSamplingAggregationPrompt: noSamplingAggregationPromptIosSwift,
|
|
183
|
+
...(iosSwiftConfig || {}),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// 检查 kuikly.json 是否存在,如果不存在则创建
|
|
187
|
+
const kuiklyConfigExists = await configManager.frameworkConfigExists('kuikly');
|
|
188
|
+
if (!kuiklyConfigExists) {
|
|
189
|
+
await configManager.saveFrameworkConfig('kuikly', {
|
|
190
|
+
enableFrameworkGuide: true,
|
|
191
|
+
frameworkGuidePrompt: frameworkPromptKuikly,
|
|
192
|
+
chunkOptimizePrompt: chunkOptimizeCodePromptKuikly,
|
|
193
|
+
aggregationOptimizePrompt: aggregationOptimizeCodePromptKuikly,
|
|
194
|
+
finalOptimizePrompt: finalOptimizeCodePromptKuikly,
|
|
195
|
+
componentAnalysisPrompt: '',
|
|
196
|
+
noSamplingAggregationPrompt: noSamplingAggregationPromptKuikly,
|
|
197
|
+
});
|
|
198
|
+
const kuiklyConfigPath = configManager.getFrameworkConfigPath('kuikly');
|
|
199
|
+
Logger.log(`已创建默认 kuikly 配置文件: ${kuiklyConfigPath}`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const kuiklyConfig = await configManager.loadFrameworkConfig('kuikly');
|
|
203
|
+
await configManager.saveFrameworkConfig('kuikly', {
|
|
204
|
+
enableFrameworkGuide: true,
|
|
205
|
+
frameworkGuidePrompt: frameworkPromptKuikly,
|
|
206
|
+
chunkOptimizePrompt: chunkOptimizeCodePromptKuikly,
|
|
207
|
+
aggregationOptimizePrompt: aggregationOptimizeCodePromptKuikly,
|
|
208
|
+
finalOptimizePrompt: finalOptimizeCodePromptKuikly,
|
|
209
|
+
componentAnalysisPrompt: '',
|
|
210
|
+
noSamplingAggregationPrompt: noSamplingAggregationPromptKuikly,
|
|
211
|
+
...(kuiklyConfig || {}),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
138
214
|
}
|
|
139
215
|
catch (error) {
|
|
140
216
|
Logger.error(`获取配置信息时出错: ${error}`);
|
|
@@ -352,47 +428,16 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
352
428
|
let curFramework = fileConfig?.convertSetting?.framework;
|
|
353
429
|
// 获取框架列表
|
|
354
430
|
const frameworks = await configManager.getFrameworks();
|
|
355
|
-
if (!curFramework || !frameworks.find((fw) => fw.value === curFramework)) {
|
|
356
|
-
curFramework = globalConfig.defaultFramework || 'react';
|
|
357
|
-
}
|
|
358
|
-
const curFrameworkDefaultConfig = (await configManager.loadFrameworkConfig(curFramework)) ||
|
|
359
|
-
(curFramework !== 'vue'
|
|
360
|
-
? {
|
|
361
|
-
chunkOptimizePrompt: chunkOptimizeCodePrompt,
|
|
362
|
-
aggregationOptimizePrompt: aggregationOptimizeCodePrompt,
|
|
363
|
-
finalOptimizePrompt: finalOptimizeCodePrompt,
|
|
364
|
-
componentAnalysisPrompt: componentAnalysisPrompt,
|
|
365
|
-
noSamplingAggregationPrompt: noSamplingAggregationPrompt,
|
|
366
|
-
}
|
|
367
|
-
: {
|
|
368
|
-
chunkOptimizePrompt: chunkOptimizeCodePromptVue,
|
|
369
|
-
aggregationOptimizePrompt: aggregationOptimizeCodePromptVue,
|
|
370
|
-
finalOptimizePrompt: finalOptimizeCodePromptVue,
|
|
371
|
-
componentAnalysisPrompt: componentAnalysisPromptVue,
|
|
372
|
-
noSamplingAggregationPrompt: noSamplingAggregationPromptVue,
|
|
373
|
-
});
|
|
374
431
|
// 如果指定了框架,加载框架配置的提示词
|
|
375
432
|
let promptSetting = {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
433
|
+
enableFrameworkGuide: savedPromptSetting?.frameworkGuidePrompt,
|
|
434
|
+
frameworkGuidePrompt: savedPromptSetting?.frameworkGuidePrompt,
|
|
435
|
+
chunkOptimizePrompt: savedPromptSetting?.chunkOptimizePrompt,
|
|
436
|
+
aggregationOptimizePrompt: savedPromptSetting?.aggregationOptimizePrompt,
|
|
437
|
+
finalOptimizePrompt: savedPromptSetting?.finalOptimizePrompt,
|
|
438
|
+
componentAnalysisPrompt: savedPromptSetting?.componentAnalysisPrompt,
|
|
439
|
+
noSamplingAggregationPrompt: savedPromptSetting?.noSamplingAggregationPrompt,
|
|
381
440
|
};
|
|
382
|
-
if (framework) {
|
|
383
|
-
// 加载框架配置
|
|
384
|
-
const frameworkConfig = await configManager.loadFrameworkConfig(framework);
|
|
385
|
-
if (frameworkConfig && Object.keys(frameworkConfig).length > 0) {
|
|
386
|
-
// 框架配置优先级更高,但如果框架配置为空,则使用已保存的或默认的
|
|
387
|
-
promptSetting = {
|
|
388
|
-
chunkOptimizePrompt: frameworkConfig.chunkOptimizePrompt || promptSetting.chunkOptimizePrompt,
|
|
389
|
-
aggregationOptimizePrompt: frameworkConfig.aggregationOptimizePrompt || promptSetting.aggregationOptimizePrompt,
|
|
390
|
-
finalOptimizePrompt: frameworkConfig.finalOptimizePrompt || promptSetting.finalOptimizePrompt,
|
|
391
|
-
componentAnalysisPrompt: frameworkConfig.componentAnalysisPrompt || promptSetting.componentAnalysisPrompt,
|
|
392
|
-
noSamplingAggregationPrompt: frameworkConfig.noSamplingAggregationPrompt || promptSetting.noSamplingAggregationPrompt,
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
441
|
res.json({
|
|
397
442
|
success: true,
|
|
398
443
|
data: {
|
|
@@ -415,6 +460,8 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
415
460
|
}
|
|
416
461
|
// 如果指定了框架,加载框架配置的提示词
|
|
417
462
|
let promptSetting = {
|
|
463
|
+
enableFrameworkGuide: false,
|
|
464
|
+
frameworkGuidePrompt: '',
|
|
418
465
|
chunkOptimizePrompt: chunkOptimizeCodePrompt,
|
|
419
466
|
aggregationOptimizePrompt: aggregationOptimizeCodePrompt,
|
|
420
467
|
finalOptimizePrompt: finalOptimizeCodePrompt,
|
|
@@ -427,6 +474,8 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
427
474
|
if (frameworkConfig && Object.keys(frameworkConfig).length > 0) {
|
|
428
475
|
// 框架配置优先级更高
|
|
429
476
|
promptSetting = {
|
|
477
|
+
enableFrameworkGuide: frameworkConfig.frameworkGuidePrompt || promptSetting.enableFrameworkGuide ? true : false,
|
|
478
|
+
frameworkGuidePrompt: frameworkConfig.frameworkGuidePrompt || promptSetting.frameworkGuidePrompt,
|
|
430
479
|
chunkOptimizePrompt: frameworkConfig.chunkOptimizePrompt || promptSetting.chunkOptimizePrompt,
|
|
431
480
|
aggregationOptimizePrompt: frameworkConfig.aggregationOptimizePrompt || promptSetting.aggregationOptimizePrompt,
|
|
432
481
|
finalOptimizePrompt: frameworkConfig.finalOptimizePrompt || promptSetting.finalOptimizePrompt,
|
|
@@ -476,6 +525,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
476
525
|
let frameworks = (await configManager.getFrameworks()) || [
|
|
477
526
|
{ value: 'react', label: 'React' },
|
|
478
527
|
{ value: 'vue', label: 'Vue' },
|
|
528
|
+
{ value: 'kuikly', label: 'Kuikly' },
|
|
479
529
|
];
|
|
480
530
|
const defaultFramework = globalConfig?.defaultFramework || 'react';
|
|
481
531
|
let promptSetting = {};
|
|
@@ -484,12 +534,12 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
484
534
|
promptSetting[fw] = {
|
|
485
535
|
...frameworkConfig,
|
|
486
536
|
enableFrameworkGuide: frameworkConfig.enableFrameworkGuide || false,
|
|
487
|
-
|
|
488
|
-
chunkOptimizePrompt: frameworkConfig.chunkOptimizePrompt ||
|
|
489
|
-
aggregationOptimizePrompt: frameworkConfig.aggregationOptimizePrompt ||
|
|
490
|
-
finalOptimizePrompt: frameworkConfig.finalOptimizePrompt ||
|
|
491
|
-
componentAnalysisPrompt: frameworkConfig.componentAnalysisPrompt ||
|
|
492
|
-
noSamplingAggregationPrompt: frameworkConfig.noSamplingAggregationPrompt ||
|
|
537
|
+
frameworkGuidePrompt: frameworkConfig.frameworkGuidePrompt || '',
|
|
538
|
+
chunkOptimizePrompt: frameworkConfig.chunkOptimizePrompt || '',
|
|
539
|
+
aggregationOptimizePrompt: frameworkConfig.aggregationOptimizePrompt || '',
|
|
540
|
+
finalOptimizePrompt: frameworkConfig.finalOptimizePrompt || '',
|
|
541
|
+
componentAnalysisPrompt: frameworkConfig.componentAnalysisPrompt || '',
|
|
542
|
+
noSamplingAggregationPrompt: frameworkConfig.noSamplingAggregationPrompt || '',
|
|
493
543
|
};
|
|
494
544
|
res.json({
|
|
495
545
|
success: true,
|
|
@@ -553,6 +603,8 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
553
603
|
return;
|
|
554
604
|
}
|
|
555
605
|
let promptSetting = {
|
|
606
|
+
enableFrameworkGuide: false,
|
|
607
|
+
frameworkGuidePrompt: '',
|
|
556
608
|
chunkOptimizePrompt: chunkOptimizeCodePrompt,
|
|
557
609
|
aggregationOptimizePrompt: aggregationOptimizeCodePrompt,
|
|
558
610
|
finalOptimizePrompt: finalOptimizeCodePrompt,
|
|
@@ -563,6 +615,8 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
563
615
|
if (frameworkConfig && Object.keys(frameworkConfig).length > 0) {
|
|
564
616
|
// 框架配置优先级更高
|
|
565
617
|
promptSetting = {
|
|
618
|
+
enableFrameworkGuide: frameworkConfig.enableFrameworkGuide || promptSetting.enableFrameworkGuide,
|
|
619
|
+
frameworkGuidePrompt: frameworkConfig.frameworkGuidePrompt || promptSetting.frameworkGuidePrompt,
|
|
566
620
|
chunkOptimizePrompt: frameworkConfig.chunkOptimizePrompt || promptSetting.chunkOptimizePrompt,
|
|
567
621
|
aggregationOptimizePrompt: frameworkConfig.aggregationOptimizePrompt || promptSetting.aggregationOptimizePrompt,
|
|
568
622
|
finalOptimizePrompt: frameworkConfig.finalOptimizePrompt || promptSetting.finalOptimizePrompt,
|
|
@@ -598,17 +652,54 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
598
652
|
}
|
|
599
653
|
// 根据框架返回对应的原始默认提示词
|
|
600
654
|
const isVue = framework.toLowerCase() === 'vue';
|
|
655
|
+
const isIosOc = framework.toLowerCase() === 'ios-oc';
|
|
656
|
+
const isIosSwift = framework.toLowerCase() === 'ios-swift';
|
|
657
|
+
const isKuikly = framework.toLowerCase() === 'kuikly';
|
|
601
658
|
const promptSetting = {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
659
|
+
enableFrameworkGuide: isKuikly ? true : false,
|
|
660
|
+
frameworkGuidePrompt: isKuikly ? frameworkPromptKuikly : '',
|
|
661
|
+
chunkOptimizePrompt: isKuikly
|
|
662
|
+
? chunkOptimizeCodePromptKuikly
|
|
663
|
+
: isIosOc
|
|
664
|
+
? chunkOptimizeCodePromptIosOc
|
|
665
|
+
: isIosSwift
|
|
666
|
+
? chunkOptimizeCodePromptIosSwift
|
|
667
|
+
: isVue
|
|
668
|
+
? chunkOptimizeCodePromptVue
|
|
669
|
+
: chunkOptimizeCodePrompt,
|
|
670
|
+
aggregationOptimizePrompt: isKuikly
|
|
671
|
+
? aggregationOptimizeCodePromptKuikly
|
|
672
|
+
: isIosOc
|
|
673
|
+
? aggregationOptimizeCodePromptIosOc
|
|
674
|
+
: isIosSwift
|
|
675
|
+
? aggregationOptimizeCodePromptIosSwift
|
|
676
|
+
: isVue
|
|
677
|
+
? aggregationOptimizeCodePromptVue
|
|
678
|
+
: aggregationOptimizeCodePrompt,
|
|
679
|
+
finalOptimizePrompt: isKuikly
|
|
680
|
+
? finalOptimizeCodePromptKuikly
|
|
681
|
+
: isIosOc
|
|
682
|
+
? finalOptimizeCodePromptIosOc
|
|
683
|
+
: isIosSwift
|
|
684
|
+
? finalOptimizeCodePromptIosSwift
|
|
685
|
+
: isVue
|
|
686
|
+
? finalOptimizeCodePromptVue
|
|
687
|
+
: finalOptimizeCodePrompt,
|
|
688
|
+
componentAnalysisPrompt: isKuikly ? '' : isIosOc ? '' : isIosSwift ? '' : isVue ? componentAnalysisPromptVue : componentAnalysisPrompt,
|
|
689
|
+
noSamplingAggregationPrompt: isKuikly
|
|
690
|
+
? noSamplingAggregationPromptKuikly
|
|
691
|
+
: isIosOc
|
|
692
|
+
? noSamplingAggregationPromptIosOc
|
|
693
|
+
: isIosSwift
|
|
694
|
+
? noSamplingAggregationPromptIosSwift
|
|
695
|
+
: isVue
|
|
696
|
+
? noSamplingAggregationPromptVue
|
|
697
|
+
: noSamplingAggregationPrompt,
|
|
607
698
|
};
|
|
608
699
|
res.json({
|
|
609
700
|
success: true,
|
|
610
701
|
data: promptSetting,
|
|
611
|
-
message: '获取默认框架配置成功',
|
|
702
|
+
message: '获取默认框架配置成功1',
|
|
612
703
|
});
|
|
613
704
|
}
|
|
614
705
|
catch (error) {
|
|
@@ -620,6 +711,67 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
620
711
|
});
|
|
621
712
|
}
|
|
622
713
|
});
|
|
714
|
+
// 获取项目中的 Markdown 文件列表(扫描 .sloth/doc 目录)
|
|
715
|
+
app.get('/getMarkdownFiles', async (req, res) => {
|
|
716
|
+
try {
|
|
717
|
+
const projectRoot = getProjectRoot(req.fileManager);
|
|
718
|
+
if (!projectRoot) {
|
|
719
|
+
res.json({
|
|
720
|
+
success: true,
|
|
721
|
+
data: [],
|
|
722
|
+
message: '未找到项目根目录',
|
|
723
|
+
});
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const markdownFiles = [];
|
|
727
|
+
const promptDir = path.join(projectRoot, '.sloth', 'prompt');
|
|
728
|
+
// 检查 .sloth/prompt 目录是否存在
|
|
729
|
+
if (!fs.existsSync(promptDir)) {
|
|
730
|
+
res.json({
|
|
731
|
+
success: true,
|
|
732
|
+
data: [],
|
|
733
|
+
message: '.sloth/prompt 目录不存在',
|
|
734
|
+
});
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
// 递归查找 prompt 目录下的 .md 文件
|
|
738
|
+
const findMarkdownFiles = (dir) => {
|
|
739
|
+
try {
|
|
740
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
741
|
+
for (const item of items) {
|
|
742
|
+
const fullPath = path.join(dir, item.name);
|
|
743
|
+
const relativePath = path.relative(promptDir, fullPath);
|
|
744
|
+
if (item.isDirectory()) {
|
|
745
|
+
findMarkdownFiles(fullPath);
|
|
746
|
+
}
|
|
747
|
+
else if (item.isFile() && item.name.endsWith('.md')) {
|
|
748
|
+
markdownFiles.push({
|
|
749
|
+
path: path.join('.sloth', 'prompt', relativePath),
|
|
750
|
+
name: item.name,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
catch (err) {
|
|
756
|
+
Logger.warn(`无法访问目录: ${dir}`, err);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
findMarkdownFiles(promptDir);
|
|
760
|
+
res.json({
|
|
761
|
+
success: true,
|
|
762
|
+
data: markdownFiles,
|
|
763
|
+
message: `找到 ${markdownFiles.length} 个文档`,
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
Logger.error('获取文档列表失败:', error);
|
|
768
|
+
res.status(500).json({
|
|
769
|
+
success: false,
|
|
770
|
+
message: '获取文档列表失败',
|
|
771
|
+
error: error instanceof Error ? error.message : String(error),
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
});
|
|
623
775
|
// 认证页面
|
|
624
776
|
app.get('/auth-page', (req, res) => {
|
|
625
777
|
try {
|
|
@@ -668,6 +820,7 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
668
820
|
try {
|
|
669
821
|
// 安全地解构请求体,处理可能为空的情况
|
|
670
822
|
const { token, value, fileKey, nodeId } = req.body || {};
|
|
823
|
+
Logger.log('submit req.body', token, value, fileKey, nodeId);
|
|
671
824
|
if (!token || !value) {
|
|
672
825
|
res.status(400).send('请求体中缺少 token 或 value');
|
|
673
826
|
return;
|
|
@@ -698,6 +851,9 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
698
851
|
Logger.log(`已保存 groupsData 到 fileKey "${fileKey}", nodeId "${nodeId}":`, value.groupsData.length, '个分组');
|
|
699
852
|
}
|
|
700
853
|
if (fileKey && value.promptSetting) {
|
|
854
|
+
if (!value.promptSetting.enableFrameworkGuide) {
|
|
855
|
+
value.promptSetting.frameworkGuidePrompt = '';
|
|
856
|
+
}
|
|
701
857
|
await req.fileManager.savePromptSetting(fileKey, nodeId, value.promptSetting);
|
|
702
858
|
Logger.log(`已保存 promptSetting 到 fileKey "${fileKey}", nodeId "${nodeId}"`);
|
|
703
859
|
}
|
|
@@ -1470,8 +1626,8 @@ export async function startHttpServer(port = PORT, mcpServer, configManagerInsta
|
|
|
1470
1626
|
}
|
|
1471
1627
|
Logger.log(`分析设计稿变更: old=${oldFileKey}/${oldNodeId}, new=${newFileKey}/${newNodeId}`);
|
|
1472
1628
|
// 1. 加载新旧 HTML 和 imageMap
|
|
1473
|
-
let oldHtml = await fileManager.loadAbsoluteHtml(oldFileKey, oldNodeId);
|
|
1474
|
-
let newHtml = await fileManager.loadAbsoluteHtml(newFileKey, newNodeId);
|
|
1629
|
+
let oldHtml = await fileManager.loadAbsoluteHtml(oldFileKey, oldNodeId, true);
|
|
1630
|
+
let newHtml = await fileManager.loadAbsoluteHtml(newFileKey, newNodeId, true);
|
|
1475
1631
|
Logger.log(`[analyzeChange] 加载 HTML 完成: oldHtml=${oldHtml?.length || 0}字符, newHtml=${newHtml?.length || 0}字符`);
|
|
1476
1632
|
// 加载 imageMap 用于替换图片路径(flatted 格式)
|
|
1477
1633
|
let oldImageMap = {};
|