Files
2026Technology-Competition/docs/superpowers/specs/setup-panel-preview.html
T

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">&times;</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">&times;</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">&times;</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">&times;</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>