577 lines
16 KiB
HTML
577 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>设置面板 - UI 预览</title>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: #0d1117;
|
|
color: #c9d1d9;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
display: flex;
|
|
justify-content: center;
|
|
min-height: 100vh;
|
|
padding: 24px;
|
|
}
|
|
.panel {
|
|
width: 380px;
|
|
background: #161b22;
|
|
border: 1px solid #30363d;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
|
align-self: flex-start;
|
|
}
|
|
|
|
/* ─── Header ─── */
|
|
.panel-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 14px 16px;
|
|
border-bottom: 1px solid #21262d;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #e6edf3;
|
|
}
|
|
.panel-header svg { flex-shrink: 0; }
|
|
|
|
/* ─── Section ─── */
|
|
.section { padding: 14px 16px; border-bottom: 1px solid #21262d; }
|
|
.section:last-of-type { border-bottom: none; }
|
|
.section-title {
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: .04em;
|
|
color: #8b949e;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
/* ─── Quick Start ─── */
|
|
.step {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 7px 0;
|
|
}
|
|
.step-circle {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
flex-shrink: 0;
|
|
background: #21262d;
|
|
color: #8b949e;
|
|
border: 2px solid #30363d;
|
|
transition: background .3s, border-color .3s, color .3s;
|
|
}
|
|
.step-circle.done {
|
|
background: rgba(139, 92, 246, 0.2);
|
|
border-color: #8b5cf6;
|
|
color: #a78bfa;
|
|
}
|
|
.step-text { font-size: 13px; color: #e6edf3; flex: 1; }
|
|
.step-hint { font-size: 11px; color: #8b949e; margin-top: 1px; }
|
|
.step-hint kbd {
|
|
display: inline-block;
|
|
padding: 1px 5px;
|
|
font-size: 10px;
|
|
font-family: 'SF Mono', Consolas, monospace;
|
|
background: #21262d;
|
|
border: 1px solid #30363d;
|
|
border-radius: 3px;
|
|
color: #8b949e;
|
|
}
|
|
|
|
/* ─── Engines ─── */
|
|
.engines { display: flex; flex-direction: column; gap: 6px; }
|
|
.engine-tab {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 10px;
|
|
border: 1px solid #21262d;
|
|
border-radius: 6px;
|
|
background: #0d1117;
|
|
cursor: default;
|
|
}
|
|
.engine-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
.dot-purple { background: #8b5cf6; }
|
|
.dot-amber { background: #d29922; }
|
|
.dot-green { background: #3fb950; }
|
|
.engine-label { font-size: 13px; color: #e6edf3; }
|
|
.engine-desc { font-size: 11px; color: #8b949e; }
|
|
|
|
/* ─── Card ─── */
|
|
.card {
|
|
background: #0d1117;
|
|
border: 1px solid #21262d;
|
|
border-radius: 8px;
|
|
padding: 10px 12px;
|
|
}
|
|
.card-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 6px;
|
|
}
|
|
.card-row:last-child { margin-bottom: 0; }
|
|
.card-label {
|
|
font-size: 12px;
|
|
color: #8b949e;
|
|
}
|
|
|
|
/* ─── Status badge ─── */
|
|
.badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 1px 8px;
|
|
border-radius: 10px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
line-height: 18px;
|
|
}
|
|
.badge-configured {
|
|
background: rgba(35, 134, 54, 0.15);
|
|
color: #3fb950;
|
|
}
|
|
.badge-unconfigured {
|
|
background: rgba(139, 148, 158, 0.12);
|
|
color: #8b949e;
|
|
}
|
|
|
|
/* ─── Field ─── */
|
|
.field { margin-bottom: 8px; }
|
|
.field:last-child { margin-bottom: 0; }
|
|
.field-label {
|
|
display: block;
|
|
font-size: 12px;
|
|
color: #8b949e;
|
|
margin-bottom: 3px;
|
|
}
|
|
select, input[type="text"], input[type="password"] {
|
|
width: 100%;
|
|
padding: 6px 10px;
|
|
background: #0d1117;
|
|
border: 1px solid #30363d;
|
|
border-radius: 6px;
|
|
color: #e6edf3;
|
|
font-size: 13px;
|
|
font-family: inherit;
|
|
outline: none;
|
|
transition: border-color .15s;
|
|
}
|
|
select:focus, input:focus { border-color: #8b5cf6; }
|
|
select {
|
|
appearance: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238b949e' viewBox='0 0 16 16'%3E%3Cpath d='M4.427 6.427l3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 6H4.604a.25.25 0 0 0-.177.427z'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 8px center;
|
|
padding-right: 30px;
|
|
cursor: pointer;
|
|
}
|
|
input::placeholder { color: #484f58; }
|
|
.field-hint {
|
|
font-size: 11px;
|
|
color: #484f58;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
/* ─── Input with button ─── */
|
|
.input-group { display: flex; gap: 4px; }
|
|
.input-group input { flex: 1; }
|
|
.input-group .btn { flex-shrink: 0; }
|
|
|
|
/* ─── Buttons ─── */
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 5px 12px;
|
|
border: 1px solid #30363d;
|
|
background: #21262d;
|
|
color: #c9d1d9;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
transition: background .15s, border-color .15s;
|
|
white-space: nowrap;
|
|
}
|
|
.btn:hover { background: #30363d; }
|
|
.btn-primary {
|
|
background: #7c3aed;
|
|
color: #fff;
|
|
border-color: #7c3aed;
|
|
}
|
|
.btn-primary:hover { background: #8b5cf6; }
|
|
.btn-primary:disabled {
|
|
opacity: .5;
|
|
cursor: not-allowed;
|
|
}
|
|
.btn-sm { padding: 2px 8px; font-size: 11px; line-height: 20px; }
|
|
|
|
/* ─── Toggle switch ─── */
|
|
.switch {
|
|
position: relative;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
width: 32px;
|
|
height: 18px;
|
|
flex-shrink: 0;
|
|
cursor: pointer;
|
|
}
|
|
.switch input { display: none; }
|
|
.switch-track {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 9px;
|
|
background: #30363d;
|
|
transition: background .2s;
|
|
}
|
|
.switch input:checked + .switch-track { background: #7c3aed; }
|
|
.switch-thumb {
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 2px;
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 50%;
|
|
background: #e6edf3;
|
|
transition: transform .2s;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
|
}
|
|
.switch input:checked ~ .switch-thumb { transform: translateX(14px); }
|
|
|
|
/* ─── Rule item ─── */
|
|
.rule-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 6px 8px;
|
|
margin-top: 4px;
|
|
border: 1px solid #21262d;
|
|
border-radius: 6px;
|
|
background: #0d1117;
|
|
transition: border-color .15s;
|
|
}
|
|
.rule-item:first-child { margin-top: 0; }
|
|
.rule-item:hover { border-color: #30363d; }
|
|
.rule-name {
|
|
flex: 1;
|
|
font-size: 13px;
|
|
font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
color: #e6edf3;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.rule-del {
|
|
flex-shrink: 0;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 4px;
|
|
border: none;
|
|
background: transparent;
|
|
color: #8b949e;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: color .15s, background .15s;
|
|
}
|
|
.rule-del:hover {
|
|
color: #f48771;
|
|
background: rgba(248,81,73,0.15);
|
|
}
|
|
|
|
/* ─── Actions bar ─── */
|
|
.actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
padding: 12px 16px;
|
|
border-top: 1px solid #21262d;
|
|
}
|
|
.actions .btn { flex: 1; justify-content: center; }
|
|
|
|
/* ─── Toast ─── */
|
|
.toast {
|
|
display: none;
|
|
margin: 0 16px 12px;
|
|
padding: 8px 12px;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
text-align: center;
|
|
animation: fadeIn .2s ease;
|
|
}
|
|
@keyframes fadeIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
|
|
.toast-success {
|
|
display: block;
|
|
background: rgba(35, 134, 54, 0.15);
|
|
border: 1px solid rgba(35, 134, 54, 0.3);
|
|
color: #3fb950;
|
|
}
|
|
.toast-error {
|
|
display: block;
|
|
background: rgba(248, 81, 73, 0.15);
|
|
border: 1px solid rgba(248, 81, 73, 0.3);
|
|
color: #f48771;
|
|
}
|
|
|
|
/* ─── Spinner ─── */
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
.spinner {
|
|
display: inline-block;
|
|
width: 14px;
|
|
height: 14px;
|
|
border: 2px solid rgba(255,255,255,0.2);
|
|
border-top-color: #fff;
|
|
border-radius: 50%;
|
|
animation: spin .6s linear infinite;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="panel">
|
|
<!-- Header -->
|
|
<div class="panel-header">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="12" cy="12" r="3"/>
|
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
|
</svg>
|
|
代码审查 · 设置
|
|
</div>
|
|
|
|
<!-- 1. 快速开始 -->
|
|
<div class="section">
|
|
<div class="section-title">快速开始</div>
|
|
<div class="step">
|
|
<div class="step-circle done">①</div>
|
|
<div>
|
|
<div class="step-text">配置 AI 模型及 API Key</div>
|
|
</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-circle">②</div>
|
|
<div>
|
|
<div class="step-text">启用自定义规则</div>
|
|
</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-circle">③</div>
|
|
<div>
|
|
<div class="step-text">保存并测试连接</div>
|
|
<div class="step-hint">快捷键 <kbd>Ctrl+Shift+R</kbd></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. 审核引擎 -->
|
|
<div class="section">
|
|
<div class="section-title">审核引擎</div>
|
|
<div class="engines">
|
|
<div class="engine-tab">
|
|
<span class="engine-dot dot-purple"></span>
|
|
<div>
|
|
<div class="engine-label">共通规则</div>
|
|
<div class="engine-desc">Linter 静态分析</div>
|
|
</div>
|
|
</div>
|
|
<div class="engine-tab">
|
|
<span class="engine-dot dot-amber"></span>
|
|
<div>
|
|
<div class="engine-label">自定义规则</div>
|
|
<div class="engine-desc">团队编码规范</div>
|
|
</div>
|
|
</div>
|
|
<div class="engine-tab">
|
|
<span class="engine-dot dot-green"></span>
|
|
<div>
|
|
<div class="engine-label">AI 审核</div>
|
|
<div class="engine-desc">深度代码审查</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. AI 模型配置 -->
|
|
<div class="section">
|
|
<div class="section-title">AI 模型配置</div>
|
|
<div class="card">
|
|
<div class="card-row">
|
|
<span class="card-label">模型提供商</span>
|
|
<span class="badge badge-configured">已配置</span>
|
|
</div>
|
|
<div class="field">
|
|
<select>
|
|
<option>DeepSeek</option>
|
|
<option>OpenAI</option>
|
|
<option>Azure OpenAI</option>
|
|
<option>腾讯混元</option>
|
|
<option>Ollama</option>
|
|
</select>
|
|
</div>
|
|
<div class="field">
|
|
<label class="field-label">模型名称</label>
|
|
<select>
|
|
<option selected>deepseek-chat</option>
|
|
<option>deepseek-reasoner</option>
|
|
<option>gpt-4o</option>
|
|
<option>gpt-4o-mini</option>
|
|
</select>
|
|
</div>
|
|
<div class="field-hint">建议使用支持结构化输出的模型。</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 4. API Key -->
|
|
<div class="section">
|
|
<div class="section-title">API Key</div>
|
|
<div class="card">
|
|
<div class="card-row">
|
|
<span class="card-label">API Key</span>
|
|
<span class="badge badge-configured">已配置</span>
|
|
</div>
|
|
<div class="field">
|
|
<input type="password" value="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" readonly>
|
|
</div>
|
|
<div class="field">
|
|
<label class="field-label">Base URL</label>
|
|
<input type="text" value="https://api.deepseek.com/v1">
|
|
</div>
|
|
<div class="field-hint">Key 仅存储在本地 VS Code 安全存储中。</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 5. 输出语言 -->
|
|
<div class="section">
|
|
<div class="section-title">输出语言</div>
|
|
<div class="field">
|
|
<label class="field-label">AI 审查结果输出语言</label>
|
|
<select>
|
|
<option selected>中文(简体)</option>
|
|
<option>English</option>
|
|
<option>日本語</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 6. 自定义规则 -->
|
|
<div class="section">
|
|
<div class="section-title">自定义规则</div>
|
|
<div class="card">
|
|
<div class="card-row">
|
|
<span class="card-label">规则列表</span>
|
|
<span class="badge badge-configured">3 条启用</span>
|
|
</div>
|
|
|
|
<div class="rule-item">
|
|
<label class="switch">
|
|
<input type="checkbox" checked>
|
|
<div class="switch-track"></div>
|
|
<div class="switch-thumb"></div>
|
|
</label>
|
|
<span class="rule-name">no-console-in-production</span>
|
|
<button class="rule-del">×</button>
|
|
</div>
|
|
<div class="rule-item">
|
|
<label class="switch">
|
|
<input type="checkbox" checked>
|
|
<div class="switch-track"></div>
|
|
<div class="switch-thumb"></div>
|
|
</label>
|
|
<span class="rule-name">max-function-lines: 80</span>
|
|
<button class="rule-del">×</button>
|
|
</div>
|
|
<div class="rule-item">
|
|
<label class="switch">
|
|
<input type="checkbox" checked>
|
|
<div class="switch-track"></div>
|
|
<div class="switch-thumb"></div>
|
|
</label>
|
|
<span class="rule-name">require-javadoc-public</span>
|
|
<button class="rule-del">×</button>
|
|
</div>
|
|
<div class="rule-item">
|
|
<label class="switch">
|
|
<input type="checkbox">
|
|
<div class="switch-track"></div>
|
|
<div class="switch-thumb"></div>
|
|
</label>
|
|
<span class="rule-name">no-any-type</span>
|
|
<button class="rule-del">×</button>
|
|
</div>
|
|
|
|
<div class="field" style="margin-top: 8px;">
|
|
<div class="input-group">
|
|
<input type="text" placeholder="输入规则名称...">
|
|
<button class="btn btn-sm" style="background:#7c3aed;color:#fff;border-color:#7c3aed;">+ 添加</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="actions">
|
|
<button class="btn" onclick="showToast('reset')">重置</button>
|
|
<button class="btn btn-primary" id="btn-test" onclick="testConnection()">保存并测试连接</button>
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div id="toast"></div>
|
|
</div>
|
|
|
|
<script>
|
|
function showToast(type, msg) {
|
|
var el = document.getElementById('toast');
|
|
el.className = 'toast';
|
|
if (type === 'success') {
|
|
el.classList.add('toast-success');
|
|
el.textContent = msg || '✓ 保存成功,API 连接正常';
|
|
} else if (type === 'error') {
|
|
el.classList.add('toast-error');
|
|
el.textContent = msg || '✗ 连接失败,请检查配置';
|
|
}
|
|
setTimeout(function() { el.className = 'toast'; }, 5000);
|
|
}
|
|
|
|
function testConnection() {
|
|
var btn = document.getElementById('btn-test');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner"></span> 测试中...';
|
|
|
|
setTimeout(function() {
|
|
// Simulate: 80% success
|
|
if (Math.random() > 0.2) {
|
|
btn.innerHTML = '✓ 已连接';
|
|
btn.className = 'btn btn-primary';
|
|
showToast('success');
|
|
// Mark step ① and ③ as done
|
|
document.querySelectorAll('.step-circle')[0].classList.add('done');
|
|
document.querySelectorAll('.step-circle')[2].classList.add('done');
|
|
} else {
|
|
btn.innerHTML = '✗ 重试';
|
|
btn.className = 'btn btn-primary';
|
|
showToast('error', '✗ API Key 无效,请重新设置');
|
|
}
|
|
btn.disabled = false;
|
|
}, 1500);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |