ui: 模型选择栏布局重设计
- 新增渐变图标指示器 - select 更大圆角 + 更粗边框 + hover/focus 渐变高亮 - 能力标签改为独立彩色 pill(Think 紫色、Vision 青色) - 箭头图标改为青色 - model-bar.js 适配新 badge 元素
This commit is contained in:
+58
-17
@@ -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
@@ -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
@@ -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';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前选择的模型
|
* 获取当前选择的模型
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user