mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-08-25 02:00:47 +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.
|
* State lives in `chat-store.js`; this module only does DOM + events.
|
||||||
*/
|
*/
|
||||||
import { t } from '../../../lib/i18n.js'
|
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 { toast } from '../../../components/toast.js'
|
||||||
import { showConfirm } from '../../../components/modal.js'
|
import { showConfirm } from '../../../components/modal.js'
|
||||||
import { getChatStore, getSourceLabel } from '../lib/chat-store.js'
|
import { getChatStore, getSourceLabel } from '../lib/chat-store.js'
|
||||||
@@ -277,6 +277,8 @@ export function render() {
|
|||||||
let showSlash = false
|
let showSlash = false
|
||||||
let slashFilter = ''
|
let slashFilter = ''
|
||||||
let gwOnline = false
|
let gwOnline = false
|
||||||
|
// null = 仍在加载首次 check,先不显示 banner 防首屏闪烁
|
||||||
|
let hermesInstalled = null
|
||||||
let currentModel = ''
|
let currentModel = ''
|
||||||
const mobileQuery = window.matchMedia('(max-width: 720px)')
|
const mobileQuery = window.matchMedia('(max-width: 720px)')
|
||||||
|
|
||||||
@@ -305,11 +307,19 @@ export function render() {
|
|||||||
// --- initial session load + model meta ---
|
// --- initial session load + model meta ---
|
||||||
store.loadSessions().then(() => draw())
|
store.loadSessions().then(() => draw())
|
||||||
store.loadProfiles().then(() => draw()).catch(() => {})
|
store.loadProfiles().then(() => draw()).catch(() => {})
|
||||||
|
// 强制刷新安装/Gateway 状态缓存,避免用户刚在仪表盘启动 Gateway 后
|
||||||
|
// 进聊天页看到 30s 过期的「未启动」误判。
|
||||||
|
invalidate('check_hermes')
|
||||||
api.checkHermes().then(info => {
|
api.checkHermes().then(info => {
|
||||||
|
hermesInstalled = !!info?.installed
|
||||||
gwOnline = !!info?.gatewayRunning
|
gwOnline = !!info?.gatewayRunning
|
||||||
currentModel = info?.model || ''
|
currentModel = info?.model || ''
|
||||||
draw()
|
draw()
|
||||||
}).catch(() => {})
|
}).catch(() => {
|
||||||
|
hermesInstalled = false
|
||||||
|
gwOnline = false
|
||||||
|
draw()
|
||||||
|
})
|
||||||
|
|
||||||
// ----------------------------------------------------------- subscription
|
// ----------------------------------------------------------- subscription
|
||||||
|
|
||||||
@@ -676,8 +686,8 @@ export function render() {
|
|||||||
${ICONS.stop}
|
${ICONS.stop}
|
||||||
</button>`
|
</button>`
|
||||||
: `<button class="hm-chat-send-btn" id="hm-chat-send"
|
: `<button class="hm-chat-send-btn" id="hm-chat-send"
|
||||||
${!active || !inputValue.trim() ? 'disabled' : ''}
|
${(!active || !inputValue.trim() || hermesInstalled === false || !gwOnline) ? 'disabled' : ''}
|
||||||
title="${escHtml(t('engine.chatSend'))}">
|
title="${escHtml(hermesInstalled === false ? t('engine.chatHealthInstallMissing') : !gwOnline ? t('engine.chatHealthGatewayDown') : t('engine.chatSend'))}">
|
||||||
${ICONS.send}
|
${ICONS.send}
|
||||||
</button>`}
|
</button>`}
|
||||||
</div>
|
</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
|
// ----------------------------------------------------------- draw
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
@@ -750,6 +785,7 @@ export function render() {
|
|||||||
${renderSidebar()}
|
${renderSidebar()}
|
||||||
<section class="hm-chat-main">
|
<section class="hm-chat-main">
|
||||||
${renderHeader()}
|
${renderHeader()}
|
||||||
|
${renderHealthBanner()}
|
||||||
<div class="hm-chat-messages" id="hm-chat-messages">
|
<div class="hm-chat-messages" id="hm-chat-messages">
|
||||||
${renderMessages()}
|
${renderMessages()}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4577,6 +4577,50 @@ body[data-active-engine="hermes"][data-theme="dark"] {
|
|||||||
margin-left: 2px;
|
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 ---- */
|
/* ---- Messages area ---- */
|
||||||
[data-engine="hermes"] .hm-chat-messages {
|
[data-engine="hermes"] .hm-chat-messages {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -293,6 +293,18 @@ export default {
|
|||||||
// Short labels for the header pill — full sentence lives in the tooltip
|
// Short labels for the header pill — full sentence lives in the tooltip
|
||||||
chatGatewayOfflineShort: _('离线', 'Offline', '離線'),
|
chatGatewayOfflineShort: _('离线', 'Offline', '離線'),
|
||||||
chatGatewayOnlineShort: _('在线', 'Online', '線上'),
|
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,有什麼可以幫你的?'),
|
chatWelcome: _('你好!我是 Hermes Agent,有什么可以帮你的?', 'Hello! I\'m Hermes Agent, how can I help?', '你好!我是 Hermes Agent,有什麼可以幫你的?'),
|
||||||
chatEmptyHint: _('开始一段对话吧', 'Start a conversation', '開始一段對話吧'),
|
chatEmptyHint: _('开始一段对话吧', 'Start a conversation', '開始一段對話吧'),
|
||||||
chatEmptyTitle: _('和 Hermes Agent 对话', 'Talk to Hermes Agent', '和 Hermes Agent 對話'),
|
chatEmptyTitle: _('和 Hermes Agent 对话', 'Talk to Hermes Agent', '和 Hermes Agent 對話'),
|
||||||
|
|||||||
Reference in New Issue
Block a user