From 554679154974a702d8a4bc5bc937b61fd250a0a1 Mon Sep 17 00:00:00 2001 From: XiaoFeng <117837368+Fzhiyu1@users.noreply.github.com> Date: Fri, 9 Jan 2026 17:02:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Plan=E5=8D=A1=E7=89=87=E6=94=AF?= =?UTF-8?q?=E6=8C=81Markdown=E6=B8=B2=E6=9F=93=E5=92=8C=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加renderPlanMarkdown函数,支持标题、列表、表格、代码块等 - 添加renderPlanSteps函数,智能解析JSON格式步骤对象 - 步骤显示模块名、描述、输入输出、逻辑等详细信息 - 添加plan-summary和step-details样式 --- src/views/planCard.ts | 195 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 185 insertions(+), 10 deletions(-) diff --git a/src/views/planCard.ts b/src/views/planCard.ts index c7fd3d1..bb8d3f2 100644 --- a/src/views/planCard.ts +++ b/src/views/planCard.ts @@ -4,6 +4,7 @@ * 功能说明: * - 显示执行计划的卡片界面 * - 包含计划标题、摘要和步骤列表 + * - 摘要支持 Markdown 格式渲染 * - 提供确认执行、修改计划、取消等操作按钮 */ @@ -43,11 +44,62 @@ export function getPlanCardStyles(): string { padding: 16px; } .plan-summary { - color: var(--vscode-descriptionForeground); + color: var(--vscode-foreground); margin-bottom: 12px; font-size: 13px; - line-height: 1.5; + line-height: 1.6; } + /* Markdown 渲染样式 */ + .plan-summary h1, .plan-summary h2, .plan-summary h3, .plan-summary h4 { + margin: 16px 0 8px 0; + font-weight: 600; + color: var(--vscode-foreground); + } + .plan-summary h1 { font-size: 18px; border-bottom: 1px solid var(--vscode-input-border); padding-bottom: 6px; } + .plan-summary h2 { font-size: 16px; } + .plan-summary h3 { font-size: 14px; } + .plan-summary h4 { font-size: 13px; } + .plan-summary p { margin: 8px 0; } + .plan-summary ul, .plan-summary ol { + margin: 8px 0; + padding-left: 24px; + } + .plan-summary li { margin: 4px 0; } + .plan-summary code { + background: var(--vscode-textCodeBlock-background); + padding: 2px 6px; + border-radius: 3px; + font-family: var(--vscode-editor-font-family); + font-size: 12px; + } + .plan-summary pre { + background: var(--vscode-textCodeBlock-background); + padding: 12px; + border-radius: 4px; + overflow-x: auto; + margin: 8px 0; + } + .plan-summary pre code { + background: none; + padding: 0; + } + .plan-summary table { + border-collapse: collapse; + width: 100%; + margin: 8px 0; + font-size: 12px; + } + .plan-summary th, .plan-summary td { + border: 1px solid var(--vscode-input-border); + padding: 6px 10px; + text-align: left; + } + .plan-summary th { + background: var(--vscode-sideBar-background); + font-weight: 600; + } + .plan-summary strong { font-weight: 600; } + .plan-summary em { font-style: italic; } .plan-steps { font-size: 13px; } @@ -58,6 +110,15 @@ export function getPlanCardStyles(): string { border-radius: 4px; line-height: 1.5; } + .plan-step strong { + color: var(--vscode-textLink-foreground); + } + .step-details { + margin-top: 4px; + font-size: 12px; + color: var(--vscode-descriptionForeground); + line-height: 1.4; + } .plan-step:last-child { margin-bottom: 0; } @@ -158,6 +219,117 @@ export function getPlanCardStyles(): string { */ export function getPlanCardScript(): string { return ` + // 简单的 Markdown 渲染函数 + function renderPlanMarkdown(text) { + if (!text) return ''; + + let html = text; + + // 转义 HTML 特殊字符(保留换行) + html = html.replace(/&/g, '&') + .replace(//g, '>'); + + // 代码块 (\`\`\`code\`\`\`) + html = html.replace(/\\x60\\x60\\x60([\\s\\S]*?)\\x60\\x60\\x60/g, '
$1');
+
+ // 行内代码 (\`code\`)
+ html = html.replace(/\\x60([^\\x60]+)\\x60/g, '$1');
+
+ // 表格处理
+ html = html.replace(/^\\|(.+)\\|\\s*\\n\\|[-:\\s|]+\\|\\s*\\n((?:\\|.+\\|\\s*\\n?)+)/gm, function(match, header, body) {
+ const headers = header.split('|').map(h => h.trim()).filter(h => h);
+ const rows = body.trim().split('\\n').map(row =>
+ row.split('|').map(cell => cell.trim()).filter(cell => cell)
+ );
+
+ let table = '| ' + h + ' | '); + table += '
|---|
| ' + cell + ' | '); + table += '
$1
'); + + // 清理多余的空行 + html = html.replace(/<\\/p>/g, ''); + html = html.replace(/\\n{2,}/g, '\\n'); + + return html; + } + + // 解析并渲染步骤列表 + function renderPlanSteps(steps) { + if (!steps || steps.length === 0) return ''; + + // 尝试解析 JSON 格式的步骤 + let parsedSteps = steps; + + // 如果是单个字符串且看起来像 JSON 数组,尝试解析 + if (steps.length === 1 && typeof steps[0] === 'string') { + const str = steps[0].trim(); + if (str.startsWith('[') && str.endsWith(']')) { + try { + parsedSteps = JSON.parse(str); + } catch (e) { + // 解析失败,保持原样 + } + } + } + + return parsedSteps.map((step, i) => { + // 如果是对象,格式化显示 + if (typeof step === 'object' && step !== null) { + const name = step.name || step.id || ('步骤 ' + (i + 1)); + const desc = step.description || ''; + const inputs = step.inputs || ''; + const outputs = step.outputs || ''; + const logic = step.logic || ''; + + let content = '' + name + ''; + if (desc) content += ':' + desc; + + let details = []; + if (inputs) details.push('输入: ' + inputs); + if (outputs) details.push('输出: ' + outputs); + if (logic) details.push('逻辑: ' + logic); + + if (details.length > 0) { + content += '