Files
IC-Coder-Plugin/src/views/conversationHistoryBar.ts
Roe-xin 9bdaf34471 feat:实现任务历史加载功能 - 完整还原对话样式
主要改进:
1. 实现selectConversation功能,支持点击任务历史列表加载会话
2. 优化会话存储格式,保存完整的segments信息(包括工具调用)
3. 添加旧格式到新格式的自动转换,兼容历史数据
4. 改进错误处理,自动清理无效的空任务目录
5. 优化路径编码逻辑,确保跨平台一致性
6. 前端支持clearChat、addUserMessage、addAiMessage命令

技术细节:
- 扩展AiMessage数据结构,添加segments字段
- 修改messageHandler保存逻辑,将完整segments保存到一条消息
- 实现loadTaskSession方法,加载指定任务的完整会话
- 添加自动清理机制,删除无效的空任务目录
2025-12-28 10:38:54 +08:00

402 lines
11 KiB
TypeScript

/**
* 获取会话历史栏的 HTML 内容
*/
export function getConversationHistoryBarContent(): string {
return `
<div class="conversation-history-bar">
<div class="history-dropdown-container">
<button class="history-dropdown-button" onclick="toggleHistoryDropdown()">
<span class="dropdown-label">Past Conversations</span>
<svg class="dropdown-icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3 0.1-12.7-6.4-12.7z" fill="currentColor"/>
</svg>
</button>
<div class="history-dropdown-menu" id="historyDropdownMenu">
<div class="history-list" id="historyList">
<!-- 会话历史列表将在这里动态生成 -->
</div>
</div>
</div>
<button class="new-conversation-button" onclick="createNewConversation()" title="新建对话">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" fill="currentColor"/>
</svg>
</button>
</div>
`;
}
/**
* 获取会话历史栏的 CSS 样式
*/
export function getConversationHistoryBarStyles(): string {
return `
.conversation-history-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
background: var(--vscode-tab-activeBackground);
border-bottom: 1px solid var(--vscode-panel-border);
flex-shrink: 0;
min-height: 35px;
}
.history-dropdown-container {
position: relative;
flex: 1;
}
.history-dropdown-button {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
background: transparent;
color: var(--vscode-input-foreground);
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s ease;
}
.history-dropdown-button:hover {
opacity: 0.8;
}
.dropdown-label {
white-space: nowrap;
}
.dropdown-icon {
width: 12px;
height: 12px;
transition: transform 0.2s ease;
flex-shrink: 0;
}
.history-dropdown-button.active .dropdown-icon {
transform: rotate(180deg);
}
.history-dropdown-menu {
position: absolute;
top: calc(100% + 4px);
left: 0;
min-width: 300px;
max-height: 400px;
background: var(--vscode-dropdown-background);
border: 1px solid var(--vscode-dropdown-border);
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
max-height: 400px;
overflow-y: auto;
z-index: 1000;
display: none;
}
.history-dropdown-menu.active {
display: block;
}
.history-list {
padding: 4px 0;
}
.history-item {
padding: 10px 16px;
cursor: pointer;
transition: background 0.2s ease;
border-bottom: 1px solid var(--vscode-panel-border);
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.history-item:last-child {
border-bottom: none;
}
.history-item:hover {
background: var(--vscode-list-hoverBackground);
}
.history-item-title {
font-size: 14px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.history-item-time {
font-size: 12px;
opacity: 0.7;
white-space: nowrap;
flex-shrink: 0;
}
.history-empty {
padding: 20px;
text-align: center;
color: var(--vscode-descriptionForeground);
font-size: 14px;
}
.history-load-more {
padding: 12px 16px;
text-align: center;
color: var(--vscode-descriptionForeground);
font-size: 12px;
border-top: 1px solid var(--vscode-panel-border);
}
.new-conversation-button {
width: 36px;
height: 36px;
padding: 0;
background: transparent;
color: var(--vscode-foreground);
border: none;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
flex-shrink: 0;
}
.new-conversation-button:hover {
opacity: 0.7;
}
.new-conversation-button:active {
opacity: 0.5;
}
.new-conversation-button svg {
width: 20px;
height: 20px;
}
/* 滚动条样式 */
.history-dropdown-menu::-webkit-scrollbar {
width: 8px;
}
.history-dropdown-menu::-webkit-scrollbar-track {
background: transparent;
}
.history-dropdown-menu::-webkit-scrollbar-thumb {
background: rgba(128, 128, 128, 0.5);
border-radius: 4px;
}
.history-dropdown-menu::-webkit-scrollbar-thumb:hover {
background: rgba(128, 128, 128, 0.7);
}
`;
}
/**
* 获取会话历史栏的 JavaScript 脚本
*/
export function getConversationHistoryBarScript(): string {
return `
// 会话历史相关变量
let conversationHistory = [];
let currentConversationId = null;
let currentOffset = 0;
let totalHistory = 0;
let hasMoreHistory = false;
let isLoadingHistory = false;
const HISTORY_PAGE_SIZE = 10;
const MAX_HISTORY_ITEMS = 100;
// 切换历史记录下拉菜单
function toggleHistoryDropdown() {
const menu = document.getElementById('historyDropdownMenu');
const button = document.querySelector('.history-dropdown-button');
if (menu.classList.contains('active')) {
menu.classList.remove('active');
button.classList.remove('active');
} else {
menu.classList.add('active');
button.classList.add('active');
// 重置并加载会话历史
resetAndLoadHistory();
}
}
// 重置并加载会话历史
function resetAndLoadHistory() {
conversationHistory = [];
currentOffset = 0;
totalHistory = 0;
hasMoreHistory = false;
const historyList = document.getElementById('historyList');
if (historyList) {
historyList.innerHTML = '<div class="history-empty">加载中...</div>';
}
loadMoreHistory();
}
// 加载更多会话历史
function loadMoreHistory() {
if (isLoadingHistory || (currentOffset > 0 && !hasMoreHistory)) {
return;
}
// 检查是否已达到最大数量
if (currentOffset >= MAX_HISTORY_ITEMS) {
return;
}
isLoadingHistory = true;
vscode.postMessage({
command: 'loadConversationHistory',
offset: currentOffset,
limit: HISTORY_PAGE_SIZE
});
}
// 渲染会话历史列表(支持追加)
function renderConversationHistory(data) {
isLoadingHistory = false;
if (!data || !data.items) {
return;
}
// 追加新数据
conversationHistory = conversationHistory.concat(data.items);
totalHistory = data.total;
hasMoreHistory = data.hasMore;
currentOffset += data.items.length;
const historyList = document.getElementById('historyList');
if (!historyList) {
return;
}
// 如果没有任何历史记录
if (conversationHistory.length === 0) {
historyList.innerHTML = '<div class="history-empty">暂无会话历史</div>';
return;
}
// 渲染所有历史记录
historyList.innerHTML = conversationHistory.map(item => \`
<div class="history-item" onclick="selectConversation('\${item.id}')">
<div class="history-item-title">\${item.title || '未命名会话'}</div>
<div class="history-item-time">\${formatTime(item.timestamp)}</div>
</div>
\`).join('');
// 如果还有更多数据,添加"加载更多"提示
if (hasMoreHistory && currentOffset < MAX_HISTORY_ITEMS) {
historyList.innerHTML += \`
<div class="history-load-more" id="loadMoreIndicator">
<span>滚动加载更多...</span>
</div>
\`;
} else if (currentOffset >= MAX_HISTORY_ITEMS && hasMoreHistory) {
historyList.innerHTML += \`
<div class="history-load-more">
<span>已显示最近 \${MAX_HISTORY_ITEMS} 条记录</span>
</div>
\`;
}
}
// 选择会话
function selectConversation(conversationId) {
currentConversationId = conversationId;
vscode.postMessage({
command: 'selectConversation',
conversationId: conversationId
});
// 关闭下拉菜单
const menu = document.getElementById('historyDropdownMenu');
const button = document.querySelector('.history-dropdown-button');
menu.classList.remove('active');
button.classList.remove('active');
}
// 创建新会话
function createNewConversation() {
vscode.postMessage({ command: 'createNewConversation' });
}
// 格式化时间
function formatTime(timestamp) {
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
// 小于1分钟
if (diff < 60000) {
return '刚刚';
}
// 小于1小时
if (diff < 3600000) {
return Math.floor(diff / 60000) + '分钟前';
}
// 小于1天
if (diff < 86400000) {
return Math.floor(diff / 3600000) + '小时前';
}
// 小于7天
if (diff < 604800000) {
return Math.floor(diff / 86400000) + '天前';
}
// 超过7天显示具体日期
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
}
// 监听下拉菜单滚动事件
const historyDropdownMenu = document.getElementById('historyDropdownMenu');
if (historyDropdownMenu) {
historyDropdownMenu.addEventListener('scroll', () => {
const menu = historyDropdownMenu;
const scrollTop = menu.scrollTop;
const scrollHeight = menu.scrollHeight;
const clientHeight = menu.clientHeight;
// 当滚动到距离底部 50px 时,加载更多
if (scrollHeight - scrollTop - clientHeight < 50) {
loadMoreHistory();
}
});
}
// 点击外部关闭下拉菜单
document.addEventListener('click', (event) => {
const container = document.querySelector('.history-dropdown-container');
const menu = document.getElementById('historyDropdownMenu');
const button = document.querySelector('.history-dropdown-button');
if (menu && menu.classList.contains('active')) {
if (!container.contains(event.target)) {
menu.classList.remove('active');
button.classList.remove('active');
}
}
});
`;
}