log-service 重构: - DOM 渲染改用 requestAnimationFrame 批量队列 - 全程 try-catch,任何异常都不阻塞主流程 - 新增 logConnection/logModel/logSession/logMemory/logRAG/logSetting/logIPC/logInit 便捷方法 全量接入(12 个文件): - header.ts: 连接状态变化(connected/disconnected/error) - model-bar.ts: 模型列表加载、模型切换、能力检测结果 - settings-modal.ts: 所有设置变更(系统提示词/上下文/温度/工具调用/命令执行/记忆)、显存释放、导出/导入 - tool-confirm-modal.ts: 工具确认弹窗、确认/取消操作 - memory-manager.ts: 记忆初始化、新增/删除、自动提取 - tool-registry.ts: 工具执行异常 - rag.ts: 文档分块、向量检索 - kb-modal.ts: 知识库创建、文档上传、批量上传 - history-modal.ts: 会话加载、会话删除 - main.ts: 应用初始化各阶段、桌面集成、菜单/托盘操作、新建会话 - input-area.ts: 消息发送、流式完成/错误、文件/图片添加、Agent Loop 错误
93 lines
3.2 KiB
TypeScript
93 lines
3.2 KiB
TypeScript
/**
|
|
* Header - 顶部导航组件
|
|
*/
|
|
|
|
import { state, KEYS } from '../state/state.js';
|
|
import { formatSize } from '../utils/utils.js';
|
|
import { OllamaAPI } from '../api/ollama.js';
|
|
import { logConnection, logError, logDebug } from '../services/log-service.js';
|
|
|
|
let connStatusEl: HTMLElement;
|
|
|
|
export function initHeader(): void {
|
|
connStatusEl = document.querySelector('#connStatus')!;
|
|
}
|
|
|
|
export async function checkConnection(): Promise<{ ok: boolean; version?: { version: string }; error?: Error }> {
|
|
const api = state.get<OllamaAPI>(KEYS.API);
|
|
if (!api) return { ok: false, error: new Error('API 未初始化') };
|
|
|
|
connStatusEl.className = 'conn-status pending';
|
|
connStatusEl.querySelector('.status-label')!.textContent = '连接中...';
|
|
|
|
try {
|
|
const version = await api.getVersion();
|
|
connStatusEl.className = 'conn-status connected';
|
|
connStatusEl.title = `已连接 (v${version.version})`;
|
|
connStatusEl.querySelector('.status-label')!.textContent = '已连接';
|
|
logConnection('connected', `Ollama v${version.version}`);
|
|
return { ok: true, version };
|
|
} catch (err) {
|
|
connStatusEl.className = 'conn-status disconnected';
|
|
connStatusEl.title = '未连接';
|
|
connStatusEl.querySelector('.status-label')!.textContent = '未连接';
|
|
logConnection('disconnected', (err as Error).message);
|
|
return { ok: false, error: err as Error };
|
|
}
|
|
}
|
|
|
|
export async function updateConnectionInfo(): Promise<void> {
|
|
const api = state.get<OllamaAPI>(KEYS.API);
|
|
if (!api) return;
|
|
|
|
const badge = document.querySelector('#connBadge')!;
|
|
const verEl = document.querySelector('#serverVersion')!;
|
|
const corsHint = document.querySelector('#corsHint') as HTMLElement;
|
|
|
|
badge.textContent = '检测中...';
|
|
badge.className = 'status-badge warning';
|
|
verEl.textContent = '';
|
|
|
|
try {
|
|
const version = await api.getVersion();
|
|
badge.textContent = '✓ 已连接';
|
|
badge.className = 'status-badge success';
|
|
verEl.textContent = `v${version.version}`;
|
|
corsHint.style.display = 'none';
|
|
logConnection('info', `Ollama v${version.version}`);
|
|
} catch (err) {
|
|
badge.textContent = '✗ 未连接';
|
|
badge.className = 'status-badge error';
|
|
verEl.textContent = '';
|
|
logError('连接检测失败', (err as Error).message);
|
|
|
|
if ((err as Error).message.includes('Failed to fetch') || (err as Error).message.includes('NetworkError') || (err as Error).message.includes('CORS')) {
|
|
corsHint.style.display = '';
|
|
} else {
|
|
corsHint.style.display = 'none';
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function updateRunningModels(): Promise<void> {
|
|
const api = state.get<OllamaAPI>(KEYS.API);
|
|
if (!api) return;
|
|
|
|
const container = document.querySelector('#runningModels')!;
|
|
try {
|
|
const data = await api.psModels();
|
|
if (!data.models || data.models.length === 0) {
|
|
container.innerHTML = '<p class="text-muted">无运行中的模型</p>';
|
|
return;
|
|
}
|
|
container.innerHTML = data.models.map(m => `
|
|
<div class="running-model">
|
|
<span class="model-name">${m.name}</span>
|
|
<span class="model-vram">${formatSize(m.size_vram || 0)} VRAM</span>
|
|
</div>
|
|
`).join('');
|
|
} catch {
|
|
container.innerHTML = '<p class="text-muted">无法获取运行信息</p>';
|
|
}
|
|
}
|