feat(desktop): multi-transport hosts with relay fallback, card-style services dropdown
- A saved host now keeps every transport its pairing link carried: direct URL plus the relay descriptor, with one token for both (the mobile connection model). Switching tries the direct leg and falls back to the E2EE tunnel; list probes report Connected · Relay when only the tunnel reaches the host; relaunch restore picks direct first - Host switching trusts the dropdown's fresh probe instead of re-probing on click (no doubled latency, no transient Unreachable flashes); statuses are written once with the final outcome, survive the dropdown closing via a last-known cache, and an unprobed host reads Checking — never Unknown - Open-in-new-window works for relay hosts: a new IPC command boots the local UI with the host id injected and the renderer picks the transport; the app render holds on the relay restore so the splash shows instead of a transient auth screen (10s safety valve) - Relay host control socket gained protocol-level keepalive: a missed pong window terminates and reconnects, so the relay can no longer hold a ghost registration that leaves every client tunnel hanging; the desktop relay probe also hard-times-out at 8s instead of hanging status flows - Services dropdown restyled with mobile-style cards: per-provider usage cards, per-host instance cards with a selected highlight and a toned status line, MCP servers grouped in a card
This commit is contained in:
@@ -144,7 +144,10 @@ export const McpDropdownContent: React.FC<McpDropdownContentProps> = ({ active,
|
||||
</div>
|
||||
</div> : null}
|
||||
|
||||
<div className={cn('max-h-64 overflow-y-auto py-2', mobileListDensity && 'space-y-1 py-3', listClassName)}>
|
||||
{/* Desktop dropdown: servers grouped in one mobile-style card; the mobile
|
||||
sheet variant keeps its own density and chrome. */}
|
||||
<div className={cn('max-h-64 overflow-y-auto', mobileListDensity ? 'space-y-1 py-3' : 'px-3 py-2.5', listClassName)}>
|
||||
<div className={cn(!mobileListDensity && sortedNames.length > 0 && 'rounded-xl bg-[var(--surface-muted)] p-1.5')}>
|
||||
{sortedNames.map((serverName) => {
|
||||
const serverStatus = status[serverName];
|
||||
const tone = statusTone(serverStatus);
|
||||
@@ -157,7 +160,7 @@ export const McpDropdownContent: React.FC<McpDropdownContentProps> = ({ active,
|
||||
key={serverName}
|
||||
className={cn(
|
||||
'flex items-center justify-between rounded-lg hover:bg-interactive-hover/50',
|
||||
mobileListDensity ? 'gap-3 px-4 py-3' : 'gap-2 px-4 py-1.5',
|
||||
mobileListDensity ? 'gap-3 px-4 py-3' : 'gap-2 px-2.5 py-2',
|
||||
)}
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -212,6 +215,7 @@ export const McpDropdownContent: React.FC<McpDropdownContentProps> = ({ active,
|
||||
{t('mcpDropdown.empty.configureInConfig')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user