Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/desktop/electron/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ if (REMOTE_DISPLAY_REASON) {
)
}

ipcMain.handle('hermes:get-remote-display-reason', () => REMOTE_DISPLAY_REASON)

// Keep the renderer running at full speed while the window is in the background
// or occluded. The chat transcript streams to screen through a
// requestAnimationFrame-gated flush; Chromium pauses rAF (and clamps timers)
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/electron/preload.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ contextBridge.exposeInMainWorld('hermesDesktop', {
return () => ipcRenderer.removeListener('hermes:bootstrap:event', listener)
},
getVersion: () => ipcRenderer.invoke('hermes:version'),
getRemoteDisplayReason: () => ipcRenderer.invoke('hermes:get-remote-display-reason'),
uninstall: {
summary: () => ipcRenderer.invoke('hermes:uninstall:summary'),
run: mode => ipcRenderer.invoke('hermes:uninstall:run', { mode })
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/app/desktop-controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DesktopInstallOverlay } from '@/components/desktop-install-overlay'
import { DesktopOnboardingOverlay } from '@/components/desktop-onboarding-overlay'
import { GatewayConnectingOverlay } from '@/components/gateway-connecting-overlay'
import { Pane, PaneMain } from '@/components/pane-shell'
import { RemoteDisplayBanner } from '@/components/remote-display-banner'
import { useMediaQuery } from '@/hooks/use-media-query'
import { useSkinCommand } from '@/themes/use-skin-command'

Expand Down Expand Up @@ -956,6 +957,7 @@ export function DesktopController() {

const overlays = (
<>
<RemoteDisplayBanner />
{!isSecondaryWindow() && <DesktopInstallOverlay />}
{!isSecondaryWindow() && (
<DesktopOnboardingOverlay
Expand Down
42 changes: 42 additions & 0 deletions apps/desktop/src/components/remote-display-banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect, useState } from 'react'

import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Codicon } from '@/components/ui/codicon'
import { useI18n } from '@/i18n'
import { Info } from '@/lib/icons'

export function RemoteDisplayBanner() {
const { t } = useI18n()
const [reason, setReason] = useState<string | null>(null)
const [dismissed, setDismissed] = useState(false)

useEffect(() => {
void window.hermesDesktop?.getRemoteDisplayReason?.().then(result => setReason(result))
}, [])

if (!reason || dismissed) {
return null
}

return (
<div className="pointer-events-none fixed left-1/2 top-[calc(var(--titlebar-height,34px)+0.75rem)] z-[200] w-[min(32rem,calc(100%-2rem))] -translate-x-1/2">
<Alert className="pointer-events-auto grid-cols-[auto_minmax(0,1fr)_auto] border-(--stroke-nous) bg-popover/95 pr-2.5 shadow-nous backdrop-blur-md">
<Info className="text-muted-foreground" />
<AlertDescription className="col-start-2">
<p className="m-0">{t.remoteDisplayBanner.message(reason)}</p>
</AlertDescription>
<Button
aria-label={t.remoteDisplayBanner.dismiss}
className="col-start-3 -mr-1 text-muted-foreground"
onClick={() => setDismissed(true)}
size="icon-xs"
type="button"
variant="ghost"
>
<Codicon name="close" size="0.875rem" />
</Button>
</Alert>
</div>
)
}
1 change: 1 addition & 0 deletions apps/desktop/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ declare global {
cancelBootstrap: () => Promise<{ ok: boolean; cancelled: boolean }>
onBootstrapEvent: (callback: (payload: DesktopBootstrapEvent) => void) => () => void
getVersion: () => Promise<DesktopVersionInfo>
getRemoteDisplayReason?: () => Promise<string | null>
updates: {
check: () => Promise<DesktopUpdateStatus>
apply: (opts?: DesktopUpdateApplyOptions) => Promise<DesktopUpdateApplyResult>
Expand Down
6 changes: 6 additions & 0 deletions apps/desktop/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ export const en: Translations = {
}
},

remoteDisplayBanner: {
message: reason =>
`Software rendering active — remote display detected (${reason}). GPU acceleration is disabled to prevent flickering.`,
dismiss: 'Dismiss'
},

titlebar: {
hideSidebar: 'Hide sidebar',
showSidebar: 'Show sidebar',
Expand Down
6 changes: 6 additions & 0 deletions apps/desktop/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ export const ja = defineLocale({
}
},

remoteDisplayBanner: {
message: reason =>
`ソフトウェアレンダリングが有効です — リモートディスプレイを検出しました(${reason})。ちらつきを防ぐため GPU アクセラレーションは無効化されています。`,
dismiss: '閉じる'
},

titlebar: {
hideSidebar: 'サイドバーを非表示',
showSidebar: 'サイドバーを表示',
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ export interface Translations {
}
}

remoteDisplayBanner: {
message: (reason: string) => string
dismiss: string
}

titlebar: {
hideSidebar: string
showSidebar: string
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/i18n/zh-hant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ export const zhHant = defineLocale({
}
},

remoteDisplayBanner: {
message: reason => `軟體繪圖已啟用 — 偵測到遠端顯示(${reason})。為防止畫面閃爍,已停用 GPU 加速。`,
dismiss: '關閉'
},

titlebar: {
hideSidebar: '隱藏側邊欄',
showSidebar: '顯示側邊欄',
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ export const zh: Translations = {
}
},

remoteDisplayBanner: {
message: reason => `软件渲染已启用 — 检测到远程显示(${reason})。为防止画面闪烁,已禁用 GPU 加速。`,
dismiss: '关闭'
},

titlebar: {
hideSidebar: '隐藏侧边栏',
showSidebar: '显示侧边栏',
Expand Down
Loading