diff --git a/projects/keepkey-vault/src/mainview/App.tsx b/projects/keepkey-vault/src/mainview/App.tsx index 7353923e..1b6228bb 100644 --- a/projects/keepkey-vault/src/mainview/App.tsx +++ b/projects/keepkey-vault/src/mainview/App.tsx @@ -966,6 +966,15 @@ function App() { onEnableEmulator={async () => { const settings = await rpcRequest('setEmulatorEnabled', { enabled: true }, 10000) setEmulatorEnabled(settings.emulatorEnabled) + // Enabling the flag alone is a dead end on first run: with no flash + // images DeviceGrid has nothing to show and the Start card vanishes. + // Bootstrap and boot a default emulator wallet so "Start emulator" + // actually starts one (mirrors the DeviceSettingsDrawer install path). + const wallets = await rpcRequest>('emulatorListWallets').catch(() => []) + if (wallets.length === 0) { + try { await rpcRequest('emulatorPair', undefined, 10000) } catch { /* may already be paired */ } + await rpcRequest('emulatorInit', { flashName: 'default' }, 30000) + } }} emulatorEnabled={emulatorEnabled} /> diff --git a/projects/keepkey-vault/src/mainview/components/DeviceGrid.tsx b/projects/keepkey-vault/src/mainview/components/DeviceGrid.tsx index d23815d4..a8a9f331 100644 --- a/projects/keepkey-vault/src/mainview/components/DeviceGrid.tsx +++ b/projects/keepkey-vault/src/mainview/components/DeviceGrid.tsx @@ -164,6 +164,38 @@ export function DeviceGrid({ onViewPortfolio, onReady, onEnableEmulator, emulato const emuRunning = emuStatus?.state === "running" const hasContent = devices.length > 0 || emuWallets.length > 0 + // Emulator entry point. Rendered identically in the empty-state hero AND the + // populated workspace so a Start-emulator affordance is ALWAYS reachable — + // registered devices (hasContent) must not hide it. Parity with macOS, where + // the emulator flow is always offered regardless of what's already in the DB. + const emulatorStartCard = (!emulatorEnabled && onEnableEmulator) ? ( + + + + + + Testing with an emulator? + + + Open the local emulator wallet picker without connecting a USB device. + + + + + + Start emulator + + + + ) : null + useEffect(() => { // Always notify parent once revealed — even when empty — so SplashScreen // can slide the logo to the top and reveal the connect-prompt hero. @@ -206,6 +238,14 @@ export function DeviceGrid({ onViewPortfolio, onReady, onEnableEmulator, emulato + {/* Emulator entry point in the populated workspace — always offered, + never hidden by registered devices (parity with the empty state). */} + {hasContent && emulatorStartCard && ( + + {emulatorStartCard} + + )} + {/* Error banner */} {error && ( @@ -249,33 +289,7 @@ export function DeviceGrid({ onViewPortfolio, onReady, onEnableEmulator, emulato - {!emulatorEnabled && onEnableEmulator && ( - - - - - - Testing with an emulator? - - - Open the local emulator wallet picker without connecting a USB device. - - - - - - Start emulator - - - - )} + {emulatorStartCard} )}