放大缩小

This commit is contained in:
史悦
2025-10-27 13:50:34 +08:00
parent 01e1083e5e
commit 4dde0e31b1
8 changed files with 2081 additions and 40 deletions

View File

@@ -284,7 +284,10 @@ iconify-icon {
} }
.svg-content-wrapper { .svg-content-wrapper {
flex: 1;
margin: 1rem;
display: inline-block; display: inline-block;
text-align: center;
transform-origin: center top; transform-origin: center top;
} }

View File

@@ -227,6 +227,9 @@
<script src="js/modules/product-canvas.js"></script> <script src="js/modules/product-canvas.js"></script>
<script src="js/modules/swot.js"></script> <script src="js/modules/swot.js"></script>
<script src="js/modules/echarts.js"></script> <script src="js/modules/echarts.js"></script>
<script src="js/modules/mermaid.js"></script>
<script src="js/vendor/mermaid.min.js"></script>
<script src="js/vendor/svg-pan-zoom.min.js"></script>
<script src="libs/js/echarts.min.js"></script> <script src="libs/js/echarts.min.js"></script>
<script src="js/core/module-runtime.js"></script> <script src="js/core/module-runtime.js"></script>
<script src="js/core/app-shell.js"></script> <script src="js/core/app-shell.js"></script>

View File

@@ -13,13 +13,16 @@ class APIClient {
this.promptFiles = { this.promptFiles = {
canvas: 'prompts/canvas-prompt.txt', canvas: 'prompts/canvas-prompt.txt',
swot: 'prompts/swot-prompt.txt', swot: 'prompts/swot-prompt.txt',
echarts: 'prompts/echarts-prompt.txt' echarts: 'prompts/echarts-prompt.txt',
mermaid: 'prompts/mermaid-prompt.txt'
}; };
this.promptFallbacks = { this.promptFallbacks = {
canvas: '你是一个专业的产品战略分析师,擅长创建产品画布。', canvas: '你是一个专业的产品战略分析师,擅长创建产品画布。',
swot: '你是一个专业的商业战略分析师擅长进行SWOT分析。', swot: '你是一个专业的商业战略分析师擅长进行SWOT分析。',
echarts: echarts:
'你是一个资深的数据可视化专家,精通将自然语言需求转化为 ECharts 配置对象,请输出结构化 JSON option。', '你是一个资深的数据可视化专家,精通将自然语言需求转化为 ECharts 配置对象,请输出结构化 JSON option。',
mermaid:
'你是一个资深的可视化工程师,擅长用 Mermaid 语法创建清晰的图示,请只输出一个 ```mermaid 代码块。',
default: default:
'你是一个可靠的智能助手,请直接回答用户的问题并提供结构化输出。' '你是一个可靠的智能助手,请直接回答用户的问题并提供结构化输出。'
}; };

View File

@@ -30,6 +30,8 @@
this.pendingCancel = false; this.pendingCancel = false;
this.streamState = null; this.streamState = null;
this.echartsInstance = null; this.echartsInstance = null;
this.mermaidPanZoom = null;
this.mermaidInitialized = false;
this.globalStore = moduleRuntime.storageService.global(); this.globalStore = moduleRuntime.storageService.global();
this.activeModuleId = null; this.activeModuleId = null;
@@ -138,17 +140,23 @@
if (this.el.downloadSvgBtn) { if (this.el.downloadSvgBtn) {
this.el.downloadSvgBtn.addEventListener('click', () => this.el.downloadSvgBtn.addEventListener('click', () =>
this.downloadArtifact() this.downloadArtifact().catch((error) =>
console.error('下载SVG失败:', error)
)
); );
} }
if (this.el.copyImageBtn) { if (this.el.copyImageBtn) {
this.el.copyImageBtn.addEventListener('click', () => this.el.copyImageBtn.addEventListener('click', () =>
this.copyArtifactImage() this.copyArtifactImage().catch((error) =>
console.error('复制图片失败:', error)
)
); );
} }
if (this.el.exportImageBtn) { if (this.el.exportImageBtn) {
this.el.exportImageBtn.addEventListener('click', () => this.el.exportImageBtn.addEventListener('click', () =>
this.exportArtifactAsImage() this.exportArtifactAsImage().catch((error) =>
console.error('导出图片失败:', error)
)
); );
} }
if (this.el.viewCodeBtn) { if (this.el.viewCodeBtn) {
@@ -723,6 +731,8 @@
this.updateStreamingContent(container, fullContent); this.updateStreamingContent(container, fullContent);
if (manifest.artifact?.type === 'svg') { if (manifest.artifact?.type === 'svg') {
this.processSvgStreamChunk(manifest, fullContent, streamState); this.processSvgStreamChunk(manifest, fullContent, streamState);
} else if (manifest.artifact?.type === 'mermaid') {
this.processMermaidStreamChunk(manifest, fullContent, streamState);
} }
}; };
@@ -817,6 +827,19 @@
messageId, messageId,
timestamp timestamp
}; };
} else if (
manifest.artifact.type === 'mermaid' &&
parsedResult.code
) {
artifactId = Utils.generateId('mermaid');
artifactPayload = {
id: artifactId,
type: manifest.artifact.type,
code: parsedResult.code,
svgContent: streamContext?.mermaid?.svgContent || null,
messageId,
timestamp
};
} else if ( } else if (
manifest.artifact.type === 'echarts-option' && manifest.artifact.type === 'echarts-option' &&
parsedResult.option parsedResult.option
@@ -891,6 +914,11 @@
const after = trim(parsedResult.afterText); const after = trim(parsedResult.afterText);
if (before) segments.push(before); if (before) segments.push(before);
if (after) segments.push(after); if (after) segments.push(after);
} else if (manifest.artifact?.type === 'mermaid') {
const before = trim(parsedResult.beforeText);
const after = trim(parsedResult.afterText);
if (before) segments.push(before);
if (after) segments.push(after);
} else if (manifest.artifact?.type === 'echarts-option') { } else if (manifest.artifact?.type === 'echarts-option') {
const before = trim(parsedResult.beforeText); const before = trim(parsedResult.beforeText);
const after = trim(parsedResult.afterText); const after = trim(parsedResult.afterText);
@@ -995,23 +1023,231 @@
} }
renderTemporarySvg(svgMarkup, isPartial = false, manifest = null) { renderTemporarySvg(svgMarkup, isPartial = false, manifest = null) {
if (!this.el.viewer || !svgMarkup) return; const moduleId = manifest?.id || this.activeModuleId;
this.el.viewer.innerHTML = ''; this.renderSvgMarkup(svgMarkup, moduleId, {
const wrapper = document.createElement('div'); opacity: isPartial ? 0.9 : 1
wrapper.className = 'svg-content-wrapper'; });
wrapper.innerHTML = svgMarkup;
this.el.viewer.appendChild(wrapper);
if (isPartial) {
wrapper.style.opacity = '0.9';
} else {
wrapper.style.opacity = '1';
} }
const uiState = this.runtime.getUiState(
manifest?.id || this.activeModuleId, getCurrentEChartsSvgElement() {
{ zoom: 1 } if (!this.echartsInstance) return null;
const dom = this.echartsInstance.getDom();
if (!dom) return null;
return dom.querySelector('svg');
}
getSvgStringFromElement(svgElement) {
if (!svgElement) return null;
const serializer = new XMLSerializer();
let svgContent = serializer.serializeToString(svgElement);
if (!svgContent.match(/^<svg[^>]+xmlns=/)) {
svgContent = svgContent.replace(
'<svg',
'<svg xmlns="http://www.w3.org/2000/svg"'
); );
wrapper.style.transform = `scale(${uiState.zoom})`; }
wrapper.style.transformOrigin = 'center top'; return svgContent;
}
initializeMermaidPanZoom(svgElement, manifest) {
if (!svgElement) return;
if (!window.svgPanZoom) {
console.warn('svgPanZoom 脚本未加载,无法提供平移缩放');
return;
}
this.destroyMermaidPanZoom();
let doPan = false;
let mousePos = { x: 0, y: 0 };
let panZoomInstance = null;
const onMouseDown = (ev) => {
if (!ev) return;
doPan = true;
mousePos = { x: ev.clientX, y: ev.clientY };
};
const onMouseMove = (ev) => {
if (!doPan || !panZoomInstance) return;
panZoomInstance.panBy({
x: ev.clientX - mousePos.x,
y: ev.clientY - mousePos.y
});
mousePos = { x: ev.clientX, y: ev.clientY };
const selection = window.getSelection && window.getSelection();
if (selection && selection.removeAllRanges) {
selection.removeAllRanges();
}
};
const onMouseUp = () => {
doPan = false;
};
const eventsHandler = {
haltEventListeners: ['mousedown', 'mousemove', 'mouseup'],
init(options) {
options.svgElement.addEventListener('mousedown', onMouseDown, false);
options.svgElement.addEventListener('mousemove', onMouseMove, false);
options.svgElement.addEventListener('mouseup', onMouseUp, false);
},
destroy(options) {
options.svgElement.removeEventListener('mousedown', onMouseDown, false);
options.svgElement.removeEventListener('mousemove', onMouseMove, false);
options.svgElement.removeEventListener('mouseup', onMouseUp, false);
}
};
this.mermaidPanZoom = window.svgPanZoom(svgElement, {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true,
minZoom: 0.25,
maxZoom: 3,
customEventsHandler: eventsHandler
});
panZoomInstance = this.mermaidPanZoom;
const uiState = this.runtime.getUiState(manifest.id, { zoom: 1 });
const initialZoom = uiState.zoom || 1;
this.mermaidPanZoom.zoom(initialZoom);
this.mermaidPanZoom.setOnZoom((zoomLevel) => {
this.runtime.updateUiState(manifest.id, { zoom: zoomLevel });
});
}
destroyMermaidPanZoom() {
if (this.mermaidPanZoom && typeof this.mermaidPanZoom.destroy === 'function') {
this.mermaidPanZoom.destroy();
}
this.mermaidPanZoom = null;
}
isZoomableManifest(manifest) {
const type = manifest?.artifact?.type;
return type === 'svg' || type === 'mermaid';
}
processMermaidStreamChunk(manifest, fullContent, streamState) {
if (!streamState) return;
if (!streamState.mermaid) {
streamState.mermaid = {
started: false,
artifactId: null,
beforeText: ''
};
}
const ctx = streamState.mermaid;
const startPattern = manifest.artifact?.startPattern || /```mermaid/i;
if (!ctx.started) {
const match = fullContent.match(startPattern);
if (match) {
ctx.started = true;
ctx.artifactId = ctx.artifactId || Utils.generateId('mermaid');
ctx.beforeText = fullContent.substring(0, match.index);
this.updateMermaidPlaceholder(streamState.container, manifest, ctx);
this.showViewerStreaming(manifest);
}
}
}
updateMermaidPlaceholder(container, manifest, ctx) {
if (!container) return;
const beforeHtml = this.parseMarkdownContent(ctx.beforeText || '');
const label = manifest.label || '图表';
container.innerHTML = `
<div class="chat-bubble-ai relative streaming-text" data-message-id="${container.dataset.messageId}">
<div>
${beforeHtml}
<div class="svg-drawing-placeholder" data-temp-id="${ctx.artifactId}">
🧠 正在生成${label}代码...
</div>
<div class="typing-cursor"></div>
</div>
</div>
`;
Utils.scrollToBottom(this.el.chatHistory);
}
async ensureMermaidReady() {
if (this.mermaidInitialized) return;
if (!window.mermaid) {
throw new Error('Mermaid 脚本未加载,请检查资源引入');
}
window.mermaid.initialize({
startOnLoad: false,
securityLevel: 'loose',
theme: 'default'
});
this.mermaidInitialized = true;
}
async renderMermaidArtifact(artifact, manifest) {
if (!this.el.viewer) return;
this.showViewerStreaming(manifest);
try {
const svgContent = await this.getMermaidSvgContent(
artifact,
manifest
);
this.destroyMermaidPanZoom();
this.renderSvgMarkup(svgContent, this.activeModuleId, {
applyTransform: false
});
const svgElement = this.el.viewer.querySelector('svg');
if (svgElement) {
svgElement.setAttribute('id', 'mermaidSvg');
this.initializeMermaidPanZoom(svgElement, manifest);
}
} catch (error) {
this.destroyMermaidPanZoom();
console.error('Mermaid 渲染失败:', error);
this.el.viewer.innerHTML = `
<div class="p-4 text-center text-red-500 font-bold">
Mermaid 渲染失败:${Utils.escapeHtml(error.message || '未知错误')}
</div>
`;
}
}
async getMermaidSvgContent(artifact, manifest) {
if (artifact.svgContent) {
return artifact.svgContent;
}
await this.ensureMermaidReady();
const renderId = `mermaid-${artifact.id || Utils.generateId('mermaid')}-${Date.now()}`;
const code = artifact.code || artifact.content || '';
if (!code.trim()) {
throw new Error('缺少 Mermaid 代码,无法渲染');
}
const { svg } = await window.mermaid.render(renderId, code);
const updatedArtifact = {
...artifact,
svgContent: svg
};
this.runtime.saveArtifact(
manifest?.id || this.activeModuleId,
updatedArtifact.id,
updatedArtifact
);
return svg;
}
async getSvgMarkupForArtifact(artifact, manifest) {
if (!artifact) return null;
if (artifact.type === 'svg') {
return artifact.content;
}
if (artifact.type === 'mermaid') {
return await this.getMermaidSvgContent(
artifact,
manifest || this.getActiveManifest()
);
}
if (artifact.type === 'echarts-option') {
const svgElement = this.getCurrentEChartsSvgElement();
if (!svgElement) return null;
return this.getSvgStringFromElement(svgElement);
}
return null;
} }
openCodeModal(content = '') { openCodeModal(content = '') {
@@ -1101,8 +1337,12 @@
} }
this.runtime.setActiveArtifact(manifest.id, artifactId); this.runtime.setActiveArtifact(manifest.id, artifactId);
if (artifact.type === 'svg') { if (artifact.type === 'svg') {
this.destroyMermaidPanZoom();
this.renderSvgArtifact(artifact); this.renderSvgArtifact(artifact);
} else if (artifact.type === 'mermaid') {
this.renderMermaidArtifact(artifact, manifest);
} else if (artifact.type === 'echarts-option') { } else if (artifact.type === 'echarts-option') {
this.destroyMermaidPanZoom();
this.renderEChartsArtifact(artifact); this.renderEChartsArtifact(artifact);
} }
this.highlightActivePlaceholder(); this.highlightActivePlaceholder();
@@ -1110,16 +1350,26 @@
} }
renderSvgArtifact(artifact) { renderSvgArtifact(artifact) {
if (!this.el.viewer) return; this.renderSvgMarkup(artifact.content, this.activeModuleId);
}
renderSvgMarkup(svgMarkup, moduleId = this.activeModuleId, options = {}) {
if (!this.el.viewer || !svgMarkup) return;
const { opacity = 1, applyTransform = true } = options;
this.el.viewer.innerHTML = ''; this.el.viewer.innerHTML = '';
const wrapper = document.createElement('div'); const wrapper = document.createElement('div');
wrapper.className = 'svg-content-wrapper'; wrapper.className = 'svg-content-wrapper';
wrapper.innerHTML = artifact.content; wrapper.innerHTML = svgMarkup;
wrapper.style.opacity = opacity;
this.el.viewer.appendChild(wrapper); this.el.viewer.appendChild(wrapper);
const uiState = this.runtime.getUiState(this.activeModuleId, { const uiState = this.runtime.getUiState(moduleId, {
zoom: 1 zoom: 1
}); });
if (applyTransform) {
wrapper.style.transform = `scale(${uiState.zoom})`; wrapper.style.transform = `scale(${uiState.zoom})`;
} else {
wrapper.style.transform = '';
}
wrapper.style.transformOrigin = 'center top'; wrapper.style.transformOrigin = 'center top';
} }
@@ -1142,31 +1392,46 @@
this.echartsInstance.dispose(); this.echartsInstance.dispose();
} }
this.echartsInstance = window.echarts.init(chartContainer, null, { this.echartsInstance = window.echarts.init(chartContainer, null, {
renderer: 'canvas' renderer: 'svg',
useDirtyRect: false
}); });
this.echartsInstance.setOption(artifact.option, true); this.echartsInstance.setOption(artifact.option, true);
} }
adjustZoom(delta) { adjustZoom(delta) {
const manifest = this.getActiveManifest(); const manifest = this.getActiveManifest();
if (manifest.artifact?.type !== 'svg') return; if (!this.isZoomableManifest(manifest)) return;
const uiState = this.runtime.getUiState(manifest.id, { zoom: 1 }); const uiState = this.runtime.getUiState(manifest.id, { zoom: 1 });
const nextZoom = Math.min( const nextZoom = Math.min(
3, 3,
Math.max(0.25, parseFloat((uiState.zoom + delta).toFixed(2))) Math.max(0.25, parseFloat((uiState.zoom + delta).toFixed(2)))
); );
this.runtime.updateUiState(manifest.id, { zoom: nextZoom }); this.runtime.updateUiState(manifest.id, { zoom: nextZoom });
if (manifest.artifact?.type === 'mermaid') {
if (this.mermaidPanZoom) {
this.mermaidPanZoom.zoom(nextZoom);
}
} else {
this.renderActiveArtifact(); this.renderActiveArtifact();
} }
}
resetZoom() { resetZoom() {
const manifest = this.getActiveManifest(); const manifest = this.getActiveManifest();
if (manifest.artifact?.type !== 'svg') return; if (!this.isZoomableManifest(manifest)) return;
this.runtime.updateUiState(manifest.id, { zoom: 1 }); this.runtime.updateUiState(manifest.id, { zoom: 1 });
if (manifest.artifact?.type === 'mermaid') {
if (this.mermaidPanZoom) {
this.mermaidPanZoom.zoom(1);
this.mermaidPanZoom.resetPan();
}
} else {
this.renderActiveArtifact(); this.renderActiveArtifact();
} }
}
downloadArtifact() { async downloadArtifact() {
const manifest = this.getActiveManifest(); const manifest = this.getActiveManifest();
const state = this.runtime.getState(manifest.id); const state = this.runtime.getState(manifest.id);
const id = state.currentArtifactId; const id = state.currentArtifactId;
@@ -1174,11 +1439,12 @@
const artifact = state.artifacts[id]; const artifact = state.artifacts[id];
if (!artifact) return; if (!artifact) return;
if (artifact.type === 'svg') { const svgMarkup = await this.getSvgMarkupForArtifact(artifact, manifest);
Utils.downloadFile(artifact.content, `${manifest.id}.svg`, 'image/svg+xml'); if (!svgMarkup) {
} else {
alert('当前图表不支持导出 SVG请使用导出图片功能'); alert('当前图表不支持导出 SVG请使用导出图片功能');
return;
} }
Utils.downloadFile(svgMarkup, `${manifest.id}.svg`, 'image/svg+xml');
} }
async copyArtifactImage() { async copyArtifactImage() {
@@ -1189,12 +1455,13 @@
const artifact = state.artifacts[id]; const artifact = state.artifacts[id];
if (!artifact) return; if (!artifact) return;
if (artifact.type !== 'svg') { const svgContent = await this.getSvgMarkupForArtifact(artifact, manifest);
if (!svgContent) {
alert('暂不支持复制此类型图表到剪贴板'); alert('暂不支持复制此类型图表到剪贴板');
return; return;
} }
const svgBlob = new Blob([artifact.content], { const svgBlob = new Blob([svgContent], {
type: 'image/svg+xml' type: 'image/svg+xml'
}); });
const svgUrl = URL.createObjectURL(svgBlob); const svgUrl = URL.createObjectURL(svgBlob);
@@ -1210,7 +1477,14 @@
ctx.setTransform(this.imageExportScale, 0, 0, this.imageExportScale, 0, 0); ctx.setTransform(this.imageExportScale, 0, 0, this.imageExportScale, 0, 0);
ctx.drawImage(image, 0, 0); ctx.drawImage(image, 0, 0);
const finalize = () => URL.revokeObjectURL(svgUrl);
canvas.toBlob(async (blob) => { canvas.toBlob(async (blob) => {
if (!blob) {
finalize();
alert('复制失败,请稍后再试');
return;
}
try { try {
const clipboardItem = new ClipboardItem({ 'image/png': blob }); const clipboardItem = new ClipboardItem({ 'image/png': blob });
await navigator.clipboard.write([clipboardItem]); await navigator.clipboard.write([clipboardItem]);
@@ -1219,13 +1493,17 @@
console.error('复制失败:', error); console.error('复制失败:', error);
alert('复制失败,请稍后再试'); alert('复制失败,请稍后再试');
} finally { } finally {
URL.revokeObjectURL(svgUrl); finalize();
} }
}); });
}; };
image.onerror = () => {
URL.revokeObjectURL(svgUrl);
alert('复制失败,请稍后再试');
};
} }
exportArtifactAsImage() { async exportArtifactAsImage() {
const manifest = this.getActiveManifest(); const manifest = this.getActiveManifest();
const state = this.runtime.getState(manifest.id); const state = this.runtime.getState(manifest.id);
const id = state.currentArtifactId; const id = state.currentArtifactId;
@@ -1233,8 +1511,9 @@
const artifact = state.artifacts[id]; const artifact = state.artifacts[id];
if (!artifact) return; if (!artifact) return;
if (artifact.type === 'svg') { const svgContent = await this.getSvgMarkupForArtifact(artifact, manifest);
const svgBlob = new Blob([artifact.content], { if (svgContent) {
const svgBlob = new Blob([svgContent], {
type: 'image/svg+xml' type: 'image/svg+xml'
}); });
const svgUrl = URL.createObjectURL(svgBlob); const svgUrl = URL.createObjectURL(svgBlob);
@@ -1257,7 +1536,14 @@
document.body.removeChild(link); document.body.removeChild(link);
URL.revokeObjectURL(svgUrl); URL.revokeObjectURL(svgUrl);
}; };
} else if (artifact.type === 'echarts-option') { image.onerror = () => {
URL.revokeObjectURL(svgUrl);
alert('导出图片失败,请稍后再试');
};
return;
}
if (artifact.type === 'echarts-option') {
if (!this.echartsInstance) { if (!this.echartsInstance) {
alert('图表实例未准备好,无法导出'); alert('图表实例未准备好,无法导出');
return; return;
@@ -1336,7 +1622,8 @@
const state = this.runtime.getState(manifest.id); const state = this.runtime.getState(manifest.id);
const hasArtifact = !!state.currentArtifactId; const hasArtifact = !!state.currentArtifactId;
if (manifest.artifact?.type !== 'svg') { const isZoomable = this.isZoomableManifest(manifest);
if (!isZoomable) {
this.el.zoomInBtn && (this.el.zoomInBtn.disabled = true); this.el.zoomInBtn && (this.el.zoomInBtn.disabled = true);
this.el.zoomOutBtn && (this.el.zoomOutBtn.disabled = true); this.el.zoomOutBtn && (this.el.zoomOutBtn.disabled = true);
this.el.zoomResetBtn && (this.el.zoomResetBtn.disabled = true); this.el.zoomResetBtn && (this.el.zoomResetBtn.disabled = true);
@@ -1362,7 +1649,6 @@
this.el.copyImageBtn.disabled = this.el.copyImageBtn.disabled =
!hasArtifact || !hasArtifact ||
!this.copyClipboardSupported || !this.copyClipboardSupported ||
manifest.artifact?.type !== 'svg' ||
manifest.exports?.allowClipboard === false; manifest.exports?.allowClipboard === false;
} }
if (this.el.exportImageBtn) { if (this.el.exportImageBtn) {
@@ -1388,6 +1674,7 @@
this.echartsInstance.dispose(); this.echartsInstance.dispose();
this.echartsInstance = null; this.echartsInstance = null;
} }
this.destroyMermaidPanZoom();
this.renderConversationHistory(); this.renderConversationHistory();
this.showViewerPlaceholder(manifest.ui?.placeholderText || ''); this.showViewerPlaceholder(manifest.ui?.placeholderText || '');
this.updateToolbarState(); this.updateToolbarState();

58
js/modules/mermaid.js Normal file
View File

@@ -0,0 +1,58 @@
(function registerMermaidModule(global) {
'use strict';
if (!global.ModuleRegistry) {
throw new Error('ModuleRegistry 未初始化');
}
const MERMAID_FENCE = /```mermaid\s*([\s\S]*?)```/i;
const parseResponse = (content = '') => {
const match = content.match(MERMAID_FENCE);
if (match) {
const beforeText = content.substring(0, match.index).trim();
const afterText = content.substring(match.index + match[0].length).trim();
const code = match[1].trim();
return {
code,
beforeText,
afterText
};
}
return {
code: '',
beforeText: content.trim(),
afterText: ''
};
};
global.ModuleRegistry.register({
id: 'mermaid',
label: 'Mermaid 图示',
icon: 'ph:circles-three-plus-duotone',
renderer: 'mermaid',
promptKey: 'mermaid',
storageNamespace: 'module:mermaid',
chat: {
placeholder: '描述你想生成的流程图、时序图或思维导图…',
streamStartToken: '```mermaid',
contextWindow: 8
},
artifact: {
type: 'mermaid',
fence: 'mermaid',
startPattern: /```mermaid/i,
parser: parseResponse
},
hooks: {},
exports: {
allowSvg: true,
allowPng: true,
allowClipboard: true,
allowCode: true
},
ui: {
placeholderText: '生成的 Mermaid 图示将在此处显示'
}
});
})(window);

1646
js/vendor/mermaid.min.js vendored Normal file

File diff suppressed because one or more lines are too long

3
js/vendor/svg-pan-zoom.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,38 @@
你是一个 mermaid 编写专家,我会给你提供一个场景,你来输出 mermaid 代码,只输出代码即可,不要输出其他的内容,无论我提出什么你都要以 mermaid 代码格式回答我;如果我没指定 mermaid 图的类型,默认使用 `flowchart LR`。
语法规则库 ()
"Mermaid 语法规则和最佳实践"
'((特殊字符处理 . "使用双引号包裹含有特殊字符或空格的文本")
(HTML实体编码 . "&lt; &gt; &amp; # 等字符使用 HTML 实体编码")
(节点命名 . "使用简洁有意义的 ID避免中文 ID")
(连接符规范 . "flowchart: --> | sequenceDiagram: ->> | classDiagram: --|>")
(注释规范 . "使用 %% 添加注释说明")
(序号处理 . "序号后不要跟空格,如 1.xxx 而非 1. xxx")
(颜色分层 . "使用不同背景色区分层级和分组"))
图表类型映射 ()
"定义支持的图表类型及其特征"
'((flowchart . (关键词 ("流程" "步骤" "过程" "决策" "分支")
语法 "flowchart TD"
适用场景 "业务流程、决策树、算法步骤"))
(sequenceDiagram . (关键词 ("交互" "通信" "调用" "请求" "响应")
语法 "sequenceDiagram"
适用场景 "系统交互、API调用、用户操作"))
(classDiagram . (关键词 ("类" "对象" "继承" "关系" "属性" "方法")
语法 "classDiagram"
适用场景 "系统设计、数据模型、架构图"))
(stateDiagram . (关键词 ("状态" "转换" "事件" "条件")
语法 "stateDiagram-v2"
适用场景 "状态机、业务状态、流程状态"))
(gantt . (关键词 ("时间" "计划" "任务" "进度" "里程碑")
语法 "gantt"
适用场景 "项目管理、时间规划、任务安排"))
(pie . (关键词 ("比例" "占比" "分布" "百分比")
语法 "pie"
适用场景 "数据分析、统计展示、比例关系")))
(专业领域 . '(流程图 时序图 类图 状态图 甘特图 饼图))
(核心能力 . '(文本分析 结构识别 语法生成 错误修复))
(技术特长 . '(Mermaid语法 图表设计 可视化 代码优化))
(工作原则 . '(准确理解 智能选择 规范输出 易于理解))
(输出标准 . '(语法正确 结构清晰 美观实用 可直接使用))