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
Binary file added build/splash.bmp
Binary file not shown.
16 changes: 16 additions & 0 deletions docs/release-notes/v1.1.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Fixes the Windows app not opening

### Fixed
- **The app could fail to open on Windows, and then do nothing on every later launch.** If Windows reserved the app's local port (common with Hyper-V, WSL, or Docker), v1.1.0 never showed a window and kept running in the background. It now falls back to another port, and if it truly can't start it shows an error instead of hanging.
- **The engine didn't start in the desktop app.** The multi-threaded engine needs a browser feature that the desktop shell wasn't enabling. It's now turned on explicitly, so the full-strength, multi-core engine runs as intended.
- Launching the app while it's already open now brings up the existing window, and opens a new one if there isn't one.

### Improved
- The Windows portable `.exe` now shows a splash screen while it unpacks, so you can tell it's starting.

### If v1.1.0 wouldn't open for you
A hidden v1.1.0 process may still be running. Open Task Manager, end any **Chess Solver** processes (or run `taskkill /IM "Chess Solver.exe" /F`), then launch v1.1.1.

### Downloads
- **Windows:** `Chess.Solver.1.1.1.exe`, a portable build. SmartScreen may warn because it's unsigned: choose *More info → Run anyway*.
- **macOS:** `Chess.Solver-1.1.1-universal.dmg` (Intel + Apple Silicon). It isn't notarized, so right-click the app → **Open** on first launch.
37 changes: 31 additions & 6 deletions electron/main.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { app, BrowserWindow, shell } = require('electron');
const { app, BrowserWindow, dialog, shell } = require('electron');
const path = require('path');
const http = require('http');
const fs = require('fs');
Expand All @@ -20,7 +20,9 @@ const MIME_TYPES = {
// A fixed port keeps the app's origin stable between launches. localStorage
// (theme, line count, think time) and the HTTP cache are keyed by origin, so a
// random port silently reset settings every launch and forced a full
// recompile of the 100MB+ engine. Falls back to any free port if taken.
// recompile of the 100MB+ engine. Falls back to any free port if the
// preferred one can't be used — taken (EADDRINUSE) or, on Windows, inside a
// range reserved by Hyper-V/WSL/Docker (EACCES).
const PREFERRED_PORT = 47813;

// 'wasm-unsafe-eval' and blob: workers are required by the multi-threaded
Expand Down Expand Up @@ -108,8 +110,11 @@ function startServer() {
const server = http.createServer(handleRequest);
const listen = (port) => server.listen(port, '127.0.0.1');
server.once('listening', () => resolve(server.address().port));
let triedFallback = false;
server.on('error', (err) => {
if (err.code === 'EADDRINUSE' && server.address() === null) {
if (!triedFallback && server.address() === null) {
triedFallback = true;
console.warn(`[server] port ${PREFERRED_PORT} unavailable (${err.code}); using a random port`);
listen(0);
} else {
reject(err);
Expand All @@ -119,6 +124,13 @@ function startServer() {
});
}

// The multi-threaded Stockfish build needs SharedArrayBuffer. The server's
// COOP/COEP headers make browsers cross-origin isolate the page, but
// Electron's BrowserWindow doesn't reliably honor them (crossOriginIsolated
// stays false), which left SharedArrayBuffer undefined and the engine unable
// to start. Enable it explicitly; must run before the app is ready.
app.commandLine.appendSwitch('enable-features', 'SharedArrayBuffer');

let appOrigin = null;

function createWindow() {
Expand Down Expand Up @@ -170,16 +182,29 @@ if (!gotInstanceLock) {
const [win] = BrowserWindow.getAllWindows();
if (win) {
if (win.isMinimized()) win.restore();
win.show();
win.focus();
} else if (appOrigin !== null) {
createWindow();
}
});
}

app.whenReady().then(async () => {
if (!gotInstanceLock) return;
const port = await startServer();
appOrigin = `http://127.0.0.1:${port}`;
createWindow();
try {
const port = await startServer();
appOrigin = `http://127.0.0.1:${port}`;
createWindow();
} catch (err) {
// Never linger as a windowless background process: it would hold the
// single-instance lock and make every later launch silently do nothing.
dialog.showErrorBox(
'Chess Solver could not start',
`The app's local server failed to start:\n\n${err && err.message ? err.message : err}`
);
app.exit(1);
}
});

// macOS convention: the app stays alive with no windows and reopens one when
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "chess-solver",
"private": true,
"version": "1.1.0",
"version": "1.1.1",
"description": "Desktop chess analysis tool powered by Stockfish 18 — set up any position and get engine-recommended moves with evaluations, tactical motifs, and principal variations.",
"author": "ccharafeddine",
"license": "MIT",
Expand Down Expand Up @@ -37,6 +37,9 @@
"target": "portable",
"icon": "build/icon.ico"
},
"portable": {
"splashImage": "build/splash.bmp"
},
"mac": {
"target": [
{
Expand Down
15 changes: 15 additions & 0 deletions progress.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Chess Solver — Progress

## v1.1.1 (2026-09-24): Windows launch fix
- v1.1.0 .exe did nothing on launch. Two causes: (1) a listen error other
than EADDRINUSE on the fixed port (Windows returns EACCES for ports in
Hyper-V/WSL/Docker excluded ranges) rejected startServer unhandled, so no
window was created but the process lived on, holding the single-instance
lock and making every later launch exit silently. Now any listen error
falls back to a random port, and a total failure shows an error box and
exits. (2) Electron's BrowserWindow doesn't honor COOP/COEP
(crossOriginIsolated stays false), so SharedArrayBuffer was undefined and
the multi-threaded engine never started; fixed with
app.commandLine.appendSwitch('enable-features', 'SharedArrayBuffer').
Both verified in real Electron under Xvfb (engine runs 3 threads, ~1M
nps; port-taken fallback; second-instance handoff).
- Portable .exe shows build/splash.bmp while it unpacks (~320MB per launch).

## v1.1.0 (2026-09-24): engine speed/strength + reliability audit
- The app had been shipping the SINGLE-threaded Stockfish build
(public/stockfish.* byte-matched stockfish-18-single), so the Threads
Expand Down
Loading