diff --git a/ui/optimization-progress.js b/ui/optimization-progress.js
new file mode 100644
index 0000000..dd262c4
--- /dev/null
+++ b/ui/optimization-progress.js
@@ -0,0 +1,373 @@
+import { extensionName } from '../utils/settings.js';
+
+let modalInstance = null;
+// 状态追踪对象,用于分别管理主模型(A)和并发模型(B)
+let trackState = {
+ main: { step: 0, text: '准备就绪...', active: false, fillEl: null, textEl: null },
+ concurrent: { step: 0, text: '等待启动...', active: false, fillEl: null, textEl: null }
+};
+const totalEstimatedSteps = 7; // 减少预估步骤数,让进度条跑得更快
+
+// 消息队列系统 - 双轨并行
+let messageQueues = {
+ main: [],
+ concurrent: []
+};
+let isProcessingQueues = {
+ main: false,
+ concurrent: false
+};
+const MIN_DISPLAY_TIME = 800;
+
+function createModalHtml() {
+ return `
+
+ `;
+}
+
+function addStyling() {
+ const styleId = 'amily2-progress-modal-style';
+ if (document.getElementById(styleId)) return;
+
+ const style = document.createElement('style');
+ style.id = styleId;
+ style.innerHTML = `
+ #amily2-progress-bar-container {
+ position: fixed;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 420px;
+ max-width: 90vw;
+ background: rgba(30, 30, 40, 0.85);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ padding: 12px 16px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
+ z-index: 2147483647 !important;
+ font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ opacity: 0;
+ transform: translate(-50%, -20px); /* 初始位置偏上,用于入场动画 */
+ }
+
+ #amily2-progress-bar-container.visible {
+ opacity: 1;
+ transform: translate(-50%, 0);
+ }
+
+ .progress-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+ }
+
+ .progress-status {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--smart-theme-body-color, #eee);
+ font-size: 13px;
+ font-weight: 500;
+ white-space: nowrap;
+ overflow: hidden;
+ flex: 1;
+ }
+
+ .track-label {
+ font-size: 11px;
+ padding: 2px 6px;
+ border-radius: 4px;
+ background: rgba(255,255,255,0.1);
+ color: rgba(255,255,255,0.8);
+ margin-right: 4px;
+ }
+
+ .track-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ opacity: 0.9;
+ }
+
+ #amily2-progress-cancel {
+ background: transparent;
+ border: none;
+ color: rgba(255, 255, 255, 0.4);
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 50%;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ margin-left: 10px;
+ }
+
+ #amily2-progress-cancel:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+ color: #ff6b6b;
+ }
+
+ .progress-track {
+ height: 4px;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 2px;
+ overflow: hidden;
+ position: relative;
+ }
+
+ .progress-fill {
+ height: 100%;
+ background: linear-gradient(90deg, var(--smart-theme-color, #9e8aff), #b3a4ff);
+ border-radius: 2px;
+ transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 0 10px rgba(158, 138, 255, 0.4);
+ position: relative;
+ }
+
+ /* 进度条光效动画 */
+ .progress-fill::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: linear-gradient(
+ 90deg,
+ transparent,
+ rgba(255, 255, 255, 0.4),
+ transparent
+ );
+ transform: translateX(-100%);
+ animation: shimmer 1.5s infinite;
+ }
+
+ @keyframes shimmer {
+ 100% {
+ transform: translateX(100%);
+ }
+ }
+ `;
+ document.head.appendChild(style);
+}
+
+export function showPlotOptimizationProgress(cancellationState) {
+ if (modalInstance) {
+ hidePlotOptimizationProgress();
+ }
+
+ addStyling();
+ document.body.insertAdjacentHTML('beforeend', createModalHtml());
+
+ modalInstance = document.getElementById('amily2-progress-bar-container');
+
+ // 初始化轨道元素引用
+ trackState.main.fillEl = document.getElementById('amily2-fill-main');
+ trackState.main.textEl = document.getElementById('amily2-text-main');
+ trackState.main.step = 0;
+ trackState.main.active = true;
+
+ trackState.concurrent.fillEl = document.getElementById('amily2-fill-concurrent');
+ trackState.concurrent.textEl = document.getElementById('amily2-text-concurrent');
+ trackState.concurrent.step = 0;
+ trackState.concurrent.active = false; // 默认不激活,直到收到相关消息
+
+ const cancelButton = document.getElementById('amily2-progress-cancel');
+
+ // 重置消息队列
+ messageQueues = {
+ main: [],
+ concurrent: []
+ };
+ isProcessingQueues = {
+ main: false,
+ concurrent: false
+ };
+
+ // 触发入场动画
+ requestAnimationFrame(() => {
+ if (modalInstance) {
+ modalInstance.classList.add('visible');
+ }
+ });
+
+ if (cancellationState && cancelButton) {
+ cancelButton.addEventListener('click', () => {
+ cancellationState.isCancelled = true;
+ if (trackState.main.textEl) trackState.main.textEl.textContent = "正在中止任务...";
+ if (trackState.main.fillEl) trackState.main.fillEl.style.backgroundColor = "#ff6b6b";
+ toastr.info("记忆管理任务已请求中止。");
+ // 延迟关闭以显示中止状态
+ setTimeout(hidePlotOptimizationProgress, 800);
+ });
+ }
+}
+
+export function updatePlotOptimizationProgress(message, isDone = false, isSkipped = false) {
+ // 如果是最后一步,强制清空所有队列并立即执行
+ if (message.includes('记忆重构完成') || message.includes('所有任务已完成')) {
+ messageQueues.main = [];
+ messageQueues.concurrent = [];
+ performUpdate(message, isDone, isSkipped);
+ setTimeout(hidePlotOptimizationProgress, 1000);
+ return;
+ }
+
+ // 判断消息归属
+ const isConcurrent = message.includes('(LLM-B)') || message.includes('(并发模型)');
+ const queueType = isConcurrent ? 'concurrent' : 'main';
+
+ // 加入对应队列
+ messageQueues[queueType].push({ message, isDone, isSkipped });
+
+ // 触发对应队列的处理
+ processQueue(queueType);
+}
+
+async function processQueue(queueType) {
+ if (isProcessingQueues[queueType] || messageQueues[queueType].length === 0) return;
+
+ isProcessingQueues[queueType] = true;
+
+ while (messageQueues[queueType].length > 0) {
+ const { message, isDone, isSkipped } = messageQueues[queueType].shift();
+
+ // 执行实际的 UI 更新
+ performUpdate(message, isDone, isSkipped);
+
+ // 如果是“开始”某个步骤(非完成/跳过),或者是重要的完成状态,我们给予展示时间
+ // 对于“请求 LLM”这种耗时操作,本身就会卡很久,所以不需要额外延迟,
+ // 但对于那些瞬间完成的步骤(如“构建提示词”),我们需要人为暂停一下。
+
+ // 简单的策略:所有状态更新都至少展示 MIN_DISPLAY_TIME
+ // 除非是 LLM 请求开始,因为那个会自然等待
+ const isLongRunningTaskStart = message.includes('请求') && !isDone && !isSkipped;
+
+ if (!isLongRunningTaskStart) {
+ await new Promise(resolve => setTimeout(resolve, MIN_DISPLAY_TIME));
+ } else {
+ // 对于 LLM 请求开始,我们只给一个很短的缓冲,让用户看清文字即可,
+ // 剩下的时间由 LLM 的实际响应时间填充
+ await new Promise(resolve => setTimeout(resolve, 500));
+ }
+ }
+
+ isProcessingQueues[queueType] = false;
+}
+
+function performUpdate(message, isDone, isSkipped) {
+ if (!modalInstance) return;
+
+ // 过滤掉一些不需要显示的辅助信息
+ if (message === '初始化任务...' || message === '所有任务已完成') {
+ if (trackState.main.textEl) trackState.main.textEl.textContent = message;
+ return;
+ }
+
+ // 判断是主模型还是并发模型的消息
+ const isConcurrent = message.includes('(LLM-B)') || message.includes('(并发模型)');
+ const track = isConcurrent ? trackState.concurrent : trackState.main;
+ const trackId = isConcurrent ? 'amily2-track-concurrent' : 'amily2-track-main';
+
+ // 如果是并发模型消息,确保轨道可见
+ if (isConcurrent && !track.active) {
+ track.active = true;
+ const trackEl = document.getElementById(trackId);
+ if (trackEl) trackEl.style.display = 'block';
+ }
+
+ // 清理消息中的标识符,让显示更干净
+ const cleanMessage = message.replace(/\(LLM-[AB]\)|\(主模型\)|\(并发模型\)/g, '').trim();
+
+ if (isDone || isSkipped) {
+ track.step++;
+ // 计算进度:最大只能到 95%,最后一步直接跳满
+ let percentage = Math.min((track.step / totalEstimatedSteps) * 100, 95);
+
+ // 如果是最终完成消息,直接满格
+ if (message.includes('记忆重构完成') || message.includes('所有任务已完成')) {
+ percentage = 100;
+ // 同时让并发轨道也满格(如果激活的话)
+ if (trackState.concurrent.active && trackState.concurrent.fillEl) {
+ trackState.concurrent.fillEl.style.width = '100%';
+ if (trackState.concurrent.textEl) trackState.concurrent.textEl.textContent = '同步完成 ✅';
+ }
+ }
+
+ if (track.fillEl) {
+ track.fillEl.style.width = `${percentage}%`;
+ }
+
+ if (track.textEl) {
+ track.textEl.textContent = `${cleanMessage} ${isSkipped ? '⚪' : '✅'}`;
+ }
+ } else {
+ if (track.textEl) {
+ track.textEl.textContent = cleanMessage;
+ }
+ }
+}
+
+export function hidePlotOptimizationProgress() {
+ // 重置消息队列
+ messageQueues = {
+ main: [],
+ concurrent: []
+ };
+ isProcessingQueues = {
+ main: false,
+ concurrent: false
+ };
+
+ if (modalInstance) {
+ modalInstance.classList.remove('visible');
+ setTimeout(() => {
+ if (modalInstance) {
+ modalInstance.remove();
+ modalInstance = null;
+ // 清理引用
+ trackState.main.fillEl = null;
+ trackState.main.textEl = null;
+ trackState.concurrent.fillEl = null;
+ trackState.concurrent.textEl = null;
+ }
+ }, 300);
+ }
+}