mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-30 04:40:18 +08:00
feat(hermes-chat): show readiness banner when Hermes is missing or Gateway is down (#256)
- Surface a top-of-input warning banner on the chat page when: * Hermes Agent is not installed -> red 'go to dashboard' banner; * Gateway is not running -> amber banner with the same dashboard link. - Disable the send button (with a contextual tooltip) so users no longer hit cryptic backend errors when their environment is not ready. - Force-refresh the cached check_hermes status on chat mount so a freshly started Gateway is reflected immediately instead of waiting 30s. - Add zh-CN/en/zh-TW copy + matching error/warn styling that reuses the existing Hermes color tokens.
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
* State lives in `chat-store.js`; this module only does DOM + events.
|
||||
*/
|
||||
import { t } from '../../../lib/i18n.js'
|
||||
import { api } from '../../../lib/tauri-api.js'
|
||||
import { api, invalidate } from '../../../lib/tauri-api.js'
|
||||
import { toast } from '../../../components/toast.js'
|
||||
import { showConfirm } from '../../../components/modal.js'
|
||||
import { getChatStore, getSourceLabel } from '../lib/chat-store.js'
|
||||
@@ -277,6 +277,8 @@ export function render() {
|
||||
let showSlash = false
|
||||
let slashFilter = ''
|
||||
let gwOnline = false
|
||||
// null = 仍在加载首次 check,先不显示 banner 防首屏闪烁
|
||||
let hermesInstalled = null
|
||||
let currentModel = ''
|
||||
const mobileQuery = window.matchMedia('(max-width: 720px)')
|
||||
|
||||
@@ -305,11 +307,19 @@ export function render() {
|
||||
// --- initial session load + model meta ---
|
||||
store.loadSessions().then(() => draw())
|
||||
store.loadProfiles().then(() => draw()).catch(() => {})
|
||||
// 强制刷新安装/Gateway 状态缓存,避免用户刚在仪表盘启动 Gateway 后
|
||||
// 进聊天页看到 30s 过期的「未启动」误判。
|
||||
invalidate('check_hermes')
|
||||
api.checkHermes().then(info => {
|
||||
hermesInstalled = !!info?.installed
|
||||
gwOnline = !!info?.gatewayRunning
|
||||
currentModel = info?.model || ''
|
||||
draw()
|
||||
}).catch(() => {})
|
||||
}).catch(() => {
|
||||
hermesInstalled = false
|
||||
gwOnline = false
|
||||
draw()
|
||||
})
|
||||
|
||||
// ----------------------------------------------------------- subscription
|
||||
|
||||
@@ -676,8 +686,8 @@ export function render() {
|
||||
${ICONS.stop}
|
||||
</button>`
|
||||
: `<button class="hm-chat-send-btn" id="hm-chat-send"
|
||||
${!active || !inputValue.trim() ? 'disabled' : ''}
|
||||
title="${escHtml(t('engine.chatSend'))}">
|
||||
${(!active || !inputValue.trim() || hermesInstalled === false || !gwOnline) ? 'disabled' : ''}
|
||||
title="${escHtml(hermesInstalled === false ? t('engine.chatHealthInstallMissing') : !gwOnline ? t('engine.chatHealthGatewayDown') : t('engine.chatSend'))}">
|
||||
${ICONS.send}
|
||||
</button>`}
|
||||
</div>
|
||||
@@ -732,6 +742,31 @@ export function render() {
|
||||
`
|
||||
}
|
||||
|
||||
// 健康状态 banner:未装/未启动 → 在输入区上方显示一条警告 + 「去仪表盘」按钮。
|
||||
// 首次 fetch 完成前返回空字符串,避免首屏闪烁。
|
||||
function renderHealthBanner() {
|
||||
if (hermesInstalled === null) return ''
|
||||
if (hermesInstalled === false) {
|
||||
return `
|
||||
<div class="hm-chat-health-banner is-error">
|
||||
<span class="hm-chat-health-icon" aria-hidden="true">⚠</span>
|
||||
<span class="hm-chat-health-msg">${escHtml(t('engine.chatHealthInstallMissing'))}</span>
|
||||
<a class="hm-chat-health-action" href="#/h/dashboard">${escHtml(t('engine.chatHealthGoDashboard'))}</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
if (!gwOnline) {
|
||||
return `
|
||||
<div class="hm-chat-health-banner is-warn">
|
||||
<span class="hm-chat-health-icon" aria-hidden="true">⚠</span>
|
||||
<span class="hm-chat-health-msg">${escHtml(t('engine.chatHealthGatewayDown'))}</span>
|
||||
<a class="hm-chat-health-action" href="#/h/dashboard">${escHtml(t('engine.chatHealthGoDashboard'))}</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------- draw
|
||||
|
||||
function draw() {
|
||||
@@ -750,6 +785,7 @@ export function render() {
|
||||
${renderSidebar()}
|
||||
<section class="hm-chat-main">
|
||||
${renderHeader()}
|
||||
${renderHealthBanner()}
|
||||
<div class="hm-chat-messages" id="hm-chat-messages">
|
||||
${renderMessages()}
|
||||
</div>
|
||||
|
||||
@@ -4577,6 +4577,50 @@ body[data-active-engine="hermes"][data-theme="dark"] {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* ---- Health banner (install / gateway readiness) ---- */
|
||||
[data-engine="hermes"] .hm-chat-health-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 28px;
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid var(--hm-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
[data-engine="hermes"] .hm-chat-health-banner.is-error {
|
||||
background: var(--hm-error-soft);
|
||||
color: var(--hm-error);
|
||||
}
|
||||
[data-engine="hermes"] .hm-chat-health-banner.is-warn {
|
||||
background: var(--hm-warn-soft);
|
||||
color: var(--hm-warn);
|
||||
}
|
||||
[data-engine="hermes"] .hm-chat-health-icon {
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
[data-engine="hermes"] .hm-chat-health-msg {
|
||||
flex: 1;
|
||||
line-height: 1.5;
|
||||
}
|
||||
[data-engine="hermes"] .hm-chat-health-action {
|
||||
flex-shrink: 0;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
border: 1px solid currentColor;
|
||||
background: transparent;
|
||||
transition: background 0.16s ease, color 0.16s ease;
|
||||
}
|
||||
[data-engine="hermes"] .hm-chat-health-action:hover {
|
||||
background: currentColor;
|
||||
color: var(--hm-surface-0);
|
||||
}
|
||||
|
||||
/* ---- Messages area ---- */
|
||||
[data-engine="hermes"] .hm-chat-messages {
|
||||
flex: 1;
|
||||
|
||||
@@ -293,6 +293,18 @@ export default {
|
||||
// Short labels for the header pill — full sentence lives in the tooltip
|
||||
chatGatewayOfflineShort: _('离线', 'Offline', '離線'),
|
||||
chatGatewayOnlineShort: _('在线', 'Online', '線上'),
|
||||
// 健康检查 banner(聊天页顶部)
|
||||
chatHealthInstallMissing: _(
|
||||
'未检测到 Hermes Agent 安装。请在仪表盘完成安装后再开始对话。',
|
||||
'Hermes Agent is not installed. Please complete installation on the dashboard before chatting.',
|
||||
'未偵測到 Hermes Agent 安裝。請在儀表板完成安裝後再開始對話。'
|
||||
),
|
||||
chatHealthGatewayDown: _(
|
||||
'Hermes Gateway 未启动,无法发送消息。请在仪表盘启动 Gateway。',
|
||||
'Hermes Gateway is not running, messages cannot be sent. Please start the Gateway on the dashboard.',
|
||||
'Hermes Gateway 未啟動,無法發送訊息。請在儀表板啟動 Gateway。'
|
||||
),
|
||||
chatHealthGoDashboard: _('去仪表盘', 'Open Dashboard', '去儀表板'),
|
||||
chatWelcome: _('你好!我是 Hermes Agent,有什么可以帮你的?', 'Hello! I\'m Hermes Agent, how can I help?', '你好!我是 Hermes Agent,有什麼可以幫你的?'),
|
||||
chatEmptyHint: _('开始一段对话吧', 'Start a conversation', '開始一段對話吧'),
|
||||
chatEmptyTitle: _('和 Hermes Agent 对话', 'Talk to Hermes Agent', '和 Hermes Agent 對話'),
|
||||
|
||||
Reference in New Issue
Block a user