fix: sidebar menu not updating after engine switch + dashboard loading skeleton

1. engine-manager: activateEngine 切换时调用 engine.boot() 检测状态
2. main.js: onEngineChange 监听器重绑 state/ready 回调,确保新引擎
   状态变化时侧边栏自动刷新
3. Hermes 仪表盘: loading 期间显示骨架屏动画,消除空白等待
4. components.css: 新增 .skeleton-line 通用骨架行样式
This commit is contained in:
晴天
2026-04-13 11:08:55 +08:00
parent c02cb8e659
commit 549801e007
4 changed files with 63 additions and 6 deletions

View File

@@ -96,6 +96,33 @@ export function render() {
}
function draw() {
// 加载骨架屏
if (loading) {
el.innerHTML = `
<div class="page-header" style="display:flex;align-items:center;gap:12px">
<h1 style="margin:0">${t('engine.hermesDashboardTitle')}</h1>
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:20px">
${[1,2,3,4].map(() => `<div class="card"><div class="card-body" style="padding:16px">
<div class="skeleton-line" style="width:60%;height:12px;margin-bottom:10px"></div>
<div class="skeleton-line" style="width:80%;height:20px"></div>
</div></div>`).join('')}
</div>
<div class="card" style="margin-bottom:20px"><div class="card-body" style="padding:20px">
<div class="skeleton-line" style="width:40%;height:16px;margin-bottom:16px"></div>
<div style="display:flex;gap:6px;margin-bottom:14px">${[1,2,3,4].map(() => '<div class="skeleton-line" style="width:60px;height:24px;border-radius:12px"></div>').join('')}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
<div class="skeleton-line" style="height:36px"></div>
<div class="skeleton-line" style="height:36px"></div>
</div>
</div></div>
<div class="card" style="margin-bottom:20px"><div class="card-body" style="padding:16px">
<div class="skeleton-line" style="width:120px;height:32px;border-radius:6px"></div>
</div></div>
`
return
}
const gwRunning = info?.gatewayRunning
const port = info?.gatewayPort || 8642
const version = info?.version || '-'

View File

@@ -88,6 +88,13 @@ export async function activateEngine(id, persist = true) {
setDefaultRoute(engine.getDefaultRoute())
}
// 切换时启动新引擎(检测安装状态等),初始化由 main.js 处理
if (persist && engine.boot) {
try { await engine.boot() } catch (e) {
console.warn('[engine-manager] boot 失败:', e)
}
}
// 持久化到 clawpanel.json
if (persist) {
try {

View File

@@ -370,6 +370,28 @@ async function boot() {
}).catch(() => {})
: Promise.resolve()
// --- 引擎状态监听管理 ---
let _engineStateUnsub = null
let _engineReadyUnsub = null
function bindEngineListeners(engine) {
// 清理旧监听
if (_engineStateUnsub) { _engineStateUnsub(); _engineStateUnsub = null }
if (_engineReadyUnsub) { _engineReadyUnsub(); _engineReadyUnsub = null }
// 注册新监听
if (engine.onStateChange) {
_engineStateUnsub = engine.onStateChange(() => renderSidebar(sidebar))
}
if (engine.onReadyChange) {
_engineReadyUnsub = engine.onReadyChange(() => renderSidebar(sidebar))
}
}
// 引擎切换时:重新绑定状态监听 + 刷新侧边栏
onEngineChange((engine) => {
bindEngineListeners(engine)
renderSidebar(sidebar)
})
ensureWebSession.then(() => getActiveEngineId() === 'openclaw' ? loadActiveInstance() : Promise.resolve()).then(async () => {
const engine = getActiveEngine()
if (!engine) return
@@ -381,12 +403,7 @@ async function boot() {
renderSidebar(sidebar)
// 监听引擎状态变化(如 setup 完成后 ready 变为 true自动刷新侧边栏
if (engine.onStateChange) {
engine.onStateChange(() => renderSidebar(sidebar))
}
if (engine.onReadyChange) {
engine.onReadyChange(() => renderSidebar(sidebar))
}
bindEngineListeners(engine)
if (!engine.isReady()) {
setDefaultRoute(engine.getSetupRoute())

View File

@@ -31,6 +31,12 @@
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton-line {
background: linear-gradient(90deg, var(--bg-secondary) 25%, var(--bg-tertiary, var(--bg-card-hover)) 50%, var(--bg-secondary) 75%);
background-size: 200% 100%;
animation: skeleton-shine 1.4s ease infinite;
border-radius: 4px;
}
/* 卡片 */
.card {