diff --git a/js/apiclient.js b/js/apiclient.js
index 5446aee..a6b7e5c 100644
--- a/js/apiclient.js
+++ b/js/apiclient.js
@@ -133,7 +133,7 @@ class APIClient {
{ role: 'user', content: userRequest }
];
- return await this.sendChatMessage(messages, { maxTokens: 3000 });
+ return await this.sendChatMessage(messages, { maxTokens: 18000 });
}
// 生成SWOT分析的专用方法
@@ -144,7 +144,7 @@ class APIClient {
{ role: 'user', content: userRequest }
];
- return await this.sendChatMessage(messages, { maxTokens: 3000 });
+ return await this.sendChatMessage(messages, { maxTokens: 18000 });
}
// 流式生成产品画布
@@ -155,7 +155,7 @@ class APIClient {
{ role: 'user', content: userRequest }
];
- return await this.sendChatMessageStream(messages, { maxTokens: 3000 }, onChunk, onComplete);
+ return await this.sendChatMessageStream(messages, { maxTokens: 13000 }, onChunk, onComplete);
}
// 流式生成SWOT分析
@@ -166,7 +166,7 @@ class APIClient {
{ role: 'user', content: userRequest }
];
- return await this.sendChatMessageStream(messages, { maxTokens: 3000 }, onChunk, onComplete);
+ return await this.sendChatMessageStream(messages, { maxTokens: 13000 }, onChunk, onComplete);
}
// 流式发送聊天请求
diff --git a/js/app.js b/js/app.js
index 569ece4..1d4366b 100644
--- a/js/app.js
+++ b/js/app.js
@@ -320,14 +320,14 @@ class ProductCanvasApp {
}
};
- const onComplete = () => {
- // 流式接收完成,处理完整消息
- this.finalizeStreamingMessage(messageId, fullContent, svgId, beforeText);
-
- this.isProcessing = false;
- this.sendButton.disabled = false;
- this.sendButton.innerHTML = '';
- };
+ const onComplete = () => {
+ // 流式接收完成,处理完整消息
+ this.finalizeStreamingMessage(messageId, fullContent, svgId);
+
+ this.isProcessing = false;
+ this.sendButton.disabled = false;
+ this.sendButton.innerHTML = '';
+ };
// 调用流式API
if (this.currentMode === 'canvas') {
@@ -394,11 +394,11 @@ class ProductCanvasApp {
}
}
- // 更新SVG后的消息内容
- updateStreamingMessageAfterSVG(container, beforeText, svgId, afterText) {
- // 使用Markdown解析文本
- const parsedBeforeText = typeof marked !== 'undefined' ? marked.parse(beforeText) : Utils.escapeHtml(beforeText);
- const parsedAfterText = typeof marked !== 'undefined' ? marked.parse(afterText) : Utils.escapeHtml(afterText);
+ // 更新SVG后的消息内容
+ updateStreamingMessageAfterSVG(container, beforeText, svgId, afterText) {
+ // 使用Markdown解析文本
+ const parsedBeforeText = typeof marked !== 'undefined' ? marked.parse(beforeText) : Utils.escapeHtml(beforeText);
+ const parsedAfterText = typeof marked !== 'undefined' ? marked.parse(afterText) : Utils.escapeHtml(afterText);
container.innerHTML = `
@@ -410,12 +410,36 @@ class ProductCanvasApp {
${parsedAfterText}
- `;
- Utils.scrollToBottom(this.chatHistory);
- }
-
- // 完成流式消息
- finalizeStreamingMessage(messageId, fullContent, svgId = null, beforeText = '') {
+ `;
+ Utils.scrollToBottom(this.chatHistory);
+ }
+
+ // 组装标准化的SVG消息字符串
+ buildSVGMessageContent(beforeText = '', svgBody = '', afterText = '') {
+ const segments = [];
+ const trimmedBefore = (beforeText || '').trim();
+ const trimmedAfter = (afterText || '').trim();
+ const trimmedSvg = (svgBody || '').trim();
+
+ if (trimmedBefore) {
+ segments.push(trimmedBefore);
+ }
+
+ if (trimmedSvg) {
+ segments.push('```svg');
+ segments.push(trimmedSvg);
+ segments.push('```');
+ }
+
+ if (trimmedAfter) {
+ segments.push(trimmedAfter);
+ }
+
+ return segments.join('\n\n').trim();
+ }
+
+ // 完成流式消息
+ finalizeStreamingMessage(messageId, fullContent, svgId = null) {
let container = document.querySelector(`.chat-bubble-ai[data-message-id="${messageId}"]`);
if (!container) {
const fallback = document.querySelector(`[data-message-id="${messageId}"]`);
@@ -426,121 +450,97 @@ class ProductCanvasApp {
}
}
if (!container) return;
-
- const message = {
- id: messageId,
- type: 'ai',
- content: fullContent,
- timestamp: new Date().toISOString()
- };
-
- this.conversationHistory[this.currentMode].push(message);
-
- // 如果已经有SVG ID(从流式处理中获得),直接使用
- if (svgId && this.svgStorage[this.currentMode][svgId]) {
- // 提取SVG后的文本
- let afterText = '';
- if (fullContent.includes('')) {
- const svgEndIndex = fullContent.indexOf('') + 6;
- afterText = fullContent.substring(svgEndIndex);
- }
-
- // 使用Markdown解析文本
- const parsedBeforeText = typeof marked !== 'undefined' ? marked.parse(beforeText) : Utils.escapeHtml(beforeText);
- const parsedAfterText = typeof marked !== 'undefined' ? marked.parse(afterText) : Utils.escapeHtml(afterText);
-
- // 更新容器内容为包含SVG的消息
- container.innerHTML = `
-
- ${parsedBeforeText}
-
- 📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG
-
- ${parsedAfterText}
-
-
-
-
-
-
- `;
- } else {
- // 使用原有的解析方法作为后备
- const parsed = Utils.parseSVGResponse(fullContent);
-
- // 如果包含SVG,存储SVG内容
- if (parsed.svgContent) {
- const newSvgId = Utils.generateId('svg');
- this.svgStorage[this.currentMode][newSvgId] = {
- content: parsed.svgContent,
- messageId: messageId,
- mode: this.currentMode,
- timestamp: new Date().toISOString()
- };
-
- this.viewSVG(newSvgId);
-
- // 使用Markdown解析文本
- const parsedBeforeText = typeof marked !== 'undefined' ? marked.parse(parsed.beforeText) : Utils.escapeHtml(parsed.beforeText);
- const parsedAfterText = typeof marked !== 'undefined' ? marked.parse(parsed.afterText) : Utils.escapeHtml(parsed.afterText);
-
- // 更新容器内容为包含SVG的消息
- container.innerHTML = `
-
- ${parsedBeforeText}
-
- 📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG
-
- ${parsedAfterText}
-
-
-
-
-
-
- `;
- } else {
- // 使用Markdown解析内容
- const parsedContent = typeof marked !== 'undefined' ? marked.parse(fullContent) : Utils.escapeHtml(fullContent);
-
- // 更新容器内容为普通消息
- container.innerHTML = `
-
- ${parsedContent}
-
-
-
-
-
-
- `;
- }
- }
-
- // 保存数据
- Utils.storage.set('canvasHistory', this.conversationHistory.canvas);
- Utils.storage.set('swotHistory', this.conversationHistory.swot);
- Utils.storage.set('canvasSVGs', this.svgStorage.canvas);
- Utils.storage.set('swotSVGs', this.svgStorage.swot);
- }
+
+ const parsed = Utils.parseSVGResponse(fullContent);
+ const timestamp = new Date().toISOString();
+
+ const message = {
+ id: messageId,
+ type: 'ai',
+ content: '',
+ timestamp
+ };
+
+ container.classList.remove('streaming-text');
+ container.setAttribute('data-message-id', messageId);
+
+ const actionFooter = `
+
+
+
+
+ `;
+
+ if (parsed.svgContent && parsed.svgContent.includes('`;
+
+ let targetSvgId = svgId || null;
+ if (!targetSvgId || !this.svgStorage[this.currentMode][targetSvgId]) {
+ targetSvgId = targetSvgId || Utils.generateId('svg');
+ }
+
+ this.svgStorage[this.currentMode][targetSvgId] = {
+ content: svgBody,
+ messageId,
+ mode: this.currentMode,
+ timestamp
+ };
+
+ const beforeHtml = parsed.beforeText
+ ? (typeof marked !== 'undefined' ? marked.parse(parsed.beforeText) : Utils.escapeHtml(parsed.beforeText))
+ : '';
+ const afterHtml = parsed.afterText
+ ? (typeof marked !== 'undefined' ? marked.parse(parsed.afterText) : Utils.escapeHtml(parsed.afterText))
+ : '';
+
+ container.innerHTML = `
+
+ ${beforeHtml}
+
+ 📊 点击查看 ${this.currentMode === 'canvas' ? '产品画布' : 'SWOT分析'} SVG
+
+ ${afterHtml}
+
+ ${actionFooter}
+ `;
+
+ this.currentSvgId = targetSvgId;
+ this.svgViewer.innerHTML = svgBody;
+
+ message.content = this.buildSVGMessageContent(parsed.beforeText, svgBody, parsed.afterText);
+ } else {
+ const sanitizedText = fullContent.replace(/^\s*```/, '').replace(/```$/, '').trim();
+ const parsedContent = sanitizedText
+ ? (typeof marked !== 'undefined' ? marked.parse(sanitizedText) : Utils.escapeHtml(sanitizedText))
+ : '';
+
+ container.innerHTML = `
+
+ ${parsedContent}
+
+ ${actionFooter}
+ `;
+
+ message.content = sanitizedText;
+ }
+
+ this.conversationHistory[this.currentMode].push(message);
+
+ // 保存数据
+ Utils.storage.set('canvasHistory', this.conversationHistory.canvas);
+ Utils.storage.set('swotHistory', this.conversationHistory.swot);
+ Utils.storage.set('canvasSVGs', this.svgStorage.canvas);
+ Utils.storage.set('swotSVGs', this.svgStorage.swot);
+ Utils.scrollToBottom(this.chatHistory);
+ }
// 清空当前对话
clearCurrentConversation() {
diff --git a/js/utils.js b/js/utils.js
index e2f15de..02b2b2b 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -21,29 +21,63 @@ function generateId(prefix = 'id') {
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
-// 解析SVG响应,提取SVG内容和前后文本
-function parseSVGResponse(response) {
- const svgRegex = /```svg\s*([\s\S]*?)```/i;
- const match = response.match(svgRegex);
-
- if (match) {
- const svgContent = match[1].trim();
- const beforeText = response.substring(0, match.index).trim();
- const afterText = response.substring(match.index + match[0].length).trim();
-
- return {
- svgContent,
- beforeText,
- afterText
- };
- }
-
- return {
- svgContent: null,
- beforeText: response,
- afterText: ''
- };
-}
+// 解析SVG响应,提取SVG内容和前后文本,容错缺失的结束反引号
+function parseSVGResponse(response = '') {
+ const content = typeof response === 'string' ? response : String(response || '');
+ const svgFenceRegex = /```(?:svg)?\s*([\s\S]*?)```/i;
+ const fenceMatch = content.match(svgFenceRegex);
+
+ if (fenceMatch) {
+ const svgBody = fenceMatch[1].trim();
+ const beforeText = content.substring(0, fenceMatch.index).trim();
+ let afterText = content.substring(fenceMatch.index + fenceMatch[0].length).trim();
+ afterText = afterText.replace(/^\s*```/, '').trim();
+
+ return {
+ svgContent: svgBody,
+ beforeText,
+ afterText
+ };
+ }
+
+ // 兼容缺失结束反引号的情况
+ const svgStartRegex = /```(?:svg)?\s*')) {
+ svgSection += '\n';
+ }
+
+ return {
+ svgContent: svgSection || null,
+ beforeText,
+ afterText
+ };
+ }
+
+ return {
+ svgContent: null,
+ beforeText: content.trim(),
+ afterText: ''
+ };
+}
// 下载文件
function downloadFile(content, filename, mimeType = 'text/plain') {
@@ -286,4 +320,4 @@ window.Utils = {
autoResizeTextarea,
StreamProcessor,
createStreamRequest
-};
\ No newline at end of file
+};