ui: 模型选择栏布局重设计

- 新增渐变图标指示器
- select 更大圆角 + 更粗边框 + hover/focus 渐变高亮
- 能力标签改为独立彩色 pill(Think 紫色、Vision 青色)
- 箭头图标改为青色
- model-bar.js 适配新 badge 元素
This commit is contained in:
thzxx
2026-04-03 13:10:00 +08:00
parent 7f842fc02e
commit 66acfb7f3e
3 changed files with 78 additions and 36 deletions
+58 -17
View File
@@ -254,27 +254,47 @@ body::before {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 8px 16px; padding: 10px 16px;
background: var(--bg-secondary); background: var(--bg-secondary);
border-bottom: 1px solid var(--border-glass); border-bottom: 1px solid var(--border-glass);
flex-shrink: 0; flex-shrink: 0;
} }
.model-bar-icon {
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
background: var(--gradient-main);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(0, 245, 212, 0.2);
}
.model-bar-icon svg {
width: 16px;
height: 16px;
color: white;
}
.model-select-wrap { .model-select-wrap {
position: relative; position: relative;
flex: 1; flex: 1;
min-width: 0;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.model-select { .model-select {
width: 100%; width: 100%;
padding: 7px 32px 7px 12px; padding: 8px 36px 8px 14px;
background: var(--bg-glass-light); background: var(--bg-glass-light);
border: 1px solid var(--border-glass); border: 1.5px solid var(--border-glass);
border-radius: var(--radius-sm); border-radius: var(--radius-md);
color: var(--text-primary); color: var(--text-primary);
font-size: 13px; font-size: 13px;
font-weight: 500;
font-family: var(--font-sans); font-family: var(--font-sans);
outline: none; outline: none;
cursor: pointer; cursor: pointer;
@@ -285,39 +305,60 @@ body::before {
.model-select:focus { .model-select:focus {
border-color: var(--accent-cyan); border-color: var(--accent-cyan);
box-shadow: 0 0 0 2px rgba(0, 245, 212, 0.1); box-shadow: 0 0 0 3px rgba(0, 245, 212, 0.12), 0 0 12px rgba(0, 245, 212, 0.1);
}
.model-select:hover {
border-color: rgba(0, 245, 212, 0.4);
} }
.model-select option { .model-select option {
background: var(--bg-secondary); background: var(--bg-secondary);
color: var(--text-primary); color: var(--text-primary);
padding: 6px; padding: 8px;
} }
.model-select-arrow { .model-select-arrow {
position: absolute; position: absolute;
right: 8px; right: 10px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
pointer-events: none; pointer-events: none;
color: var(--text-muted); color: var(--accent-cyan);
display: flex; display: flex;
opacity: 0.6;
} }
.model-select-arrow svg { .model-select-arrow svg {
width: 14px; width: 16px;
height: 14px; height: 16px;
} }
.model-info-badge { .model-badges {
display: flex;
gap: 6px;
flex-shrink: 0;
}
.model-badge {
font-size: 11px; font-size: 11px;
color: var(--text-muted); font-weight: 600;
white-space: nowrap; white-space: nowrap;
padding: 2px 8px; padding: 4px 10px;
background: var(--bg-glass-light); border-radius: 20px;
border-radius: 10px; letter-spacing: 0.3px;
border: 1px solid var(--border-glass); }
display: none;
.think-badge {
color: var(--accent-purple);
background: rgba(123, 47, 247, 0.12);
border: 1px solid rgba(123, 47, 247, 0.25);
}
.vision-badge {
color: var(--accent-cyan);
background: rgba(0, 245, 212, 0.08);
border: 1px solid rgba(0, 245, 212, 0.2);
} }
/* ── Think 按钮(输入栏内) ── */ /* ── Think 按钮(输入栏内) ── */
+10 -1
View File
@@ -47,6 +47,12 @@
<!-- ═══════════════ 模型选择栏 ═══════════════ --> <!-- ═══════════════ 模型选择栏 ═══════════════ -->
<div class="model-bar"> <div class="model-bar">
<div class="model-bar-icon">
<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>
</div>
<div class="model-select-wrap"> <div class="model-select-wrap">
<select class="model-select" id="modelSelect"> <select class="model-select" id="modelSelect">
<option value="">正在加载模型...</option> <option value="">正在加载模型...</option>
@@ -55,7 +61,10 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
</span> </span>
</div> </div>
<span class="model-info-badge" id="modelInfoBadge" title="模型信息"></span> <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>
</div>
</div> </div>
<!-- ═══════════════ 聊天区域 ═══════════════ --> <!-- ═══════════════ 聊天区域 ═══════════════ -->
+10 -18
View File
@@ -6,14 +6,15 @@
import { state, KEYS } from '../state.js'; import { state, KEYS } from '../state.js';
import { formatSize } from '../utils.js'; import { formatSize } from '../utils.js';
let modelSelectEl, modelInfoBadgeEl, thinkCheckbox, thinkWrap; let modelSelectEl, badgeThinkEl, badgeVisionEl, thinkCheckbox, thinkWrap;
/** 缓存模型能力 { modelName: { think: bool, vision: bool } } */ /** 缓存模型能力 { modelName: { think: bool, vision: bool } } */
const modelCapabilityCache = new Map(); const modelCapabilityCache = new Map();
export function initModelBar() { export function initModelBar() {
modelSelectEl = document.querySelector('#modelSelect'); modelSelectEl = document.querySelector('#modelSelect');
modelInfoBadgeEl = document.querySelector('#modelInfoBadge'); badgeThinkEl = document.querySelector('#badgeThink');
badgeVisionEl = document.querySelector('#badgeVision');
thinkCheckbox = document.querySelector('#toggleThink'); thinkCheckbox = document.querySelector('#toggleThink');
thinkWrap = document.querySelector('#thinkToggleWrap'); thinkWrap = document.querySelector('#thinkToggleWrap');
@@ -25,7 +26,8 @@ export function initModelBar() {
await checkModelCapability(model); await checkModelCapability(model);
} else { } else {
setThinkAvailable(false); setThinkAvailable(false);
setModelBadge(''); badgeThinkEl.style.display = 'none';
badgeVisionEl.style.display = 'none';
} }
}); });
} }
@@ -45,7 +47,7 @@ export async function loadModels() {
if (!data.models || data.models.length === 0) { if (!data.models || data.models.length === 0) {
modelSelectEl.innerHTML = '<option value="">未安装任何模型</option>'; modelSelectEl.innerHTML = '<option value="">未安装任何模型</option>';
setModelBadge(''); badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
return; return;
} }
@@ -74,7 +76,7 @@ export async function loadModels() {
} catch (err) { } catch (err) {
console.error('[ModelBar] 加载模型失败:', err); console.error('[ModelBar] 加载模型失败:', err);
modelSelectEl.innerHTML = '<option value="">加载失败 - 检查连接</option>'; modelSelectEl.innerHTML = '<option value="">加载失败 - 检查连接</option>';
setModelBadge(''); badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
} }
} }
@@ -116,11 +118,9 @@ async function checkModelCapability(modelName) {
function applyCapability(modelName, caps) { function applyCapability(modelName, caps) {
setThinkAvailable(caps.think); setThinkAvailable(caps.think);
// 更新信息标签 // 更新能力标签
const tags = []; badgeThinkEl.style.display = caps.think ? '' : 'none';
if (caps.think) tags.push('🧠 Think'); badgeVisionEl.style.display = caps.vision ? '' : 'none';
if (caps.vision) tags.push('👁️ Vision');
setModelBadge(tags.join(' '));
} }
/** /**
@@ -139,14 +139,6 @@ function setThinkAvailable(available) {
} }
} }
/**
* 更新模型信息标签
*/
function setModelBadge(text) {
modelInfoBadgeEl.textContent = text;
modelInfoBadgeEl.style.display = text ? '' : 'none';
}
/** /**
* 获取当前选择的模型 * 获取当前选择的模型
*/ */