diff --git a/js/app.js b/js/app.js
index 1d4366b..cfa09d5 100644
--- a/js/app.js
+++ b/js/app.js
@@ -14,22 +14,26 @@ if (typeof marked !== 'undefined') {
}
class ProductCanvasApp {
- constructor() {
- this.currentMode = 'canvas'; // 'canvas' 或 'swot'
- this.svgStorage = {};
- this.currentSvgId = null;
- this.conversationHistory = {};
+ constructor() {
+ this.currentMode = 'canvas'; // 'canvas' 或 'swot'
+ this.svgStorage = {};
+ this.currentSvgId = null;
+ this.conversationHistory = {};
this.isProcessing = false;
this.currentStreamingMessage = null;
this.initElements();
this.initEventListeners();
this.loadSavedData();
- this.updateModeUI();
- }
-
- // 初始化DOM元素引用
- initElements() {
+ this.updateModeUI();
+ }
+
+ getModeDisplayName(mode = this.currentMode) {
+ return mode === 'canvas' ? '产品画布' : 'SWOT分析';
+ }
+
+ // 初始化DOM元素引用
+ initElements() {
// 模式切换按钮
this.canvasBtn = document.getElementById('canvas-mode-btn');
this.swotBtn = document.getElementById('swot-mode-btn');
@@ -117,54 +121,97 @@ class ProductCanvasApp {
canvas: savedCanvasHistory,
swot: savedSwotHistory
};
- this.renderConversationHistory();
-
- // 加载SVG存储(按模式分别存储)
- const savedCanvasSVGs = Utils.storage.get('canvasSVGs', {});
- const savedSwotSVGs = Utils.storage.get('swotSVGs', {});
+ this.renderConversationHistory();
+
+ // 加载SVG存储(按模式分别存储)
+ const savedCanvasSVGs = Utils.storage.get('canvasSVGs', {});
+ const savedSwotSVGs = Utils.storage.get('swotSVGs', {});
this.svgStorage = {
canvas: savedCanvasSVGs,
- swot: savedSwotSVGs
- };
-
- // 加载API配置
+ swot: savedSwotSVGs
+ };
+
+ this.renderSvgViewerForMode();
+
+ // 加载API配置
const apiConfig = window.apiClient.getConfig();
this.apiUrlInput.value = apiConfig.url || '';
this.apiKeyInput.value = apiConfig.key || '';
this.apiModelInput.value = apiConfig.model || '';
}
- // 切换模式
- switchMode(mode) {
- if (this.currentMode === mode) return;
-
- this.currentMode = mode;
- Utils.storage.set('currentMode', mode);
- this.updateModeUI();
- }
+ // 切换模式
+ switchMode(mode) {
+ if (this.currentMode === mode) return;
+
+ this.currentMode = mode;
+ Utils.storage.set('currentMode', mode);
+ this.currentSvgId = null;
+ this.currentMode = mode;
+ Utils.storage.set('currentMode', mode);
+ this.updateModeUI();
+ this.renderConversationHistory();
+ this.renderSvgViewerForMode();
+ }
// 更新模式UI
- updateModeUI() {
- if (this.currentMode === 'canvas') {
- this.canvasBtn.classList.add('mode-btn-active');
+ updateModeUI() {
+ if (this.currentMode === 'canvas') {
+ this.canvasBtn.classList.add('mode-btn-active');
this.canvasBtn.classList.remove('mode-btn-inactive');
this.swotBtn.classList.remove('mode-btn-active');
this.swotBtn.classList.add('mode-btn-inactive');
- this.pageTitle.textContent = '产品画布';
- if (!this.currentSvgId) {
- this.placeholderText.textContent = '生成的产品画布将在此处显示';
- }
- } else {
+ this.pageTitle.textContent = '产品画布';
+ if (!this.currentSvgId) {
+ this.placeholderText.textContent = '生成的产品画布将在此处显示';
+ }
+ } else {
this.swotBtn.classList.add('mode-btn-active');
this.swotBtn.classList.remove('mode-btn-inactive');
this.canvasBtn.classList.remove('mode-btn-active');
this.canvasBtn.classList.add('mode-btn-inactive');
- this.pageTitle.textContent = 'SWOT分析';
- if (!this.currentSvgId) {
- this.placeholderText.textContent = '生成的SWOT分析将在此处显示';
- }
- }
- }
+ this.pageTitle.textContent = 'SWOT分析';
+ if (!this.currentSvgId) {
+ this.placeholderText.textContent = '生成的SWOT分析将在此处显示';
+ }
+ }
+ }
+
+ showSvgPlaceholder() {
+ const label = this.getModeDisplayName();
+ this.currentSvgId = null;
+ this.svgViewer.innerHTML = `
+
+ `;
+ }
+
+ renderSvgViewerForMode() {
+ const svgStore = this.svgStorage[this.currentMode] || {};
+ const history = this.conversationHistory[this.currentMode] || [];
+
+ let latestSvgId = null;
+ for (let i = history.length - 1; i >= 0; i--) {
+ const message = history[i];
+ if (message.type !== 'ai') continue;
+ for (const [svgId, svg] of Object.entries(svgStore)) {
+ if (svg.messageId === message.id) {
+ latestSvgId = svgId;
+ break;
+ }
+ }
+ if (latestSvgId) break;
+ }
+
+ if (latestSvgId && svgStore[latestSvgId]) {
+ this.currentSvgId = latestSvgId;
+ this.svgViewer.innerHTML = svgStore[latestSvgId].content;
+ } else {
+ this.showSvgPlaceholder();
+ }
+ }
// 发送消息
async sendMessage() {
@@ -723,37 +770,69 @@ class ProductCanvasApp {
this.chatHistory.appendChild(messageDiv);
}
- // 渲染对话历史
- renderConversationHistory() {
- this.chatHistory.innerHTML = '';
-
- // 获取当前模式的对话历史
- const currentHistory = this.conversationHistory[this.currentMode] || [];
-
- for (const message of currentHistory) {
- if (message.type === 'ai') {
- const parsed = Utils.parseSVGResponse(message.content);
-
- // 查找对应的SVG
- let svgId = null;
- const currentSvgStorage = this.svgStorage[this.currentMode] || {};
- for (const [id, svg] of Object.entries(currentSvgStorage)) {
- if (svg.messageId === message.id) {
- svgId = id;
- break;
- }
- }
-
- if (svgId && parsed.svgContent) {
- this.renderMessageWithSVG(message, parsed, svgId);
- } else {
- this.renderMessage(message);
- }
- } else {
- this.renderMessage(message);
- }
- }
- }
+ // 渲染对话历史
+ renderConversationHistory() {
+ this.chatHistory.innerHTML = '';
+
+ // 获取当前模式的对话历史
+ const currentHistory = this.conversationHistory[this.currentMode] || [];
+ const currentSvgStorage = this.svgStorage[this.currentMode] || {};
+ let hasStorageUpdate = false;
+ let hasHistoryUpdate = false;
+
+ for (const message of currentHistory) {
+ if (message.type === 'ai') {
+ const parsed = Utils.parseSVGResponse(message.content);
+
+ // 查找或补建对应的SVG
+ let svgId = null;
+ for (const [id, svg] of Object.entries(currentSvgStorage)) {
+ if (svg.messageId === message.id) {
+ svgId = id;
+ break;
+ }
+ }
+
+ const hasSvgContent = parsed.svgContent && parsed.svgContent.includes('`;
+ svgId = Utils.generateId('svg');
+ currentSvgStorage[svgId] = {
+ content: normalizedSvg,
+ messageId: message.id,
+ mode: this.currentMode,
+ timestamp: message.timestamp || new Date().toISOString()
+ };
+ parsed.svgContent = normalizedSvg;
+ message.content = this.buildSVGMessageContent(parsed.beforeText, normalizedSvg, parsed.afterText);
+ hasStorageUpdate = true;
+ hasHistoryUpdate = true;
+ }
+ this.renderMessageWithSVG(message, parsed, svgId);
+ continue;
+ }
+
+ this.renderMessage(message);
+ } else {
+ this.renderMessage(message);
+ }
+ }
+
+ if (hasStorageUpdate) {
+ this.svgStorage[this.currentMode] = currentSvgStorage;
+ Utils.storage.set('canvasSVGs', this.svgStorage.canvas || {});
+ Utils.storage.set('swotSVGs', this.svgStorage.swot || {});
+ }
+ if (hasHistoryUpdate) {
+ Utils.storage.set('canvasHistory', this.conversationHistory.canvas || []);
+ Utils.storage.set('swotHistory', this.conversationHistory.swot || []);
+ }
+
+ Utils.scrollToBottom(this.chatHistory);
+ }
// 显示SVG
viewSVG(svgId) {
diff --git a/prompts/canvas-prompt.txt b/prompts/canvas-prompt.txt
index 02f11d2..0e3ff02 100644
--- a/prompts/canvas-prompt.txt
+++ b/prompts/canvas-prompt.txt
@@ -3,7 +3,11 @@
请用中文回复,并在回复中包含SVG格式的产品画布图表。
-产品精益画布助手,下面是SVG画布的模板,注意使用markdown格式回复
+产品精益画布助手,下面是SVG画布的模板,注意使用markdown格式回复,
+
+- 解决方案、门槛优势、关键指标、渠道 文字不要超过7行
+- 成本分析、收入分析 文字不要超过6行
+
```