/** * MemoryModal - Agent 记忆管理(简化版) * 基于工作空间 MEMORY.md 文件 */ import { loadAllEntries, addEntry, removeEntry, DuplicateEntryError, type MemoryEntry, type MemoryType } from '../services/memory-service.js'; import { showToast } from './toast.js'; import { showPrompt, showConfirm } from './prompt-modal.js'; import { escapeHtml, formatTime } from '../utils/utils.js'; let modalEl: HTMLElement; // ── 初始化 ── export function initMemoryModal(): void { const overlay = document.createElement('div'); overlay.className = 'modal-overlay'; overlay.id = 'memoryModal'; overlay.style.display = 'none'; overlay.innerHTML = ` `; document.querySelector('#app')!.appendChild(overlay); modalEl = overlay; overlay.addEventListener('click', (e) => { if (e.target === overlay) closeMemoryModal(); }); overlay.querySelector('#btnCloseMemoryModal')!.addEventListener('click', closeMemoryModal); overlay.querySelector('#btnAddMemoryLg')!.addEventListener('click', openAddDialog); // 列表点击:删除按钮 overlay.querySelector('#memoryListSimple')!.addEventListener('click', async (e) => { const target = e.target as HTMLElement; if (target.classList.contains('memory-item-delete')) { const id = target.dataset.id!; const entries = await loadAllEntries(); const entry = entries.find(e => e.id === id); if (entry && await showConfirm(`确定删除这条记忆?\n\n${entry.content.slice(0, 100)}`, '删除记忆')) { await removeEntry(entry.content.slice(0, 50)); renderList(); showToast('记忆已删除', 'info', 1500); } } }); // header 按钮 document.querySelector('#btnMemory')?.addEventListener('click', openMemoryModal); } // ── 打开/关闭 ── export async function openMemoryModal(): Promise { modalEl.style.display = ''; await renderList(); } export function closeMemoryModal(): void { modalEl.style.display = 'none'; } // ── 列表渲染 ── async function renderList(): Promise { const listEl = modalEl.querySelector('#memoryListSimple') as HTMLElement; const footerEl = modalEl.querySelector('#memoryFooterSimple') as HTMLElement; let entries: MemoryEntry[]; try { entries = await loadAllEntries(); } catch (err) { listEl.innerHTML = `
加载失败: ${escapeHtml((err as Error).message)}
`; return; } if (entries.length === 0) { listEl.innerHTML = `
🧠
暂无记忆
AI 在对话中可通过 memory 工具自动管理记忆
工作空间 MEMORY.md 文件存储
`; footerEl.textContent = '在对话中让 AI "记住xxx"即可自动添加记忆'; return; } // 按类型分组渲染 const typeOrder: MemoryType[] = ['rule', 'preference', 'fact']; const typeIcons: Record = { rule: '📏', preference: '⚙️', fact: '📌' }; const typeNames: Record = { rule: '规则', preference: '偏好', fact: '事实' }; let html = ''; for (const type of typeOrder) { const typeEntries = entries.filter(e => e.type === type); if (typeEntries.length === 0) continue; html += `
${typeIcons[type]} ${typeNames[type]} (${typeEntries.length})
`; for (const entry of typeEntries) { const stars = '★'.repeat(Math.min(entry.importance, 10)); html += `
${escapeHtml(entry.content)}
${stars} ${entry.id} ${entry.tags.length > 0 ? `🏷 ${entry.tags.join(', ')}` : ''}
`; } } listEl.innerHTML = html; footerEl.textContent = `共 ${entries.length} 条记忆 · 存储于工作空间 MEMORY.md`; } // ── 添加对话框 ── async function openAddDialog(): Promise { const type = await showPrompt({ title: '添加记忆', message: '选择记忆类型:', type: 'select', defaultValue: 'fact', options: [ { value: 'fact', label: '📌 事实 — 项目信息、身份背景' }, { value: 'preference', label: '⚙️ 偏好 — 语言风格、技术栈偏好' }, { value: 'rule', label: '📏 规则 — 编码规范、输出要求' } ] }); if (type === null) return; const memoryType = (type as MemoryType) || 'fact'; const content = await showPrompt({ title: '添加记忆', message: '请输入记忆内容:', type: 'textarea', placeholder: '例如:用户正在开发一个 Electron 桌面应用' }); if (!content?.trim()) return; const importanceStr = await showPrompt({ title: '添加记忆', message: '重要性(1-10,默认 5):', defaultValue: '5', placeholder: '5' }); const importance = Math.min(10, Math.max(1, parseInt(importanceStr || '5') || 5)); try { await addEntry(memoryType, content.trim(), importance); await renderList(); showToast('记忆已添加', 'success', 1500); } catch (err) { if (err instanceof DuplicateEntryError) { showToast('该记忆已存在,无需重复添加', 'info', 2000); } else { showToast(`添加失败: ${(err as Error).message}`, 'error'); } } }