diff --git a/src/engines/hermes/pages/chat.js b/src/engines/hermes/pages/chat.js index a3fcc84..946598e 100644 --- a/src/engines/hermes/pages/chat.js +++ b/src/engines/hermes/pages/chat.js @@ -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} ` : ``} @@ -732,6 +742,31 @@ export function render() { ` } + // 健康状态 banner:未装/未启动 → 在输入区上方显示一条警告 + 「去仪表盘」按钮。 + // 首次 fetch 完成前返回空字符串,避免首屏闪烁。 + function renderHealthBanner() { + if (hermesInstalled === null) return '' + if (hermesInstalled === false) { + return ` +
+ + ${escHtml(t('engine.chatHealthInstallMissing'))} + ${escHtml(t('engine.chatHealthGoDashboard'))} +
+ ` + } + if (!gwOnline) { + return ` +
+ + ${escHtml(t('engine.chatHealthGatewayDown'))} + ${escHtml(t('engine.chatHealthGoDashboard'))} +
+ ` + } + return '' + } + // ----------------------------------------------------------- draw function draw() { @@ -750,6 +785,7 @@ export function render() { ${renderSidebar()}
${renderHeader()} + ${renderHealthBanner()}
${renderMessages()}
diff --git a/src/engines/hermes/style/hermes.css b/src/engines/hermes/style/hermes.css index dd16b40..8ea93ff 100644 --- a/src/engines/hermes/style/hermes.css +++ b/src/engines/hermes/style/hermes.css @@ -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; diff --git a/src/locales/modules/engine.js b/src/locales/modules/engine.js index c85ae82..b2ae4e9 100644 --- a/src/locales/modules/engine.js +++ b/src/locales/modules/engine.js @@ -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 對話'),