feat(jvm): 接入 AI 结构化变更计划

- 新增 JVM AI 计划解析器与 fenced json 契约测试
- 为 JVM 资源页注入 AI 计划生成 prompt 并支持回填草稿
- 在 AI 对话上下文中补充 JVM 资源约束与应用入口
This commit is contained in:
Syngnat
2026-04-23 12:42:02 +08:00
parent 9a61622568
commit 3cb2d494cc
5 changed files with 376 additions and 6 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from 'react';
import { Tooltip, message } from 'antd';
import { Button, Tooltip, message } from 'antd';
import { UserOutlined, RobotOutlined, EditOutlined, ReloadOutlined, DeleteOutlined, CheckOutlined, CopyOutlined, PlayCircleOutlined, ApiOutlined, LoadingOutlined, CaretRightOutlined, CaretDownOutlined } from '@ant-design/icons';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
@@ -7,8 +7,10 @@ import mermaid from 'mermaid';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { AIChatMessage, AIToolCall } from '../../types';
import { useStore } from '../../store';
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
import { normalizeAiMarkdown } from '../../utils/aiMarkdown';
import { extractJVMChangePlan } from '../../utils/jvmAiPlan';
// 🔧 性能优化:将 ReactMarkdown 包装为 Memo 组件并提取固定的 plugins
const remarkPlugins = [remarkGfm];
@@ -568,6 +570,12 @@ export const AIMessageBubble: React.FC<AIMessageBubbleProps> = React.memo(({ msg
}
return { displayContent: content, parsedThinking: '' };
}, [msg.content, msg.thinking]);
const jvmPlan = React.useMemo(() => {
if (isUser) {
return null;
}
return extractJVMChangePlan(displayContent);
}, [displayContent, isUser]);
const isTypingThinking = !!(msg.loading && msg.phase === 'thinking');
if (msg.role === 'tool') return null;
@@ -695,6 +703,22 @@ export const AIMessageBubble: React.FC<AIMessageBubbleProps> = React.memo(({ msg
activeDbName={activeDbName}
/>
)}
{!isUser && jvmPlan && (
<div style={{ marginTop: 12 }}>
<Button
size="small"
type="primary"
onClick={() => {
const targetTabId = useStore.getState().activeTabId;
window.dispatchEvent(new CustomEvent('gonavi:jvm-apply-ai-plan', {
detail: { plan: jvmPlan, targetTabId },
}));
}}
>
JVM
</Button>
</div>
)}
{/* 错误原文复制按钮 */}
{!isUser && msg.rawError && (
<div style={{ marginTop: 8 }}>