mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-06 20:02:49 +08:00
feat(setup): simplify Hermes install — remove extras, add inline logs
1. 移除可选组件(extras)选择 UI,只安装核心包,提高安装成功率 2. 安装过程中内嵌显示实时日志 + 进度条 + 百分比 3. 安装前显示安装说明(uv 管理器、核心包、扩展后装提示) 4. 进度条样式优化:渐变色、更粗、更圆润 5. 日志面板优化:等宽字体、空状态提示、min-height 6. 新增 4 个 i18n 键(installDescSimple/installInfoUv/installInfoCore/installInfoExtrasLater)
This commit is contained in:
@@ -18,18 +18,7 @@ const ICONS = {
|
||||
done: `<svg viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2" width="24" height="24"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>`,
|
||||
}
|
||||
|
||||
// 可选 extras
|
||||
const EXTRAS_LIST = [
|
||||
{ key: 'cron', i18n: 'extraCron', recommended: true },
|
||||
{ key: 'cli', i18n: 'extraCli', recommended: true },
|
||||
{ key: 'pty', i18n: 'extraPty', recommended: true },
|
||||
{ key: 'mcp', i18n: 'extraMcp', recommended: true },
|
||||
{ key: 'messaging', i18n: 'extraMessaging' },
|
||||
{ key: 'feishu', i18n: 'extraFeishu' },
|
||||
{ key: 'dingtalk', i18n: 'extraDingtalk' },
|
||||
{ key: 'slack', i18n: 'extraSlack' },
|
||||
{ key: 'voice', i18n: 'extraVoice' },
|
||||
]
|
||||
// 核心安装不带 extras,后续可在管理页面按需安装
|
||||
|
||||
// Hermes 使用 OpenAI 兼容接口,过滤出兼容的服务商
|
||||
const HERMES_PROVIDERS = PROVIDER_PRESETS.filter(p => !p.hidden)
|
||||
@@ -45,8 +34,6 @@ export function render() {
|
||||
let logs = []
|
||||
let installing = false
|
||||
let progress = 0
|
||||
let showLogs = false
|
||||
let selectedExtras = ['cron', 'cli', 'pty', 'mcp']
|
||||
let unlisten = null
|
||||
|
||||
function draw() {
|
||||
@@ -62,7 +49,6 @@ export function render() {
|
||||
${phase === 'configure' ? renderConfigure() : ''}
|
||||
${phase === 'gateway' ? renderGateway() : ''}
|
||||
${phase === 'complete' ? renderComplete() : ''}
|
||||
${renderLogPanel()}
|
||||
<div style="margin-top:16px;text-align:right">
|
||||
<a href="https://hermes-agent.nousresearch.com/docs/getting-started/installation/" target="_blank" rel="noopener"
|
||||
style="font-size:13px;color:var(--accent);text-decoration:none">
|
||||
@@ -143,34 +129,38 @@ export function render() {
|
||||
|
||||
// --- 安装阶段 ---
|
||||
function renderInstall() {
|
||||
const extrasHtml = EXTRAS_LIST.map(ex => {
|
||||
const checked = selectedExtras.includes(ex.key) ? 'checked' : ''
|
||||
return `<label class="hermes-extra-item">
|
||||
<input type="checkbox" value="${ex.key}" ${checked} class="hermes-extra-cb">
|
||||
<span>${t('engine.' + ex.i18n)}${ex.recommended ? ' ⭐' : ''}</span>
|
||||
</label>`
|
||||
}).join('')
|
||||
|
||||
const btnText = installing ? `${ICONS.spinner} ${t('engine.installingBtn')}` : `${ICONS.rocket} ${t('engine.installBtn')}`
|
||||
const btnDisabled = installing ? 'disabled' : ''
|
||||
|
||||
return `<div class="card" style="margin-bottom:16px">
|
||||
<div class="card-body" style="padding:24px">
|
||||
<h3 style="margin:0 0 4px;font-size:16px">${t('engine.installTitle')}</h3>
|
||||
<p style="color:var(--text-secondary);margin:0 0 20px;font-size:13px">${t('engine.installDesc')}</p>
|
||||
<p style="color:var(--text-secondary);margin:0 0 16px;font-size:13px">${t('engine.installDescSimple')}</p>
|
||||
|
||||
<div style="margin-bottom:20px">
|
||||
<div style="font-size:13px;font-weight:600;margin-bottom:8px">${t('engine.extrasTitle')}</div>
|
||||
<p style="font-size:12px;color:var(--text-tertiary);margin:0 0 10px">${t('engine.extrasDesc')}</p>
|
||||
<div class="hermes-extras-grid">${extrasHtml}</div>
|
||||
<button class="btn-text hermes-select-all" style="margin-top:6px;font-size:12px">${t('engine.extraAll')}</button>
|
||||
</div>
|
||||
${installing || progress > 0 ? `
|
||||
<div class="hermes-install-status">
|
||||
<div class="hermes-progress"><div class="hermes-progress-bar" style="width:${progress}%"></div></div>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:6px">
|
||||
<span class="hermes-progress-text" style="font-size:12px;color:var(--text-tertiary)">${progress >= 100 ? t('engine.installSuccess') : t('engine.installingBtn')}</span>
|
||||
<span style="font-size:12px;color:var(--text-tertiary);font-family:monospace">${Math.min(progress, 100)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hermes-log-panel" style="margin-top:12px">
|
||||
<div class="hermes-log-content">${logs.map(l => `<div>${esc(l)}</div>`).join('')}</div>
|
||||
</div>
|
||||
` : `
|
||||
<div class="hermes-install-info">
|
||||
<div class="hermes-detect-row" style="margin-bottom:6px">${ICONS.check} <span>${t('engine.installInfoUv')}</span></div>
|
||||
<div class="hermes-detect-row" style="margin-bottom:6px">${ICONS.check} <span>${t('engine.installInfoCore')}</span></div>
|
||||
<div class="hermes-detect-row" style="color:var(--text-tertiary)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
<span>${t('engine.installInfoExtrasLater')}</span>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
|
||||
${progress > 0 ? `<div class="hermes-progress"><div class="hermes-progress-bar" style="width:${progress}%"></div></div>` : ''}
|
||||
|
||||
<div style="display:flex;gap:10px;align-items:center">
|
||||
<div style="display:flex;gap:10px;align-items:center;margin-top:16px">
|
||||
<button class="btn btn-primary hermes-install-btn" ${btnDisabled}>${btnText}</button>
|
||||
${!installing ? `<button class="btn-text hermes-toggle-logs" style="font-size:12px">${showLogs ? t('engine.hideLogs') : t('engine.viewLogs')}</button>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
@@ -254,14 +244,6 @@ export function render() {
|
||||
</div>`
|
||||
}
|
||||
|
||||
// --- 日志面板 ---
|
||||
function renderLogPanel() {
|
||||
if (!showLogs || logs.length === 0) return ''
|
||||
return `<div class="hermes-log-panel">
|
||||
<div class="hermes-log-content">${logs.map(l => `<div>${esc(l)}</div>`).join('')}</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
}
|
||||
@@ -277,22 +259,6 @@ export function render() {
|
||||
})
|
||||
// 安装按钮
|
||||
el.querySelector('.hermes-install-btn')?.addEventListener('click', doInstall)
|
||||
// 全选 extras
|
||||
el.querySelector('.hermes-select-all')?.addEventListener('click', () => {
|
||||
selectedExtras = EXTRAS_LIST.map(e => e.key)
|
||||
draw()
|
||||
})
|
||||
// extras checkbox
|
||||
el.querySelectorAll('.hermes-extra-cb').forEach(cb => {
|
||||
cb.addEventListener('change', () => {
|
||||
if (cb.checked && !selectedExtras.includes(cb.value)) selectedExtras.push(cb.value)
|
||||
else selectedExtras = selectedExtras.filter(k => k !== cb.value)
|
||||
})
|
||||
})
|
||||
// 日志切换
|
||||
el.querySelector('.hermes-toggle-logs')?.addEventListener('click', () => {
|
||||
showLogs = !showLogs; draw()
|
||||
})
|
||||
// 服务商预设按钮
|
||||
el.querySelectorAll('.hermes-preset-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
@@ -387,18 +353,20 @@ export function render() {
|
||||
async function doInstall() {
|
||||
installing = true
|
||||
progress = 0
|
||||
showLogs = true
|
||||
logs = []
|
||||
draw()
|
||||
|
||||
// 监听事件
|
||||
// 监听事件(Tauri 模式下有 hermes-install-log/progress 事件)
|
||||
try {
|
||||
const { listen } = await import('@tauri-apps/api/event')
|
||||
const u1 = await listen('hermes-install-log', (e) => {
|
||||
logs.push(String(e.payload))
|
||||
const line = String(e.payload)
|
||||
logs.push(line)
|
||||
const logEl = el.querySelector('.hermes-log-content')
|
||||
if (logEl) {
|
||||
logEl.innerHTML += `<div>${esc(String(e.payload))}</div>`
|
||||
const div = document.createElement('div')
|
||||
div.textContent = line
|
||||
logEl.appendChild(div)
|
||||
logEl.scrollTop = logEl.scrollHeight
|
||||
}
|
||||
})
|
||||
@@ -406,20 +374,25 @@ export function render() {
|
||||
progress = Number(e.payload) || 0
|
||||
const bar = el.querySelector('.hermes-progress-bar')
|
||||
if (bar) bar.style.width = progress + '%'
|
||||
const pctEl = el.querySelector('.hermes-progress-text')
|
||||
if (pctEl) pctEl.textContent = progress >= 100 ? t('engine.installSuccess') : t('engine.installingBtn')
|
||||
// 更新百分比数字
|
||||
const pctNum = bar?.parentElement?.nextElementSibling?.querySelector('span:last-child')
|
||||
if (pctNum) pctNum.textContent = Math.min(progress, 100) + '%'
|
||||
})
|
||||
unlisten = () => { u1(); u2() }
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
await api.installHermes('uv-tool', selectedExtras)
|
||||
await api.installHermes('uv-tool', [])
|
||||
installing = false
|
||||
progress = 100
|
||||
logs.push(t('engine.installSuccess'))
|
||||
logs.push('✅ ' + t('engine.installSuccess'))
|
||||
phase = 'configure'
|
||||
draw()
|
||||
} catch (e) {
|
||||
installing = false
|
||||
logs.push(`${t('engine.installFailed')}: ${e}`)
|
||||
logs.push(`❌ ${t('engine.installFailed')}: ${e}`)
|
||||
draw()
|
||||
} finally {
|
||||
if (unlisten) { unlisten(); unlisten = null }
|
||||
|
||||
@@ -26,6 +26,10 @@ export default {
|
||||
// 安装阶段
|
||||
installTitle: _('安装 Hermes Agent', 'Install Hermes Agent', '安裝 Hermes Agent'),
|
||||
installDesc: _('通过 uv 自动安装(含 Python 环境),无需手动操作', 'Auto-install via uv (includes Python), no manual steps', '透過 uv 自動安裝(含 Python 環境),無需手動操作'),
|
||||
installDescSimple: _('一键安装 Hermes Agent 核心包,安装完成后可在管理页面按需添加扩展组件。', 'One-click install of Hermes Agent core package. Extensions can be added later from the management page.', '一鍵安裝 Hermes Agent 核心包,安裝完成後可在管理頁面按需添加擴展組件。'),
|
||||
installInfoUv: _('自动下载 uv 包管理器(如未安装)', 'Auto-download uv package manager (if not installed)', '自動下載 uv 包管理器(如未安裝)'),
|
||||
installInfoCore: _('安装 hermes-agent 核心包', 'Install hermes-agent core package', '安裝 hermes-agent 核心包'),
|
||||
installInfoExtrasLater: _('扩展组件(定时任务、MCP、消息渠道等)可在安装后按需添加', 'Extensions (cron, MCP, messaging, etc.) can be added later as needed', '擴展組件(定時任務、MCP、訊息頻道等)可在安裝後按需添加'),
|
||||
installBtn: _('一键安装', 'Install Now', '一鍵安裝'),
|
||||
installingBtn: _('正在安装...', 'Installing...', '正在安裝...'),
|
||||
installSuccess: _('安装成功!', 'Installation successful!', '安裝成功!'),
|
||||
|
||||
@@ -1786,40 +1786,44 @@
|
||||
|
||||
/* === Hermes Setup — Progress Bar === */
|
||||
.hermes-progress {
|
||||
height: 6px;
|
||||
height: 8px;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.hermes-progress-bar {
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
border-radius: 3px;
|
||||
transition: width 0.3s ease;
|
||||
background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 80%, #fff));
|
||||
border-radius: 4px;
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
|
||||
/* === Hermes Setup — Log Panel === */
|
||||
.hermes-log-panel {
|
||||
margin-top: 12px;
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.hermes-log-content {
|
||||
max-height: 240px;
|
||||
max-height: 260px;
|
||||
min-height: 80px;
|
||||
overflow-y: auto;
|
||||
padding: 12px 16px;
|
||||
background: var(--bg-tertiary);
|
||||
font-family: var(--font-mono, monospace);
|
||||
font-family: var(--font-mono, 'JetBrains Mono', 'Fira Code', monospace);
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
line-height: 1.8;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.hermes-log-content div {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
.hermes-log-content:empty::before {
|
||||
content: '等待日志...';
|
||||
color: var(--text-tertiary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* === Hermes Setup — Form === */
|
||||
.hermes-form {
|
||||
|
||||
Reference in New Issue
Block a user