feat(desktop): embeddable Gateway settings panel

Add an `embedded` flag to GatewaySettings (and a `bare` variant to
SettingsContent) that drops the page title/intro, Diagnostics row,
"Save for next restart", and the page gutters — so the same panel can be reused
inside a tighter surface without a second connection form to maintain. No change
to the standalone Settings → Gateway page (defaults off).
This commit is contained in:
Brooklyn Nicholson 2026-07-12 04:05:52 -04:00
parent e1fa54d367
commit f8152d2323
2 changed files with 47 additions and 33 deletions

View file

@ -110,7 +110,11 @@ function ScopeChip({ active, label, onSelect }: { active: boolean; label: string
)
}
export function GatewaySettings() {
// `embedded` trims the page chrome for reuse inside the boot-failure recovery
// card: the outer title/intro, the "Save for next restart" action, and the
// Diagnostics row are redundant there (the card owns its header + a single
// reconnect action), so only the connection controls render.
export function GatewaySettings({ embedded = false }: { embedded?: boolean } = {}) {
const { t } = useI18n()
const g = t.settings.gateway
const [loading, setLoading] = useState(true)
@ -707,17 +711,19 @@ export function GatewaySettings() {
}
return (
<SettingsContent>
<div className="mb-5">
<div className="flex items-center gap-2 text-[length:var(--conversation-text-font-size)] font-medium">
<Globe className="size-4 text-muted-foreground" />
{g.title}
{state.envOverride ? <Pill tone="primary">{g.envOverride}</Pill> : null}
<SettingsContent bare={embedded}>
{embedded ? null : (
<div className="mb-5">
<div className="flex items-center gap-2 text-[length:var(--conversation-text-font-size)] font-medium">
<Globe className="size-4 text-muted-foreground" />
{g.title}
{state.envOverride ? <Pill tone="primary">{g.envOverride}</Pill> : null}
</div>
<p className="mt-2 max-w-2xl text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">
{g.intro}
</p>
</div>
<p className="mt-2 max-w-2xl text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">
{g.intro}
</p>
</div>
)}
{namedProfiles.length > 0 ? (
<div className="mb-5 grid gap-2">
@ -1037,14 +1043,16 @@ export function GatewaySettings() {
{g.testRemote}
</Button>
) : null}
<Button
disabled={state.envOverride || saving}
onClick={() => void save(false)}
size="sm"
variant="textStrong"
>
{g.saveForRestart}
</Button>
{embedded ? null : (
<Button
disabled={state.envOverride || saving}
onClick={() => void save(false)}
size="sm"
variant="textStrong"
>
{g.saveForRestart}
</Button>
)}
<Button disabled={state.envOverride || saving} onClick={() => void save(true)} size="sm">
{saving ? <Loader2 className="animate-spin" /> : null}
{g.saveAndReconnect}
@ -1052,18 +1060,20 @@ export function GatewaySettings() {
</div>
) : null}
<div className="mt-6 grid gap-1">
<ListRow
action={
<Button onClick={() => void window.hermesDesktop?.revealLogs()} size="sm" variant="textStrong">
<FileText />
{g.openLogs}
</Button>
}
description={g.diagnosticsDesc}
title={g.diagnostics}
/>
</div>
{embedded ? null : (
<div className="mt-6 grid gap-1">
<ListRow
action={
<Button onClick={() => void window.hermesDesktop?.revealLogs()} size="sm" variant="textStrong">
<FileText />
{g.openLogs}
</Button>
}
description={g.diagnosticsDesc}
title={g.diagnostics}
/>
</div>
)}
</SettingsContent>
)
}

View file

@ -8,10 +8,14 @@ import { cn } from '@/lib/utils'
import { PAGE_INSET_X } from '../layout-constants'
export function SettingsContent({ children }: { children: ReactNode }) {
// `bare` drops the page gutters + tall bottom pad for embedding in a tighter
// surface (e.g. the boot-failure recovery card owns its own padding).
export function SettingsContent({ children, bare = false }: { children: ReactNode; bare?: boolean }) {
return (
<section className="min-h-0 overflow-hidden">
<div className={cn('h-full min-h-0 overflow-y-auto pb-20', PAGE_INSET_X)}>{children}</div>
<div className={cn('h-full min-h-0 overflow-y-auto', bare ? 'px-5 pb-6' : cn('pb-20', PAGE_INSET_X))}>
{children}
</div>
</section>
)
}