Compare commits

...

232 Commits

Author SHA1 Message Date
Florian Rappl
931aec8cd9 Updated lodash 2026-02-02 16:15:19 +01:00
Florian Rappl
bbd1065a05 Merge branch 'epsitec-improvements' into feature/secure-connection 2026-02-02 16:13:18 +01:00
Florian Rappl
0fa0abd093 Merge branch 'develop' of https://github.com/ElectronNET/Electron.NET into feature/secure-connection 2026-02-02 16:01:30 +01:00
Pierre Arnaud
bfd51e64f7 Fix dead-lock on shutdown 2026-01-31 15:37:52 +01:00
Pierre Arnaud
bb4337a31c Cosmetic 2026-01-31 15:37:35 +01:00
Pierre Arnaud
57753eb632 Replace Console.WriteLine with ILogger for startup time
Changed startup time measurement to use structured logging via ILogger
instead of Console.WriteLine. This provides:
- Consistent logging format with other app messages
- Proper log level (Information)
- Structured data (ElapsedMilliseconds as named parameter)
- Better integration with logging infrastructure

Output now appears as:
info: Electron.Startup[0]
      App startup time until Electron launch: 1284 ms
2026-01-31 14:37:32 +01:00
Pierre Arnaud
614673605a Add explicit BrowserRefresh logging filter
Added 'Microsoft.AspNetCore.Watch.BrowserRefresh' to Warning level
to suppress the 'Middleware loaded' and 'Script injected' debug messages
that appear when running with dotnet watch or Ctrl+F5 from Visual Studio.
2026-01-31 14:30:07 +01:00
Pierre Arnaud
52744a1922 Final cleanup: Suppress remaining debug output
- Removed '|| ' prefix from ProcessRunner stdout/stderr redirection
  * Changed to Debug.WriteLine (only visible when debugger attached)
- Suppressed [Startup] timing messages in StartupManager
  * Moved to Debug.WriteLine for diagnostic purposes
- Suppressed 'No testhost detected' messages
- Added logging configuration to suppress ASP.NET Watch debug messages
  * Microsoft.AspNetCore.Watch set to Warning level

Result: Clean console output in development with only meaningful messages.
Timing and debug traces still available when debugger is attached.
2026-01-31 14:27:34 +01:00
Pierre Arnaud
03da5cd7cb Phase 4: Replace Console output with Debug.WriteLine in diagnostic code
- Replaced Console.WriteLine with System.Diagnostics.Debug.WriteLine in:
  * LaunchOrderDetector (probe scoring)
  * UnpackagedDetector (probe scoring)
  * ElectronProcessActive (StartInternal traces)
- Removed '[StartInternal]: after run:' trace
- Debug.WriteLine only outputs when debugger is attached

These were the noisiest debugging traces. Other Console.WriteLine usage
in StartupManager, ProcessRunner, etc. would require ILogger injection
which is a larger architectural change deferred for now.
2026-01-31 11:19:28 +01:00
Pierre Arnaud
805d942b11 Phase 3: Configure SignalR logging to reduce verbosity
- Updated SafeLogger to support minimum log level threshold
- Configure SignalR log level based on environment:
  * Debug mode: Info level (connection events only)
  * Development/Production: Warning level (errors/warnings only)
- Eliminates verbose packet-level logging
- Connection lifecycle events still visible in debug mode

This dramatically reduces console noise while preserving important events.
2026-01-31 11:17:26 +01:00
Pierre Arnaud
29b1f088ce Phase 2: Clean up debugging traces and use environment-aware logging
- Removed 'Entry!!!:' debugging leftover from main.js
- Replaced all console.log/warn/error with logger methods
- Preserved console.time/timeEnd for performance measurements
- Updated browserWindows.js and webContents.js to use logger
- Changed debug traces to DEBUG level (won't show in production)
- Changed info messages to INFO level (show in development)
- All errors use ERROR level (always shown)

Socket.IO connection messages now respect log levels based on environment.
2026-01-31 11:16:11 +01:00
Pierre Arnaud
4a62103749 Phase 1: Add environment-aware logging infrastructure
- Created logger.js with log levels (DEBUG, INFO, WARN, ERROR)
- Implemented environment detection (debug/development/production)
- Auto-detect based on flags, NODE_ENV, and debugger
- Preserve console.time/timeEnd for performance measurements
- Updated SignalR bridge to use new logger system
- Replaced safeConsole calls with environment-aware logger

Default log levels:
- Debug mode: DEBUG and above
- Development: INFO and above
- Production: WARN and above
2026-01-31 11:09:25 +01:00
Pierre Arnaud
38ee6fabb4 Only enable SignalR detailed errors in development
Changed EnableDetailedErrors to be conditional based on:
- Debugger attached (DebuggerHelper.IsAttached)
- context.HostingEnvironment.IsDevelopment()

Updated ConfigureServices to accept WebHostBuilderContext to properly
access the hosting environment instead of directly reading environment
variables. This prevents detailed error messages from being exposed in
production builds, which is important for security.
2026-01-31 10:04:29 +01:00
Pierre Arnaud
0ee2bbe31c Refactor debugger detection to use shared DebuggerHelper
Created a new DebuggerHelper class with Lazy<bool> initialization that is
shared by both LaunchOrderDetector and UnpackagedDetector. This eliminates
code duplication and provides a cleaner, more maintainable solution.

- Added DebuggerHelper.cs with lazy-initialized IsAttached property
- Refactored LaunchOrderDetector to use DebuggerHelper
- Refactored UnpackagedDetector to use DebuggerHelper
- Removed nullable bool pattern in favor of Lazy<bool>
2026-01-31 09:38:26 +01:00
Pierre Arnaud
a88e10bbf2 Measure total startup time 2026-01-31 09:18:24 +01:00
Pierre Arnaud
39c7e61ae5 Auto-quit Electron when pipe to .NET process breaks
When an EPIPE error is detected (indicating the .NET process has terminated),
Electron now automatically quits gracefully instead of just suppressing the error.
This ensures the Electron window doesn't remain open after the backend dies.

Uses setImmediate to allow any pending operations to complete before quitting,
and includes a flag to prevent multiple quit attempts.
2026-01-31 09:17:09 +01:00
Pierre Arnaud
96c454aedb Add process-level EPIPE error handlers
The previous fix using try-catch wasn't sufficient because EPIPE errors
are thrown asynchronously at the socket level when writing to stdout/stderr.
Added error event handlers to process.stdout and process.stderr to catch
and suppress EPIPE errors at the source, preventing the error dialog entirely.
2026-01-31 09:10:48 +01:00
Pierre Arnaud
5a77284610 Fix EPIPE error when .NET process terminates before Electron
- Added SafeLogger class to wrap SignalR logging and prevent EPIPE errors
- Enhanced safeConsole wrapper to include warn method
- Replaced all console calls in SignalRBridge with safeConsole
- Configured SignalR to use custom SafeLogger instead of default ConsoleLogger

This prevents the 'broken pipe, write' error dialog when the .NET process
is killed before the Electron process can cleanly shut down.
2026-01-31 09:04:58 +01:00
Pierre Arnaud
0a23659196 Phase 1b: Optimize startup detection for faster .NET initialization
Performance optimizations to startup detection:
- Reduced GatherBuildInfo: >1000ms → 7ms (99% faster!)
- Reduced CollectProcessData: →  86ms
- Reduced DetectAppTypeAndStartup: → 91ms
- Total StartupManager.Initialize: → 91ms (down from >1 second)

Electron startup also improved significantly:
- Module loading: 719ms → 108ms (85% faster!)
- SignalR connection: 884ms → 403ms (54% faster!)
- Total Electron startup: 2.3s → 700ms (70% faster!)

Optimizations applied:

1. Directory checks (UnpackagedDetector):
   - Changed GetDirectories().Any() to direct Directory.Exists()
   - Avoids expensive directory enumeration
   - Saves ~200-500ms on slow disks

2. Debugger state caching:
   - Cache Debugger.IsAttached result in static field
   - Avoids multiple expensive debugger checks
   - Used by both UnpackagedDetector and LaunchOrderDetector
   - Saves ~50-100ms per check

3. Added timing measurements:
   - Added Stopwatch to StartupManager.Initialize()
   - Detailed timing for each initialization phase
   - Visibility into startup performance

Results:
- .NET startup detection: >1000ms → 91ms
- Electron module loading: 719ms → 108ms
- SignalR connection: 884ms → 403ms
- **Total improvement: ~70% faster startup**

The combination of faster directory checks, cached debugger state,
and parallel module loading (from previous commit) results in
sub-second startup times in development mode.
2026-01-30 23:02:13 +01:00
Pierre Arnaud
5d224568d0 Phase 1: Implement parallel module loading for faster startup
Performance optimization:
- Refactored 18 sequential require() calls to load in parallel
- Use Promise.all() to load all API modules simultaneously
- Added comprehensive startup timing measurements

Timing breakdown (before → after):
- Module loading: ~900-1200ms → 719ms (20-40% faster)
- Total Electron startup: Measured at 2.3 seconds
- SignalR connection: 884ms
- Host ready signal: 33ms

Implementation:
- Load all modules in parallel using Promise.resolve().then()
- Organize modules by priority (critical, secondary, utility)
- Maintain backward compatibility with global variable assignments
- Add console.time() measurements for each phase

Benefits:
- Faster perceived startup time
- Non-blocking module initialization
- Clear visibility into startup bottlenecks
- Foundation for future lazy loading optimizations

This is the first phase of startup optimization, targeting the
biggest bottleneck (sequential module loading) with the least effort.
2026-01-30 22:56:37 +01:00
Pierre Arnaud
1c0b9378d2 Phase 6: Add comprehensive authentication documentation
Created new authentication guide:
- docs/SignalR-Authentication-Guide.md (500+ lines)
- Complete threat model and security architecture
- Flow diagrams and implementation details
- Troubleshooting guide with common issues
- FAQ covering design decisions and usage

Updated SignalR implementation summary:
- Added authentication & security section
- Documented token flow and cookie management
- Security properties and protection scope
- Logging & monitoring guidelines
- Multi-user testing procedures
- Updated file changes summary and success metrics

Documentation includes:
- Architecture diagrams (ASCII art)
- Code examples for all components
- Step-by-step authentication flow
- Security considerations and rationale
- Common troubleshooting scenarios
- Testing recommendations
2026-01-30 22:39:45 +01:00
Pierre Arnaud
c12a706289 Phase 5.2: Add comprehensive logging and error handling for authentication
Middleware logging:
- Log successful authentication with cookie setting
- Log failed authentication attempts with path and remote IP
- Log token prefix (first 8 chars) for invalid tokens, never full token
- Added structured logging with ILogger<T>

Electron error handling:
- Detect 401 authentication errors in SignalR connection
- Provide helpful error message about --authtoken parameter
- Differentiate auth errors from other connection failures

Documentation:
- Added XML comments explaining security model
- Documented token generation rationale (128-bit entropy)
- Clarified middleware validation flow in comments

Security:
- Never log full token values
- Generic error messages to prevent information leakage
- Failed auth attempts logged for security monitoring
2026-01-30 22:37:37 +01:00
Pierre Arnaud
8cc3fe4fd7 Phase 5.1: Fix quit handler to support both Socket.IO and SignalR modes
- Added checks for undefined variables before cleanup
- Socket.IO resources (server, apiProcess, io) only cleaned up in legacy mode
- SignalR connection properly stopped in SignalR mode
- Improved error messages to identify which cleanup failed
- Prevents 'Cannot read properties of undefined' error on app quit
2026-01-30 22:36:18 +01:00
Pierre Arnaud
893de1510d Phase 3: Pass authentication token to SignalR connection URL
- Modified main.js to pass authToken to SignalRBridge constructor
- Updated signalr-bridge.js to append token to hub URL
- Removed skip logic for negotiate endpoint in middleware
- SignalR negotiate request now includes token for authentication
- Cookie is set after successful negotiate for subsequent requests

This enables SignalR connections to authenticate using the same
token-based mechanism as HTTP requests, completing the authentication
flow for both Blazor pages and SignalR hub communication.
2026-01-30 22:35:39 +01:00
Pierre Arnaud
6f49a663ea Phase 4: Inject authentication service into RuntimeController and set token 2026-01-30 19:20:03 +01:00
Pierre Arnaud
5b9e2b8b3b Phase 2.1: Register authentication services and middleware in Program.cs 2026-01-30 19:19:23 +01:00
Pierre Arnaud
dee640c526 Phase 1.2: Extract token in Electron and append to URL
- Extract authtoken from command-line in main.js

- Store token in global.authToken for access by API modules

- Append token to initial window URL as query parameter

- Update both JS and TS versions of browserWindows

- First request will be: http://localhost:PORT/?token=GUID
2026-01-30 19:14:09 +01:00
Pierre Arnaud
f598fbf5ce Phase 1.1: Generate authentication token in RuntimeController
- Add authenticationToken field to store GUID

- Generate secure token using Guid.NewGuid().ToString('N')

- Pass token to Electron via --authtoken command-line parameter

- Token is 32 hex characters with 128 bits of entropy
2026-01-30 19:13:12 +01:00
Pierre Arnaud
6847520ea8 Remove HSTS and HTTPS redirection for Electron apps
HSTS and HTTPS redirection are designed for public web servers, not
desktop applications:

- ASP.NET Core only listens on http://localhost (local-only)
- No man-in-the-middle risk for same-machine communication
- HTTPS would require certificate setup with no security benefit
- HTTPS overhead slows down local IPC unnecessarily

Electron apps should use plain HTTP for localhost communication.
2026-01-30 17:30:07 +01:00
Pierre Arnaud
75151282ff Remove unnecessary UseWebSockets() call
SignalR's MapHub<T>() automatically enables WebSocket support, making
explicit UseWebSockets() redundant. SignalR also supports fallback
transports (Server-Sent Events, Long Polling) if WebSockets are unavailable.

Updated documentation to reflect this and clarify that WebSockets are
enabled automatically by MapHub().
2026-01-30 17:28:40 +01:00
Pierre Arnaud
17ef6853ab Fix ASP0014 warning: Use top-level route registration for ElectronHub
Changed from app.UseEndpoints() pattern to direct app.MapHub() call,
following modern ASP.NET Core conventions. This removes the analyzer
warning while maintaining the same functionality.
2026-01-30 17:14:14 +01:00
Pierre Arnaud
12f011bc33 Add comprehensive SignalR implementation documentation
Created detailed summary document incorporating:
- All 6 implementation phases (complete)
- Usage examples with code snippets
- Architecture decisions and rationale
- Critical fixes and their solutions
- Blazor Server integration considerations
- Backward compatibility guarantees
- Known limitations and future enhancements
- Success metrics and validation

Document serves as both implementation reference and user guide.
2026-01-30 17:11:57 +01:00
Pierre Arnaud
217fe83334 Add documentation comments to SignalR implementation
Added comprehensive code comments explaining:
- RuntimeControllerAspNetDotnetFirstSignalR: .NET-first startup flow and key differences from Socket.IO
- SignalRFacade: Type conversion handling and event propagation details
- signalr-bridge.js: Socket.IO compatibility layer and arg handling
- main.js: Keep-alive window pattern and SignalR startup sequence

Comments focus on explaining WHY decisions were made, not just WHAT the code does.
2026-01-30 17:08:37 +01:00
Pierre Arnaud
1fc881674d Clean up debug logging from SignalR implementation
Remove excessive console logging that was added during debugging:
- Removed verbose logging from Program.cs (app ready callback steps)
- Removed HTTP request logging middleware
- Cleaned up RuntimeControllerAspNetDotnetFirstSignalR lifecycle logging
- Streamlined ElectronHub connection/event logging
- Simplified SignalRFacade event handling logging
- Reduced JavaScript logging in main.js and signalr-bridge.js
- Reset log levels to Warning for SignalR components in appsettings

Kept only essential error logging and critical state transitions.
Production-ready logging levels maintained.
2026-01-30 17:07:24 +01:00
Pierre Arnaud
6e369aabef Fix static files and CSS asset reference for SignalR sample
- Fix middleware order: UseAntiforgery must be between UseRouting and UseEndpoints
- Add UseStaticFiles() to serve wwwroot content
- Fix scoped CSS bundle reference: use lowercase 'electronnet-samples-blazorsignalr.styles.css' to match generated asset name
- Add HTTP request logging for debugging
- Enable detailed logging for routing and static files in development
2026-01-30 16:58:29 +01:00
Pierre Arnaud
06a332827b Fix window shutdown and URL port for SignalR mode
- Destroy keep-alive window when first real window is created (enables proper window-all-closed behavior)
- Update ElectronNetRuntime.AspNetWebPort with actual port after Kestrel starts (fixes http://localhost:0 issue)
2026-01-30 16:43:53 +01:00
Pierre Arnaud
23f79244ae Fix SignalR event args spreading for Electron handlers
- Changed event handler to receive args as single array parameter
- Spread array elements as individual arguments to match Socket.IO behavior
2026-01-30 16:40:20 +01:00
Pierre Arnaud
6b9187cf6e Fix SignalR bridge communication issues
- Fix duplicate SignalR connection in main.js (removed redundant connect block)
- Fix race condition: Electron now signals 'electron-host-ready' after loading API modules
- Fix type conversion in SignalRFacade for event handlers (handle JsonElement and numeric types)
- Fix SignalR argument mismatch: pass args as array instead of spread, use object[] instead of params
- .NET now waits for electron-host-ready before calling app ready callback
2026-01-30 16:36:27 +01:00
Pierre Arnaud
108ef19a3b CRITICAL FIX: Prevent Electron from quitting in SignalR mode
ROOT CAUSE: Electron quits when app.on('ready') completes with 0 windows.
In SignalR mode, no windows are created immediately, so Electron exits,
triggering ElectronProcess_Stopped and shutting down ASP.NET.

SOLUTION: Create an invisible 1x1 keep-alive window in SignalR mode to
prevent Electron from quitting while waiting for SignalR connection.

Also:
- Make app.on('ready') async and await startSignalRApiBridge()
- Add window-all-closed handler for SignalR mode
- Add extensive debug logging to track lifecycle
- Don't subscribe to electronProcess.Ready in SignalR controller

This fixes the premature shutdown that prevented SignalR connection.
2026-01-30 15:29:48 +01:00
Pierre Arnaud
8c6020e35b WIP: Debugging SignalR connection timeout
- Add WebSockets middleware to ASP.NET pipeline
- Move HTTPS redirect to production only
- Add extensive debug logging in startSignalRApiBridge
- Enable Warning level logging in SignalR client

Issue: signalRBridge.connect() hangs and never resolves
- Connection starts but never completes
- No error messages from SignalR client
- ASP.NET shuts down after timeout
- ElectronHub.OnConnectedAsync never called

Next: Investigate why WebSocket connection doesn't establish
2026-01-30 13:27:03 +01:00
Pierre Arnaud
547e9f1196 Fix: Add safe console wrapper to prevent EPIPE errors
- Wrap all console.log/error calls in try-catch to handle EPIPE
- Disable SignalR client logging (LogLevel.None)
- Prevents Electron crash when console pipes are closed

This fixes the 'EPIPE: broken pipe, write' error that was preventing
the Electron window from displaying.
2026-01-30 13:22:35 +01:00
Pierre Arnaud
4b971af119 Fix: Execute app ready callback in SignalR mode
- Add RunReadyCallback execution when SignalR connects
- This triggers window creation and other app initialization
- Fixes missing variables in main.js (desktopCapturer, electronHostHook, touchBar, shellApi)
- Window now opens successfully in SignalR mode

Known issue: EPIPE console error when logging to closed pipe (to be fixed)
2026-01-30 13:19:49 +01:00
Pierre Arnaud
da8216b292 Implement bidirectional event routing for SignalR mode
- Update signalr-bridge.js to handle .NET→Electron events via 'event' channel
- Add socket.io-compatible .on() and .emit() methods to SignalRBridge
- Update main.js to load all Electron API modules with SignalR bridge
- Update SignalRFacade.Emit() to send events via 'event' channel
- Add ElectronHub.ElectronEvent() to receive Electron→.NET events
- Add SignalRFacade.TriggerEvent() to invoke .NET event handlers
- Remove duplicate ElectronEvent method from hub

This enables full bidirectional communication:
- .NET can call Electron APIs via Emit (e.g., createBrowserWindow)
- Electron can send events back to .NET (e.g., BrowserWindowCreated)
- Event handlers registered via On/Once now work with SignalR
2026-01-30 13:09:55 +01:00
Pierre Arnaud
be609a513e Refactor: Introduce IFacade interface for SocketIO and SignalR facades
- Create IFacade interface defining common API for SocketIO and SignalR
- Update SocketIoFacade to implement IFacade
- Update SignalRFacade to implement IFacade (add Connect no-op)
- Update RuntimeControllerBase.Socket to return IFacade
- Update RuntimeControllerAspNetBase.Socket to return IFacade
- Update RuntimeControllerDotNetFirst.Socket to return IFacade
- Update ElectronNetRuntime.GetSocket() to return IFacade
- Update BridgeConnector.Socket to return IFacade

This enables polymorphic usage of both facades throughout the codebase
and prepares for full Electron API integration with SignalR mode.
2026-01-30 13:03:19 +01:00
Pierre Arnaud
c4a8de6c4e Fix psl dist folder issue by copying from source after npm install 2026-01-30 12:52:40 +01:00
Pierre Arnaud
5b3d5e07ee Add @microsoft/signalr to package.template.json for npm install during build 2026-01-30 12:46:57 +01:00
Pierre Arnaud
9135aff855 Fix electronurl parameter case sensitivity for Electron command line 2026-01-30 12:39:32 +01:00
Pierre Arnaud
e9efb26dff Fix main.js to check SignalR flags first before legacy mode flags 2026-01-30 12:37:12 +01:00
Pierre Arnaud
e29a3bc27a Fix IsUnpackaged extension to include UnpackedDotnetFirstSignalR 2026-01-30 12:34:42 +01:00
Pierre Arnaud
f55abb357c Fix Electron launch: subscribe to ASP.NET Ready event in SignalR controller 2026-01-30 12:26:17 +01:00
Pierre Arnaud
0b92336de2 Fix dynamic port binding: use 127.0.0.1 instead of localhost for port 0 2026-01-30 12:22:07 +01:00
Pierre Arnaud
4c17027039 Add BlazorSignalR sample to solution file 2026-01-30 12:20:08 +01:00
Pierre Arnaud
5d04ab686a Add ElectronNET.Samples.BlazorSignalR - complete sample app for SignalR mode 2026-01-30 12:17:20 +01:00
Pierre Arnaud
de0c02c503 Add comprehensive documentation for SignalR-based startup mode 2026-01-30 12:11:48 +01:00
Pierre Arnaud
054f5b1c4c Complete Phase 5: Add SignalR startup detection and port 0 configuration 2026-01-30 12:10:49 +01:00
Pierre Arnaud
04ec52208a Fix compilation errors - Phase 4 complete (basic structure) 2026-01-30 12:08:43 +01:00
Pierre Arnaud
268b9c90ce Update RuntimeControllerAspNetDotnetFirstSignalR to use SignalRFacade 2026-01-30 12:06:29 +01:00
Pierre Arnaud
cb7d721b7d Add SignalRFacade for SignalR-based API communication 2026-01-30 12:04:33 +01:00
Pierre Arnaud
c1740b53fc Add SignalR client support to Electron Host for new startup modes 2026-01-30 12:02:46 +01:00
Pierre Arnaud
40aed60c7d Add RuntimeControllerAspNetDotnetFirstSignalR for SignalR-based startup 2026-01-30 12:00:27 +01:00
Pierre Arnaud
8ee81f6abd Add ElectronHub and SignalR infrastructure for new startup modes 2026-01-30 11:58:31 +01:00
Pierre Arnaud
7f2ea4839e Add PackagedDotnetFirstSignalR and UnpackedDotnetFirstSignalR startup methods 2026-01-30 11:57:40 +01:00
Florian Rappl
456135a562 Merge pull request #1015 from hillin/main
Update Migration Guide
2026-01-21 12:18:25 +01:00
lucas
300f52510c Update Migration Guide with details on electron-builder.json location and launch settings 2026-01-21 15:30:44 +08:00
Florian Rappl
d85a64f515 Merge pull request #1014 from softworkz/submit_new_event
BrowserWindow: Add OnBoundsChanged event
2026-01-21 08:19:16 +01:00
Florian Rappl
53698d1d44 Merge pull request #1013 from softworkz/submit_timeout
Double-up timeout for electron-builder and other fixes
2026-01-21 08:07:50 +01:00
Florian Rappl
04a224aa4f Merge pull request #1012 from softworkz/submit_migcheck_updates
Relax Migration check for package.json in root
2026-01-21 08:06:55 +01:00
softworkz
891da140b7 Relax Migration check for package.json in root 2026-01-21 06:50:07 +01:00
softworkz
17f761d184 Fix FormatException 2026-01-21 06:48:28 +01:00
softworkz
9f6489891e ElectronNET.Host: Revert JS SDK to default value from VS 2026 2026-01-21 06:48:28 +01:00
softworkz
c8f1cdf59f Add tests for BrowserWindow.OnBoundsChanged event 2026-01-21 06:47:28 +01:00
softworkz
6c95dfd476 BrowserWindow: Add OnBoundsChanged event
While not being an original Electron event, this one includes the bounds values,
saving the additional roundtrip for calling GetBoundsAsync
2026-01-21 06:47:28 +01:00
softworkz
b180fc2ea8 Double-up timeout for electron-builder 2026-01-21 05:10:24 +01:00
Florian Rappl
682a1c38ed Optional 2025-12-23 21:10:57 +01:00
Florian Rappl
809b0a6be7 Added missing methods 2025-12-23 00:38:37 +01:00
Florian Rappl
ae3f755648 Fixed center handling 2025-12-22 12:32:52 +01:00
Florian Rappl
7e6760a428 Merge pull request #998 from ElectronNET/develop
Release 0.4.0
2025-12-19 00:09:34 +01:00
Florian Rappl
29fdbb5315 Updated changelog 2025-12-18 23:58:04 +01:00
Florian Rappl
36bba6a49f Merge pull request #995 from softworkz/submit_packageid
Set PackageId from ElectronPackageId
2025-12-18 21:57:28 +01:00
Florian Rappl
44a010e0ed Merge pull request #996 from softworkz/submit_singleinstance
Fix ElectronSingleInstance handling
2025-12-18 21:56:59 +01:00
softworkz
25770db138 Update src/ElectronNET/build/ElectronNET.Core.targets
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-18 17:25:52 +01:00
Florian Rappl
1c99ab02cc Merge pull request #997 from softworkz/submit_rerun
Try fix retry-test-jobs
2025-12-18 16:56:29 +01:00
softworkz
73c56e2450 Try fix retry-test-jobs 2025-12-18 16:13:04 +01:00
softworkz
30037fce69 Fix ElectronSingleInstance handling 2025-12-18 15:52:56 +01:00
softworkz
69048d5565 Set PackageId from ElectronPackageId 2025-12-18 15:12:21 +01:00
Florian Rappl
d9c8e04b5c Meta for 0.4.0 2025-12-17 16:40:46 +01:00
Florian Rappl
368ef412bb Merge pull request #988 from softworkz/submit_crossdebug
Core: Introduce cross-platform npm restore and check mismatch on publish
2025-12-17 15:29:48 +01:00
Florian Rappl
cb20fbad25 Merge pull request #991 from ElectronNET/develop
Release 0.3.1
2025-12-16 19:00:07 +01:00
Florian Rappl
89cdf2f55b Merge pull request #992 from softworkz/submit_rerun
Try fix job re-run
2025-12-16 18:53:23 +01:00
softworkz
1c62c1f132 Try fix job re-run 2025-12-16 16:02:43 +01:00
Florian Rappl
d3e3188681 Prepare for 0.3.1 2025-12-16 15:51:39 +01:00
Florian Rappl
4f95043309 Merge pull request #990 from softworkz/submit_packageid
Use ElectronPackageId instead of PackageId
2025-12-16 15:48:43 +01:00
softworkz
13f1203ccc Use ElectronPackageId instead of PackageId 2025-12-16 14:54:20 +01:00
softworkz
9d0378798b ElectronProcessActive: Add check for platform mismatch 2025-12-15 13:22:53 +01:00
softworkz
1d6ef7a250 ElectronProcessActive: Mark binaries as executable when debugging 2025-12-15 12:18:42 +01:00
softworkz
2d6d4e2320 Fix up incorrect symlinks created by npm on Windows 2025-12-15 12:18:42 +01:00
softworkz
df8e269d5c Core: Introduce cross-platform npm restore and check mismatch on publish 2025-12-15 12:18:42 +01:00
Florian Rappl
bdfbcd5b77 Merge pull request #986 from ElectronNET/develop
Release 0.3.0
2025-12-14 22:13:10 +01:00
Florian Rappl
19e785f53f Fixed whitespace 2025-12-14 15:30:21 +01:00
Florian Rappl
0b453177b5 Added contributing guide 2025-12-14 15:20:15 +01:00
Florian Rappl
8ed7f27722 Make sure the electron-host-hook strays unobtrusive 2025-12-14 00:49:34 +01:00
Florian Rappl
321b8cd9e5 Merge branch 'develop' of https://github.com/ElectronNET/Electron.NET into develop 2025-12-14 00:36:59 +01:00
Florian Rappl
b326f863d8 Updated changelog 2025-12-14 00:36:53 +01:00
Florian Rappl
f148fe6a14 Merge pull request #983 from markatosi/develop
Wiki Package building macOS sections added
2025-12-14 00:32:37 +01:00
Florian Rappl
515d325731 Fixed startup in VS Code 2025-12-13 23:43:12 +01:00
markatosi
f32a40fe17 Add macOS profiles for ASP.NET and Console apps
Added profiles for ASP.NET and Console applications on macOS.
2025-12-13 12:18:38 -10:00
Florian Rappl
c53a5a9163 Merge pull request #982 from markatosi/develop
Change Example code for AutoMenuHide
2025-12-13 12:28:41 +01:00
markatosi
8d4cdddc46 Conditionally set AutoHideMenuBar for Windows/Linux 2025-12-12 13:45:56 -10:00
Florian Rappl
70e8f85123 Merge pull request #977 from softworkz/submit_revertnet6
Combine and separate workflows
2025-12-12 07:23:26 +01:00
softworkz
5a7cbd972f Combine and separate workflows
- Separate between PR and Push execution (with and without secrets)
- Turn tests into re-usable workflows and call them from the main ones
2025-12-12 02:15:44 +01:00
softworkz
33da428c45 Fix build after PR merge
Re-adding .net6 caused the collection expression to fail
2025-12-12 01:39:02 +01:00
softworkz
f1b4766360 Add automatic retry for failed test jobs 2025-12-12 00:51:13 +01:00
softworkz
e070759645 integration-tests: Add random delay 2025-12-12 00:49:16 +01:00
Florian Rappl
49eaa5e129 Merge pull request #973 from davidroth/set-menu-items
Add dynamic tray menu update functionality
2025-12-11 22:58:06 +01:00
Florian Rappl
b3f5a3c52b Merge pull request #976 from softworkz/submit_revertnet6
Partially revert "Drop .NET 6.0, default to .NET 10.0 and update dependencies"
2025-12-11 22:57:40 +01:00
softworkz
0d732ce99e Partially revert "Drop .NET 6.0, default to .NET 10.0 and update dependencies" 2025-12-11 22:35:45 +01:00
David Roth
218b99808b Add dynamic tray menu update functionality
Introduced a `SetMenuItems` method in `Tray.cs` to enable updating the tray's context menu dynamically. This method clears existing menu items, adds new ones, and registers click handlers. Added XML documentation for the method.
2025-12-11 18:04:31 +01:00
Florian Rappl
7f507a6d86 Merge pull request #975 from davidroth/missing-usings
Fix build by adding necessary using directives for async and DI setup.
2025-12-11 10:55:17 +01:00
Florian Rappl
81b3793966 Merge pull request #974 from davidroth/dotnet-targets
Drop .NET 6.0, default to .NET 10.0 and update dependencies
2025-12-11 10:54:44 +01:00
David Roth
1baa5c6200 Drop .NET 6.0, default to .NET 10.0 and update dependencies
Updated all projects to target .NET 10.0, removing support for .NET 6.0. Upgraded key dependencies, including:
- `Microsoft.Build` and `Microsoft.Build.Tasks.Core` to 18.0.2.
- `Nuke.Common` to 10.1.0.
- `System.Drawing.Common` to 10.0.1.
- `System.Text.Json` to 10.0.1.

Removed `NoWarn` configurations for .NET 6.0 in `ElectronNET.AspNet.csproj`. Reformatted the `Copy` task in `ElectronNET.Build.csproj` for readability. Updated `.pubxml` files and other project files to reflect the framework upgrade.
2025-12-11 07:25:07 +01:00
David Roth
5f76804065 Fix buidl by adding necessary using directives for async and DI setup. 2025-12-11 07:23:30 +01:00
Florian Rappl
70ffebf39d Updated changelog 2025-12-10 23:03:57 +01:00
Florian Rappl
0a54735f35 Merge branch 'main' of https://github.com/ElectronNET/Electron.NET into develop 2025-12-10 22:54:56 +01:00
Florian Rappl
1ac371b3da Merge pull request #962 from softworkz/submit_testupd
Collection of test fixes
2025-12-09 15:23:33 +01:00
Florian Rappl
e854451043 Merge pull request #968 from adityashirsatrao007/feat/host-hook-sample
feat: Add ElectronHostHook sample application (#967)
2025-12-09 10:21:36 +01:00
Aditya
331c2f548c fix: address review comments (protocol, config, startup) 2025-12-07 23:08:09 +05:30
Aditya
b00adcbd38 feat: Add ElectronHostHook sample application (#967) 2025-12-07 22:57:35 +05:30
Florian Rappl
ed6d24dbb1 Merge pull request #964 from niteshsinghal85/custom_main_example
Add custom_main.js guide to documentation
2025-12-07 18:08:26 +01:00
Florian Rappl
2c82206c62 Merge pull request #966 from softworkz/submit_migrationchecks
Add migration checks
2025-12-07 18:00:18 +01:00
softworkz
d0eb0880b1 Fix publishing documentation 2025-12-07 16:12:49 +01:00
softworkz
0321804128 Add documentation for migration checks 2025-12-07 16:12:49 +01:00
softworkz
f548ff08d4 Add migration checks 2025-12-07 16:12:23 +01:00
Nitesh Singhal
bbc7c79d5a Merge branch 'custom_main_example' of https://github.com/niteshsinghal85/Electron.NET into custom_main_example 2025-12-07 18:17:26 +05:30
Nitesh Singhal
2389ae32bd Update Custom_main.md to clarify usage of custom_main.js 2025-12-07 18:16:48 +05:30
Nitesh Singhal
42fecbdc98 Update docs/_Sidebar.md
Co-authored-by: softworkz <4985349+softworkz@users.noreply.github.com>
2025-12-07 18:10:16 +05:30
softworkz
95e02e2655 Introduce common base class for tests 2025-12-07 11:56:32 +01:00
softworkz
153625ba51 Introduce IntegrationFactAttribute 2025-12-07 11:56:26 +01:00
softworkz
c97f914e7a Introduce TimeSpan extensions 2025-12-07 11:20:43 +01:00
Nitesh Singhal
d95b41fbae Add custom_main.js guide to documentation 2025-12-07 15:00:28 +05:30
softworkz
2b2b26e13b GetSetUserAgent 2025-12-06 21:08:47 +01:00
softworkz
9bb2adca78 Use 3s timeout for GetUserAgentAsync and updat test 2025-12-06 20:53:15 +01:00
softworkz
88d2daacb1 WebContentsTests: Experiment!
Re-enable some tests for CI as experiment
2025-12-06 20:35:51 +01:00
softworkz
c90f003519 GetSetZoomLevel: Don't use shared window
Because we need a visible window and the main window
visibility must not be mutated by tests
2025-12-06 20:11:27 +01:00
softworkz
daa9f399e9 ScreenTests: Remove invalid constraints
Negative values for cursor position are not invalid
2025-12-06 20:09:05 +01:00
softworkz
6246b44d68 ReadyToShow_event_fires: Destroy window 2025-12-06 20:08:01 +01:00
softworkz
347c1ef0e4 Show_hide_visibility_roundtrip: Don't use shared window 2025-12-06 20:06:34 +01:00
softworkz
7c8eeef225 GetPrintersAsync: Increase timeout 2025-12-06 20:04:23 +01:00
Florian Rappl
44a820e285 Merge pull request #960 from agracio/apis
Removing all properties merged in #958
2025-12-06 14:12:59 +01:00
agracio
8ccc7bfdd0 updating Microsoft.VisualStudio.JavaScript.Sdk to correct version 2025-12-06 12:30:05 +00:00
agracio
cf185fd2fb removing properties from webContents.ts 2025-12-06 12:11:58 +00:00
agracio
4ee11aaeae removing properties 2025-12-06 11:58:30 +00:00
Florian Rappl
f1063bbe98 Merge pull request #958 from agracio/apis
Additional APIs for WebContents
2025-12-05 09:10:11 +01:00
agracio
7c39d28f81 adding missing semicolon 2025-12-05 00:09:22 +00:00
agracio
e52bf69dee excluding some tests based on GitHub runner OS 2025-12-04 20:36:41 +00:00
agracio
46d395ad12 excluding some tests based on GitHub runner OS 2025-12-04 20:31:14 +00:00
agracio
c0e3a595c3 adding a couple await Task.Delay(500); to test in order to resolve flaky tests 2025-12-04 19:26:24 +00:00
agracio
65db66b4e9 trying a different env variable for skipping tests 2025-12-04 19:16:41 +00:00
agracio
ce8a86baf5 trying to fix some tests on CI 2025-12-04 19:13:20 +00:00
agracio
7eaf84a021 attempting to fix some tests on CI 2025-12-04 19:07:26 +00:00
agracio
90f5f5dbe2 attempting to fix some tests on CI 2025-12-04 18:38:12 +00:00
agracio
e09ef35be4 removing incorrect import 2025-12-04 18:08:42 +00:00
agracio
dae521180f adding WebContents APIs for Zoom, Audio and DevTools 2025-12-04 17:52:01 +00:00
Florian Rappl
c81ed54fff Merge pull request #953 from kevinfoley/readme-fix
README.md: fix grammar and typos
2025-11-29 21:12:35 +01:00
Kevin Foley
276ce8f4dc README.md: fix grammar and typos 2025-11-25 17:13:20 -08:00
Florian Rappl
20212cdca7 Merge pull request #951 from agracio/macos-intel-runners
adding macOS Intel runner
2025-11-24 09:01:23 +01:00
agracio
0acdc65271 adding macOS Intel runner 2025-11-24 00:45:31 +00:00
Florian Rappl
c92a54fbda Merge pull request #949 from softworkz/submit_model_update
Update all model classes to Electron API 39.2
2025-11-22 14:17:25 +01:00
Florian Rappl
4de7760556 Merge pull request #948 from softworkz/submit_extend_test_matrix
CI Tests: Extend matrix - 6 runners and 2 Electron versions
2025-11-22 14:11:50 +01:00
softworkz
3821ca60ea getPrinters: Increase timeout 2025-11-22 02:27:44 +01:00
softworkz
59887cce5f Guard deserialization error 2025-11-22 02:24:32 +01:00
softworkz
77834d0f2e Drop workaround via DescriptinoAttribute: No longer needed 2025-11-22 02:24:32 +01:00
softworkz
6761119241 Update all model classes to Electron API 39.2 2025-11-22 02:24:32 +01:00
softworkz
41d9f0afc7 Integration CI Tests: Extend matrix for more runners and two Electron versions 2025-11-22 00:57:15 +01:00
Florian Rappl
1d554fda60 Merge pull request #945 from softworkz/submit_outpath
BrowserWindowOptions; Fix default  and add more Tests
2025-11-21 00:34:09 +01:00
softworkz
2f4d37d823 Try macOS 26 2025-11-20 20:32:08 +01:00
softworkz
1815951092 BrowserWindowOptions; Fix default for FullScreenable
Electron's default is true
2025-11-20 02:56:00 +01:00
Florian Rappl
aa483ebaa4 Updated version 2025-11-19 09:14:54 +01:00
Florian Rappl
9b4e0e4504 Merge pull request #942 from softworkz/submit_outpath
electron-builder: Provide correct output path
2025-11-19 02:14:45 +01:00
softworkz
d05191d1e2 electron-builder: Provide correct output path and fix publish url 2025-11-19 02:05:48 +01:00
Florian Rappl
ef41329a91 Merge pull request #938 from softworkz/submit_ipc_types
Fix API break: public API must not expose JsonElement objects
2025-11-17 16:27:56 +01:00
Florian Rappl
edafa8ccdb Merge pull request #939 from softworkz/submit_cleanup
Cleanup & Consistency
2025-11-17 15:15:42 +01:00
softworkz
55c035323b Use non-generic TaskCompletionSource where applicable 2025-11-17 13:58:47 +01:00
softworkz
2e5c6200a6 ShowItemInFolderAsync: Fix endless hang 2025-11-17 13:58:47 +01:00
softworkz
be518a7a10 IpcMain: Properly handle Ipc message params (always 2-elem array) 2025-11-17 13:57:04 +01:00
softworkz
9e9558b837 SocketIoFacade: Remove unused method
It's not needed for compatibility since SocketIoFacade is internal
2025-11-17 13:18:41 +01:00
softworkz
5f6e4a9e9d Disable Nullable for all projects (was already for API/AspNet) 2025-11-17 13:18:41 +01:00
softworkz
b89c08ee96 IpcMainTests: Properly test for the types of values (must not be JsonElement) 2025-11-17 13:17:01 +01:00
softworkz
8ff875435b Fix API break: public API must not expose JsonElement objects
This changes deserialization to the way how it was with
Json.Net: Only .net primitive types are exposed publicly
2025-11-17 13:17:01 +01:00
Florian Rappl
bfad0cd45a Merge pull request #937 from softworkz/submit_move_workflow
Move PR comment creation to separate workflow on:pull_request_target
2025-11-17 09:03:53 +01:00
softworkz
91ed116cb1 Harden reading of PR number against injection attacks 2025-11-17 03:33:51 +01:00
softworkz
54eac4b521 Add workflow files to solution 2025-11-17 02:49:57 +01:00
softworkz
ac05ded844 Move PR comment creation to separate workflow on:pull_request_target 2025-11-17 02:49:57 +01:00
Florian Rappl
5434b0ec8e Merge pull request #936 from ElectronNET/develop
Release 0.2.0
2025-11-16 23:19:06 +01:00
Florian Rappl
20acd31f1a Updated changelog 2025-11-16 09:59:46 +01:00
Florian Rappl
f53d071bd9 Merge pull request #929 from softworkz/submit_platform_annotations
ElectronNET.API: Add platform support attributes
2025-11-16 09:38:31 +01:00
softworkz
2cf3095450 BrowserWindowTests: Add delays everywhere 2025-11-16 03:05:33 +01:00
softworkz
0ec791da9d Improve test Progress_bar_and_always_on_top_toggle 2025-11-16 02:59:50 +01:00
softworkz
0580942a59 ElectronNET API: Add platform support attributes 2025-11-16 02:53:28 +01:00
Florian Rappl
8e8d88c48f Merge pull request #931 from softworkz/submit_update_tests
Integration Tests Everywhere!
2025-11-16 02:42:22 +01:00
softworkz
61476e3ca4 Try fix remaining tests 2025-11-16 01:04:58 +01:00
softworkz
9488576d8f Add integration-tests workflow 2025-11-16 00:30:07 +01:00
softworkz
ff1b802838 Update ElectronFixture 2025-11-16 00:30:07 +01:00
softworkz
c98ad58290 Add xunit.runner.json 2025-11-16 00:30:07 +01:00
softworkz
a30239e3a6 Update electron start 2025-11-16 00:30:07 +01:00
softworkz
17f24749cd Project defaults: Make RuntimeIdentifier overrideable 2025-11-16 00:30:07 +01:00
softworkz
7558037b91 ElectronNET.IntegrationTests: Add platform support attributes 2025-11-16 00:30:07 +01:00
softworkz
e4485fd483 Remove Can_set_app_logs_path test
It's pointless because it doesn't ever throw
2025-11-16 00:24:17 +01:00
softworkz
1a964b405e BrowserWindow tests: use about:blank as url 2025-11-16 00:24:17 +01:00
softworkz
dfcb2345f3 Test project: Update to .net10 2025-11-16 00:24:17 +01:00
softworkz
3f10d6b5dd Remove tests for app badgecount
These require a full-blown desktop environment which is hardly ever available where these tests are run
2025-11-16 00:24:17 +01:00
Florian Rappl
90c3eb2c88 Merge pull request #935 from softworkz/submit_ts_project
ElectronNET.Host: Replace website by a real ts project
2025-11-15 16:02:02 +01:00
Florian Rappl
dbf76a1d6d Merge pull request #927 from softworkz/submit_whitespace2
Fix formatting and add GitHub Action to check trailing whitespace
2025-11-15 16:00:36 +01:00
softworkz
8e7892ebd4 Fix whitespace formatting 2025-11-15 13:52:19 +01:00
softworkz
30b547b8d3 Add R# settings 2025-11-15 13:41:25 +01:00
softworkz
30b4d92291 Add GitHub Action to check trailing whitespace on PRs 2025-11-15 13:41:25 +01:00
softworkz
6c9027faf3 WebApp: Do not compiled TS on build
(it's sufficient on save)
2025-11-15 13:14:27 +01:00
softworkz
c712027ea3 ElectronNET.Host: Replace website by a real ts project 2025-11-15 13:13:35 +01:00
Florian Rappl
7889057022 Merge pull request #933 from softworkz/submit_invocation_rename
Rename PropertyGet to more generic Invocator
2025-11-15 12:38:48 +01:00
Florian Rappl
68c50f1c1e Merge pull request #928 from softworkz/submit_remove_priv
Remove softworkz' special sauce
2025-11-15 12:37:24 +01:00
Florian Rappl
1006355bb7 Merge pull request #930 from softworkz/submit_pkg_update
build project: Update packages (due to vulnerabilities)
2025-11-15 12:35:34 +01:00
Florian Rappl
12c5391164 Merge pull request #932 from softworkz/submit_platform_specific
Webb app: add platform guards and promote analyzer severity
2025-11-15 12:34:57 +01:00
Florian Rappl
8ba24c0f2f Merge pull request #934 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/js-yaml-4.1.1
Bump js-yaml from 4.1.0 to 4.1.1 in /src/ElectronNET.WebApp/ElectronHostHook
2025-11-15 12:25:28 +01:00
dependabot[bot]
bb7ae8d711 Bump js-yaml in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-15 10:40:05 +00:00
softworkz
385dcfbf52 ApiBase: Code style and whitespace 2025-11-15 10:21:18 +01:00
softworkz
515430ff96 ApiBase: Rename PropertyGet to more generic Invocator 2025-11-15 10:21:18 +01:00
softworkz
a6d67a4dfe Make CA1416 and error (platform-depended reachabílitiy of code 2025-11-15 09:50:00 +01:00
softworkz
946b2af81a Webb app: add platform conditions 2025-11-15 09:50:00 +01:00
softworkz
1e483e9cc4 build project: Update packages (due to vulnerabilities) 2025-11-15 09:35:42 +01:00
softworkz
5305e17ba9 MSBuild props/targets: Adapt demo/test projects for change 2025-11-15 08:44:50 +01:00
softworkz
442084a3e5 MSBuild props/targets: Remove wrapped files 2025-11-15 08:44:50 +01:00
softworkz
cd205edabb common.props: Remove personal extras 2025-11-15 08:27:34 +01:00
Florian Rappl
10bf461b51 Prepare for Blazor support 2025-11-14 17:38:55 +01:00
380 changed files with 72614 additions and 4249 deletions

137
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,137 @@
# Contributing
## Project Scope
The Electron.NET project ultimately tries to provide a framework for developing cross-platform client applications on the basis of .NET and Electron. Anything that is related to this goal will be considered. The project aims to be as close to Electron with .NET as a basis as possible. If your contribution does not reflect that goal, the chances of accepting it are limited.
## Code License
This is an open source project falling under the [MIT License](../LICENSE). By using, distributing, or contributing to this project, you accept and agree that all code within the Electron.NET project and its libraries are licensed under MIT license.
## Becoming a Contributor
Usually appointing someone as a contributor follows this process:
1. An individual contributes actively via discussions (reporting bugs, giving feedback to existing or opening new issues) and / or pull requests
2. The individual is either directly asked, invited or asks for contributor rights on the project
3. The individual uses the contribution rights to sustain or increase the active contributions
Every contributor might have to sign the contributor's license agreement (CLA) to establish a legal trust between the project and its contributors.
## Working on Electron.NET
### Issue Discussion
Discussion of issues should be placed transparently in the issue tracker here on GitHub.
* [General issues, bugs, new features](https://github.com/ElectronNET/Electron.NET/issues)
* [General discussions, help, exchange of ideas](https://github.com/ElectronNET/Electron.NET/discussions)
### Modifying the code
Electron.NET and its libraries uses features from the latest versions of C# (e.g., C# 10). You will therefore need a C# compiler that is up for the job.
1. Fork and clone the repo.
2. First try to build the ElectronNET.Core library and see if you get the tests running.
3. You will be required to resolve some dependencies via NuGet.
The build system of Electron.NET uses NUKE.
### Code Conventions
Most parts in the Electron.NET project are fairly straight forward. Among these are:
* Always use statement blocks for control statements, e.g., in a for-loop, if-condition, ...
* You may use a simple (throw) statement in case of enforcing contracts on argument
* Be explicit about modifiers (some files follow an older convention of the code base, but we settled on the explicit style)
### Development Workflow
1. If no issue already exists for the work you'll be doing, create one to document the problem(s) being solved and self-assign.
2. Otherwise please let us know that you are working on the problem. Regular status updates (e.g. "still in progress", "no time anymore", "practically done", "pull request issued") are highly welcome.
3. Create a new branch—please don't work in the `main` branch directly. It is reserved for releases. We recommend naming the branch to match the issue being addressed (`feature/#777` or `issue-777`).
4. Add failing tests for the change you want to make. Tests are crucial and should be taken from W3C (or other specification).
5. Fix stuff. Always go from edge case to edge case.
6. All tests should pass now. Also your new implementation should not break existing tests.
7. Update the documentation to reflect any changes. (or document such changes in the original issue)
8. Push to your fork or push your issue-specific branch to the main repository, then submit a pull request against `develop`.
Just to illustrate the git workflow for Electron.NET a little bit more we've added the following graphs.
Initially, Electron.NET starts at the `main` branch. This branch should contain the latest stable (or released) version.
Here we now created a new branch called `develop`. This is the development branch.
Now active work is supposed to be done. Therefore a new branch should be created. Let's create one:
```sh
git checkout -b feature/#777
```
There may be many of these feature branches. Most of them are also pushed to the server for discussion or synchronization.
```sh
git push -u origin feature/#777
```
Now feature branches may be closed when they are done. Here we simply merge with the feature branch(es). For instance the following command takes the `feature/#777` branch from the server and merges it with the `develop` branch.
```sh
git checkout develop
git pull
git pull origin feature/#777
git push
```
Finally, we may have all the features that are needed to release a new version of Electron.NET. Here we tag the release. For instance for the 1.0 release we use `v1.0`.
```sh
git checkout main
git merge develop
git tag v1.0
```
(The last part is automatically performed by our CI system. Don't tag manually.)
### Versioning
The rules of [semver](http://semver.org/) don't necessarily apply here, but we will try to stay quite close to them.
Prior to version 1.0.0 we use the following scheme:
1. MINOR versions for reaching a feature milestone potentially combined with dramatic API changes
2. PATCH versions for refinements (e.g. performance improvements, bug fixes)
After releasing version 1.0.0 the scheme changes to become:
1. MAJOR versions at maintainers' discretion following significant changes to the codebase (e.g., API changes)
2. MINOR versions for backwards-compatible enhancements (e.g., performance improvements)
3. PATCH versions for backwards-compatible bug fixes (e.g., spec compliance bugs, support issues)
#### Code style
Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.** In general most of the [C# coding guidelines from Microsoft](https://msdn.microsoft.com/en-us/library/ff926074.aspx) are followed. This project prefers type inference with `var` to explicitly stating (redundant) information.
It is also important to keep a certain `async`-flow and to always use `ConfigureAwait(false)` in conjunction with an `await` expression.
## Backwards Compatibility
We always try to remain backwards compatible beyond the currently supported versions of .NET.
For instance, in December 2025 there have been activity to remove .NET 6 support from the codebase. We rejected this. Key points:
1. We have absolutely no need to drop `.net6` support. It doesn't hurt us in any way.
2. Many are still using `.net6`, including Electron.NET (non-Core) users. It doesn't make sense to force them to update two things at the same time (.NET + Electron.NET).
3. We MUST NOT and NEVER update `Microsoft.Build.Utilities.Core`. This will make Electron.NET stop working on older Visual Studio and MSBuild versions. There's are also no reasons to update it in the first place.
It's important to note that the Microsoft label of "Out of support" on .NET has almost no practical meaning. We've rarely (if ever) seen any bugs fixed in the same .NET version which mattered. The bugs that all new .NET versions have are much worse than mature .NET versions which are declared as "out of support". Keep in mind that the LTS matters most for active development / ongoing supported projects. If, e.g., a TV has been released a decade ago it most likely won't be patched. Still, you might want to deploy applications to it, which then naturally would involve being based on "out of support" versions of the framework.
TL;DR: Unless there is a technical reason (e.g., a crucial new API not being available) we should not drop "out of support" .NET versions. At the time of writing (December 2025) the minimum supported .NET version remains at `.net6`.
## Timeline
**All of this information is related to ElectronNET.Core pre-v1!**
We pretty much release whenever we have something new (i.e., do fixes such as a 0.1.1, or add new features, such as a 0.2.0) quite quickly.
We will go for a 1.0.0 release of this as early as ~mid of January 2026 (unless we find some critical things or want to extend the beta phase for ElectronNET.Core). This should be sufficient time to get some user input and have enough experience to call it stable.

View File

@@ -1,33 +1,25 @@
name: CI
name: Build and Publish
on: [push, pull_request]
on: [push]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
concurrency:
group: build-publish-${{ github.ref }}
cancel-in-progress: true
jobs:
# linux:
# runs-on: ubuntu-latest
# timeout-minutes: 10
Integration-Tests:
uses: ./.github/workflows/integration-tests.yml
name: '1'
# steps:
# - uses: actions/checkout@v4
# - name: Setup dotnet
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: |
# 6.0.x
# 8.0.x
# 10.0.x
# - name: Build
# run: ./build.sh
windows:
Publish:
needs: [Integration-Tests]
runs-on: windows-latest
timeout-minutes: 10
name: '2 / Publish'
steps:
- uses: actions/checkout@v4

39
.github/workflows/PR Validation.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: PR Validation
on: [pull_request]
concurrency:
group: pr-validation-${{ github.ref }}
cancel-in-progress: true
jobs:
Whitespace-Check:
uses: ./.github/workflows/trailing-whitespace-check.yml
secrets: inherit
name: '1'
Tests:
needs: Whitespace-Check
uses: ./.github/workflows/integration-tests.yml
secrets: inherit
name: '2'
build:
needs: [Whitespace-Check, Tests]
runs-on: windows-latest
timeout-minutes: 10
name: '3 / Build'
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
10.0.x
- name: Build
run: .\build.ps1

220
.github/workflows/integration-tests.yml vendored Normal file
View File

@@ -0,0 +1,220 @@
name: Tests
on:
workflow_call:
concurrency:
group: integration-tests-${{ github.ref }}
cancel-in-progress: true
jobs:
tests:
name: ${{ matrix.os }} API-${{ matrix.electronVersion }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-24.04, windows-2022, windows-2025, macos-14, macos-15-intel, macos-26]
electronVersion: ['30.4.0', '38.2.2']
include:
- os: ubuntu-22.04
rid: linux-x64
- os: ubuntu-24.04
rid: linux-x64
- os: windows-2022
rid: win-x64
- os: windows-2025
rid: win-x64
- os: macos-14
rid: osx-arm64
- os: macos-15-intel
rid: osx-x64
- os: macos-26
rid: osx-arm64
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_NOLOGO: 1
CI: true
ELECTRON_ENABLE_LOGGING: 1
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Random delay (0-20 seconds)
shell: bash
run: |
DELAY=$((RANDOM % 21))
echo "Waiting for $DELAY seconds..."
sleep $DELAY
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Restore
run: dotnet restore -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
- name: Build
run: dotnet build --no-restore -c Release -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
- name: Install Linux GUI dependencies
if: runner.os == 'Linux'
run: |
set -e
sudo apt-get update
. /etc/os-release
if [ "$VERSION_ID" = "24.04" ]; then ALSA_PKG=libasound2t64; else ALSA_PKG=libasound2; fi
echo "Using ALSA package: $ALSA_PKG"
sudo apt-get install -y xvfb \
libgtk-3-0 libnss3 libgdk-pixbuf-2.0-0 libdrm2 libgbm1 libxss1 libxtst6 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libx11-xcb1 "$ALSA_PKG"
- name: Run tests (Linux)
if: runner.os == 'Linux'
continue-on-error: true
run: |
mkdir -p test-results
xvfb-run -a dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj \
-c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} \
--logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" \
--logger "console;verbosity=detailed" \
--results-directory test-results
- name: Run tests (Windows)
if: runner.os == 'Windows'
continue-on-error: true
run: |
New-Item -ItemType Directory -Force -Path test-results | Out-Null
dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results
- name: Run tests (macOS)
if: runner.os == 'macOS'
continue-on-error: true
run: |
mkdir -p test-results
dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results
- name: Upload raw test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.os }}-electron-${{ matrix.electronVersion }}
path: test-results/*.trx
retention-days: 7
summary:
name: Test Results
runs-on: ubuntu-24.04
if: always()
needs: [tests]
permissions:
actions: read
contents: read
checks: write
pull-requests: write
steps:
- name: Download all test results
uses: actions/download-artifact@v4
with:
path: test-results
- name: Setup .NET (for CTRF conversion)
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Install CTRF TRX→CTRF converter (dotnet tool)
run: |
dotnet new tool-manifest
dotnet tool install DotnetCtrfJsonReporter --local
- name: Convert TRX → CTRF and clean names (filePath=OS|Electron X.Y.Z)
shell: bash
run: |
set -euo pipefail
mkdir -p ctrf
shopt -s globstar nullglob
conv=0
for trx in test-results/**/*.trx; do
base="$(basename "$trx" .trx)" # e.g. ubuntu-22.04-electron-30.4.0
os="${base%%-electron-*}"
electron="${base#*-electron-}"
label="$os|Electron $electron"
outdir="ctrf/${label}"
mkdir -p "$outdir"
out="${outdir}/ctrf-report.json"
dotnet tool run DotnetCtrfJsonReporter -p "$trx" -d "$outdir" -f "ctrf-report.json"
jq --arg fp "$label" '.results.tests |= map(.filePath = $fp)' "$out" > "${out}.tmp" && mv "${out}.tmp" "$out"
echo "Converted & normalized $trx -> $out"
conv=$((conv+1))
done
echo "Processed $conv TRX file(s)"
- name: Publish Test Report
if: always()
uses: ctrf-io/github-test-reporter@v1
with:
report-path: 'ctrf/**/*.json'
summary: true
pull-request: false
status-check: false
status-check-name: 'Integration Tests'
use-suite-name: true
update-comment: true
always-group-by: true
overwrite-comment: true
exit-on-fail: true
group-by: 'suite'
upload-artifact: true
fetch-previous-results: true
summary-report: false
summary-delta-report: true
github-report: true
test-report: false
test-list-report: false
failed-report: true
failed-folded-report: false
skipped-report: true
suite-folded-report: true
suite-list-report: false
file-report: true
previous-results-report: true
insights-report: true
flaky-report: true
flaky-rate-report: true
fail-rate-report: false
slowest-report: false
report-order: 'summary-delta-report,failed-report,skipped-report,suite-folded-report,file-report,previous-results-report,github-report'
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Save PR Number
if: github.event_name == 'pull_request'
run: echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
- name: Write PR Number to File
if: github.event_name == 'pull_request'
run: echo "$PR_NUMBER" > pr_number.txt
shell: bash
- name: Upload PR Number Artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: pr_number
path: pr_number.txt
- name: Summary
run: echo "All matrix test jobs completed."

81
.github/workflows/pr-comment.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Create PR Comments
on:
workflow_run:
workflows: [ "PR Validation" ]
types: [completed]
permissions:
contents: read
actions: read
pull-requests: write
jobs:
pr-comment:
name: Post Test Result as PR comment
runs-on: ubuntu-24.04
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion != 'cancelled'
steps:
- name: Download CTRF artifact
uses: dawidd6/action-download-artifact@v8
with:
github_token: ${{ github.token }}
run_id: ${{ github.event.workflow_run.id }}
name: ctrf-report
path: ctrf
- name: Download PR Number Artifact
uses: dawidd6/action-download-artifact@v8
with:
github_token: ${{ github.token }}
run_id: ${{ github.event.workflow_run.id }}
name: pr_number
path: pr_number
- name: Read PR Number
run: |
set -Eeuo pipefail
FILE='pr_number/pr_number.txt'
# Ensure file exists
if [ ! -f "$FILE" ] || [ -L "$FILE" ]; then
echo "Error: $FILE is missing or is not a regular file." >&2
exit 1
fi
# Chec file size
if [ "$(wc -c < "$FILE" | tr -d ' ')" -gt 200 ]; then
echo "Error: $FILE is too large." >&2
exit 1
fi
# Read first line
PR_NUMBER=""
IFS= read -r PR_NUMBER < "$FILE" || true
# Validate whether it's a number
if ! [[ "$PR_NUMBER" =~ ^[0-9]{1,10}$ ]]; then
echo "Error: PR_NUMBER is not a valid integer on the first line." >&2
exit 1
fi
printf 'PR_NUMBER=%s\n' "$PR_NUMBER" >> "$GITHUB_ENV"
- name: Post PR Comment
uses: ctrf-io/github-test-reporter@v1
with:
report-path: 'ctrf/**/*.json'
issue: ${{ env.PR_NUMBER }}
summary: true
pull-request: true
use-suite-name: true
update-comment: true
always-group-by: true
overwrite-comment: true
upload-artifact: false
pull-request-report: true
env:
GITHUB_TOKEN: ${{ github.token }}

49
.github/workflows/retry-test-jobs.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Tests auto-rerun
on:
workflow_run:
workflows: [ "PR Validation", "Build and Publish" ]
types: [ completed ]
jobs:
rerun-failed-matrix-jobs-once:
if: >
${{
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt == 1
}}
runs-on: ubuntu-24.04
permissions:
actions: write
contents: read
steps:
- name: Decide whether to rerun (only if matrix jobs failed)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
echo "Inspecting jobs of workflow run $RUN_ID in $REPO"
jobs_json="$(gh api -R $REPO repos/$REPO/actions/runs/$RUN_ID/jobs)"
echo "Jobs and conclusions:"
echo "$jobs_json" | jq '.jobs[] | {name: .name, conclusion: .conclusion}'
failed_matrix_jobs=$(echo "$jobs_json" | jq -r '
[ .jobs[]
| select(.conclusion == "failure"
and (.name | contains(" API-")))
]
| length // 0
')
failed_matrix_jobs=${failed_matrix_jobs:-0}
if [ "${failed_matrix_jobs}" -gt 0 ]; then
echo "Detected failing Integration Tests jobs re-running failed jobs for this run."
gh run rerun -R "$REPO" "$RUN_ID" --failed
else
echo "Only non-matrix jobs (like Test Results) failed not auto-rerunning."
fi

View File

@@ -0,0 +1,84 @@
name: Whitespace Check
on:
workflow_call:
jobs:
check-whitespace:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for trailing whitespace
run: |
echo "Checking for trailing whitespace in changed files..."
# Get the base branch
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
# Get list of changed files (excluding deleted files)
CHANGED_FILES=$(git diff --name-only --diff-filter=d "$BASE_SHA" "$HEAD_SHA")
if [ -z "$CHANGED_FILES" ]; then
echo "No files to check."
exit 0
fi
# File patterns to check (text files)
PATTERNS="\.cs$|\.csproj$|\.sln$|\.ts$|\.html$|\.css$|\.scss$"
# Directories and file patterns to exclude
EXCLUDE_PATTERNS="(^|\/)(\.|node_modules|bin|obj|artifacts|packages|\.vs|\.nuke\/temp)($|\/)"
ERRORS_FOUND=0
TEMP_FILE=$(mktemp)
while IFS= read -r file; do
# Skip if file doesn't exist (shouldn't happen with --diff-filter=d, but just in case)
if [ ! -f "$file" ]; then
continue
fi
# Check if file matches patterns to check
if ! echo "$file" | grep -qE "$PATTERNS"; then
continue
fi
# Check if file should be excluded
if echo "$file" | grep -qE "$EXCLUDE_PATTERNS"; then
continue
fi
# Find trailing whitespace lines, excluding XML doc placeholder lines that are exactly "/// " (one space)
MATCHES=$(grep -n '[[:space:]]$' "$file" | grep -vE '^[0-9]+:[[:space:]]*/// $' || true)
if [ -n "$MATCHES" ]; then
echo "❌ Trailing whitespace found in: $file"
echo "$MATCHES" | head -10
TOTAL=$(echo "$MATCHES" | wc -l)
if [ "$TOTAL" -gt 10 ]; then
echo " ... and $(($TOTAL - 10)) more lines"
fi
echo "1" >> "$TEMP_FILE"
fi
done <<< "$CHANGED_FILES"
ERRORS_FOUND=$(wc -l < "$TEMP_FILE" 2>/dev/null || echo "0")
rm -f "$TEMP_FILE"
if [ "$ERRORS_FOUND" -gt 0 ]; then
echo ""
echo "❌ Found trailing whitespace in $ERRORS_FOUND file(s)."
echo "Please remove trailing whitespace from the files listed above."
exit 1
else
echo "✅ No trailing whitespace found in changed files."
exit 0
fi

View File

@@ -1,3 +1,57 @@
# 0.4.1
## ElectronNET.Core
- Fixed handling of `Center` property for windows (#1001)
- Added missing methods on `Cookies` (#1000)
# 0.4.0
## ElectronNET.Core
- Fixed ElectronSingleInstance handling (#996) @softworkz
- Fixed `PackageId` handling (#993) @softworkz
- Added cross-platform npm restore and check mismatch on publish (#988) @softworkz
# 0.3.1
## ElectronNET.Core
- Fixed issue transforming the project ID (#989, #990) @softworkz
# 0.3.0
## ElectronNET.Core
- Updated infrastructure (#937, #939) @softworkz
- Updated all model classes to Electron API 39.2 (#949) @softworkz
- Fixed output path for `electron-builder` (#942) @softworkz
- Fixed floating point display resolution (#944) @softworkz
- Fixed error in case of missing electron-host-hook (#978)
- Fixed previous API break using exposed `JsonElement` objects (#938) @softworkz
- Fixed and improved several test cases (#962) @softworkz
- Fixed startup of Electron.NET from VS Code Debug Adapter (#952)
- Fixed the `BrowserWindowOptions` (#945) @softworkz
- Fixed example for `AutoMenuHide` to reflect platform capabilities (#982) @markatosi
- Added several migration checks for publishing (#966) @softworkz
- Added more test runners for E2E tests (#950, #951) @agracio
- Added dynamic updates for tray menu (#973) @davidroth
- Added matrix tests with 6 runners and 2 electron version (#948) @softworkz
- Added additional APIs for WebContents (#958) @agracio
- Added documentation for MacOS package publish (#983) @markatosi
- Added sample application for `ElectronHostHook` (#967) @adityashirsatrao007
# 0.2.0
## ElectronNET.Core
- Updated dependencies (#930) @softworkz
- Updated integration tests (#931) @softworkz
- Updated `ElectronNET.Host` (#935) @softworkz
- Removed transition period specific build configuration (#928) @softworkz
- Added `IsRunningBlazor` option to `BrowserWindowOptions` (#926)
- Added platform support attributes (#929) @softworkz
# 0.1.0
## ElectronNET.Core

View File

@@ -14,9 +14,9 @@ Build cross platform desktop applications with .NET 6/8/10 - from console apps t
## Wait - how does that work exactly?
Well... there are lots of different approaches how to get a X-plat desktop app running. Electron.NET provides a range of ways to build .NET based solutions using Electron at the side of presentation.
Well... there are lots of different approaches how to get a X-plat desktop app running. Electron.NET provides a range of ways to build .NET-based solutions using Electron at the side of presentation.
While the classic Electron.NET setup, using an ASP.NET host ran by the Electron side is still the primary way, there's more flexibility now: both, dotnet and Electron are now able to launch the other for better lifetime management, and when you don't need a local web server - like when running content from files or remote servers, you can drop the ASP.NET stack altogether and got with a lightweight console app instead.
While the classic Electron.NET setup (using an ASP.NET host run by the Electron side) is still the primary way, there's more flexibility now. Both .NET and Electron are now able to launch the other for better lifetime management, and when you don't need a local web server (like when running content from files or remote servers), you can drop the ASP.NET stack altogether and go with a lightweight console app instead.
## 📦 NuGet
@@ -58,22 +58,22 @@ To do so, use the `UseElectron` extension method on a `WebApplicationBuilder`, a
using ElectronNET.API;
using ElectronNET.API.Entities;
public static void Main(string[] args)
{
WebHost.CreateDefaultBuilder(args)
.UseElectron(args, ElectronAppReady)
.UseStartup<Startup>()
.Build()
.Run();
}
public static void Main(string[] args)
{
WebHost.CreateDefaultBuilder(args)
.UseElectron(args, ElectronAppReady)
.UseStartup<Startup>()
.Build()
.Run();
}
public static async Task ElectronAppReady()
{
var browserWindow = await Electron.WindowManager.CreateWindowAsync(
new BrowserWindowOptions { Show = false });
public static async Task ElectronAppReady()
{
var browserWindow = await Electron.WindowManager.CreateWindowAsync(
new BrowserWindowOptions { Show = false });
browserWindow.OnReadyToShow += () => browserWindow.Show();
}
browserWindow.OnReadyToShow += () => browserWindow.Show();
}
```
### Minimal API Example
@@ -113,13 +113,64 @@ app.MapRazorPages();
app.Run();
```
### Blazor
For a project with Blazor you can use:
```cs
using ElectronNET.API;
using ElectronNET.API.Entities;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddElectron(); // <-- might be useful to set up DI
builder.UseElectron(args, async () =>
{
var options = new BrowserWindowOptions {
Show = false,
IsRunningBlazor = true, // <-- crucial
};
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux())
options.AutoHideMenuBar = true;
var browserWindow = await Electron.WindowManager.CreateWindowAsync(options);
browserWindow.OnReadyToShow += () => browserWindow.Show();
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
}
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<BlazorApp.Components.App>()
.AddInteractiveWebAssemblyRenderMode();
app.Run();
```
The `IsRunningBlazor` option makes sure to set up the renderer in a way that Blazor can just run without any interference. This includes things such as HMR for development.
## 🚀 Starting and Debugging the Application
Just press `F5` in Visual Studio or use dotnet for debugging.
## 📔 Usage of the Electron API
A complete documentation is available on the Wiki.
Complete documentation is available on the Wiki.
In this YouTube video, we show you how you can create a new project, use the Electron.NET API, debug a application and build an executable desktop app for Windows: [Electron.NET - Getting Started](https://www.youtube.com/watch?v=nuM6AojRFHk)

View File

@@ -0,0 +1,256 @@
# Migration Checks
Electron.NET includes automatic build-time validation checks that help users migrating from previous versions avoid common configuration issues. These checks run automatically during the build process and provide helpful guidance when problems are detected.
## Overview
When you build an Electron.NET project, the following validation checks are performed:
| Code | Check | Description |
|------|-------|-------------|
| [ELECTRON001](#1-packagejson-rules) | package.json location rules | Ensures `package.json`/`package-lock.json` arent present in unsupported locations (root `package.json` handled separately) |
| [ELECTRON008](#1-packagejson-rules) | root package.json contains electron | Warns when root `package.json` contains the word `electron` (case-insensitive) |
| [ELECTRON009](#1-packagejson-rules) | root package.json copied to output | Warns when root `package.json` is configured to be copied to output/publish |
| [ELECTRON002](#2-electron-manifestjson-not-allowed) | electron-manifest.json not allowed | Detects deprecated manifest files |
| [ELECTRON003](#3-electron-builderjson-location) | electron-builder.json location | Verifies electron-builder.json exists in Properties folder |
| [ELECTRON004](#3-electron-builderjson-location) | electron-builder.json wrong location | Warns if electron-builder.json is found in incorrect locations |
| [ELECTRON005](#4-parent-paths-not-allowed-in-electron-builderjson) | Parent paths not allowed | Checks for `..` references in config |
| [ELECTRON006](#5-publish-profile-validation) | ASP.NET publish profile mismatch | Warns when ASP.NET projects have console-style profiles |
| [ELECTRON007](#5-publish-profile-validation) | Console publish profile mismatch | Warns when console projects have ASP.NET-style profiles |
---
## 1. package.json rules
**Warning Codes:** `ELECTRON001`, `ELECTRON008`, `ELECTRON009`
### What is checked
The build system scans for `package.json` and `package-lock.json` files in your project directory.
Rules:
- **ELECTRON001**: `package.json` / `package-lock.json` must not exist in the project directory or subdirectories
- Exception: `ElectronHostHook` folder is allowed
- Note: a **root** `package.json` is **excluded** from `ELECTRON001` and validated by `ELECTRON008` / `ELECTRON009`
- **ELECTRON008**: If a root `package.json` exists, it must **not** contain electron-related dependencies or configuration.
- **ELECTRON009**: If a root `package.json` exists, it must **not** be configured to be copied to output/publish (for example via `CopyToOutputDirectory` / `CopyToPublishDirectory` metadata)
### Why this matters
Electron.NET generates its Electron-related `package.json` during publishing based on MSBuild properties. A user-maintained Electron-related `package.json` can conflict with that process.
Also, ensuring the root `package.json` is not copied prevents accidentally shipping it with the published app.
### Exception
A `package.json` / `package-lock.json` file **is allowed** in the `ElectronHostHook` folder if you're using custom host hooks.
### How to fix
If you have an Electron-related `package.json` from older Electron.NET versions:
1. **Open your project's `.csproj` file**
2. **Add the required properties** to a PropertyGroup with the label `ElectronNetCommon`:
```xml
<PropertyGroup Label="ElectronNetCommon">
<ElectronPackageId>my-electron-app</ElectronPackageId>
<Title>My Electron App</Title>
<Version>1.0.0</Version>
<Description>My awesome Electron.NET application</Description>
<Company>My Company</Company>
<Copyright>Copyright © 2025</Copyright>
<ElectronVersion>30.0.9</ElectronVersion>
</PropertyGroup>
```
3. **Delete** Electron-related `package.json` / `package-lock.json` files (except those under `ElectronHostHook` if applicable)
If you keep a root `package.json` for non-Electron reasons:
- Ensure it does **not** contain electron dependencies or configuration (fixes `ELECTRON008`)
- Ensure it is **not** copied to output/publish (fixes `ELECTRON009`)
> **See also:** [Migration Guide](Migration-Guide.md) for complete migration instructions.
---
## 2. electron-manifest.json not allowed
**Warning Code:** `ELECTRON002`
### What is checked
The build system checks for the presence of `electron.manifest.json` or `electron-manifest.json` files in your project.
### Why this matters
The `electron.manifest.json` file format is deprecated. All configuration should now be specified using:
- MSBuild properties in your `.csproj` file (for application metadata)
- The `electron-builder.json` file in the `Properties` folder (for build configuration)
### How to fix
1. **Migrate application properties** to your `.csproj` file (see [Migration Guide](Migration-Guide.md))
2. **Move the `build` section** from `electron.manifest.json` to `Properties/electron-builder.json`
3. **Delete the old `electron.manifest.json`** file
**Example electron-builder.json:**
```json
{
"compression": "maximum",
"win": {
"icon": "Assets/app.ico",
"target": ["nsis", "portable"]
},
"linux": {
"icon": "Assets/app.png",
"target": ["AppImage", "deb"]
},
"mac": {
"icon": "Assets/app.icns",
"target": ["dmg", "zip"]
}
}
```
---
## 3. electron-builder.json Location
**Warning Codes:** `ELECTRON003`, `ELECTRON004`
### What is checked
- `ELECTRON003`: Verifies that an `electron-builder.json` file exists in the `Properties` folder
- `ELECTRON004`: Warns if `electron-builder.json` is found in incorrect locations
### Why this matters
The `electron-builder.json` file must be located in the `Properties` folder so it can be properly copied to the output directory during publishing.
### How to fix
1. **Create the Properties folder** if it doesn't exist
2. **Move or create** `electron-builder.json` in `Properties/electron-builder.json`
3. **Remove** any `electron-builder.json` files from other locations
**Expected structure:**
```
MyProject/
├── Properties/
│ ├── electron-builder.json ✅ Correct location
│ ├── launchSettings.json
│ └── PublishProfiles/
├── MyProject.csproj
└── Program.cs
```
---
## 4. Parent paths not allowed in electron-builder.json
**Warning Code:** `ELECTRON005`
### What is checked
The build system scans the `electron-builder.json` file for parent-path references (`..`).
### Why this matters
During the publish process, the `electron-builder.json` file is copied to the build output directory. Any relative paths in this file are resolved from that location, not from your project directory. Parent-path references (`../`) will not work correctly because they would point outside the published application.
### How to fix
1. **Move resource files** (icons, installers, etc.) inside your project folder structure
2. **Configure the files** to be copied to the output directory in your `.csproj`:
```xml
<ItemGroup>
<Content Include="Assets\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
```
3. **Update paths** in `electron-builder.json` to use relative paths without `..`:
**Before (incorrect):**
```json
{
"win": {
"icon": "../SharedAssets/app.ico"
}
}
```
**After (correct):**
```json
{
"win": {
"icon": "Assets/app.ico"
}
}
```
---
## 5. Publish Profile Validation
**Warning Codes:** `ELECTRON006`, `ELECTRON007`
### What is checked
The build system examines `.pubxml` files in the `Properties/PublishProfiles` folder and validates that they match the project type:
- **ELECTRON006**: For **ASP.NET projects** (using `Microsoft.NET.Sdk.Web`), checks that publish profiles include `WebPublishMethod`. This property is required for proper ASP.NET publishing.
- **ELECTRON007**: For **console/other projects** (not using the Web SDK), checks that publish profiles do NOT include the `WebPublishMethod` property. These ASP.NET-specific properties are incorrect for non-web applications.
### Why this matters
Electron.NET supports both ASP.NET and console application project types, each requiring different publish profile configurations:
| Project Type | SDK | Expected Properties |
|--------------|-----|---------------------|
| ASP.NET (Razor Pages, MVC, Blazor) | `Microsoft.NET.Sdk.Web` | `WebPublishMethod`, no `PublishProtocol` |
| Console Application | `Microsoft.NET.Sdk` | `PublishProtocol`, no `WebPublishMethod` |
Using the wrong publish profile type can lead to incomplete or broken builds.
### How to fix
1. **Delete existing publish profiles** from `Properties/PublishProfiles/`
2. **Create new publish profiles** using the Visual Studio Publishing Wizard:
- Right-click on the project in Solution Explorer
- Select **Publish...**
- Follow the wizard to create a **Folder** publish profile
For correct publish profile examples for both ASP.NET and Console applications, see **[Package Building](../Using/Package-Building.md#step-1-create-publish-profiles)**.
---
## Disabling Migration Checks
If you need to disable specific migration checks (not recommended), you can set the following properties in your `.csproj` file:
```xml
<PropertyGroup>
<!-- Disable all migration checks -->
<ElectronSkipMigrationChecks>true</ElectronSkipMigrationChecks>
</PropertyGroup>
```
> ⚠️ **Warning:** Disabling migration checks may result in build or runtime errors. Only disable checks if you fully understand the implications.
---
## See Also
- [Migration Guide](Migration-Guide.md) - Complete step-by-step migration instructions
- [Advanced Migration Topics](Advanced-Migration-Topics.md) - Complex migration scenarios
- [Configuration](../Using/Configuration.md) - Project configuration options
- [Package Building](../Using/Package-Building.md) - Building distributable packages

View File

@@ -31,7 +31,7 @@ dotnet add package ElectronNET.Core.AspNet # For ASP.NET projects
### Step 2: Configure Project Settings
**Auto-generated Configuration:**
ElectronNET.Core automatically creates `electron-builder.json` during the first build or NuGet restore. No manual configuration is needed for basic setups.
ElectronNET.Core automatically creates `electron-builder.json` in the `Properties` folder of your project during the first build or NuGet restore. No manual configuration is needed for basic setups.
**Migrate Existing Configuration:**
If you have an existing `electron.manifest.json` file:
@@ -63,6 +63,9 @@ You can also manually edit `electron-builder.json`:
}
```
**Modify Launch Settings:**
ElectronNET.Core no longer needs a separate CLI tool (electronize.exe) for launching. You should update your launch settings to use either the ASP.NET-first or Electron-first approach. See [Debugging](../Using/Debugging.md) for details.
## 🎯 Testing Migration
After completing the migration steps:

View File

@@ -54,7 +54,7 @@ Add the Electron.NET configuration to your `.csproj` file:
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ElectronNET.Core" Version="0.1.0" />
<PackageReference Include="ElectronNET.Core" Version="0.4.1" />
</ItemGroup>
```

View File

@@ -0,0 +1,583 @@
# Electron.NET SignalR Authentication Guide
This guide explains the token-based authentication system implemented for SignalR mode in Electron.NET, designed to protect applications in multi-user environments.
## Table of Contents
1. [Overview](#overview)
2. [Threat Model](#threat-model)
3. [Authentication Architecture](#authentication-architecture)
4. [Implementation Details](#implementation-details)
5. [Security Properties](#security-properties)
6. [Troubleshooting](#troubleshooting)
7. [FAQ](#faq)
## Overview
Electron.NET's SignalR mode includes built-in authentication to ensure that only the Electron process spawned by a specific .NET instance can connect to that instance's HTTP and SignalR endpoints.
**When is this important?**
- Multi-user Windows Server environments (Terminal Services, RDP)
- Shared development machines with multiple users
- Any scenario where multiple users run the same application simultaneously
**What does it protect against?**
- User A's Electron process connecting to User B's .NET backend
- Unauthorized port scanning and connection attempts from other users
- Accidental misconfigurations causing cross-user connections
## Threat Model
### The Problem
When multiple users run the same Electron.NET application on a shared server:
1. Each .NET process binds to a TCP port (e.g., `http://localhost:58971`)
2. TCP ports are visible to **all users** on the machine
3. Without authentication, User A's Electron could connect to User B's backend
```
┌─────────────────────────────────────────────────┐
│ Windows Server (Terminal Services) │
├─────────────────────────────────────────────────┤
│ User A Session │
│ ├─ .NET Process (localhost:58971) │
│ └─ Electron Process │
│ │
│ User B Session │
│ ├─ .NET Process (localhost:61234) │
│ └─ Electron Process (could connect to 58971) │ ❌ Prevent this!
└─────────────────────────────────────────────────┘
```
### The Solution
Each .NET process generates a unique authentication token when launching Electron. Only requests with the correct token are allowed to connect.
```
┌─────────────────────────────────────────────────┐
│ User A Session │
│ ├─ .NET (token: abc123...) │
│ └─ Electron (has token abc123...) │ ✅ Authenticated
│ │
│ User B Session │
│ ├─ .NET (token: xyz789...) │
│ └─ Electron (has token xyz789...) │ ✅ Authenticated
│ │
│ ❌ User B's Electron → User A's .NET │
│ (lacks token abc123...) │ ❌ Rejected (401)
└─────────────────────────────────────────────────┘
```
## Authentication Architecture
### Flow Diagram
```
┌──────────────┐ ┌─────────────────┐
│ .NET Process │ │ Electron Process│
└──────┬───────┘ └────────┬────────┘
│ │
│ 1. Generate Token (GUID) │
│ Token: a3f8b2c1d4e5... │
│ │
│ 2. Launch Electron │
│ --authtoken=a3f8b2c1d4e5... │
│────────────────────────────────────────────────────>│
│ │
│ │ 3. Extract Token
│ │ global.authToken = ...
│ │
│ │ 4. Initial Page Load
│<────────────────────────────────────────────────────│
│ GET /?token=a3f8b2c1d4e5... │
│ │
│ 5. Validate Token │
│ ✓ Valid → Set Cookie │
│────────────────────────────────────────────────────>│
│ HTTP 200 + Set-Cookie: ElectronAuth=... │
│ │
│ │ 6. SignalR Connection
│<────────────────────────────────────────────────────│
│ GET /electron-hub?token=a3f8b2c1d4e5... │
│ │
│ 7. Validate Token, Set Cookie │
│────────────────────────────────────────────────────>│
│ HTTP 200 + Set-Cookie │
│ │
│ │ 8. Subsequent Requests
│<────────────────────────────────────────────────────│
│ GET /api/data │
│ Cookie: ElectronAuth=a3f8b2c1d4e5... │
│ │
│ 9. Validate Cookie │
│────────────────────────────────────────────────────>│
│ HTTP 200 (authenticated) │
│ │
```
### Key Steps
1. **Token Generation**: .NET generates 128-bit cryptographic random GUID
2. **Command-Line Passing**: Token passed to Electron via `--authtoken` parameter
3. **Token Extraction**: Electron stores token in `global.authToken`
4. **URL Appending**: Token appended to initial page and SignalR connection URLs
5. **Middleware Validation**: Every HTTP request validated by middleware
6. **Cookie Setting**: Valid token results in secure HttpOnly cookie
7. **Cookie-Based Requests**: Subsequent requests use cookie (no token in URL)
## Implementation Details
### 1. Token Generation (.NET)
**File**: `src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs`
```csharp
private void LaunchElectron()
{
// Generate secure authentication token (128-bit cryptographic random GUID)
this.authenticationToken = Guid.NewGuid().ToString("N"); // 32 hex chars
// Register token with authentication service
this.authenticationService.SetExpectedToken(this.authenticationToken);
// Launch Electron with token
var args = $"--unpackeddotnetsignalr --electronurl={this.actualUrl} --authtoken={this.authenticationToken}";
this.electronProcess = new ElectronProcessActive(isUnPacked, ElectronNetRuntime.ElectronExecutable, args, this.port.Value);
_ = this.electronProcess.Start();
}
```
**Token Format**: 32-character hexadecimal string (e.g., `a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5`)
### 2. Authentication Service (.NET)
**File**: `src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs`
```csharp
public class ElectronAuthenticationService : IElectronAuthenticationService
{
private string _expectedToken;
private readonly object _lock = new object();
public void SetExpectedToken(string token)
{
lock (_lock)
{
_expectedToken = token;
}
}
public bool ValidateToken(string token)
{
if (string.IsNullOrEmpty(token))
return false;
lock (_lock)
{
if (string.IsNullOrEmpty(_expectedToken))
return false;
// Constant-time comparison prevents timing attacks
return ConstantTimeEquals(token, _expectedToken);
}
}
private static bool ConstantTimeEquals(string a, string b)
{
if (a == null || b == null || a.Length != b.Length)
return false;
var result = 0;
for (int i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}
}
```
**Key Features**:
- Thread-safe with lock
- Constant-time comparison (prevents timing attacks)
- Singleton lifetime (one per .NET instance)
### 3. Authentication Middleware (.NET)
**File**: `src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs`
```csharp
public async Task InvokeAsync(HttpContext context)
{
// Check if authentication cookie exists
var authCookie = context.Request.Cookies["ElectronAuth"];
if (!string.IsNullOrEmpty(authCookie))
{
if (_authService.ValidateToken(authCookie))
{
await _next(context);
return;
}
else
{
_logger.LogWarning("Invalid cookie for path {Path}", context.Request.Path);
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized: Invalid authentication");
return;
}
}
// No cookie - check for token in query string
var token = context.Request.Query["token"].ToString();
if (!string.IsNullOrEmpty(token) && _authService.ValidateToken(token))
{
// Valid token - set cookie for future requests
context.Response.Cookies.Append("ElectronAuth", token, new CookieOptions
{
HttpOnly = true,
SameSite = SameSiteMode.Strict,
Path = "/",
Secure = false, // localhost is HTTP
IsEssential = true
});
await _next(context);
return;
}
// Reject - no valid cookie or token
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized: Authentication required");
}
```
**Middleware Order** (IMPORTANT):
```csharp
app.UseMiddleware<ElectronAuthenticationMiddleware>(); // ← FIRST
app.UseRouting();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapHub<ElectronHub>("/electron-hub");
app.MapRazorComponents<App>();
```
### 4. Token Extraction (Electron)
**File**: `src/ElectronNET.Host/main.js`
```javascript
// Extract authentication token from command-line
if (app.commandLine.hasSwitch('authtoken')) {
global.authToken = app.commandLine.getSwitchValue('authtoken');
}
```
### 5. Token in URLs (Electron)
**Initial Page Load** (`src/ElectronNET.Host/api/browserWindows.js`):
```javascript
// Append token to window URL
if (global.authToken) {
const separator = electronUrl.includes('?') ? '&' : '?';
electronUrl = `${electronUrl}${separator}token=${global.authToken}`;
}
window.loadURL(electronUrl);
```
**SignalR Connection** (`src/ElectronNET.Host/api/signalr-bridge.js`):
```javascript
async connect() {
// Append token to SignalR hub URL
const connectionUrl = this.authToken ?
`${this.hubUrl}?token=${this.authToken}` :
this.hubUrl;
this.connection = new signalR.HubConnectionBuilder()
.withUrl(connectionUrl)
.build();
await this.connection.start();
}
```
### 6. Service Registration (Application)
**File**: Your application's `Program.cs`
```csharp
var builder = WebApplication.CreateBuilder(args);
// Register authentication service as singleton
builder.Services.AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>();
builder.Services.AddElectron();
var app = builder.Build();
// Register middleware BEFORE UseRouting()
app.UseMiddleware<ElectronAuthenticationMiddleware>();
app.UseRouting();
app.MapHub<ElectronHub>("/electron-hub");
app.Run();
```
## Security Properties
### Cookie Configuration
| Property | Value | Purpose |
|----------|-------|---------|
| `HttpOnly` | `true` | Prevents JavaScript access (XSS protection) |
| `SameSite` | `Strict` | Prevents CSRF attacks |
| `Path` | `/` | Cookie sent with all requests |
| `Secure` | `false` | Cannot use on localhost HTTP |
| `IsEssential` | `true` | Required for app to function |
| **Lifetime** | Session | Expires when Electron closes |
### Token Properties
- **Entropy**: 128 bits (2^128 possible values)
- **Format**: 32 hexadecimal characters (GUID without hyphens)
- **Generation**: `Guid.NewGuid()` uses cryptographically secure RNG
- **Lifetime**: Entire application session
- **Uniqueness**: Each .NET instance generates unique token
### What's Protected
**All HTTP endpoints**:
- Blazor Server pages (`/`, `/counter`, etc.)
- Static files (`/css/app.css`, `/js/script.js`)
- API endpoints (custom controllers)
- SignalR hub (`/electron-hub`)
**Both transport modes**:
- Initial token-based authentication (query parameter)
- Cookie-based subsequent requests
**Cross-user isolation**:
- Different users = different tokens
- Invalid token = 401 Unauthorized
### What's NOT Protected Against
**Same-user attacks** (by design):
- Process memory inspection
- Debugger attachment
- Command-line parameter visibility
**Rationale**: A malicious process running as the same user already has full access to:
- Process memory (can read token from RAM)
- Cookies (stored in Electron's data directory)
- All files owned by the user
Token-based authentication focuses on **cross-user isolation**, not same-user security.
## Troubleshooting
### Problem: 401 Unauthorized on Initial Load
**Symptoms**:
```
GET / HTTP/1.1
Response: 401 Unauthorized
```
**Possible Causes**:
1. Token not passed to Electron
2. Token not appended to URL
3. Middleware rejecting valid token
**Debugging Steps**:
1. Check Electron command-line:
```javascript
console.log('Auth Token:', global.authToken);
```
Should print 32-character hex string.
2. Check URL in browser window:
```javascript
console.log('Loading URL:', electronUrl);
```
Should include `?token=<guid>` parameter.
3. Check .NET logs:
```
[Warning] Authentication failed: Invalid token (prefix: a3f8b2c1...)
```
4. Verify middleware registration:
```csharp
app.UseMiddleware<ElectronAuthenticationMiddleware>(); // Before UseRouting()
```
### Problem: SignalR Connection Fails
**Symptoms**:
```
[SignalRBridge] Authentication failed: The authentication token is invalid or missing.
```
**Possible Causes**:
1. Token not passed to `SignalRBridge` constructor
2. Token not appended to hub URL
3. Cookie not being sent
**Debugging Steps**:
1. Check token passed to SignalRBridge:
```javascript
console.log('SignalRBridge token:', this.authToken);
```
2. Check connection URL:
```javascript
console.log('Hub URL:', connectionUrl);
```
Should be `http://localhost:PORT/electron-hub?token=<guid>`.
3. Enable verbose logging:
```javascript
.configureLogging(signalR.LogLevel.Debug)
```
### Problem: Cookie Not Persisting
**Symptoms**: Every request includes token in URL, cookie never set.
**Possible Causes**:
1. Cookie settings incompatible with browser
2. Middleware not setting cookie
3. Response headers not sent
**Debugging Steps**:
1. Check response headers:
```
Set-Cookie: ElectronAuth=a3f8b2c1...; Path=/; HttpOnly; SameSite=Strict
```
2. Check subsequent requests:
```
Cookie: ElectronAuth=a3f8b2c1...
```
3. Enable middleware logging:
```csharp
_logger.LogInformation("Setting cookie for path {Path}", path);
```
### Problem: Token Visible in Process List
**Observation**:
```powershell
wmic process where "name='electron.exe'" get commandline
...--authtoken=a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5...
```
**Is this a problem?** No, by design.
**Explanation**:
- Command-line parameters are visible to same-user processes
- This is acceptable because:
- Same user already has access to process memory
- Same user can read cookies from Electron's data directory
- Token-based auth protects against **other users**, not same-user processes
**If you need same-user protection**: Use OS-level access controls (file permissions, process isolation) or consider named pipes with ACLs.
## FAQ
### Q: Why use tokens instead of Process ID validation?
**A**: PIDs can be recycled and reused, making validation unreliable. Additionally:
- PIDs don't provide cryptographic security
- Parent-child validation is platform-specific
- Adds complexity without meaningful security benefit
Token-based authentication provides:
- Cryptographic randomness (128-bit entropy)
- Simple cross-platform implementation
- No race conditions or PID recycling issues
### Q: Why not use HTTPS with certificates?
**A**: Localhost doesn't support HTTPS certificates easily:
- Self-signed certificates trigger browser warnings
- Certificate management adds complexity
- Token-based auth provides equivalent security for localhost IPC
### Q: Can I disable authentication?
**A**: Not recommended, but possible by removing middleware registration:
```csharp
// Remove this line:
// app.UseMiddleware<ElectronAuthenticationMiddleware>();
```
**Warning**: Only do this if:
- Application runs on single-user machines only
- No Terminal Services / RDP access
- You understand the security implications
### Q: Does this work with hot reload?
**A**: Yes, cookie persists across hot reload as long as Electron process keeps running.
### Q: What about multiple Electron windows?
**A**: All windows in the same Electron process share cookies automatically. Authentication works seamlessly across multiple windows.
### Q: How do I test authentication?
**Test 1 - Happy Path**:
1. Run application normally
2. Check logs for "Authentication successful"
3. Verify cookie is set (DevTools → Application → Cookies)
4. Subsequent requests should not include token in URL
**Test 2 - Invalid Token**:
1. Modify token in browser URL: `?token=invalid`
2. Should receive 401 Unauthorized
3. Check logs for "Authentication failed: Invalid token"
**Test 3 - No Token**:
1. Open browser manually to `http://localhost:PORT/`
2. Should receive 401 Unauthorized
3. Check logs for "Authentication failed: No cookie or token"
**Test 4 - Multi-User** (Windows Server/Terminal Services):
1. Launch app as User A
2. In User B session, try to connect to User A's port
3. Should receive 401 Unauthorized
### Q: What about packaged applications?
**A**: Authentication works identically in packaged mode. The `--authtoken` parameter is included in the packaged Electron executable.
### Q: Can I customize the cookie name?
**A**: Yes, modify `AuthCookieName` constant in `ElectronAuthenticationMiddleware.cs`:
```csharp
private const string AuthCookieName = "MyCustomCookieName";
```
## Summary
Electron.NET's token-based authentication provides:
**Security**: 128-bit entropy, constant-time comparison, secure cookies
**Simplicity**: Automatic token generation and validation
**Compatibility**: Works with Blazor, SignalR, and static files
**Monitoring**: Structured logging for security events
**Multi-User**: Cross-user isolation on shared servers
The authentication system is **enabled by default** in SignalR mode and requires minimal configuration. For most applications, simply register the services and middleware - everything else happens automatically.
For additional help or questions, see the [SignalR Implementation Summary](SignalR-Implementation-Summary.md) or open an issue on GitHub.

View File

@@ -0,0 +1,450 @@
# SignalR Implementation Summary
This document summarizes the completed implementation of SignalR-based bidirectional communication in Electron.NET as an alternative to Socket.IO.
## Overview
The SignalR implementation provides a modern, .NET-native alternative to Socket.IO for communication between the ASP.NET Core host and the Electron process. This new startup mode was designed specifically for **Blazor Server applications** where ASP.NET Core and Electron need tighter integration and lifecycle control.
**Key Innovation**: .NET-first startup with dynamic port assignment - ASP.NET Core starts first, binds to port 0 (letting Kestrel choose an available port), then launches Electron with the actual URL.
## Primary Use Case
Blazor Server applications where:
- ASP.NET Core owns the application lifecycle
- Dynamic port binding is needed (no fixed port configuration)
- Modern SignalR infrastructure is preferred over Socket.IO
- Single process debugging is desired (.NET process controls Electron)
## Implementation Phases (All Complete)
### Phase 1: Core Infrastructure ✅
- Added new `StartupMethod` enum values:
- `UnpackagedDotnetFirstSignalR`
- `PackagedDotnetFirstSignalR`
- Created `ElectronHub` SignalR hub for bidirectional communication
- Registered hub endpoint at `/electron-hub` (separate from Blazor's `/_blazor` hub)
### Phase 2: Runtime Controller ✅
- Created `RuntimeControllerAspNetDotnetFirstSignalR`
- Implemented logic to:
- Bind Kestrel to port 0
- Wait for Kestrel startup and capture actual port via `IServerAddressesFeature`
- Launch Electron with `--electronurl` parameter
- Wait for SignalR connection from Electron
- Transition to Ready state when connected
### Phase 3: Electron/Node.js Side ✅
- Added `@microsoft/signalr` npm package dependency
- Created SignalR connection module (`signalr-bridge.js`)
- Updated `main.js` to detect SignalR modes and connect to `/electron-hub`
- Implemented Socket.IO-compatible interface for API compatibility
### Phase 4: API Bridge Adaptation ✅
- Created `SignalRFacade` implementing `IFacade` interface
- Ensured existing Electron API classes work with SignalR
- Implemented type conversion helper for SignalR's JSON deserialization
- Event routing from both directions (.NET ↔ Electron)
### Phase 5: Configuration & Extensions ✅
- Updated `WebHostBuilderExtensions` for automatic SignalR configuration
- Added startup mode detection via command-line flags
- Configured dynamic port binding (port 0) for SignalR modes
- Integrated with `UseElectron()` API for seamless usage
### Phase 6: Testing & Fixes ✅
- Created sample Blazor Server application
- Fixed multiple critical issues discovered during integration testing
- Cleaned up debug logging
- Added comprehensive code documentation
## Key Components
### 1. SignalRFacade (`src/ElectronNET.AspNet/Bridge/SignalRFacade.cs`)
- Implements `IFacade` interface to match Socket.IO facade API
- Handles bidirectional event routing using `IHubContext<ElectronHub>`
- Includes `ConvertToType<T>` helper for handling SignalR's JSON deserialization quirks
- Critical fix: Handles `JsonElement` and numeric type conversions (long → int)
### 2. ElectronHub (`src/ElectronNET.AspNet/Hubs/ElectronHub.cs`)
- SignalR hub for .NET ↔ Electron communication
- Key methods:
- `RegisterElectronClient()` - Called by Electron on connection
- `ElectronEvent(string, object[])` - Receives events from Electron
- Connection/disconnection handlers notify runtime controller
### 3. RuntimeControllerAspNetDotnetFirstSignalR
- Manages SignalR mode lifecycle
- Critical flow:
1. Wait for ASP.NET server to start
2. Capture dynamic port from `IServerAddressesFeature`
3. Update `ElectronNetRuntime.AspNetWebPort` with actual port
4. Launch Electron with `--electronurl` parameter
5. Wait for `electron-host-ready` signal before calling app ready callback
### 4. SignalRBridge (`src/ElectronNET.Host/api/signalr-bridge.js`)
- JavaScript SignalR client that mimics Socket.IO interface
- Provides `on()` and `emit()` methods for API compatibility
- Critical fix: Event args passed as arrays, spread when calling handlers
- Uses `@microsoft/signalr` npm package
### 5. Main.js Startup (`src/ElectronNET.Host/main.js`)
- Detects SignalR mode via `--unpackeddotnetsignalr` or `--dotnetpackedsignalr` flags
- Creates invisible keep-alive window (destroyed when first real window is created)
- Loads API modules then signals `electron-host-ready` to .NET
## Usage
Enable SignalR mode by passing the appropriate command-line flag:
```bash
# Unpacked mode (development)
dotnet run --unpackeddotnetsignalr
# Packed mode (production)
dotnet run --dotnetpackedsignalr
```
Or set environment variable (deprecated, flags preferred):
```bash
ELECTRON_USE_SIGNALR=true
```
In your ASP.NET Core Program.cs:
```csharp
var builder = WebApplication.CreateBuilder(args);
// Add Electron.NET services
builder.Services.AddElectron();
// Configure Electron with SignalR mode
builder.WebHost.UseElectron(args, async () =>
{
var window = await Electron.WindowManager.CreateWindowAsync();
window.OnReadyToShow += () => window.Show();
});
var app = builder.Build();
app.UseRouting();
// Map SignalR hub for Electron communication
app.MapHub<ElectronHub>("/electron-hub");
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
app.Run();
```
**Note**: `UseElectron()` automatically detects SignalR mode and configures everything. The rest happens automatically:
1. Port 0 binding (dynamic port assignment)
2. Electron launch with actual URL
3. SignalR connection establishment (WebSockets enabled automatically by MapHub)
4. App ready callback execution
2. Electron launch with actual URL
3. SignalR connection establishment (WebSockets enabled automatically by `MapHub`)
4. App ready callback execution
## Architecture Decisions
### Why .NET-First Startup?
SignalR mode uses .NET-first startup (vs. Electron-first in Socket.IO mode) because:
1. **No port scanning needed** - .NET can pass the actual URL to Electron
2. **SignalR hub must be registered** before Electron connects
3. **Simpler lifecycle** - ASP.NET controls when Electron launches
4. **Better for Blazor Server** - Blazor is already running when Electron starts
5. **Single process debugging** - Developer debugs .NET process which owns Electron
### Why IFacade Interface?
Introducing `IFacade` allows `BridgeConnector.Socket` to return either `SocketIOFacade` or `SignalRFacade` based on startup mode, ensuring existing API code works with both transport mechanisms without modification.
### Why Keep-Alive Window?
Electron quits immediately on macOS if no windows exist. The keep-alive window ensures Electron stays running during the connection and API initialization phase. It's automatically destroyed when the first real window is created.
### Why 'electron-host-ready' Signal?
Without this signal, .NET would call the app ready callback before Electron finished loading API modules, causing API calls to fail. The signal ensures proper initialization order:
1. Electron connects to SignalR
2. Electron loads all API modules (browserWindows, dialog, menu, etc.)
3. Electron signals `electron-host-ready`
4. .NET calls app ready callback
5. App code can safely use Electron APIs
## Blazor Server Considerations
### SignalR Hub Coexistence
Blazor Server already uses SignalR for component communication (`/_blazor` hub). Our implementation:
- Uses separate endpoint (`/electron-hub`) to avoid conflicts
- Both hubs coexist on the same Kestrel server without interference
- No impact on Blazor's reconnection logic
- Compatible with hot reload scenarios
### Lifecycle Integration
- Electron window creation happens **after** Blazor app is ready
- `UseElectron()` callback fires when SignalR hub is connected
- Blazor components can inject Electron services to control windows
- Proper disposal when Electron process exits
### Development Experience
- Hot reload works for both Blazor and Electron integration
- F5 debugging works seamlessly
- No need to manually coordinate ports
- Single process to debug (.NET process owns the lifecycle)
## Critical Fixes Applied
### 1. Race Condition: API Module Loading
**Problem**: .NET called app ready callback before Electron finished loading API modules.
**Solution**: Electron signals `electron-host-ready` after loading all API modules. .NET waits for this signal before calling the app ready callback.
### 2. Event Argument Mismatch
**Problem**: SignalR sent event data as nested arrays `[[data]]` instead of `[data]`.
**Solution**:
- C#: Use explicit `object[] args` parameter (not `params`)
- JS: Always pass args as array: `invoke('ElectronEvent', eventName, args)`
- JS: Spread args when calling handlers: `handler(...argsArray)`
### 3. Type Conversion Failures
**Problem**: SignalR deserializes JSON numbers as `JsonElement` or `long`, causing `Once<int>` handlers to fail silently.
**Solution**: `SignalRFacade.ConvertToType<T>` handles JsonElement deserialization and numeric conversions.
### 4. Window Shutdown Not Triggering Exit
**Problem**: Keep-alive window prevented `window-all-closed` event from firing.
**Solution**: Destroy keep-alive window when first real window is created using `app.once('browser-window-created')`.
### 5. Dynamic Port Not Propagated
**Problem**: When using port 0, Kestrel assigns a dynamic port, but `ElectronNetRuntime.AspNetWebPort` was not updated.
**Solution**: Update `AspNetWebPort` after capturing port from `IServerAddressesFeature` in `CapturePortAndLaunchElectron()`.
### 7. Blazor Static Files Not Loading
**Problem**: Blazor CSS and framework files returned 404 errors.
**Solution**:
- Added `app.UseStaticFiles()` to serve wwwroot content
- Fixed middleware order: `UseAntiforgery()` must be between `UseRouting()` and `UseEndpoints()`
- Updated scoped CSS asset reference to use lowercase name matching .NET 9+ convention
## Authentication & Security
### Token-Based Authentication (Multi-User Protection)
SignalR mode includes built-in authentication to prevent unauthorized connections in multi-user scenarios (e.g., Windows Server with Terminal Services/RDP).
**Threat Model**: On shared servers, multiple users can run the same application simultaneously. Without authentication, User A's Electron process could potentially connect to User B's ASP.NET backend.
**Solution**: Token-based authentication with secure cookies.
### Authentication Flow
1. **.NET generates token**: When launching Electron, `RuntimeControllerAspNetDotnetFirstSignalR` generates a cryptographically secure GUID (128-bit entropy)
2. **Token passed via command-line**: Electron receives `--authtoken=<guid>` parameter
3. **Token appended to URLs**:
- Initial page load: `http://localhost:PORT/?token=<guid>`
- SignalR connection: `http://localhost:PORT/electron-hub?token=<guid>`
4. **Middleware validates token**: `ElectronAuthenticationMiddleware` checks every HTTP request
5. **Cookie set on first request**: After successful token validation, secure HttpOnly cookie is set
6. **Subsequent requests use cookie**: No token in URLs after initial authentication
### Security Properties
- **Cookie Settings**:
- `HttpOnly`: true (prevents JavaScript access, XSS protection)
- `SameSite`: Strict (prevents CSRF)
- `Path`: / (applies to all routes)
- `Secure`: false (localhost is HTTP, not HTTPS)
- `IsEssential`: true (required for app to function)
- **Lifetime**: Session scope (expires when Electron closes)
- **Token Validation**:
- Constant-time string comparison (prevents timing attacks)
- Token stored in singleton service (one per .NET instance)
- Never logged in full (only first 8 characters for debugging)
- **Protection Scope**:
- All HTTP endpoints (Blazor pages, static files, API calls)
- SignalR hub connection (negotiate and all hub traffic)
- Both initial request and cookie-based requests validated
### What This Protects Against
**Protected**:
- Cross-user connections (User A → User B's backend)
- Port scanning attacks from other users
- Accidental connections from misconfigured processes
**NOT Protected Against** (By Design):
- Malicious same-user processes with debugger access
- Process memory inspection tools (same privilege level)
- Command-line parameter visibility (same user can see all processes)
**Rationale**: Same-user attacks already have full access to process memory, files, and cookies. Token-based authentication focuses on cross-user isolation, which is the primary threat in multi-user environments.
### Implementation Components
1. **IElectronAuthenticationService** (`src/ElectronNET.AspNet/Services/`)
- Singleton service storing expected token
- Thread-safe with lock-based validation
- Constant-time comparison to prevent timing attacks
2. **ElectronAuthenticationMiddleware** (`src/ElectronNET.AspNet/Middleware/`)
- Validates every HTTP request before routing
- Checks cookie first, then token query parameter
- Sets cookie on first valid token
- Returns 401 for invalid/missing authentication
- Structured logging for security monitoring
3. **Token Generation** (`RuntimeControllerAspNetDotnetFirstSignalR.cs`)
- `Guid.NewGuid().ToString("N")` = 32 hex characters
- Called in `LaunchElectron()` method
- Registered with authentication service immediately
4. **Electron Integration** (`main.js`, `signalr-bridge.js`)
- Extracts token from `--authtoken` parameter
- Stores in `global.authToken` for module access
- Appends to browser window URL and SignalR connection URL
### Usage in Custom Applications
Authentication is **enabled by default** in SignalR mode. No additional configuration required beyond service registration:
```csharp
var builder = WebApplication.CreateBuilder(args);
// Register authentication service (singleton)
builder.Services.AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>();
builder.Services.AddElectron();
var app = builder.Build();
// Register middleware BEFORE UseRouting()
app.UseMiddleware<ElectronAuthenticationMiddleware>();
app.UseRouting();
app.MapHub<ElectronHub>("/electron-hub");
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
app.Run();
```
The rest is automatic:
- Token generation happens when Electron launches
- Token validation happens on every request
- Cookie management is handled by the middleware
### Logging & Monitoring
Authentication events are logged with structured logging:
**Successful authentication**:
```
[Information] Authentication successful: Setting cookie for path /
```
**Failed authentication**:
```
[Warning] Authentication failed: Invalid token (prefix: a3f8b2c1...) for path / from 127.0.0.1
[Warning] Authentication failed: No cookie or token provided for path /api/data from 127.0.0.1
[Warning] Authentication failed: Invalid cookie for path /_blazor from 127.0.0.1
```
**SignalR connection failures**:
```
[SignalRBridge] Authentication failed: The authentication token is invalid or missing.
[SignalRBridge] Please ensure the --authtoken parameter is correctly passed to Electron.
```
Log failed authentication attempts for security monitoring and troubleshooting.
### Testing Multi-User Scenarios
To test authentication in multi-user environments:
1. **Run as different Windows users**:
```powershell
# User A session
dotnet run
# User B session (different RDP/Terminal Services session)
dotnet run
```
2. **Verify isolation**: User A's Electron cannot access User B's backend
3. **Check logs**: Failed auth attempts should be logged
4. **Monitor tokens**: Each instance generates unique token
For development testing on single-user machines, simulate by running multiple instances and attempting to connect with wrong/missing tokens.
## Backward Compatibility
This is a **new optional startup mode** - all existing modes continue to work unchanged:
- `PackagedElectronFirst` - unchanged
- `PackagedDotnetFirst` - unchanged
- `UnpackedElectronFirst` - unchanged
- `UnpackedDotnetFirst` - unchanged
Existing applications do not need to change. SignalR mode is opt-in via command-line flags.
## File Changes Summary
**New Files**:
- `src/ElectronNET.AspNet/Bridge/SignalRFacade.cs` (225 lines)
- `src/ElectronNET.AspNet/Hubs/ElectronHub.cs` (108 lines)
- `src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs` (163 lines)
- `src/ElectronNET.AspNet/Services/IElectronAuthenticationService.cs` (20 lines)
- `src/ElectronNET.AspNet/Services/ElectronAuthenticationService.cs` (65 lines)
- `src/ElectronNET.AspNet/Middleware/ElectronAuthenticationMiddleware.cs` (105 lines)
- `src/ElectronNET.Host/api/signalr-bridge.js` (125 lines)
**Modified Files**:
- `src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs` - Added SignalR service registration
- `src/ElectronNET.Host/main.js` - Added SignalR startup flow and token extraction
- `src/ElectronNET.Host/api/browserWindows.js` - Token appended to window URLs
- `src/ElectronNET.Host/package.json` - Added `@microsoft/signalr` dependency
- `src/ElectronNET.Samples.BlazorSignalR/Program.cs` - Sample with authentication
**Total Changes**: ~1,220 lines added
## Testing Recommendations
1. Test with dynamic port (port 0) to ensure URL propagation works
2. Verify window-all-closed triggers app exit
3. Test rapid window creation/destruction
4. Verify reconnection behavior if SignalR connection drops
5. Test with both packed and unpacked modes
6. Verify API calls work correctly (especially those returning data)
## Known Limitations
1. **Request-response pattern not yet implemented** - `InvokeElectronApi` is a placeholder. Current API calls use event-based pattern.
2. **TouchBar API not yet supported** on macOS SignalR mode
3. **SignalR automatic reconnection** may cause issues with pending API calls (needs circuit breaker pattern)
## Future Enhancements
1. **Request-response pattern** - Implement proper async/await pattern for API calls that return values
2. **Metrics/diagnostics** - Add SignalR connection health monitoring
3. **Circuit breaker** - Handle reconnection scenarios gracefully
4. **Integration tests** - Comprehensive test suite for SignalR mode
5. **Performance benchmarks** - Compare SignalR vs Socket.IO performance
## Success Metrics
The implementation is considered complete and functional:
- ✅ .NET starts first with dynamic port (port 0)
- ✅ Electron launches with actual URL
- ✅ SignalR connection establishes successfully
- ✅ API modules load before app ready callback
- ✅ Window creation works from .NET
- ✅ Window shutdown triggers app exit
- ✅ Blazor Server pages load with correct styling
- ✅ Both SignalR hubs coexist (Electron + Blazor)
- ✅ Clean codebase with minimal debug logging
- ✅ Comprehensive inline documentation
- ✅ Token-based authentication for multi-user scenarios
- ✅ Secure cookie-based session management
- ✅ Structured logging for security monitoring
- ✅ Protection against cross-user connection attempts

View File

@@ -0,0 +1,236 @@
# SignalR-Based Startup Mode for Electron.NET
## Overview
This feature adds a new startup mode for Electron.NET where:
- **.NET/ASP.NET Core starts first** and binds to port 0 (dynamic port)
- **Kestrel picks an available port** automatically
- **Electron process is launched** with the actual URL
- **SignalR is used for communication** instead of socket.io
- **Blazor Server apps** can coexist with Electron control
## Status
**Phases 1-5 Complete** - Infrastructure ready, basic functionality implemented
⏸️ **Phase 6 Pending** - Full API integration, testing, and documentation
## How It Works
### Startup Sequence
1. ASP.NET Core application starts
2. Kestrel binds to `http://localhost:0` (random available port)
3. `RuntimeControllerAspNetDotnetFirstSignalR` captures the actual port via `IServerAddressesFeature`
4. Electron process is launched with `--electronUrl=http://localhost:XXXXX`
5. Electron's main.js detects SignalR mode (via `--dotnetpackedsignalr` or `--unpackeddotnetsignalr` flag)
6. Electron connects to SignalR hub at `/electron-hub`
7. Hub notifies runtime controller of successful connection
8. Application transitions to "Ready" state
9. `ElectronAppReady` callback is invoked
### Communication Flow
```
.NET/Kestrel (Port 0) ←→ SignalR Hub (/electron-hub) ←→ Electron Process
↓ ↓ ↓
Blazor Server ElectronHub class SignalR Client
(/_blazor hub) (API commands) (main.js + signalr-bridge.js)
```
## Usage
### 1. Enable SignalR Mode
Set the environment variable:
```bash
ELECTRON_USE_SIGNALR=true
```
Or in launchSettings.json:
```json
{
"environmentVariables": {
"ELECTRON_USE_SIGNALR": "true"
}
}
```
### 2. Configure ASP.NET Core
In your `Program.cs`:
```csharp
using ElectronNET.API;
using ElectronNET.API.Entities;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddElectron();
builder.UseElectron(args, async () =>
{
var window = await Electron.WindowManager.CreateWindowAsync(
new BrowserWindowOptions { Show = false });
window.OnReadyToShow += () => window.Show();
});
var app = builder.Build();
// Configure middleware
app.UseStaticFiles();
app.UseRouting();
// Map the Electron SignalR hub
app.MapElectronHub(); // ← Required for SignalR mode
app.MapRazorPages();
app.Run();
```
### 3. Run Your Application
Just press F5 in Visual Studio or run:
```bash
dotnet run
```
The application will:
- Automatically detect SignalR mode via environment variable
- Bind Kestrel to port 0
- Launch Electron with the correct URL
- Establish SignalR connection
## Components
### .NET Side
- **`ElectronHub`** - SignalR hub at `/electron-hub`
- **`SignalRFacade`** - Mimics `SocketIoFacade` interface for compatibility
- **`RuntimeControllerAspNetDotnetFirstSignalR`** - Lifecycle management
- **`StartupMethod.PackagedDotnetFirstSignalR`** - For packaged apps
- **`StartupMethod.UnpackedDotnetFirstSignalR`** - For debugging
### Electron Side
- **`signalr-bridge.js`** - SignalR client wrapper
- **`main.js`** - Detects SignalR mode and connects to hub
- **`@microsoft/signalr`** npm package
## Key Features
**Dynamic Port Assignment** - No hardcoded ports, no conflicts
**Blazor Server Compatible** - Separate hub endpoints (`/electron-hub` vs `/_blazor`)
**Bidirectional Communication** - Both .NET→Electron and Electron→.NET
**Hot Reload Support** - SignalR automatic reconnection
**Multiple Instances** - Each instance gets its own port
## Current Limitations (Phase 6 Work Needed)
⚠️ **Electron API Integration** - Existing Electron APIs (WindowManager, Dialog, etc.) still use SocketIoFacade. Full integration requires:
- Refactoring APIs to work with both facades, or
- Creating an adapter pattern
⚠️ **Request-Response Pattern** - Current hub methods are one-way. Need to implement proper async request-response for API calls.
⚠️ **Event Routing** - Electron events need to be routed through SignalR back to .NET.
⚠️ **Testing** - Integration tests needed to validate end-to-end functionality.
## What's Implemented
### Phase 1: Core Infrastructure ✅
- New `StartupMethod` enum values
- `ElectronHub` SignalR hub
- Hub endpoint registration
### Phase 2: Runtime Controller ✅
- `RuntimeControllerAspNetDotnetFirstSignalR`
- Port 0 binding logic
- Electron launch with URL parameter
- SignalR connection tracking
### Phase 3: Electron/Node.js Side ✅
- `@microsoft/signalr` package integration
- SignalR connection module
- Startup mode detection
- URL parameter handling
### Phase 4: API Bridge ✅ (Basic Structure)
- `SignalRFacade` class
- Event handler system
- Hub connection integration
### Phase 5: Configuration ✅
- Environment variable detection
- Port 0 configuration
- Automatic service registration
## Next Steps (Phase 6)
To fully utilize this feature, the following work is recommended:
1. **API Integration** - Make existing Electron APIs work with SignalR
2. **Sample Application** - Create a Blazor Server demo
3. **Integration Tests** - Validate end-to-end scenarios
4. **Documentation** - Complete user guides and examples
5. **Performance Testing** - Compare with socket.io mode
## Files Changed
### .NET
- `src/ElectronNET.API/Runtime/Data/StartupMethod.cs`
- `src/ElectronNET.AspNet/Hubs/ElectronHub.cs`
- `src/ElectronNET.AspNet/Bridge/SignalRFacade.cs`
- `src/ElectronNET.AspNet/Runtime/Controllers/RuntimeControllerAspNetDotnetFirstSignalR.cs`
- `src/ElectronNET.AspNet/API/ElectronEndpointRouteBuilderExtensions.cs`
- `src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs`
- `src/ElectronNET.API/Runtime/StartupManager.cs`
### Electron/Node.js
- `src/ElectronNET.Host/package.json`
- `src/ElectronNET.Host/main.js`
- `src/ElectronNET.Host/api/signalr-bridge.js` (new file)
## Commits
```
7f2ea48 - Add PackagedDotnetFirstSignalR and UnpackedDotnetFirstSignalR startup methods
8ee81f6 - Add ElectronHub and SignalR infrastructure for new startup modes
40aed60 - Add RuntimeControllerAspNetDotnetFirstSignalR for SignalR-based startup
c1740b5 - Add SignalR client support to Electron Host for new startup modes
cb7d721 - Add SignalRFacade for SignalR-based API communication
268b9c9 - Update RuntimeControllerAspNetDotnetFirstSignalR to use SignalRFacade
04ec522 - Fix compilation errors - Phase 4 complete (basic structure)
054f5b1 - Complete Phase 5: Add SignalR startup detection and port 0 configuration
```
## Benefits Over Socket.io Mode
- **Better Integration** - Native SignalR is part of ASP.NET Core stack
- **Type Safety** - SignalR has better TypeScript support
- **Performance** - SignalR is optimized for ASP.NET Core
- **Reliability** - Built-in reconnection and error handling
- **Scalability** - Can leverage SignalR's scale-out features
- **Consistency** - Blazor Server already uses SignalR
## Contributing
To contribute to Phase 6 (full API integration):
1. Focus on adapting existing Electron API classes to work with SignalRFacade
2. Implement request-response pattern in ElectronHub
3. Add integration tests
4. Create sample applications
5. Update documentation
## License
MIT - Same as Electron.NET
---
**Created**: January 30, 2026
**Status**: Infrastructure Complete, API Integration Pending
**Contact**: See Electron.NET maintainers

View File

@@ -25,7 +25,7 @@ These are the current default values when you don't make any changes:
<ElectronSingleInstance>true</ElectronSingleInstance>
<ElectronSplashScreen></ElectronSplashScreen>
<ElectronIcon></ElectronIcon>
<PackageId>$(MSBuildProjectName.Replace(".", "-").ToLower())</PackageId>
<ElectronPackageId>$(MSBuildProjectName.Replace(".", "-").ToLower())</ElectronPackageId>
<ElectronBuilderJson>electron-builder.json</ElectronBuilderJson>
<Title>$(MSBuildProjectName)</Title>
</PropertyGroup>
@@ -38,15 +38,15 @@ Since electron builder still expects a `package.json` file to exist, ElectronNET
```json
{
"name": "$(PackageId)",
"name": "$(ElectronPackageId)",
"productName": "$(ElectronTitle)",
"build": {
"appId": "$(PackageId)",
"appId": "$(ElectronPackageId)",
"linux": {
"desktop": {
"entry": { "Name": "$(Title)" }
},
"executableName": "$(PackageId)"
"executableName": "$(ElectronPackageId)"
},
"deb": {
"desktop": {

74
docs/Using/Custom_main.md Normal file
View File

@@ -0,0 +1,74 @@
# Using custom_main.js
This guide explains how to include and use a `custom_main.js` file in your Electron.NET application for advanced Electron/Node.js customization.
## Why use custom_main.js?
- Register custom protocol handlers (e.g., `myapp://`) — protocols must be registered before the app is fully initialized
- Integrate Node.js modules (e.g., telemetry, OS APIs)
- Control startup logic (abort, environment checks)
- Set up IPC messaging or preload scripts
## Step-by-Step Process
### 1. Create the custom_main.js file
Place your custom logic in `electron/custom_main.js`:
```javascript
module.exports.onStartup = function(host) {
// Example: Register a global shortcut for opening dev tools
const { app, globalShortcut, BrowserWindow } = require('electron');
app.on('ready', () => {
const ret = globalShortcut.register('Control+Shift+I', () => {
BrowserWindow.getAllWindows().forEach(win => win.webContents.openDevTools());
console.log('Ctrl+Shift+I is pressed: DevTools opened!');
});
});
app.on('will-quit', () => {
globalShortcut.unregisterAll();
});
return true;
};
```
### 2. Configure your .csproj to copy custom_main.js to output
Add this to your `.csproj` file:
```xml
<ItemGroup>
<None Update="electron\custom_main.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>.electron\custom_main.js</TargetPath>
</None>
</ItemGroup>
```
### 3. Build and run your app
Use the standard build/run commands:
```powershell
dotnet build
dotnet run
```
Electron.NET will automatically load and execute your `custom_main.js` before initializing the .NET backend.
## Advanced Usage
Use environment variables to control features:
```javascript
const env = process.env.ASPNETCORE_ENVIRONMENT || 'Production';
if (env === 'Development') { /* enable dev features */ }
```
## Notes
- `custom_main.js` must use CommonJS syntax (`module.exports.onStartup = ...`).
- Place the file in your source directory and copy it to `.electron` using `.csproj`.
- Electron.NET will abort startup if `onStartup` returns `false`.
### Complete example is available here [ElectronNetSampleApp](https://github.com/niteshsinghal85/ElectronNetSampleApp)

View File

@@ -26,12 +26,15 @@ Add publish profiles to `Properties/PublishProfiles/`:
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<DeleteExistingFiles>true</DeleteExistingFiles>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>48eff821-2f4d-60cc-aa44-be0f1d6e5f35</ProjectGuid>
<SelfContained>true</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
</PropertyGroup>
</Project>
```
@@ -46,12 +49,61 @@ Add publish profiles to `Properties/PublishProfiles/`:
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<DeleteExistingFiles>true</DeleteExistingFiles>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<ProjectGuid>48eff821-2f4d-60cc-aa44-be0f1d6e5f35</ProjectGuid>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>
```
#### ASP.NET Application Profile (macOS Apple Silicon ARM64)
**osx-arm64.pubxml:**
```xml
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<DeleteExistingFiles>true</DeleteExistingFiles>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
<ProjectGuid>48eff821-2f4d-60cc-aa44-be0f1d6e5f35</ProjectGuid>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>
```
#### ASP.NET Application Profile (macOS Intel x64)
**osx-x64.pubxml:**
```xml
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<DeleteExistingFiles>true</DeleteExistingFiles>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
<ProjectGuid>48eff821-2f4d-60cc-aa44-be0f1d6e5f35</ProjectGuid>
<SelfContained>true</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
</PropertyGroup>
</Project>
```
@@ -97,6 +149,46 @@ Add publish profiles to `Properties/PublishProfiles/`:
</Project>
```
#### Console Application Profile (macOS Apple Silicon ARM64)
**osx-arm64.pubxml:**
```xml
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
</PropertyGroup>
</Project>
```
#### Console Application Profile (macOS Intel x64)
**osx-x64.pubxml:**
```xml
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
</PropertyGroup>
</Project>
```
### Step 2: Configure Electron Builder
ElectronNET.Core automatically adds a default `electron-builder.json` file under `Properties\electron-builder.json`.

View File

@@ -9,6 +9,7 @@
- [What's new?](Core/What's-New.md)
- [Migration Guide](Core/Migration-Guide.md)
- [Migration Checks](Core/Migration-Checks.md)
- [Advanced Migration](Core/Advanced-Migration-Topics.md)
# Getting Started
@@ -23,6 +24,7 @@
- [Startup-Methods](Using/Startup-Methods.md)
- [Debugging](Using/Debugging.md)
- [Package Building](Using/Package-Building.md)
- [Adding a `custom_main.js`](Using/Custom_main.md)
# API Reference

View File

@@ -85,7 +85,7 @@ public sealed class ReleaseNotesParser
// Parse content.
var notes = new List<string>();
while (true)
{
// Sanity checks.

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace></RootNamespace>
<NoWarn>CS0649;CS0169</NoWarn>
<NukeRootDirectory>..</NukeRootDirectory>
@@ -11,7 +11,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="9.0.4" />
<PackageReference Include="Microsoft.Build" Version="18.0.2" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="18.0.2" />
<PackageReference Include="Nuke.Common" Version="10.1.0" />
</ItemGroup>
<ItemGroup>

4
src/.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
[*.cs]
# CA1416: Validate platform compatibility
dotnet_diagnostic.CA1416.severity = error

View File

@@ -1,4 +1,5 @@
// ReSharper disable InconsistentNaming
namespace ElectronNET.API
{
using Common;
@@ -17,6 +18,7 @@ namespace ElectronNET.API
DashesLowerFirst,
NoDashUpperFirst
}
protected enum SocketTaskMessageNameTypes
{
DashesLowerFirst,
@@ -29,15 +31,15 @@ namespace ElectronNET.API
CamelCase,
}
private const int PropertyTimeout = 1000;
private static readonly TimeSpan InvocationTimeout = 1000.ms();
private readonly string objectName;
private readonly ConcurrentDictionary<string, PropertyGetter> propertyGetters;
private readonly ConcurrentDictionary<string, string> propertyEventNames = new();
private readonly ConcurrentDictionary<string, string> propertyMessageNames = new();
private readonly ConcurrentDictionary<string, Invocator> invocators;
private readonly ConcurrentDictionary<string, string> invocationEventNames = new();
private readonly ConcurrentDictionary<string, string> invocationMessageNames = new();
private readonly ConcurrentDictionary<string, string> methodMessageNames = new();
private static readonly ConcurrentDictionary<string, EventContainer> eventContainers = new();
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, PropertyGetter>> AllPropertyGetters = new();
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, Invocator>> AllInvocators = new();
private readonly object objLock = new object();
@@ -58,7 +60,7 @@ namespace ElectronNET.API
protected ApiBase()
{
this.objectName = this.GetType().Name.LowerFirst();
propertyGetters = AllPropertyGetters.GetOrAdd(objectName, _ => new ConcurrentDictionary<string, PropertyGetter>());
this.invocators = AllInvocators.GetOrAdd(this.objectName, _ => new ConcurrentDictionary<string, Invocator>());
}
protected void CallMethod0([CallerMemberName] string callerName = null)
@@ -113,21 +115,26 @@ namespace ElectronNET.API
}
}
protected Task<T> GetPropertyAsync<T>(object arg = null, [CallerMemberName] string callerName = null)
protected Task<T> InvokeAsync<T>(object arg = null, [CallerMemberName] string callerName = null)
{
return this.InvokeAsyncWithTimeout<T>(InvocationTimeout, arg, callerName);
}
protected Task<T> InvokeAsyncWithTimeout<T>(TimeSpan invocationTimeout, object arg = null, [CallerMemberName] string callerName = null)
{
Debug.Assert(callerName != null, nameof(callerName) + " != null");
lock (this.objLock)
{
return this.propertyGetters.GetOrAdd(callerName, _ =>
return this.invocators.GetOrAdd(callerName, _ =>
{
var getter = new PropertyGetter<T>(this, callerName, PropertyTimeout, arg);
var getter = new Invocator<T>(this, callerName, invocationTimeout, arg);
getter.Task<T>().ContinueWith(_ =>
{
lock (this.objLock)
{
return this.propertyGetters.TryRemove(callerName, out var _);
return this.invocators.TryRemove(callerName, out var _);
}
});
@@ -135,15 +142,15 @@ namespace ElectronNET.API
}).Task<T>();
}
}
protected void AddEvent(Action value, int? id = null, [CallerMemberName] string callerName = null)
{
Debug.Assert(callerName != null, nameof(callerName) + " != null");
var eventName = EventName(callerName);
var eventKey = EventKey(eventName, id);
var eventName = this.EventName(callerName);
lock (objLock)
var eventKey = this.EventKey(eventName, id);
lock (this.objLock)
{
var container = eventContainers.GetOrAdd(eventKey, _ =>
{
@@ -156,14 +163,14 @@ namespace ElectronNET.API
container.Register(value);
}
}
protected void RemoveEvent(Action value, int? id = null, [CallerMemberName] string callerName = null)
{
Debug.Assert(callerName != null, nameof(callerName) + " != null");
var eventName = EventName(callerName);
var eventKey = EventKey(eventName, id);
var eventName = this.EventName(callerName);
var eventKey = this.EventKey(eventName, id);
lock (objLock)
lock (this.objLock)
{
if (eventContainers.TryGetValue(eventKey, out var container) && !container.Unregister(value))
{
@@ -172,15 +179,15 @@ namespace ElectronNET.API
}
}
}
protected void AddEvent<T>(Action<T> value, int? id = null, [CallerMemberName] string callerName = null)
{
Debug.Assert(callerName != null, nameof(callerName) + " != null");
var eventName = EventName(callerName);
var eventKey = EventKey(eventName, id);
lock (objLock)
var eventName = this.EventName(callerName);
var eventKey = this.EventKey(eventName, id);
lock (this.objLock)
{
var container = eventContainers.GetOrAdd(eventKey, _ =>
{
@@ -197,10 +204,10 @@ namespace ElectronNET.API
protected void RemoveEvent<T>(Action<T> value, int? id = null, [CallerMemberName] string callerName = null)
{
Debug.Assert(callerName != null, nameof(callerName) + " != null");
var eventName = EventName(callerName);
var eventKey = EventKey(eventName, id);
var eventName = this.EventName(callerName);
var eventKey = this.EventKey(eventName, id);
lock (objLock)
lock (this.objLock)
{
if (eventContainers.TryGetValue(eventKey, out var container) && !container.Unregister(value))
{
@@ -212,33 +219,33 @@ namespace ElectronNET.API
private string EventName(string callerName)
{
switch (SocketEventNameType)
switch (this.SocketEventNameType)
{
case SocketEventNameTypes.DashedLower:
return $"{objectName}-{callerName.ToDashedEventName()}";
return $"{this.objectName}-{callerName.ToDashedEventName()}";
case SocketEventNameTypes.CamelCase:
return $"{objectName}-{callerName.ToCamelCaseEventName()}";
return $"{this.objectName}-{callerName.ToCamelCaseEventName()}";
default:
throw new ArgumentOutOfRangeException();
}
}
private string EventKey(string eventName, int? id)
{
return string.Format(CultureInfo.InvariantCulture, "{0}{1:D}", eventName, id);
}
internal abstract class PropertyGetter
internal abstract class Invocator
{
public abstract Task<T> Task<T>();
}
internal class PropertyGetter<T> : PropertyGetter
internal class Invocator<T> : Invocator
{
private readonly Task<T> tcsTask;
private TaskCompletionSource<T> tcs;
public PropertyGetter(ApiBase apiBase, string callerName, int timeoutMs, object arg = null)
public Invocator(ApiBase apiBase, string callerName, TimeSpan timeout, object arg = null)
{
this.tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
this.tcsTask = this.tcs.Task;
@@ -249,22 +256,22 @@ namespace ElectronNET.API
switch (apiBase.SocketTaskEventNameType)
{
case SocketTaskEventNameTypes.DashesLowerFirst:
eventName = apiBase.propertyEventNames.GetOrAdd(callerName, s => $"{apiBase.objectName}-{s.StripAsync().LowerFirst()}-completed");
eventName = apiBase.invocationEventNames.GetOrAdd(callerName, s => $"{apiBase.objectName}-{s.StripAsync().LowerFirst()}-completed");
break;
case SocketTaskEventNameTypes.NoDashUpperFirst:
eventName = apiBase.propertyEventNames.GetOrAdd(callerName, s => $"{apiBase.objectName}{s.StripAsync()}Completed");
eventName = apiBase.invocationEventNames.GetOrAdd(callerName, s => $"{apiBase.objectName}{s.StripAsync()}Completed");
break;
default:
throw new ArgumentOutOfRangeException();
}
switch (apiBase.SocketTaskMessageNameType)
{
case SocketTaskMessageNameTypes.DashesLowerFirst:
messageName = apiBase.propertyMessageNames.GetOrAdd(callerName, s => $"{apiBase.objectName}-{s.StripAsync().LowerFirst()}");
messageName = apiBase.invocationMessageNames.GetOrAdd(callerName, s => $"{apiBase.objectName}-{s.StripAsync().LowerFirst()}");
break;
case SocketTaskMessageNameTypes.NoDashUpperFirst:
messageName = apiBase.propertyMessageNames.GetOrAdd(callerName, s => apiBase.objectName + s.StripAsync());
messageName = apiBase.invocationMessageNames.GetOrAdd(callerName, s => apiBase.objectName + s.StripAsync());
break;
default:
throw new ArgumentOutOfRangeException();
@@ -289,17 +296,17 @@ namespace ElectronNET.API
}
}
});
if (arg != null)
{
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id, arg) : BridgeConnector.Socket.Emit(messageName, arg);
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id, arg) : BridgeConnector.Socket.Emit(messageName, arg);
}
else
{
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id) : BridgeConnector.Socket.Emit(messageName);
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id) : BridgeConnector.Socket.Emit(messageName);
}
System.Threading.Tasks.Task.Delay(PropertyTimeout).ContinueWith(_ =>
System.Threading.Tasks.Task.Delay(timeout).ContinueWith(_ =>
{
if (this.tcs != null)
{
@@ -307,7 +314,9 @@ namespace ElectronNET.API
{
if (this.tcs != null)
{
var ex = new TimeoutException($"No response after {timeoutMs:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
var ex = new TimeoutException(
$"No response after {(long)timeout.TotalMilliseconds}ms trying to retrieve value {apiBase.objectName}.{callerName}()"
);
this.tcs.TrySetException(ex);
this.tcs = null;
}
@@ -321,7 +330,7 @@ namespace ElectronNET.API
return this.tcsTask as Task<T1>;
}
}
[SuppressMessage("ReSharper", "InconsistentlySynchronizedField")]
private class EventContainer
{
@@ -330,44 +339,44 @@ namespace ElectronNET.API
private Action<T> GetEventActionT<T>()
{
return (Action<T>)eventActionT;
return (Action<T>)this.eventActionT;
}
private void SetEventActionT<T>(Action<T> actionT)
{
eventActionT = actionT;
this.eventActionT = actionT;
}
public void OnEventAction() => eventAction?.Invoke();
public void OnEventAction() => this.eventAction?.Invoke();
public void OnEventActionT<T>(T p) => GetEventActionT<T>()?.Invoke(p);
public void OnEventActionT<T>(T p) => this.GetEventActionT<T>()?.Invoke(p);
public void Register(Action receiver)
{
eventAction += receiver;
this.eventAction += receiver;
}
public void Register<T>(Action<T> receiver)
{
var actionT = GetEventActionT<T>();
var actionT = this.GetEventActionT<T>();
actionT += receiver;
SetEventActionT(actionT);
this.SetEventActionT(actionT);
}
public bool Unregister(Action receiver)
{
eventAction -= receiver;
this.eventAction -= receiver;
return this.eventAction != null;
}
public bool Unregister<T>(Action<T> receiver)
{
var actionT = GetEventActionT<T>();
var actionT = this.GetEventActionT<T>();
actionT -= receiver;
SetEventActionT(actionT);
this.SetEventActionT(actionT);
return actionT != null;
}
}
}
}
}

View File

@@ -2,6 +2,7 @@ using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -58,7 +59,7 @@ namespace ElectronNET.API
private event Action _windowAllClosed;
/// <summary>
/// Emitted before the application starts closing its windows.
/// Emitted before the application starts closing its windows.
/// <para/>
/// Note: If application quit was initiated by <see cref="AutoUpdater.QuitAndInstall"/> then <see cref="BeforeQuit"/>
/// is emitted after emitting close event on all windows and closing them.
@@ -259,6 +260,8 @@ namespace ElectronNET.API
/// screen readers, are enabled or disabled. See https://www.chromium.org/developers/design-documents/accessibility for more details.
/// </summary>
/// <returns><see langword="true"/> when Chrome's accessibility support is enabled, <see langword="false"/> otherwise.</returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public event Action<bool> AccessibilitySupportChanged
{
add => AddEvent(value, GetHashCode());
@@ -316,6 +319,7 @@ namespace ElectronNET.API
/// <para/>
/// On Windows, you have to parse the arguments using App.CommandLine to get the filepath.
/// </summary>
[SupportedOSPlatform("macOS")]
public event Action<string> OpenFile
{
add => AddEvent(value, GetHashCode());
@@ -327,6 +331,7 @@ namespace ElectronNET.API
/// Emitted when a MacOS user wants to open a URL with the application. Your application's Info.plist file must
/// define the URL scheme within the CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication.
/// </summary>
[SupportedOSPlatform("macOS")]
public event Action<string> OpenUrl
{
add => AddEvent(value, GetHashCode());
@@ -366,7 +371,7 @@ namespace ElectronNET.API
{
get
{
return this.GetPropertyAsync<string>();
return this.InvokeAsync<string>();
}
}
@@ -399,7 +404,6 @@ namespace ElectronNET.API
private static object _syncRoot = new object();
/// <summary>
/// Try to close all windows. The <see cref="BeforeQuit"/> event will be emitted first. If all windows are successfully
/// closed, the <see cref="WillQuit"/> event will be emitted and by default the application will terminate. This method
@@ -482,6 +486,7 @@ namespace ElectronNET.API
/// <summary>
/// Hides all application windows without minimizing them.
/// </summary>
[SupportedOSPlatform("macOS")]
public void Hide()
{
this.CallMethod0();
@@ -490,6 +495,7 @@ namespace ElectronNET.API
/// <summary>
/// Shows application windows after they were hidden. Does not automatically focus them.
/// </summary>
[SupportedOSPlatform("macOS")]
public void Show()
{
this.CallMethod0();
@@ -501,7 +507,7 @@ namespace ElectronNET.API
public async Task<string> GetAppPathAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<string>().ConfigureAwait(false);
return await this.InvokeAsync<string>().ConfigureAwait(false);
}
/// <summary>
@@ -533,7 +539,7 @@ namespace ElectronNET.API
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.Once<string>("appGetPathCompleted", taskCompletionSource.SetResult);
BridgeConnector.Socket.Emit("appGetPath", pathName.GetDescription());
BridgeConnector.Socket.Emit("appGetPath", pathName);
return await taskCompletionSource.Task
.ConfigureAwait(false);
@@ -554,18 +560,18 @@ namespace ElectronNET.API
/// </summary>
public void SetPath(PathName name, string path)
{
this.CallMethod2(name.GetDescription(), path);
this.CallMethod2(name, path);
}
/// <summary>
/// The version of the loaded application. If no version is found in the applications package.json file,
/// The version of the loaded application. If no version is found in the applications package.json file,
/// the version of the current bundle or executable is returned.
/// </summary>
/// <returns>The version of the loaded application.</returns>
public async Task<string> GetVersionAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<string>().ConfigureAwait(false);
return await this.InvokeAsync<string>().ConfigureAwait(false);
}
/// <summary>
@@ -579,7 +585,7 @@ namespace ElectronNET.API
public async Task<string> GetLocaleAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<string>().ConfigureAwait(false);
return await this.InvokeAsync<string>().ConfigureAwait(false);
}
/// <summary>
@@ -587,6 +593,8 @@ namespace ElectronNET.API
/// list from the task bar, and on macOS you can visit it from dock menu.
/// </summary>
/// <param name="path">Path to add.</param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void AddRecentDocument(string path)
{
this.CallMethod1(path);
@@ -595,6 +603,8 @@ namespace ElectronNET.API
/// <summary>
/// Clears the recent documents list.
/// </summary>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void ClearRecentDocuments()
{
this.CallMethod0();
@@ -710,6 +720,8 @@ namespace ElectronNET.API
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public async Task<bool> RemoveAsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default)
{
return await this.RemoveAsDefaultProtocolClientAsync(protocol, null, null, cancellationToken).ConfigureAwait(false);
@@ -723,6 +735,8 @@ namespace ElectronNET.API
/// <param name="path">Defaults to process.execPath.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public async Task<bool> RemoveAsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default)
{
return await this.RemoveAsDefaultProtocolClientAsync(protocol, path, null, cancellationToken).ConfigureAwait(false);
@@ -737,6 +751,8 @@ namespace ElectronNET.API
/// <param name="args">Defaults to an empty array.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public async Task<bool> RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -827,6 +843,7 @@ namespace ElectronNET.API
/// <param name="userTasks">Array of <see cref="UserTask"/> objects.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
[SupportedOSPlatform("Windows")]
public async Task<bool> SetUserTasksAsync(UserTask[] userTasks, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -847,10 +864,11 @@ namespace ElectronNET.API
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Jump List settings.</returns>
[SupportedOSPlatform("Windows")]
public async Task<JumpListSettings> GetJumpListSettingsAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<JumpListSettings>().ConfigureAwait(false);
return await this.InvokeAsync<JumpListSettings>().ConfigureAwait(false);
}
/// <summary>
@@ -869,6 +887,7 @@ namespace ElectronNET.API
/// omitted from the Jump List. The list of removed items can be obtained using <see cref="GetJumpListSettingsAsync"/>.
/// </summary>
/// <param name="categories">Array of <see cref="JumpListCategory"/> objects.</param>
[SupportedOSPlatform("Windows")]
public void SetJumpList(JumpListCategory[] categories)
{
this.CallMethod1(categories);
@@ -941,7 +960,7 @@ namespace ElectronNET.API
public async Task<bool> HasSingleInstanceLockAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<bool>().ConfigureAwait(false);
return await this.InvokeAsync<bool>().ConfigureAwait(false);
}
/// <summary>
@@ -951,6 +970,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type">Uniquely identifies the activity. Maps to <see href="https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType">NSUserActivity.activityType</see>.</param>
/// <param name="userInfo">App-specific state to store for use by another device.</param>
[SupportedOSPlatform("macOS")]
public void SetUserActivity(string type, object userInfo)
{
SetUserActivity(type, userInfo, null);
@@ -968,6 +988,7 @@ namespace ElectronNET.API
/// <param name="webpageUrl">
/// The webpage to load in a browser if no suitable app is installed on the resuming device. The scheme must be http or https.
/// </param>
[SupportedOSPlatform("macOS")]
public void SetUserActivity(string type, object userInfo, string webpageUrl)
{
this.CallMethod3(type, userInfo, webpageUrl);
@@ -977,15 +998,17 @@ namespace ElectronNET.API
/// The type of the currently running activity.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
[SupportedOSPlatform("macOS")]
public async Task<string> GetCurrentActivityTypeAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<string>().ConfigureAwait(false);
return await this.InvokeAsync<string>().ConfigureAwait(false);
}
/// <summary>
/// Invalidates the current <see href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html">Handoff</see> user activity.
/// </summary>
[SupportedOSPlatform("macOS")]
public void InvalidateCurrentActivity()
{
this.CallMethod0();
@@ -994,6 +1017,7 @@ namespace ElectronNET.API
/// <summary>
/// Marks the current <see href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html">Handoff</see> user activity as inactive without invalidating it.
/// </summary>
[SupportedOSPlatform("macOS")]
public void ResignCurrentActivity()
{
this.CallMethod0();
@@ -1003,6 +1027,7 @@ namespace ElectronNET.API
/// Changes the <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx">Application User Model ID</see> to id.
/// </summary>
/// <param name="id">Model Id.</param>
[SupportedOSPlatform("Windows")]
public void SetAppUserModelId(string id)
{
this.CallMethod1(id);
@@ -1017,6 +1042,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Result of import. Value of 0 indicates success.</returns>
[SupportedOSPlatform("Linux")]
public async Task<int> ImportCertificateAsync(ImportCertificateOptions options, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -1043,7 +1069,7 @@ namespace ElectronNET.API
public async Task<ProcessMetric[]> GetAppMetricsAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<ProcessMetric[]>().ConfigureAwait(false);
return await this.InvokeAsync<ProcessMetric[]>().ConfigureAwait(false);
}
/// <summary>
@@ -1055,7 +1081,7 @@ namespace ElectronNET.API
public async Task<GPUFeatureStatus> GetGpuFeatureStatusAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<GPUFeatureStatus>().ConfigureAwait(false);
return await this.InvokeAsync<GPUFeatureStatus>().ConfigureAwait(false);
}
/// <summary>
@@ -1068,6 +1094,8 @@ namespace ElectronNET.API
/// <param name="count">Counter badge.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("macOS")]
public async Task<bool> SetBadgeCountAsync(int count, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -1087,10 +1115,12 @@ namespace ElectronNET.API
/// The current value displayed in the counter badge.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("macOS")]
public async Task<int> GetBadgeCountAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<int>().ConfigureAwait(false);
return await this.InvokeAsync<int>().ConfigureAwait(false);
}
/// <summary>
@@ -1102,16 +1132,19 @@ namespace ElectronNET.API
/// Whether the current desktop environment is Unity launcher.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
[SupportedOSPlatform("Linux")]
public async Task<bool> IsUnityRunningAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<bool>().ConfigureAwait(false);
return await this.InvokeAsync<bool>().ConfigureAwait(false);
}
/// <summary>
/// If you provided path and args options to <see cref="SetLoginItemSettings"/> then you need to pass the same
/// arguments here for <see cref="LoginItemSettings.OpenAtLogin"/> to be set correctly.
/// </summary>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public async Task<LoginItemSettings> GetLoginItemSettingsAsync(CancellationToken cancellationToken = default)
{
return await this.GetLoginItemSettingsAsync(null, cancellationToken).ConfigureAwait(false);
@@ -1123,6 +1156,8 @@ namespace ElectronNET.API
/// </summary>
/// <param name="options"></param>
/// <param name="cancellationToken">The cancellation token.</param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public async Task<LoginItemSettings> GetLoginItemSettingsAsync(LoginItemSettingsOptions options, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -1152,6 +1187,8 @@ namespace ElectronNET.API
/// you'll want to set the launch path to Update.exe, and pass arguments that specify your application name.
/// </summary>
/// <param name="loginSettings"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetLoginItemSettings(LoginSettings loginSettings)
{
this.CallMethod1(loginSettings);
@@ -1163,10 +1200,12 @@ namespace ElectronNET.API
/// See <see href="chromium.org/developers/design-documents/accessibility">Chromium's accessibility docs</see> for more details.
/// </summary>
/// <returns><see langword="true"/> if Chromes accessibility support is enabled, <see langword="false"/> otherwise.</returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public async Task<bool> IsAccessibilitySupportEnabledAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await this.GetPropertyAsync<bool>().ConfigureAwait(false);
return await this.InvokeAsync<bool>().ConfigureAwait(false);
}
/// <summary>
@@ -1179,6 +1218,8 @@ namespace ElectronNET.API
/// Note: Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default.
/// </summary>
/// <param name="enabled">Enable or disable <see href="https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree">accessibility tree</see> rendering.</param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetAccessibilitySupportEnabled(bool enabled)
{
this.CallMethod1(enabled);
@@ -1245,7 +1286,7 @@ namespace ElectronNET.API
return Task.Run(() =>
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.Once<string>("appGetUserAgentFallbackCompleted", taskCompletionSource.SetResult);
BridgeConnector.Socket.Emit("appGetUserAgentFallback");
@@ -1295,4 +1336,4 @@ namespace ElectronNET.API
public async Task Once(string eventName, Action<object> action)
=> await Events.Instance.Once(ModuleName, eventName, action).ConfigureAwait(false);
}
}
}

View File

@@ -10,7 +10,7 @@ namespace ElectronNET.API
/// <summary>
/// Enable apps to automatically update themselves. Based on electron-updater.
/// </summary>
public sealed class AutoUpdater: ApiBase
public sealed class AutoUpdater : ApiBase
{
protected override SocketTaskEventNameTypes SocketTaskEventNameType => SocketTaskEventNameTypes.DashesLowerFirst;
protected override SocketTaskMessageNameTypes SocketTaskMessageNameType => SocketTaskMessageNameTypes.DashesLowerFirst;
@@ -23,7 +23,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<bool>()).Result;
return Task.Run(() => this.InvokeAsync<bool>()).Result;
}
set
{
@@ -40,7 +40,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<bool>()).Result;
return Task.Run(() => this.InvokeAsync<bool>()).Result;
}
set
{
@@ -49,7 +49,7 @@ namespace ElectronNET.API
}
/// <summary>
/// *GitHub provider only.* Whether to allow update to pre-release versions.
/// *GitHub provider only.* Whether to allow update to pre-release versions.
/// Defaults to "true" if application version contains prerelease components (e.g. "0.12.1-alpha.1", here "alpha" is a prerelease component), otherwise "false".
///
/// If "true", downgrade will be allowed("allowDowngrade" will be set to "true").
@@ -58,7 +58,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<bool>()).Result;
return Task.Run(() => this.InvokeAsync<bool>()).Result;
}
set
{
@@ -67,14 +67,14 @@ namespace ElectronNET.API
}
/// <summary>
/// *GitHub provider only.*
/// *GitHub provider only.*
/// Get all release notes (from current version to latest), not just the latest (Default is false).
/// </summary>
public bool FullChangelog
{
get
{
return Task.Run(() => GetPropertyAsync<bool>()).Result;
return Task.Run(() => this.InvokeAsync<bool>()).Result;
}
set
{
@@ -91,7 +91,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<bool>()).Result;
return Task.Run(() => this.InvokeAsync<bool>()).Result;
}
set
{
@@ -106,7 +106,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<string>()).Result;
return Task.Run(() => this.InvokeAsync<string>()).Result;
}
}
@@ -117,12 +117,12 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<SemVer>());
return Task.Run(() => this.InvokeAsync<SemVer>());
}
}
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
[Obsolete("Use the asynchronous version ChannelAsync instead")]
@@ -135,19 +135,19 @@ namespace ElectronNET.API
}
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
public Task<string> ChannelAsync
{
get
{
return Task.Run(() => GetPropertyAsync<string>());
return Task.Run(() => this.InvokeAsync<string>());
}
}
/// <summary>
/// Set the update channel. Not applicable for GitHub.
/// Set the update channel. Not applicable for GitHub.
/// </summary>
public string SetChannel
{
@@ -165,7 +165,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<Dictionary<string, string>>());
return Task.Run(() => this.InvokeAsync<Dictionary<string, string>>());
}
}
@@ -199,7 +199,7 @@ namespace ElectronNET.API
}
/// <summary>
/// Emitted when there is an available update.
/// Emitted when there is an available update.
/// The update is downloaded automatically if AutoDownload is true.
/// </summary>
public event Action<UpdateInfo> OnUpdateAvailable
@@ -332,11 +332,11 @@ namespace ElectronNET.API
}
/// <summary>
/// Restarts the app and installs the update after it has been downloaded.
/// It should only be called after `update-downloaded` has been emitted.
///
/// Note: QuitAndInstall() will close all application windows first and only emit `before-quit` event on `app` after that.
/// This is different from the normal quit event sequence.
/// Restarts the app and installs the update after it has been downloaded.
/// It should only be called after `update-downloaded` has been emitted.
///
/// Note: QuitAndInstall() will close all application windows first and only emit `before-quit` event on `app` after that.
/// This is different from the normal quit event sequence.
/// </summary>
/// <param name="isSilent">*windows-only* Runs the installer in silent mode. Defaults to `false`.</param>
/// <param name="isForceRunAfter">Run the app after finish even on silent install. Not applicable for macOS. Ignored if `isSilent` is set to `false`.</param>
@@ -374,9 +374,5 @@ namespace ElectronNET.API
return tcs.Task;
}
}
}
}

View File

@@ -8,10 +8,11 @@ namespace ElectronNET.API
/// It is like a child window, except that it is positioned relative to its owning window.
/// It is meant to be an alternative to the webview tag.
/// </summary>
public class BrowserView: ApiBase
public class BrowserView : ApiBase
{
protected override SocketTaskEventNameTypes SocketTaskEventNameType => SocketTaskEventNameTypes.DashesLowerFirst;
protected override SocketTaskMessageNameTypes SocketTaskMessageNameType => SocketTaskMessageNameTypes.DashesLowerFirst;
/// <summary>
/// Gets the identifier.
/// </summary>
@@ -30,7 +31,7 @@ namespace ElectronNET.API
{
get
{
return Task.Run(() => GetPropertyAsync<Rectangle>()).Result;
return Task.Run(() => this.InvokeAsync<Rectangle>()).Result;
}
set
{
@@ -69,5 +70,4 @@ namespace ElectronNET.API
BridgeConnector.Socket.Emit("browserView-setBackgroundColor", Id, color);
}
}
}
}

View File

@@ -1,10 +1,11 @@
using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
// ReSharper disable InconsistentNaming
@@ -27,7 +28,7 @@ public class BrowserWindow : ApiBase
public override int Id { get; protected set; }
/// <summary>
/// Emitted when the web page has been rendered (while not being shown) and
/// Emitted when the web page has been rendered (while not being shown) and
/// window can be displayed without a visual flash.
/// </summary>
public event Action OnReadyToShow
@@ -55,8 +56,8 @@ public class BrowserWindow : ApiBase
}
/// <summary>
/// Emitted when the window is closed.
/// After you have received this event you should remove the
/// Emitted when the window is closed.
/// After you have received this event you should remove the
/// reference to the window and avoid using it any more.
/// </summary>
public event Action OnClosed
@@ -68,6 +69,7 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Emitted when window session is going to end due to force shutdown or machine restart or session log off.
/// </summary>
[SupportedOSPlatform("Windows")]
public event Action OnSessionEnd
{
add => AddEvent(value, Id);
@@ -184,9 +186,24 @@ public class BrowserWindow : ApiBase
remove => RemoveEvent(value, Id);
}
/// <summary>
/// Emitted when the window is moved or resized.
/// </summary>
/// <remarks>
/// While not being an original Electron event, this one includes the bounds values,
/// saving the additional roundtrip for calling <see cref="GetBoundsAsync"/>.
/// </remarks>
public event Action<Rectangle> OnBoundsChanged
{
add => AddEvent(value, Id);
remove => RemoveEvent(value, Id);
}
/// <summary>
/// macOS: Emitted once when the window is moved to a new position.
/// </summary>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public event Action OnMoved
{
add => AddEvent(value, Id);
@@ -230,14 +247,16 @@ public class BrowserWindow : ApiBase
}
/// <summary>
/// Emitted when an App Command is invoked. These are typically related to
/// keyboard media keys or browser commands, as well as the “Back” button
/// Emitted when an App Command is invoked. These are typically related to
/// keyboard media keys or browser commands, as well as the “Back” button
/// built into some mice on Windows.
///
/// Commands are lowercased, underscores are replaced with hyphens,
/// and the APPCOMMAND_ prefix is stripped off.e.g.APPCOMMAND_BROWSER_BACKWARD
/// Commands are lowercased, underscores are replaced with hyphens,
/// and the APPCOMMAND_ prefix is stripped off.e.g.APPCOMMAND_BROWSER_BACKWARD
/// is emitted as browser-backward.
/// </summary>
[SupportedOSPlatform("Windows")]
[SupportedOSPlatform("Linux")]
public event Action<string> OnAppCommand
{
add => AddEvent(value, Id);
@@ -247,6 +266,7 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Emitted on 3-finger swipe. Possible directions are up, right, down, left.
/// </summary>
[SupportedOSPlatform("macOS")]
public event Action<string> OnSwipe
{
add => AddEvent(value, Id);
@@ -256,6 +276,7 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Emitted when the window opens a sheet.
/// </summary>
[SupportedOSPlatform("macOS")]
public event Action OnSheetBegin
{
add => AddEvent(value, Id);
@@ -265,6 +286,7 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Emitted when the window has closed a sheet.
/// </summary>
[SupportedOSPlatform("macOS")]
public event Action OnSheetEnd
{
add => AddEvent(value, Id);
@@ -274,6 +296,7 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Emitted when the native new tab button is clicked.
/// </summary>
[SupportedOSPlatform("macOS")]
public event Action OnNewWindowForTab
{
add => AddEvent(value, Id);
@@ -287,15 +310,15 @@ public class BrowserWindow : ApiBase
}
/// <summary>
/// Force closing the window, the unload and beforeunload event wont be
/// emitted for the web page, and close event will also not be emitted
/// Force closing the window, the unload and beforeunload event wont be
/// emitted for the web page, and close event will also not be emitted
/// for this window, but it guarantees the closed event will be emitted.
/// </summary>
public void Destroy() => this.CallMethod0();
/// <summary>
/// Try to close the window. This has the same effect as a user manually
/// clicking the close button of the window. The web page may cancel the close though.
/// Try to close the window. This has the same effect as a user manually
/// clicking the close button of the window. The web page may cancel the close though.
/// </summary>
public void Close() => this.CallMethod0();
@@ -313,13 +336,13 @@ public class BrowserWindow : ApiBase
/// Whether the window is focused.
/// </summary>
/// <returns></returns>
public Task<bool> IsFocusedAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsFocusedAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Whether the window is destroyed.
/// </summary>
/// <returns></returns>
public Task<bool> IsDestroyedAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsDestroyedAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Shows and gives focus to the window.
@@ -340,13 +363,13 @@ public class BrowserWindow : ApiBase
/// Whether the window is visible to the user.
/// </summary>
/// <returns></returns>
public Task<bool> IsVisibleAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsVisibleAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Whether current window is a modal window.
/// </summary>
/// <returns></returns>
public Task<bool> IsModalAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsModalAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Maximizes the window. This will also show (but not focus) the window if it isnt being displayed already.
@@ -362,7 +385,7 @@ public class BrowserWindow : ApiBase
/// Whether the window is maximized.
/// </summary>
/// <returns></returns>
public Task<bool> IsMaximizedAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsMaximizedAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Minimizes the window. On some platforms the minimized window will be shown in the Dock.
@@ -378,7 +401,7 @@ public class BrowserWindow : ApiBase
/// Whether the window is minimized.
/// </summary>
/// <returns></returns>
public Task<bool> IsMinimizedAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsMinimizedAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window should be in fullscreen mode.
@@ -390,10 +413,10 @@ public class BrowserWindow : ApiBase
/// Whether the window is in fullscreen mode.
/// </summary>
/// <returns></returns>
public Task<bool> IsFullScreenAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsFullScreenAsync() => this.InvokeAsync<bool>();
/// <summary>
/// This will make a window maintain an aspect ratio. The extra size allows a developer to have space,
/// This will make a window maintain an aspect ratio. The extra size allows a developer to have space,
/// specified in pixels, not included within the aspect ratio calculations. This API already takes into
/// account the difference between a windows size and its content size.
///
@@ -401,7 +424,7 @@ public class BrowserWindow : ApiBase
/// of controls on the left edge, 25 pixels of controls on the right edge and 50 pixels of controls below
/// the player. In order to maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within
/// the player itself we would call this function with arguments of 16/9 and[40, 50]. The second argument
/// doesnt care where the extra width and height are within the content viewonly that they exist. Just
/// doesnt care where the extra width and height are within the content viewonly that they exist. Just
/// sum any extra width and height areas you have within the overall content view.
/// </summary>
/// <param name="aspectRatio">The aspect ratio to maintain for some portion of the content view.</param>
@@ -410,7 +433,7 @@ public class BrowserWindow : ApiBase
this.CallMethod2(aspectRatio, extraSize);
/// <summary>
/// This will make a window maintain an aspect ratio. The extra size allows a developer to have space,
/// This will make a window maintain an aspect ratio. The extra size allows a developer to have space,
/// specified in pixels, not included within the aspect ratio calculations. This API already takes into
/// account the difference between a windows size and its content size.
///
@@ -418,7 +441,7 @@ public class BrowserWindow : ApiBase
/// of controls on the left edge, 25 pixels of controls on the right edge and 50 pixels of controls below
/// the player. In order to maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within
/// the player itself we would call this function with arguments of 16/9 and[40, 50]. The second argument
/// doesnt care where the extra width and height are within the content viewonly that they exist. Just
/// doesnt care where the extra width and height are within the content viewonly that they exist. Just
/// sum any extra width and height areas you have within the overall content view.
/// </summary>
/// <param name="aspectRatio">The aspect ratio to maintain for some portion of the content view.</param>
@@ -429,24 +452,27 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Uses Quick Look to preview a file at a given path.
/// </summary>
/// <param name="path">The absolute path to the file to preview with QuickLook. This is important as
/// Quick Look uses the file name and file extension on the path to determine the content type of the
/// <param name="path">The absolute path to the file to preview with QuickLook. This is important as
/// Quick Look uses the file name and file extension on the path to determine the content type of the
/// file to open.</param>
[SupportedOSPlatform("macOS")]
public void PreviewFile(string path) => this.CallMethod1(path);
/// <summary>
/// Uses Quick Look to preview a file at a given path.
/// </summary>
/// <param name="path">The absolute path to the file to preview with QuickLook. This is important as
/// Quick Look uses the file name and file extension on the path to determine the content type of the
/// <param name="path">The absolute path to the file to preview with QuickLook. This is important as
/// Quick Look uses the file name and file extension on the path to determine the content type of the
/// file to open.</param>
/// <param name="displayname">The name of the file to display on the Quick Look modal view. This is
/// <param name="displayname">The name of the file to display on the Quick Look modal view. This is
/// purely visual and does not affect the content type of the file. Defaults to path.</param>
[SupportedOSPlatform("macOS")]
public void PreviewFile(string path, string displayname) => this.CallMethod2(path, displayname);
/// <summary>
/// Closes the currently open Quick Look panel.
/// </summary>
[SupportedOSPlatform("macOS")]
public void CloseFilePreview() => this.CallMethod0();
/// <summary>
@@ -466,7 +492,7 @@ public class BrowserWindow : ApiBase
/// Gets the bounds asynchronous.
/// </summary>
/// <returns></returns>
public Task<Rectangle> GetBoundsAsync() => this.GetPropertyAsync<Rectangle>();
public Task<Rectangle> GetBoundsAsync() => this.InvokeAsync<Rectangle>();
/// <summary>
/// Resizes and moves the windows client area (e.g. the web page) to the supplied bounds.
@@ -485,7 +511,7 @@ public class BrowserWindow : ApiBase
/// Gets the content bounds asynchronous.
/// </summary>
/// <returns></returns>
public Task<Rectangle> GetContentBoundsAsync() => this.GetPropertyAsync<Rectangle>();
public Task<Rectangle> GetContentBoundsAsync() => this.InvokeAsync<Rectangle>();
/// <summary>
/// Resizes the window to width and height.
@@ -506,7 +532,7 @@ public class BrowserWindow : ApiBase
/// Contains the windows width and height.
/// </summary>
/// <returns></returns>
public Task<int[]> GetSizeAsync() => this.GetPropertyAsync<int[]>();
public Task<int[]> GetSizeAsync() => this.InvokeAsync<int[]>();
/// <summary>
/// Resizes the windows client area (e.g. the web page) to width and height.
@@ -527,7 +553,7 @@ public class BrowserWindow : ApiBase
/// Contains the windows client areas width and height.
/// </summary>
/// <returns></returns>
public Task<int[]> GetContentSizeAsync() => this.GetPropertyAsync<int[]>();
public Task<int[]> GetContentSizeAsync() => this.InvokeAsync<int[]>();
/// <summary>
/// Sets the minimum size of window to width and height.
@@ -540,7 +566,7 @@ public class BrowserWindow : ApiBase
/// Contains the windows minimum width and height.
/// </summary>
/// <returns></returns>
public Task<int[]> GetMinimumSizeAsync() => this.GetPropertyAsync<int[]>();
public Task<int[]> GetMinimumSizeAsync() => this.InvokeAsync<int[]>();
/// <summary>
/// Sets the maximum size of window to width and height.
@@ -553,7 +579,7 @@ public class BrowserWindow : ApiBase
/// Contains the windows maximum width and height.
/// </summary>
/// <returns></returns>
public Task<int[]> GetMaximumSizeAsync() => this.GetPropertyAsync<int[]>();
public Task<int[]> GetMaximumSizeAsync() => this.InvokeAsync<int[]>();
/// <summary>
/// Sets whether the window can be manually resized by user.
@@ -565,12 +591,14 @@ public class BrowserWindow : ApiBase
/// Whether the window can be manually resized by user.
/// </summary>
/// <returns></returns>
public Task<bool> IsResizableAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsResizableAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window can be moved by user. On Linux does nothing.
/// </summary>
/// <param name="movable"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetMovable(bool movable) => this.CallMethod1(movable);
/// <summary>
@@ -579,12 +607,16 @@ public class BrowserWindow : ApiBase
/// On Linux always returns true.
/// </summary>
/// <returns>On Linux always returns true.</returns>
public Task<bool> IsMovableAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task<bool> IsMovableAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window can be manually minimized by user. On Linux does nothing.
/// </summary>
/// <param name="minimizable"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetMinimizable(bool minimizable) => this.CallMethod1(minimizable);
/// <summary>
@@ -593,12 +625,16 @@ public class BrowserWindow : ApiBase
/// On Linux always returns true.
/// </summary>
/// <returns>On Linux always returns true.</returns>
public Task<bool> IsMinimizableAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task<bool> IsMinimizableAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window can be manually maximized by user. On Linux does nothing.
/// </summary>
/// <param name="maximizable"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetMaximizable(bool maximizable) => this.CallMethod1(maximizable);
/// <summary>
@@ -607,7 +643,9 @@ public class BrowserWindow : ApiBase
/// On Linux always returns true.
/// </summary>
/// <returns>On Linux always returns true.</returns>
public Task<bool> IsMaximizableAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task<bool> IsMaximizableAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the maximize/zoom window button toggles fullscreen mode or maximizes the window.
@@ -619,12 +657,14 @@ public class BrowserWindow : ApiBase
/// Whether the maximize/zoom window button toggles fullscreen mode or maximizes the window.
/// </summary>
/// <returns></returns>
public Task<bool> IsFullScreenableAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsFullScreenableAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window can be manually closed by user. On Linux does nothing.
/// </summary>
/// <param name="closable"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetClosable(bool closable) => this.CallMethod1(closable);
/// <summary>
@@ -633,45 +673,47 @@ public class BrowserWindow : ApiBase
/// On Linux always returns true.
/// </summary>
/// <returns>On Linux always returns true.</returns>
public Task<bool> IsClosableAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task<bool> IsClosableAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window should show always on top of other windows.
/// After setting this, the window is still a normal window, not a toolbox
/// Sets whether the window should show always on top of other windows.
/// After setting this, the window is still a normal window, not a toolbox
/// window which can not be focused on.
/// </summary>
/// <param name="flag"></param>
public void SetAlwaysOnTop(bool flag) => this.CallMethod1(flag);
/// <summary>
/// Sets whether the window should show always on top of other windows.
/// After setting this, the window is still a normal window, not a toolbox
/// Sets whether the window should show always on top of other windows.
/// After setting this, the window is still a normal window, not a toolbox
/// window which can not be focused on.
/// </summary>
/// <param name="flag"></param>
/// <param name="level">Values include normal, floating, torn-off-menu, modal-panel, main-menu,
/// status, pop-up-menu and screen-saver. The default is floating.
/// <param name="level">Values include normal, floating, torn-off-menu, modal-panel, main-menu,
/// status, pop-up-menu and screen-saver. The default is floating.
/// See the macOS docs</param>
public void SetAlwaysOnTop(bool flag, OnTopLevel level) => this.CallMethod2(flag, level.GetDescription());
public void SetAlwaysOnTop(bool flag, OnTopLevel level) => this.CallMethod2(flag, level);
/// <summary>
/// Sets whether the window should show always on top of other windows.
/// After setting this, the window is still a normal window, not a toolbox
/// Sets whether the window should show always on top of other windows.
/// After setting this, the window is still a normal window, not a toolbox
/// window which can not be focused on.
/// </summary>
/// <param name="flag"></param>
/// <param name="level">Values include normal, floating, torn-off-menu, modal-panel, main-menu,
/// status, pop-up-menu and screen-saver. The default is floating.
/// <param name="level">Values include normal, floating, torn-off-menu, modal-panel, main-menu,
/// status, pop-up-menu and screen-saver. The default is floating.
/// See the macOS docs</param>
/// <param name="relativeLevel">The number of layers higher to set this window relative to the given level.
/// <param name="relativeLevel">The number of layers higher to set this window relative to the given level.
/// The default is 0. Note that Apple discourages setting levels higher than 1 above screen-saver.</param>
public void SetAlwaysOnTop(bool flag, OnTopLevel level, int relativeLevel) => this.CallMethod3(flag, level.GetDescription(), relativeLevel);
public void SetAlwaysOnTop(bool flag, OnTopLevel level, int relativeLevel) => this.CallMethod3(flag, level, relativeLevel);
/// <summary>
/// Whether the window is always on top of other windows.
/// </summary>
/// <returns></returns>
public Task<bool> IsAlwaysOnTopAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsAlwaysOnTopAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Moves window to the center of the screen.
@@ -721,7 +763,7 @@ public class BrowserWindow : ApiBase
/// Contains the windows current position.
/// </summary>
/// <returns></returns>
public Task<int[]> GetPositionAsync() => this.GetPropertyAsync<int[]>();
public Task<int[]> GetPositionAsync() => this.InvokeAsync<int[]>();
/// <summary>
/// Changes the title of native window to title.
@@ -735,23 +777,25 @@ public class BrowserWindow : ApiBase
/// Note: The title of web page can be different from the title of the native window.
/// </summary>
/// <returns></returns>
public Task<string> GetTitleAsync() => this.GetPropertyAsync<string>();
public Task<string> GetTitleAsync() => this.InvokeAsync<string>();
/// <summary>
/// Changes the attachment point for sheets on macOS.
/// By default, sheets are attached just below the window frame,
/// Changes the attachment point for sheets on macOS.
/// By default, sheets are attached just below the window frame,
/// but you may want to display them beneath a HTML-rendered toolbar.
/// </summary>
/// <param name="offsetY"></param>
[SupportedOSPlatform("macOS")]
public void SetSheetOffset(float offsetY) => this.CallMethod1(offsetY);
/// <summary>
/// Changes the attachment point for sheets on macOS.
/// By default, sheets are attached just below the window frame,
/// Changes the attachment point for sheets on macOS.
/// By default, sheets are attached just below the window frame,
/// but you may want to display them beneath a HTML-rendered toolbar.
/// </summary>
/// <param name="offsetY"></param>
/// <param name="offsetX"></param>
[SupportedOSPlatform("macOS")]
public void SetSheetOffset(float offsetY, float offsetX) => this.CallMethod2(offsetY, offsetX);
/// <summary>
@@ -764,6 +808,8 @@ public class BrowserWindow : ApiBase
/// Makes the window not show in the taskbar.
/// </summary>
/// <param name="skip"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetSkipTaskbar(bool skip) => this.CallMethod1(skip);
/// <summary>
@@ -776,39 +822,43 @@ public class BrowserWindow : ApiBase
/// Whether the window is in kiosk mode.
/// </summary>
/// <returns></returns>
public Task<bool> IsKioskAsync() => this.GetPropertyAsync<bool>();
public Task<bool> IsKioskAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Returns the native type of the handle is HWND on Windows, NSView* on macOS, and Window (unsigned long) on Linux.
/// </summary>
/// <returns>string of the native handle obtained, HWND on Windows, NSView* on macOS, and Window (unsigned long) on Linux.</returns>
public Task<string> GetNativeWindowHandle() => this.GetPropertyAsync<string>();
public Task<string> GetNativeWindowHandle() => this.InvokeAsync<string>();
/// <summary>
/// Sets the pathname of the file the window represents,
/// Sets the pathname of the file the window represents,
/// and the icon of the file will show in windows title bar.
/// </summary>
/// <param name="filename"></param>
[SupportedOSPlatform("macOS")]
public void SetRepresentedFilename(string filename) => this.CallMethod1(filename);
/// <summary>
/// The pathname of the file the window represents.
/// </summary>
/// <returns></returns>
public Task<string> GetRepresentedFilenameAsync() => this.GetPropertyAsync<string>();
[SupportedOSPlatform("macOS")]
public Task<string> GetRepresentedFilenameAsync() => this.InvokeAsync<string>();
/// <summary>
/// Specifies whether the windows document has been edited,
/// Specifies whether the windows document has been edited,
/// and the icon in title bar will become gray when set to true.
/// </summary>
/// <param name="edited"></param>
[SupportedOSPlatform("macOS")]
public void SetDocumentEdited(bool edited) => this.CallMethod1(edited);
/// <summary>
/// Whether the windows document has been edited.
/// </summary>
/// <returns></returns>
public Task<bool> IsDocumentEditedAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("macOS")]
public Task<bool> IsDocumentEditedAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Focuses the on web view.
@@ -821,14 +871,14 @@ public class BrowserWindow : ApiBase
public void BlurWebView() => this.CallMethod0();
/// <summary>
/// The url can be a remote address (e.g. http://) or
/// The url can be a remote address (e.g. http://) or
/// a path to a local HTML file using the file:// protocol.
/// </summary>
/// <param name="url"></param>
public void LoadURL(string url) => this.CallMethod1(url);
/// <summary>
/// The url can be a remote address (e.g. http://) or
/// The url can be a remote address (e.g. http://) or
/// a path to a local HTML file using the file:// protocol.
/// </summary>
/// <param name="url"></param>
@@ -857,10 +907,12 @@ public class BrowserWindow : ApiBase
private List<MenuItem> _items = new List<MenuItem>();
/// <summary>
/// Sets the menu as the windows menu bar,
/// Sets the menu as the windows menu bar,
/// setting it to null will remove the menu bar.
/// </summary>
/// <param name="menuItems"></param>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
public void SetMenu(MenuItem[] menuItems)
{
menuItems.AddMenuItemsId();
@@ -878,6 +930,8 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Remove the window's menu bar.
/// </summary>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
public void RemoveMenu() => this.CallMethod0();
/// <summary>
@@ -920,7 +974,7 @@ public class BrowserWindow : ApiBase
/// On Windows and Linux always returns true.
/// </summary>
/// <returns></returns>
public Task<bool> HasShadowAsync() => this.GetPropertyAsync<bool>();
public Task<bool> HasShadowAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Gets the thumbar buttons.
@@ -939,17 +993,18 @@ public class BrowserWindow : ApiBase
private List<ThumbarButton> _thumbarButtons = new List<ThumbarButton>();
/// <summary>
/// Add a thumbnail toolbar with a specified set of buttons to the thumbnail
/// image of a window in a taskbar button layout. Returns a Boolean object
/// Add a thumbnail toolbar with a specified set of buttons to the thumbnail
/// image of a window in a taskbar button layout. Returns a Boolean object
/// indicates whether the thumbnail has been added successfully.
///
/// The number of buttons in thumbnail toolbar should be no greater than 7 due
/// The number of buttons in thumbnail toolbar should be no greater than 7 due
/// to the limited room.Once you setup the thumbnail toolbar, the toolbar cannot
/// be removed due to the platforms limitation.But you can call the API with an
/// empty array to clean the buttons.
/// </summary>
/// <param name="thumbarButtons"></param>
/// <returns>Whether the buttons were added successfully.</returns>
[SupportedOSPlatform("Windows")]
public Task<bool> SetThumbarButtonsAsync(ThumbarButton[] thumbarButtons)
{
var tcs = new TaskCompletionSource<bool>();
@@ -977,55 +1032,67 @@ public class BrowserWindow : ApiBase
/// an empty region: {x: 0, y: 0, width: 0, height: 0}.
/// </summary>
/// <param name="rectangle"></param>
[SupportedOSPlatform("Windows")]
public void SetThumbnailClip(Rectangle rectangle) => this.CallMethod1(rectangle);
/// <summary>
/// Sets the toolTip that is displayed when hovering over the window thumbnail in the taskbar.
/// </summary>
/// <param name="tooltip"></param>
[SupportedOSPlatform("Windows")]
public void SetThumbnailToolTip(string tooltip) => this.CallMethod1(tooltip);
/// <summary>
/// Sets the properties for the windows taskbar button.
///
/// Note: relaunchCommand and relaunchDisplayName must always be set together.
/// Note: relaunchCommand and relaunchDisplayName must always be set together.
/// If one of those properties is not set, then neither will be used.
/// </summary>
/// <param name="options"></param>
[SupportedOSPlatform("Windows")]
public void SetAppDetails(AppDetailsOptions options) => this.CallMethod1(options);
/// <summary>
/// Same as webContents.showDefinitionForSelection().
/// </summary>
[SupportedOSPlatform("macOS")]
public void ShowDefinitionForSelection() => this.CallMethod0();
/// <summary>
/// Sets whether the window menu bar should hide itself automatically.
/// Sets whether the window menu bar should hide itself automatically.
/// Once set the menu bar will only show when users press the single Alt key.
///
/// If the menu bar is already visible, calling setAutoHideMenuBar(true) wont hide it immediately.
/// </summary>
/// <param name="hide"></param>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
public void SetAutoHideMenuBar(bool hide) => this.CallMethod1(hide);
/// <summary>
/// Whether menu bar automatically hides itself.
/// </summary>
/// <returns></returns>
public Task<bool> IsMenuBarAutoHideAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
public Task<bool> IsMenuBarAutoHideAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the menu bar should be visible. If the menu bar is auto-hide,
/// users can still bring up the menu bar by pressing the single Alt key.
/// </summary>
/// <param name="visible"></param>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
public void SetMenuBarVisibility(bool visible) => this.CallMethod1(visible);
/// <summary>
/// Whether the menu bar is visible.
/// </summary>
/// <returns></returns>
public Task<bool> IsMenuBarVisibleAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
public Task<bool> IsMenuBarVisibleAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Sets whether the window should be visible on all workspaces.
@@ -1033,6 +1100,8 @@ public class BrowserWindow : ApiBase
/// Note: This API does nothing on Windows.
/// </summary>
/// <param name="visible"></param>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("macOS")]
public void SetVisibleOnAllWorkspaces(bool visible) => this.CallMethod1(visible);
/// <summary>
@@ -1041,12 +1110,14 @@ public class BrowserWindow : ApiBase
/// Note: This API always returns false on Windows.
/// </summary>
/// <returns></returns>
public Task<bool> IsVisibleOnAllWorkspacesAsync() => this.GetPropertyAsync<bool>();
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("macOS")]
public Task<bool> IsVisibleOnAllWorkspacesAsync() => this.InvokeAsync<bool>();
/// <summary>
/// Makes the window ignore all mouse events.
///
/// All mouse events happened in this window will be passed to the window
/// All mouse events happened in this window will be passed to the window
/// below this window, but if this window has focus, it will still receive keyboard events.
/// </summary>
/// <param name="ignore"></param>
@@ -1055,20 +1126,24 @@ public class BrowserWindow : ApiBase
/// <summary>
/// Prevents the window contents from being captured by other apps.
///
/// On macOS it sets the NSWindows sharingType to NSWindowSharingNone.
/// On macOS it sets the NSWindows sharingType to NSWindowSharingNone.
/// On Windows it calls SetWindowDisplayAffinity with WDA_MONITOR.
/// </summary>
/// <param name="enable"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetContentProtection(bool enable) => this.CallMethod1(enable);
/// <summary>
/// Changes whether the window can be focused.
/// </summary>
/// <param name="focusable"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void SetFocusable(bool focusable) => this.CallMethod1(focusable);
/// <summary>
/// Sets parent as current windows parent window,
/// Sets parent as current windows parent window,
/// passing null will turn current window into a top-level window.
/// </summary>
/// <param name="parent"></param>
@@ -1090,7 +1165,7 @@ public class BrowserWindow : ApiBase
/// <returns></returns>
public async Task<BrowserWindow> GetParentWindowAsync()
{
var browserWindowId = await this.GetPropertyAsync<int>().ConfigureAwait(false);
var browserWindowId = await this.InvokeAsync<int>().ConfigureAwait(false);
var browserWindow = Electron.WindowManager.BrowserWindows.ToList().Single(x => x.Id == browserWindowId);
return browserWindow;
}
@@ -1101,7 +1176,7 @@ public class BrowserWindow : ApiBase
/// <returns></returns>
public async Task<List<BrowserWindow>> GetChildWindowsAsync()
{
var browserWindowIds = await this.GetPropertyAsync<int[]>().ConfigureAwait(false);
var browserWindowIds = await this.InvokeAsync<int[]>().ConfigureAwait(false);
var browserWindows = new List<BrowserWindow>();
foreach (var id in browserWindowIds)
@@ -1117,16 +1192,18 @@ public class BrowserWindow : ApiBase
/// Controls whether to hide cursor when typing.
/// </summary>
/// <param name="autoHide"></param>
[SupportedOSPlatform("macOS")]
public void SetAutoHideCursor(bool autoHide) => this.CallMethod1(autoHide);
/// <summary>
/// Adds a vibrancy effect to the browser window.
/// Adds a vibrancy effect to the browser window.
/// Passing null or an empty string will remove the vibrancy effect on the window.
/// </summary>
/// <param name="type">Can be appearance-based, light, dark, titlebar, selection,
/// menu, popover, sidebar, medium-light or ultra-dark.
/// <param name="type">Can be appearance-based, light, dark, titlebar, selection,
/// menu, popover, sidebar, medium-light or ultra-dark.
/// See the macOS documentation for more details.</param>
public void SetVibrancy(Vibrancy type) => this.CallMethod1(type.GetDescription());
[SupportedOSPlatform("macOS")]
public void SetVibrancy(Vibrancy type) => this.CallMethod1(type);
/// <summary>
/// Render and control web pages.
@@ -1134,8 +1211,8 @@ public class BrowserWindow : ApiBase
public WebContents WebContents { get; internal set; }
/// <summary>
/// A BrowserView can be used to embed additional web content into a BrowserWindow.
/// It is like a child window, except that it is positioned relative to its owning window.
/// A BrowserView can be used to embed additional web content into a BrowserWindow.
/// It is like a child window, except that it is positioned relative to its owning window.
/// It is meant to be an alternative to the webview tag.
/// </summary>
/// <param name="browserView"></param>
@@ -1144,4 +1221,4 @@ public class BrowserWindow : ApiBase
// This message name does not match the default ApiBase naming convention.
BridgeConnector.Socket.Emit("browserWindow-setBrowserView", Id, browserView.Id);
}
}
}

View File

@@ -1,7 +1,9 @@
using ElectronNET.API.Entities;
using ElectronNET.API.Serialization;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Threading.Tasks;
// ReSharper disable InconsistentNaming
namespace ElectronNET.API
@@ -9,7 +11,7 @@ namespace ElectronNET.API
/// <summary>
/// Perform copy and paste operations on the system clipboard.
/// </summary>
public sealed class Clipboard: ApiBase
public sealed class Clipboard : ApiBase
{
protected override SocketTaskEventNameTypes SocketTaskEventNameType => SocketTaskEventNameTypes.DashesLowerFirst;
protected override SocketTaskMessageNameTypes SocketTaskMessageNameType => SocketTaskMessageNameTypes.DashesLowerFirst;
@@ -45,7 +47,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns>The content in the clipboard as plain text.</returns>
public Task<string> ReadTextAsync(string type = "") => GetPropertyAsync<string>(type);
public Task<string> ReadTextAsync(string type = "") => this.InvokeAsync<string>(type);
/// <summary>
/// Writes the text into the clipboard as plain text.
@@ -62,7 +64,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string> ReadHTMLAsync(string type = "") => GetPropertyAsync<string>(type);
public Task<string> ReadHTMLAsync(string type = "") => this.InvokeAsync<string>(type);
/// <summary>
/// Writes markup to the clipboard.
@@ -79,7 +81,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string> ReadRTFAsync(string type = "") => GetPropertyAsync<string>(type);
public Task<string> ReadRTFAsync(string type = "") => this.InvokeAsync<string>(type);
/// <summary>
/// Writes the text into the clipboard in RTF.
@@ -92,23 +94,27 @@ namespace ElectronNET.API
}
/// <summary>
/// Returns an Object containing title and url keys representing
/// the bookmark in the clipboard. The title and url values will
/// Returns an Object containing title and url keys representing
/// the bookmark in the clipboard. The title and url values will
/// be empty strings when the bookmark is unavailable.
/// </summary>
/// <returns></returns>
public Task<ReadBookmark> ReadBookmarkAsync() => GetPropertyAsync<ReadBookmark>();
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task<ReadBookmark> ReadBookmarkAsync() => this.InvokeAsync<ReadBookmark>();
/// <summary>
/// Writes the title and url into the clipboard as a bookmark.
///
/// Note: Most apps on Windows dont support pasting bookmarks
/// into them so you can use clipboard.write to write both a
/// into them so you can use clipboard.write to write both a
/// bookmark and fallback text to the clipboard.
/// </summary>
/// <param name="title"></param>
/// <param name="url"></param>
/// <param name="type"></param>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public void WriteBookmark(string title, string url, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-writeBookmark", title, url, type);
@@ -120,13 +126,15 @@ namespace ElectronNET.API
/// find pasteboard whenever the application is activated.
/// </summary>
/// <returns></returns>
public Task<string> ReadFindTextAsync() => GetPropertyAsync<string>();
[SupportedOSPlatform("macOS")]
public Task<string> ReadFindTextAsync() => this.InvokeAsync<string>();
/// <summary>
/// macOS: Writes the text into the find pasteboard as plain text. This method uses
/// macOS: Writes the text into the find pasteboard as plain text. This method uses
/// synchronous IPC when called from the renderer process.
/// </summary>
/// <param name="text"></param>
[SupportedOSPlatform("macOS")]
public void WriteFindText(string text)
{
BridgeConnector.Socket.Emit("clipboard-writeFindText", text);
@@ -146,7 +154,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string[]> AvailableFormatsAsync(string type = "") => GetPropertyAsync<string[]>(type);
public Task<string[]> AvailableFormatsAsync(string type = "") => this.InvokeAsync<string[]>(type);
/// <summary>
/// Writes data to the clipboard.
@@ -163,7 +171,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<NativeImage> ReadImageAsync(string type = "") => GetPropertyAsync<NativeImage>(type);
public Task<NativeImage> ReadImageAsync(string type = "") => this.InvokeAsync<NativeImage>(type);
/// <summary>
/// Writes an image to the clipboard.
@@ -175,4 +183,4 @@ namespace ElectronNET.API
BridgeConnector.Socket.Emit("clipboard-writeImage", JsonSerializer.Serialize(image, ElectronJson.Options), type);
}
}
}
}

View File

@@ -2,6 +2,7 @@ using ElectronNET.API.Entities;
using ElectronNET.API.Serialization;
using System;
using System.Text.Json;
using System.Threading.Tasks;
namespace ElectronNET.API
{
@@ -54,12 +55,79 @@ namespace ElectronNET.API
_changed -= value;
if (_changed == null)
{
BridgeConnector.Socket.Off("webContents-session-cookies-changed" + Id);
}
}
}
private event Action<Cookie, CookieChangedCause, bool> _changed;
/// <summary>
/// Sends a request to get all cookies matching filter, and resolves a callack with the response.
/// </summary>
/// <param name="filter">
/// </param>
/// <returns>A task which resolves an array of cookie objects.</returns>
public Task<Cookie[]> GetAsync(CookieFilter filter)
{
var tcs = new TaskCompletionSource<Cookie[]>();
var guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.Once<Cookie[]>("webContents-session-cookies-get-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-get", Id, filter, guid);
return tcs.Task;
}
/// <summary>
///
/// </summary>
/// <param name="details"></param>
/// <returns></returns>
public Task SetAsync(CookieDetails details)
{
var tcs = new TaskCompletionSource<object>();
var guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.Once<object>("webContents-session-cookies-set-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-set", Id, details, guid);
return tcs.Task;
}
/// <summary>
/// Removes the cookies matching url and name
/// </summary>
/// <param name="url">The URL associated with the cookie.</param>
/// <param name="name">The name of cookie to remove.</param>
/// <returns>A task which resolves when the cookie has been removed</returns>
public Task RemoveAsync(string url, string name)
{
var tcs = new TaskCompletionSource<object>();
var guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.Once<object>("webContents-session-cookies-remove-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-remove", Id, url, name, guid);
return tcs.Task;
}
/// <summary>
/// Writes any unwritten cookies data to disk.
/// </summary>
/// <returns>A task which resolves when the cookie store has been flushed</returns>
public Task FlushStoreAsync()
{
var tcs = new TaskCompletionSource<object>();
var guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.Once<object>("webContents-session-cookies-flushStore-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-flushStore", Id, guid);
return tcs.Task;
}
}
}
}

View File

@@ -1,5 +1,6 @@
using ElectronNET.API.Entities;
using System;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Threading.Tasks;
@@ -37,8 +38,8 @@ namespace ElectronNET.API
}
/// <summary>
/// Note: On Windows and Linux an open dialog can not be both a file selector
/// and a directory selector, so if you set properties to ['openFile', 'openDirectory']
/// Note: On Windows and Linux an open dialog can not be both a file selector
/// and a directory selector, so if you set properties to ['openFile', 'openDirectory']
/// on these platforms, a directory selector will be shown.
/// </summary>
/// <param name="browserWindow">The browserWindow argument allows the dialog to attach itself to a parent window, making it modal.</param>
@@ -50,9 +51,9 @@ namespace ElectronNET.API
var guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.Once<string[]>("showOpenDialogComplete" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("showOpenDialog",
browserWindow,
options,
BridgeConnector.Socket.Emit("showOpenDialog",
browserWindow,
options,
guid);
return tcs.Task;
@@ -167,9 +168,9 @@ namespace ElectronNET.API
/// <summary>
/// Displays a modal dialog that shows an error message.
///
/// This API can be called safely before the ready event the app module emits,
/// it is usually used to report errors in early stage of startup.If called
/// before the app readyevent on Linux, the message will be emitted to stderr,
/// This API can be called safely before the ready event the app module emits,
/// it is usually used to report errors in early stage of startup.If called
/// before the app readyevent on Linux, the message will be emitted to stderr,
/// and no GUI dialog will appear.
/// </summary>
/// <param name="title">The title to display in the error box.</param>
@@ -181,11 +182,13 @@ namespace ElectronNET.API
/// <summary>
/// On macOS, this displays a modal dialog that shows a message and certificate information,
/// and gives the user the option of trusting/importing the certificate. If you provide a
/// and gives the user the option of trusting/importing the certificate. If you provide a
/// browserWindow argument the dialog will be attached to the parent window, making it modal.
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task ShowCertificateTrustDialogAsync(CertificateTrustDialogOptions options)
{
return ShowCertificateTrustDialogAsync(null, options);
@@ -193,18 +196,20 @@ namespace ElectronNET.API
/// <summary>
/// On macOS, this displays a modal dialog that shows a message and certificate information,
/// and gives the user the option of trusting/importing the certificate. If you provide a
/// and gives the user the option of trusting/importing the certificate. If you provide a
/// browserWindow argument the dialog will be attached to the parent window, making it modal.
/// </summary>
/// <param name="browserWindow"></param>
/// <param name="options"></param>
/// <returns></returns>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public Task ShowCertificateTrustDialogAsync(BrowserWindow browserWindow, CertificateTrustDialogOptions options)
{
var tcs = new TaskCompletionSource<object>();
var tcs = new TaskCompletionSource();
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.Once("showCertificateTrustDialogComplete" + guid, () => tcs.SetResult(null));
BridgeConnector.Socket.Once("showCertificateTrustDialogComplete" + guid, () => tcs.SetResult());
BridgeConnector.Socket.Emit("showCertificateTrustDialog",
browserWindow,
options,
@@ -212,7 +217,5 @@ namespace ElectronNET.API
return tcs.Task;
}
}
}
}

View File

@@ -1,16 +1,16 @@
using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
using ElectronNET.API.Serialization;
using System.Collections.Generic;
using System.Text.Json;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
namespace ElectronNET.API
{
/// <summary>
/// Control your app in the macOS dock.
/// </summary>
[SupportedOSPlatform("macOS")]
public sealed class Dock
{
private static Dock _dock;
@@ -57,7 +57,7 @@ namespace ElectronNET.API
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
{
BridgeConnector.Socket.Once<int>("dock-bounce-completed", tcs.SetResult);
BridgeConnector.Socket.Emit("dock-bounce", type.GetDescription());
BridgeConnector.Socket.Emit("dock-bounce", type);
return await tcs.Task
.ConfigureAwait(false);
@@ -208,7 +208,5 @@ namespace ElectronNET.API
{
BridgeConnector.Socket.Emit("dock-setIcon", image);
}
}
}
}

View File

@@ -140,7 +140,7 @@
/// <summary>
/// Allows you to execute native JavaScript/TypeScript code from the host process.
///
/// It is only possible if the Electron.NET CLI has previously added an
/// It is only possible if the Electron.NET CLI has previously added an
/// ElectronHostHook directory:
/// <c>electronize add HostHook</c>
/// </summary>
@@ -153,7 +153,7 @@
}
/// <summary>
/// Allows you to execute native Lock and Unlock process.
/// Allows you to execute native Lock and Unlock process.
/// </summary>
public static PowerMonitor PowerMonitor
{

View File

@@ -1,8 +1,11 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
/// About panel options.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class AboutPanelOptions
{
/// <summary>
@@ -21,28 +24,35 @@
public string Copyright { get; set; }
/// <summary>
/// The app's build version number.
/// The app's build version number (macOS).
/// </summary>
[SupportedOSPlatform("macos")]
public string Version { get; set; }
/// <summary>
/// Credit information.
/// Credit information (macOS, Windows).
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
public string Credits { get; set; }
/// <summary>
/// List of app authors.
/// List of app authors (Linux).
/// </summary>
[SupportedOSPlatform("linux")]
public string[] Authors { get; set; }
/// <summary>
/// The app's website.
/// The app's website (Linux).
/// </summary>
[SupportedOSPlatform("linux")]
public string Website { get; set; }
/// <summary>
/// Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
/// Path to the app's icon in a JPEG or PNG file format. On Linux, will be shown as 64x64 pixels while retaining aspect ratio. On Windows, a 48x48 PNG will result in the best visual quality.
/// </summary>
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("windows")]
public string IconPath { get; set; }
}
}

View File

@@ -1,32 +1,35 @@
using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class AddRepresentationOptions
{
/// <summary>
/// Gets or sets the width
/// Gets or sets the width in pixels. Defaults to 0. Required if a bitmap buffer is specified as <see cref="Buffer"/>.
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// Gets or sets the height in pixels. Defaults to 0. Required if a bitmap buffer is specified as <see cref="Buffer"/>.
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// Gets or sets the image scale factor. Defaults to 1.0.
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
/// <summary>
/// Gets or sets the buffer
/// Gets or sets the buffer containing the raw image data.
/// </summary>
public byte[] Buffer { get; set; }
/// <summary>
/// Gets or sets the dataURL
/// Gets or sets the data URL containing a base 64 encoded PNG or JPEG image.
/// </summary>
public string DataUrl { get; set; }
}

View File

@@ -1,32 +1,36 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("windows")]
public class AppDetailsOptions
{
/// <summary>
/// Windows App User Model ID. It has to be set, otherwise the other options will have no effect.
/// Window's App User Model ID. It has to be set, otherwise the other options will have no effect.
/// </summary>
public string AppId { get; set; }
/// <summary>
/// Windows Relaunch Icon.
/// Window's relaunch icon resource path.
/// </summary>
public string AppIconPath { get; set; }
/// <summary>
/// Index of the icon in appIconPath. Ignored when appIconPath is not set. Default is 0.
/// Index of the icon in <see cref="AppIconPath"/>. Ignored when <see cref="AppIconPath"/> is not set. Default is 0.
/// </summary>
public int AppIconIndex { get; set; }
/// <summary>
/// Windows Relaunch Command.
/// Window's relaunch command.
/// </summary>
public string RelaunchCommand { get; set; }
/// <summary>
/// Windows Relaunch Display Name.
/// Window's relaunch display name.
/// </summary>
public string RelaunchDisplayName { get; set; }
}

View File

@@ -5,6 +5,7 @@ namespace ElectronNET.API.Entities
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class AutoResizeOptions
{
/// <summary>
@@ -35,4 +36,4 @@ namespace ElectronNET.API.Entities
[DefaultValue(false)]
public bool Vertical { get; set; } = false;
}
}
}

View File

@@ -3,11 +3,12 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class BitmapOptions
{
/// <summary>
/// Gets or sets the scale factor
/// The image scale factor. Defaults to 1.0.
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}
}

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class Blob : IPostData
{
/// <summary>

View File

@@ -28,4 +28,4 @@
/// </summary>
public bool IsAdminRightsRequired { get; set; }
}
}
}

View File

@@ -3,23 +3,26 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class BrowserViewConstructorOptions
{
/// <summary>
/// See BrowserWindow.
/// Gets or sets the web preferences for the view (see WebPreferences).
/// </summary>
public WebPreferences WebPreferences { get; set; }
/// <summary>
/// A proxy to set on creation in the format host:port.
/// Gets or sets a proxy to use on creation in the format host:port.
/// The proxy can be alternatively set using the BrowserView.WebContents.SetProxyAsync function.
/// </summary>
/// <remarks>This is custom shortcut. Not part of the Electron API.</remarks>
public string Proxy { get; set; }
/// <summary>
/// The credentials of the Proxy in the format username:password.
/// Gets or sets the credentials of the proxy in the format username:password.
/// These will only be used if the Proxy field is also set.
/// </summary>
/// <remarks>This is custom shortcut. Not part of the Electron API.</remarks>
public string ProxyCredentials { get; set; }
}
}
}

View File

@@ -1,7 +1,7 @@
using ElectronNET.Converter;
using System.ComponentModel;
using System.Text.Json.Serialization;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -24,13 +24,13 @@ namespace ElectronNET.API.Entities
/// ( if y is used) Window's left offset from screen. Default is to center the
/// window.
/// </summary>
public int X { get; set; } = -1;
public int? X { get; set; }
/// <summary>
/// ( if x is used) Window's top offset from screen. Default is to center the
/// window.
/// </summary>
public int Y { get; set; } = -1;
public int? Y { get; set; }
/// <summary>
/// The width and height would be used as web page's size, which means the actual
@@ -73,24 +73,32 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Whether window is movable. This is not implemented on Linux. Default is true.
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
[DefaultValue(true)]
public bool Movable { get; set; } = true;
/// <summary>
/// Whether window is minimizable. This is not implemented on Linux. Default is true.
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
[DefaultValue(true)]
public bool Minimizable { get; set; } = true;
/// <summary>
/// Whether window is maximizable. This is not implemented on Linux. Default is true.
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
[DefaultValue(true)]
public bool Maximizable { get; set; } = true;
/// <summary>
/// Whether window is closable. This is not implemented on Linux. Default is true.
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
[DefaultValue(true)]
public bool Closable { get; set; } = true;
@@ -116,16 +124,24 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Whether the window can be put into fullscreen mode. On macOS, also whether the
/// maximize/zoom button should toggle full screen mode or maximize window.Default
/// is true.
/// maximize/zoom button should toggle full screen mode or maximize window. Default
/// is true (Electron default).
/// </summary>
public bool Fullscreenable { get; set; }
[DefaultValue(true)]
public bool Fullscreenable { get; set; } = true; // FIX: previously defaulted to false in C#
/// <summary>
/// Whether to show the window in taskbar. Default is false.
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
public bool SkipTaskbar { get; set; }
/// <summary>
/// Determines if Blazor is used. Will disable "module" and "process" globals. Default is false.
/// </summary>
public bool IsRunningBlazor { get; set; }
/// <summary>
/// The kiosk mode. Default is false.
/// </summary>
@@ -137,8 +153,7 @@ namespace ElectronNET.API.Entities
public string Title { get; set; } = "Electron.NET";
/// <summary>
/// The window icon. On Windows it is recommended to use ICO icons to get best
/// visual effects, you can also leave it undefined so the executable's icon will be used.
/// The window icon. Can be a NativeImage or a string path. On Windows it is recommended to use ICO icons; when undefined, the executable's icon will be used.
/// </summary>
public string Icon { get; set; }
@@ -149,7 +164,7 @@ namespace ElectronNET.API.Entities
public bool Show { get; set; } = true;
/// <summary>
/// Specify false to create a . Default is true.
/// Specify false to create a frameless window. Default is true.
/// </summary>
[DefaultValue(true)]
public bool Frame { get; set; } = true;
@@ -164,6 +179,7 @@ namespace ElectronNET.API.Entities
/// Whether the web view accepts a single mouse-down event that simultaneously
/// activates the window.Default is false.
/// </summary>
[SupportedOSPlatform("macos")]
public bool AcceptFirstMouse { get; set; }
/// <summary>
@@ -174,28 +190,35 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Auto hide the menu bar unless the Alt key is pressed. Default is false.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
public bool AutoHideMenuBar { get; set; }
/// <summary>
/// Enable the window to be resized larger than screen. Default is false.
/// </summary>
[SupportedOSPlatform("macos")]
public bool EnableLargerThanScreen { get; set; }
/// <summary>
/// Window's background color as Hexadecimal value, like #66CD00 or #FFF or
/// #80FFFFFF (alpha is supported). Default is #FFF (white).
/// The window's background color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha in #AARRGGBB format is supported if transparent is set to true. Default is #FFF (white).
/// </summary>
public string BackgroundColor { get; set; }
/// <summary>
/// Whether window should have a shadow. This is only implemented on macOS. Default
/// is true.
/// Initial opacity of the window, between 0.0 (fully transparent) and 1.0 (fully opaque). Only implemented on Windows and macOS.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public double? Opacity { get; set; }
/// <summary>
/// Whether window should have a shadow. Default is true.
/// </summary>
public bool HasShadow { get; set; }
/// <summary>
/// Forces using dark theme for the window, only works on some GTK+3 desktop
/// environments.Default is false.
/// Forces using dark theme for the window, only works on some GTK+3 desktop environments. Default is false.
/// </summary>
public bool DarkTheme { get; set; }
@@ -215,6 +238,12 @@ namespace ElectronNET.API.Entities
/// </summary>
public TitleBarStyle TitleBarStyle { get; set; }
/// <summary>
/// Set a custom position for the traffic light buttons in frameless windows (macOS).
/// </summary>
[SupportedOSPlatform("macos")]
public Point TrafficLightPosition { get; set; }
/// <summary>
/// Configures the window's title bar overlay when using a frameless window.
/// Can be either:
@@ -228,9 +257,9 @@ namespace ElectronNET.API.Entities
public TitleBarOverlay TitleBarOverlay { get; set; }
/// <summary>
/// Shows the title in the tile bar in full screen mode on macOS for all
/// titleBarStyle options.Default is false.
/// Shows the title in the title bar in full screen mode on macOS for all titleBarStyle options. Default is false.
/// </summary>
/// <remarks>Not documented by MCP base-window-options / browser-window-options.</remarks>
public bool FullscreenWindowTitle { get; set; }
/// <summary>
@@ -238,6 +267,7 @@ namespace ElectronNET.API.Entities
/// window frame.Setting it to false will remove window shadow and window
/// animations. Default is true.
/// </summary>
[SupportedOSPlatform("windows")]
[DefaultValue(true)]
public bool ThickFrame { get; set; } = true;
@@ -247,14 +277,17 @@ namespace ElectronNET.API.Entities
/// Windows versions older than Windows 11 Build 22000 this property has no effect, and
/// frameless windows will not have rounded corners.
/// </summary>
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("windows")]
[DefaultValue(true)]
public bool RoundedCorners { get; set; } = true;
/// <summary>
/// Add a type of vibrancy effect to the window, only on macOS. Can be
/// appearance-based, light, dark, titlebar, selection, menu, popover, sidebar,
/// medium-light or ultra-dark.
/// appearance-based, titlebar, selection, menu, popover, sidebar, header, sheet,
/// window, hud, fullscreen-ui, tooltip, content, under-window, or under-page.
/// </summary>
[SupportedOSPlatform("macos")]
public Vibrancy Vibrancy { get; set; }
/// <summary>
@@ -264,6 +297,7 @@ namespace ElectronNET.API.Entities
/// it to zoom to the width of the screen.This will also affect the behavior when
/// calling maximize() directly.Default is false.
/// </summary>
[SupportedOSPlatform("macos")]
public bool ZoomToPageWidth { get; set; }
/// <summary>
@@ -272,6 +306,7 @@ namespace ElectronNET.API.Entities
/// adds a native new tab button to your window's tab bar and allows your app and
/// window to receive the new-window-for-tab event.
/// </summary>
[SupportedOSPlatform("macos")]
public string TabbingIdentifier { get; set; }
/// <summary>
@@ -283,16 +318,39 @@ namespace ElectronNET.API.Entities
/// A proxy to set on creation in the format host:port.
/// The proxy can be alternatively set using the BrowserWindow.WebContents.SetProxyAsync function.
/// </summary>
/// <remarks>Not documented by MCP base-window-options / browser-window-options.</remarks>
public string Proxy { get; set; }
/// <summary>
/// The credentials of the Proxy in the format username:password.
/// These will only be used if the Proxy field is also set.
/// </summary>
/// <remarks>Not documented by MCP base-window-options / browser-window-options.</remarks>
public string ProxyCredentials { get; set; }
/// <summary>
/// Gets or sets whether to use pre-Lion fullscreen on macOS. Default is false.
/// </summary>
[SupportedOSPlatform("macos")]
public bool SimpleFullscreen { get; set; }
/// <summary>
/// Gets or sets whether the window should be hidden when the user toggles into mission control (macOS).
/// </summary>
[SupportedOSPlatform("macos")]
public bool HiddenInMissionControl { get; set; }
/// <summary>
/// Gets or sets how the material appearance should reflect window activity state on macOS. Must be used with the vibrancy property.
/// Possible values: 'followWindow' (default), 'active', 'inactive'.
/// </summary>
[SupportedOSPlatform("macos")]
public string VisualEffectState { get; set; }
/// <summary>
/// Gets or sets the system-drawn background material on Windows. Can be 'auto', 'none', 'mica', 'acrylic' or 'tabbed'.
/// </summary>
[SupportedOSPlatform("windows")]
public string BackgroundMaterial { get; set; }
}
}
}

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CPUUsage
{
/// <summary>
@@ -11,9 +12,14 @@
public double PercentCPUUsage { get; set; }
/// <summary>
/// The number of average idle cpu wakeups per second since the last call to
/// getCPUUsage.First call returns 0.
/// Total seconds of CPU time used since process startup, if available.
/// </summary>
public int IdleWakeupsPerSecond { get; set; }
public double? CumulativeCPUUsage { get; set; }
/// <summary>
/// The number of average idle CPU wakeups per second since the last call to
/// getCPUUsage. First call returns 0. Will always return 0 on Windows.
/// </summary>
public double IdleWakeupsPerSecond { get; set; }
}
}
}

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class Certificate
{
/// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CertificatePrincipal
{
/// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CertificateTrustDialogOptions
{
/// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary>
/// Provide metadata about the current loaded Chrome extension
/// </summary>
/// <yremarks>Project-specific: no matching Electron structure found in MCP docs (electronjs).</yremarks>
public class ChromeExtensionInfo
{
/// <summary>
@@ -26,4 +27,4 @@
/// </summary>
public string Version { get; set; }
}
}
}

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class ClearStorageDataOptions
{
/// <summary>
@@ -11,14 +12,17 @@
public string Origin { get; set; }
/// <summary>
/// The types of storages to clear, can contain: appcache, cookies, filesystem,
/// indexdb, localstorage, shadercache, websql, serviceworkers, cachestorage.
/// The types of storages to clear. Can contain: cookies, filesystem, indexdb,
/// localstorage, shadercache, websql, serviceworkers, cachestorage.
/// If not specified, all storage types are cleared.
/// </summary>
public string[] Storages { get; set; }
/// <summary>
/// The types of quotas to clear, can contain: temporary, persistent, syncable.
/// The types of quotas to clear. Can contain: temporary. If not specified,
/// all quotas are cleared. The <c>quotas</c> option is deprecated;
/// "temporary" is the only remaining supported quota type.
/// </summary>
public string[] Quotas { get; set; }
}
}
}

View File

@@ -1,53 +1,59 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Cookie structure as used by Electron session.cookies APIs.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class Cookie
{
/// <summary>
/// The name of the cookie.
/// Gets or sets the name of the cookie.
/// </summary>
public string Name { get; set; }
/// <summary>
/// The value of the cookie.
/// Gets or sets the value of the cookie.
/// </summary>
public string Value { get; set; }
/// <summary>
/// (optional) - The domain of the cookie; this will be normalized with a preceding dot so that it's also valid for subdomains.
/// Gets or sets the domain of the cookie; this will be normalized with a preceding dot so that it's also valid for subdomains.
/// </summary>
public string Domain { get; set; }
/// <summary>
/// (optional) - Whether the cookie is a host-only cookie; this will only be true if no domain was passed.
/// Gets or sets a value indicating whether the cookie is a host-only cookie; this will only be true if no domain was passed.
/// </summary>
public bool HostOnly { get; set; }
public bool? HostOnly { get; set; }
/// <summary>
/// (optional) - The path of the cookie.
/// Gets or sets the path of the cookie.
/// </summary>
public string Path { get; set; }
/// <summary>
/// (optional) - Whether the cookie is marked as secure.
/// Gets or sets a value indicating whether the cookie is marked as secure.
/// </summary>
public bool Secure { get; set; }
public bool? Secure { get; set; }
/// <summary>
/// (optional) - Whether the cookie is marked as HTTP only.
/// Gets or sets a value indicating whether the cookie is marked as HTTP only.
/// </summary>
public bool HttpOnly { get; set; }
public bool? HttpOnly { get; set; }
/// <summary>
/// (optional) - Whether the cookie is a session cookie or a persistent cookie with an expiration date.
/// Gets or sets a value indicating whether the cookie is a session cookie or a persistent cookie with an expiration date.
/// </summary>
public bool Session { get; set; }
public bool? Session { get; set; }
/// <summary>
/// (optional) - The expiration date of the cookie as the number of seconds since the UNIX epoch. Not provided for session cookies.
/// Gets or sets the expiration date of the cookie as the number of seconds since the UNIX epoch. Not provided for session cookies.
/// </summary>
public long ExpirationDate { get; set; }
public double? ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the SameSite policy applied to this cookie. Can be "unspecified", "no_restriction", "lax" or "strict".
/// </summary>
public string SameSite { get; set; }
}
}

View File

@@ -1,17 +1,15 @@
using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
{
using System.Text.Json.Serialization;
/// <summary>
/// The cause of the change
/// The cause of the cookie change (per Electron Cookies 'changed' event).
/// </summary>
public enum CookieChangedCause
{
/// <summary>
///The cookie was changed directly by a consumer's action.
/// The cookie was changed directly by a consumer's action.
/// </summary>
[JsonPropertyName("explicit")]
@explicit,
/// <summary>
@@ -20,19 +18,19 @@ namespace ElectronNET.API.Entities
overwrite,
/// <summary>
/// The cookie was automatically removed as it expired.
/// The cookie was automatically removed as it expired.
/// </summary>
expired,
/// <summary>
/// The cookie was automatically evicted during garbage collection.
/// The cookie was automatically evicted during garbage collection.
/// </summary>
evicted,
/// <summary>
/// The cookie was overwritten with an already-expired expiration date.
/// The cookie was overwritten with an already-expired expiration date.
/// </summary>
[JsonPropertyName("expired_overwrite")]
expiredOverwrite
}
}
}

View File

@@ -5,54 +5,59 @@ namespace ElectronNET.API.Entities
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CookieDetails
{
/// <summary>
/// The URL to associate the cookie with. The callback will be rejected if the URL is invalid.
/// Gets or sets the URL to associate the cookie with. The operation will be rejected if the URL is invalid.
/// </summary>
public string Url { get; set; }
/// <summary>
/// (optional) - The name of the cookie. Empty by default if omitted.
/// Gets or sets the name of the cookie. Empty by default if omitted.
/// </summary>
[DefaultValue("")]
public string Name { get; set; }
/// <summary>
/// (optional) - The value of the cookie. Empty by default if omitted.
/// Gets or sets the value of the cookie. Empty by default if omitted.
/// </summary>
[DefaultValue("")]
public string Value { get; set; }
/// <summary>
/// (optional) - The domain of the cookie; this will be normalized with a preceding dot so that it's also valid for subdomains. Empty by default if omitted.
/// Gets or sets the domain of the cookie; this will be normalized with a preceding dot so that it's also valid for subdomains. Empty by default if omitted.
/// </summary>
[DefaultValue("")]
public string Domain { get; set; }
/// <summary>
/// (optional) - The path of the cookie. Empty by default if omitted.
/// Gets or sets the path of the cookie. Empty by default if omitted.
/// </summary>
[DefaultValue("")]
public string Path { get; set; }
/// <summary>
/// (optional) - Whether the cookie is marked as secure. Defaults to false.
/// Gets or sets a value indicating whether the cookie should be marked as secure. Defaults to false unless the SameSite policy is set to <c>no_restriction</c> (SameSite=None).
/// </summary>
[DefaultValue(false)]
public bool Secure { get; set; }
/// <summary>
/// (optional) - Whether the cookie is marked as HTTP only. Defaults to false.
/// Gets or sets a value indicating whether the cookie should be marked as HTTP only. Defaults to false.
/// </summary>
[DefaultValue(false)]
public bool HttpOnly { get; set; }
/// <summary>
/// (optional) - The expiration date of the cookie as the number of seconds since the UNIX epoch.
/// If omitted then the cookie becomes a session cookie and will not be retained between sessions.
/// Gets or sets the expiration date of the cookie as the number of seconds since the UNIX epoch. If omitted, the cookie becomes a session cookie and will not be retained between sessions.
/// </summary>
[DefaultValue(0)]
public long ExpirationDate { get; set; }
public double ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the SameSite policy to apply to this cookie. Can be "unspecified", "no_restriction", "lax" or "strict". Default is "lax".
/// </summary>
public string SameSite { get; set; }
}
}

View File

@@ -3,10 +3,11 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CookieFilter
{
/// <summary>
/// (optional) - Retrieves cookies which are associated with url.Empty implies retrieving cookies of all URLs.
/// (optional) - Retrieves cookies which are associated with url. Empty implies retrieving cookies of all URLs.
/// </summary>
public string Url { get; set; }
@@ -28,11 +29,16 @@
/// <summary>
/// (optional) - Filters cookies by their Secure property.
/// </summary>
public bool Secure { get; set; }
public bool? Secure { get; set; }
/// <summary>
/// (optional) - Filters out session or persistent cookies.
/// </summary>
public bool Session { get; set; }
public bool? Session { get; set; }
/// <summary>
/// (optional) - Filters cookies by httpOnly.
/// </summary>
public bool? HttpOnly { get; set; }
}
}

View File

@@ -3,21 +3,22 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CreateFromBitmapOptions
{
/// <summary>
/// Gets or sets the width
/// Gets or sets the width in pixels. Required for nativeImage.createFromBitmap(buffer, options).
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// Gets or sets the height in pixels. Required for nativeImage.createFromBitmap(buffer, options).
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// Gets or sets the image scale factor. Optional, defaults to 1.0.
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}
}

View File

@@ -3,20 +3,21 @@ namespace ElectronNET.API.Entities
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CreateFromBufferOptions
{
/// <summary>
/// Gets or sets the width
/// Gets or sets the width. Required for bitmap buffers passed to nativeImage.createFromBuffer.
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// Gets or sets the height. Required for bitmap buffers passed to nativeImage.createFromBuffer.
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// The image scale factor. Optional, defaults to 1.0.
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class CreateInterruptedDownloadOptions
{
/// <summary>
@@ -16,7 +17,7 @@
public string[] UrlChain { get; set; }
/// <summary>
///
/// (optional) - MIME type of the download.
/// </summary>
public string MimeType { get; set; }
@@ -41,9 +42,10 @@
public string ETag { get; set; }
/// <summary>
/// Time when download was started in number of seconds since UNIX epoch.
/// (optional) - Time when download was started in number of seconds since UNIX epoch.
/// Electron documents this as a Number (Double).
/// </summary>
public int StartTime { get; set; }
public double? StartTime { get; set; }
/// <summary>
///
@@ -64,4 +66,4 @@
ETag = eTag;
}
}
}
}

View File

@@ -21,6 +21,12 @@
/// </value>
public string Html { get; set; }
/// <summary>
/// Gets or sets the image.
/// Maps to clipboard.write({ image: NativeImage }).
/// </summary>
public NativeImage Image { get; set; }
/// <summary>
/// Gets or sets the RTF.

View File

@@ -34,5 +34,10 @@
/// Defaults to Impact.
/// </summary>
public string Fantasy { get; set; }
/// <summary>
/// Defaults to Latin Modern Math.
/// </summary>
public string Math { get; set; }
}
}

View File

@@ -1,9 +1,9 @@
namespace ElectronNET.API.Entities
{
/// <summary>
/// Opens the devtools with specified dock state, can be right, bottom, undocked,
/// detach.Defaults to last used dock state.In undocked mode it's possible to dock
/// back.In detach mode it's not.
/// Opens the devtools with specified dock state, can be left, right, bottom, undocked,
/// detach. Defaults to last used dock state. In undocked mode it's possible to dock
/// back. In detach mode it's not.
/// </summary>
public enum DevToolsMode
{
@@ -25,6 +25,11 @@
/// <summary>
/// The detach
/// </summary>
detach
detach,
/// <summary>
/// The left
/// </summary>
left,
}
}

View File

@@ -3,10 +3,11 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class Display
{
/// <summary>
/// Can be available, unavailable, unknown.
/// Gets or sets the accelerometer support status; can be 'available', 'unavailable', or 'unknown'.
/// </summary>
public string AccelerometerSupport { get; set; }
@@ -19,57 +20,72 @@
public Rectangle Bounds { get; set; }
/// <summary>
/// The number of bits per pixel.
/// Gets or sets the number of bits per pixel.
/// </summary>
public int ColorDepth { get; set; }
/// <summary>
/// Represent a color space (three-dimensional object which contains all realizable color combinations) for the purpose of color conversions.
/// Gets or sets the color space description used for color conversions.
/// </summary>
public string ColorSpace { get; set; }
/// <summary>
/// The number of bits per color component.
/// Gets or sets the number of bits per color component.
/// </summary>
public int DepthPerComponent { get; set; }
/// <summary>
/// The display refresh rate.
/// Gets or sets a value indicating whether the display is detected by the system.
/// </summary>
public int DisplayFrequency { get; set; }
public bool Detected { get; set; }
/// <summary>
/// Unique identifier associated with the display.
/// Gets or sets the display refresh rate.
/// </summary>
public double DisplayFrequency { get; set; }
/// <summary>
/// Gets or sets the unique identifier associated with the display. A value of -1 means the display is invalid or the correct id is not yet known, and a value of -10 means the display is a virtual display assigned to a unified desktop.
/// </summary>
public long Id { get; set; }
/// <summary>
/// true for an internal display and false for an external display.
/// Gets or sets a value indicating whether the display is internal (true) or external (false).
/// </summary>
public bool Internal { get; set; }
/// <summary>
/// User-friendly label, determined by the platform.
/// Gets or sets the user-friendly label, determined by the platform.
/// </summary>
public string Label { get; set; }
/// <summary>
/// Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees.
/// Gets or sets the maximum cursor size in native pixels.
/// </summary>
public Size MaximumCursorSize { get; set; }
/// <summary>
/// Gets or sets the display's origin in pixel coordinates. Only available on windowing systems that position displays in pixel coordinates (e.g., X11).
/// </summary>
public Point NativeOrigin { get; set; }
/// <summary>
/// Gets or sets the screen rotation in clock-wise degrees. Can be 0, 90, 180, or 270.
/// </summary>
public int Rotation { get; set; }
/// <summary>
/// Output device's pixel scale factor.
/// Gets or sets the output device's pixel scale factor.
/// </summary>
public double ScaleFactor { get; set; }
/// <summary>
/// Can be available, unavailable, unknown.
/// Gets or sets the touch support status; can be 'available', 'unavailable', or 'unknown'.
/// </summary>
public string TouchSupport { get; set; }
/// <summary>
/// Whether or not the display is a monochrome display.
/// Gets or sets a value indicating whether the display is monochrome.
/// </summary>
public bool Monochrome { get; set; }
@@ -82,10 +98,10 @@
public Size Size { get; set; }
/// <summary>
/// Gets or sets the work area.
/// Gets or sets the work area of the display in DIP points.
/// </summary>
/// <value>
/// The work area.
/// The work area of the display in DIP points.
/// </value>
public Rectangle WorkArea { get; set; }

View File

@@ -1,8 +1,21 @@
namespace ElectronNET.API
using System.Runtime.Versioning;
namespace ElectronNET.API
{
/// <summary>
///
/// </summary>
public enum DisplayBalloonIconType
{
none,
info,
warning,
error,
custom
}
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("Windows")]
public class DisplayBalloonOptions
{
/// <summary>
@@ -28,5 +41,29 @@
/// The content.
/// </value>
public string Content { get; set; }
/// <summary>
/// (optional) - Icon type for the balloon: none, info, warning, error or custom.
/// Default is custom.
/// </summary>
public DisplayBalloonIconType IconType { get; set; } = DisplayBalloonIconType.custom;
/// <summary>
/// (optional) - Use the large version of the icon. Default is true.
/// Maps to Windows NIIF_LARGE_ICON.
/// </summary>
public bool LargeIcon { get; set; } = true;
/// <summary>
/// (optional) - Do not play the associated sound. Default is false.
/// Maps to Windows NIIF_NOSOUND.
/// </summary>
public bool NoSound { get; set; }
/// <summary>
/// (optional) - Do not display the balloon if the current user is in "quiet time".
/// Default is false. Maps to Windows NIIF_RESPECT_QUIET_TIME.
/// </summary>
public bool RespectQuietTime { get; set; }
}
}

View File

@@ -1,10 +1,13 @@
using System.ComponentModel;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
/// Defines the DockBounceType enumeration.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("macOS")]
public enum DockBounceType
{
/// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class EnableNetworkEmulationOptions
{
/// <summary>
@@ -12,17 +13,20 @@
/// <summary>
/// RTT in ms. Defaults to 0 which will disable latency throttling.
/// Electron documents this as a Number (Double).
/// </summary>
public int Latency { get; set; }
public double Latency { get; set; }
/// <summary>
/// Download rate in Bps. Defaults to 0 which will disable download throttling.
/// Electron documents this as a Number (Double).
/// </summary>
public int DownloadThroughput { get; set; }
public double DownloadThroughput { get; set; }
/// <summary>
/// Upload rate in Bps. Defaults to 0 which will disable upload throttling.
/// Electron documents this as a Number (Double).
/// </summary>
public int UploadThroughput { get; set; }
public double UploadThroughput { get; set; }
}
}
}

View File

@@ -3,6 +3,7 @@
/// <summary>
/// Docs: https://electronjs.org/docs/api/structures/extension
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class Extension
{
/// <summary>
@@ -35,4 +36,4 @@
/// </summary>
public string Version { get; set; }
}
}
}

View File

@@ -3,6 +3,7 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class FileFilter
{
/// <summary>

View File

@@ -3,14 +3,13 @@
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class FileIconOptions
{
/// <summary>
/// Gets the size.
/// The requested icon size string passed to app.getFileIcon:
/// "small" (16x16), "normal" (32x32), or "large" (48x48 on Linux, 32x32 on Windows; unsupported on macOS).
/// </summary>
/// <value>
/// The size.
/// </value>
public string Size { get; private set; }
/// <summary>

View File

@@ -1,23 +1,28 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public enum FileIconSize
{
/// <summary>
/// The small
/// small - 16x16 (per app.getFileIcon size mapping).
/// </summary>
small,
/// <summary>
/// The normal
/// normal - 32x32 (per app.getFileIcon size mapping).
/// </summary>
normal,
/// <summary>
/// The large
/// large - 48x48 on Linux, 32x32 on Windows, unsupported on macOS (per app.getFileIcon size mapping).
/// </summary>
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("Windows")]
large
}
}

View File

@@ -1,15 +1,18 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
/// Controls the behavior of <see cref="App.Focus(FocusOptions)"/>.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class FocusOptions
{
/// <summary>
/// Make the receiver the active app even if another app is currently active.
/// <para/>
/// You should seek to use the <see cref="Steal"/> option as sparingly as possible.
/// You should seek to use the steal option as sparingly as possible.
/// </summary>
[SupportedOSPlatform("macOS")]
public bool Steal { get; set; }
}
}

View File

@@ -1,85 +1,99 @@
using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
{
/// <summary>
/// Graphics Feature Status from chrome://gpu/ as returned by app.getGPUFeatureStatus().
/// Each field reflects the status of a GPU capability reported by Chromium.
///
/// Possible values for all fields:
/// - disabled_software: Software only. Hardware acceleration disabled (yellow)
/// - disabled_off: Disabled (red)
/// - disabled_off_ok: Disabled (yellow)
/// - unavailable_software: Software only, hardware acceleration unavailable (yellow)
/// - unavailable_off: Unavailable (red)
/// - unavailable_off_ok: Unavailable (yellow)
/// - enabled_readback: Hardware accelerated but at reduced performance (yellow)
/// - enabled_force: Hardware accelerated on all pages (green)
/// - enabled: Hardware accelerated (green)
/// - enabled_on: Enabled (green)
/// - enabled_force_on: Force enabled (green)
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class GPUFeatureStatus
{
/// <summary>
/// Canvas.
/// Gets or sets the status for Canvas.
/// </summary>
[JsonPropertyName("2d_canvas")]
public string Canvas { get; set; }
/// <summary>
/// Flash.
/// Gets or sets the status for Flash.
/// </summary>
[JsonPropertyName("flash_3d")]
public string Flash3D { get; set; }
/// <summary>
/// Flash Stage3D.
/// Gets or sets the status for Flash Stage3D.
/// </summary>
[JsonPropertyName("flash_stage3d")]
public string FlashStage3D { get; set; }
/// <summary>
/// Flash Stage3D Baseline profile.
/// Gets or sets the status for Flash Stage3D Baseline profile.
/// </summary>
[JsonPropertyName("flash_stage3d_baseline")]
public string FlashStage3dBaseline { get; set; }
/// <summary>
/// Compositing.
/// Gets or sets the status for Compositing.
/// </summary>
[JsonPropertyName("gpu_compositing")]
public string GpuCompositing { get; set; }
/// <summary>
/// Multiple Raster Threads.
/// Gets or sets the status for Multiple Raster Threads.
/// </summary>
[JsonPropertyName("multiple_raster_threads")]
public string MultipleRasterThreads { get; set; }
/// <summary>
/// Native GpuMemoryBuffers.
/// Gets or sets the status for Native GpuMemoryBuffers.
/// </summary>
[JsonPropertyName("native_gpu_memory_buffers")]
public string NativeGpuMemoryBuffers { get; set; }
/// <summary>
/// Rasterization.
/// Gets or sets the status for Rasterization.
/// </summary>
public string Rasterization { get; set; }
/// <summary>
/// Video Decode.
/// Gets or sets the status for Video Decode.
/// </summary>
[JsonPropertyName("video_decode")]
public string VideoDecode { get; set; }
/// <summary>
/// Video Encode.
/// Gets or sets the status for Video Encode.
/// </summary>
[JsonPropertyName("video_encode")]
public string VideoEncode { get; set; }
/// <summary>
/// VPx Video Decode.
/// Gets or sets the status for VPx Video Decode.
/// </summary>
[JsonPropertyName("vpx_decode")]
public string VpxDecode { get; set; }
/// <summary>
/// WebGL.
/// Gets or sets the status for WebGL.
/// </summary>
public string Webgl { get; set; }
/// <summary>
/// WebGL2.
/// Gets or sets the status for WebGL2.
/// </summary>
public string Webgl2 { get; set; }
}

View File

@@ -1,15 +1,17 @@
namespace ElectronNET.API.Entities
{
/// <summary>
/// Interface to use Electrons PostData Object
/// Represents a postData item for loadURL/webContents.loadURL options.
/// Valid types per Electron docs: 'rawData' and 'file'.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public interface IPostData
{
/// <summary>
/// One of the following:
/// rawData - <see cref="UploadRawData"/> The data is available as a Buffer, in the rawData field.
/// file - <see cref="UploadFile"/> The object represents a file. The filePath, offset, length and modificationTime fields will be used to describe the file.
/// blob - <see cref="Blob"/> The object represents a Blob. The blobUUID field will be used to describe the Blob.
/// rawData - <see cref="UploadRawData"/>.
/// file - <see cref="UploadFile"/>.
/// Based on Electron postData definitions.
/// </summary>
public string Type { get; }
}

View File

@@ -1,8 +1,12 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Options for app.importCertificate(options) on Linux.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("linux")]
public class ImportCertificateOptions
{
/// <summary>

View File

@@ -6,8 +6,10 @@ namespace ElectronNET.API.Entities
using System.Text.Json.Serialization;
/// <summary>
///
/// Input event payload as used by webContents 'input-event' and 'before-input-event'.
/// Fields map to KeyboardEvent properties where noted, and type/modifiers follow Electron's InputEvent structure.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class InputEvent
{
/// <summary>
@@ -57,15 +59,95 @@ namespace ElectronNET.API.Entities
/// <summary>
/// An array of modifiers of the event, can be `shift`, `control`, `ctrl`, `alt`,
/// `meta`, `command`, `cmd`, `isKeypad`, `isAutoRepeat`, `leftButtonDown`,
/// `middleButtonDown`, `rightButtonDown`, `capsLock`, `numLock`, `left`, `right`
/// `meta`, `command`, `cmd`, `iskeypad`, `isautorepeat`, `leftbuttondown`,
/// `middlebuttondown`, `rightbuttondown`, `capslock`, `numlock`, `left`, `right`.
/// </summary>
[JsonConverter(typeof(ModifierTypeListConverter))]
public List<ModifierType> Modifiers { get; set; }
/// <summary>
/// For MouseInputEvent: The x-coordinate of the event (Integer).
/// </summary>
public int? X { get; set; }
/// <summary>
/// For MouseInputEvent: The y-coordinate of the event (Integer).
/// </summary>
public int? Y { get; set; }
/// <summary>
/// For MouseInputEvent: The button pressed, can be 'left', 'middle', or 'right' (optional).
/// </summary>
public string Button { get; set; }
/// <summary>
/// For MouseInputEvent: Global x in screen coordinates (Integer, optional).
/// </summary>
public int? GlobalX { get; set; }
/// <summary>
/// For MouseInputEvent: Global y in screen coordinates (Integer, optional).
/// </summary>
public int? GlobalY { get; set; }
/// <summary>
/// For MouseInputEvent: Movement delta on x-axis since last event (Integer, optional).
/// </summary>
public int? MovementX { get; set; }
/// <summary>
/// For MouseInputEvent: Movement delta on y-axis since last event (Integer, optional).
/// </summary>
public int? MovementY { get; set; }
/// <summary>
/// For MouseInputEvent: Click count (Integer, optional).
/// </summary>
public int? ClickCount { get; set; }
/// <summary>
/// For MouseWheelInputEvent: Horizontal scroll delta (Integer, optional).
/// </summary>
public int? DeltaX { get; set; }
/// <summary>
/// For MouseWheelInputEvent: Vertical scroll delta (Integer, optional).
/// </summary>
public int? DeltaY { get; set; }
/// <summary>
/// For MouseWheelInputEvent: Horizontal wheel ticks (Integer, optional).
/// </summary>
public int? WheelTicksX { get; set; }
/// <summary>
/// For MouseWheelInputEvent: Vertical wheel ticks (Integer, optional).
/// </summary>
public int? WheelTicksY { get; set; }
/// <summary>
/// For MouseWheelInputEvent: Horizontal acceleration ratio (Integer, optional).
/// </summary>
public int? AccelerationRatioX { get; set; }
/// <summary>
/// For MouseWheelInputEvent: Vertical acceleration ratio (Integer, optional).
/// </summary>
public int? AccelerationRatioY { get; set; }
/// <summary>
/// For MouseWheelInputEvent: True if wheel deltas are precise (optional).
/// </summary>
public bool? HasPreciseScrollingDeltas { get; set; }
/// <summary>
/// For MouseWheelInputEvent: True if the target can scroll (optional).
/// </summary>
public bool? CanScroll { get; set; }
/// <summary>
/// Can be `undefined`, `mouseDown`, `mouseUp`, `mouseMove`, `mouseEnter`,
/// `mouseLeave`, `contextMenu`, `mouseWheel`, `rawKeyDown`, `keyDown`, `keyUp`,
/// `mouseLeave`, `contextMenu`, `mouseWheel`, `rawKeyDown`, `keyDown`, `keyUp`, `char`,
/// `gestureScrollBegin`, `gestureScrollEnd`, `gestureScrollUpdate`,
/// `gestureFlingStart`, `gestureFlingCancel`, `gesturePinchBegin`,
/// `gesturePinchEnd`, `gesturePinchUpdate`, `gestureTapDown`, `gestureShowPress`,
@@ -77,6 +159,4 @@ namespace ElectronNET.API.Entities
/// </summary>
public InputEventType Type { get; set; }
}
}
}

View File

@@ -60,6 +60,11 @@ public enum InputEventType
/// </summary>
keyUp,
/// <summary>
///
/// </summary>
@char,
/// <summary>
///
/// </summary>

View File

@@ -1,27 +1,29 @@
using System.Text.Json.Serialization;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Jump List category definition used with app.setJumpList(categories).
/// Matches Electron's JumpListCategory structure.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("windows")]
public class JumpListCategory
{
/// <summary>
/// Must be set if type is custom, otherwise it should be omitted.
/// Gets or sets the name; must be set if <c>type</c> is <c>custom</c>, otherwise it should be omitted.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Array of objects if type is tasks or custom, otherwise it should be omitted.
/// Gets or sets the array of <see cref="JumpListItem"/> objects if <c>type</c> is <c>tasks</c> or <c>custom</c>; otherwise it should be omitted.
/// </summary>
public JumpListItem[] Items { get; set; }
/// <summary>
/// One of the following: "tasks" | "frequent" | "recent" | "custom"
/// Gets or sets the category type. One of: <c>tasks</c> | <c>frequent</c> | <c>recent</c> | <c>custom</c>.
/// </summary>
public JumpListCategoryType Type { get; set; }
}
}
}

View File

@@ -1,8 +1,12 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Jump list category kinds for app.setJumpList (Windows).
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("Windows")]
public enum JumpListCategoryType
{
/// <summary>

View File

@@ -1,59 +1,65 @@
using System.Text.Json.Serialization;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Jump List item used in app.setJumpList(categories) on Windows.
/// Matches Electron's JumpListItem structure.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("windows")]
public class JumpListItem
{
/// <summary>
/// The command line arguments when program is executed. Should only be set if type is task.
/// Gets or sets the command line arguments when <c>program</c> is executed. Should only be set if <c>type</c> is <c>task</c>.
/// </summary>
public string Args { get; set; }
/// <summary>
/// Description of the task (displayed in a tooltip). Should only be set if type is task.
/// Gets or sets the description of the task (displayed in a tooltip). Should only be set if <c>type</c> is <c>task</c>. Maximum length 260 characters.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The index of the icon in the resource file. If a resource file contains multiple
/// Gets or sets the index of the icon in the resource file. If a resource file contains multiple
/// icons this value can be used to specify the zero-based index of the icon that
/// should be displayed for this task.If a resource file contains only one icon,
/// should be displayed for this task. If a resource file contains only one icon,
/// this property should be set to zero.
/// </summary>
public int IconIndex { get; set; }
/// <summary>
/// The absolute path to an icon to be displayed in a Jump List, which can be an
/// Gets or sets the absolute path to an icon to be displayed in a Jump List, which can be an
/// arbitrary resource file that contains an icon(e.g. .ico, .exe, .dll). You can
/// usually specify process.execPath to show the program icon.
/// </summary>
public string IconPath { get; set; }
/// <summary>
/// Path of the file to open, should only be set if type is file.
/// Gets or sets the path of the file to open; should only be set if <c>type</c> is <c>file</c>.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Path of the program to execute, usually you should specify process.execPath
/// which opens the current program.Should only be set if type is task.
/// Gets or sets the path of the program to execute, usually specify process.execPath
/// which opens the current program. Should only be set if <c>type</c> is <c>task</c>.
/// </summary>
public string Program { get; set; }
/// <summary>
/// The text to be displayed for the item in the Jump List. Should only be set if type is task.
/// Gets or sets the text to be displayed for the item in the Jump List. Should only be set if <c>type</c> is <c>task</c>.
/// </summary>
public string Title { get; set; }
/// <summary>
/// One of the following: "task" | "separator" | "file"
/// Gets or sets the item type. One of: <c>task</c> | <c>separator</c> | <c>file</c>.
/// </summary>
public JumpListItemType Type { get; set; }
/// <summary>
/// Gets or sets the working directory. Default is empty.
/// </summary>
public string WorkingDirectory { get; set; }
}
}
}

View File

@@ -1,8 +1,12 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Jump list item kinds for app.setJumpList (Windows).
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("Windows")]
public enum JumpListItemType
{
/// <summary>

View File

@@ -1,20 +1,24 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Settings returned by app.getJumpListSettings() on Windows.
/// Matches Electron's JumpListSettings object.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("windows")]
public class JumpListSettings
{
/// <summary>
/// The minimum number of items that will be shown in the Jump List (for a more detailed description of this value see the
/// <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd378398(v=vs.85).aspx">MSDN</see> docs).
/// The minimum number of items that will be shown in the Jump List.
/// </summary>
public int MinItems { get; set; } = 0;
/// <summary>
/// Array of JumpListItem objects that correspond to items that the user has explicitly removed from custom categories
/// in the Jump List. These items must not be re-added to the Jump List in the next call to <see cref="App.SetJumpList"/>, Windows will
/// not display any custom category that contains any of the removed items.
/// in the Jump List. These items must not be re-added to the Jump List in the next call to app.setJumpList(categories);
/// Windows will not display any custom category that contains any of the removed items.
/// </summary>
public JumpListItem[] RemovedItems { get; set; } = new JumpListItem[0];
}

View File

@@ -1,12 +1,14 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Options for BrowserWindow.loadURL(url, options) / webContents.loadURL(url, options).
/// Matches Electron's loadURL options.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class LoadURLOptions
{
/// <summary>
/// A HTTP Referrer url.
/// An HTTP Referrer URL. In Electron this may be a string or a Referrer object.
/// </summary>
public string HttpReferrer { get; set; }
@@ -16,20 +18,18 @@
public string UserAgent { get; set; }
/// <summary>
/// Base url (with trailing path separator) for files to be loaded by the data url.
/// This is needed only if the specified url is a data url and needs to load other
/// files.
/// Base URL (with trailing path separator) for files to be loaded by the data URL.
/// Needed only if the specified URL is a data URL and needs to load other files.
/// </summary>
public string BaseURLForDataURL { get; set; }
/// <summary>
/// Extra headers for the request.
/// Extra headers separated by "\n".
/// </summary>
public string ExtraHeaders { get; set; }
/// <summary>
/// PostData Object for the request.
/// Can be <see cref="UploadRawData"/>, <see cref="UploadFile"/> or <see cref="Blob"/>
/// Post data for the request. Matches Electron's postData: (UploadRawData | UploadFile)[]
/// </summary>
public IPostData[] PostData { get; set; }
}

View File

@@ -0,0 +1,37 @@
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
/// Windows launch entry as returned by app.getLoginItemSettings().launchItems.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("windows")]
public class LoginItemLaunchItem
{
/// <summary>
/// Name value of a registry entry.
/// </summary>
public string Name { get; set; }
/// <summary>
/// The executable to an app that corresponds to a registry entry.
/// </summary>
public string Path { get; set; }
/// <summary>
/// The command-line arguments to pass to the executable.
/// </summary>
public string[] Args { get; set; }
/// <summary>
/// One of <c>user</c> or <c>machine</c>. Indicates whether the registry entry is under HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE.
/// </summary>
public string Scope { get; set; }
/// <summary>
/// True if the app registry key is startup approved and therefore shows as enabled in Task Manager and Windows settings.
/// </summary>
public bool Enabled { get; set; }
}
}

View File

@@ -1,8 +1,11 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Return object for app.getLoginItemSettings() on macOS and Windows.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class LoginItemSettings
{
/// <summary>
@@ -11,29 +14,52 @@
public bool OpenAtLogin { get; set; }
/// <summary>
/// <see langword="true"/> if the app is set to open as hidden at login. This setting is not available
/// <see langword="true"/> if the app is set to open as hidden at login. Deprecated on macOS 13 and up; not available
/// on <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see>.
/// </summary>
[SupportedOSPlatform("macos")]
public bool OpenAsHidden { get; set; }
/// <summary>
/// <see langword="true"/> if the app was opened at login automatically. This setting is not available
/// on <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see>.
/// </summary>
[SupportedOSPlatform("macos")]
public bool WasOpenedAtLogin { get; set; }
/// <summary>
/// <see langword="true"/> if the app was opened as a hidden login item. This indicates that the app should not
/// open any windows at startup. This setting is not available on
/// open any windows at startup. Deprecated on macOS 13 and up; not available on
/// <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see>.
/// </summary>
[SupportedOSPlatform("macos")]
public bool WasOpenedAsHidden { get; set; }
/// <summary>
/// <see langword="true"/> if the app was opened as a login item that should restore the state from the previous
/// session. This indicates that the app should restore the windows that were open the last time the app was closed.
/// This setting is not available on <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see>.
/// Deprecated on macOS 13 and up; not available on <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see>.
/// </summary>
[SupportedOSPlatform("macos")]
public bool RestoreState { get; set; }
/// <summary>
/// macOS status: one of <c>not-registered</c>, <c>enabled</c>, <c>requires-approval</c>, or <c>not-found</c>.
/// </summary>
[SupportedOSPlatform("macos")]
public string Status { get; set; }
/// <summary>
/// Windows: true if app is set to open at login and its run key is not deactivated.
/// Differs from <c>OpenAtLogin</c> as it ignores the <c>args</c> option; this is true if the given executable would be launched at login with any arguments.
/// </summary>
[SupportedOSPlatform("windows")]
public bool ExecutableWillLaunchAtLogin { get; set; }
/// <summary>
/// Windows launch entries found in registry.
/// </summary>
[SupportedOSPlatform("windows")]
public LoginItemLaunchItem[] LaunchItems { get; set; }
}
}

View File

@@ -1,4 +1,6 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
@@ -8,11 +10,25 @@
/// <summary>
/// The executable path to compare against. Defaults to process.execPath.
/// </summary>
[SupportedOSPlatform("windows")]
public string Path { get; set; }
/// <summary>
/// The command-line arguments to compare against. Defaults to an empty array.
/// </summary>
[SupportedOSPlatform("windows")]
public string[] Args { get; set; }
/// <summary>
/// The type of service to query on macOS 13+. Defaults to 'mainAppService'. Only available on macOS 13 and up.
/// </summary>
[SupportedOSPlatform("macos")]
public string Type { get; set; }
/// <summary>
/// The name of the service. Required if type is non-default. Only available on macOS 13 and up.
/// </summary>
[SupportedOSPlatform("macos")]
public string ServiceName { get; set; }
}
}

View File

@@ -1,8 +1,11 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Settings object for app.setLoginItemSettings() on macOS and Windows.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class LoginSettings
{
/// <summary>
@@ -14,19 +17,46 @@
/// <summary>
/// <see langword="true"/> to open the app as hidden. Defaults to <see langword="false"/>. The user can edit this
/// setting from the System Preferences so app.getLoginItemSettings().wasOpenedAsHidden should be checked when the app is
/// opened to know the current value. This setting is not available on <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see>.
/// opened to know the current value. This setting is not available on <see href="https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide">MAS builds</see> and does not work on macOS 13 and up.
/// </summary>
[SupportedOSPlatform("macos")]
public bool OpenAsHidden { get; set; }
/// <summary>
/// The executable to launch at login. Defaults to process.execPath.
/// </summary>
[SupportedOSPlatform("windows")]
public string Path { get; set; }
/// <summary>
/// The command-line arguments to pass to the executable. Defaults to an empty
/// array.Take care to wrap paths in quotes.
/// </summary>
[SupportedOSPlatform("windows")]
public string[] Args { get; set; }
/// <summary>
/// The type of service to add as a login item. Defaults to 'mainAppService'. Only available on macOS 13 and up.
/// </summary>
[SupportedOSPlatform("macos")]
public string Type { get; set; }
/// <summary>
/// The name of the service. Required if <c>Type</c> is non-default. Only available on macOS 13 and up.
/// </summary>
[SupportedOSPlatform("macos")]
public string ServiceName { get; set; }
/// <summary>
/// Change the startup approved registry key and enable/disable the app in Task Manager and Windows Settings. Defaults to true.
/// </summary>
[SupportedOSPlatform("windows")]
public bool Enabled { get; set; } = true;
/// <summary>
/// Value name to write into registry. Defaults to the app's AppUserModelId().
/// </summary>
[SupportedOSPlatform("windows")]
public string Name { get; set; }
}
}

View File

@@ -1,33 +1,42 @@
namespace ElectronNET.API.Entities;
/// <summary>
///
/// Margins object used by webContents.print options and webContents.printToPDF.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class Margins
{
/// <summary>
/// Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen,
/// Gets or sets the margin type. Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen,
/// you will also need to specify `top`, `bottom`, `left`, and `right`.
/// </summary>
public string MarginType { get; set; }
/// <summary>
/// The top margin of the printed web page, in pixels.
/// Gets or sets the top margin of the printed web page. Units depend on API:
/// - webContents.print: pixels
/// - webContents.printToPDF: inches
/// </summary>
public int Top { get; set; }
public double Top { get; set; }
/// <summary>
/// The bottom margin of the printed web page, in pixels.
/// Gets or sets the bottom margin of the printed web page. Units depend on API:
/// - webContents.print: pixels
/// - webContents.printToPDF: inches
/// </summary>
public int Bottom { get; set; }
public double Bottom { get; set; }
/// <summary>
/// The left margin of the printed web page, in pixels.
/// Gets or sets the left margin of the printed web page. Units depend on API:
/// - webContents.print: pixels
/// - webContents.printToPDF: inches
/// </summary>
public int Left { get; set; }
public double Left { get; set; }
/// <summary>
/// The right margin of the printed web page, in pixels.
/// Gets or sets the right margin of the printed web page. Units depend on API:
/// - webContents.print: pixels
/// - webContents.printToPDF: inches
/// </summary>
public int Right { get; set; }
public double Right { get; set; }
}

View File

@@ -1,24 +1,28 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Process memory info as returned by process.getProcessMemoryInfo().
/// Values are reported in Kilobytes.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class MemoryInfo
{
/// <summary>
/// The amount of memory currently pinned to actual physical RAM.
/// Gets or sets the amount of memory currently pinned to actual physical RAM.
/// </summary>
public int WorkingSetSize { get; set; }
/// <summary>
/// The maximum amount of memory that has ever been pinned to actual physical RAM.
/// Gets or sets the maximum amount of memory that has ever been pinned to actual physical RAM.
/// </summary>
public int PeakWorkingSetSize { get; set; }
/// <summary>
/// The amount of memory not shared by other processes, such as JS heap or HTML
/// content.
/// Gets or sets the amount of memory not shared by other processes, such as JS heap or HTML content. Windows only.
/// </summary>
[SupportedOSPlatform("windows")]
public int PrivateBytes { get; set; }
}
}

View File

@@ -1,28 +1,29 @@
using System;
using System.Text.Json.Serialization;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class MenuItem
{
/// <summary>
/// Will be called with click(menuItem, browserWindow, event) when the menu item is
/// Will be called with click(menuItem, browserWindow, event) when the menu item is
/// clicked.
/// </summary>
[JsonIgnore]
public Action Click { get; set; }
/// <summary>
/// Define the action of the menu item, when specified the click property will be
/// ignored.
/// Gets or sets the action (role) of the menu item. When specified, the click property will be ignored.
/// </summary>
public MenuRole Role { get; set; }
/// <summary>
/// Can be normal, separator, submenu, checkbox or radio.
/// Gets or sets the menu item type. Can be normal, separator, submenu, checkbox, radio, header (macOS 14+), or palette (macOS 14+).
/// </summary>
public MenuType Type { get; set; }
@@ -42,8 +43,15 @@ namespace ElectronNET.API.Entities
/// <value>
/// The sublabel.
/// </value>
[SupportedOSPlatform("macos")]
public string Sublabel { get; set; }
/// <summary>
/// Hover text for this menu item (macOS).
/// </summary>
[SupportedOSPlatform("macos")]
public string ToolTip { get; set; }
/// <summary>
/// Gets or sets the accelerator.
@@ -63,17 +71,31 @@ namespace ElectronNET.API.Entities
public string Icon { get; set; }
/// <summary>
/// If false, the menu item will be greyed out and unclickable.
/// Gets or sets a value indicating whether the item is enabled. If false, the menu item will be greyed out and unclickable.
/// </summary>
public bool Enabled { get; set; } = true;
/// <summary>
/// If false, the menu item will be entirely hidden.
/// Gets or sets a value indicating whether the item is visible. If false, the menu item will be entirely hidden.
/// </summary>
public bool Visible { get; set; } = true;
/// <summary>
/// Should only be specified for checkbox or radio type menu items.
/// Gets or sets a value indicating whether the accelerator should work when the item is hidden. Default is true (macOS).
/// When false, prevents the accelerator from triggering the item if the item is not visible.
/// </summary>
[SupportedOSPlatform("macos")]
public bool? AcceleratorWorksWhenHidden { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the accelerator should be registered with the system or only displayed (Linux/Windows). Defaults to true.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
public bool? RegisterAccelerator { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the item is checked. Should only be specified for checkbox or radio items.
/// </summary>
public bool Checked { get; set; }
@@ -85,17 +107,44 @@ namespace ElectronNET.API.Entities
public MenuItem[] Submenu { get; set; }
/// <summary>
/// Unique within a single menu. If defined then it can be used as a reference to
/// this item by the position attribute.
/// The item to share when the role is shareMenu (macOS).
/// </summary>
[SupportedOSPlatform("macos")]
public SharingItem SharingItem { get; set; }
/// <summary>
/// Gets or sets a unique id within a single menu. If defined then it can be used as a reference for placement.
/// </summary>
public string Id { get; internal set; }
/// <summary>
/// This field allows fine-grained definition of the specific location within a
/// given menu.
/// This field allows fine-grained definition of the specific location within a given menu.
/// </summary>
public string Position { get; set; }
/// <summary>
/// Gets or sets a list of item ids. Inserts this item before the item(s) with the specified id(s).
/// If the referenced item doesn't exist the item will be inserted at the end of the menu.
/// Also implies that this item should be placed in the same group as the referenced item(s).
/// </summary>
public string[] Before { get; set; }
/// <summary>
/// Gets or sets a list of item ids. Inserts this item after the item(s) with the specified id(s).
/// If the referenced item doesn't exist the item will be inserted at the end of the menu.
/// </summary>
public string[] After { get; set; }
/// <summary>
/// Gets or sets a list of item ids. Places this item's containing group before the containing group
/// of the item(s) with the specified id(s).
/// </summary>
public string[] BeforeGroupContaining { get; set; }
/// <summary>
/// Gets or sets a list of item ids. Places this item's containing group after the containing group
/// of the item(s) with the specified id(s).
/// </summary>
public string[] AfterGroupContaining { get; set; }
}
}
}

View File

@@ -31,14 +31,14 @@
paste,
/// <summary>
/// The pasteandmatchstyle
/// The pasteAndMatchStyle
/// </summary>
pasteandmatchstyle,
pasteAndMatchStyle,
/// <summary>
/// The selectall
/// The selectAll
/// </summary>
selectall,
selectAll,
/// <summary>
/// The delete
@@ -68,12 +68,12 @@
/// <summary>
/// Reload the current window ignoring the cache.
/// </summary>
forcereload,
forceReload,
/// <summary>
/// Toggle developer tools in the current window
/// </summary>
toggledevtools,
toggleDevTools,
/// <summary>
/// Toggle full screen mode on the current window
@@ -83,17 +83,17 @@
/// <summary>
/// Reset the focused pages zoom level to the original size
/// </summary>
resetzoom,
resetZoom,
/// <summary>
/// Zoom in the focused page by 10%
/// </summary>
zoomin,
zoomIn,
/// <summary>
/// Zoom out the focused page by 10%
/// </summary>
zoomout,
zoomOut,
/// <summary>
/// Whole default “Edit” menu (Undo, Copy, etc.)
@@ -118,7 +118,7 @@
/// <summary>
/// Only macOS: Map to the hideOtherApplications action
/// </summary>
hideothers,
hideOthers,
/// <summary>
/// Only macOS: Map to the unhideAllApplications action
@@ -128,12 +128,12 @@
/// <summary>
/// Only macOS: Map to the startSpeaking action
/// </summary>
startspeaking,
startSpeaking,
/// <summary>
/// Only macOS: Map to the stopSpeaking action
/// </summary>
stopspeaking,
stopSpeaking,
/// <summary>
/// Only macOS: Map to the arrangeInFront action
@@ -158,6 +158,108 @@
/// <summary>
/// Only macOS: The submenu is a “Services” menu
/// </summary>
services
services,
/// <summary>
/// Toggle built-in spellchecker.
/// </summary>
toggleSpellChecker,
/// <summary>
/// The submenu is a "File" menu.
/// </summary>
fileMenu,
/// <summary>
/// The submenu is a "View" menu.
/// </summary>
viewMenu,
/// <summary>
/// The application menu.
/// </summary>
appMenu,
/// <summary>
/// The submenu is a "Share" menu.
/// </summary>
shareMenu,
/// <summary>
/// Displays a list of files recently opened by the app.
/// </summary>
recentDocuments,
/// <summary>
/// Clear the recent documents list.
/// </summary>
clearRecentDocuments,
/// <summary>
/// Toggle the tab bar (macOS).
/// </summary>
toggleTabBar,
/// <summary>
/// Select the next tab (macOS).
/// </summary>
selectNextTab,
/// <summary>
/// Select the previous tab (macOS).
/// </summary>
selectPreviousTab,
/// <summary>
/// Show all tabs (macOS).
/// </summary>
showAllTabs,
/// <summary>
/// Merge all windows (macOS).
/// </summary>
mergeAllWindows,
/// <summary>
/// Move the current tab to a new window (macOS).
/// </summary>
moveTabToNewWindow,
/// <summary>
/// Show substitutions panel (macOS).
/// </summary>
showSubstitutions,
/// <summary>
/// Toggle smart quotes (macOS).
/// </summary>
toggleSmartQuotes,
/// <summary>
/// Toggle smart dashes (macOS).
/// </summary>
toggleSmartDashes,
/// <summary>
/// Toggle text replacement (macOS).
/// </summary>
toggleTextReplacement,
// Backwards-compatibility aliases (old identifiers) to avoid breaking existing code.
// These map to the same enum values as their official values.
pasteandmatchstyle = pasteAndMatchStyle,
selectall = selectAll,
forcereload = forceReload,
toggledevtools = toggleDevTools,
resetzoom = resetZoom,
zoomin = zoomIn,
zoomout = zoomOut,
hideothers = hideOthers,
startspeaking = startSpeaking,
stopspeaking = stopSpeaking,
togglespellchecker = toggleSpellChecker,
togglesmartquotes = toggleSmartQuotes,
togglesmartdashes = toggleSmartDashes,
toggletextreplacement = toggleTextReplacement
}
}

View File

@@ -1,33 +1,48 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Menu item types matching Electron's MenuItem.type values.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public enum MenuType
{
/// <summary>
/// The normal
/// Normal menu item.
/// </summary>
normal,
/// <summary>
/// The separator
/// Separator between items.
/// </summary>
separator,
/// <summary>
/// The submenu
/// Submenu container.
/// </summary>
submenu,
/// <summary>
/// The checkbox
/// Checkbox item.
/// </summary>
checkbox,
/// <summary>
/// The radio
/// Radio item.
/// </summary>
radio
radio,
/// <summary>
/// Header item (macOS 14+).
/// </summary>
[SupportedOSPlatform("macos")]
header,
/// <summary>
/// Palette item (macOS 14+).
/// </summary>
[SupportedOSPlatform("macos")]
palette
}
}

View File

@@ -1,89 +1,82 @@
using System.Text.Json.Serialization;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Options for dialog.showMessageBox / dialog.showMessageBoxSync.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class MessageBoxOptions
{
/// <summary>
/// Can be "none", "info", "error", "question" or "warning". On Windows, "question"
/// displays the same icon as "info", unless you set an icon using the "icon"
/// option. On macOS, both "warning" and "error" display the same warning icon.
/// Gets or sets the type. Can be "none", "info", "error", "question" or "warning". On Windows, "question" displays the same icon as "info", unless you set an icon using the "icon" option. On macOS, both "warning" and "error" display the same warning icon.
/// </summary>
public MessageBoxType Type { get; set; }
/// <summary>
/// Array of texts for buttons. On Windows, an empty array will result in one button
/// labeled "OK".
/// Gets or sets the array of texts for buttons. On Windows, an empty array will result in one button labeled "OK".
/// </summary>
public string[] Buttons { get; set; }
/// <summary>
/// Index of the button in the buttons array which will be selected by default when
/// the message box opens.
/// Gets or sets the index of the button in the buttons array which will be selected by default when the message box opens.
/// </summary>
public int DefaultId { get; set; }
/// <summary>
/// Title of the message box, some platforms will not show it.
/// Gets or sets the title of the message box; some platforms will not show it.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Content of the message box.
/// Gets or sets the content of the message box.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Extra information of the message.
/// Gets or sets the extra information of the message.
/// </summary>
public string Detail { get; set; }
/// <summary>
/// If provided, the message box will include a checkbox with the given label. The
/// checkbox state can be inspected only when using callback.
/// Gets or sets the checkbox label. If provided, the message box will include a checkbox with the given label.
/// </summary>
public string CheckboxLabel { get; set; }
/// <summary>
/// Initial checked state of the checkbox. false by default.
/// Gets or sets the initial checked state of the checkbox. Defaults to false.
/// </summary>
public bool CheckboxChecked { get; set; }
/// <summary>
/// Gets or sets the icon.
/// Gets or sets the icon for the message box.
/// </summary>
/// <value>
/// The icon.
/// </value>
public string Icon { get; set; }
/// <summary>
/// The index of the button to be used to cancel the dialog, via the Esc key. By
/// default this is assigned to the first button with "cancel" or "no" as the label.
/// If no such labeled buttons exist and this option is not set, 0 will be used as
/// the return value or callback response. This option is ignored on Windows.
/// Gets or sets the custom width of the text in the message box.
/// </summary>
[SupportedOSPlatform("macos")]
public int? TextWidth { get; set; }
/// <summary>
/// Gets or sets the index of the button to be used to cancel the dialog via the Esc key. By default this is assigned to the first button with "cancel" or "no" as the label. If no such labeled buttons exist and this option is not set, 0 will be used.
/// </summary>
public int CancelId { get; set; }
/// <summary>
/// On Windows Electron will try to figure out which one of the buttons are common
/// buttons(like "Cancel" or "Yes"), and show the others as command links in the
/// dialog.This can make the dialog appear in the style of modern Windows apps. If
/// you don't like this behavior, you can set noLink to true.
/// Gets or sets a value indicating whether to disable Windows command-links behavior (noLink).
/// On Windows Electron will try to figure out which one of the buttons are common buttons (like "Cancel" or "Yes"), and show the others as command links in the dialog. Set to true to disable this behavior.
/// </summary>
[SupportedOSPlatform("windows")]
public bool NoLink { get; set; }
/// <summary>
/// Normalize the keyboard access keys across platforms. Default is false. Enabling
/// this assumes AND character is used in the button labels for the placement of the keyboard
/// Gets or sets a value indicating whether to normalize the keyboard access keys across platforms. Default is false. Enabling this assumes '&amp;' is used in the button labels for the placement of the keyboard
/// shortcut access key and labels will be converted so they work correctly on each
/// platform, AND characters are removed on macOS, converted to _ on Linux, and left
/// untouched on Windows.For example, a button label of VieANDw will be converted to
/// Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and
/// platform, '&amp;' characters are removed on macOS, converted to '_' on Linux, and left
/// untouched on Windows. For example, a button label of "View" will be converted to "Vie_w" on Linux and "View" on macOS and can be selected via Alt-W on Windows and
/// Linux.
/// </summary>
public bool NormalizeAccessKeys { get; set; }
@@ -97,6 +90,4 @@ namespace ElectronNET.API.Entities
Message = message;
}
}
}
}

View File

@@ -1,24 +1,19 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Result returned by dialog.showMessageBox / dialog.showMessageBoxSync.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class MessageBoxResult
{
/// <summary>
/// Gets or sets the response.
/// The index of the clicked button.
/// </summary>
/// <value>
/// The response.
/// </value>
public int Response { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [checkbox checked].
/// The checked state of the checkbox if CheckboxLabel was set; otherwise false.
/// </summary>
/// <value>
/// <c>true</c> if [checkbox checked]; otherwise, <c>false</c>.
/// </value>
public bool CheckboxChecked { get; set; }
}
}
}

View File

@@ -1,8 +1,9 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// Message box type for dialog.showMessageBox/showMessageBoxSync.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public enum MessageBoxType
{
/// <summary>

View File

@@ -1,8 +1,9 @@
namespace ElectronNET.API.Entities;
/// <summary>
/// Specifies the possible modifier keys for a keyboard input.
/// Specifies the possible modifier keys for a keyboard input (maps to InputEvent.modifiers).
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public enum ModifierType
{
/// <summary>

View File

@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -268,6 +269,12 @@ namespace ElectronNET.API.Entities
/// </summary>
public bool IsTemplateImage => _isTemplateImage;
/// <summary>
/// Whether the image is considered a macOS template image.
/// </summary>
[SupportedOSPlatform("macos")]
public bool IsMacTemplateImage => _isTemplateImage;
/// <summary>
/// Deprecated. Marks the image as a template image.
/// </summary>

View File

@@ -8,6 +8,7 @@ using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
{
/// <yremarks>Project-specific: JSON converter for NativeImage; no MCP structure equivalent.</yremarks>
internal class NativeImageJsonConverter : JsonConverter<NativeImage>
{
public override void Write(Utf8JsonWriter writer, NativeImage value, JsonSerializerOptions options)
@@ -35,5 +36,4 @@ namespace ElectronNET.API.Entities
return new NativeImage(newDictionary);
}
}
}
}

View File

@@ -1,17 +1,21 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("macos")]
public class NotificationAction
{
/// <summary>
/// The label for the given action.
/// Gets or sets the label for the action.
/// </summary>
public string Text { get; set; }
/// <summary>
/// The type of action, can be button.
/// Gets or sets the type of action; can be 'button'.
/// </summary>
public string Type { get; set; }
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.Versioning;
using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
@@ -6,72 +7,85 @@ namespace ElectronNET.API.Entities
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class NotificationOptions
{
/// <summary>
/// A title for the notification, which will be shown at the top of the notification
/// window when it is shown.
/// Gets or sets the title for the notification, which will be shown at the top of the notification window when it is shown.
/// </summary>
public string Title { get; set; }
/// <summary>
/// A subtitle for the notification, which will be displayed below the title.
/// Gets or sets the subtitle for the notification, which will be displayed below the title.
/// </summary>
public string SubTitle { get; set; }
[SupportedOSPlatform("macos")]
[JsonPropertyName("subtitle")]
public string Subtitle { get; set; }
/// <summary>
/// The body text of the notification, which will be displayed below the title or
/// subtitle.
/// Gets or sets the body text of the notification, which will be displayed below the title or subtitle.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Whether or not to emit an OS notification noise when showing the notification.
/// Gets or sets a value indicating whether to suppress the OS notification noise when showing the notification.
/// </summary>
public bool Silent { get; set; }
/// <summary>
/// An icon to use in the notification.
/// Gets or sets an icon to use in the notification. Can be a string path or a NativeImage. If a string is passed, it must be a valid path to a local icon file.
/// </summary>
public string Icon { get; set; }
/// <summary>
/// Whether or not to add an inline reply option to the notification.
/// Gets or sets a value indicating whether to add an inline reply option to the notification.
/// </summary>
[SupportedOSPlatform("macos")]
public bool HasReply { get; set; }
/// <summary>
/// The timeout duration of the notification. Can be 'default' or 'never'.
/// Gets or sets the timeout duration of the notification. Can be 'default' or 'never'.
/// </summary>
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("windows")]
public string TimeoutType { get; set; }
/// <summary>
/// The placeholder to write in the inline reply input field.
/// Gets or sets the placeholder to write in the inline reply input field.
/// </summary>
[SupportedOSPlatform("macos")]
public string ReplyPlaceholder { get; set; }
/// <summary>
/// The name of the sound file to play when the notification is shown.
/// Gets or sets the name of the sound file to play when the notification is shown.
/// </summary>
[SupportedOSPlatform("macos")]
public string Sound { get; set; }
/// <summary>
/// The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
/// Gets or sets the urgency level of the notification. Can be 'normal', 'critical', or 'low'.
/// </summary>
[SupportedOSPlatform("linux")]
public string Urgency { get; set; }
/// <summary>
/// Actions to add to the notification. Please read the available actions and
/// limitations in the NotificationAction documentation.
/// Gets or sets the actions to add to the notification. Please read the available actions and limitations in the NotificationAction documentation.
/// </summary>
public NotificationAction Actions { get; set; }
[SupportedOSPlatform("macos")]
public NotificationAction[] Actions { get; set; }
/// <summary>
/// A custom title for the close button of an alert. An empty string will cause the
/// default localized text to be used.
/// Gets or sets a custom title for the close button of an alert. An empty string will cause the default localized text to be used.
/// </summary>
[SupportedOSPlatform("macos")]
public string CloseButtonText { get; set; }
/// <summary>
/// Gets or sets a custom description of the Notification on Windows superseding all properties above. Provides full customization of design and behavior of the notification.
/// </summary>
[SupportedOSPlatform("windows")]
public string ToastXml { get; set; }
/// <summary>
/// Emitted when the notification is shown to the user, note this could be fired
/// multiple times as a notification can be shown multiple times through the Show()
@@ -127,6 +141,7 @@ namespace ElectronNET.API.Entities
/// The string the user entered into the inline reply field
/// </summary>
[JsonIgnore]
[SupportedOSPlatform("macos")]
public Action<string> OnReply { get; set; }
/// <summary>
@@ -139,10 +154,11 @@ namespace ElectronNET.API.Entities
internal string ReplyID { get; set; }
/// <summary>
/// macOS only - The index of the action that was activated
/// macOS only - The index of the action that was activated.
/// </summary>
[JsonIgnore]
public Action<string> OnAction { get; set; }
[SupportedOSPlatform("macos")]
public Action<int> OnAction { get; set; }
/// <summary>
/// Gets or sets the action identifier.
@@ -153,6 +169,14 @@ namespace ElectronNET.API.Entities
[JsonInclude]
internal string ActionID { get; set; }
/// <summary>
/// Windows only: Emitted when an error is encountered while creating and showing the native notification.
/// Corresponds to the 'failed' event on Notification.
/// </summary>
[JsonIgnore]
[SupportedOSPlatform("windows")]
public Action<string> OnFailed { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="NotificationOptions"/> class.
/// </summary>

View File

@@ -15,4 +15,24 @@ public class OnDidFailLoadInfo
/// Validated URL.
/// </summary>
public string ValidatedUrl { get; set; }
}
/// <summary>
/// Error description string.
/// </summary>
public string ErrorDescription { get; set; }
/// <summary>
/// True if the event pertains to the main frame.
/// </summary>
public bool IsMainFrame { get; set; }
/// <summary>
/// The process id for the frame.
/// </summary>
public int FrameProcessId { get; set; }
/// <summary>
/// The routing id for the frame.
/// </summary>
public int FrameRoutingId { get; set; }
}

View File

@@ -1,17 +1,23 @@
namespace ElectronNET.API.Entities;
/// <summary>
/// 'OnDidNavigate' event details.
/// 'did-navigate' event details for main frame navigation.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class OnDidNavigateInfo
{
/// <summary>
/// Navigated URL.
/// The URL navigated to.
/// </summary>
public string Url { get; set; }
/// <summary>
/// HTTP response code.
/// HTTP response code (-1 for non-HTTP navigations).
/// </summary>
public int HttpResponseCode { get; set; }
}
/// <summary>
/// HTTP status text (empty for non-HTTP navigations).
/// </summary>
public string HttpStatusText { get; set; }
}

View File

@@ -1,10 +1,14 @@
using System.ComponentModel;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// String values for the 'level' parameter of BrowserWindow.setAlwaysOnTop.
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
[SupportedOSPlatform("macOS")]
[SupportedOSPlatform("Windows")]
public enum OnTopLevel
{
/// <summary>

View File

@@ -1,4 +1,3 @@
using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
@@ -9,11 +8,19 @@ namespace ElectronNET.API.Entities
public class OpenDevToolsOptions
{
/// <summary>
/// Opens the devtools with specified dock state, can be right, bottom, undocked,
/// detach.Defaults to last used dock state.In undocked mode it's possible to dock
/// back.In detach mode it's not.
/// Opens the DevTools with specified dock state. Can be left, right, bottom, undocked, or detach.
/// Defaults to the last used dock state. In undocked mode it's possible to dock back; in detach mode it's not.
/// </summary>
public DevToolsMode Mode { get; set; }
}
}
/// <summary>
/// Whether to bring the opened DevTools window to the foreground. Default is true.
/// </summary>
public bool Activate { get; set; } = true;
/// <summary>
/// A title for the DevTools window (only visible in undocked or detach mode).
/// </summary>
public string Title { get; set; }
}
}

View File

@@ -1,11 +1,13 @@
using System.Text.Json.Serialization;
namespace ElectronNET.API.Entities
{
using System.Runtime.Versioning;
/// <summary>
///
/// </summary>
/// <remarks>Up-to-date with Electron API 39.2</remarks>
public class OpenDialogOptions
{
/// <summary>
@@ -30,18 +32,19 @@ namespace ElectronNET.API.Entities
public string ButtonLabel { get; set; }
/// <summary>
/// Contains which features the dialog should use. The following values are supported:
/// Gets or sets which features the dialog should use. The following values are supported:
/// 'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'
/// </summary>
public OpenDialogProperty[] Properties { get; set; }
/// <summary>
/// Message to display above input boxes.
/// Gets or sets the message to display above input boxes.
/// </summary>
[SupportedOSPlatform("macos")]
public string Message { get; set; }
/// <summary>
/// The filters specifies an array of file types that can be displayed or
/// Gets or sets the filters specifying an array of file types that can be displayed or
/// selected when you want to limit the user to a specific type. For example:
/// </summary>
/// <example>
@@ -56,6 +59,11 @@ namespace ElectronNET.API.Entities
/// </code>
/// </example>
public FileFilter[] Filters { get; set; }
}
}
/// <summary>
/// Create security scoped bookmarks when packaged for the Mac App Store.
/// </summary>
[SupportedOSPlatform("macos")]
public bool SecurityScopedBookmarks { get; set; }
}
}

View File

@@ -1,5 +1,7 @@
namespace ElectronNET.API.Entities
{
using System.Runtime.Versioning;
/// <summary>
///
/// </summary>
@@ -28,21 +30,31 @@
/// <summary>
/// The create directory
/// </summary>
[SupportedOSPlatform("macos")]
createDirectory,
/// <summary>
/// The prompt to create
/// </summary>
[SupportedOSPlatform("windows")]
promptToCreate,
/// <summary>
/// The no resolve aliases
/// </summary>
[SupportedOSPlatform("macos")]
noResolveAliases,
/// <summary>
/// The treat package as directory
/// </summary>
treatPackageAsDirectory
[SupportedOSPlatform("macos")]
treatPackageAsDirectory,
/// <summary>
/// Do not add the item being opened to the recent documents list (Windows).
/// </summary>
[SupportedOSPlatform("windows")]
dontAddToRecent
}
}

Some files were not shown because too many files have changed in this diff Show More