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.
@@ -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
  },
@@ -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?.enableFrameworkGuide ? savedPromptSetting?.frameworkGuidePrompt : '';
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(placeholderPrompt + '\n' + replacedCode);
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;
@@ -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
- if (!config.frameworks) {
84
- config.frameworks = [];
85
- }
86
- if (typeof config.frameworks[0] === 'string') {
87
- config.frameworks = config.frameworks.map((f) => {
88
- return {
89
- label: f.charAt(0).toUpperCase() + f.slice(1),
90
- value: f,
91
- isCustom: f !== 'react' && f !== 'vue', // todo: 暂时兼容,仅 react 是默认框架,若新增框架需调整
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
- chunkOptimizePrompt: savedPromptSetting?.chunkOptimizePrompt || curFrameworkDefaultConfig.chunkOptimizePrompt,
377
- aggregationOptimizePrompt: savedPromptSetting?.aggregationOptimizePrompt || curFrameworkDefaultConfig.aggregationOptimizePrompt,
378
- finalOptimizePrompt: savedPromptSetting?.finalOptimizePrompt || curFrameworkDefaultConfig.finalOptimizePrompt,
379
- componentAnalysisPrompt: savedPromptSetting?.componentAnalysisPrompt || curFrameworkDefaultConfig.componentAnalysisPrompt,
380
- noSamplingAggregationPrompt: savedPromptSetting?.noSamplingAggregationPrompt || curFrameworkDefaultConfig.noSamplingAggregationPrompt,
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
- frameworkPrompt: frameworkConfig.frameworkPrompt || '',
488
- chunkOptimizePrompt: frameworkConfig.chunkOptimizePrompt || chunkOptimizeCodePrompt,
489
- aggregationOptimizePrompt: frameworkConfig.aggregationOptimizePrompt || aggregationOptimizeCodePrompt,
490
- finalOptimizePrompt: frameworkConfig.finalOptimizePrompt || finalOptimizeCodePrompt,
491
- componentAnalysisPrompt: frameworkConfig.componentAnalysisPrompt || componentAnalysisPrompt,
492
- noSamplingAggregationPrompt: frameworkConfig.noSamplingAggregationPrompt || 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
- chunkOptimizePrompt: isVue ? chunkOptimizeCodePromptVue : chunkOptimizeCodePrompt,
603
- aggregationOptimizePrompt: isVue ? aggregationOptimizeCodePromptVue : aggregationOptimizeCodePrompt,
604
- finalOptimizePrompt: isVue ? finalOptimizeCodePromptVue : finalOptimizeCodePrompt,
605
- componentAnalysisPrompt: isVue ? componentAnalysisPromptVue : componentAnalysisPrompt,
606
- noSamplingAggregationPrompt: isVue ? noSamplingAggregationPromptVue : noSamplingAggregationPrompt,
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 = {};