Files
metona-ollama-desktop/index.html
T
thzxx 6088ea4629 feat: 重新设计模型选择栏与 Think 模式
模型选择栏:
- 去掉原生 select 样式,自定义 appearance:none + 箭头图标
- 模型列表显示参数大小 (如 qwen2.5 · 7B)
- 选中模型后展示能力标签 (🧠 Think / 👁️ Vision)

Think 模式:
- 从模型栏移至输入区域,紧邻发送按钮
- 使用灯泡图标按钮替代原来的 toggle 开关
- 默认 disabled,切换模型时调用 /api/show 检测 capabilities
- capabilities 包含 'thinking' 时才启用,否则禁用并灰显
- 模型能力结果缓存,避免重复请求
2026-04-03 11:46:29 +08:00

215 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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="#0a0a1a">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="description" content="Metona Ollama Client - 流式聊天、多模态、历史管理">
<link rel="manifest" href="manifest.json">
<link rel="icon" href="assets/icons/llama.ico" type="image/x-icon">
<title>Metona Ollama</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="app">
<!-- ═══════════════ 顶部导航 ═══════════════ -->
<header class="app-header">
<div class="header-left">
<span class="logo">🦙</span>
<span class="app-title">Metona Ollama</span>
<span class="app-version">v2.0.0</span>
</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="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>
</div>
</header>
<!-- ═══════════════ 模型选择栏 ═══════════════ -->
<div class="model-bar">
<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>
<span class="model-info-badge" id="modelInfoBadge" 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>
</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="input-row">
<button class="icon-btn attach-btn" id="btnAttach" 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>
<input type="file" id="fileInput" accept="image/*" multiple style="display:none;">
<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>
<!-- ═══════════════ 设置模态框 ═══════════════ -->
<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">
系统提示词(System Prompt
<label class="toggle-label" style="margin-left:auto;display:inline-flex;">
<input type="checkbox" id="toggleSystemPrompt">
<span class="toggle-slider"></span>
</label>
</label>
<textarea class="setting-input" id="inputSystemPrompt" rows="4" placeholder="例如:你是一个专业的编程助手,用中文回答。" style="resize:vertical;min-height:80px;" disabled></textarea>
<label class="setting-label" style="margin-top:12px;">上下文长度(num_ctx</label>
<div style="display:flex;gap:8px;align-items:center;">
<input class="setting-input" id="inputNumCtx" type="number" min="512" max="131072" step="512" value="24576" style="margin-bottom:0;">
<span class="text-muted" style="white-space:nowrap;">tokens</span>
</div>
<p class="text-muted" style="margin-top:4px;font-size:11px;">常见值:2048 / 4096 / 8192 / 32768。值越大上下文越长,显存占用越高。</p>
</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>
<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>
<input type="file" id="importFileInput" accept=".json" style="display:none;">
<button class="btn btn-danger" id="btnClearAllHistory">清空所有历史</button>
</div>
<p class="text-muted" style="margin-top:6px;font-size:11px;">导出格式为 JSON,可备份后在其他设备导入恢复。</p>
</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>
<!-- ═══════════════ 图片预览 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>
<!-- ═══════════════ Toast 通知容器 ═══════════════ -->
<div class="toast-container" id="toastContainer"></div>
<!-- ═══════════════ 应用入口(ES Module)═══════════════ -->
<script type="module" src="./js/app.js"></script>
</body>
</html>