mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-09-22 23:15:25 +00:00
Compare commits
11 Commits
0.6.0-pre.
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0c0523c7d | ||
|
|
6d94cf9538 | ||
|
|
521e7c935a | ||
|
|
730eacbd52 | ||
|
|
deca8166ec | ||
|
|
a04ca0b9bc | ||
|
|
fffd7dfb79 | ||
|
|
98c72af3d6 | ||
|
|
703cf8f880 | ||
|
|
248d4bd664 | ||
|
|
d5da1dbc9b |
13
Changelog.md
13
Changelog.md
@@ -3,14 +3,27 @@
|
|||||||
## ElectronNET.Core
|
## ElectronNET.Core
|
||||||
|
|
||||||
- Updated dependencies
|
- Updated dependencies
|
||||||
|
- Updated `WebContents` zoom level APIs to use `double` instead of `int` (#956)
|
||||||
|
- Improved migration checks to honor custom output paths and to detect `ProjectGuid` in publish profiles (#946)
|
||||||
- Fixed single instance handling on macOS (#1040)
|
- Fixed single instance handling on macOS (#1040)
|
||||||
- Fixed slow socket bridge startup by binding to an explicit loopback address (#1103)
|
- Fixed slow socket bridge startup by binding to an explicit loopback address (#1103)
|
||||||
- Fixed socket bridge connection when a system proxy is configured (#1105)
|
- Fixed socket bridge connection when a system proxy is configured (#1105)
|
||||||
- Fixed electron-builder using the host RID instead of the target RID (#1097)
|
- Fixed electron-builder using the host RID instead of the target RID (#1097)
|
||||||
- Fixed cross-compilation behavior on same platform (#1098) @epsnm
|
- Fixed cross-compilation behavior on same platform (#1098) @epsnm
|
||||||
- Fixed resolution of unrelated target (#1099) @epsnm
|
- Fixed resolution of unrelated target (#1099) @epsnm
|
||||||
|
- Fixed false alarm for `ELECTRON001` on a root `package-lock.json` (#946)
|
||||||
|
- Added `ElectronNET.Core.Templates` package with a `dotnet new electron-blazor` template (#414)
|
||||||
|
- Added `WebContents.OnZoomChanged` event (#956)
|
||||||
|
- Added `WebContents` page loading APIs `LoadFileAsync`, `IsLoadingAsync`, `IsLoadingMainFrameAsync`, `IsWaitingForResponseAsync`, `Reload`, `ReloadIgnoringCache` and `Stop` (#956)
|
||||||
|
- Added `WebContents.InsertCSSAsync` and `WebContents.RemoveInsertedCSSAsync` for dynamic CSS (#956)
|
||||||
|
- Added `WebContents` editing and selection APIs (undo, redo, cut, copy, paste, insert text, select, ...) (#956)
|
||||||
|
- Added `WebContents.FindInPageAsync`, `WebContents.StopFindInPage` and the `OnFoundInPage` event (#956)
|
||||||
|
- Added `WebContents` audio events `OnAudioStateChanged`, `OnMediaStartedPlaying` and `OnMediaPaused` (#956)
|
||||||
|
- Added `WebContents.ScrollToTop` and `WebContents.ScrollToBottom` (#956)
|
||||||
- Added target framework customization (#1095) @epsnm
|
- Added target framework customization (#1095) @epsnm
|
||||||
- Added configurable Electron root directory for custom packaging layouts (#1106) @DYH1319
|
- Added configurable Electron root directory for custom packaging layouts (#1106) @DYH1319
|
||||||
|
- Added ability for `custom_main.js` to modify command line switches (#1029) @AeonSake
|
||||||
|
- Added migration checks for incomplete `ElectronHostHook` folders (`ELECTRON010`, `ELECTRON011`) (#946)
|
||||||
- Added `ElectronExecutableName` to separate the product name from the executable name (#1003) @AeonSake
|
- Added `ElectronExecutableName` to separate the product name from the executable name (#1003) @AeonSake
|
||||||
- Added build extensibility properties `ElectronSkipExecCommands` and `ElectronIntermediatePublishDir` (#1106) @DYH1319
|
- Added build extensibility properties `ElectronSkipExecCommands` and `ElectronIntermediatePublishDir` (#1106) @DYH1319
|
||||||
|
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ While the classic Electron.NET setup (using an ASP.NET host run by the Electron
|
|||||||
* ElectronNET.Core: [](https://www.nuget.org/packages/ElectronNET.Core.API/)
|
* ElectronNET.Core: [](https://www.nuget.org/packages/ElectronNET.Core.API/)
|
||||||
* ElectronNET.Core.API: [](https://www.nuget.org/packages/ElectronNET.Core.API/)
|
* ElectronNET.Core.API: [](https://www.nuget.org/packages/ElectronNET.Core.API/)
|
||||||
* ElectronNET.Core.AspNet: [](https://www.nuget.org/packages/ElectronNET.Core.AspNet/)
|
* ElectronNET.Core.AspNet: [](https://www.nuget.org/packages/ElectronNET.Core.AspNet/)
|
||||||
|
* ElectronNET.Core.Templates: [](https://www.nuget.org/packages/ElectronNET.Core.Templates/)
|
||||||
|
|
||||||
## 🛠 Requirements to Run
|
## 🛠 Requirements to Run
|
||||||
|
|
||||||
You should have installed:
|
You should have installed:
|
||||||
|
|
||||||
* .NET 6/8 or later.
|
* .NET 8/10 or later.
|
||||||
* The minimum base OS is the same as [.NET 6](https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md) / [.NET 8](https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md).
|
* The minimum base OS is the same as [.NET 8](https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md) / [.NET 10](https://github.com/dotnet/core/blob/main/release-notes/10.0/supported-os.md).
|
||||||
* Node.JS using at least [Version 22.x](https://nodejs.org).
|
* Node.JS using at least [Version 22.x](https://nodejs.org).
|
||||||
|
|
||||||
## 👩🏫 Usage with ASP.NET
|
## 👩🏫 Usage with ASP.NET
|
||||||
@@ -202,6 +203,8 @@ Alternatively, consider using a GitHub sponsorship for the core maintainers:
|
|||||||
- [Gregor Biswanger](https://github.com/sponsors/GregorBiswanger)
|
- [Gregor Biswanger](https://github.com/sponsors/GregorBiswanger)
|
||||||
- [Florian Rappl](https://github.com/sponsors/FlorianRappl)
|
- [Florian Rappl](https://github.com/sponsors/FlorianRappl)
|
||||||
|
|
||||||
|
Or just [buy Florian a coffee](https://buymeacoffee.com/florianrappl) :coffee:.
|
||||||
|
|
||||||
Any support appreciated! 🍻
|
Any support appreciated! 🍻
|
||||||
|
|
||||||
## 🎉 License
|
## 🎉 License
|
||||||
|
|||||||
@@ -58,6 +58,23 @@ Works for both BrowserWindows and BrowserViews.
|
|||||||
- `isBrowserWindow` - Whether the webContents belong to a BrowserWindow or not (the other option is a BrowserView)
|
- `isBrowserWindow` - Whether the webContents belong to a BrowserWindow or not (the other option is a BrowserView)
|
||||||
- `path` - Absolute path to the CSS file location
|
- `path` - Absolute path to the CSS file location
|
||||||
|
|
||||||
|
#### 🧊 `Task<string> InsertCSSAsync(string css, string cssOrigin = null)`
|
||||||
|
Injects CSS into the current web page and returns a unique key for the inserted style sheet.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `css` - The style sheet to inject
|
||||||
|
- `cssOrigin` - Can be either `user` or `author`, defaults to `author`
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
|
||||||
|
The key of the inserted style sheet, to be used with `RemoveInsertedCSSAsync`.
|
||||||
|
|
||||||
|
#### 🧊 `Task RemoveInsertedCSSAsync(string key)`
|
||||||
|
Removes a previously inserted style sheet from the current web page.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `key` - The key returned by `InsertCSSAsync`
|
||||||
|
|
||||||
#### 🧊 `Task LoadURLAsync(string url)`
|
#### 🧊 `Task LoadURLAsync(string url)`
|
||||||
Loads the url in the window. The url must contain the protocol prefix.
|
Loads the url in the window. The url must contain the protocol prefix.
|
||||||
|
|
||||||
@@ -113,6 +130,106 @@ Prints window's web page as PDF with Chromium's preview printing custom settings
|
|||||||
|
|
||||||
Whether the PDF generation succeeded.
|
Whether the PDF generation succeeded.
|
||||||
|
|
||||||
|
#### 🧊 `Task<double> GetZoomFactorAsync()`
|
||||||
|
Returns the current zoom factor. A factor of `1.0` means 100%.
|
||||||
|
|
||||||
|
#### 🧊 `void SetZoomFactor(double factor)`
|
||||||
|
Changes the zoom factor to the specified factor. The zoom factor is the zoom percent divided by 100, so 300% = `3.0`. The factor must be greater than `0.0`.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `factor` - The zoom factor
|
||||||
|
|
||||||
|
#### 🧊 `Task<double> GetZoomLevelAsync()`
|
||||||
|
Returns the current zoom level.
|
||||||
|
|
||||||
|
#### 🧊 `void SetZoomLevel(double level)`
|
||||||
|
Changes the zoom level to the specified level. The original size is `0` and each increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of the original size, respectively.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `level` - The zoom level
|
||||||
|
|
||||||
|
#### 🧊 `Task SetVisualZoomLevelLimitsAsync(double minimumLevel, double maximumLevel)`
|
||||||
|
Sets the maximum and minimum pinch-to-zoom level.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `minimumLevel` - The minimum pinch-to-zoom level
|
||||||
|
- `maximumLevel` - The maximum pinch-to-zoom level
|
||||||
|
|
||||||
|
#### 🧊 `Task LoadFileAsync(string filePath, LoadFileOptions options = null)`
|
||||||
|
Loads the given HTML file, relative to the root of the application.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `filePath` - Path to the HTML file
|
||||||
|
- `options` - Optional `Query`, `Search` and `Hash` parts of the resulting URL
|
||||||
|
|
||||||
|
#### 🧊 `void SetAudioMuted(bool muted)`
|
||||||
|
Mutes or unmutes the audio on the current web page.
|
||||||
|
|
||||||
|
#### 🧊 `Task<bool> IsAudioMutedAsync()`
|
||||||
|
Whether this page has been muted.
|
||||||
|
|
||||||
|
#### 🧊 `Task<bool> IsCurrentlyAudibleAsync()`
|
||||||
|
Whether audio is currently playing.
|
||||||
|
|
||||||
|
#### 🧊 `Task<string> GetUserAgentAsync()` / `void SetUserAgent(string userAgent)`
|
||||||
|
Gets or overrides the user agent for this web page.
|
||||||
|
|
||||||
|
#### 🧊 `Task<bool> IsLoadingAsync()`
|
||||||
|
Whether the web page is still loading resources.
|
||||||
|
|
||||||
|
#### 🧊 `Task<bool> IsLoadingMainFrameAsync()`
|
||||||
|
Whether the main frame (and not just iframes or frames within it) is still loading.
|
||||||
|
|
||||||
|
#### 🧊 `Task<bool> IsWaitingForResponseAsync()`
|
||||||
|
Whether the web page is waiting for a first response from the main resource of the page.
|
||||||
|
|
||||||
|
#### 🧊 `void Reload()`
|
||||||
|
Reloads the current web page.
|
||||||
|
|
||||||
|
#### 🧊 `void ReloadIgnoringCache()`
|
||||||
|
Reloads the current web page and ignores the cache.
|
||||||
|
|
||||||
|
#### 🧊 `void Stop()`
|
||||||
|
Stops any pending navigation.
|
||||||
|
|
||||||
|
#### 🧊 `void Undo()` / `void Redo()`
|
||||||
|
Executes the `undo` / `redo` editing command in the web page.
|
||||||
|
|
||||||
|
#### 🧊 `void Cut()` / `void Copy()` / `void Paste()` / `void PasteAndMatchStyle()` / `void Delete()`
|
||||||
|
Executes the corresponding editing command in the web page.
|
||||||
|
|
||||||
|
#### 🧊 `void CopyImageAt(int x, int y)`
|
||||||
|
Copies the image at the given position to the clipboard.
|
||||||
|
|
||||||
|
#### 🧊 `void SelectAll()` / `void Unselect()` / `void CenterSelection()`
|
||||||
|
Selects all content, clears the selection, or scrolls to the current selection.
|
||||||
|
|
||||||
|
#### 🧊 `void ScrollToTop()` / `void ScrollToBottom()`
|
||||||
|
Scrolls to the top or the bottom of the current web page.
|
||||||
|
|
||||||
|
#### 🧊 `void AdjustSelection(AdjustSelectionOptions options)`
|
||||||
|
Adjusts the start and end points of the current text selection by the given amounts. Negative amounts move towards the beginning of the document.
|
||||||
|
|
||||||
|
#### 🧊 `Task InsertTextAsync(string text)`
|
||||||
|
Inserts text into the focused element.
|
||||||
|
|
||||||
|
#### 🧊 `void Replace(string text)` / `void ReplaceMisspelling(string text)`
|
||||||
|
Replaces the current selection, or the currently misspelled word, with the given text.
|
||||||
|
|
||||||
|
#### 🧊 `Task<int> FindInPageAsync(string text, FindInPageOptions options = null)`
|
||||||
|
Starts a request to find all matches for the text in the web page. Results are reported via the `OnFoundInPage` event.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `text` - Content to be searched, must not be empty
|
||||||
|
- `options` - Optional `Forward`, `FindNext` and `MatchCase` flags
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
|
||||||
|
The request id of the find request.
|
||||||
|
|
||||||
|
#### 🧊 `void StopFindInPage(StopFindInPageAction action)`
|
||||||
|
Stops any `FindInPageAsync` request with the given action (`ClearSelection`, `KeepSelection` or `ActivateSelection`).
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
#### ⚡ `InputEvent`
|
#### ⚡ `InputEvent`
|
||||||
@@ -142,6 +259,21 @@ Emitted when the document in the top-level frame is loaded.
|
|||||||
#### ⚡ `OnWillRedirect`
|
#### ⚡ `OnWillRedirect`
|
||||||
Emitted when a server side redirect occurs during navigation.
|
Emitted when a server side redirect occurs during navigation.
|
||||||
|
|
||||||
|
#### ⚡ `OnZoomChanged`
|
||||||
|
Emitted when the user changes the zoom level using the mouse wheel or the keyboard. The handler receives the `ZoomDirection` (`In` or `Out`).
|
||||||
|
|
||||||
|
#### ⚡ `OnFoundInPage`
|
||||||
|
Emitted when a result is available for a `FindInPageAsync` request. The handler receives a `FoundInPageResult`.
|
||||||
|
|
||||||
|
#### ⚡ `OnAudioStateChanged`
|
||||||
|
Emitted when media becomes audible or inaudible. The handler receives `true` if one or more frames or child web contents are emitting audio.
|
||||||
|
|
||||||
|
#### ⚡ `OnMediaStartedPlaying`
|
||||||
|
Emitted when media starts playing.
|
||||||
|
|
||||||
|
#### ⚡ `OnMediaPaused`
|
||||||
|
Emitted when media is paused or done playing.
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
### Page Loading
|
### Page Loading
|
||||||
@@ -279,6 +411,82 @@ webContents.OnCrashed += (killed) =>
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Zoom Control
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Set the zoom to 150%
|
||||||
|
webContents.SetZoomFactor(1.5);
|
||||||
|
|
||||||
|
var factor = await webContents.GetZoomFactorAsync();
|
||||||
|
Console.WriteLine($"Current zoom: {factor * 100}%");
|
||||||
|
|
||||||
|
// Zoom levels are relative: each step is 20% larger/smaller, 0 is the original size
|
||||||
|
webContents.SetZoomLevel(2);
|
||||||
|
|
||||||
|
// Restrict pinch-to-zoom
|
||||||
|
await webContents.SetVisualZoomLevelLimitsAsync(1, 3);
|
||||||
|
|
||||||
|
// React to zoom changes triggered by the user
|
||||||
|
webContents.OnZoomChanged += (direction) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"User zoomed {direction}");
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** The zoom factor is shared by all windows using the same session partition. Assign a unique `WebPreferences.Partition` per window if each window should keep its own zoom.
|
||||||
|
|
||||||
|
### Dynamic CSS
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var key = await webContents.InsertCSSAsync("body { background-color: #202020; }");
|
||||||
|
|
||||||
|
// ... later
|
||||||
|
await webContents.RemoveInsertedCSSAsync(key);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Editing and Selection
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
await webContents.InsertTextAsync("Hello from .NET");
|
||||||
|
|
||||||
|
webContents.SelectAll();
|
||||||
|
webContents.Copy();
|
||||||
|
webContents.Unselect();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Find in Page
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
webContents.OnFoundInPage += (result) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{result.ActiveMatchOrdinal}/{result.Matches} matches");
|
||||||
|
|
||||||
|
if (result.FinalUpdate)
|
||||||
|
{
|
||||||
|
webContents.StopFindInPage(StopFindInPageAction.ClearSelection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var requestId = await webContents.FindInPageAsync("electron", new FindInPageOptions { MatchCase = false });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
webContents.SetAudioMuted(true);
|
||||||
|
|
||||||
|
var muted = await webContents.IsAudioMutedAsync();
|
||||||
|
var audible = await webContents.IsCurrentlyAudibleAsync();
|
||||||
|
|
||||||
|
webContents.OnAudioStateChanged += (isAudible) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(isAudible ? "Page started emitting audio" : "Page went silent");
|
||||||
|
};
|
||||||
|
|
||||||
|
webContents.OnMediaStartedPlaying += () => Console.WriteLine("Media playing");
|
||||||
|
webContents.OnMediaPaused += () => Console.WriteLine("Media paused");
|
||||||
|
```
|
||||||
|
|
||||||
## Related APIs
|
## Related APIs
|
||||||
|
|
||||||
- [Electron.WindowManager](WindowManager.md) - Windows containing web contents
|
- [Electron.WindowManager](WindowManager.md) - Windows containing web contents
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ When you build an Electron.NET project, the following validation checks are perf
|
|||||||
| [ELECTRON005](#4-parent-paths-not-allowed-in-electron-builderjson) | Parent paths not allowed | Checks for `..` references in config |
|
| [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 |
|
| [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 |
|
| [ELECTRON007](#5-publish-profile-validation) | Console publish profile mismatch | Warns when console projects have ASP.NET-style profiles |
|
||||||
|
| [ELECTRON010](#6-electronhosthook-folder-validation) | ElectronHostHook without package.json | Warns when hook TypeScript files exist without a `package.json` |
|
||||||
|
| [ELECTRON011](#6-electronhosthook-folder-validation) | ElectronHostHook without tsconfig.json | Warns when hook TypeScript files exist without a `tsconfig.json` |
|
||||||
|
|
||||||
|
All checks skip the build output folders (`bin`, `obj`, `publish`, `.electron`, `node_modules`) as well as any custom output paths configured via `BaseOutputPath` / `BaseIntermediateOutputPath` (for example the .NET `artifacts` output layout).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@ Rules:
|
|||||||
|
|
||||||
- **ELECTRON001**: `package.json` / `package-lock.json` must not exist in the project directory or subdirectories
|
- **ELECTRON001**: `package.json` / `package-lock.json` must not exist in the project directory or subdirectories
|
||||||
- Exception: `ElectronHostHook` folder is allowed
|
- Exception: `ElectronHostHook` folder is allowed
|
||||||
- Note: a **root** `package.json` is **excluded** from `ELECTRON001` and validated by `ELECTRON008` / `ELECTRON009`
|
- Note: a **root** `package.json` (and its `package-lock.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.
|
- **ELECTRON008**: If a root `package.json` exists, it must **not** contain electron-related dependencies or configuration.
|
||||||
|
|
||||||
@@ -113,6 +117,10 @@ The `electron.manifest.json` file format is deprecated. All configuration should
|
|||||||
"mac": {
|
"mac": {
|
||||||
"icon": "Assets/app.icns",
|
"icon": "Assets/app.icns",
|
||||||
"target": ["dmg", "zip"]
|
"target": ["dmg", "zip"]
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": true,
|
||||||
|
"perMachine": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -208,7 +216,7 @@ The build system examines `.pubxml` files in the `Properties/PublishProfiles` fo
|
|||||||
|
|
||||||
- **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.
|
- **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.
|
- **ELECTRON007**: For **console/other projects** (not using the Web SDK), checks that publish profiles do NOT include the `WebPublishMethod` or `ProjectGuid` property. These ASP.NET-specific properties are incorrect for non-web applications.
|
||||||
|
|
||||||
### Why this matters
|
### Why this matters
|
||||||
|
|
||||||
@@ -233,9 +241,54 @@ For correct publish profile examples for both ASP.NET and Console applications,
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 6. ElectronHostHook Folder Validation
|
||||||
|
|
||||||
|
**Warning Codes:** `ELECTRON010`, `ELECTRON011`
|
||||||
|
|
||||||
|
### What is checked
|
||||||
|
|
||||||
|
If the project contains an `ElectronHostHook` folder with TypeScript files:
|
||||||
|
|
||||||
|
- **ELECTRON010**: The folder must contain a `package.json`
|
||||||
|
- **ELECTRON011**: The folder must contain a `tsconfig.json`
|
||||||
|
|
||||||
|
### Why this matters
|
||||||
|
|
||||||
|
Custom host hook code is only compiled and packaged when the `ElectronHostHook` folder declares its npm dependencies in a `package.json`. If that file is missing, the hook is silently dropped and the corresponding `Electron.HostHook` calls fail at runtime. The `tsconfig.json` is required so the hook sources are compiled with the expected settings.
|
||||||
|
|
||||||
|
### How to fix
|
||||||
|
|
||||||
|
Add the missing files to the `ElectronHostHook` folder:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "electron-host-hook",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"socket.io": "^4.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **See also:** [HostHook API](../API/HostHook.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Disabling Migration Checks
|
## Disabling Migration Checks
|
||||||
|
|
||||||
If you need to disable specific migration checks (not recommended), you can set the following properties in your `.csproj` file:
|
Individual checks can be downgraded to messages by their warning code:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<PropertyGroup>
|
||||||
|
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);ELECTRON005</MSBuildWarningsAsMessages>
|
||||||
|
</PropertyGroup>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to disable all migration checks (not recommended), you can set the following property in your `.csproj` file:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -48,17 +48,19 @@ You can also manually edit `electron-builder.json`:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"linux": {
|
"linux": {
|
||||||
"target": [
|
"target": ["tar.xz"]
|
||||||
"tar.xz"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "portable",
|
"target": "nsis",
|
||||||
"arch": "x64"
|
"arch": "x64"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": true,
|
||||||
|
"perMachine": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ See [System Requirements](../GettingStarted/System-Requirements.md).
|
|||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
> [!Tip]
|
||||||
|
> To skip the manual setup below, scaffold a ready-to-run app with the
|
||||||
|
> [project templates](Templates.md): `dotnet new install ElectronNET.Core.Templates` followed by
|
||||||
|
> `dotnet new electron-blazor -n MyDesktopApp`.
|
||||||
|
|
||||||
### 1. Create ASP.NET Core Project
|
### 1. Create ASP.NET Core Project
|
||||||
|
|
||||||
#### Visual Studio
|
#### Visual Studio
|
||||||
|
|||||||
88
docs/GettingStarted/Templates.md
Normal file
88
docs/GettingStarted/Templates.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
# Project Templates
|
||||||
|
|
||||||
|
Electron.NET ships a `dotnet new` template package so you can scaffold a ready-to-run desktop app instead of wiring up an ASP.NET project by hand.
|
||||||
|
|
||||||
|
## 🛠 System Requirements
|
||||||
|
|
||||||
|
See [System Requirements](../GettingStarted/System-Requirements.md).
|
||||||
|
|
||||||
|
## 📦 Install the Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet new install ElectronNET.Core.Templates
|
||||||
|
```
|
||||||
|
|
||||||
|
To update to the latest version, run the same command again. To remove them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet new uninstall ElectronNET.Core.Templates
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Available Templates
|
||||||
|
|
||||||
|
| Template | Short name | Description |
|
||||||
|
|----------|------------|-------------|
|
||||||
|
| Electron.NET Blazor App | `electron-blazor` | A Blazor Server app hosted in an Electron shell |
|
||||||
|
|
||||||
|
## 🧱 Create a Blazor App
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet new electron-blazor -n MyDesktopApp
|
||||||
|
cd MyDesktopApp
|
||||||
|
```
|
||||||
|
|
||||||
|
Then start it in the Electron shell:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet build
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Option | Values | Default | Description |
|
||||||
|
|--------|--------|---------|-------------|
|
||||||
|
| `-f`, `--framework` | `net8.0`, `net10.0` | `net10.0` | The target framework of the generated project |
|
||||||
|
| `-e`, `--electron-version` | any Electron version | `38.2.2` | The Electron version the app is built against |
|
||||||
|
| `-p`, `--port` | port number | `8001` | The port the ASP.NET server uses during development |
|
||||||
|
| `--no-restore` | — | — | Skip the automatic `dotnet restore` |
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet new electron-blazor -n MyDesktopApp -f net10.0 -e 30.4.0 -p 8123
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 What You Get
|
||||||
|
|
||||||
|
```
|
||||||
|
MyDesktopApp/
|
||||||
|
├── Components/
|
||||||
|
│ ├── Layout/ MainLayout.razor, NavMenu.razor
|
||||||
|
│ ├── Pages/ Home.razor, Counter.razor
|
||||||
|
│ ├── App.razor
|
||||||
|
│ ├── Routes.razor
|
||||||
|
│ └── _Imports.razor
|
||||||
|
├── Properties/
|
||||||
|
│ ├── PublishProfiles/ win-x64, linux-x64 and osx-arm64 folder profiles
|
||||||
|
│ ├── electron-builder.json
|
||||||
|
│ └── launchSettings.json
|
||||||
|
├── wwwroot/app.css
|
||||||
|
├── appsettings.json
|
||||||
|
├── Program.cs
|
||||||
|
└── MyDesktopApp.csproj
|
||||||
|
```
|
||||||
|
|
||||||
|
The project already references `ElectronNET.Core` and `ElectronNET.Core.AspNet`, calls `builder.UseElectron(...)` in `Program.cs`, and passes all [Migration Checks](../Core/Migration-Checks.md) out of the box.
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
> `ElectronNET.API` also defines a type named `App`, which collides with the Blazor root
|
||||||
|
> component. That is why the template calls `app.MapRazorComponents<MyDesktopApp.Components.App>()`
|
||||||
|
> with a fully qualified type name.
|
||||||
|
|
||||||
|
## 🚀 Next Steps
|
||||||
|
|
||||||
|
- **[Configuration](../Using/Configuration.md)** - Adjust app metadata and Electron settings
|
||||||
|
- **[Debugging](../Using/Debugging.md)** - Debug the .NET and Electron sides
|
||||||
|
- **[Package Building](../Using/Package-Building.md)** - Create distributable packages
|
||||||
@@ -7,6 +7,7 @@ This guide explains how to include and use a `custom_main.js` file in your Elect
|
|||||||
- Register custom protocol handlers (e.g., `myapp://`) — protocols must be registered before the app is fully initialized
|
- 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)
|
- Integrate Node.js modules (e.g., telemetry, OS APIs)
|
||||||
- Control startup logic (abort, environment checks)
|
- Control startup logic (abort, environment checks)
|
||||||
|
- Modify Chromium / Electron.NET command line switches before they are evaluated
|
||||||
- Set up IPC messaging or preload scripts
|
- Set up IPC messaging or preload scripts
|
||||||
|
|
||||||
## Step-by-Step Process
|
## Step-by-Step Process
|
||||||
@@ -65,10 +66,29 @@ Use environment variables to control features:
|
|||||||
if (env === 'Development') { /* enable dev features */ }
|
if (env === 'Development') { /* enable dev features */ }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Modifying Command Line Switches
|
||||||
|
|
||||||
|
`onStartup` is invoked before Electron.NET reads any of its own switches (`manifest`, `unpackedelectron`, `unpackeddotnet`, `dotnetpacked`, `electronforcedport`, `electronurl`), so the hook can append or override them:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
module.exports.onStartup = function (host) {
|
||||||
|
const { app } = require('electron');
|
||||||
|
|
||||||
|
// Force a fixed socket bridge port
|
||||||
|
app.commandLine.appendSwitch('electronforcedport', '8000');
|
||||||
|
|
||||||
|
// Chromium switches, e.g. to disable the GPU sandbox
|
||||||
|
app.commandLine.appendSwitch('disable-gpu-sandbox');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- `custom_main.js` must use CommonJS syntax (`module.exports.onStartup = ...`).
|
- `custom_main.js` must use CommonJS syntax (`module.exports.onStartup = ...`).
|
||||||
- Place the file in your source directory and copy it to `.electron` using `.csproj`.
|
- Place the file in your source directory and copy it to `.electron` using `.csproj`.
|
||||||
- Electron.NET will abort startup if `onStartup` returns `false`.
|
- Electron.NET will abort startup if `onStartup` returns `false`.
|
||||||
|
- `onStartup` runs before the Electron.NET command line switches are evaluated, so switches set there are picked up by the host.
|
||||||
|
|
||||||
### Complete example is available here [ElectronNetSampleApp](https://github.com/niteshsinghal85/ElectronNetSampleApp)
|
### Complete example is available here [ElectronNetSampleApp](https://github.com/niteshsinghal85/ElectronNetSampleApp)
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
- [System Requirements](GettingStarted/System-Requirements.md)
|
- [System Requirements](GettingStarted/System-Requirements.md)
|
||||||
|
- [Project Templates](GettingStarted/Templates.md)
|
||||||
- [With ASP.Net](GettingStarted/ASP.Net.md)
|
- [With ASP.Net](GettingStarted/ASP.Net.md)
|
||||||
- [With a Console App](GettingStarted/Console-App.md)
|
- [With a Console App](GettingStarted/Console-App.md)
|
||||||
|
|
||||||
|
|||||||
18
src/ElectronNET.API/API/Entities/AdjustSelectionOptions.cs
Normal file
18
src/ElectronNET.API/API/Entities/AdjustSelectionOptions.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace ElectronNET.API.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Options for <see cref="WebContents.AdjustSelection"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class AdjustSelectionOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Amount to shift the start index of the current selection.
|
||||||
|
/// </summary>
|
||||||
|
public int? Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Amount to shift the end index of the current selection.
|
||||||
|
/// </summary>
|
||||||
|
public int? End { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/ElectronNET.API/API/Entities/FindInPageOptions.cs
Normal file
24
src/ElectronNET.API/API/Entities/FindInPageOptions.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace ElectronNET.API.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Options for <see cref="WebContents.FindInPageAsync"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class FindInPageOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to search forward or backward, defaults to true.
|
||||||
|
/// </summary>
|
||||||
|
public bool? Forward { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to begin a new text finding session with this request. Should be true
|
||||||
|
/// for initial requests, and false for subsequent requests. Defaults to false.
|
||||||
|
/// </summary>
|
||||||
|
public bool? FindNext { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether search should be case-sensitive, defaults to false.
|
||||||
|
/// </summary>
|
||||||
|
public bool? MatchCase { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/ElectronNET.API/API/Entities/FoundInPageResult.cs
Normal file
33
src/ElectronNET.API/API/Entities/FoundInPageResult.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace ElectronNET.API.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The result of a text finding session, reported by the found-in-page event.
|
||||||
|
/// </summary>
|
||||||
|
public class FoundInPageResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The identifier of the request returned by FindInPageAsync.
|
||||||
|
/// </summary>
|
||||||
|
public int RequestId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Position of the active match.
|
||||||
|
/// </summary>
|
||||||
|
public int ActiveMatchOrdinal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of matches.
|
||||||
|
/// </summary>
|
||||||
|
public int Matches { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinates of the first match region.
|
||||||
|
/// </summary>
|
||||||
|
public Rectangle SelectionArea { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether more responses are to follow.
|
||||||
|
/// </summary>
|
||||||
|
public bool FinalUpdate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/ElectronNET.API/API/Entities/LoadFileOptions.cs
Normal file
25
src/ElectronNET.API/API/Entities/LoadFileOptions.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ElectronNET.API.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Options for loading a local file into a web page.
|
||||||
|
/// </summary>
|
||||||
|
public class LoadFileOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Passed to url.format().
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Query { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Passed to url.format().
|
||||||
|
/// </summary>
|
||||||
|
public string Search { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Passed to url.format().
|
||||||
|
/// </summary>
|
||||||
|
public string Hash { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/ElectronNET.API/API/Entities/StopFindInPageAction.cs
Normal file
23
src/ElectronNET.API/API/Entities/StopFindInPageAction.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace ElectronNET.API.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines what happens to the selection when a text finding session is stopped.
|
||||||
|
/// </summary>
|
||||||
|
public enum StopFindInPageAction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Clear the selection.
|
||||||
|
/// </summary>
|
||||||
|
ClearSelection,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Translate the selection into a normal selection.
|
||||||
|
/// </summary>
|
||||||
|
KeepSelection,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focus and click the selection node.
|
||||||
|
/// </summary>
|
||||||
|
ActivateSelection
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/ElectronNET.API/API/Entities/ZoomDirection.cs
Normal file
18
src/ElectronNET.API/API/Entities/ZoomDirection.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace ElectronNET.API.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The direction of a zoom change triggered by the user.
|
||||||
|
/// </summary>
|
||||||
|
public enum ZoomDirection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The user zoomed in.
|
||||||
|
/// </summary>
|
||||||
|
In,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user zoomed out.
|
||||||
|
/// </summary>
|
||||||
|
Out
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,6 +113,52 @@ public class WebContents : ApiBase
|
|||||||
remove => RemoveEvent(value, Id);
|
remove => RemoveEvent(value, Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emitted when the user is requesting to change the zoom level using the mouse wheel or the keyboard.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<ZoomDirection> OnZoomChanged
|
||||||
|
{
|
||||||
|
add => AddEvent(value, Id);
|
||||||
|
remove => RemoveEvent(value, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emitted when a result is available for a <see cref="FindInPageAsync(string, FindInPageOptions)"/> request.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<FoundInPageResult> OnFoundInPage
|
||||||
|
{
|
||||||
|
add => AddEvent(value, Id);
|
||||||
|
remove => RemoveEvent(value, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emitted when media becomes audible or inaudible. The parameter is true if one or more
|
||||||
|
/// frames or child web contents are emitting audio.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<bool> OnAudioStateChanged
|
||||||
|
{
|
||||||
|
add => AddEvent(value, Id);
|
||||||
|
remove => RemoveEvent(value, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emitted when media starts playing.
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnMediaStartedPlaying
|
||||||
|
{
|
||||||
|
add => AddEvent(value, Id);
|
||||||
|
remove => RemoveEvent(value, Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emitted when media is paused or done playing.
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnMediaPaused
|
||||||
|
{
|
||||||
|
add => AddEvent(value, Id);
|
||||||
|
remove => RemoveEvent(value, Id);
|
||||||
|
}
|
||||||
|
|
||||||
internal WebContents(int id)
|
internal WebContents(int id)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
@@ -304,6 +350,68 @@ public class WebContents : ApiBase
|
|||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the given file in the window. The file path must be a path to an HTML file
|
||||||
|
/// relative to the root of your application.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">Path to the HTML file.</param>
|
||||||
|
/// <param name="options">Optional query, search and hash parts of the resulting URL.</param>
|
||||||
|
public Task LoadFileAsync(string filePath, LoadFileOptions options = null)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource();
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Once("webContents-loadFile-complete" + Id, () =>
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Off("webContents-loadFile-error" + Id);
|
||||||
|
tcs.SetResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Once<string>("webContents-loadFile-error" + Id, (error) => { tcs.SetException(new InvalidOperationException(error)); });
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Emit("webContents-loadFile", Id, filePath, options);
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns boolean - Whether web page is still loading resources.
|
||||||
|
/// </summary>
|
||||||
|
public Task<bool> IsLoadingAsync() => InvokeAsync<bool>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns boolean - Whether the main frame (and not just iframes or frames within it) is still loading.
|
||||||
|
/// </summary>
|
||||||
|
public Task<bool> IsLoadingMainFrameAsync() => InvokeAsync<bool>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns boolean - Whether the web page is waiting for a first-response from the main resource of the page.
|
||||||
|
/// </summary>
|
||||||
|
public Task<bool> IsWaitingForResponseAsync() => InvokeAsync<bool>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads the current web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Reload()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-reload", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads the current page and ignores cache.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadIgnoringCache()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-reloadIgnoringCache", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops any pending navigation.
|
||||||
|
/// </summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-stop", Id);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inserts CSS into the web page.
|
/// Inserts CSS into the web page.
|
||||||
/// See: https://www.electronjs.org/docs/api/web-contents#contentsinsertcsscss-options
|
/// See: https://www.electronjs.org/docs/api/web-contents#contentsinsertcsscss-options
|
||||||
@@ -316,6 +424,37 @@ public class WebContents : ApiBase
|
|||||||
BridgeConnector.Socket.Emit("webContents-insertCSS", Id, isBrowserWindow, path);
|
BridgeConnector.Socket.Emit("webContents-insertCSS", Id, isBrowserWindow, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Injects CSS into the current web page and returns a unique key for the inserted
|
||||||
|
/// stylesheet, which can be used with <see cref="RemoveInsertedCSSAsync"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="css">The style sheet to inject.</param>
|
||||||
|
/// <param name="cssOrigin">Can be either 'user' or 'author'. Defaults to 'author'.</param>
|
||||||
|
/// <returns>The key of the inserted style sheet.</returns>
|
||||||
|
public Task<string> InsertCSSAsync(string css, string cssOrigin = null)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<string>();
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Once<string>("webContents-insertCSSText-completed" + Id, tcs.SetResult);
|
||||||
|
BridgeConnector.Socket.Emit("webContents-insertCSSText", Id, css, cssOrigin);
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the inserted CSS from the current web page.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key returned by <see cref="InsertCSSAsync"/>.</param>
|
||||||
|
public Task RemoveInsertedCSSAsync(string key)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource();
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Once("webContents-removeInsertedCSS-completed" + Id, tcs.SetResult);
|
||||||
|
BridgeConnector.Socket.Emit("webContents-removeInsertedCSS", Id, key);
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns number - The current zoom factor.
|
/// Returns number - The current zoom factor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -337,14 +476,14 @@ public class WebContents : ApiBase
|
|||||||
/// Returns number - The current zoom level.
|
/// Returns number - The current zoom level.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<int> GetZoomLevelAsync() => InvokeAsync<int>();
|
public Task<double> GetZoomLevelAsync() => InvokeAsync<double>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the zoom level to the specified level.
|
/// Changes the zoom level to the specified level.
|
||||||
/// The original size is 0 and each increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively.
|
/// The original size is 0 and each increment above or below represents zooming 20% larger or smaller to default limits of 300% and 50% of original size, respectively.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="level"></param>
|
/// <param name="level"></param>
|
||||||
public void SetZoomLevel(int level)
|
public void SetZoomLevel(double level)
|
||||||
{
|
{
|
||||||
BridgeConnector.Socket.Emit("webContents-setZoomLevel", Id, level);
|
BridgeConnector.Socket.Emit("webContents-setZoomLevel", Id, level);
|
||||||
}
|
}
|
||||||
@@ -354,7 +493,7 @@ public class WebContents : ApiBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="minimumLevel"></param>
|
/// <param name="minimumLevel"></param>
|
||||||
/// <param name="maximumLevel"></param>
|
/// <param name="maximumLevel"></param>
|
||||||
public Task SetVisualZoomLevelLimitsAsync(int minimumLevel, int maximumLevel)
|
public Task SetVisualZoomLevelLimitsAsync(double minimumLevel, double maximumLevel)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource();
|
var tcs = new TaskCompletionSource();
|
||||||
|
|
||||||
@@ -399,4 +538,178 @@ public class WebContents : ApiBase
|
|||||||
{
|
{
|
||||||
BridgeConnector.Socket.Emit("webContents-setUserAgent", Id, userAgent);
|
BridgeConnector.Socket.Emit("webContents-setUserAgent", Id, userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command undo in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-undo", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command redo in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Redo()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-redo", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command cut in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Cut()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-cut", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command copy in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Copy()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-copy", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the image at the given position to the clipboard.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x"></param>
|
||||||
|
/// <param name="y"></param>
|
||||||
|
public void CopyImageAt(int x, int y)
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-copyImageAt", Id, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command paste in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Paste()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-paste", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command pasteAndMatchStyle in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void PasteAndMatchStyle()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-pasteAndMatchStyle", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command delete in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Delete()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-delete", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command selectAll in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void SelectAll()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-selectAll", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command unselect in web page.
|
||||||
|
/// </summary>
|
||||||
|
public void Unselect()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-unselect", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjusts the current text selection starting and ending points by the given amounts.
|
||||||
|
/// A negative amount moves the selection towards the beginning of the document.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public void AdjustSelection(AdjustSelectionOptions options)
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-adjustSelection", Id, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls to the current text selection.
|
||||||
|
/// </summary>
|
||||||
|
public void CenterSelection()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-centerSelection", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls to the top of the current web page.
|
||||||
|
/// </summary>
|
||||||
|
public void ScrollToTop()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-scrollToTop", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolls to the bottom of the current web page.
|
||||||
|
/// </summary>
|
||||||
|
public void ScrollToBottom()
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-scrollToBottom", Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts text to the focused element.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text to be inserted.</param>
|
||||||
|
public Task InsertTextAsync(string text)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource();
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Once("webContents-insertText-completed" + Id, tcs.SetResult);
|
||||||
|
BridgeConnector.Socket.Emit("webContents-insertText", Id, text);
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command replace in web page.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
public void Replace(string text)
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-replace", Id, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the editing command replaceMisspelling in web page.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
public void ReplaceMisspelling(string text)
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-replaceMisspelling", Id, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts a request to find all matches for the text in the web page.
|
||||||
|
/// The result of the request can be obtained by subscribing to the <see cref="OnFoundInPage"/> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Content to be searched, must not be empty.</param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns>The request id used for the request.</returns>
|
||||||
|
public Task<int> FindInPageAsync(string text, FindInPageOptions options = null)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
|
||||||
|
BridgeConnector.Socket.Once<int>("webContents-findInPage-completed" + Id, tcs.SetResult);
|
||||||
|
BridgeConnector.Socket.Emit("webContents-findInPage", Id, text, options);
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops any findInPage request for the web contents with the provided action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
public void StopFindInPage(StopFindInPageAction action)
|
||||||
|
{
|
||||||
|
BridgeConnector.Socket.Emit("webContents-stopFindInPage", Id, action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +111,41 @@ module.exports = (socket) => {
|
|||||||
electronSocket.emit("webContents-domReady" + id);
|
electronSocket.emit("webContents-domReady" + id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
socket.on("register-webContents-zoomChanged", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents.removeAllListeners("zoom-changed");
|
||||||
|
browserWindow.webContents.on("zoom-changed", (_, zoomDirection) => {
|
||||||
|
electronSocket.emit("webContents-zoomChanged" + id, zoomDirection);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
socket.on("register-webContents-foundInPage", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents.removeAllListeners("found-in-page");
|
||||||
|
browserWindow.webContents.on("found-in-page", (_, result) => {
|
||||||
|
electronSocket.emit("webContents-foundInPage" + id, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
socket.on("register-webContents-audioStateChanged", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents.removeAllListeners("audio-state-changed");
|
||||||
|
browserWindow.webContents.on("audio-state-changed", (event) => {
|
||||||
|
electronSocket.emit("webContents-audioStateChanged" + id, event.audible);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
socket.on("register-webContents-mediaStartedPlaying", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents.removeAllListeners("media-started-playing");
|
||||||
|
browserWindow.webContents.on("media-started-playing", () => {
|
||||||
|
electronSocket.emit("webContents-mediaStartedPlaying" + id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
socket.on("register-webContents-mediaPaused", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents.removeAllListeners("media-paused");
|
||||||
|
browserWindow.webContents.on("media-paused", () => {
|
||||||
|
electronSocket.emit("webContents-mediaPaused" + id);
|
||||||
|
});
|
||||||
|
});
|
||||||
socket.on("webContents-openDevTools", (id, options) => {
|
socket.on("webContents-openDevTools", (id, options) => {
|
||||||
if (options) {
|
if (options) {
|
||||||
getWindowById(id).webContents.openDevTools(options);
|
getWindowById(id).webContents.openDevTools(options);
|
||||||
@@ -308,6 +343,36 @@ module.exports = (socket) => {
|
|||||||
electronSocket.emit("webContents-loadURL-error" + id, error);
|
electronSocket.emit("webContents-loadURL-error" + id, error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
socket.on("webContents-loadFile", (id, filePath, options) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents
|
||||||
|
.loadFile(filePath, options ?? undefined)
|
||||||
|
.then(() => {
|
||||||
|
electronSocket.emit("webContents-loadFile-complete" + id);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
electronSocket.emit("webContents-loadFile-error" + id, error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
socket.on("webContents-isLoading", (id) => {
|
||||||
|
electronSocket.emit("webContents-isLoading-completed", getWindowById(id).webContents.isLoading());
|
||||||
|
});
|
||||||
|
socket.on("webContents-isLoadingMainFrame", (id) => {
|
||||||
|
electronSocket.emit("webContents-isLoadingMainFrame-completed", getWindowById(id).webContents.isLoadingMainFrame());
|
||||||
|
});
|
||||||
|
socket.on("webContents-isWaitingForResponse", (id) => {
|
||||||
|
electronSocket.emit("webContents-isWaitingForResponse-completed", getWindowById(id).webContents.isWaitingForResponse());
|
||||||
|
});
|
||||||
|
socket.on("webContents-reload", (id) => {
|
||||||
|
getWindowById(id).webContents.reload();
|
||||||
|
});
|
||||||
|
socket.on("webContents-reloadIgnoringCache", (id) => {
|
||||||
|
getWindowById(id).webContents.reloadIgnoringCache();
|
||||||
|
});
|
||||||
|
socket.on("webContents-stop", (id) => {
|
||||||
|
getWindowById(id).webContents.stop();
|
||||||
|
});
|
||||||
socket.on("webContents-insertCSS", (id, isBrowserWindow, path) => {
|
socket.on("webContents-insertCSS", (id, isBrowserWindow, path) => {
|
||||||
if (isBrowserWindow) {
|
if (isBrowserWindow) {
|
||||||
const browserWindow = getWindowById(id);
|
const browserWindow = getWindowById(id);
|
||||||
@@ -330,6 +395,14 @@ module.exports = (socket) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
socket.on("webContents-insertCSSText", async (id, css, cssOrigin) => {
|
||||||
|
const key = await getWindowById(id).webContents.insertCSS(css, cssOrigin ? { cssOrigin } : undefined);
|
||||||
|
electronSocket.emit("webContents-insertCSSText-completed" + id, key);
|
||||||
|
});
|
||||||
|
socket.on("webContents-removeInsertedCSS", async (id, key) => {
|
||||||
|
await getWindowById(id).webContents.removeInsertedCSS(key);
|
||||||
|
electronSocket.emit("webContents-removeInsertedCSS-completed" + id);
|
||||||
|
});
|
||||||
socket.on("webContents-session-getAllExtensions", (id) => {
|
socket.on("webContents-session-getAllExtensions", (id) => {
|
||||||
const browserWindow = getWindowById(id);
|
const browserWindow = getWindowById(id);
|
||||||
const extensionsList = browserWindow.webContents.session.getAllExtensions();
|
const extensionsList = browserWindow.webContents.session.getAllExtensions();
|
||||||
@@ -403,6 +476,65 @@ module.exports = (socket) => {
|
|||||||
socket.on("webContents-setUserAgent", (id, userAgent) => {
|
socket.on("webContents-setUserAgent", (id, userAgent) => {
|
||||||
getWindowById(id).webContents.setUserAgent(userAgent);
|
getWindowById(id).webContents.setUserAgent(userAgent);
|
||||||
});
|
});
|
||||||
|
socket.on("webContents-undo", (id) => {
|
||||||
|
getWindowById(id).webContents.undo();
|
||||||
|
});
|
||||||
|
socket.on("webContents-redo", (id) => {
|
||||||
|
getWindowById(id).webContents.redo();
|
||||||
|
});
|
||||||
|
socket.on("webContents-cut", (id) => {
|
||||||
|
getWindowById(id).webContents.cut();
|
||||||
|
});
|
||||||
|
socket.on("webContents-copy", (id) => {
|
||||||
|
getWindowById(id).webContents.copy();
|
||||||
|
});
|
||||||
|
socket.on("webContents-copyImageAt", (id, x, y) => {
|
||||||
|
getWindowById(id).webContents.copyImageAt(x, y);
|
||||||
|
});
|
||||||
|
socket.on("webContents-paste", (id) => {
|
||||||
|
getWindowById(id).webContents.paste();
|
||||||
|
});
|
||||||
|
socket.on("webContents-pasteAndMatchStyle", (id) => {
|
||||||
|
getWindowById(id).webContents.pasteAndMatchStyle();
|
||||||
|
});
|
||||||
|
socket.on("webContents-delete", (id) => {
|
||||||
|
getWindowById(id).webContents.delete();
|
||||||
|
});
|
||||||
|
socket.on("webContents-selectAll", (id) => {
|
||||||
|
getWindowById(id).webContents.selectAll();
|
||||||
|
});
|
||||||
|
socket.on("webContents-unselect", (id) => {
|
||||||
|
getWindowById(id).webContents.unselect();
|
||||||
|
});
|
||||||
|
socket.on("webContents-adjustSelection", (id, options) => {
|
||||||
|
getWindowById(id).webContents.adjustSelection(options ?? {});
|
||||||
|
});
|
||||||
|
socket.on("webContents-centerSelection", (id) => {
|
||||||
|
getWindowById(id).webContents.centerSelection();
|
||||||
|
});
|
||||||
|
socket.on("webContents-scrollToTop", (id) => {
|
||||||
|
getWindowById(id).webContents.scrollToTop();
|
||||||
|
});
|
||||||
|
socket.on("webContents-scrollToBottom", (id) => {
|
||||||
|
getWindowById(id).webContents.scrollToBottom();
|
||||||
|
});
|
||||||
|
socket.on("webContents-insertText", async (id, text) => {
|
||||||
|
await getWindowById(id).webContents.insertText(text);
|
||||||
|
electronSocket.emit("webContents-insertText-completed" + id);
|
||||||
|
});
|
||||||
|
socket.on("webContents-replace", (id, text) => {
|
||||||
|
getWindowById(id).webContents.replace(text);
|
||||||
|
});
|
||||||
|
socket.on("webContents-replaceMisspelling", (id, text) => {
|
||||||
|
getWindowById(id).webContents.replaceMisspelling(text);
|
||||||
|
});
|
||||||
|
socket.on("webContents-findInPage", (id, text, options) => {
|
||||||
|
const requestId = getWindowById(id).webContents.findInPage(text, options ?? undefined);
|
||||||
|
electronSocket.emit("webContents-findInPage-completed" + id, requestId);
|
||||||
|
});
|
||||||
|
socket.on("webContents-stopFindInPage", (id, action) => {
|
||||||
|
getWindowById(id).webContents.stopFindInPage(action);
|
||||||
|
});
|
||||||
function getWindowById(id) {
|
function getWindowById(id) {
|
||||||
if (id >= 1000) {
|
if (id >= 1000) {
|
||||||
return (0, browserView_1.browserViewMediateService)(id - 1000);
|
return (0, browserView_1.browserViewMediateService)(id - 1000);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -103,6 +103,51 @@ export = (socket: Socket) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("register-webContents-zoomChanged", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
|
||||||
|
browserWindow.webContents.removeAllListeners("zoom-changed");
|
||||||
|
browserWindow.webContents.on("zoom-changed", (_, zoomDirection) => {
|
||||||
|
electronSocket.emit("webContents-zoomChanged" + id, zoomDirection);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("register-webContents-foundInPage", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
|
||||||
|
browserWindow.webContents.removeAllListeners("found-in-page");
|
||||||
|
browserWindow.webContents.on("found-in-page", (_, result) => {
|
||||||
|
electronSocket.emit("webContents-foundInPage" + id, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("register-webContents-audioStateChanged", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
|
||||||
|
browserWindow.webContents.removeAllListeners("audio-state-changed");
|
||||||
|
browserWindow.webContents.on("audio-state-changed", (event) => {
|
||||||
|
electronSocket.emit("webContents-audioStateChanged" + id, event.audible);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("register-webContents-mediaStartedPlaying", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
|
||||||
|
browserWindow.webContents.removeAllListeners("media-started-playing");
|
||||||
|
browserWindow.webContents.on("media-started-playing", () => {
|
||||||
|
electronSocket.emit("webContents-mediaStartedPlaying" + id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("register-webContents-mediaPaused", (id) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
|
||||||
|
browserWindow.webContents.removeAllListeners("media-paused");
|
||||||
|
browserWindow.webContents.on("media-paused", () => {
|
||||||
|
electronSocket.emit("webContents-mediaPaused" + id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("webContents-openDevTools", (id, options) => {
|
socket.on("webContents-openDevTools", (id, options) => {
|
||||||
if (options) {
|
if (options) {
|
||||||
getWindowById(id).webContents.openDevTools(options);
|
getWindowById(id).webContents.openDevTools(options);
|
||||||
@@ -409,6 +454,52 @@ export = (socket: Socket) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-loadFile", (id, filePath, options) => {
|
||||||
|
const browserWindow = getWindowById(id);
|
||||||
|
browserWindow.webContents
|
||||||
|
.loadFile(filePath, options ?? undefined)
|
||||||
|
.then(() => {
|
||||||
|
electronSocket.emit("webContents-loadFile-complete" + id);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
electronSocket.emit("webContents-loadFile-error" + id, error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-isLoading", (id) => {
|
||||||
|
electronSocket.emit(
|
||||||
|
"webContents-isLoading-completed",
|
||||||
|
getWindowById(id).webContents.isLoading(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-isLoadingMainFrame", (id) => {
|
||||||
|
electronSocket.emit(
|
||||||
|
"webContents-isLoadingMainFrame-completed",
|
||||||
|
getWindowById(id).webContents.isLoadingMainFrame(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-isWaitingForResponse", (id) => {
|
||||||
|
electronSocket.emit(
|
||||||
|
"webContents-isWaitingForResponse-completed",
|
||||||
|
getWindowById(id).webContents.isWaitingForResponse(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-reload", (id) => {
|
||||||
|
getWindowById(id).webContents.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-reloadIgnoringCache", (id) => {
|
||||||
|
getWindowById(id).webContents.reloadIgnoringCache();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-stop", (id) => {
|
||||||
|
getWindowById(id).webContents.stop();
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("webContents-insertCSS", (id, isBrowserWindow, path) => {
|
socket.on("webContents-insertCSS", (id, isBrowserWindow, path) => {
|
||||||
if (isBrowserWindow) {
|
if (isBrowserWindow) {
|
||||||
const browserWindow = getWindowById(id);
|
const browserWindow = getWindowById(id);
|
||||||
@@ -431,6 +522,19 @@ export = (socket: Socket) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-insertCSSText", async (id, css, cssOrigin) => {
|
||||||
|
const key = await getWindowById(id).webContents.insertCSS(
|
||||||
|
css,
|
||||||
|
cssOrigin ? { cssOrigin } : undefined,
|
||||||
|
);
|
||||||
|
electronSocket.emit("webContents-insertCSSText-completed" + id, key);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-removeInsertedCSS", async (id, key) => {
|
||||||
|
await getWindowById(id).webContents.removeInsertedCSS(key);
|
||||||
|
electronSocket.emit("webContents-removeInsertedCSS-completed" + id);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("webContents-session-getAllExtensions", (id) => {
|
socket.on("webContents-session-getAllExtensions", (id) => {
|
||||||
const browserWindow = getWindowById(id);
|
const browserWindow = getWindowById(id);
|
||||||
const extensionsList = browserWindow.webContents.session.getAllExtensions();
|
const extensionsList = browserWindow.webContents.session.getAllExtensions();
|
||||||
@@ -557,6 +661,87 @@ export = (socket: Socket) => {
|
|||||||
getWindowById(id).webContents.setUserAgent(userAgent);
|
getWindowById(id).webContents.setUserAgent(userAgent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-undo", (id) => {
|
||||||
|
getWindowById(id).webContents.undo();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-redo", (id) => {
|
||||||
|
getWindowById(id).webContents.redo();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-cut", (id) => {
|
||||||
|
getWindowById(id).webContents.cut();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-copy", (id) => {
|
||||||
|
getWindowById(id).webContents.copy();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-copyImageAt", (id, x, y) => {
|
||||||
|
getWindowById(id).webContents.copyImageAt(x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-paste", (id) => {
|
||||||
|
getWindowById(id).webContents.paste();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-pasteAndMatchStyle", (id) => {
|
||||||
|
getWindowById(id).webContents.pasteAndMatchStyle();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-delete", (id) => {
|
||||||
|
getWindowById(id).webContents.delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-selectAll", (id) => {
|
||||||
|
getWindowById(id).webContents.selectAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-unselect", (id) => {
|
||||||
|
getWindowById(id).webContents.unselect();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-adjustSelection", (id, options) => {
|
||||||
|
getWindowById(id).webContents.adjustSelection(options ?? {});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-centerSelection", (id) => {
|
||||||
|
getWindowById(id).webContents.centerSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-scrollToTop", (id) => {
|
||||||
|
getWindowById(id).webContents.scrollToTop();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-scrollToBottom", (id) => {
|
||||||
|
getWindowById(id).webContents.scrollToBottom();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-insertText", async (id, text) => {
|
||||||
|
await getWindowById(id).webContents.insertText(text);
|
||||||
|
electronSocket.emit("webContents-insertText-completed" + id);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-replace", (id, text) => {
|
||||||
|
getWindowById(id).webContents.replace(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-replaceMisspelling", (id, text) => {
|
||||||
|
getWindowById(id).webContents.replaceMisspelling(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-findInPage", (id, text, options) => {
|
||||||
|
const requestId = getWindowById(id).webContents.findInPage(
|
||||||
|
text,
|
||||||
|
options ?? undefined,
|
||||||
|
);
|
||||||
|
electronSocket.emit("webContents-findInPage-completed" + id, requestId);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("webContents-stopFindInPage", (id, action) => {
|
||||||
|
getWindowById(id).webContents.stopFindInPage(action);
|
||||||
|
});
|
||||||
|
|
||||||
function getWindowById(
|
function getWindowById(
|
||||||
id: number,
|
id: number,
|
||||||
): Electron.BrowserWindow | Electron.BrowserView {
|
): Electron.BrowserWindow | Electron.BrowserView {
|
||||||
|
|||||||
@@ -29,32 +29,11 @@ let electronforcedport;
|
|||||||
let electronUrl;
|
let electronUrl;
|
||||||
let authToken = randomUUID().split('-').join('');
|
let authToken = randomUUID().split('-').join('');
|
||||||
|
|
||||||
if (app.commandLine.hasSwitch('manifest')) {
|
|
||||||
manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app.commandLine.hasSwitch('unpackedelectron')) {
|
|
||||||
unpackedelectron = true;
|
|
||||||
}
|
|
||||||
else if (app.commandLine.hasSwitch('unpackeddotnet')) {
|
|
||||||
unpackeddotnet = true;
|
|
||||||
}
|
|
||||||
else if (app.commandLine.hasSwitch('dotnetpacked')) {
|
|
||||||
dotnetpacked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app.commandLine.hasSwitch('electronforcedport')) {
|
|
||||||
electronforcedport = +app.commandLine.getSwitchValue('electronforcedport');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store in global for access by browser windows
|
// Store in global for access by browser windows
|
||||||
global.authToken = authToken;
|
global.authToken = authToken;
|
||||||
|
|
||||||
if (app.commandLine.hasSwitch('electronurl')) {
|
|
||||||
electronUrl = app.commandLine.getSwitchValue('electronurl');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom startup hook: look for custom_main.js and invoke its onStartup(host) if present.
|
// Custom startup hook: look for custom_main.js and invoke its onStartup(host) if present.
|
||||||
|
// Runs before any command line switch is evaluated so the hook can still modify them.
|
||||||
// If the hook returns false, abort Electron startup.
|
// If the hook returns false, abort Electron startup.
|
||||||
try {
|
try {
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@@ -77,6 +56,28 @@ try {
|
|||||||
console.error('Error while executing custom_main.js:', err);
|
console.error('Error while executing custom_main.js:', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (app.commandLine.hasSwitch('manifest')) {
|
||||||
|
manifestJsonFileName = app.commandLine.getSwitchValue('manifest');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.commandLine.hasSwitch('unpackedelectron')) {
|
||||||
|
unpackedelectron = true;
|
||||||
|
}
|
||||||
|
else if (app.commandLine.hasSwitch('unpackeddotnet')) {
|
||||||
|
unpackeddotnet = true;
|
||||||
|
}
|
||||||
|
else if (app.commandLine.hasSwitch('dotnetpacked')) {
|
||||||
|
dotnetpacked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.commandLine.hasSwitch('electronforcedport')) {
|
||||||
|
electronforcedport = +app.commandLine.getSwitchValue('electronforcedport');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.commandLine.hasSwitch('electronurl')) {
|
||||||
|
electronUrl = app.commandLine.getSwitchValue('electronurl');
|
||||||
|
}
|
||||||
|
|
||||||
const currentPath = __dirname;
|
const currentPath = __dirname;
|
||||||
let currentBinPath = path.join(currentPath.replace('app.asar', ''), 'bin');
|
let currentBinPath = path.join(currentPath.replace('app.asar', ''), 'bin');
|
||||||
let manifestJsonFilePath = path.join(currentPath, manifestJsonFileName);
|
let manifestJsonFilePath = path.join(currentPath, manifestJsonFileName);
|
||||||
|
|||||||
@@ -145,6 +145,111 @@ public class MigrationChecksTargetsTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MigrationChecksTargets_BuildWithRootPackageLockJson_ShouldNotEmitELECTRON001Warning()
|
||||||
|
{
|
||||||
|
// A root package.json is validated by ELECTRON008/ELECTRON009 and therefore allowed.
|
||||||
|
// Its accompanying package-lock.json must not be reported by ELECTRON001.
|
||||||
|
|
||||||
|
var tempDir = CreateTempProjectDirectory();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await File.WriteAllTextAsync(
|
||||||
|
Path.Combine(tempDir, "package.json"),
|
||||||
|
"""{ "devDependencies": { "vite": "^5.0.0" } }""");
|
||||||
|
await File.WriteAllTextAsync(
|
||||||
|
Path.Combine(tempDir, "package-lock.json"),
|
||||||
|
"""{ "lockfileVersion": 3 }""");
|
||||||
|
|
||||||
|
await WriteMinimalCsprojAsync(tempDir);
|
||||||
|
|
||||||
|
var (exitCode, output) = await RunDotnetBuildAsync(tempDir);
|
||||||
|
|
||||||
|
exitCode.Should().Be(0, $"Full build output:\n{output}");
|
||||||
|
|
||||||
|
output.Should().NotContain(
|
||||||
|
"ELECTRON001",
|
||||||
|
$"a root package-lock.json belongs to the allowed root package.json. " +
|
||||||
|
$"Full build output:\n{output}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(tempDir, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MigrationChecksTargets_BuildWithIncompleteHostHookFolder_ShouldEmitELECTRON010AndELECTRON011Warnings()
|
||||||
|
{
|
||||||
|
// Host hook TypeScript sources are silently ignored when package.json/tsconfig.json
|
||||||
|
// are missing, so the migration checks must point that out.
|
||||||
|
|
||||||
|
var tempDir = CreateTempProjectDirectory();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var hookDir = Path.Combine(tempDir, "ElectronHostHook");
|
||||||
|
Directory.CreateDirectory(hookDir);
|
||||||
|
await File.WriteAllTextAsync(Path.Combine(hookDir, "index.ts"), "export class HookService {}");
|
||||||
|
|
||||||
|
await WriteMinimalCsprojAsync(tempDir);
|
||||||
|
|
||||||
|
var (exitCode, output) = await RunDotnetBuildAsync(tempDir);
|
||||||
|
|
||||||
|
exitCode.Should().Be(0, $"Full build output:\n{output}");
|
||||||
|
|
||||||
|
output.Should().Contain(
|
||||||
|
"ELECTRON010",
|
||||||
|
$"the ElectronHostHook folder has TypeScript files but no package.json. " +
|
||||||
|
$"Full build output:\n{output}");
|
||||||
|
output.Should().Contain(
|
||||||
|
"ELECTRON011",
|
||||||
|
$"the ElectronHostHook folder has TypeScript files but no tsconfig.json. " +
|
||||||
|
$"Full build output:\n{output}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(tempDir, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MigrationChecksTargets_BuildWithConsoleProfileContainingProjectGuid_ShouldEmitELECTRON007Warning()
|
||||||
|
{
|
||||||
|
// ProjectGuid is an ASP.NET publish profile property and must be reported for
|
||||||
|
// non-web projects, just like WebPublishMethod.
|
||||||
|
|
||||||
|
var tempDir = CreateTempProjectDirectory();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var profilesDir = Path.Combine(tempDir, "Properties", "PublishProfiles");
|
||||||
|
Directory.CreateDirectory(profilesDir);
|
||||||
|
await File.WriteAllTextAsync(
|
||||||
|
Path.Combine(profilesDir, "FolderProfile.pubxml"),
|
||||||
|
"""
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ProjectGuid>00000000-0000-0000-0000-000000000000</ProjectGuid>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
|
""");
|
||||||
|
|
||||||
|
await WriteMinimalCsprojAsync(tempDir);
|
||||||
|
|
||||||
|
var (exitCode, output) = await RunDotnetBuildAsync(tempDir);
|
||||||
|
|
||||||
|
exitCode.Should().Be(0, $"Full build output:\n{output}");
|
||||||
|
|
||||||
|
output.Should().Contain(
|
||||||
|
"ELECTRON007",
|
||||||
|
$"a console project must not use an ASP.NET publish profile. " +
|
||||||
|
$"Full build output:\n{output}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(tempDir, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|||||||
@@ -108,13 +108,19 @@ namespace ElectronNET.IntegrationTests.Tests
|
|||||||
await Task.Delay(500.ms());
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
var ok = await window.WebContents.GetZoomLevelAsync();
|
var ok = await window.WebContents.GetZoomLevelAsync();
|
||||||
ok.Should().Be(0);
|
ok.Should().Be(0.0);
|
||||||
|
|
||||||
window.WebContents.SetZoomLevel(2);
|
window.WebContents.SetZoomLevel(2);
|
||||||
await Task.Delay(500.ms());
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
ok = await window.WebContents.GetZoomLevelAsync();
|
ok = await window.WebContents.GetZoomLevelAsync();
|
||||||
ok.Should().Be(2);
|
ok.Should().Be(2.0);
|
||||||
|
|
||||||
|
window.WebContents.SetZoomLevel(0.5);
|
||||||
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
|
ok = await window.WebContents.GetZoomLevelAsync();
|
||||||
|
ok.Should().BeApproximately(0.5, 0.001);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -193,5 +199,94 @@ namespace ElectronNET.IntegrationTests.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IntegrationFact]
|
||||||
|
public async Task InsertAndRemoveCSS_check()
|
||||||
|
{
|
||||||
|
var wc = this.MainWindow.WebContents;
|
||||||
|
await wc.LoadURLAsync("data:text/html,<html><body><p>CSS Test</p></body></html>");
|
||||||
|
|
||||||
|
var key = await wc.InsertCSSAsync("body { background-color: rgb(255, 0, 0); }");
|
||||||
|
key.Should().NotBeNullOrEmpty();
|
||||||
|
|
||||||
|
var color = await wc.ExecuteJavaScriptAsync<string>("getComputedStyle(document.body).backgroundColor");
|
||||||
|
color.Should().Be("rgb(255, 0, 0)");
|
||||||
|
|
||||||
|
await wc.RemoveInsertedCSSAsync(key);
|
||||||
|
|
||||||
|
color = await wc.ExecuteJavaScriptAsync<string>("getComputedStyle(document.body).backgroundColor");
|
||||||
|
color.Should().NotBe("rgb(255, 0, 0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
[IntegrationFact]
|
||||||
|
public async Task EditCommands_check()
|
||||||
|
{
|
||||||
|
var wc = this.MainWindow.WebContents;
|
||||||
|
await wc.LoadURLAsync("data:text/html,<html><body><input id='in' autofocus /></body></html>");
|
||||||
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
|
await wc.InsertTextAsync("Electron.NET");
|
||||||
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
|
var value = await wc.ExecuteJavaScriptAsync<string>("document.getElementById('in').value");
|
||||||
|
value.Should().Be("Electron.NET");
|
||||||
|
|
||||||
|
wc.SelectAll();
|
||||||
|
wc.Delete();
|
||||||
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
|
value = await wc.ExecuteJavaScriptAsync<string>("document.getElementById('in').value");
|
||||||
|
value.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[IntegrationFact]
|
||||||
|
public async Task FindInPage_check()
|
||||||
|
{
|
||||||
|
var wc = this.MainWindow.WebContents;
|
||||||
|
await wc.LoadURLAsync("data:text/html,<html><body><p>needle in a haystack</p></body></html>");
|
||||||
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<FoundInPageResult>();
|
||||||
|
|
||||||
|
void OnFound(FoundInPageResult result) => tcs.TrySetResult(result);
|
||||||
|
|
||||||
|
wc.OnFoundInPage += OnFound;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var requestId = await wc.FindInPageAsync("needle");
|
||||||
|
requestId.Should().BeGreaterThan(0);
|
||||||
|
|
||||||
|
var completed = await Task.WhenAny(tcs.Task, Task.Delay(5.seconds()));
|
||||||
|
completed.Should().Be(tcs.Task);
|
||||||
|
|
||||||
|
var result = await tcs.Task;
|
||||||
|
result.RequestId.Should().Be(requestId);
|
||||||
|
result.Matches.Should().BeGreaterThan(0);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
wc.OnFoundInPage -= OnFound;
|
||||||
|
wc.StopFindInPage(StopFindInPageAction.ClearSelection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[IntegrationFact]
|
||||||
|
public async Task Reload_and_loading_state_check()
|
||||||
|
{
|
||||||
|
var wc = this.MainWindow.WebContents;
|
||||||
|
await wc.LoadURLAsync("data:text/html,<html><body><h1>Reload Test</h1></body></html>");
|
||||||
|
await Task.Delay(500.ms());
|
||||||
|
|
||||||
|
(await wc.IsLoadingAsync()).Should().BeFalse();
|
||||||
|
(await wc.IsLoadingMainFrameAsync()).Should().BeFalse();
|
||||||
|
(await wc.IsWaitingForResponseAsync()).Should().BeFalse();
|
||||||
|
|
||||||
|
wc.Reload();
|
||||||
|
await Task.Delay(1.seconds());
|
||||||
|
|
||||||
|
var title = await wc.ExecuteJavaScriptAsync<string>("document.querySelector('h1').textContent");
|
||||||
|
title.Should().Be("Reload Test");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "!Config", "!Config", "{02EA
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.AspNet", "ElectronNET.AspNet\ElectronNET.AspNet.csproj", "{DD10D21A-D131-1D9C-33F9-406046E0C5B0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.AspNet", "ElectronNET.AspNet\ElectronNET.AspNet.csproj", "{DD10D21A-D131-1D9C-33F9-406046E0C5B0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.Templates", "ElectronNET.Templates\ElectronNET.Templates.csproj", "{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}"
|
||||||
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{1BB6F634-2831-4496-83A6-BC6761DCEC8D}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{1BB6F634-2831-4496-83A6-BC6761DCEC8D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
@@ -45,6 +47,10 @@ Global
|
|||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -54,6 +60,7 @@ Global
|
|||||||
{829FC339-4785-4229-ABA5-53ADB544DA00} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
{829FC339-4785-4229-ABA5-53ADB544DA00} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
{8860606D-6847-F22A-5AED-DF4E0984DD24} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
{8860606D-6847-F22A-5AED-DF4E0984DD24} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {81A62E71-9E04-4EFE-AD5C-23165375F8EF}
|
SolutionGuid = {81A62E71-9E04-4EFE-AD5C-23165375F8EF}
|
||||||
|
|||||||
65
src/ElectronNET.Templates/ElectronNET.Templates.csproj
Normal file
65
src/ElectronNET.Templates/ElectronNET.Templates.csproj
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<Import Project="..\common.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||||
|
<PackageId>$(PackageNamePrefix).Templates</PackageId>
|
||||||
|
<Title>$(PackageId)</Title>
|
||||||
|
<Description>$(DescriptionFirstPart) This package contains the 'dotnet new' project templates.</Description>
|
||||||
|
<PackageTags>electron aspnetcore blazor dotnet-new templates</PackageTags>
|
||||||
|
<PackageType>Template</PackageType>
|
||||||
|
<IncludeContentInPack>true</IncludeContentInPack>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
<ContentTargetFolders>content</ContentTargetFolders>
|
||||||
|
<!-- Template content contains dot-prefixed folders (.template.config) that must be packed -->
|
||||||
|
<NoDefaultExcludes>true</NoDefaultExcludes>
|
||||||
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
|
<IncludeSymbols>false</IncludeSymbols>
|
||||||
|
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||||
|
<NoWarn>$(NoWarn);NU5128</NoWarn>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<!-- The version pinned in the template project files on disk. It is replaced by the
|
||||||
|
actual package version while packing, so a template always scaffolds an app that
|
||||||
|
references the Electron.NET packages it was shipped with. -->
|
||||||
|
<TemplatePlaceholderVersion>0.6.0</TemplatePlaceholderVersion>
|
||||||
|
<TemplateProjectFile>templates\blazor\ElectronBlazorApp.csproj</TemplateProjectFile>
|
||||||
|
<TemplateProjectPackagePath>content\templates\blazor\ElectronBlazorApp.csproj</TemplateProjectPackagePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**;$(TemplateProjectFile)" />
|
||||||
|
<Compile Remove="**\*" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\ElectronNET\PackageIcon.png" Pack="true" PackagePath="\" />
|
||||||
|
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RewriteTemplateProjectVersion" BeforeTargets="_GetPackageFiles">
|
||||||
|
<PropertyGroup>
|
||||||
|
<_TemplateProjectSource>$(MSBuildProjectDirectory)\$(TemplateProjectFile)</_TemplateProjectSource>
|
||||||
|
<_TemplateProjectTarget>$(IntermediateOutputPath)templates\blazor\ElectronBlazorApp.csproj</_TemplateProjectTarget>
|
||||||
|
<_TemplateProjectContent>$([System.IO.File]::ReadAllText('$(_TemplateProjectSource)'))</_TemplateProjectContent>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Error Condition="!$(_TemplateProjectContent.Contains('Version="$(TemplatePlaceholderVersion)"'))"
|
||||||
|
Text="'$(TemplateProjectFile)' contains no PackageReference pinned to $(TemplatePlaceholderVersion), so the package version cannot be injected. Align the template's PackageReference versions with the TemplatePlaceholderVersion property." />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<_TemplateProjectContent>$(_TemplateProjectContent.Replace('Version="$(TemplatePlaceholderVersion)"', 'Version="$(Version)"'))</_TemplateProjectContent>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<WriteLinesToFile File="$(_TemplateProjectTarget)"
|
||||||
|
Lines="$([MSBuild]::Escape($(_TemplateProjectContent)))"
|
||||||
|
Overwrite="true"
|
||||||
|
WriteOnlyWhenDifferent="true" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="$(_TemplateProjectTarget)" Pack="true" PackagePath="$(TemplateProjectPackagePath)" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/dotnetcli.host",
|
||||||
|
"symbolInfo": {
|
||||||
|
"Framework": {
|
||||||
|
"longName": "framework",
|
||||||
|
"shortName": "f"
|
||||||
|
},
|
||||||
|
"ElectronVersion": {
|
||||||
|
"longName": "electron-version",
|
||||||
|
"shortName": "e"
|
||||||
|
},
|
||||||
|
"Port": {
|
||||||
|
"longName": "port",
|
||||||
|
"shortName": "p"
|
||||||
|
},
|
||||||
|
"skipRestore": {
|
||||||
|
"longName": "no-restore",
|
||||||
|
"shortName": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/template",
|
||||||
|
"author": "Electron.NET",
|
||||||
|
"classifications": [ "Blazor", "Desktop", "Electron", "Web" ],
|
||||||
|
"identity": "ElectronNET.Core.Templates.Blazor.CSharp",
|
||||||
|
"groupIdentity": "ElectronNET.Core.Templates.Blazor",
|
||||||
|
"name": "Electron.NET Blazor App",
|
||||||
|
"description": "A Blazor Server app hosted in an Electron shell, wired up with Electron.NET.",
|
||||||
|
"shortName": "electron-blazor",
|
||||||
|
"tags": {
|
||||||
|
"language": "C#",
|
||||||
|
"type": "project"
|
||||||
|
},
|
||||||
|
"sourceName": "ElectronBlazorApp",
|
||||||
|
"preferNameDirectory": true,
|
||||||
|
"defaultName": "ElectronBlazorApp",
|
||||||
|
"symbols": {
|
||||||
|
"Framework": {
|
||||||
|
"type": "parameter",
|
||||||
|
"description": "The target framework for the project.",
|
||||||
|
"datatype": "choice",
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"choice": "net10.0",
|
||||||
|
"description": "Target net10.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"choice": "net8.0",
|
||||||
|
"description": "Target net8.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"replaces": "net8.0",
|
||||||
|
"defaultValue": "net10.0"
|
||||||
|
},
|
||||||
|
"ElectronVersion": {
|
||||||
|
"type": "parameter",
|
||||||
|
"description": "The version of Electron to build the app against.",
|
||||||
|
"datatype": "text",
|
||||||
|
"replaces": "ELECTRON_VERSION",
|
||||||
|
"defaultValue": "38.2.2"
|
||||||
|
},
|
||||||
|
"Port": {
|
||||||
|
"type": "parameter",
|
||||||
|
"description": "Port number to use for the ASP.NET server during development.",
|
||||||
|
"datatype": "integer",
|
||||||
|
"replaces": "8001",
|
||||||
|
"defaultValue": "8001"
|
||||||
|
},
|
||||||
|
"skipRestore": {
|
||||||
|
"type": "parameter",
|
||||||
|
"datatype": "bool",
|
||||||
|
"description": "If specified, skips the automatic restore of the project on create.",
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"primaryOutputs": [
|
||||||
|
{
|
||||||
|
"path": "ElectronBlazorApp.csproj"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"postActions": [
|
||||||
|
{
|
||||||
|
"id": "restore",
|
||||||
|
"condition": "(!skipRestore)",
|
||||||
|
"description": "Restore NuGet packages required by this project.",
|
||||||
|
"manualInstructions": [
|
||||||
|
{
|
||||||
|
"text": "Run 'dotnet restore'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
|
||||||
|
"continueOnError": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<base href="/" />
|
||||||
|
<ResourcePreloader />
|
||||||
|
<link rel="stylesheet" href="app.css" />
|
||||||
|
<ImportMap />
|
||||||
|
<title>ElectronBlazorApp</title>
|
||||||
|
<HeadOutlet />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<Routes />
|
||||||
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
<NavMenu />
|
||||||
|
|
||||||
|
<main>
|
||||||
|
@Body
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<nav class="nav-menu">
|
||||||
|
<NavLink href="" Match="NavLinkMatch.All">Home</NavLink>
|
||||||
|
<NavLink href="counter">Counter</NavLink>
|
||||||
|
</nav>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
@page "/counter"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Counter</PageTitle>
|
||||||
|
|
||||||
|
<h1>Counter</h1>
|
||||||
|
|
||||||
|
<p role="status">Current count: @currentCount</p>
|
||||||
|
|
||||||
|
<button class="btn" @onclick="IncrementCount">Click me</button>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private int currentCount = 0;
|
||||||
|
|
||||||
|
private void IncrementCount()
|
||||||
|
{
|
||||||
|
currentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
@page "/"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
@using ElectronNET.API
|
||||||
|
@using ElectronNET.API.Entities
|
||||||
|
|
||||||
|
<PageTitle>Home</PageTitle>
|
||||||
|
|
||||||
|
<h1>Hello from Electron.NET!</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This Blazor app is served by ASP.NET Core and displayed in an Electron window.
|
||||||
|
Everything in the <code>ElectronNET.API</code> namespace is available from your components.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button class="btn" @onclick="ShowNotification">Show a native notification</button>
|
||||||
|
|
||||||
|
<p role="status">@status</p>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string? status;
|
||||||
|
|
||||||
|
private void ShowNotification()
|
||||||
|
{
|
||||||
|
if (HybridSupport.IsElectronActive)
|
||||||
|
{
|
||||||
|
Electron.Notification.Show(new NotificationOptions("ElectronBlazorApp", "Hello from Blazor!"));
|
||||||
|
status = "Notification sent.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = "Not running inside Electron.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<Router AppAssembly="typeof(Program).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||||
|
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
</Router>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
@using System.Net.Http
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using ElectronBlazorApp
|
||||||
|
@using ElectronBlazorApp.Components
|
||||||
|
@using ElectronBlazorApp.Components.Layout
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<!-- Electron hosts the app out of process -->
|
||||||
|
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
|
||||||
|
<PublishTrimmed>false</PublishTrimmed>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Label="ElectronNetCommon">
|
||||||
|
<ElectronPackageId>electron-blazor-app</ElectronPackageId>
|
||||||
|
<Title>ElectronBlazorApp</Title>
|
||||||
|
<Description>A Blazor app running in Electron.</Description>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<Company>My Company</Company>
|
||||||
|
<Copyright>Copyright © $(Company)</Copyright>
|
||||||
|
<ElectronVersion>ELECTRON_VERSION</ElectronVersion>
|
||||||
|
<ElectronSingleInstance>true</ElectronSingleInstance>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ElectronNET.Core" Version="0.6.0" />
|
||||||
|
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
48
src/ElectronNET.Templates/templates/blazor/Program.cs
Normal file
48
src/ElectronNET.Templates/templates/blazor/Program.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using ElectronNET.API;
|
||||||
|
using ElectronNET.API.Entities;
|
||||||
|
using ElectronNET.AspNet.Middleware;
|
||||||
|
using ElectronNET.AspNet.Services;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddRazorComponents()
|
||||||
|
.AddInteractiveServerComponents();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>();
|
||||||
|
|
||||||
|
builder.Services.AddElectron();
|
||||||
|
|
||||||
|
// Starts the Electron shell and invokes ElectronAppReady once it is up.
|
||||||
|
builder.UseElectron(args, ElectronAppReady);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseMiddleware<ElectronAuthenticationMiddleware>();
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseAntiforgery();
|
||||||
|
|
||||||
|
// Fully qualified because ElectronNET.API also defines an 'App' type.
|
||||||
|
app.MapRazorComponents<ElectronBlazorApp.Components.App>()
|
||||||
|
.AddInteractiveServerRenderMode();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
static async Task ElectronAppReady()
|
||||||
|
{
|
||||||
|
var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
|
||||||
|
{
|
||||||
|
IsRunningBlazor = true,
|
||||||
|
Width = 1152,
|
||||||
|
Height = 940,
|
||||||
|
Show = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.OnReadyToShow += () => window.Show();
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<DeleteExistingFiles>true</DeleteExistingFiles>
|
||||||
|
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||||
|
<LaunchSiteAfterPublish>false</LaunchSiteAfterPublish>
|
||||||
|
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||||
|
<PublishProvider>FileSystem</PublishProvider>
|
||||||
|
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
|
||||||
|
<WebPublishMethod>FileSystem</WebPublishMethod>
|
||||||
|
<_TargetId>Folder</_TargetId>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<DeleteExistingFiles>true</DeleteExistingFiles>
|
||||||
|
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||||
|
<LaunchSiteAfterPublish>false</LaunchSiteAfterPublish>
|
||||||
|
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||||
|
<PublishProvider>FileSystem</PublishProvider>
|
||||||
|
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
|
||||||
|
<WebPublishMethod>FileSystem</WebPublishMethod>
|
||||||
|
<_TargetId>Folder</_TargetId>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<DeleteExistingFiles>true</DeleteExistingFiles>
|
||||||
|
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||||
|
<LaunchSiteAfterPublish>false</LaunchSiteAfterPublish>
|
||||||
|
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||||
|
<PublishProvider>FileSystem</PublishProvider>
|
||||||
|
<PublishUrl>publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishUrl>
|
||||||
|
<WebPublishMethod>FileSystem</WebPublishMethod>
|
||||||
|
<_TargetId>Folder</_TargetId>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compression": "maximum",
|
||||||
|
"linux": {
|
||||||
|
"target": [ "AppImage" ],
|
||||||
|
"executableArgs": [ "--no-sandbox" ]
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "nsis",
|
||||||
|
"arch": "x64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"target": [ "dmg" ]
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": true,
|
||||||
|
"perMachine": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"ElectronBlazorApp": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "http://localhost:8001/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
49
src/ElectronNET.Templates/templates/blazor/wwwroot/app.css
Normal file
49
src/ElectronNET.Templates/templates/blazor/wwwroot/app.css
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
background-color: #1b1b1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu a {
|
||||||
|
color: #d7d7d7;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu a.active {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #1861ac;
|
||||||
|
}
|
||||||
@@ -29,9 +29,6 @@ namespace ElectronNET.WebApp.Controllers
|
|||||||
|
|
||||||
Electron.IpcMain.On("auto-update", async (args) =>
|
Electron.IpcMain.On("auto-update", async (args) =>
|
||||||
{
|
{
|
||||||
// Electron.NET CLI Command for deploy:
|
|
||||||
// electronize build /target win /electron-params --publish=always
|
|
||||||
|
|
||||||
var currentVersion = await Electron.App.GetVersionAsync();
|
var currentVersion = await Electron.App.GetVersionAsync();
|
||||||
var updateCheckResult = await Electron.AutoUpdater.CheckForUpdatesAndNotifyAsync();
|
var updateCheckResult = await Electron.AutoUpdater.CheckForUpdatesAndNotifyAsync();
|
||||||
var availableVersion = updateCheckResult.UpdateInfo.Version;
|
var availableVersion = updateCheckResult.UpdateInfo.Version;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<h3>The <code>HostHook</code> API allows you to execute your own JavaScript/TypeScript code on the host process.</h3>
|
<h3>The <code>HostHook</code> API allows you to execute your own JavaScript/TypeScript code on the host process.</h3>
|
||||||
|
|
||||||
<p>Create first an ElectronHostHook directory via the Electron.NET CLI, with the following command: <code>electronize add hosthook</code>.</p>
|
<p>Create first an ElectronHostHook directory with a package.json and a JavaScript file.</p>
|
||||||
<p>In this directory you can install any NPM packages and embed your own JavaScript/TypeScript code. It is also possible to respond to events from the host process.</p>
|
<p>In this directory you can install any NPM packages and embed your own JavaScript/TypeScript code. It is also possible to respond to events from the host process.</p>
|
||||||
<p>You find the sample source code in <code>Controllers\HostHookController.cs</code> and in the <code>ElectronHostHook</code> folder.</p>
|
<p>You find the sample source code in <code>Controllers\HostHookController.cs</code> and in the <code>ElectronHostHook</code> folder.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "!Config", "!Config", "{02EA
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.AspNet", "ElectronNET.AspNet\ElectronNET.AspNet.csproj", "{DD10D21A-D131-1D9C-33F9-406046E0C5B0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.AspNet", "ElectronNET.AspNet\ElectronNET.AspNet.csproj", "{DD10D21A-D131-1D9C-33F9-406046E0C5B0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.Templates", "ElectronNET.Templates\ElectronNET.Templates.csproj", "{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}"
|
||||||
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.ConsoleApp", "ElectronNET.ConsoleApp\ElectronNET.ConsoleApp.csproj", "{EE38A326-5DE8-AF09-9EB9-DF0878938783}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.ConsoleApp", "ElectronNET.ConsoleApp\ElectronNET.ConsoleApp.csproj", "{EE38A326-5DE8-AF09-9EB9-DF0878938783}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
{1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6} = {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6}
|
{1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6} = {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6}
|
||||||
@@ -99,6 +101,10 @@ Global
|
|||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{EE38A326-5DE8-AF09-9EB9-DF0878938783}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{EE38A326-5DE8-AF09-9EB9-DF0878938783}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{EE38A326-5DE8-AF09-9EB9-DF0878938783}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EE38A326-5DE8-AF09-9EB9-DF0878938783}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EE38A326-5DE8-AF09-9EB9-DF0878938783}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EE38A326-5DE8-AF09-9EB9-DF0878938783}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -131,6 +137,7 @@ Global
|
|||||||
{829FC339-4785-4229-ABA5-53ADB544DA00} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
{829FC339-4785-4229-ABA5-53ADB544DA00} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
{8860606D-6847-F22A-5AED-DF4E0984DD24} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
{8860606D-6847-F22A-5AED-DF4E0984DD24} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
{DD10D21A-D131-1D9C-33F9-406046E0C5B0} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
{DD10D21A-D131-1D9C-33F9-406046E0C5B0} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
|
{3F2A1D64-9C71-4E1B-9A2D-5C7B0E4F8A11} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D}
|
||||||
{EE38A326-5DE8-AF09-9EB9-DF0878938783} = {EDCBFC49-2AEE-4BAF-9368-4409298C52FC}
|
{EE38A326-5DE8-AF09-9EB9-DF0878938783} = {EDCBFC49-2AEE-4BAF-9368-4409298C52FC}
|
||||||
{015CB06B-6CAE-209F-E050-21C3ACA5FE9F} = {985D39A7-5216-4945-8167-2FD0CB387BD8}
|
{015CB06B-6CAE-209F-E050-21C3ACA5FE9F} = {985D39A7-5216-4945-8167-2FD0CB387BD8}
|
||||||
{06CAADC7-DE5B-47B4-AB2A-E9501459A2D1} = {D36CDFFD-3438-42E4-A7FF-88BA19AC4964}
|
{06CAADC7-DE5B-47B4-AB2A-E9501459A2D1} = {D36CDFFD-3438-42E4-A7FF-88BA19AC4964}
|
||||||
|
|||||||
@@ -9,10 +9,22 @@
|
|||||||
ElectronCheckNoManifestJson;
|
ElectronCheckNoManifestJson;
|
||||||
ElectronCheckElectronBuilderJson;
|
ElectronCheckElectronBuilderJson;
|
||||||
ElectronCheckNoParentPaths;
|
ElectronCheckNoParentPaths;
|
||||||
ElectronCheckPubxmlFiles
|
ElectronCheckPubxmlFiles;
|
||||||
|
ElectronCheckHostHookFolder
|
||||||
</ElectronMigrationChecksDependsOn>
|
</ElectronMigrationChecksDependsOn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Directories that are never scanned by the migration checks. Besides the well-known
|
||||||
|
bin/obj/publish/node_modules folders this also honors custom output paths (e.g. the
|
||||||
|
.NET 'artifacts' output layout) so that generated files never trigger false alarms.
|
||||||
|
-->
|
||||||
|
<PropertyGroup>
|
||||||
|
<_ElectronCheckExcludes>$(MSBuildProjectDirectory)\bin\**\*;$(MSBuildProjectDirectory)\obj\**\*;$(MSBuildProjectDirectory)\publish\**\*;$(MSBuildProjectDirectory)\node_modules\**\*;$(MSBuildProjectDirectory)\.electron\**\*</_ElectronCheckExcludes>
|
||||||
|
<_ElectronCheckExcludes Condition="'$(BaseOutputPath)' != ''">$(_ElectronCheckExcludes);$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(BaseOutputPath)'))**\*</_ElectronCheckExcludes>
|
||||||
|
<_ElectronCheckExcludes Condition="'$(BaseIntermediateOutputPath)' != ''">$(_ElectronCheckExcludes);$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)'))**\*</_ElectronCheckExcludes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Main migration checks target that runs before build -->
|
<!-- Main migration checks target that runs before build -->
|
||||||
<Target Name="ElectronMigrationChecks"
|
<Target Name="ElectronMigrationChecks"
|
||||||
BeforeTargets="BeforeBuild"
|
BeforeTargets="BeforeBuild"
|
||||||
@@ -32,16 +44,13 @@
|
|||||||
<_InvalidPackageJson Include="$(MSBuildProjectDirectory)\**\package.json"
|
<_InvalidPackageJson Include="$(MSBuildProjectDirectory)\**\package.json"
|
||||||
Exclude="$(MSBuildProjectDirectory)\package.json;
|
Exclude="$(MSBuildProjectDirectory)\package.json;
|
||||||
$(MSBuildProjectDirectory)\ElectronHostHook\**\package.json;
|
$(MSBuildProjectDirectory)\ElectronHostHook\**\package.json;
|
||||||
$(MSBuildProjectDirectory)\bin\**\package.json;
|
$(_ElectronCheckExcludes)" />
|
||||||
$(MSBuildProjectDirectory)\obj\**\package.json;
|
<!-- The root package-lock.json belongs to the root package.json, which is validated
|
||||||
$(MSBuildProjectDirectory)\publish\**\package.json;
|
by ELECTRON008/ELECTRON009 instead of ELECTRON001. -->
|
||||||
$(MSBuildProjectDirectory)\node_modules\**\package.json" />
|
|
||||||
<_InvalidPackageLockJson Include="$(MSBuildProjectDirectory)\**\package-lock.json"
|
<_InvalidPackageLockJson Include="$(MSBuildProjectDirectory)\**\package-lock.json"
|
||||||
Exclude="$(MSBuildProjectDirectory)\ElectronHostHook\**\package-lock.json;
|
Exclude="$(MSBuildProjectDirectory)\package-lock.json;
|
||||||
$(MSBuildProjectDirectory)\bin\**\package-lock.json;
|
$(MSBuildProjectDirectory)\ElectronHostHook\**\package-lock.json;
|
||||||
$(MSBuildProjectDirectory)\obj\**\package-lock.json;
|
$(_ElectronCheckExcludes)" />
|
||||||
$(MSBuildProjectDirectory)\publish\**\package-lock.json;
|
|
||||||
$(MSBuildProjectDirectory)\node_modules\**\package-lock.json" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -141,10 +150,7 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_InvalidManifestJson Include="$(MSBuildProjectDirectory)\**\electron.manifest.json;$(MSBuildProjectDirectory)\**\electron-manifest.json"
|
<_InvalidManifestJson Include="$(MSBuildProjectDirectory)\**\electron.manifest.json;$(MSBuildProjectDirectory)\**\electron-manifest.json"
|
||||||
Exclude="$(MSBuildProjectDirectory)\bin\**\*;
|
Exclude="$(_ElectronCheckExcludes)" />
|
||||||
$(MSBuildProjectDirectory)\obj\**\*;
|
|
||||||
$(MSBuildProjectDirectory)\publish\**\*;
|
|
||||||
$(MSBuildProjectDirectory)\node_modules\**\*" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -187,10 +193,7 @@ MIGRATION REQUIRED:
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_ElectronBuilderJsonWrongLocation Include="$(MSBuildProjectDirectory)\**\electron-builder.json"
|
<_ElectronBuilderJsonWrongLocation Include="$(MSBuildProjectDirectory)\**\electron-builder.json"
|
||||||
Exclude="$(MSBuildProjectDirectory)\Properties\electron-builder.json;
|
Exclude="$(MSBuildProjectDirectory)\Properties\electron-builder.json;
|
||||||
$(MSBuildProjectDirectory)\bin\**\*;
|
$(_ElectronCheckExcludes)" />
|
||||||
$(MSBuildProjectDirectory)\obj\**\*;
|
|
||||||
$(MSBuildProjectDirectory)\publish\**\*;
|
|
||||||
$(MSBuildProjectDirectory)\node_modules\**\*" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -296,10 +299,11 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
|||||||
</_PubxmlFileInfo>
|
</_PubxmlFileInfo>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Check each file for WebPublishMethod presence -->
|
<!-- Check each file for the ASP.NET specific properties (WebPublishMethod / ProjectGuid) -->
|
||||||
<ItemGroup Condition="'$(_HasPubxmlFiles)' == 'true'">
|
<ItemGroup Condition="'$(_HasPubxmlFiles)' == 'true'">
|
||||||
<_PubxmlFileInfoWithFlags Include="@(_PubxmlFileInfo)" Condition="'%(Identity)' != ''">
|
<_PubxmlFileInfoWithFlags Include="@(_PubxmlFileInfo)" Condition="'%(Identity)' != ''">
|
||||||
<HasWebPublishMethod>$([System.Text.RegularExpressions.Regex]::IsMatch('%(FileContent)', '<WebPublishMethod>'))</HasWebPublishMethod>
|
<HasWebPublishMethod>$([System.Text.RegularExpressions.Regex]::IsMatch('%(FileContent)', '<WebPublishMethod>'))</HasWebPublishMethod>
|
||||||
|
<HasProjectGuid>$([System.Text.RegularExpressions.Regex]::IsMatch('%(FileContent)', '<ProjectGuid>'))</HasProjectGuid>
|
||||||
</_PubxmlFileInfoWithFlags>
|
</_PubxmlFileInfoWithFlags>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -309,10 +313,10 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
|||||||
Condition="'$(_IsAspNetProject)' == 'true' AND '%(HasWebPublishMethod)' == 'False'" />
|
Condition="'$(_IsAspNetProject)' == 'true' AND '%(HasWebPublishMethod)' == 'False'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- For Console/Non-ASP.NET projects: find pubxml files that HAVE WebPublishMethod -->
|
<!-- For Console/Non-ASP.NET projects: find pubxml files that HAVE ASP.NET specific properties -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_ConsolePubxmlWithAspNetProperties Include="@(_PubxmlFileInfoWithFlags)"
|
<_ConsolePubxmlWithAspNetProperties Include="@(_PubxmlFileInfoWithFlags)"
|
||||||
Condition="'$(_IsAspNetProject)' != 'true' AND '%(HasWebPublishMethod)' == 'True'" />
|
Condition="'$(_IsAspNetProject)' != 'true' AND ('%(HasWebPublishMethod)' == 'True' OR '%(HasProjectGuid)' == 'True')" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Warning for ASP.NET projects with incorrect pubxml files -->
|
<!-- Warning for ASP.NET projects with incorrect pubxml files -->
|
||||||
@@ -336,7 +340,7 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
|||||||
<Warning
|
<Warning
|
||||||
Condition="'@(_ConsolePubxmlWithAspNetProperties)' != ''"
|
Condition="'@(_ConsolePubxmlWithAspNetProperties)' != ''"
|
||||||
Code="ELECTRON007"
|
Code="ELECTRON007"
|
||||||
Text="The publish profile '%(_ConsolePubxmlWithAspNetProperties.Identity)' appears to be configured for ASP.NET publishing (containing the WebPublishMethod property), but this is a console application project.
|
Text="The publish profile '%(_ConsolePubxmlWithAspNetProperties.Identity)' appears to be configured for ASP.NET publishing (containing the WebPublishMethod and/or ProjectGuid property), but this is a console application project.
|
||||||
|
|
||||||
RECOMMENDED ACTION:
|
RECOMMENDED ACTION:
|
||||||
1. Delete the existing publish profiles
|
1. Delete the existing publish profiles
|
||||||
@@ -351,4 +355,59 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
|
|||||||
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Check 6: The ElectronHostHook folder must be set up correctly
|
||||||
|
|
||||||
|
Custom host hook TypeScript code is only compiled and packaged when the folder also
|
||||||
|
contains a package.json (and a tsconfig.json for the TypeScript compilation). Without
|
||||||
|
those files the hook code is silently ignored.
|
||||||
|
-->
|
||||||
|
<Target Name="ElectronCheckHostHookFolder"
|
||||||
|
Condition="Exists('$(MSBuildProjectDirectory)\ElectronHostHook')">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_HostHookTsFiles Include="$(MSBuildProjectDirectory)\ElectronHostHook\*.ts" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<_HasHostHookTsFiles>false</_HasHostHookTsFiles>
|
||||||
|
<_HasHostHookTsFiles Condition="@(_HostHookTsFiles->Count()) > 0">true</_HasHostHookTsFiles>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Warning Condition="'$(_HasHostHookTsFiles)' == 'true' AND !Exists('$(MSBuildProjectDirectory)\ElectronHostHook\package.json')"
|
||||||
|
Code="ELECTRON010"
|
||||||
|
Text="The 'ElectronHostHook' folder contains TypeScript files but no package.json.
|
||||||
|
|
||||||
|
Folder: $(MSBuildProjectDirectory)\ElectronHostHook
|
||||||
|
|
||||||
|
The host hook code is only compiled and packaged when the folder contains a package.json declaring its npm dependencies. Without it the hook is silently ignored at runtime.
|
||||||
|
|
||||||
|
HOW TO FIX:
|
||||||
|
Add a package.json to the 'ElectronHostHook' folder, for example:
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "electron-host-hook",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"dependencies": { "socket.io": "^4.8.1" },
|
||||||
|
"devDependencies": { "typescript": "^5.9.3" }
|
||||||
|
}
|
||||||
|
|
||||||
|
For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migration-Checks#6-electronhosthook-folder-validation" />
|
||||||
|
|
||||||
|
<Warning Condition="'$(_HasHostHookTsFiles)' == 'true' AND !Exists('$(MSBuildProjectDirectory)\ElectronHostHook\tsconfig.json')"
|
||||||
|
Code="ELECTRON011"
|
||||||
|
Text="The 'ElectronHostHook' folder contains TypeScript files but no tsconfig.json.
|
||||||
|
|
||||||
|
Folder: $(MSBuildProjectDirectory)\ElectronHostHook
|
||||||
|
|
||||||
|
Without a tsconfig.json the hook TypeScript sources are not compiled with the expected settings.
|
||||||
|
|
||||||
|
HOW TO FIX:
|
||||||
|
Add a tsconfig.json to the 'ElectronHostHook' folder.
|
||||||
|
|
||||||
|
For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migration-Checks#6-electronhosthook-folder-validation" />
|
||||||
|
|
||||||
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user