diff --git a/src/ElectronNET.AspNet/Bridge/SignalRFacade.cs b/src/ElectronNET.AspNet/Bridge/SignalRFacade.cs index 48b16bd..185fc31 100644 --- a/src/ElectronNET.AspNet/Bridge/SignalRFacade.cs +++ b/src/ElectronNET.AspNet/Bridge/SignalRFacade.cs @@ -11,6 +11,13 @@ namespace ElectronNET.API /// /// SignalR-based facade that mimics the SocketIoFacade interface /// for compatibility with existing Electron API code. + /// + /// Key implementation details: + /// - Uses IHubContext to send events to Electron via 'event' hub method + /// - Receives events from Electron via ElectronHub.ElectronEvent() method + /// - Includes ConvertToType<T> helper to handle JsonElement and numeric type conversions + /// - Event args are passed as arrays to match SignalR serialization behavior + /// - Connection ID is set by ElectronHub when Electron client connects /// internal class SignalRFacade : IFacade { diff --git a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs index 0291573..619b3af 100644 --- a/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs +++ b/src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs @@ -14,6 +14,14 @@ namespace ElectronNET.AspNet.Runtime using Microsoft.AspNetCore.SignalR; using ElectronNET.AspNet.Hubs; + /// + /// Runtime controller for SignalR-based .NET-first startup mode. + /// Key differences from Socket.IO mode: + /// - Waits for ASP.NET server to start, then captures the dynamic port + /// - Launches Electron with the actual URL (no port scanning needed) + /// - Uses SignalRFacade instead of SocketIOFacade for bidirectional communication + /// - Waits for 'electron-host-ready' signal to ensure API modules are loaded before calling app callback + /// internal class RuntimeControllerAspNetDotnetFirstSignalR : RuntimeControllerAspNetBase { private ElectronProcessBase electronProcess; diff --git a/src/ElectronNET.Host/api/signalr-bridge.js b/src/ElectronNET.Host/api/signalr-bridge.js index b4d7053..fa9fdf0 100644 --- a/src/ElectronNET.Host/api/signalr-bridge.js +++ b/src/ElectronNET.Host/api/signalr-bridge.js @@ -1,4 +1,14 @@ -// SignalR connection module for Electron.NET +/** + * SignalR connection module for Electron.NET + * + * This module provides a Socket.IO-compatible interface for SignalR communication. + * Key features: + * - Mimics Socket.IO's on() and emit() methods for compatibility with existing API modules + * - Handles event registration and propagation between Electron and .NET + * - Event args are always passed as arrays to match C# ElectronEvent(string, object[]) signature + * - Spreads args when calling handlers to match Socket.IO behavior + * - Supports automatic reconnection with configurable logging level + */ const signalR = require('@microsoft/signalr'); // Safe console wrapper that catches EPIPE errors diff --git a/src/ElectronNET.Host/main.js b/src/ElectronNET.Host/main.js index 515a731..3b7b1a7 100644 --- a/src/ElectronNET.Host/main.js +++ b/src/ElectronNET.Host/main.js @@ -171,6 +171,8 @@ app.on('ready', async () => { } // Check if we're using SignalR-based startup + // SignalR mode is activated by --unpackeddotnetsignalr or --dotnetpackedsignalr flags + // .NET passes the actual server URL via --electronurl parameter (no port scanning needed) if (unpackeddotnetsignalr || dotnetpackedsignalr) { if (!electronUrl) { console.error('[Electron] ERROR: SignalR mode requires --electronUrl parameter'); @@ -179,6 +181,7 @@ app.on('ready', async () => { } // Create a temporary invisible window to keep Electron alive during startup. + // Without any windows, Electron would quit immediately on macOS. // This will be destroyed once the first real window is created. const { BrowserWindow } = require('electron'); const keepAliveWindow = new BrowserWindow({ @@ -420,6 +423,18 @@ function startSocketApiBridge(port) { }); } +/** + * Starts the SignalR API bridge for .NET-first SignalR mode. + * + * Flow: + * 1. Connect to SignalR hub at /electron-hub endpoint + * 2. Register as Electron client + * 3. Load all API modules (same modules as Socket.IO mode) + * 4. Signal 'electron-host-ready' to .NET to trigger app ready callback + * + * This ensures .NET doesn't call the app ready callback until all API modules + * are loaded and ready to handle requests from .NET code. + */ async function startSignalRApiBridge(baseUrl) { const { SignalRBridge } = require('./api/signalr-bridge'); const hubUrl = `${baseUrl}/electron-hub`;