- 新增视频上传按钮,支持 mp4/avi/mov/mkv/webm (≤10MB) - ffmpeg 1fps 提取帧序列,自动缩放至512px,带时序标注 - 消息中注入帧序列时间线,多模态模型原生理解视频时序 - 视频按钮与 vision 模型能力自动联动 - 版本号 0.11.6 → 0.11.7,同步更新 README/帮助面板/菜单
837 lines
64 KiB
HTML
837 lines
64 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<meta name="theme-color" content="#FAF7F2">
|
||
<meta name="description" content="Metona Ollama Desktop - TypeScript + Electron AI 聊天客户端">
|
||
<link rel="icon" href="/assets/icons/llama.ico" type="image/x-icon">
|
||
<title>Metona Ollama</title>
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
|
||
<!-- ═══════════════ 日志面板(左侧)═══════════════ -->
|
||
<aside class="log-panel" id="logPanel">
|
||
<div class="log-panel-header">
|
||
<span class="log-panel-title">📋 执行日志</span>
|
||
<button class="btn btn-sm" id="btnExportLog" title="导出日志" style="padding:2px 8px;font-size:11px;opacity:0.7;">📥 导出</button>
|
||
</div>
|
||
<div class="log-panel-body" id="logPanelBody"></div>
|
||
</aside>
|
||
|
||
<!-- ═══════════════ 主内容区 ═══════════════ -->
|
||
<div class="main-wrap with-workspace" id="mainWrap">
|
||
|
||
<!-- ═══════════════ 顶部导航 ═══════════════ -->
|
||
<header class="app-header">
|
||
<div class="header-left">
|
||
<span class="logo">🦙</span>
|
||
<span class="app-title">Metona Ollama</span>
|
||
<span class="app-version">v0.11.7</span>
|
||
<button class="icon-btn help-btn" id="btnHelp" title="使用帮助">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="header-right">
|
||
<div class="conn-status pending" id="connStatus" title="连接状态">
|
||
<span class="status-dot"></span>
|
||
<span class="status-label">未连接</span>
|
||
</div>
|
||
<button class="icon-btn" id="btnNewChat" title="新建会话">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnMemory" title="Agent 记忆">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
|
||
<line x1="9" y1="21" x2="15" y2="21"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnTools" title="工具面板">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnTokenDashboard" title="Token 监控">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/><line x1="7" y1="13" x2="7" y2="17"/><line x1="11" y1="9" x2="11" y2="17"/><line x1="15" y1="5" x2="15" y2="17"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnHistory" title="历史记录">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn" id="btnSettings" title="设置">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn searxng-btn" id="btnSearxng" title="SearXNG 搜索配置">
|
||
<span class="searxng-icon-dot" id="searxngIconDot"></span>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ═══════════════ 模型选择栏 ═══════════════ -->
|
||
<div class="model-bar">
|
||
<div class="model-bar-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||
<path d="M9 9h6M9 12h6M9 15h4"/>
|
||
</svg>
|
||
</div>
|
||
<div class="model-select-wrap">
|
||
<select class="model-select" id="modelSelect">
|
||
<option value="">正在加载模型...</option>
|
||
</select>
|
||
<span class="model-select-arrow">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
||
</span>
|
||
</div>
|
||
<div class="model-badges" id="modelBadges">
|
||
<span class="model-badge think-badge" id="badgeThink" style="display:none;">🧠 Think</span>
|
||
<span class="model-badge vision-badge" id="badgeVision" style="display:none;">👁️ Vision</span>
|
||
<span class="model-badge tools-badge" id="badgeTools" style="display:none;">🔧 Tools</span>
|
||
</div>
|
||
<span class="ctx-total" id="ctxTotal" title="模型上下文窗口大小"></span>
|
||
<span class="ctx-remain" id="ctxRemain" style="display:none;" title="剩余可用上下文"></span>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 聊天区域 ═══════════════ -->
|
||
<main class="chat-area" id="chatArea">
|
||
<div class="empty-state" id="emptyState">
|
||
<div class="empty-icon">
|
||
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="100" cy="100" r="80" stroke="rgba(0,245,212,0.2)" stroke-width="2"/>
|
||
<circle cx="100" cy="100" r="60" stroke="rgba(123,47,247,0.2)" stroke-width="2"/>
|
||
<path d="M70 90c0-16.57 13.43-30 30-30s30 13.43 30 30" stroke="rgba(0,245,212,0.5)" stroke-width="2" stroke-linecap="round"/>
|
||
<circle cx="85" cy="100" r="5" fill="rgba(0,245,212,0.6)"/>
|
||
<circle cx="115" cy="100" r="5" fill="rgba(0,245,212,0.6)"/>
|
||
<path d="M88 120c4 4 20 4 24 0" stroke="rgba(0,245,212,0.4)" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</div>
|
||
<h2>开始对话</h2>
|
||
<p>选择一个模型,输入消息开始聊天</p>
|
||
</div>
|
||
<div class="messages" id="messagesContainer" style="display:none;"></div>
|
||
<button class="scroll-to-bottom" id="scrollToBottom" style="display:none;" title="回到最新">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="6 9 12 15 18 9"/>
|
||
</svg>
|
||
<span>回到底部</span>
|
||
</button>
|
||
</main>
|
||
|
||
<!-- ═══════════════ 历史只读提示栏 ═══════════════ -->
|
||
<div class="history-view-bar" id="historyBar" style="display:none;">
|
||
<span>📖 正在查看历史记录(只读模式)</span>
|
||
<button class="btn btn-sm btn-outline" id="btnExitHistory">返回新会话</button>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 输入区域 ═══════════════ -->
|
||
<div class="input-area" id="inputArea">
|
||
<div class="image-preview" id="imagePreview" style="display:none;"></div>
|
||
<div class="file-preview" id="filePreview" style="display:none;"></div>
|
||
<div class="input-row">
|
||
<button class="icon-btn attach-btn" id="btnAttachImg" title="上传图片">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<polyline points="21 15 16 10 5 21"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn attach-btn" id="btnAttachVideo" title="上传视频(提取帧分析)">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polygon points="23 7 16 12 23 17 23 7"/>
|
||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
||
</svg>
|
||
</button>
|
||
<button class="icon-btn attach-btn" id="btnAttachFile" title="上传文件(文本/代码)">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/>
|
||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||
<polyline points="10 9 9 9 8 9"/>
|
||
</svg>
|
||
</button>
|
||
<textarea class="chat-input" id="chatInput" placeholder="输入消息..." rows="1"></textarea>
|
||
<label class="think-toggle" id="thinkToggleWrap" title="Think 模式未可用">
|
||
<input type="checkbox" id="toggleThink" disabled>
|
||
<span class="think-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2a7 7 0 0 1 7 7c0 2.38-1.19 4.47-3 5.74V17a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2.26C6.19 13.47 5 11.38 5 9a7 7 0 0 1 7-7z"/>
|
||
<line x1="9" y1="21" x2="15" y2="21"/>
|
||
</svg>
|
||
</span>
|
||
</label>
|
||
<button class="send-btn" id="btnSend">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div><!-- /.main-wrap -->
|
||
|
||
<!-- ═══════════════ 工作空间面板(右侧,常驻显示,flex 子元素)═══════════════ -->
|
||
<aside class="workspace-panel" id="workspacePanel">
|
||
<div class="ws-header">
|
||
<span class="ws-title">🖥️ 工作空间</span>
|
||
<div class="ws-tabs">
|
||
<button class="ws-header-tab" id="wsTabTerminal">💻 命令行</button>
|
||
<button class="ws-header-tab active" id="wsTabTools">🔧 工具</button>
|
||
<button class="ws-header-tab" id="wsTabFiles">📁 文件</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 终端 Tab -->
|
||
<div class="ws-content" id="wsTerminalContent" style="display:none;">
|
||
<div class="ws-term-output" id="wsTermOutput"></div>
|
||
<div class="ws-term-toolbar">
|
||
<span class="ws-term-hint" id="wsTermHint">等待 AI 执行命令...</span>
|
||
<div class="ws-term-toolbar-btns">
|
||
<button class="ws-toolbar-btn ws-stop-btn" id="wsStopBtn" title="终止当前命令" disabled>
|
||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||
<rect x="6" y="6" width="12" height="12" rx="1"/>
|
||
</svg>
|
||
</button>
|
||
<button class="ws-toolbar-btn" id="wsClearBtn" title="清空">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="3 6 5 6 21 6"/>
|
||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 工具调用 Tab -->
|
||
<div class="ws-content" id="wsToolsContent">
|
||
<div class="ws-tools-output" id="wsToolsOutput"></div>
|
||
<div class="ws-term-toolbar">
|
||
<span class="ws-tools-hint" id="wsToolsHint">等待工具调用...</span>
|
||
<div class="ws-term-toolbar-btns">
|
||
<button class="ws-toolbar-btn" id="wsToolsClearBtn" title="清空">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="3 6 5 6 21 6"/>
|
||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件 Tab -->
|
||
<div class="ws-content" id="wsFilesContent" style="display:none;">
|
||
<div class="ws-file-toolbar">
|
||
<button class="ws-toolbar-btn" id="wsFileUpBtn" title="上级目录">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="15 18 9 12 15 6"/>
|
||
</svg>
|
||
</button>
|
||
<span class="ws-file-path" id="wsFilePath">~</span>
|
||
<button class="ws-toolbar-btn" id="wsFileRefreshBtn" title="刷新">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="23 4 23 10 17 10"/>
|
||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="ws-file-list" id="wsFileList"></div>
|
||
<div class="ws-preview" id="wsPreview" style="display:none;">
|
||
<div class="ws-preview-header">
|
||
<span class="ws-preview-title" id="wsPreviewTitle"></span>
|
||
<button class="icon-btn" id="wsPreviewClose">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="ws-preview-content" id="wsPreviewContent"></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ═══════════════ 设置模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="settingsModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>⚙️ 设置</h3>
|
||
<button class="icon-btn" id="btnCloseSettings">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="setting-group">
|
||
<label class="setting-label">Ollama 服务地址</label>
|
||
<input class="setting-input" id="inputServerUrl" type="url" placeholder="http://127.0.0.1:11434">
|
||
<div class="connection-info" id="connInfo">
|
||
<span class="status-badge" id="connBadge">检测中...</span>
|
||
<span class="server-version" id="serverVersion"></span>
|
||
</div>
|
||
<div class="cors-hint" id="corsHint" style="display:none;">
|
||
<p>⚠️ 连接失败,可能是 CORS 问题。请在启动 Ollama 时设置环境变量:</p>
|
||
<code>OLLAMA_ORIGINS="*" ollama serve</code>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label" style="margin-top:12px;">温度(Temperature):<span id="tempValue">0.7</span></label>
|
||
<input type="range" id="inputTemperature" class="temp-slider" min="0" max="2" step="0.1" value="0.7">
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">低值更精确稳定,高值更有创意多样(0 = 确定性,2 = 最大随机)</p>
|
||
<label class="setting-label" style="margin-top:12px;">Agent 最大轮数(maxTurns)</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<input class="setting-input" id="inputMaxTurns" type="number" min="1" max="500" step="1" value="85" style="margin-bottom:0;">
|
||
<span class="text-muted" style="white-space:nowrap;">轮</span>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">Agent Loop 最大工具调用轮数。默认 85 轮,复杂任务可能需要更多轮次。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">⏱️ 超时设置(Timeout)</label>
|
||
<p class="text-muted" style="font-size:11px;margin-bottom:10px;">调整各通信层超时时长。留空则使用默认值(HTTP:30s / 流式:300s / MCP:60s)。设为 0 可禁用超时。</p>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||
<div>
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputHttpTimeout">🌐 HTTP 请求超时(秒)</label>
|
||
<input class="setting-input" id="inputHttpTimeout" type="number" min="0" step="5" placeholder="30" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">web_search / web_fetch 等 HTTP 请求的超时</p>
|
||
</div>
|
||
<div>
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputStreamTimeout">📡 流式调用超时(秒)</label>
|
||
<input class="setting-input" id="inputStreamTimeout" type="number" min="0" step="10" placeholder="300" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">Ollama 单轮流式响应的最大等待时间</p>
|
||
</div>
|
||
<div style="grid-column:1/-1;">
|
||
<label style="font-size:12px;color:var(--text-secondary);" for="inputMCPTimeout">🔌 MCP 请求超时(秒)</label>
|
||
<input class="setting-input" id="inputMCPTimeout" type="number" min="0" step="10" placeholder="60" style="margin-bottom:0;">
|
||
<p class="text-muted" style="font-size:10px;">MCP Server JSON-RPC 请求的超时(需重启 MCP 服务器生效)</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">运行中的模型</label>
|
||
<div class="running-models" id="runningModels">
|
||
<p class="text-muted">无运行中的模型</p>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🖥️ 工作空间目录</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<input class="setting-input" id="inputWorkspaceDir" type="text" placeholder="默认: 应用数据目录/workspace" style="margin-bottom:0;flex:1;" readonly>
|
||
<button class="btn btn-sm btn-outline" id="btnBrowseWorkspace">📂 浏览</button>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:4px;font-size:11px;">工作空间用于存放 AI 执行命令时的工作文件。命令行默认在此目录执行。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">
|
||
🧠 Agent 记忆系统
|
||
<label class="toggle-label" style="margin-left:auto;display:inline-flex;">
|
||
<input type="checkbox" id="toggleMemoryEnabled" checked>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</label>
|
||
<p class="text-muted" style="font-size:11px;">启用后,AI 会自动从对话中提取关键信息(事实、偏好、规则)并跨会话记住。发送消息时自动检索相关记忆注入上下文。</p>
|
||
<div class="tool-list" style="margin-top:8px;">
|
||
<div class="tool-item"><span>📌 事实</span><span class="text-muted">项目信息、身份背景</span></div>
|
||
<div class="tool-item"><span>⚙️ 偏好</span><span class="text-muted">语言风格、技术栈偏好</span></div>
|
||
<div class="tool-item"><span>📏 规则</span><span class="text-muted">编码规范、输出格式要求</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🧠 向量记忆引擎</label>
|
||
<p class="text-muted" style="font-size:11px;margin-bottom:8px;">选择嵌入模型以启用向量语义记忆。未选择时仅使用关键词匹配。</p>
|
||
<select class="setting-input" id="selectMemoryEmbedModel">
|
||
<option value="">未选择(仅关键词模式)</option>
|
||
</select>
|
||
<div id="memoryVectorStatus" class="text-muted" style="font-size:11px;margin-top:4px;"></div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🤖 子代理默认模型</label>
|
||
<p class="text-muted" style="font-size:11px;margin-bottom:8px;">子代理(spawn_task)使用的默认模型。留空则使用当前选中的模型。可为简单任务选择更快的模型。</p>
|
||
<select class="setting-input" id="selectSubAgentModel">
|
||
<option value="">跟随当前模型</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">显存管理</label>
|
||
<button class="btn btn-danger" id="btnReleaseVRAM">释放显存(卸载模型)</button>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">数据管理</label>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||
<button class="btn btn-outline" id="btnExportAll">📦 导出全部会话</button>
|
||
<button class="btn btn-outline" id="btnImportSessions">📥 导入会话</button>
|
||
<button class="btn btn-danger" id="btnClearAllHistory">清空所有历史</button>
|
||
</div>
|
||
<p class="text-muted" style="margin-top:6px;font-size:11px;">导出格式为 JSON,可备份后在其他设备导入恢复。</p>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">🔌 MCP 服务器</label>
|
||
<div style="display:flex;gap:8px;margin-bottom:8px;">
|
||
<input class="setting-input" id="inputMCPName" type="text" placeholder="名称" style="margin-bottom:0;width:100px;">
|
||
<input class="setting-input" id="inputMCPCommand" type="text" placeholder="命令 (如: npx)" style="margin-bottom:0;width:100px;">
|
||
<input class="setting-input" id="inputMCPArgs" type="text" placeholder="参数 (空格分隔)" style="margin-bottom:0;flex:1;">
|
||
<button class="btn btn-sm btn-outline" id="btnAddMCPServer">➕ 添加</button>
|
||
</div>
|
||
<div id="mcpServerList" style="font-size:12px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 历史记录模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="historyModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>🕐 历史记录</h3>
|
||
<button class="icon-btn" id="btnCloseHistory">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="history-search-wrap">
|
||
<input class="history-search-input" id="historySearchInput" type="text" placeholder="搜索会话标题或内容...">
|
||
<button class="history-search-clear" id="historySearchClear" style="display:none;">✕</button>
|
||
</div>
|
||
<div class="history-list" id="historyList"></div>
|
||
<div class="history-pagination" id="historyPagination"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 帮助模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="helpModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>📖 使用帮助</h3>
|
||
<button class="icon-btn" id="btnCloseHelp">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="help-section"><h4>🚀 快速开始</h4><ol><li>确保 Ollama 已启动(默认 <code>http://127.0.0.1:11434</code>,可在设置中修改)</li><li>顶部模型栏选择一个模型,右侧会显示模型能力徽章(🧠 Think / 👁️ Vision / 🔧 Tools)</li><li>输入消息,按 <kbd>Enter</kbd> 发送,<kbd>Shift+Enter</kbd> 换行</li></ol></div>
|
||
<div class="help-section"><h4>💬 聊天功能</h4><ul><li><strong>流式回复</strong> — 实时打字效果,随时点 ■ 停止</li><li><strong>Think 推理</strong> — 下方 Think 按钮切换,让模型展示深度思考过程(需模型支持,如 Qwen3)</li><li><strong>上下文长度自动检测</strong> — 切换模型时自动从模型元数据获取实际支持的上下文长度(如 Qwen3.6 27B = 262,144 tokens),无需手动配置</li><li><strong>多模态</strong> — 上传图片或视频(≤10MB),模型需支持 Vision。图片自动压缩,视频 1fps 提取帧序列带时序标注</li><li><strong>文件分析</strong> — 支持 50+ 种文本/代码格式,单文件 ≤500KB,自动剥离注释并按上下文预算智能截断</li><li><strong>AI 回复顶部</strong> — 每条 AI 回复上方展示 📋 系统提示词折叠卡片,可点击查看实际发送给模型的完整上下文</li></ul></div>
|
||
<div class="help-section"><h4>🔧 Tool Calling(始终开启)</h4><ul><li>所有消息均通过 <strong>Agent Loop 自主调用本地工具</strong>,像一个本地 Agent,无普通聊天模式</li><li><strong>44 个工具</strong>,分为 10 类:<ul><li><strong>文件系统</strong>(16 个):read_file / write_file / list_directory / search_files / create_directory / delete_file / move_file / copy_file / edit_file / get_file_info / tree / download_file / diff_files / replace_in_files / read_multiple_files / compress</li><li><strong>命令执行</strong>(1 个):run_command(实时流式输出,支持自动/需确认/禁用三种模式,可配置超时)</li><li><strong>联网搜索</strong>(2 个):web_search(支持 SearXNG 元搜索引擎 JSON API / 四引擎 HTML 解析,双模式可切换)/ web_fetch(反爬headers+UA自动切换+自动回退浏览器渲染)</li><li><strong>Git</strong>(1 个):git(17 个子操作,push/pull/clone 内置60-120s超时保护)</li><li><strong>浏览器控制</strong>(9 个):browser_open / browser_screenshot / browser_evaluate / browser_extract / browser_click / browser_type / browser_scroll / browser_wait / browser_close</li><li><strong>记忆 & 会话 & Skill</strong>(8 个):memory_search / memory_add / memory_replace / memory_remove / session_list / session_read / skill_list / skill_view</li><li><strong>子代理</strong>(1 个):spawn_task</li><li><strong>系统工具</strong>(6 个):datetime / calculator / random / uuid / json_format / hash</li></ul></li><li>read_file 支持 binary模式读取/base64解码/字节分页/2000行默认+截断hint自动续读</li><li>write_file 支持 base64写二进制文件/mode(overwrite+append)合并append_file功能/10MB</li><li>edit_file 支持 use_regex 正则替换</li><li>search_files 支持正则表达式搜索(use_regex=true)</li><li>browser_screenshot 支持全页面截图+元素截图</li><li>browser_extract 支持CSS选择器提取特定区域</li><li>new browser_wait 等待元素出现或定时等待</li><li>仅 <code>run_command</code> 支持三模式切换:自动/需确认/禁用</li><li>危险命令(<code>rm -rf</code>、<code>mkfs</code>、反弹 shell 等)和系统路径(<code>/etc</code>、<code>~/.ssh</code> 等)被自动拦截</li><li>工具调用以<strong>可视化卡片</strong>展示状态(pending → running → success/error),在工作空间🔧工具页签中显示</li><li>默认最大 <strong>85 轮</strong>工具调用循环,上下文使用率>80%时自动缩减到3轮</li><li>独立工具自动<strong>并行执行</strong>(16个只读工具加入并行白名单),有依赖关系的工具串行执行</li></ul></div>
|
||
<div class="help-section"><h4>🧠 Agent 记忆系统</h4><ul><li>设置中开启后,AI 从对话中<strong>自动提取关键信息</strong>(事实/偏好/规则),跨会话持续积累</li><li><strong>增量提取</strong>:长对话中每 20 轮自动触发轻量级记忆提取,不等到对话结束</li><li>对话结束时自动触发完整记忆提取</li><li>新对话时自动检索相关记忆注入上下文,让 AI "记住"你</li><li>点击顶部 🧠 按钮打开记忆面板:搜索、筛选、编辑、删除</li><li><strong>记忆容量上限 500 条</strong>,超限时自动清理低价值条目(规则类型受保护)</li><li><strong>向量语义搜索</strong>:在设置中选择嵌入模型后,支持语义级别记忆检索(更精准)</li><li>未选择嵌入模型时,使用关键词匹配检索记忆</li><li>AI 可通过 memory 工具主动管理记忆</li><li>记忆写入前自动安全扫描(prompt injection / 敏感信息 / 不可见字符检测)</li><li>记忆存储在本地 SQLite,不会上传到任何服务器</li></ul></div>
|
||
<div class="help-section"><h4>🤖 Agent Loop v0.11.7 增强</h4><ul><li><strong>🎬 视频上传</strong>:支持上传 .mp4/.avi/.mov/.mkv/.webm 等视频(≤10MB),自动 1fps 提取帧序列(带时间戳),多模态模型原生理解视频时序关系</li><li><strong>智能上下文压缩</strong>:滑动窗口 + LLM结构化JSON摘要,超过50%上下文窗口自动触发</li><li><strong>流式调用超时保护</strong>:可配置超时(默认300s),Ollama 卡死不再永久阻塞</li><li><strong>HTTP/MCP 超时可配</strong>:设置面板可分别调整 HTTP(默认30s)和 MCP(默认60s)超时</li><li><strong>Final Answer 智能检测</strong>:多模式匹配(Final Answer/最终答案/最终回答/总结),避免漏检或误触</li><li><strong>Token 感知迭代预算</strong>:上下文使用率>80%时自动缩减剩余轮次到3轮</li><li><strong>工具缓存 TTL</strong>:搜索5分钟/网页10分钟/文件30分钟,过期自动刷新</li><li><strong>自动子任务拆解</strong>:检测"同时/分别/以及"等并行关键词,自动 spawn 子代理并行处理</li><li><strong>旧工具结果自动截断</strong>:超过10轮的工具结果自动截断到500字符,控制上下文膨胀</li></ul></div>
|
||
<div class="help-section"><h4>🔌 MCP(Model Context Protocol)</h4><ul><li>支持连接外部 MCP Server,动态扩展工具能力</li><li>设置面板可添加/启用/禁用/删除 MCP 服务器</li><li>MCP 工具以 <code>mcp_{server}_{tool}</code> 前缀注册,与内置工具统一调度</li><li>启动时自动连接已启用的 MCP 服务器</li></ul></div>
|
||
<div class="help-section"><h4>🔍 SearXNG 元搜索引擎</h4><ul><li>点击顶部 🔍 按钮打开配置面板,可接入自部署的 SearXNG 实例</li><li><strong>JSON 模式</strong>:调用 SearXNG JSON API,聚合 70+ 引擎结果,结构化解析</li><li><strong>HTML 模式</strong>:获取原始搜索结果页面,交由 AI 自行分析提取信息</li><li>支持认证 Key(HTTP Header Authorization),保护私有实例</li><li>启用后替代内置四引擎方案;关闭即回退,无缝切换</li><li>所有参数(引擎、语言、安全搜索、时间范围等)均可独立配置</li></ul></div>
|
||
<div class="help-section"><h4>📋 自定义文件(SOUL.md / AGENT.md / USER.md)</h4><ul><li>在工作空间目录创建以下文件即可自定义 AI 行为,修改后下一轮对话立即生效</li><li><strong>SOUL.md</strong> — AI 身份、性格、行为准则(<strong>永远不可被压缩</strong>,注入为最高优先级系统提示词)</li><li><strong>AGENT.md</strong> — 工具调用规则、链式调用模式、核心约束(内置精简默认版,可通过工作空间覆盖)</li><li><strong>USER.md</strong> — 用户画像:技术栈、偏好、习惯等个人信息,AI 在对话中自动参考</li><li>可在 AI 回复顶部的 📋 系统提示词卡片中查看实际注入的完整上下文</li><li>删除工作空间中的文件即可恢复为内置默认版本</li></ul></div>
|
||
<div class="help-section"><h4>🎯 技能自动生成</h4><ul><li>AI 完成包含 <strong>2+ 工具调用</strong>的复杂任务后,自动从执行轨迹中提取可复用技能</li><li><strong>语义匹配</strong>:配置嵌入模型后,根据用户消息的语义自动匹配最相关技能</li><li><strong>参数自优化</strong>:同一技能链多次执行后,自动合并高频参数提示,越用越精准</li><li><strong>技能衰减</strong>:30 天未使用的技能自动降权,90 天几乎忽略</li><li><strong>链合并</strong>:相同前缀(前 2 步相同)的多个技能自动合并为抽象技能</li><li>AI 可通过 <code>skill_list</code>(渐进式摘要)和 <code>skill_view</code>(完整工具链)查看技能</li><li>技能存储在本地 SQLite,不会上传到任何服务器</li></ul></div>
|
||
<div class="help-section"><h4>📊 Token 实时监控</h4><ul><li>点击顶部 📊 按钮打开 Token 监控仪表盘</li><li><strong>全局统计</strong> — 跨会话累计 Token 消耗,柱状图按会话展示趋势</li><li><strong>当前会话</strong> — 实时显示本轮对话的 Token 消耗,按轮次展示明细</li><li>每 2 秒自动刷新数据,支持输入/输出分色显示</li></ul></div>
|
||
<div class="help-section"><h4>🖥️ 布局说明</h4><ul><li><strong>左侧面板</strong> — 执行日志,实时显示应用运行日志(连接、模型加载、工具调用等)</li><li><strong>中间区域</strong> — 聊天消息,顶部 Header + 模型栏,底部输入框</li><li><strong>右侧面板</strong> — 工作空间(常驻显示),包含 3 个页签:<ul><li><strong>💻 命令行 Tab</strong> — 终端界面,实时流式输出,支持长时间运行(无超时),单一终端进程</li><li><strong>🔧 工具 Tab</strong> — 展示本轮对话的工具调用卡片,含状态统计(总数 ✅ 成功 ❌ 失败),AI 执行命令时自动切到此页签</li><li><strong>📁 文件 Tab</strong> — 浏览工作空间目录,点击文件预览内容(带行号),支持上级目录导航</li></ul></li><li>工作空间目录可在设置中修改</li></ul></div>
|
||
<div class="help-section"><h4>🕐 历史记录</h4><ul><li>所有会话自动保存到本地 SQLite</li><li>点击顶部 🕐 按钮查看、搜索、恢复历史会话</li><li>支持导出 JSON / <code>.metona</code> 加密备份</li></ul></div>
|
||
<div class="help-section"><h4>⚡ 快捷键 & 命令</h4><table class="help-shortcuts"><tr><td><kbd>Enter</kbd></td><td>发送消息</td></tr><tr><td><kbd>Shift + Enter</kbd></td><td>换行</td></tr><tr><td><kbd>Ctrl + N</kbd></td><td>新建会话</td></tr><tr><td><kbd>Ctrl + M</kbd></td><td>记忆管理</td></tr><tr><td><kbd>Esc</kbd></td><td>关闭弹窗</td></tr></table><p style="margin-top:8px;font-size:12px;color:var(--text-secondary);">输入框命令(直接输入后发送):</p><table class="help-shortcuts"><tr><td><code>/retry</code></td><td>重试上一轮回复</td></tr><tr><td><code>/undo</code></td><td>撤销最后一条用户消息及回复</td></tr><tr><td><code>/compress</code></td><td>用 AI 摘要压缩长对话上下文</td></tr></table></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 工具面板模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="toolsModal" style="display:none;">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3>🔧 工具面板(44 个)</h3>
|
||
<button class="icon-btn" id="btnCloseTools">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="text-muted" style="font-size:12px;margin-bottom:12px;">AI 始终可在对话中自主调用以下工具(Tool Calling 永久开启)。高风险操作需用户确认。</p>
|
||
<div class="tools-grid">
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📄</span><span class="tool-card-name">read_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">读取文件(文本/binary/base64),2000行默认,截断自动提示续读</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">✏️</span><span class="tool-card-name">write_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">写入/追加文件,支持base64二进制,最大10MB</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📁</span><span class="tool-card-name">list_directory</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">列出目录内容(2000条上限),支持递归/偏移分页/扩展名过滤</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔍</span><span class="tool-card-name">search_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">按文件名或内容搜索,支持正则表达式</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📂</span><span class="tool-card-name">create_directory</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">创建目录,自动创建父级</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🗑️</span><span class="tool-card-name">delete_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">删除文件或目录,返回被删文件数和总大小</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📦</span><span class="tool-card-name">move_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">移动或重命名文件/目录</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">copy_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">复制文件或目录到新位置</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🌐</span><span class="tool-card-name">web_fetch</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">抓取网页内容,反爬headers+UA切换+自动回退浏览器渲染</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">✂️</span><span class="tool-card-name">edit_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">查找替换(支持正则use_regex),全部或单次替换</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">💻</span><span class="tool-card-name">run_command</span><span class="tool-card-badge confirm" id="runCommandBadge">需确认</span></div>
|
||
<div class="tool-card-desc">执行 shell 命令,无超时限制</div>
|
||
<div style="margin-top:6px;display:flex;align-items:center;gap:8px;">
|
||
<label style="margin:0;font-size:11px;color:var(--text-secondary);">执行模式:</label>
|
||
<select id="selectRunCommandMode" style="font-size:11px;padding:2px 6px;background:var(--bg-card);color:var(--text-primary);border:1px solid var(--border-default);border-radius:4px;">
|
||
<option value="confirm">需确认</option>
|
||
<option value="auto">自动执行</option>
|
||
<option value="disabled">禁用</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">ℹ️</span><span class="tool-card-name">get_file_info</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">获取文件/目录详细信息:大小、日期、权限</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🌳</span><span class="tool-card-name">tree</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">以树形结构展示目录内容</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⬇️</span><span class="tool-card-name">download_file</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">从 URL 下载文件到本地</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔀</span><span class="tool-card-name">diff_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">对比两个文件,显示差异</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔄</span><span class="tool-card-name">replace_in_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">按 glob 模式批量查找替换多个文件</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📚</span><span class="tool-card-name">read_multiple_files</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">并行读取最多50个文件,每文件10KB</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔖</span><span class="tool-card-name">git</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">Git 全操作(17个子命令:status/log/diff/add/commit/push等)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🗜️</span><span class="tool-card-name">compress</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">创建或解压 zip/tar.gz 归档</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔍</span><span class="tool-card-name">web_search</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">SearXNG JSON API / 四引擎并行,默认30条</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🧠</span><span class="tool-card-name">memory_search</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">语义搜索 Agent 记忆,检索相关历史信息</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">💾</span><span class="tool-card-name">memory_add</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">添加记忆条目(事实/偏好/规则),跨会话积累</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔄</span><span class="tool-card-name">memory_replace</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">替换已有记忆内容(子串匹配 old_text,替换为 new_content)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🗑️</span><span class="tool-card-name">memory_remove</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">删除记忆条目(子串匹配 old_text)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">session_list</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">列出历史会话,支持按标题搜索</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📖</span><span class="tool-card-name">session_read</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">读取历史会话消息内容</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🤖</span><span class="tool-card-name">spawn_task</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">生成子代理独立执行任务,完成后回传结果</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📚</span><span class="tool-card-name">skill_list</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">列出所有自动生成的技能(名称、描述、成功率)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📖</span><span class="tool-card-name">skill_view</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">查看技能详情(完整工具链、参数提示、使用统计)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🌐</span><span class="tool-card-name">browser_open</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">打开URL,支持等待特定CSS元素出现(SPA页面)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📸</span><span class="tool-card-name">browser_screenshot</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">截图(视口/全页面/指定元素),返回base64 PNG</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⚡</span><span class="tool-card-name">browser_evaluate</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">在浏览器页面中执行 JavaScript</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📰</span><span class="tool-card-name">browser_extract</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">提取页面文本+链接,支持CSS选择器指定区域</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">👆</span><span class="tool-card-name">browser_click</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">点击元素,支持等待出现+自动滚动到视图</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⌨️</span><span class="tool-card-name">browser_type</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">输入文本,支持追加模式(不先清空)+自动提交表单</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">↕️</span><span class="tool-card-name">browser_scroll</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">滚动页面(上/下/顶/底)或滚动到指定元素</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">⏳</span><span class="tool-card-name">browser_wait</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">等待指定元素出现或固定时间延迟</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">❌</span><span class="tool-card-name">browser_close</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">关闭 Agent 浏览器并释放资源</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🕐</span><span class="tool-card-name">datetime</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">获取系统精确时间(ISO/Unix/日期/时间/完整)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔢</span><span class="tool-card-name">calculator</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">安全数学计算(+ - * / ** % (),递归下降解析器)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🎲</span><span class="tool-card-name">random</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">随机数/随机选择(整数/浮点/抽取/字符串)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🆔</span><span class="tool-card-name">uuid</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">生成 UUID v4 唯一标识符(批量最多20个)</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">📋</span><span class="tool-card-name">json_format</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">JSON 格式化 + 语法验证 + 键排序</div>
|
||
</div>
|
||
<div class="tool-card">
|
||
<div class="tool-card-header"><span class="tool-card-icon">🔐</span><span class="tool-card-name">hash</span><span class="tool-card-badge auto">自动</span></div>
|
||
<div class="tool-card-desc">哈希计算(MD5/SHA1/SHA256/SHA384/SHA512)</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ Token 监控仪表盘 ═══════════════ -->
|
||
<div class="modal-overlay" id="tokenDashboardModal" style="display:none;">
|
||
<div class="modal" style="max-width:860px;width:860px;">
|
||
<div class="modal-header">
|
||
<h3>📊 Token 实时监控</h3>
|
||
<button class="icon-btn" id="btnCloseTokenDashboard">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body" style="max-height:75vh;overflow-y:auto;">
|
||
<div class="td-overview" id="tdOverview"></div>
|
||
<div class="td-view-toggle" id="tdViewToggle">
|
||
<button class="td-view-btn active" data-view="global">🌍 全局统计</button>
|
||
<button class="td-view-btn" data-view="session">💬 当前会话</button>
|
||
</div>
|
||
<div class="td-section">
|
||
<div class="td-section-title" id="tdChartTitle">📈 Token 消耗趋势 — 全局</div>
|
||
<div class="td-chart" id="tdChart"></div>
|
||
</div>
|
||
<div class="td-section">
|
||
<div class="td-section-title" id="tdTableTitle">📋 明细(按时间倒序)</div>
|
||
<div class="td-table-wrap">
|
||
<table class="td-table">
|
||
<thead>
|
||
<tr>
|
||
<th>轮次</th>
|
||
<th>角色</th>
|
||
<th>输入</th>
|
||
<th>输出</th>
|
||
<th>合计</th>
|
||
<th>耗时</th>
|
||
<th>时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tdTableBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 图片预览 Lightbox ═══════════════ -->
|
||
<div class="lightbox-overlay" id="lightbox" style="display:none;">
|
||
<button class="lightbox-close" id="lightboxClose">✕</button>
|
||
<img class="lightbox-img" id="lightboxImg" src="" alt="预览">
|
||
</div>
|
||
|
||
<!-- ═══════════════ 工具调用确认对话框 ═══════════════ -->
|
||
<div class="modal-overlay" id="toolConfirmModal" style="display:none;">
|
||
<div class="modal" style="max-width:520px;">
|
||
<div class="modal-header">
|
||
<h3 id="toolConfirmTitle">⚠️ 确认操作</h3>
|
||
</div>
|
||
<div class="modal-body" id="toolConfirmBody">
|
||
<p>AI 请求执行一个需要确认的操作。</p>
|
||
</div>
|
||
<div class="modal-actions" style="padding:12px 20px 16px;">
|
||
<div style="flex:1;"></div>
|
||
<button class="btn btn-outline" id="toolCancelBtn">❌ 取消</button>
|
||
<button class="btn btn-primary" id="toolConfirmBtn">✅ 确认执行</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ Toast 通知容器 ═══════════════ -->
|
||
<div class="toast-container" id="toastContainer"></div>
|
||
|
||
<!-- ═══════════════ SearXNG 配置模态框 ═══════════════ -->
|
||
<div class="modal-overlay" id="searxngModal" style="display:none;">
|
||
<div class="modal" style="max-width:640px;">
|
||
<div class="modal-header">
|
||
<h3>🔍 SearXNG 元搜索引擎</h3>
|
||
<button class="icon-btn" id="btnCloseSearxng">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="setting-group">
|
||
<label class="setting-label">
|
||
<span class="searxng-badge" id="searxngBadge">⚪ 未启用</span>
|
||
<label class="toggle-label" style="margin-left:auto;display:inline-flex;">
|
||
<input type="checkbox" id="toggleSearxngEnabled">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</label>
|
||
<p class="text-muted" style="font-size:11px;margin-top:4px;">
|
||
启用后,联网搜索将使用 SearXNG 元搜索引擎(隐私保护、70+ 引擎聚合),替代内置的四引擎 HTML 解析方案。
|
||
</p>
|
||
</div>
|
||
|
||
<div class="setting-group">
|
||
<label class="setting-label">SearXNG API 地址</label>
|
||
<input class="setting-input" id="searxngUrl" type="url" placeholder="请输入 SearXNG 实例地址" required style="margin-bottom:6px;">
|
||
<p class="text-muted" style="font-size:11px;">SearXNG 实例的根地址,无需带 /search 路径</p>
|
||
</div>
|
||
|
||
<div class="setting-group">
|
||
<label class="setting-label">搜索引擎</label>
|
||
<input class="setting-input" id="searxngEngines" type="text" placeholder="留空使用实例默认引擎,逗号分隔" style="margin-bottom:6px;">
|
||
<p class="text-muted" style="font-size:11px;">逗号分隔的引擎列表。常用: google, bing, duckduckgo, wikipedia, brave, qwant, startpage</p>
|
||
</div>
|
||
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;">
|
||
<div class="setting-group">
|
||
<label class="setting-label">语言</label>
|
||
<select class="setting-input" id="searxngLanguage" style="margin-bottom:0;">
|
||
<option value="zh-CN">简体中文</option>
|
||
<option value="zh-TW">繁體中文</option>
|
||
<option value="en">English</option>
|
||
<option value="ja">日本語</option>
|
||
<option value="ko">한국어</option>
|
||
<option value="">自动检测</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">安全搜索</label>
|
||
<select class="setting-input" id="searxngSafeSearch" style="margin-bottom:0;">
|
||
<option value="0">关闭</option>
|
||
<option value="1">中等</option>
|
||
<option value="2">严格</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">时间范围</label>
|
||
<select class="setting-input" id="searxngTimeRange" style="margin-bottom:0;">
|
||
<option value="">不限</option>
|
||
<option value="day">最近一天</option>
|
||
<option value="week">最近一周</option>
|
||
<option value="month">最近一月</option>
|
||
<option value="year">最近一年</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">单次结果数</label>
|
||
<input class="setting-input" id="searxngMaxResults" type="number" min="0" max="50" step="1" placeholder="0=使用默认值" style="margin-bottom:0;">
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">返回格式</label>
|
||
<select class="setting-input" id="searxngFormat" style="margin-bottom:0;">
|
||
<option value="json">JSON(结构化解析)</option>
|
||
<option value="html">HTML(原始网页,AI 自行分析)</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">自动抓取条数</label>
|
||
<input class="setting-input" id="searxngFetchCount" type="number" min="0" step="1" placeholder="0=使用AI指定值" style="margin-bottom:0;">
|
||
</div>
|
||
<div class="setting-group">
|
||
<label class="setting-label">抓取类型</label>
|
||
<select class="setting-input" id="searxngFetchMode" style="margin-bottom:0;">
|
||
<option value="sequential">顺序抓取</option>
|
||
<option value="random">随机抓取</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="setting-group">
|
||
<label class="setting-label">🔑 认证</label>
|
||
<div style="display:flex;gap:8px;align-items:center;">
|
||
<select class="setting-input" id="searxngAuthType" style="width:110px;flex-shrink:0;margin-bottom:0;">
|
||
<option value="bearer">Bearer</option>
|
||
<option value="basic">Basic</option>
|
||
</select>
|
||
<input class="setting-input" id="searxngAuthKey" type="password" placeholder="留空则不发送认证" style="flex:1;margin-bottom:0;">
|
||
</div>
|
||
<p class="text-muted" style="font-size:11px;margin-top:4px;">Bearer: API Token · Basic: 账号:密码(自动 Base64 编码)。需配合 Nginx 反向代理验证</p>
|
||
</div>
|
||
|
||
<div class="modal-actions">
|
||
<div style="flex:1;"></div>
|
||
<button class="btn btn-primary" id="btnSaveSearxng">💾 保存配置</button>
|
||
</div>
|
||
|
||
<div style="margin-top:16px;padding:12px;background:var(--bg-layer);border-radius:var(--radius-md);">
|
||
<p style="font-size:11px;color:var(--text-secondary);margin:0;">
|
||
💡 <strong>提示:</strong>SearXNG 是开源元搜索引擎,不追踪用户、不记录隐私。
|
||
需要自行部署或使用公共实例。API 端点格式:<code>{{URL}}/search?q={{query}}&format=json</code>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ 应用入口 ════════════════ -->
|
||
<script type="module" src="./main.ts"></script>
|
||
</body>
|
||
</html>
|