feat: 抽取 plan-card 为独立组件并优化样式
主要改动: 1. 创建独立的 planCard.ts 组件 - 导出 getPlanCardStyles() 和 getPlanCardScript() - 包含 renderPlanCardInSegment 和 renderPlanCardStatic 两个渲染函数 2. 优化 messageArea.ts - 移除原有的 plan-card 内联样式和脚本(约 80 行) - 引入 planCard 组件并调用其函数 - 添加 addRule 和 updateNode 工具映射 3. 优化计划卡片样式 - 调整外边距:segment-plan 从 8px 增加到 12px - 优化内边距:header 12px 16px,body 16px,actions 14px 16px - 改进按钮布局:使用垂直布局,按钮和输入框分行显示 - 增加元素间距:步骤间距 6px,按钮间距 10px - 添加行高 1.5 提升可读性 4. 添加 plannerIconSvg 图标
This commit is contained in:
@ -76,3 +76,8 @@ export const stopIconSvg = `
|
|||||||
*/
|
*/
|
||||||
export const agentIconSvg = `
|
export const agentIconSvg = `
|
||||||
<svg t="1767101071638" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7779" width="16" height="16" style="display: inline-block; vertical-align: middle;"><path d="M173.474909 410.414545c-20.293818 0-33.838545-13.498182-33.838545-33.792v-135.377454C139.636364 187.066182 187.019636 139.636364 241.198545 139.636364h135.447273c20.270545 0 33.815273 13.591273 33.815273 33.885091s-13.591273 33.838545-33.885091 33.838545h-135.447273c-20.317091 0-33.815273 13.591273-33.815272 33.885091v135.377454c0.046545 20.293818-13.498182 33.792-33.838546 33.792z m203.101091 473.902546h-135.447273C187.042909 884.317091 139.636364 836.933818 139.636364 782.754909V647.447273c0-20.386909 13.544727-33.838545 33.838545-33.838546s33.885091 13.451636 33.885091 33.838546v135.330909c0 20.293818 13.544727 33.931636 33.838545 33.931636h135.447273c20.270545 0 33.815273 13.451636 33.815273 33.745455-0.046545 20.340364-13.591273 33.885091-33.885091 33.885091z m406.178909 0H647.447273c-20.386909 0-33.931636-13.544727-33.931637-33.931636 0-20.293818 13.544727-33.745455 33.931637-33.745455h135.330909c20.386909 0 33.838545-13.637818 33.838545-33.931636V647.447273c0-20.386909 13.544727-33.838545 33.931637-33.838546 20.293818 0 33.838545 13.451636 33.838545 33.838546v135.330909c-0.046545 54.178909-47.522909 101.562182-101.608727 101.562182z m67.723636-473.902546c-20.386909 0-33.931636-13.498182-33.931636-33.792v-135.377454c0-20.340364-13.451636-33.885091-33.838545-33.885091H647.447273c-20.386909 0-33.931636-13.498182-33.931637-33.838545S627.083636 139.636364 647.424 139.636364h135.330909c54.085818 0 101.562182 47.429818 101.562182 101.608727v135.377454c0 20.293818-13.544727 33.792-33.838546 33.792z m0 135.493819H173.474909c-20.293818 0-33.838545-13.591273-33.838545-33.931637s13.544727-33.885091 33.838545-33.885091h677.003636c20.293818 0 33.838545 13.591273 33.838546 33.885091s-13.544727 33.931636-33.838546 33.931637z" fill="#8a8a8a" p-id="7780"></path></svg>`;
|
<svg t="1767101071638" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7779" width="16" height="16" style="display: inline-block; vertical-align: middle;"><path d="M173.474909 410.414545c-20.293818 0-33.838545-13.498182-33.838545-33.792v-135.377454C139.636364 187.066182 187.019636 139.636364 241.198545 139.636364h135.447273c20.270545 0 33.815273 13.591273 33.815273 33.885091s-13.591273 33.838545-33.885091 33.838545h-135.447273c-20.317091 0-33.815273 13.591273-33.815272 33.885091v135.377454c0.046545 20.293818-13.498182 33.792-33.838546 33.792z m203.101091 473.902546h-135.447273C187.042909 884.317091 139.636364 836.933818 139.636364 782.754909V647.447273c0-20.386909 13.544727-33.838545 33.838545-33.838546s33.885091 13.451636 33.885091 33.838546v135.330909c0 20.293818 13.544727 33.931636 33.838545 33.931636h135.447273c20.270545 0 33.815273 13.451636 33.815273 33.745455-0.046545 20.340364-13.591273 33.885091-33.885091 33.885091z m406.178909 0H647.447273c-20.386909 0-33.931636-13.544727-33.931637-33.931636 0-20.293818 13.544727-33.745455 33.931637-33.745455h135.330909c20.386909 0 33.838545-13.637818 33.838545-33.931636V647.447273c0-20.386909 13.544727-33.838545 33.931637-33.838546 20.293818 0 33.838545 13.451636 33.838545 33.838546v135.330909c-0.046545 54.178909-47.522909 101.562182-101.608727 101.562182z m67.723636-473.902546c-20.386909 0-33.931636-13.498182-33.931636-33.792v-135.377454c0-20.340364-13.451636-33.885091-33.838545-33.885091H647.447273c-20.386909 0-33.931636-13.498182-33.931637-33.838545S627.083636 139.636364 647.424 139.636364h135.330909c54.085818 0 101.562182 47.429818 101.562182 101.608727v135.377454c0 20.293818-13.544727 33.792-33.838546 33.792z m0 135.493819H173.474909c-20.293818 0-33.838545-13.591273-33.838545-33.931637s13.544727-33.885091 33.838545-33.885091h677.003636c20.293818 0 33.838545 13.591273 33.838546 33.885091s-13.544727 33.931636-33.838546 33.931637z" fill="#8a8a8a" p-id="7780"></path></svg>`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* planner 图标 SVG
|
||||||
|
*/
|
||||||
|
export const plannerIconSvg = `<svg t="1767143425474" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10954" width="16" height="16"><path d="M860.544 633.856c-82.368 0-152.128 69.632-158.464 152h-354.88c-31.616 0-63.296-31.68-63.296-63.296V437.376c12.608 0 25.344 6.4 44.288 6.4h380.16c12.672 69.696 76.032 126.656 152.128 126.656 88.704 0 158.336-69.696 158.336-158.4s-69.632-158.4-158.336-158.4c-76.096 0-139.456 57.024-152.128 126.656h-361.216c-31.616 0-63.296-31.68-63.296-63.296v-133.12h164.736c31.68 0 63.296-22.848 63.296-54.528a55.04 55.04 0 0 0-56-56h-380.16c-31.68 0-70.72 17.984-70.72 56s31.68 54.528 63.36 54.528h133.056v538.624c0 69.696 57.088 126.656 126.72 126.656h386.56c25.344 57.088 82.368 101.376 145.728 101.376a156.8 156.8 0 0 0 158.336-158.4 156.608 156.608 0 0 0-158.208-158.272z m0-316.8c50.624 0 94.912 44.288 94.912 94.976s-44.288 94.976-94.912 94.976c-50.752 0-95.104-44.288-95.104-94.976s44.352-94.976 95.104-94.976z m0 570.24c-50.752 0-95.104-44.352-95.104-95.04s44.352-95.04 95.104-95.04c50.624 0 94.912 44.352 94.912 95.04s-44.288 95.04-94.912 95.04z" p-id="10955" fill="#8a8a8a"></path></svg>`;
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
getWaveformPreviewScript,
|
getWaveformPreviewScript,
|
||||||
} from "./waveformPreviewContent";
|
} from "./waveformPreviewContent";
|
||||||
import { getAgentCardStyles, getAgentCardScript } from "./agentCard";
|
import { getAgentCardStyles, getAgentCardScript } from "./agentCard";
|
||||||
|
import { getPlanCardStyles, getPlanCardScript } from "./planCard";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取消息区域的 HTML 内容
|
* 获取消息区域的 HTML 内容
|
||||||
@ -532,87 +533,7 @@ export function getMessageAreaStyles(): string {
|
|||||||
|
|
||||||
${getAgentCardStyles()}
|
${getAgentCardStyles()}
|
||||||
|
|
||||||
/* 计划卡片样式 */
|
${getPlanCardStyles()}
|
||||||
.segment-plan {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
.plan-card {
|
|
||||||
border: 1px solid var(--vscode-input-border);
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
background: var(--vscode-editor-background);
|
|
||||||
}
|
|
||||||
.plan-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
background: var(--vscode-sideBar-background);
|
|
||||||
border-bottom: 1px solid var(--vscode-input-border);
|
|
||||||
}
|
|
||||||
.plan-icon {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
.plan-title {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.plan-body {
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
.plan-summary {
|
|
||||||
color: var(--vscode-descriptionForeground);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
.plan-steps {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
.plan-step {
|
|
||||||
padding: 6px 8px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
background: var(--vscode-list-hoverBackground);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.plan-step:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.step-num {
|
|
||||||
color: var(--vscode-textLink-foreground);
|
|
||||||
font-weight: 500;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
.plan-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 12px;
|
|
||||||
border-top: 1px solid var(--vscode-input-border);
|
|
||||||
background: var(--vscode-sideBar-background);
|
|
||||||
}
|
|
||||||
.plan-btn {
|
|
||||||
padding: 6px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.plan-btn-confirm {
|
|
||||||
background: var(--vscode-button-background);
|
|
||||||
color: var(--vscode-button-foreground);
|
|
||||||
}
|
|
||||||
.plan-btn-confirm:hover {
|
|
||||||
background: var(--vscode-button-hoverBackground);
|
|
||||||
}
|
|
||||||
.plan-btn-modify {
|
|
||||||
background: var(--vscode-input-background);
|
|
||||||
color: var(--vscode-foreground);
|
|
||||||
border: 1px solid var(--vscode-input-border);
|
|
||||||
}
|
|
||||||
.plan-btn-cancel {
|
|
||||||
background: transparent;
|
|
||||||
color: var(--vscode-descriptionForeground);
|
|
||||||
}
|
|
||||||
|
|
||||||
${getWaveformPreviewContent()}
|
${getWaveformPreviewContent()}
|
||||||
`;
|
`;
|
||||||
@ -631,6 +552,8 @@ export function getMessageAreaScript(): string {
|
|||||||
|
|
||||||
${getAgentCardScript()}
|
${getAgentCardScript()}
|
||||||
|
|
||||||
|
${getPlanCardScript()}
|
||||||
|
|
||||||
// 工具名称映射
|
// 工具名称映射
|
||||||
function getToolDisplayName(toolName) {
|
function getToolDisplayName(toolName) {
|
||||||
const toolNameMap = {
|
const toolNameMap = {
|
||||||
@ -653,6 +576,8 @@ export function getMessageAreaScript(): string {
|
|||||||
'addPlan': '已添加计划',
|
'addPlan': '已添加计划',
|
||||||
'addEdge': '已添加边',
|
'addEdge': '已添加边',
|
||||||
'showPlan': '已显示计划',
|
'showPlan': '已显示计划',
|
||||||
|
'addRule': '已添加规则',
|
||||||
|
'updateNode': '已更新节点',
|
||||||
'spawnExplorer': '代码探索'
|
'spawnExplorer': '代码探索'
|
||||||
};
|
};
|
||||||
return toolNameMap[toolName] || toolName;
|
return toolNameMap[toolName] || toolName;
|
||||||
@ -1026,92 +951,8 @@ export function getMessageAreaScript(): string {
|
|||||||
// 智能体卡片渲染
|
// 智能体卡片渲染
|
||||||
renderAgentCard(segment, segmentDiv);
|
renderAgentCard(segment, segmentDiv);
|
||||||
} else if (segment.type === 'plan') {
|
} else if (segment.type === 'plan') {
|
||||||
// 计划卡片渲染(类似 askUser)
|
// 计划卡片渲染(使用独立组件)
|
||||||
segmentDiv.className += ' segment-plan';
|
renderPlanCardInSegment(segment, segmentDiv, answeredQuestions);
|
||||||
|
|
||||||
// 检查是否已回答
|
|
||||||
const isAnswered = answeredQuestions.has(segment.askId);
|
|
||||||
const selectedAnswer = answeredQuestions.get(segment.askId);
|
|
||||||
|
|
||||||
if (isAnswered) {
|
|
||||||
segmentDiv.classList.add('answered');
|
|
||||||
}
|
|
||||||
|
|
||||||
const stepsHtml = (segment.planSteps || []).map((step, i) =>
|
|
||||||
\`<div class="plan-step"><span class="step-num">\${i + 1}.</span> \${step}</div>\`
|
|
||||||
).join('');
|
|
||||||
|
|
||||||
// 选项按钮
|
|
||||||
const options = ['确认执行', '修改计划', '取消'];
|
|
||||||
const optionsHtml = options.map(opt => {
|
|
||||||
const isSelected = isAnswered && opt === selectedAnswer;
|
|
||||||
return \`<button class="question-option\${isSelected ? ' selected' : ''}" data-option="\${opt}">\${opt}</button>\`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
segmentDiv.innerHTML = \`
|
|
||||||
<div class="plan-card">
|
|
||||||
<div class="plan-header">
|
|
||||||
<span class="plan-icon">📋</span>
|
|
||||||
<span class="plan-title">\${segment.planTitle || '执行计划'}</span>
|
|
||||||
</div>
|
|
||||||
<div class="plan-body">
|
|
||||||
<div class="plan-summary">\${segment.planSummary || ''}</div>
|
|
||||||
<div class="plan-steps">\${stepsHtml}</div>
|
|
||||||
</div>
|
|
||||||
<div class="plan-actions">
|
|
||||||
<div class="question-options" data-ask-id="\${segment.askId}">\${optionsHtml}</div>
|
|
||||||
<div class="custom-input-container" style="display: \${isAnswered ? 'none' : 'flex'};">
|
|
||||||
<input type="text" class="custom-input" placeholder="输入修改建议..." />
|
|
||||||
<button class="custom-submit">提交</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
\`;
|
|
||||||
|
|
||||||
// 只在未回答时添加事件监听
|
|
||||||
if (!isAnswered) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const optionButtons = segmentDiv.querySelectorAll('.question-option');
|
|
||||||
optionButtons.forEach(btn => {
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
const option = this.getAttribute('data-option');
|
|
||||||
// 发送答案到后端
|
|
||||||
handleQuestionAnswerInSegment(segment.askId, option, segmentDiv);
|
|
||||||
// 同时发送 planAction 用于模式切换
|
|
||||||
const actionMap = {
|
|
||||||
'确认执行': 'confirm',
|
|
||||||
'修改计划': 'modify',
|
|
||||||
'取消': 'cancel'
|
|
||||||
};
|
|
||||||
vscode.postMessage({
|
|
||||||
command: 'planAction',
|
|
||||||
action: actionMap[option] || option,
|
|
||||||
planTitle: segment.planTitle
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const submitBtn = segmentDiv.querySelector('.custom-submit');
|
|
||||||
const customInput = segmentDiv.querySelector('.custom-input');
|
|
||||||
if (submitBtn && customInput) {
|
|
||||||
submitBtn.addEventListener('click', function() {
|
|
||||||
const customValue = customInput.value.trim();
|
|
||||||
if (customValue) {
|
|
||||||
handleQuestionAnswerInSegment(segment.askId, customValue, segmentDiv);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
customInput.addEventListener('keypress', function(e) {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
const customValue = customInput.value.trim();
|
|
||||||
if (customValue) {
|
|
||||||
handleQuestionAnswerInSegment(segment.askId, customValue, segmentDiv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSegmentedMessage.appendChild(segmentDiv);
|
currentSegmentedMessage.appendChild(segmentDiv);
|
||||||
@ -1262,46 +1103,8 @@ export function getMessageAreaScript(): string {
|
|||||||
// 智能体卡片渲染
|
// 智能体卡片渲染
|
||||||
renderAgentCard(segment, segmentDiv);
|
renderAgentCard(segment, segmentDiv);
|
||||||
} else if (segment.type === 'plan') {
|
} else if (segment.type === 'plan') {
|
||||||
// 计划卡片渲染
|
// 计划卡片渲染(使用独立组件)
|
||||||
segmentDiv.className += ' segment-plan';
|
renderPlanCardStatic(segment, segmentDiv);
|
||||||
const stepsHtml = (segment.planSteps || []).map((step, i) =>
|
|
||||||
\`<div class="plan-step"><span class="step-num">\${i + 1}.</span> \${step}</div>\`
|
|
||||||
).join('');
|
|
||||||
|
|
||||||
segmentDiv.innerHTML = \`
|
|
||||||
<div class="plan-card">
|
|
||||||
<div class="plan-header">
|
|
||||||
<span class="plan-icon">📋</span>
|
|
||||||
<span class="plan-title">\${segment.planTitle || '执行计划'}</span>
|
|
||||||
</div>
|
|
||||||
<div class="plan-body">
|
|
||||||
<div class="plan-summary">\${segment.planSummary || ''}</div>
|
|
||||||
<div class="plan-steps">\${stepsHtml}</div>
|
|
||||||
</div>
|
|
||||||
<div class="plan-actions">
|
|
||||||
<button class="plan-btn plan-btn-confirm" data-action="confirm">确认执行</button>
|
|
||||||
<button class="plan-btn plan-btn-modify" data-action="modify">修改计划</button>
|
|
||||||
<button class="plan-btn plan-btn-cancel" data-action="cancel">取消</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
\`;
|
|
||||||
|
|
||||||
// 绑定按钮事件
|
|
||||||
setTimeout(() => {
|
|
||||||
const planCard = segmentDiv.querySelector('.plan-card');
|
|
||||||
if (planCard) {
|
|
||||||
planCard.querySelectorAll('.plan-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', (e) => {
|
|
||||||
const action = e.currentTarget?.dataset?.action;
|
|
||||||
vscode.postMessage({
|
|
||||||
command: 'planAction',
|
|
||||||
action: action,
|
|
||||||
planTitle: segment.planTitle
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
container.appendChild(segmentDiv);
|
container.appendChild(segmentDiv);
|
||||||
|
|||||||
273
src/views/planCard.ts
Normal file
273
src/views/planCard.ts
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
/**
|
||||||
|
* 计划卡片组件
|
||||||
|
*
|
||||||
|
* 功能说明:
|
||||||
|
* - 显示执行计划的卡片界面
|
||||||
|
* - 包含计划标题、摘要和步骤列表
|
||||||
|
* - 提供确认执行、修改计划、取消等操作按钮
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { plannerIconSvg } from "../constants/toolIcons";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取计划卡片的样式
|
||||||
|
*/
|
||||||
|
export function getPlanCardStyles(): string {
|
||||||
|
return `
|
||||||
|
/* 计划卡片样式 */
|
||||||
|
.segment-plan {
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
.plan-card {
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
}
|
||||||
|
.plan-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--vscode-sideBar-background);
|
||||||
|
border-bottom: 1px solid var(--vscode-input-border);
|
||||||
|
}
|
||||||
|
.plan-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.plan-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.plan-body {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.plan-summary {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.plan-steps {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.plan-step {
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
background: var(--vscode-list-hoverBackground);
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.plan-step:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.step-num {
|
||||||
|
color: var(--vscode-textLink-foreground);
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
.plan-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-top: 1px solid var(--vscode-input-border);
|
||||||
|
background: var(--vscode-sideBar-background);
|
||||||
|
}
|
||||||
|
.plan-actions .question-options {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.plan-btn {
|
||||||
|
padding: 8px 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.plan-btn-confirm {
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
}
|
||||||
|
.plan-btn-confirm:hover {
|
||||||
|
background: var(--vscode-button-hoverBackground);
|
||||||
|
}
|
||||||
|
.plan-btn-modify {
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
}
|
||||||
|
.plan-btn-cancel {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
.plan-actions .custom-input-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.plan-actions .custom-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.plan-actions .custom-submit {
|
||||||
|
padding: 8px 18px;
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.plan-actions .custom-submit:hover {
|
||||||
|
background: var(--vscode-button-hoverBackground);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取计划卡片的脚本
|
||||||
|
*/
|
||||||
|
export function getPlanCardScript(): string {
|
||||||
|
return `
|
||||||
|
// 渲染计划卡片(在 updateSegmentsRealtime 中使用)
|
||||||
|
function renderPlanCardInSegment(segment, segmentDiv, answeredQuestions) {
|
||||||
|
segmentDiv.className += ' segment-plan';
|
||||||
|
|
||||||
|
// 检查是否已回答
|
||||||
|
const isAnswered = answeredQuestions.has(segment.askId);
|
||||||
|
const selectedAnswer = answeredQuestions.get(segment.askId);
|
||||||
|
|
||||||
|
if (isAnswered) {
|
||||||
|
segmentDiv.classList.add('answered');
|
||||||
|
}
|
||||||
|
|
||||||
|
const stepsHtml = (segment.planSteps || []).map((step, i) =>
|
||||||
|
\`<div class="plan-step"><span class="step-num">\${i + 1}.</span> \${step}</div>\`
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
// 选项按钮
|
||||||
|
const options = ['确认执行', '修改计划', '取消'];
|
||||||
|
const optionsHtml = options.map(opt => {
|
||||||
|
const isSelected = isAnswered && opt === selectedAnswer;
|
||||||
|
return \`<button class="question-option\${isSelected ? ' selected' : ''}" data-option="\${opt}">\${opt}</button>\`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
segmentDiv.innerHTML = \`
|
||||||
|
<div class="plan-card">
|
||||||
|
<div class="plan-header">
|
||||||
|
<span class="plan-icon">${plannerIconSvg}</span>
|
||||||
|
<span class="plan-title">\${segment.planTitle || '执行计划'}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-body">
|
||||||
|
<div class="plan-summary">\${segment.planSummary || ''}</div>
|
||||||
|
<div class="plan-steps">\${stepsHtml}</div>
|
||||||
|
</div>
|
||||||
|
<div class="plan-actions">
|
||||||
|
<div class="question-options" data-ask-id="\${segment.askId}">\${optionsHtml}</div>
|
||||||
|
<div class="custom-input-container" style="display: \${isAnswered ? 'none' : 'flex'};">
|
||||||
|
<input type="text" class="custom-input" placeholder="输入修改建议..." />
|
||||||
|
<button class="custom-submit">提交</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
\`;
|
||||||
|
|
||||||
|
// 只在未回答时添加事件监听
|
||||||
|
if (!isAnswered) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const optionButtons = segmentDiv.querySelectorAll('.question-option');
|
||||||
|
optionButtons.forEach(btn => {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
const option = this.getAttribute('data-option');
|
||||||
|
// 发送答案到后端
|
||||||
|
handleQuestionAnswerInSegment(segment.askId, option, segmentDiv);
|
||||||
|
// 同时发送 planAction 用于模式切换
|
||||||
|
const actionMap = {
|
||||||
|
'确认执行': 'confirm',
|
||||||
|
'修改计划': 'modify',
|
||||||
|
'取消': 'cancel'
|
||||||
|
};
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'planAction',
|
||||||
|
action: actionMap[option] || option,
|
||||||
|
planTitle: segment.planTitle
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const submitBtn = segmentDiv.querySelector('.custom-submit');
|
||||||
|
const customInput = segmentDiv.querySelector('.custom-input');
|
||||||
|
if (submitBtn && customInput) {
|
||||||
|
submitBtn.addEventListener('click', function() {
|
||||||
|
const customValue = customInput.value.trim();
|
||||||
|
if (customValue) {
|
||||||
|
handleQuestionAnswerInSegment(segment.askId, customValue, segmentDiv);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
customInput.addEventListener('keypress', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const customValue = customInput.value.trim();
|
||||||
|
if (customValue) {
|
||||||
|
handleQuestionAnswerInSegment(segment.askId, customValue, segmentDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染计划卡片(在 renderSegments 中使用)
|
||||||
|
function renderPlanCardStatic(segment, segmentDiv) {
|
||||||
|
segmentDiv.className += ' segment-plan';
|
||||||
|
const stepsHtml = (segment.planSteps || []).map((step, i) =>
|
||||||
|
\`<div class="plan-step"><span class="step-num">\${i + 1}.</span> \${step}</div>\`
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
segmentDiv.innerHTML = \`
|
||||||
|
<div class="plan-card">
|
||||||
|
<div class="plan-header">
|
||||||
|
<span class="plan-icon">📋</span>
|
||||||
|
<span class="plan-title">\${segment.planTitle || '执行计划'}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-body">
|
||||||
|
<div class="plan-summary">\${segment.planSummary || ''}</div>
|
||||||
|
<div class="plan-steps">\${stepsHtml}</div>
|
||||||
|
</div>
|
||||||
|
<div class="plan-actions">
|
||||||
|
<button class="plan-btn plan-btn-confirm" data-action="confirm">确认执行</button>
|
||||||
|
<button class="plan-btn plan-btn-modify" data-action="modify">修改计划</button>
|
||||||
|
<button class="plan-btn plan-btn-cancel" data-action="cancel">取消</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
\`;
|
||||||
|
|
||||||
|
// 绑定按钮事件
|
||||||
|
setTimeout(() => {
|
||||||
|
const planCard = segmentDiv.querySelector('.plan-card');
|
||||||
|
if (planCard) {
|
||||||
|
planCard.querySelectorAll('.plan-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
const action = e.currentTarget?.dataset?.action;
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'planAction',
|
||||||
|
action: action,
|
||||||
|
planTitle: segment.planTitle
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user