mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-29 04:10:00 +08:00
fix(ui): hide useless docker manager on desktop, auto-start hermes dashboard, polish layout
Four independent UI fixes the user spotted in one screenshot tour:
services.js — desktop docker manager
ClawPanel is not a docker management tool. The "Docker 多实例管理"
block on the OpenClaw services page only makes sense for users who
deployed ClawPanel itself in Web mode (serve.js / dev-api) and want
to orchestrate multiple OpenClaw containers from one panel.
On desktop Tauri this block always degrades to either "未启用" with
a connect ENOENT error (no docker daemon on the user box) or to a
generic "unavailable" placeholder — pure visual noise. Skip the
whole config-section in render() and bail out of loadDockerManager()
when isTauriRuntime() is true. Web mode keeps the feature.
profiles.js — Hermes Profile manager could not load
Profile API only exists on the Hermes Dashboard process at 9119,
which the user has to start by hand. When it is offline, the page
showed a raw "由于目标计算机积极拒绝, 无法连接 (10061)" error which
is useless: users do not know they need to start a separate process.
load() now does the same probe → auto-start dance that
extensions.js / dashboard.js use for their 9119 links: probe first,
call hermesDashboardStart() if not running, only then issue the
/api/profiles request. If the start itself fails, we fall through to
the original catch and humanize-error renders a real reason.
lazy-deps.js — "[object Object]" on load failure
humanizeError() returns { message, hint, raw, action? }, not a
string. The catch branch passed the object straight into
escapeHtml(), so String(obj) coerced to "[object Object]". Switch
to humanizeErrorText() which is exactly the (message + hint)
one-liner string variant.
layout.css — header buttons crammed against the description
Several pages (hermes profiles / lazy-deps / files / gateways /
group-chat / kanban / oauth) put their header buttons inside
<div class="config-actions"> nested in <div class="page-header">,
but the existing flex layout rule only matched .page-actions. The
buttons therefore stacked directly under .page-desc with zero
visible gap. Add .config-actions to both the desktop flex selector
and the @media (max-width:768px) column-stack selector so all
these pages get the same title-left / actions-right layout.
## Verification
- npm run build (no warnings beyond the existing chunk size note)
- Playwright on /services in browser mode: docker section present;
same page after mocking window.__TAURI_INTERNALS__: section gone
- Playwright on /lazy-deps: rendered content does not contain the
string "[object Object]"
- Playwright dynamic-imports profiles / lazy-deps / files render():
computed style on .page-header is display:flex, flex-direction:row,
title and button share the same getBoundingClientRect().top, button
pushed to the right edge (justify-content:space-between effective)
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
import { t } from '../../../lib/i18n.js'
|
||||
import { api } from '../../../lib/tauri-api.js'
|
||||
import { toast } from '../../../components/toast.js'
|
||||
import { humanizeError } from '../../../lib/humanize-error.js'
|
||||
import { humanizeError, humanizeErrorText } from '../../../lib/humanize-error.js'
|
||||
import { svgIcon } from '../lib/svg-icons.js'
|
||||
|
||||
// feature 分类配置(决定分组顺序 + 图标 + 文案)
|
||||
@@ -72,7 +72,9 @@ async function loadAndRender(page) {
|
||||
try {
|
||||
featuresResp = await api.hermesLazyDepsFeatures()
|
||||
} catch (e) {
|
||||
content.innerHTML = `<div style="color:var(--error);padding:20px">${escapeHtml(humanizeError(e, t('hermesLazyDeps.loadFailed')))}</div>`
|
||||
// humanizeError 返回 { message, hint, raw } 对象,String(obj) 会变成 "[object Object]"。
|
||||
// 这里用 humanizeErrorText 直接拿格式化后的字符串。
|
||||
content.innerHTML = `<div style="color:var(--error);padding:20px">${escapeHtml(humanizeErrorText(e, t('hermesLazyDeps.loadFailed')))}</div>`
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,15 @@ export function render() {
|
||||
loading = true
|
||||
error = ''
|
||||
draw()
|
||||
// 9119 Dashboard 是独立进程,profile/* API 只由它提供。
|
||||
// 先 probe + 自动启动,避免用户看到「网络连接失败」这种无头错误。
|
||||
// 启动失败也不在这里中断,下面 hermesDashboardApi 抛出的连接错误会由 humanizeError 显示。
|
||||
try {
|
||||
const probe = await api.hermesDashboardProbe()
|
||||
if (!probe?.running) {
|
||||
await api.hermesDashboardStart().catch(() => {})
|
||||
}
|
||||
} catch { /* probe 失败也继续尝试调用 */ }
|
||||
try {
|
||||
const resp = await api.hermesDashboardApi('GET', '/api/profiles')
|
||||
const list = Array.isArray(resp) ? resp : (resp?.profiles || [])
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 服务管理页面
|
||||
* 服务启停 + 更新检测 + 配置备份管理
|
||||
*/
|
||||
import { api } from '../lib/tauri-api.js'
|
||||
import { api, isTauriRuntime } from '../lib/tauri-api.js'
|
||||
import { toast } from '../components/toast.js'
|
||||
import { humanizeError } from '../lib/humanize-error.js'
|
||||
import { showConfirm, showModal, showUpgradeModal } from '../components/modal.js'
|
||||
@@ -34,11 +34,12 @@ export async function render() {
|
||||
</div>
|
||||
<div id="version-bar"><div class="stat-card loading-placeholder" style="height:80px;margin-bottom:var(--space-lg)"></div></div>
|
||||
<div id="services-list"><div class="stat-card loading-placeholder" style="height:64px"></div></div>
|
||||
${isTauriRuntime() ? '' : `
|
||||
<div class="config-section" id="docker-manager-section">
|
||||
<div class="config-section-title">${t('services.dockerManager')}</div>
|
||||
<div class="form-hint" style="margin-bottom:var(--space-sm)">${t('services.dockerManagerHint')}</div>
|
||||
<div id="docker-manager-bar"><div class="stat-card loading-placeholder" style="height:96px"></div></div>
|
||||
</div>
|
||||
</div>`}
|
||||
<div class="config-section" id="config-editor-section" style="display:none">
|
||||
<div class="config-section-title">${t('services.configEditor')}</div>
|
||||
<div class="form-hint" style="margin-bottom:var(--space-sm)">${t('services.configEditorHint')}</div>
|
||||
@@ -194,6 +195,9 @@ async function hasDockerManagerBackend() {
|
||||
}
|
||||
|
||||
async function loadDockerManager(page) {
|
||||
// Docker 多实例管理仅在 Web 部署模式(serve.js / dev-api)下有意义。
|
||||
// 桌面 Tauri 用户不需要管理多个 OpenClaw 容器,整段 UI 已在 render() 里跳过渲染。
|
||||
if (isTauriRuntime()) return
|
||||
const bar = page.querySelector('#docker-manager-bar')
|
||||
if (!bar) return
|
||||
const backendReady = await hasDockerManagerBackend()
|
||||
|
||||
@@ -539,7 +539,8 @@
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.page-header:has(.page-actions) {
|
||||
.page-header:has(.page-actions),
|
||||
.page-header:has(.config-actions) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
@@ -849,7 +850,8 @@
|
||||
.page-title {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
.page-header:has(.page-actions) {
|
||||
.page-header:has(.page-actions),
|
||||
.page-header:has(.config-actions) {
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user