mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Fix Electron stuck on splash in hwibridge mode (#2600)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
496f3f5d79
commit
50d81434a8
6 changed files with 158 additions and 17 deletions
91
.github/workflows/electron-smoketest.yml
vendored
Normal file
91
.github/workflows/electron-smoketest.yml
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
name: Electron smoketest
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'pyinstaller/electron/**'
|
||||
- '.github/workflows/electron-smoketest.yml'
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'pyinstaller/electron/**'
|
||||
- '.github/workflows/electron-smoketest.yml'
|
||||
|
||||
jobs:
|
||||
smoketest:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: pyinstaller/electron/package-lock.json
|
||||
|
||||
- name: Install xvfb and electron runtime libs
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
xvfb \
|
||||
libgtk-3-0 \
|
||||
libnss3 \
|
||||
libasound2t64 \
|
||||
libgbm1 \
|
||||
libxss1 \
|
||||
libxtst6
|
||||
|
||||
- name: Install electron deps
|
||||
working-directory: pyinstaller/electron
|
||||
run: npm ci
|
||||
|
||||
- name: Syntax check all electron JS
|
||||
working-directory: pyinstaller/electron
|
||||
run: |
|
||||
for f in main.js src/*.js; do
|
||||
echo "checking $f"
|
||||
node --check "$f"
|
||||
done
|
||||
|
||||
- name: Seed dev data folder
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/specter_dev"
|
||||
echo '{"version":"","sha256":{"x64":"","arm64":""}}' \
|
||||
> "$RUNNER_TEMP/specter_dev/version-data.json"
|
||||
|
||||
- name: Boot electron, assert splash reaches no-specterd branch
|
||||
working-directory: pyinstaller/electron
|
||||
env:
|
||||
NODE_ENV: development
|
||||
SPECTER_DATA_FOLDER: ${{ runner.temp }}/specter_dev
|
||||
run: |
|
||||
set -u
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 1280x960x24' \
|
||||
./node_modules/.bin/electron . --no-sandbox > electron.log 2>&1 &
|
||||
PID=$!
|
||||
trap 'kill $PID 2>/dev/null || true; wait $PID 2>/dev/null || true' EXIT
|
||||
|
||||
# Expected log line emitted by uiHelpers.js:updatingLoaderMsg when
|
||||
# main.js reaches the "specterd missing + no version" branch.
|
||||
# If main.js changes this message, update both sides together.
|
||||
EXPECTED="Specterd was not found and no version is configured"
|
||||
|
||||
for i in $(seq 1 30); do
|
||||
if grep -qF "$EXPECTED" electron.log; then
|
||||
echo "smoketest passed (splash reached no-specterd branch)"
|
||||
exit 0
|
||||
fi
|
||||
if ! kill -0 $PID 2>/dev/null; then
|
||||
echo "electron exited before reaching expected state"
|
||||
echo "--- electron.log ---"
|
||||
cat electron.log
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "expected log line not seen within 30s"
|
||||
echo "--- electron.log ---"
|
||||
cat electron.log
|
||||
exit 1
|
||||
|
|
@ -160,7 +160,9 @@ app.whenReady().then(() => {
|
|||
downloadSpecterd(specterdPath)
|
||||
} else {
|
||||
updatingLoaderMsg(
|
||||
'Specterd file could not be validated and no version is configured in the settings<br>Please go to Preferences and set version to fetch or add an executable manually...'
|
||||
'Specterd file could not be validated and no version is configured in the settings<br>Please go to Preferences and set version to fetch or add an executable manually...',
|
||||
false,
|
||||
{ isHtml: true }
|
||||
)
|
||||
updateSpecterdStatus('Failed to locate specterd...')
|
||||
}
|
||||
|
|
@ -169,8 +171,13 @@ app.whenReady().then(() => {
|
|||
if (appSettings.specterdVersion) {
|
||||
downloadSpecterd(specterdPath)
|
||||
} else {
|
||||
// NB: smoketest workflow (.github/workflows/electron-smoketest.yml) greps
|
||||
// for the leading "Specterd was not found and no version is configured"
|
||||
// substring to assert splash reached this branch. Keep both in sync.
|
||||
updatingLoaderMsg(
|
||||
'Specterd was not found and no version is configured in the settings<br>Please go to Preferences and set version to fetch or add an executable manually...'
|
||||
'Specterd was not found and no version is configured in the settings<br>Please go to Preferences and set version to fetch or add an executable manually...',
|
||||
false,
|
||||
{ isHtml: true }
|
||||
)
|
||||
updateSpecterdStatus('Failed to locate specterd...')
|
||||
}
|
||||
|
|
@ -200,6 +207,15 @@ app.on('before-quit', (event) => {
|
|||
}
|
||||
})
|
||||
|
||||
ipcMain.on('open-settings', (event) => {
|
||||
const senderUrl = event.senderFrame?.url || ''
|
||||
if (!senderUrl.startsWith('file://') || !senderUrl.endsWith('/splash.html')) {
|
||||
logger.warn(`Rejected open-settings IPC from untrusted sender: ${senderUrl}`)
|
||||
return
|
||||
}
|
||||
openPreferences()
|
||||
})
|
||||
|
||||
ipcMain.on('request-mainprocess-action', (event, arg) => {
|
||||
switch (arg.message) {
|
||||
case 'save-preferences':
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
// All of the Node.js APIs are available in the preload process.
|
||||
// It has the same sandbox as a Chrome extension.
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
|
||||
const updateSpinner = (show) => {
|
||||
const spinnerElement = document.getElementById('spinner');
|
||||
if (spinnerElement) {
|
||||
|
|
@ -19,10 +17,23 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||
window.api.receive('update-loader-message', (data) => {
|
||||
const launchTextElement = document.getElementById('launch-text');
|
||||
if (launchTextElement) {
|
||||
launchTextElement.textContent = data.msg;
|
||||
if (data.isHtml) {
|
||||
launchTextElement.innerHTML = data.msg;
|
||||
} else {
|
||||
launchTextElement.textContent = data.msg;
|
||||
}
|
||||
updateSpinner(data.showSpinner);
|
||||
}
|
||||
const settingsBtn = document.getElementById('open-settings-btn');
|
||||
if (settingsBtn) {
|
||||
settingsBtn.classList.toggle('hidden', !data.showSettingsButton);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
const openSettingsBtn = document.getElementById('open-settings-btn');
|
||||
if (openSettingsBtn) {
|
||||
openSettingsBtn.addEventListener('click', () => {
|
||||
window.api.send('open-settings');
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
@ -29,11 +29,13 @@ function checkSpecterd(logs, specterdStarted) {
|
|||
}
|
||||
|
||||
let specterIsRunning = false
|
||||
let currentSpecterURL = 'http://localhost:25441'
|
||||
function startSpecterd(specterdPath, automaticWalletImport = false) {
|
||||
if (platformName == 'win64') {
|
||||
specterdPath += '.exe'
|
||||
}
|
||||
let hwiBridgeMode = appSettings.mode == 'hwibridge'
|
||||
currentSpecterURL = hwiBridgeMode ? appSettings.specterURL : 'http://localhost:25441'
|
||||
updatingLoaderMsg('Launching Specter ...', (showSpinner = 'true'))
|
||||
updateSpecterdStatus('Launching Specter ...')
|
||||
let specterdArgs = ['server']
|
||||
|
|
@ -85,7 +87,7 @@ function startSpecterd(specterdPath, automaticWalletImport = false) {
|
|||
}, 3000)
|
||||
} else {
|
||||
logger.info('Normal startup of Specter.')
|
||||
createWindow(appSettings.specterURL)
|
||||
createWindow(currentSpecterURL)
|
||||
}
|
||||
} else if (serverdStatus === 'timeout') {
|
||||
showError('Specter does not seem to start. Check the logs in the menu for more details.')
|
||||
|
|
@ -113,7 +115,7 @@ function startSpecterd(specterdPath, automaticWalletImport = false) {
|
|||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow(appSettings.specterURL)
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow(currentSpecterURL)
|
||||
})
|
||||
// since these are streams, you can pipe them elsewhere
|
||||
specterdProcess.on('close', (code) => {
|
||||
|
|
@ -167,7 +169,7 @@ app.on('open-url', (_, url) => {
|
|||
// Only proceed with the import if the importFromWalletSoftwareBtn can be found.
|
||||
// If it is not, users are redirected by specterd to the configure connection screen.
|
||||
function importWallet(walletData) {
|
||||
loadUrl(appSettings.specterURL + '/wallets/new_wallet/')
|
||||
loadUrl(currentSpecterURL + '/wallets/new_wallet/')
|
||||
let code = `
|
||||
const importFromWalletSoftwareBtn = document.getElementById('import-from-wallet-software-btn')
|
||||
if (importFromWalletSoftwareBtn) {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@
|
|||
<link rel="stylesheet" type="text/css" href="./typography.css">
|
||||
<link rel="stylesheet" type="text/css" href="./output.css">
|
||||
<body class="bg-dark-900">
|
||||
<div width="100%" height="100%" class="flex space-x-2 items-center justify-center h-screen">
|
||||
<div width="100%" height="100%" class="flex flex-col space-y-4 items-center justify-center h-screen">
|
||||
<div class="flex space-x-2 items-center">
|
||||
<svg id="spinner" class="text-white rounded-full bg-dark-700 w-4 h-4 animate-spin hidden" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M52.8359,43.6067l1.30463e-08,-1.88843e-08c-0.312506,0.452349 -0.932543,0.565714 -1.38489,0.253208c-0.0990138,-0.0684038 -0.184804,-0.154194 -0.253208,-0.253208l-6.9888,-9.9742l-5.91546e-08,-8.31834e-08c-0.328807,-0.46237 -0.220533,-1.10375 0.241837,-1.43255c0.168889,-0.120103 0.370155,-0.186318 0.577363,-0.189948h4.9835l3.54852e-07,0.000503677c-0.00686545,-9.94223 -8.0722,-17.9964 -18.0144,-17.9896c-4.13508,0.00285541 -8.14312,1.42919 -11.3504,4.03924l4.60883e-08,-3.91242e-08c-0.842285,0.715013 -2.10472,0.611839 -2.81974,-0.230446c-0.715013,-0.842285 -0.611839,-2.10472 0.230446,-2.81974l3.46098e-07,-2.83814e-07c9.39634,-7.70537 23.26,-6.33457 30.9654,3.06177c3.22304,3.93035 4.98589,8.85536 4.98903,13.9382h4.9935l2.44822e-09,4.33431e-11c0.567328,0.0100438 1.0191,0.478096 1.00905,1.04542c-0.0036664,0.207098 -0.0698445,0.408252 -0.189853,0.577075Zm-40.0129,-23.1717l6.9888,9.9742l-2.71216e-09,-3.81325e-09c0.32881,0.4623 0.220594,1.10362 -0.241706,1.43243c-0.168809,0.120065 -0.369975,0.186285 -0.577094,0.189969h-4.983l2.60913e-07,0.000383721c0.0068661,9.94206 8.07207,17.9961 18.0141,17.9893c4.1354,-0.00285595 8.14372,-1.42943 11.3511,-4.03989l-7.47965e-08,6.34738e-08c0.8424,-0.714877 2.10482,-0.6115 2.8197,0.2309c0.714877,0.8424 0.6115,2.10482 -0.2309,2.8197l4.04125e-07,-3.31403e-07c-9.39617,7.70531 -23.2597,6.33461 -30.965,-3.06156c-3.22311,-3.93039 -4.98597,-8.85549 -4.98903,-13.9384h-4.9947l5.45126e-08,9.68797e-10c-0.567328,-0.0100825 -1.01906,-0.478166 -1.00898,-1.04549c0.00367938,-0.207033 0.0698308,-0.408123 0.189782,-0.576906l6.9888,-9.9742l-3.21808e-08,4.66031e-08c0.312407,-0.452417 0.93242,-0.565917 1.38484,-0.25351c0.0990287,0.0683822 0.184838,0.154154 0.253263,0.253153Z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
<p id="launch-text" class="text-lg">Launching Specter Desktop...</p>
|
||||
<p id="launch-text" class="text-lg">Launching Specter Desktop...</p>
|
||||
</div>
|
||||
<button id="open-settings-btn" class="hidden px-4 py-2 bg-dark-700 text-white rounded hover:bg-dark-600 cursor-pointer">Open Settings</button>
|
||||
</div>
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -20,16 +20,27 @@ const executeJavaScript = (code) => {
|
|||
mainWindow.webContents.executeJavaScript(code)
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
function showError(error) {
|
||||
updatingLoaderMsg('Specter encountered an error:' + error.toString())
|
||||
}
|
||||
|
||||
function updatingLoaderMsg(msg, showSpinner = false) {
|
||||
function updatingLoaderMsg(msg, showSpinner = false, { isHtml = false, showSettingsButton = false } = {}) {
|
||||
if (mainWindow) {
|
||||
// see preload.js where this is setup
|
||||
mainWindow.webContents.send('update-loader-message', {
|
||||
msg,
|
||||
showSpinner,
|
||||
isHtml,
|
||||
showSettingsButton,
|
||||
})
|
||||
} else {
|
||||
logger.error('mainWindow not initialized in updatingLoaderMsg')
|
||||
|
|
@ -78,11 +89,18 @@ function initMainWindow(dimensions) {
|
|||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
mainWindow.webContents.on('did-fail-load', function () {
|
||||
mainWindow.webContents.on('did-fail-load', function (event, errorCode, errorDescription, validatedURL) {
|
||||
mainWindow.loadURL(`file://${__dirname}/splash.html`)
|
||||
updatingLoaderMsg(
|
||||
`Failed to load: ${appSettings.specterURL}<br>Please make sure the URL is entered correctly in the settings and try again...</b>`
|
||||
)
|
||||
const failedUrl = escapeHtml(validatedURL || appSettings.specterURL || '')
|
||||
let msg
|
||||
if (appSettings.mode === 'hwibridge') {
|
||||
msg = `Failed to load remote Specter at: ${failedUrl}<br><br>` +
|
||||
`You are running in <b>HWI Bridge mode</b>, which requires a remote Specter server.<br>` +
|
||||
`If you want to run Specter locally instead, open <b>Settings</b> (${isMac ? 'Cmd' : 'Ctrl'}+, or via the tray icon) and switch to "Run Specter locally".`
|
||||
} else {
|
||||
msg = `Failed to load: ${failedUrl}<br>Please make sure the URL is entered correctly in the settings and try again...`
|
||||
}
|
||||
updatingLoaderMsg(msg, false, { isHtml: true, showSettingsButton: true })
|
||||
})
|
||||
return mainWindow
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue