mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-09-22 15:04:47 +00:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0c0523c7d | ||
|
|
6d94cf9538 | ||
|
|
521e7c935a | ||
|
|
730eacbd52 | ||
|
|
deca8166ec | ||
|
|
a04ca0b9bc | ||
|
|
fffd7dfb79 | ||
|
|
98c72af3d6 | ||
|
|
703cf8f880 | ||
|
|
248d4bd664 | ||
|
|
d5da1dbc9b | ||
|
|
672ff6c40d | ||
|
|
7cd552a78f | ||
|
|
042d8e6c70 | ||
|
|
0140749753 | ||
|
|
7af2e954c7 | ||
|
|
da0e1f17db | ||
|
|
9cb210040d | ||
|
|
7e8f45b16d | ||
|
|
76dc687269 | ||
|
|
dcf4585510 | ||
|
|
a6d7434adf | ||
|
|
76ff44f67e | ||
|
|
20ab82b16d | ||
|
|
b27f705a6a | ||
|
|
8697c4de44 | ||
|
|
376a791e48 | ||
|
|
195a29052e | ||
|
|
f577a53227 | ||
|
|
382d544468 | ||
|
|
18f08f9aff | ||
|
|
def289ab54 | ||
|
|
4011f3d30b | ||
|
|
26b9d86ee4 | ||
|
|
3b57de976a | ||
|
|
d9e9b3d2e6 | ||
|
|
1ec1fde3c6 | ||
|
|
e5ccf75c9f | ||
|
|
819e643ab8 | ||
|
|
7fe3f9b5b6 | ||
|
|
16cbc5f2a4 | ||
|
|
3a2c997e6a | ||
|
|
f88f5f8b18 | ||
|
|
4e39f5f377 | ||
|
|
7fa5ec5b0b | ||
|
|
2f8f717dbb | ||
|
|
525d6185fb | ||
|
|
73703819be | ||
|
|
09249f74c6 | ||
|
|
cfd8be36f3 | ||
|
|
6055635eb5 | ||
|
|
74a734ae58 | ||
|
|
d4402d1040 | ||
|
|
4b65ed26a4 | ||
|
|
e12145004e | ||
|
|
3e8ebde05b | ||
|
|
820d7bbcec | ||
|
|
5eeab1bd24 | ||
|
|
1fff688098 | ||
|
|
9b701f5258 | ||
|
|
b41c7e51b1 | ||
|
|
1d24067f4b | ||
|
|
44e5c4e230 | ||
|
|
0b3053477d | ||
|
|
3fecc0ee1b | ||
|
|
95089e1721 | ||
|
|
cec178cf50 | ||
|
|
20af8a692c | ||
|
|
4e69bcf030 | ||
|
|
5ae5bf0df7 | ||
|
|
5da5d1c618 | ||
|
|
b6e496b2c6 | ||
|
|
5974cb9e43 | ||
|
|
d4d4e9dddd | ||
|
|
3b5f21c724 | ||
|
|
5aebe807c3 | ||
|
|
bdee008cc8 | ||
|
|
7fc5c9b611 | ||
|
|
86e5d74c03 | ||
|
|
e79b029e16 | ||
|
|
5bdf6230b9 | ||
|
|
8c2382a05c | ||
|
|
10b1b686cb | ||
|
|
d7d2521d0c | ||
|
|
392419e5a7 | ||
|
|
f92f07a378 | ||
|
|
9b045cbf01 | ||
|
|
94ff3c1c4d | ||
|
|
773937448d | ||
|
|
b094051dcb | ||
|
|
3a10e7e98d | ||
|
|
056661028b | ||
|
|
27a8c69a9a | ||
|
|
8e900655b9 | ||
|
|
066efa80e2 | ||
|
|
18944fe222 | ||
|
|
0e83af032c | ||
|
|
1ec84cfef0 | ||
|
|
1620c50627 | ||
|
|
295b250e58 | ||
|
|
644628c803 | ||
|
|
a18944d4a4 | ||
|
|
b5858a697a | ||
|
|
58b9ad0125 | ||
|
|
e44927a655 | ||
|
|
ed9a24bb5e | ||
|
|
94df47efa6 | ||
|
|
d9f3da87a8 | ||
|
|
a33d76ae2f | ||
|
|
15d6a23016 | ||
|
|
c76ce9702d | ||
|
|
279a823eb7 | ||
|
|
fdab81a3a6 | ||
|
|
558e582789 | ||
|
|
c4251955d9 | ||
|
|
af5e70c000 | ||
|
|
fee3806953 | ||
|
|
979b25ffe7 | ||
|
|
07be82cca6 | ||
|
|
fe939e0fda | ||
|
|
50c9076af6 | ||
|
|
d7e1a09880 | ||
|
|
30b7b1d251 | ||
|
|
e9b3ba0add | ||
|
|
89f721d1b3 | ||
|
|
833d288891 | ||
|
|
e529d092c3 | ||
|
|
11f71feeb8 | ||
|
|
1e8b02648a | ||
|
|
2a14840c6b | ||
|
|
005feddcc5 | ||
|
|
3f14f9ae7e | ||
|
|
ce271fd30a | ||
|
|
1d1b9537b5 | ||
|
|
4c48588ce2 | ||
|
|
c9945a87a9 | ||
|
|
8fc197b32a | ||
|
|
7930831b6d | ||
|
|
201046164c | ||
|
|
b8151a2fad | ||
|
|
c1bf6d9423 | ||
|
|
c8268fae0e | ||
|
|
8eead56cbc | ||
|
|
e28eadc32d | ||
|
|
9a935c009e | ||
|
|
c22c248f0b | ||
|
|
842e8700ef | ||
|
|
3850a71579 | ||
|
|
d5df30718b | ||
|
|
77ca79acc9 | ||
|
|
05fb9e5a29 | ||
|
|
850d8f6902 | ||
|
|
497e6aa423 | ||
|
|
41c5b5af62 | ||
|
|
10679029e8 | ||
|
|
a451e3a599 | ||
|
|
09c735a10f | ||
|
|
8009348941 | ||
|
|
b3b124bde1 | ||
|
|
21226e1ef0 | ||
|
|
7174118a3e | ||
|
|
092789a5ec | ||
|
|
bff3fffcbd | ||
|
|
f4da9588c9 | ||
|
|
04c1415f6d | ||
|
|
456135a562 | ||
|
|
300f52510c | ||
|
|
d85a64f515 | ||
|
|
53698d1d44 | ||
|
|
04a224aa4f | ||
|
|
891da140b7 | ||
|
|
17f761d184 | ||
|
|
9f6489891e | ||
|
|
c8f1cdf59f | ||
|
|
6c95dfd476 | ||
|
|
b180fc2ea8 | ||
|
|
682a1c38ed | ||
|
|
809b0a6be7 | ||
|
|
ae3f755648 |
14
.github/CONTRIBUTING.md
vendored
14
.github/CONTRIBUTING.md
vendored
@@ -134,4 +134,16 @@ TL;DR: Unless there is a technical reason (e.g., a crucial new API not being ava
|
||||
|
||||
We pretty much release whenever we have something new (i.e., do fixes such as a 0.1.1, or add new features, such as a 0.2.0) quite quickly.
|
||||
|
||||
We will go for a 1.0.0 release of this as early as ~mid of January 2026 (unless we find some critical things or want to extend the beta phase for ElectronNET.Core). This should be sufficient time to get some user input and have enough experience to call it stable.
|
||||
We will go for a 1.0.0 release of this as early as ~mid of June 2026 (unless we find some critical things or want to extend the beta phase for ElectronNET.Core). This should be sufficient time to get some user input and have enough experience to call it stable.
|
||||
|
||||
## Updating Electron Versions
|
||||
|
||||
The releases of Electron are found on the [releases.electronjs.org](https://releases.electronjs.org/release?page=1) website.
|
||||
|
||||
You can update the `src\ElectronNET\build\ElectronNETRules.Project.xaml` file with new entries using the following script (run it in the browser's console when being on the Electron Releases website):
|
||||
|
||||
```js
|
||||
[...new Set([...document.querySelectorAll('tbody tr a[href^="/release/v"]')].map(m => m.getAttribute('href').replace('/release/v', '')))].map(m => `<EnumValue Name="${m}" DisplayName="${m}" />`).sort().join('\n')
|
||||
```
|
||||
|
||||
Alternatively, use the website's information to feed into an AI agent of your choice.
|
||||
|
||||
4
.github/workflows/Build and Publish.yml
vendored
4
.github/workflows/Build and Publish.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: Build and Publish
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
19
.github/workflows/integration-tests.yml
vendored
19
.github/workflows/integration-tests.yml
vendored
@@ -4,13 +4,14 @@ on:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: integration-tests-${{ github.ref }}
|
||||
group: integration-tests-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: ${{ matrix.os }} API-${{ matrix.electronVersion }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 25
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -42,13 +43,6 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Random delay (0-20 seconds)
|
||||
shell: bash
|
||||
run: |
|
||||
DELAY=$((RANDOM % 21))
|
||||
echo "Waiting for $DELAY seconds..."
|
||||
sleep $DELAY
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
@@ -79,27 +73,31 @@ jobs:
|
||||
- name: Run tests (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
continue-on-error: true
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
mkdir -p test-results
|
||||
xvfb-run -a dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj \
|
||||
-c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} \
|
||||
--logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" \
|
||||
--logger "console;verbosity=detailed" \
|
||||
--blame-hang --blame-hang-timeout 5min \
|
||||
--results-directory test-results
|
||||
|
||||
- name: Run tests (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
continue-on-error: true
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path test-results | Out-Null
|
||||
dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results
|
||||
dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --blame-hang --blame-hang-timeout 5min --results-directory test-results
|
||||
|
||||
- name: Run tests (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
continue-on-error: true
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
mkdir -p test-results
|
||||
dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results
|
||||
dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --blame-hang --blame-hang-timeout 5min --results-directory test-results
|
||||
|
||||
- name: Upload raw test results
|
||||
if: always()
|
||||
@@ -114,6 +112,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
if: always()
|
||||
needs: [tests]
|
||||
timeout-minutes: 10
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
|
||||
2
.github/workflows/retry-test-jobs.yml
vendored
2
.github/workflows/retry-test-jobs.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
run: |
|
||||
echo "Inspecting jobs of workflow run $RUN_ID in $REPO"
|
||||
|
||||
jobs_json="$(gh api -R $REPO repos/$REPO/actions/runs/$RUN_ID/jobs)"
|
||||
jobs_json="$(gh api repos/$REPO/actions/runs/$RUN_ID/jobs)"
|
||||
|
||||
echo "Jobs and conclusions:"
|
||||
echo "$jobs_json" | jq '.jobs[] | {name: .name, conclusion: .conclusion}'
|
||||
|
||||
74
Changelog.md
74
Changelog.md
@@ -1,3 +1,77 @@
|
||||
# 0.6.0
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
- 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 slow socket bridge startup by binding to an explicit loopback address (#1103)
|
||||
- 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 cross-compilation behavior on same platform (#1098) @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 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 build extensibility properties `ElectronSkipExecCommands` and `ElectronIntermediatePublishDir` (#1106) @DYH1319
|
||||
|
||||
# 0.5.2
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
- Fixed token param being appended to external URLs (#1075)
|
||||
- Fixed startup mode discriminator in case of existing `wwwroot` (#1050)
|
||||
- Fixed CA1416 on ASP.NET project (#1091) @epsnm
|
||||
- Fixed loading issue in plain Visual Studio (#1084) @epsnm
|
||||
- Fixed bloated ASAR file (#1080) @epsnm
|
||||
- Improved selection of runtime identifier (#1081) @epsnm
|
||||
- Added more variants for `UseElectron` (#1076) @AeonSake
|
||||
- Added support for modern MacOS app icon (#1047)
|
||||
|
||||
# 0.5.1
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
- Fixed slicing of arguments for packaged applications (#1072)
|
||||
- Added support for Electron 42+ (#1073)
|
||||
|
||||
# 0.5.0
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
- Updated internal facade from `SocketIoFacade` to `ISocketConnection` (#1037)
|
||||
- Fixed `HasShadow` is not `true` by default (#1049) @AeonSake
|
||||
- Fixed missing `ReadAllLines` in MSBuild execution (#1035)
|
||||
- Fixed serialization of release notes for the updater (#1041)
|
||||
- Added automatic port selection and secured IPC communication (#1038)
|
||||
|
||||
# 0.4.1
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
- Updated documentation for preload scripts (#1031) @AeonSake
|
||||
- Updated timeout for electron-builder (#1013) @softworkz
|
||||
- Updated disposal avoiding exceptions on teardown (#1026) @softworkz
|
||||
- Updated migration guide (#1015) @hilin
|
||||
- Fixed handling of `Center` property for windows (#1001)
|
||||
- Fixed false alarm for `ELECTRON001`, `ELECTRON008`, and `ELECTRON009` (#1012) @softworkz
|
||||
- Added missing methods on `Cookies` (#1000)
|
||||
- Added overload for `GetAllDisplaysAsync` with timeout (#1033) @softworkz
|
||||
- Added `OnBoundsChanged` event (#1014) @softworkz
|
||||
- Added new events for `ipcMain` (#1019) @DYH1319
|
||||
|
||||
# 0.4.0
|
||||
|
||||
## ElectronNET.Core
|
||||
|
||||
15
Directory.Build.props
Normal file
15
Directory.Build.props
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!-- This file prevents unintended imports of unrelated MSBuild files -->
|
||||
<!-- Uncomment to include parent Directory.Build.props file -->
|
||||
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />-->
|
||||
|
||||
<PropertyGroup>
|
||||
<ElectronNetBuildProps>$([MSBuild]::GetPathOfFileAbove('ElectronNet.Build.props', '$(MSBuildThisFileDirectory)../'))</ElectronNetBuildProps>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Only import specific project related MSBuild file -->
|
||||
<Import Project="$(ElectronNetBuildProps)" Condition=" '$(ElectronNetBuildProps)' != '' " />
|
||||
|
||||
</Project>
|
||||
@@ -2,7 +2,7 @@
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!-- This file prevents unintended imports of unrelated MSBuild files -->
|
||||
<!-- Uncomment to include parent Directory.Build.props file -->
|
||||
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />-->
|
||||
<!-- Uncomment to include parent Directory.Packages.props file -->
|
||||
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Packages.props', '$(MSBuildThisFileDirectory)../'))" />-->
|
||||
|
||||
</Project>
|
||||
@@ -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.API: [](https://www.nuget.org/packages/ElectronNET.Core.API/)
|
||||
* 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
|
||||
|
||||
You should have installed:
|
||||
|
||||
* .NET 6/8 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).
|
||||
* .NET 8/10 or later.
|
||||
* 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).
|
||||
|
||||
## 👩🏫 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)
|
||||
- [Florian Rappl](https://github.com/sponsors/FlorianRappl)
|
||||
|
||||
Or just [buy Florian a coffee](https://buymeacoffee.com/florianrappl) :coffee:.
|
||||
|
||||
Any support appreciated! 🍻
|
||||
|
||||
## 🎉 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)
|
||||
- `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)`
|
||||
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.
|
||||
|
||||
#### 🧊 `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
|
||||
|
||||
#### ⚡ `InputEvent`
|
||||
@@ -142,6 +259,21 @@ Emitted when the document in the top-level frame is loaded.
|
||||
#### ⚡ `OnWillRedirect`
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
- [Electron.WindowManager](WindowManager.md) - Windows containing web contents
|
||||
|
||||
@@ -8,34 +8,54 @@ When you build an Electron.NET project, the following validation checks are perf
|
||||
|
||||
| Code | Check | Description |
|
||||
|------|-------|-------------|
|
||||
| [ELECTRON001](#1-packagejson-not-allowed) | package.json not allowed | Ensures no package.json exists outside ElectronHostHook |
|
||||
| [ELECTRON001](#1-packagejson-rules) | package.json location rules | Ensures `package.json`/`package-lock.json` aren’t present in unsupported locations (root `package.json` handled separately) |
|
||||
| [ELECTRON008](#1-packagejson-rules) | root package.json contains electron | Warns when root `package.json` contains the word `electron` (case-insensitive) |
|
||||
| [ELECTRON009](#1-packagejson-rules) | root package.json copied to output | Warns when root `package.json` is configured to be copied to output/publish |
|
||||
| [ELECTRON002](#2-electron-manifestjson-not-allowed) | electron-manifest.json not allowed | Detects deprecated manifest files |
|
||||
| [ELECTRON003](#3-electron-builderjson-location) | electron-builder.json location | Verifies electron-builder.json exists in Properties folder |
|
||||
| [ELECTRON004](#3-electron-builderjson-location) | electron-builder.json wrong location | Warns if electron-builder.json is found in incorrect locations |
|
||||
| [ELECTRON005](#4-parent-paths-not-allowed-in-electron-builderjson) | Parent paths not allowed | Checks for `..` references in config |
|
||||
| [ELECTRON006](#5-publish-profile-validation) | ASP.NET publish profile mismatch | Warns when ASP.NET projects have console-style profiles |
|
||||
| [ELECTRON007](#5-publish-profile-validation) | Console publish profile mismatch | Warns when console projects have ASP.NET-style profiles |
|
||||
| [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).
|
||||
|
||||
---
|
||||
|
||||
## 1. package.json not allowed
|
||||
## 1. package.json rules
|
||||
|
||||
**Warning Code:** `ELECTRON001`
|
||||
**Warning Codes:** `ELECTRON001`, `ELECTRON008`, `ELECTRON009`
|
||||
|
||||
### What is checked
|
||||
|
||||
The build system scans for `package.json` and `package-lock.json` files in your project directory. These files should not exist in the project root or subdirectories (with one exception).
|
||||
The build system scans for `package.json` and `package-lock.json` files in your project directory.
|
||||
|
||||
Rules:
|
||||
|
||||
- **ELECTRON001**: `package.json` / `package-lock.json` must not exist in the project directory or subdirectories
|
||||
- Exception: `ElectronHostHook` folder is allowed
|
||||
- Note: a **root** `package.json` (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.
|
||||
|
||||
- **ELECTRON009**: If a root `package.json` exists, it must **not** be configured to be copied to output/publish (for example via `CopyToOutputDirectory` / `CopyToPublishDirectory` metadata)
|
||||
|
||||
### Why this matters
|
||||
|
||||
In previous versions of Electron.NET, a `package.json` file was required in the project. The new version generates this file automatically from MSBuild properties defined in your `.csproj` file.
|
||||
Electron.NET generates its Electron-related `package.json` during publishing based on MSBuild properties. A user-maintained Electron-related `package.json` can conflict with that process.
|
||||
|
||||
Also, ensuring the root `package.json` is not copied prevents accidentally shipping it with the published app.
|
||||
|
||||
### Exception
|
||||
|
||||
A `package.json` file **is allowed** in the `ElectronHostHook` folder if you're using custom host hooks. This is the only valid location for a manually maintained package.json.
|
||||
A `package.json` / `package-lock.json` file **is allowed** in the `ElectronHostHook` folder if you're using custom host hooks.
|
||||
|
||||
### How to fix
|
||||
|
||||
If you have an Electron-related `package.json` from older Electron.NET versions:
|
||||
|
||||
1. **Open your project's `.csproj` file**
|
||||
2. **Add the required properties** to a PropertyGroup with the label `ElectronNetCommon`:
|
||||
|
||||
@@ -51,7 +71,12 @@ A `package.json` file **is allowed** in the `ElectronHostHook` folder if you're
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
3. **Delete the old `package.json`** file from your project root
|
||||
3. **Delete** Electron-related `package.json` / `package-lock.json` files (except those under `ElectronHostHook` if applicable)
|
||||
|
||||
If you keep a root `package.json` for non-Electron reasons:
|
||||
|
||||
- Ensure it does **not** contain electron dependencies or configuration (fixes `ELECTRON008`)
|
||||
- Ensure it is **not** copied to output/publish (fixes `ELECTRON009`)
|
||||
|
||||
> **See also:** [Migration Guide](Migration-Guide.md) for complete migration instructions.
|
||||
|
||||
@@ -92,6 +117,10 @@ The `electron.manifest.json` file format is deprecated. All configuration should
|
||||
"mac": {
|
||||
"icon": "Assets/app.icns",
|
||||
"target": ["dmg", "zip"]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": true,
|
||||
"perMachine": false
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -187,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.
|
||||
|
||||
- **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
|
||||
|
||||
@@ -212,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
|
||||
|
||||
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
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -31,7 +31,7 @@ dotnet add package ElectronNET.Core.AspNet # For ASP.NET projects
|
||||
### Step 2: Configure Project Settings
|
||||
|
||||
**Auto-generated Configuration:**
|
||||
ElectronNET.Core automatically creates `electron-builder.json` during the first build or NuGet restore. No manual configuration is needed for basic setups.
|
||||
ElectronNET.Core automatically creates `electron-builder.json` in the `Properties` folder of your project during the first build or NuGet restore. No manual configuration is needed for basic setups.
|
||||
|
||||
**Migrate Existing Configuration:**
|
||||
If you have an existing `electron.manifest.json` file:
|
||||
@@ -48,21 +48,26 @@ You can also manually edit `electron-builder.json`:
|
||||
```json
|
||||
{
|
||||
"linux": {
|
||||
"target": [
|
||||
"tar.xz"
|
||||
]
|
||||
"target": ["tar.xz"]
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "portable",
|
||||
"target": "nsis",
|
||||
"arch": "x64"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": true,
|
||||
"perMachine": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Modify Launch Settings:**
|
||||
ElectronNET.Core no longer needs a separate CLI tool (electronize.exe) for launching. You should update your launch settings to use either the ASP.NET-first or Electron-first approach. See [Debugging](../Using/Debugging.md) for details.
|
||||
|
||||
## 🎯 Testing Migration
|
||||
|
||||
After completing the migration steps:
|
||||
|
||||
@@ -10,6 +10,11 @@ See [System Requirements](../GettingStarted/System-Requirements.md).
|
||||
|
||||
## 🚀 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
|
||||
|
||||
#### Visual Studio
|
||||
|
||||
@@ -54,7 +54,7 @@ Add the Electron.NET configuration to your `.csproj` file:
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.4.0" />
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.6.0" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
@@ -28,9 +28,77 @@ These are the current default values when you don't make any changes:
|
||||
<ElectronPackageId>$(MSBuildProjectName.Replace(".", "-").ToLower())</ElectronPackageId>
|
||||
<ElectronBuilderJson>electron-builder.json</ElectronBuilderJson>
|
||||
<Title>$(MSBuildProjectName)</Title>
|
||||
<ElectronExecutableName></ElectronExecutableName>
|
||||
<ElectronRootDir>../..</ElectronRootDir>
|
||||
<ElectronSkipExecCommands>false</ElectronSkipExecCommands>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Product Name and Executable Name
|
||||
|
||||
`Title` is the product name of the application. It is used for the window title, the installer, the macOS app bundle and the Linux desktop entry, and it may contain spaces and other characters that are awkward in a file name.
|
||||
|
||||
`ElectronExecutableName` controls the name of the executable inside the package (electron-builder's `executableName`). When it is not set, it defaults to the following behavior:
|
||||
|
||||
| Target | Default |
|
||||
| --- | --- |
|
||||
| Windows | `$(Title)` |
|
||||
| Linux / macOS | `$(ElectronPackageId)` |
|
||||
|
||||
So to ship a product called `My Great App` with a `mygreatapp` binary on every platform:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<Title>My Great App</Title>
|
||||
<ElectronExecutableName>mygreatapp</ElectronExecutableName>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `ElectronExecutable` is a separate, advanced setting: it is the binary Electron.NET launches when the .NET app starts first. It defaults to `ElectronExecutableName` and only needs to be changed when the package contains a launcher stub (for example added by an electron-builder hook) that should be started instead of the Electron binary.
|
||||
|
||||
### Custom Packaging Layout
|
||||
|
||||
`ElectronRootDir` tells the .NET app where to find the Electron binary when it is started first (DotNet-First startup of a packaged app). The path is resolved relative to the directory of the .NET executable; absolute paths are used as-is.
|
||||
|
||||
The default `../..` matches the standard Electron-First packaging layout produced by `dotnet publish`, where the .NET app is placed in `resources/bin` of the Electron package:
|
||||
|
||||
```
|
||||
<install-root>/
|
||||
MyApp # Electron executable
|
||||
resources/
|
||||
bin/
|
||||
MyApp # .NET executable
|
||||
```
|
||||
|
||||
If your own packaging pipeline places the .NET executable at the package root and Electron in a subdirectory, set `ElectronRootDir` accordingly:
|
||||
|
||||
```xml
|
||||
<ElectronRootDir>electron</ElectronRootDir>
|
||||
```
|
||||
|
||||
```
|
||||
<install-root>/
|
||||
MyApp # .NET executable
|
||||
electron/
|
||||
MyApp # Electron executable
|
||||
resources/
|
||||
locales/
|
||||
```
|
||||
|
||||
The property is also used to detect whether the app runs packaged or unpackaged, so it must point to the directory that actually contains the Electron binary.
|
||||
|
||||
### Build Extensibility
|
||||
|
||||
These properties are useful when Electron.NET is embedded into a custom build or CI pipeline:
|
||||
|
||||
| Property | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `ElectronSkipExecCommands` | `false` | Skips all `npm`/`npx` invocations of the Electron build and publish targets. Use it when the dependencies and the Electron distribution are provided by the pipeline itself (for example from a cache or an offline mirror). |
|
||||
| `ElectronIntermediatePublishDir` | `$(IntermediateOutputPath)PubTmp\` | Overrides the intermediate directory the .NET app is published to before it is assembled into the Electron package. |
|
||||
|
||||
`ElectronPackageId` and `Title` only fall back to the project name when they are not already set, so both can be defined by a `Directory.Build.props` or passed on the command line.
|
||||
|
||||
### Relation to package.json
|
||||
|
||||
ElectronNET.Core does not work with an `electron-manifest.json` file anymore.
|
||||
@@ -42,11 +110,17 @@ Since electron builder still expects a `package.json` file to exist, ElectronNET
|
||||
"productName": "$(ElectronTitle)",
|
||||
"build": {
|
||||
"appId": "$(ElectronPackageId)",
|
||||
"win": {
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"mac": {
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"linux": {
|
||||
"desktop": {
|
||||
"entry": { "Name": "$(Title)" }
|
||||
},
|
||||
"executableName": "$(ElectronPackageId)"
|
||||
"executableName": "$(ElectronExecutableName)"
|
||||
},
|
||||
"deb": {
|
||||
"desktop": {
|
||||
@@ -70,6 +144,12 @@ Since electron builder still expects a `package.json` file to exist, ElectronNET
|
||||
}
|
||||
```
|
||||
|
||||
### App Icon Path
|
||||
|
||||
The `ElectronIcon` property supports classic icon files (such as `.ico` and `.icns`) and modern macOS `.icon` app icon packages.
|
||||
|
||||
For `.icon`, provide the folder path (for example `Assets/MyApp.icon`). During build/publish, Electron.NET copies the full directory into the Electron output so `electron-builder.json` can reference it (for example `"mac": { "icon": "MyApp.icon" }`).
|
||||
|
||||
### Node.js Integration
|
||||
|
||||
Electron.NET requires Node.js integration to be enabled for IPC to function. If you are not using the IPC functionality you can disable Node.js integration like so:
|
||||
@@ -85,6 +165,26 @@ BrowserWindowOptions browserWindowOptions = new BrowserWindowOptions
|
||||
```
|
||||
|
||||
|
||||
### Preload Script
|
||||
|
||||
If you require the use of a [preload script](https://www.electronjs.org/docs/latest/tutorial/tutorial-preload), you can specify the file path of the script when creating a new window like so:
|
||||
|
||||
```csharp
|
||||
WebPreferences wp = new WebPreferences();
|
||||
wp.Preload = "path/to/preload.js";
|
||||
BrowserWindowOptions browserWindowOptions = new BrowserWindowOptions
|
||||
{
|
||||
WebPreferences = wp
|
||||
};
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When using a preload script _AND_ running a Blazor app, `IsRunningBlazor` must be set to `false` (or removed) and the following lines must be added to the preload script:
|
||||
> ```js
|
||||
> global.process = undefined;
|
||||
> global.module = undefined;
|
||||
> ```
|
||||
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
|
||||
@@ -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
|
||||
- Integrate Node.js modules (e.g., telemetry, OS APIs)
|
||||
- Control startup logic (abort, environment checks)
|
||||
- Modify Chromium / Electron.NET command line switches before they are evaluated
|
||||
- Set up IPC messaging or preload scripts
|
||||
|
||||
## Step-by-Step Process
|
||||
@@ -65,10 +66,29 @@ Use environment variables to control 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
|
||||
|
||||
- `custom_main.js` must use CommonJS syntax (`module.exports.onStartup = ...`).
|
||||
- Place the file in your source directory and copy it to `.electron` using `.csproj`.
|
||||
- Electron.NET will abort startup if `onStartup` returns `false`.
|
||||
- `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)
|
||||
@@ -222,6 +222,15 @@ The publish process will:
|
||||
> macOS builds can't be created on Windows machines because they require symlinks that aren't supported on Windows (per [this Electron issue](https://github.com/electron-userland/electron-packager/issues/71)). macOS builds can be produced on either Linux or macOS machines.
|
||||
|
||||
|
||||
## Custom Packaging Pipelines
|
||||
|
||||
The default layout is Electron-First: electron-builder produces the Electron package and the .NET app is placed in `resources/bin`. If you assemble the package yourself and put the .NET executable at the package root with Electron in a subdirectory, set `ElectronRootDir` so the .NET app can locate the Electron binary at runtime.
|
||||
|
||||
For pipelines that provide the npm dependencies themselves, `ElectronSkipExecCommands` suppresses all `npm`/`npx` invocations, and `ElectronIntermediatePublishDir` redirects the intermediate publish output.
|
||||
|
||||
See [Configuration](Configuration.md#custom-packaging-layout) for details.
|
||||
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
- **[Startup Methods](Startup-Methods.md)** - Understanding different launch modes for packaged apps
|
||||
|
||||
32
docs/Using/Secure-Communication.md
Normal file
32
docs/Using/Secure-Communication.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Secure Communication
|
||||
|
||||
By default, the IPC communication between .NET and Node.js is secured on startup. Consequently, multiple instances running on different user accounts (but shared on the same machine) can safely co-exist. However, this protection is not enough to secure the web application behind - or make any security statement w.r.t. a malicious root user.
|
||||
|
||||
## Securing the Web Application
|
||||
|
||||
You can opt-in to also guard your ASP.NET Core application using the same mechanism that is already used to protected the IPC broker that deals with the .NET to Node.js communication.
|
||||
|
||||
The key to opt-in is to provide another service *before* calling `AddElectron` on the service collection.
|
||||
|
||||
The following two namespaces are used in the next instructions:
|
||||
|
||||
```cs
|
||||
using ElectronNET.AspNet.Middleware;
|
||||
using ElectronNET.AspNet.Services;
|
||||
```
|
||||
|
||||
You'll need the following line:
|
||||
|
||||
```cs
|
||||
builder.Services.AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>();
|
||||
```
|
||||
|
||||
This way, Electron.NET is notified that you want to store and re-use the authentication token that has been negotiated between the .NET and Node.js processes at startup.
|
||||
|
||||
With this being set up you can register a middleware to actually deny requests that have originated outside of your Electron.NET application:
|
||||
|
||||
```cs
|
||||
app.UseMiddleware<ElectronAuthenticationMiddleware>();
|
||||
```
|
||||
|
||||
This must be placed above any routing (e.g., before calling `UseRouting` on the web application) in order to properly take effect.
|
||||
@@ -15,6 +15,7 @@
|
||||
# Getting Started
|
||||
|
||||
- [System Requirements](GettingStarted/System-Requirements.md)
|
||||
- [Project Templates](GettingStarted/Templates.md)
|
||||
- [With ASP.Net](GettingStarted/ASP.Net.md)
|
||||
- [With a Console App](GettingStarted/Console-App.md)
|
||||
|
||||
@@ -24,6 +25,7 @@
|
||||
- [Startup-Methods](Using/Startup-Methods.md)
|
||||
- [Debugging](Using/Debugging.md)
|
||||
- [Package Building](Using/Package-Building.md)
|
||||
- [Secure Communication](Using/Secure-Communication.md)
|
||||
- [Adding a `custom_main.js`](Using/Custom_main.md)
|
||||
|
||||
# API Reference
|
||||
|
||||
@@ -314,7 +314,9 @@ namespace ElectronNET.API
|
||||
{
|
||||
if (this.tcs != null)
|
||||
{
|
||||
var ex = new TimeoutException($"No response after {timeout:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
|
||||
var ex = new TimeoutException(
|
||||
$"No response after {(long)timeout.TotalMilliseconds}ms trying to retrieve value {apiBase.objectName}.{callerName}()"
|
||||
);
|
||||
this.tcs.TrySetException(ex);
|
||||
this.tcs = null;
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ namespace ElectronNET.API
|
||||
|
||||
public void DisposeSocket()
|
||||
{
|
||||
BridgeConnector.Socket.DisposeSocket();
|
||||
BridgeConnector.Socket.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -186,6 +186,19 @@ public class BrowserWindow : ApiBase
|
||||
remove => RemoveEvent(value, Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emitted when the window is moved or resized.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// While not being an original Electron event, this one includes the bounds values,
|
||||
/// saving the additional roundtrip for calling <see cref="GetBoundsAsync"/>.
|
||||
/// </remarks>
|
||||
public event Action<Rectangle> OnBoundsChanged
|
||||
{
|
||||
add => AddEvent(value, Id);
|
||||
remove => RemoveEvent(value, Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// macOS: Emitted once when the window is moved to a new position.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using ElectronNET.API.Entities;
|
||||
using ElectronNET.API.Serialization;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
@@ -54,10 +55,79 @@ namespace ElectronNET.API
|
||||
_changed -= value;
|
||||
|
||||
if (_changed == null)
|
||||
{
|
||||
BridgeConnector.Socket.Off("webContents-session-cookies-changed" + Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private event Action<Cookie, CookieChangedCause, bool> _changed;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to get all cookies matching filter, and resolves a callack with the response.
|
||||
/// </summary>
|
||||
/// <param name="filter">
|
||||
/// </param>
|
||||
/// <returns>A task which resolves an array of cookie objects.</returns>
|
||||
public Task<Cookie[]> GetAsync(CookieFilter filter)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<Cookie[]>();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
|
||||
BridgeConnector.Socket.Once<Cookie[]>("webContents-session-cookies-get-completed" + guid, tcs.SetResult);
|
||||
BridgeConnector.Socket.Emit("webContents-session-cookies-get", Id, filter, guid);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="details"></param>
|
||||
/// <returns></returns>
|
||||
public Task SetAsync(CookieDetails details)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
|
||||
BridgeConnector.Socket.Once<object>("webContents-session-cookies-set-completed" + guid, tcs.SetResult);
|
||||
BridgeConnector.Socket.Emit("webContents-session-cookies-set", Id, details, guid);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the cookies matching url and name
|
||||
/// </summary>
|
||||
/// <param name="url">The URL associated with the cookie.</param>
|
||||
/// <param name="name">The name of cookie to remove.</param>
|
||||
/// <returns>A task which resolves when the cookie has been removed</returns>
|
||||
public Task RemoveAsync(string url, string name)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
|
||||
BridgeConnector.Socket.Once<object>("webContents-session-cookies-remove-completed" + guid, tcs.SetResult);
|
||||
BridgeConnector.Socket.Emit("webContents-session-cookies-remove", Id, url, name, guid);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes any unwritten cookies data to disk.
|
||||
/// </summary>
|
||||
/// <returns>A task which resolves when the cookie store has been flushed</returns>
|
||||
public Task FlushStoreAsync()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
|
||||
BridgeConnector.Socket.Once<object>("webContents-session-cookies-flushStore-completed" + guid, tcs.SetResult);
|
||||
BridgeConnector.Socket.Emit("webContents-session-cookies-flushStore", Id, guid);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
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,13 +24,13 @@ namespace ElectronNET.API.Entities
|
||||
/// ( if y is used) Window's left offset from screen. Default is to center the
|
||||
/// window.
|
||||
/// </summary>
|
||||
public int X { get; set; } = -1;
|
||||
public int? X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ( if x is used) Window's top offset from screen. Default is to center the
|
||||
/// window.
|
||||
/// </summary>
|
||||
public int Y { get; set; } = -1;
|
||||
public int? Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The width and height would be used as web page's size, which means the actual
|
||||
@@ -215,7 +215,8 @@ namespace ElectronNET.API.Entities
|
||||
/// <summary>
|
||||
/// Whether window should have a shadow. Default is true.
|
||||
/// </summary>
|
||||
public bool HasShadow { get; set; }
|
||||
[DefaultValue(true)]
|
||||
public bool HasShadow { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Forces using dark theme for the window, only works on some GTK+3 desktop environments. Default is false.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the cookie is a host-only cookie; this will only be true if no domain was passed.
|
||||
/// </summary>
|
||||
public bool HostOnly { get; set; }
|
||||
public bool? HostOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path of the cookie.
|
||||
@@ -34,22 +34,22 @@
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the cookie is marked as secure.
|
||||
/// </summary>
|
||||
public bool Secure { get; set; }
|
||||
public bool? Secure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the cookie is marked as HTTP only.
|
||||
/// </summary>
|
||||
public bool HttpOnly { get; set; }
|
||||
public bool? HttpOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the cookie is a session cookie or a persistent cookie with an expiration date.
|
||||
/// </summary>
|
||||
public bool Session { get; set; }
|
||||
public bool? Session { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expiration date of the cookie as the number of seconds since the UNIX epoch. Not provided for session cookies.
|
||||
/// </summary>
|
||||
public double ExpirationDate { get; set; }
|
||||
public double? ExpirationDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SameSite policy applied to this cookie. Can be "unspecified", "no_restriction", "lax" or "strict".
|
||||
|
||||
@@ -29,16 +29,16 @@
|
||||
/// <summary>
|
||||
/// (optional) - Filters cookies by their Secure property.
|
||||
/// </summary>
|
||||
public bool Secure { get; set; }
|
||||
public bool? Secure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// (optional) - Filters out session or persistent cookies.
|
||||
/// </summary>
|
||||
public bool Session { get; set; }
|
||||
public bool? Session { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// (optional) - Filters cookies by httpOnly.
|
||||
/// </summary>
|
||||
public bool HttpOnly { get; set; }
|
||||
public bool? HttpOnly { 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
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace ElectronNET.API.Entities
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using ElectronNET.API.Converter;
|
||||
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
@@ -24,7 +28,8 @@
|
||||
/// <summary>
|
||||
/// Gets or sets the release notes.
|
||||
/// </summary>
|
||||
public ReleaseNoteInfo[] ReleaseNotes { get; set; } = new ReleaseNoteInfo[0];
|
||||
[JsonConverter(typeof(ReleaseNotesConverter))]
|
||||
public ReleaseNoteInfo[] ReleaseNotes { get; set; } = Array.Empty<ReleaseNoteInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the release date.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,29 @@ namespace ElectronNET.API
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to the renderer process synchronously via channel,
|
||||
/// you can also send arbitrary arguments.
|
||||
///
|
||||
/// Note: Sending a synchronous message will block the whole renderer process,
|
||||
/// unless you know what you are doing you should never use it.
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
/// <param name="listener"></param>
|
||||
public void OnSync(string channel, Func<object, Task<object>> listener)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("registerSyncIpcMainChannel", channel);
|
||||
BridgeConnector.Socket.On<JsonElement>(channel, (args) =>
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var arg = FormatArguments(args);
|
||||
var result = await listener(arg);
|
||||
BridgeConnector.Socket.Emit(channel + "Sync", result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a one time listener method for the event. This listener is invoked only
|
||||
/// the next time a message is sent to channel, after which it is removed.
|
||||
@@ -154,5 +177,88 @@ namespace ElectronNET.API
|
||||
{
|
||||
BridgeConnector.Socket.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a handler for an invokeable IPC. This handler will be called
|
||||
/// whenever a renderer calls ipcRenderer.invoke(channel, ...args).
|
||||
/// </summary>
|
||||
/// <param name="channel">Channelname.</param>
|
||||
/// <param name="listener">Callback Method.</param>
|
||||
public void Handle(string channel, Func<object, object> listener)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("registerHandleIpcMainChannel", channel);
|
||||
BridgeConnector.Socket.On<JsonElement>(channel, (args) =>
|
||||
{
|
||||
var arg = FormatArguments(args);
|
||||
var result = listener(arg);
|
||||
BridgeConnector.Socket.Emit(channel + "Handle", result);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a handler for an invokeable IPC. This handler will be called
|
||||
/// whenever a renderer calls ipcRenderer.invoke(channel, ...args).
|
||||
/// </summary>
|
||||
/// <param name="channel">Channelname.</param>
|
||||
/// <param name="listener">Callback Method.</param>
|
||||
public void Handle(string channel, Func<object, Task<object>> listener)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("registerHandleIpcMainChannel", channel);
|
||||
BridgeConnector.Socket.On<JsonElement>(channel, (args) =>
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var arg = FormatArguments(args);
|
||||
var result = await listener(arg);
|
||||
BridgeConnector.Socket.Emit(channel + "Handle", result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a single invokeable IPC message, then removes the listener.
|
||||
/// See ipcMain.handle(channel, listener).
|
||||
/// </summary>
|
||||
/// <param name="channel">Channelname.</param>
|
||||
/// <param name="listener">Callback Method.</param>
|
||||
public void HandleOnce(string channel, Func<object, object> listener)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("registerHandleOnceIpcMainChannel", channel);
|
||||
BridgeConnector.Socket.Once<JsonElement>(channel, (args) =>
|
||||
{
|
||||
var arg = FormatArguments(args);
|
||||
var result = listener(arg);
|
||||
BridgeConnector.Socket.Emit(channel + "HandleOnce", result);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a single invokeable IPC message, then removes the listener.
|
||||
/// See ipcMain.handle(channel, listener).
|
||||
/// </summary>
|
||||
/// <param name="channel">Channelname.</param>
|
||||
/// <param name="listener">Callback Method.</param>
|
||||
public void HandleOnce(string channel, Func<object, Task<object>> listener)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("registerHandleOnceIpcMainChannel", channel);
|
||||
BridgeConnector.Socket.Once<JsonElement>(channel, (args) =>
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var arg = FormatArguments(args);
|
||||
var result = await listener(arg);
|
||||
BridgeConnector.Socket.Emit(channel + "HandleOnce", result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any handler for channel, if present.
|
||||
/// </summary>
|
||||
/// <param name="channel">Channelname.</param>
|
||||
public void RemoveHandler(string channel)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("removeHandlerIpcMainChannel", channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,15 @@ namespace ElectronNET.API
|
||||
/// <returns>An array of displays that are currently available.</returns>
|
||||
public Task<Display[]> GetAllDisplaysAsync() => this.InvokeAsync<Display[]>();
|
||||
|
||||
/// <summary>
|
||||
/// An array of displays that are currently available.
|
||||
/// </summary>
|
||||
/// <param name="invocationTimeout">The invocation timeout.</param>
|
||||
/// <returns>
|
||||
/// An array of displays that are currently available.
|
||||
/// </returns>
|
||||
public Task<Display[]> GetAllDisplaysAsync(TimeSpan invocationTimeout) => this.InvokeAsyncWithTimeout<Display[]>(invocationTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// The display nearest the specified point.
|
||||
/// </summary>
|
||||
|
||||
@@ -113,6 +113,52 @@ public class WebContents : ApiBase
|
||||
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)
|
||||
{
|
||||
Id = id;
|
||||
@@ -304,6 +350,68 @@ public class WebContents : ApiBase
|
||||
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>
|
||||
/// Inserts CSS into the web page.
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Returns number - The current zoom factor.
|
||||
/// </summary>
|
||||
@@ -337,14 +476,14 @@ public class WebContents : ApiBase
|
||||
/// Returns number - The current zoom level.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task<int> GetZoomLevelAsync() => InvokeAsync<int>();
|
||||
public Task<double> GetZoomLevelAsync() => InvokeAsync<double>();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public void SetZoomLevel(int level)
|
||||
public void SetZoomLevel(double level)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("webContents-setZoomLevel", Id, level);
|
||||
}
|
||||
@@ -354,7 +493,7 @@ public class WebContents : ApiBase
|
||||
/// </summary>
|
||||
/// <param name="minimumLevel"></param>
|
||||
/// <param name="maximumLevel"></param>
|
||||
public Task SetVisualZoomLevelLimitsAsync(int minimumLevel, int maximumLevel)
|
||||
public Task SetVisualZoomLevelLimitsAsync(double minimumLevel, double maximumLevel)
|
||||
{
|
||||
var tcs = new TaskCompletionSource();
|
||||
|
||||
@@ -399,4 +538,178 @@ public class WebContents : ApiBase
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ namespace ElectronNET.API
|
||||
|
||||
internal WindowManager()
|
||||
{
|
||||
EnsureBrowserWindowClosedSubscription();
|
||||
}
|
||||
|
||||
internal static WindowManager Instance
|
||||
@@ -106,17 +107,6 @@ namespace ElectronNET.API
|
||||
tcs.SetResult(browserWindow);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Once<int[]>("BrowserWindowClosed", (ids) =>
|
||||
{
|
||||
for (int index = 0; index < _browserWindows.Count; index++)
|
||||
{
|
||||
if (!ids.Contains(_browserWindows[index].Id))
|
||||
{
|
||||
_browserWindows.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (loadUrl.Equals("http://localhost", StringComparison.OrdinalIgnoreCase) && ElectronNetRuntime.AspNetWebPort.HasValue)
|
||||
{
|
||||
loadUrl = $"{loadUrl}:{ElectronNetRuntime.AspNetWebPort}";
|
||||
@@ -130,11 +120,8 @@ namespace ElectronNET.API
|
||||
options.Height += 7;
|
||||
}
|
||||
|
||||
if (options.X == -1 && options.Y == -1)
|
||||
if (!options.X.HasValue && !options.Y.HasValue)
|
||||
{
|
||||
options.X = 0;
|
||||
options.Y = 0;
|
||||
|
||||
await BridgeConnector.Socket.Emit("createBrowserWindow", options, loadUrl).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
@@ -152,6 +139,49 @@ namespace ElectronNET.API
|
||||
return await tcs.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private readonly object _browserWindowSubscriptionSync = new();
|
||||
private bool _browserWindowClosedSubscribed;
|
||||
|
||||
private void EnsureBrowserWindowClosedSubscription()
|
||||
{
|
||||
if (_browserWindowClosedSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_browserWindowSubscriptionSync)
|
||||
{
|
||||
if (_browserWindowClosedSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BridgeConnector.Socket.On<int[]>("BrowserWindowClosed", HandleBrowserWindowClosed);
|
||||
BridgeConnector.Socket.On<int>("BrowserWindowRecreated", HandleBrowserWindowRecreated);
|
||||
_browserWindowClosedSubscribed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBrowserWindowRecreated(int id)
|
||||
{
|
||||
if (_browserWindows.All(window => window.Id != id))
|
||||
{
|
||||
_browserWindows.Add(new BrowserWindow(id));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBrowserWindowClosed(int[] ids)
|
||||
{
|
||||
if (ids == null || ids.Length == 0)
|
||||
{
|
||||
_browserWindows.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var existingIds = ids.ToHashSet();
|
||||
_browserWindows.RemoveAll(window => !existingIds.Contains(window.Id));
|
||||
}
|
||||
|
||||
private bool IsWindows10()
|
||||
{
|
||||
return RuntimeInformation.OSDescription.Contains("Windows 10");
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ElectronNET.API
|
||||
{
|
||||
internal static class BridgeConnector
|
||||
{
|
||||
public static SocketIoFacade Socket
|
||||
public static ISocketConnection Socket
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
56
src/ElectronNET.API/Bridge/ISocketConnection.cs
Normal file
56
src/ElectronNET.API/Bridge/ISocketConnection.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace ElectronNET.API;
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Common interface for communication facades.
|
||||
/// Provides methods for bidirectional communication between .NET and Electron.
|
||||
/// </summary>
|
||||
internal interface ISocketConnection : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised when the bridge connection is established.
|
||||
/// </summary>
|
||||
event EventHandler BridgeConnected;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the bridge connection is lost.
|
||||
/// </summary>
|
||||
event EventHandler BridgeDisconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Establishes the connection to Electron.
|
||||
/// </summary>
|
||||
void Connect();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a persistent event handler.
|
||||
/// </summary>
|
||||
void On(string eventName, Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a persistent event handler with a typed parameter.
|
||||
/// </summary>
|
||||
void On<T>(string eventName, Action<T> action);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a one-time event handler.
|
||||
/// </summary>
|
||||
void Once(string eventName, Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a one-time event handler with a typed parameter.
|
||||
/// </summary>
|
||||
void Once<T>(string eventName, Action<T> action);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an event handler.
|
||||
/// </summary>
|
||||
void Off(string eventName);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to Electron.
|
||||
/// </summary>
|
||||
Task Emit(string eventName, params object[] args);
|
||||
}
|
||||
@@ -3,19 +3,38 @@
|
||||
namespace ElectronNET.API;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.API.Serialization;
|
||||
using SocketIO.Serializer.SystemTextJson;
|
||||
using SocketIO = SocketIOClient.SocketIO;
|
||||
using SocketIOOptions = SocketIOClient.SocketIOOptions;
|
||||
|
||||
internal class SocketIoFacade
|
||||
internal class SocketIOConnection : ISocketConnection
|
||||
{
|
||||
private readonly SocketIO _socket;
|
||||
private readonly object _lockObj = new object();
|
||||
private bool _isDisposed;
|
||||
|
||||
public SocketIoFacade(string uri)
|
||||
public SocketIOConnection(string uri, string authorization)
|
||||
{
|
||||
_socket = new SocketIO(uri);
|
||||
var opts = new SocketIOOptions
|
||||
{
|
||||
// The bridge is a loopback IPC channel, so it must never go through a
|
||||
// (system) proxy - otherwise the handshake never reaches the socket server.
|
||||
Proxy = DirectProxy.Instance,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(authorization))
|
||||
{
|
||||
opts.ExtraHeaders = new Dictionary<string, string>
|
||||
{
|
||||
["authorization"] = authorization
|
||||
};
|
||||
}
|
||||
|
||||
_socket = new SocketIO(uri, opts);
|
||||
_socket.Serializer = new SystemTextJsonSerializer(ElectronJson.Options);
|
||||
// Use default System.Text.Json serializer from SocketIOClient.
|
||||
// Outgoing args are normalized to camelCase via SerializeArg in Emit.
|
||||
@@ -27,6 +46,8 @@ internal class SocketIoFacade
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
this.CheckDisposed();
|
||||
|
||||
_socket.OnError += (sender, e) => { Console.WriteLine($"BridgeConnector Error: {sender} {e}"); };
|
||||
|
||||
_socket.OnConnected += (_, _) =>
|
||||
@@ -46,6 +67,8 @@ internal class SocketIoFacade
|
||||
|
||||
public void On(string eventName, Action action)
|
||||
{
|
||||
this.CheckDisposed();
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
_socket.On(eventName, _ => { Task.Run(action); });
|
||||
@@ -54,6 +77,8 @@ internal class SocketIoFacade
|
||||
|
||||
public void On<T>(string eventName, Action<T> action)
|
||||
{
|
||||
this.CheckDisposed();
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
_socket.On(eventName, response =>
|
||||
@@ -66,6 +91,8 @@ internal class SocketIoFacade
|
||||
|
||||
public void Once(string eventName, Action action)
|
||||
{
|
||||
this.CheckDisposed();
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
_socket.On(eventName, _ =>
|
||||
@@ -78,6 +105,8 @@ internal class SocketIoFacade
|
||||
|
||||
public void Once<T>(string eventName, Action<T> action)
|
||||
{
|
||||
this.CheckDisposed();
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
_socket.On(eventName, (socketIoResponse) =>
|
||||
@@ -90,6 +119,11 @@ internal class SocketIoFacade
|
||||
|
||||
public void Off(string eventName)
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
_socket.Off(eventName);
|
||||
@@ -98,11 +132,44 @@ internal class SocketIoFacade
|
||||
|
||||
public async Task Emit(string eventName, params object[] args)
|
||||
{
|
||||
await _socket.EmitAsync(eventName, args).ConfigureAwait(false);
|
||||
if (!_isDisposed)
|
||||
{
|
||||
await _socket.EmitAsync(eventName, args).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void DisposeSocket()
|
||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_socket.Dispose();
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_isDisposed = true;
|
||||
_socket.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckDisposed()
|
||||
{
|
||||
if (this._isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(SocketIOConnection));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DirectProxy : IWebProxy
|
||||
{
|
||||
public static readonly DirectProxy Instance = new DirectProxy();
|
||||
|
||||
public ICredentials Credentials { get; set; }
|
||||
|
||||
public Uri GetProxy(Uri destination) => destination;
|
||||
|
||||
public bool IsBypassed(Uri host) => true;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
private readonly StringBuilder stdOut = new StringBuilder(4 * 1024);
|
||||
private readonly StringBuilder stdErr = new StringBuilder(4 * 1024);
|
||||
|
||||
public event EventHandler<string> LineReceived;
|
||||
|
||||
private volatile ManualResetEvent stdOutEvent;
|
||||
private volatile ManualResetEvent stdErrEvent;
|
||||
private volatile Stopwatch stopwatch;
|
||||
@@ -35,10 +37,18 @@
|
||||
public ProcessRunner(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
this.ExitWaitTimeout = DefaultExitWaitTimeout;
|
||||
}
|
||||
|
||||
public event EventHandler<EventArgs> ProcessExited;
|
||||
|
||||
/// <summary>Gets or sets the value <see cref="ExitWaitTimeout"/> is initialized with.</summary>
|
||||
public static int DefaultExitWaitTimeout { get; set; } = 5000;
|
||||
|
||||
/// <summary>Gets or sets the time in milliseconds to wait for the process and its I/O to finish
|
||||
/// after the exited event has been raised. Values of zero or less wait indefinitely.</summary>
|
||||
public int ExitWaitTimeout { get; set; }
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
private Process Process
|
||||
@@ -496,8 +506,17 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
var timeout = this.ExitWaitTimeout;
|
||||
|
||||
// This shouldn't throw here, but the mono process implementation doesn't always behave as it should.
|
||||
this.process.WaitForExit();
|
||||
if (timeout > 0)
|
||||
{
|
||||
this.process.WaitForExit(timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.process.WaitForExit();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -571,6 +590,7 @@
|
||||
if (e.Data != null)
|
||||
{
|
||||
Console.WriteLine("|| " + e.Data);
|
||||
LineReceived?.Invoke(this, e.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
91
src/ElectronNET.API/Converter/ReleaseNotesConverter.cs
Normal file
91
src/ElectronNET.API/Converter/ReleaseNotesConverter.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using ElectronNET.API.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ElectronNET.API.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the polymorphic shape of releaseNotes coming from electron-builder.
|
||||
/// Depending on the updater.fullChangelog setting, electron-builder sends:
|
||||
/// - null → when there are no notes
|
||||
/// - "some string" → plain string (FullChangelog = false, default)
|
||||
/// - ["note A", "note B"] → array of strings (after broken normalize in older TS)
|
||||
/// - [{ version, note }, ...] → array of objects (FullChangelog = true)
|
||||
/// All forms are normalised to ReleaseNoteInfo[] so the C# model stays clean.
|
||||
/// See: https://github.com/ElectronNET/Electron.NET/issues/1039
|
||||
/// </summary>
|
||||
public class ReleaseNotesConverter : JsonConverter<ReleaseNoteInfo[]>
|
||||
{
|
||||
// Ensure the converter is called even when the JSON token is null,
|
||||
// so we can return an empty array instead of null.
|
||||
public override bool HandleNull => true;
|
||||
|
||||
public override ReleaseNoteInfo[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.Null:
|
||||
return Array.Empty<ReleaseNoteInfo>();
|
||||
|
||||
case JsonTokenType.String:
|
||||
// Plain string: "Some release notes"
|
||||
return new[] { new ReleaseNoteInfo { Note = reader.GetString() } };
|
||||
|
||||
case JsonTokenType.StartArray:
|
||||
var list = new List<ReleaseNoteInfo>();
|
||||
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
// Array of strings: ["Note A", "Note B"]
|
||||
list.Add(new ReleaseNoteInfo { Note = reader.GetString() });
|
||||
}
|
||||
else if (reader.TokenType == JsonTokenType.StartObject)
|
||||
{
|
||||
// Array of objects: [{ "version": "1.0", "note": "..." }]
|
||||
var entry = JsonSerializer.Deserialize<ReleaseNoteInfo>(ref reader, options) ?? new ReleaseNoteInfo();
|
||||
list.Add(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Skip();
|
||||
}
|
||||
}
|
||||
return list.ToArray();
|
||||
|
||||
default:
|
||||
throw new JsonException($"Unexpected token {reader.TokenType} when reading releaseNotes.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ReleaseNoteInfo[] value, JsonSerializerOptions options)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
writer.WriteNullValue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.Length == 0)
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
writer.WriteEndArray();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteStartArray();
|
||||
foreach (var item in value)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
if (item.Version is not null)
|
||||
{
|
||||
writer.WriteString("version", item.Version);
|
||||
}
|
||||
writer.WriteString("note", item.Note);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<Import Project="..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
|
||||
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||
<PackageId>$(PackageNamePrefix).API</PackageId>
|
||||
<Title>$(PackageId)</Title>
|
||||
@@ -29,7 +29,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="SocketIOClient" Version="3.1.2" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.22" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.6" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.6" Condition=" '$(TargetFramework)' == 'net6.0' " />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
internal const int DefaultSocketPort = 8000;
|
||||
internal const int DefaultWebPort = 8001;
|
||||
internal const string ElectronPortArgumentName = "electronPort";
|
||||
internal const string ElectronHostArgumentName = "electronHost";
|
||||
internal const string ElectronPidArgumentName = "electronPID";
|
||||
internal const string ElectronAuthTokenArgumentName = "electronAuthToken";
|
||||
|
||||
/// <summary>Initializes the <see cref="ElectronNetRuntime"/> class.</summary>
|
||||
static ElectronNetRuntime()
|
||||
@@ -26,8 +28,15 @@
|
||||
|
||||
public static string ElectronExtraArguments { get; set; }
|
||||
|
||||
public static string ElectronAuthToken { get; internal set; }
|
||||
|
||||
public static int? ElectronSocketPort { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The loopback address the Electron socket bridge is listening on.
|
||||
/// </summary>
|
||||
public static string ElectronSocketHost { get; internal set; }
|
||||
|
||||
public static int? AspNetWebPort { get; internal set; }
|
||||
|
||||
public static StartupMethod StartupMethod { get; internal set; }
|
||||
@@ -47,9 +56,7 @@
|
||||
|
||||
internal static int? ElectronProcessId { get; set; }
|
||||
|
||||
internal static Func<Task> OnAppReadyCallback { get; set; }
|
||||
|
||||
internal static SocketIoFacade GetSocket()
|
||||
internal static ISocketConnection GetSocket()
|
||||
{
|
||||
return RuntimeControllerCore?.Socket;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{
|
||||
}
|
||||
|
||||
internal abstract SocketIoFacade Socket { get; }
|
||||
internal abstract ISocketConnection Socket { get; }
|
||||
|
||||
internal abstract ElectronProcessBase ElectronProcess { get; }
|
||||
|
||||
|
||||
@@ -13,13 +13,12 @@
|
||||
{
|
||||
private ElectronProcessBase electronProcess;
|
||||
private SocketBridgeService socketBridge;
|
||||
private int? port;
|
||||
|
||||
public RuntimeControllerDotNetFirst()
|
||||
{
|
||||
}
|
||||
|
||||
internal override SocketIoFacade Socket
|
||||
internal override ISocketConnection Socket
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -41,19 +40,13 @@
|
||||
var isUnPacked = ElectronNetRuntime.StartupMethod.IsUnpackaged();
|
||||
var electronBinaryName = ElectronNetRuntime.ElectronExecutable;
|
||||
var args = string.Format("{0} {1}", ElectronNetRuntime.ElectronExtraArguments, Environment.CommandLine).Trim();
|
||||
this.port = ElectronNetRuntime.ElectronSocketPort;
|
||||
|
||||
if (!this.port.HasValue)
|
||||
{
|
||||
this.port = PortHelper.GetFreePort(ElectronNetRuntime.DefaultSocketPort);
|
||||
ElectronNetRuntime.ElectronSocketPort = this.port;
|
||||
}
|
||||
var port = ElectronNetRuntime.ElectronSocketPort ?? 0;
|
||||
|
||||
Console.Error.WriteLine("[StartCore]: isUnPacked: {0}", isUnPacked);
|
||||
Console.Error.WriteLine("[StartCore]: electronBinaryName: {0}", electronBinaryName);
|
||||
Console.Error.WriteLine("[StartCore]: args: {0}", args);
|
||||
|
||||
this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, this.port.Value);
|
||||
this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, port);
|
||||
this.electronProcess.Ready += this.ElectronProcess_Ready;
|
||||
this.electronProcess.Stopped += this.ElectronProcess_Stopped;
|
||||
|
||||
@@ -63,8 +56,10 @@
|
||||
|
||||
private void ElectronProcess_Ready(object sender, EventArgs e)
|
||||
{
|
||||
var port = ElectronNetRuntime.ElectronSocketPort.Value;
|
||||
var token = ElectronNetRuntime.ElectronAuthToken;
|
||||
this.TransitionState(LifetimeState.Started);
|
||||
this.socketBridge = new SocketBridgeService(this.port!.Value);
|
||||
this.socketBridge = new SocketBridgeService(port, token);
|
||||
this.socketBridge.Ready += this.SocketBridge_Ready;
|
||||
this.socketBridge.Stopped += this.SocketBridge_Stopped;
|
||||
this.socketBridge.Start();
|
||||
|
||||
@@ -11,13 +11,12 @@
|
||||
{
|
||||
private ElectronProcessBase electronProcess;
|
||||
private SocketBridgeService socketBridge;
|
||||
private int? port;
|
||||
|
||||
public RuntimeControllerElectronFirst()
|
||||
{
|
||||
}
|
||||
|
||||
internal override SocketIoFacade Socket
|
||||
internal override ISocketConnection Socket
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -36,12 +35,8 @@
|
||||
|
||||
protected override Task StartCore()
|
||||
{
|
||||
this.port = ElectronNetRuntime.ElectronSocketPort;
|
||||
|
||||
if (!this.port.HasValue)
|
||||
{
|
||||
throw new Exception("No port has been specified by Electron!");
|
||||
}
|
||||
var port = ElectronNetRuntime.ElectronSocketPort.Value;
|
||||
var token = ElectronNetRuntime.ElectronAuthToken;
|
||||
|
||||
if (!ElectronNetRuntime.ElectronProcessId.HasValue)
|
||||
{
|
||||
@@ -49,7 +44,7 @@
|
||||
}
|
||||
|
||||
this.TransitionState(LifetimeState.Starting);
|
||||
this.socketBridge = new SocketBridgeService(this.port!.Value);
|
||||
this.socketBridge = new SocketBridgeService(port, token);
|
||||
this.socketBridge.Ready += this.SocketBridge_Ready;
|
||||
this.socketBridge.Stopped += this.SocketBridge_Stopped;
|
||||
this.socketBridge.Start();
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
public string ElectronVersion { get; internal set; }
|
||||
|
||||
public string ElectronRootDir { get; internal set; }
|
||||
|
||||
public string RuntimeIdentifier { get; internal set; }
|
||||
|
||||
public bool ElectronSingleInstance { get; internal set; }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace ElectronNET.Runtime.Helpers
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the directory that contains the Electron binary of a packaged application.
|
||||
/// </summary>
|
||||
internal static class ElectronRootDirResolver
|
||||
{
|
||||
/// <summary>The location of the Electron binary relative to the .NET application directory in the
|
||||
/// default (Electron-first) layout, where the .NET application is placed in 'resources/bin'.</summary>
|
||||
internal const string DefaultElectronRootDir = "../..";
|
||||
|
||||
/// <summary>Resolves the Electron root directory relative to the given .NET application directory.</summary>
|
||||
/// <param name="baseDirectory">The directory containing the .NET application.</param>
|
||||
/// <returns>The directory containing the Electron binary.</returns>
|
||||
public static DirectoryInfo Resolve(DirectoryInfo baseDirectory)
|
||||
{
|
||||
var rootDir = ElectronNetRuntime.BuildInfo?.ElectronRootDir;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rootDir))
|
||||
{
|
||||
rootDir = DefaultElectronRootDir;
|
||||
}
|
||||
|
||||
// An absolute ElectronRootDir is used as-is by Path.Combine.
|
||||
return new DirectoryInfo(Path.GetFullPath(Path.Combine(baseDirectory.FullName, rootDir)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,15 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
// Packaged, if the Electron binary is found where ElectronRootDir points to.
|
||||
var electronExecutable = ElectronNetRuntime.ElectronExecutable;
|
||||
|
||||
if (!string.IsNullOrEmpty(electronExecutable) &&
|
||||
File.Exists(Path.Combine(ElectronRootDirResolver.Resolve(dir).FullName, electronExecutable)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dir.GetDirectories().Any(e => e.Name == ".electron"))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
{
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.Common;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Launches and manages the Electron app process.
|
||||
@@ -15,6 +19,8 @@
|
||||
[Localizable(false)]
|
||||
internal class ElectronProcessActive : ElectronProcessBase
|
||||
{
|
||||
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at (\\S+) using ([a-f0-9]+)$");
|
||||
|
||||
private readonly bool isUnpackaged;
|
||||
private readonly string electronBinaryName;
|
||||
private readonly string extraArguments;
|
||||
@@ -82,7 +88,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = dir.Parent!.Parent!;
|
||||
dir = ElectronRootDirResolver.Resolve(dir);
|
||||
startCmd = Path.Combine(dir.FullName, this.electronBinaryName);
|
||||
args = $"-dotnetpacked -electronforcedport={this.socketPort:D} " + this.extraArguments;
|
||||
workingDir = dir.FullName;
|
||||
@@ -101,7 +107,6 @@
|
||||
}
|
||||
|
||||
var osPart = buildInfoRid.Split('-').First();
|
||||
|
||||
var mismatch = false;
|
||||
|
||||
switch (osPart)
|
||||
@@ -155,20 +160,61 @@
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StartInternal(string startCmd, string args, string directoriy)
|
||||
private async Task StartInternal(string startCmd, string args, string directory)
|
||||
{
|
||||
var tcs = new TaskCompletionSource();
|
||||
using var cts = new CancellationTokenSource(2 * 60_000); // cancel after 2 minutes
|
||||
using var _ = cts.Token.Register(() =>
|
||||
{
|
||||
// Time is over - let's kill the process and move on
|
||||
this.process.Cancel();
|
||||
// We don't want to raise exceptions here - just pass the barrier
|
||||
tcs.TrySetResult();
|
||||
});
|
||||
|
||||
void Read_SocketIO_Parameters(object sender, string line)
|
||||
{
|
||||
// Look for "Electron Socket: listening on port %s at ..."
|
||||
var match = extractor.Match(line);
|
||||
|
||||
if (match?.Success ?? false)
|
||||
{
|
||||
var port = int.Parse(match.Groups[1].Value);
|
||||
var host = match.Groups[2].Value;
|
||||
var token = match.Groups[3].Value;
|
||||
|
||||
this.process.LineReceived -= Read_SocketIO_Parameters;
|
||||
ElectronNetRuntime.ElectronAuthToken = token;
|
||||
ElectronNetRuntime.ElectronSocketPort = port;
|
||||
ElectronNetRuntime.ElectronSocketHost = host;
|
||||
tcs.SetResult();
|
||||
}
|
||||
}
|
||||
|
||||
void Monitor_SocketIO_Failure(object sender, EventArgs e)
|
||||
{
|
||||
// We don't want to raise exceptions here - just pass the barrier
|
||||
if (tcs.Task.IsCompleted)
|
||||
{
|
||||
this.Process_Exited(sender, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
tcs.TrySetResult();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(10.ms()).ConfigureAwait(false);
|
||||
|
||||
Console.Error.WriteLine("[StartInternal]: startCmd: {0}", startCmd);
|
||||
Console.Error.WriteLine("[StartInternal]: args: {0}", args);
|
||||
|
||||
this.process = new ProcessRunner("ElectronRunner");
|
||||
this.process.ProcessExited += this.Process_Exited;
|
||||
this.process.Run(startCmd, args, directoriy);
|
||||
this.process.ProcessExited += Monitor_SocketIO_Failure;
|
||||
this.process.LineReceived += Read_SocketIO_Parameters;
|
||||
this.process.Run(startCmd, args, directory);
|
||||
|
||||
await Task.Delay(500.ms()).ConfigureAwait(false);
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
|
||||
Console.Error.WriteLine("[StartInternal]: after run:");
|
||||
|
||||
@@ -178,11 +224,11 @@
|
||||
Console.Error.WriteLine("[StartInternal]: Process is not running: " + this.process.StandardOutput);
|
||||
|
||||
Task.Run(() => this.TransitionState(LifetimeState.Stopped));
|
||||
|
||||
throw new Exception("Failed to launch the Electron process.");
|
||||
}
|
||||
|
||||
this.TransitionState(LifetimeState.Ready);
|
||||
else
|
||||
{
|
||||
this.TransitionState(LifetimeState.Ready);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -8,22 +8,36 @@
|
||||
internal class SocketBridgeService : LifetimeServiceBase
|
||||
{
|
||||
private readonly int socketPort;
|
||||
private readonly string authorization;
|
||||
private readonly string socketUrl;
|
||||
private SocketIoFacade socket;
|
||||
private SocketIOConnection socket;
|
||||
|
||||
public SocketBridgeService(int socketPort)
|
||||
public SocketBridgeService(int socketPort, string authorization)
|
||||
{
|
||||
this.socketPort = socketPort;
|
||||
this.socketUrl = $"http://localhost:{this.socketPort}";
|
||||
this.authorization = authorization;
|
||||
this.socketUrl = $"http://{FormatHost(ElectronNetRuntime.ElectronSocketHost)}:{this.socketPort}";
|
||||
}
|
||||
|
||||
// The Electron host reports the loopback address it is actually listening on; only
|
||||
// when it is unknown we have to fall back to the ambiguous hostname.
|
||||
private static string FormatHost(string socketHost)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(socketHost))
|
||||
{
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
return socketHost.Contains(':') ? $"[{socketHost}]" : socketHost;
|
||||
}
|
||||
|
||||
public int SocketPort => this.socketPort;
|
||||
|
||||
internal SocketIoFacade Socket => this.socket;
|
||||
internal SocketIOConnection Socket => this.socket;
|
||||
|
||||
protected override Task StartCore()
|
||||
{
|
||||
this.socket = new SocketIoFacade(this.socketUrl);
|
||||
this.socket = new SocketIOConnection(this.socketUrl, this.authorization);
|
||||
this.socket.BridgeConnected += this.Socket_BridgeConnected;
|
||||
this.socket.BridgeDisconnected += this.Socket_BridgeDisconnected;
|
||||
Task.Run(this.Connect);
|
||||
@@ -33,7 +47,7 @@
|
||||
|
||||
protected override Task StopCore()
|
||||
{
|
||||
this.socket.DisposeSocket();
|
||||
this.socket.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +94,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
var pidArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronPidArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
var hostArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronHostArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hostArg != null)
|
||||
{
|
||||
var parts = hostArg.Split('=', StringSplitOptions.TrimEntries);
|
||||
|
||||
if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]))
|
||||
{
|
||||
ElectronNetRuntime.ElectronSocketHost = parts[1];
|
||||
|
||||
Console.WriteLine("Use Electron Host: " + parts[1]);
|
||||
}
|
||||
}
|
||||
var pidArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronPidArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
if (pidArg != null)
|
||||
{
|
||||
var parts = pidArg.Split('=', StringSplitOptions.TrimEntries);
|
||||
@@ -106,6 +118,20 @@
|
||||
Console.WriteLine("Electron Process ID: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
var authTokenArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronAuthTokenArgumentName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (authTokenArg != null)
|
||||
{
|
||||
var parts = authTokenArg.Split('=', StringSplitOptions.TrimEntries);
|
||||
|
||||
if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]))
|
||||
{
|
||||
var result = parts[1];
|
||||
ElectronNetRuntime.ElectronAuthToken = result;
|
||||
Console.WriteLine("Use Auth Token: " + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetElectronExecutable()
|
||||
@@ -152,6 +178,7 @@
|
||||
{
|
||||
buildInfo.ElectronExecutable = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.ElectronExecutable))?.Value;
|
||||
buildInfo.ElectronVersion = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.ElectronVersion))?.Value;
|
||||
buildInfo.ElectronRootDir = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.ElectronRootDir))?.Value;
|
||||
buildInfo.RuntimeIdentifier = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.RuntimeIdentifier))?.Value;
|
||||
buildInfo.Title = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.Title))?.Value;
|
||||
buildInfo.Version = attributes.FirstOrDefault(e => e.Key == nameof(buildInfo.Version))?.Value;
|
||||
|
||||
@@ -44,5 +44,98 @@
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core application and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="WebApplicationBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process, forwarded to Electron.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="WebApplicationBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// var builder = WebApplication.CreateBuilder(args)
|
||||
/// .UseElectron(args, async (processArgs) =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
///
|
||||
/// var app = builder.Build();
|
||||
/// app.MapRazorPages();
|
||||
/// app.Run();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static WebApplicationBuilder UseElectron(this WebApplicationBuilder builder, string[] args, Func<string[], Task> onAppReadyCallback)
|
||||
{
|
||||
builder.WebHost.UseElectron(args, onAppReadyCallback);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core application and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="WebApplicationBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process, forwarded to Electron.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="WebApplicationBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// var builder = WebApplication.CreateBuilder(args)
|
||||
/// .UseElectron(args, async (serviceProvider) =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
///
|
||||
/// var app = builder.Build();
|
||||
/// app.MapRazorPages();
|
||||
/// app.Run();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static WebApplicationBuilder UseElectron(this WebApplicationBuilder builder, string[] args, Func<IServiceProvider, Task> onAppReadyCallback)
|
||||
{
|
||||
builder.WebHost.UseElectron(args, onAppReadyCallback);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core application and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="WebApplicationBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process, forwarded to Electron.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="WebApplicationBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// var builder = WebApplication.CreateBuilder(args)
|
||||
/// .UseElectron(args, async (serviceProvider, processArgs) =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
///
|
||||
/// var app = builder.Build();
|
||||
/// app.MapRazorPages();
|
||||
/// app.Run();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static WebApplicationBuilder UseElectron(this WebApplicationBuilder builder, string[] args, Func<IServiceProvider, string[], Task> onAppReadyCallback)
|
||||
{
|
||||
builder.WebHost.UseElectron(args, onAppReadyCallback);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.AspNet;
|
||||
@@ -10,6 +11,7 @@
|
||||
using ElectronNET.Runtime.Helpers;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="IWebHostBuilder"/> to enable Electron.NET
|
||||
@@ -59,30 +61,181 @@
|
||||
/// </example>
|
||||
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<Task> onAppReadyCallback)
|
||||
{
|
||||
ElectronNetRuntime.OnAppReadyCallback = onAppReadyCallback;
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddSingleton<IAppReadyCallbackResolver>(_ => new AppReadyCallbackResolver(onAppReadyCallback));
|
||||
});
|
||||
|
||||
return UseElectronCore(builder, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core web host and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IWebHostBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="IWebHostBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// using Microsoft.AspNetCore.Hosting;
|
||||
/// using Microsoft.Extensions.Hosting;
|
||||
/// using ElectronNET.API;
|
||||
///
|
||||
/// public class Program
|
||||
/// {
|
||||
/// public static void Main(string[] args)
|
||||
/// {
|
||||
/// Host.CreateDefaultBuilder(args)
|
||||
/// .ConfigureWebHostDefaults(webBuilder =>
|
||||
/// {
|
||||
/// webBuilder.UseStartup<Startup>();
|
||||
/// webBuilder.UseElectron(args, async (processArgs) =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
/// })
|
||||
/// .Build()
|
||||
/// .Run();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<string[], Task> onAppReadyCallback)
|
||||
{
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddSingleton<IAppReadyCallbackResolver>(_ => new AppReadyCallbackResolver(args, onAppReadyCallback));
|
||||
});
|
||||
|
||||
return UseElectronCore(builder, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core web host and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IWebHostBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="IWebHostBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// using Microsoft.AspNetCore.Hosting;
|
||||
/// using Microsoft.Extensions.Hosting;
|
||||
/// using ElectronNET.API;
|
||||
///
|
||||
/// public class Program
|
||||
/// {
|
||||
/// public static void Main(string[] args)
|
||||
/// {
|
||||
/// Host.CreateDefaultBuilder(args)
|
||||
/// .ConfigureWebHostDefaults(webBuilder =>
|
||||
/// {
|
||||
/// webBuilder.UseStartup<Startup>();
|
||||
/// webBuilder.UseElectron(args, async (serviceProvider) =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
/// })
|
||||
/// .Build()
|
||||
/// .Run();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<IServiceProvider, Task> onAppReadyCallback)
|
||||
{
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddSingleton<IAppReadyCallbackResolver>(provider => new AppReadyCallbackResolver(provider, onAppReadyCallback));
|
||||
});
|
||||
|
||||
return UseElectronCore(builder, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core web host and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IWebHostBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="IWebHostBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// using Microsoft.AspNetCore.Hosting;
|
||||
/// using Microsoft.Extensions.Hosting;
|
||||
/// using ElectronNET.API;
|
||||
///
|
||||
/// public class Program
|
||||
/// {
|
||||
/// public static void Main(string[] args)
|
||||
/// {
|
||||
/// Host.CreateDefaultBuilder(args)
|
||||
/// .ConfigureWebHostDefaults(webBuilder =>
|
||||
/// {
|
||||
/// webBuilder.UseStartup<Startup>();
|
||||
/// webBuilder.UseElectron(args, async (serviceProvider, processArgs) =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
/// })
|
||||
/// .Build()
|
||||
/// .Run();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<IServiceProvider, string[], Task> onAppReadyCallback)
|
||||
{
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddSingleton<IAppReadyCallbackResolver>(provider => new AppReadyCallbackResolver(provider, args, onAppReadyCallback));
|
||||
});
|
||||
|
||||
return UseElectronCore(builder, args);
|
||||
}
|
||||
|
||||
private static IWebHostBuilder UseElectronCore(IWebHostBuilder builder, string[] args)
|
||||
{
|
||||
// no matter how this is set - let's unset to prevent Electron not starting as expected
|
||||
// e.g., VS Code sets this env variable, but this will cause `require("electron")` to not
|
||||
// work as expected, see issue #952
|
||||
Environment.SetEnvironmentVariable("ELECTRON_RUN_AS_NODE", null);
|
||||
|
||||
var webPort = PortHelper.GetFreePort(ElectronNetRuntime.AspNetWebPort ?? ElectronNetRuntime.DefaultWebPort);
|
||||
ElectronNetRuntime.AspNetWebPort = webPort;
|
||||
var webPort = ElectronNetRuntime.AspNetWebPort ?? 0;
|
||||
|
||||
// check for the content folder if its exists in base director otherwise no need to include
|
||||
// It was used before because we are publishing the project which copies everything to bin folder and contentroot wwwroot was folder there.
|
||||
// now we have implemented the live reload if app is run using /watch then we need to use the default project path.
|
||||
if (Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}\\wwwroot"))
|
||||
// In packaged mode, static content is deployed alongside the app binaries, so we must
|
||||
// point content root to the process base directory. In unpackaged/watch scenarios we
|
||||
// keep the default project content root to preserve live reload behavior.
|
||||
var isPackagedStartup = ElectronNetRuntime.StartupMethod == StartupMethod.PackagedElectronFirst ||
|
||||
ElectronNetRuntime.StartupMethod == StartupMethod.PackagedDotnetFirst;
|
||||
|
||||
// For port 0 (dynamic port assignment), Kestrel requires binding to specific IP (127.0.0.1) not localhost
|
||||
var host = webPort == 0 ? "127.0.0.1" : "localhost";
|
||||
|
||||
if (isPackagedStartup)
|
||||
{
|
||||
builder = builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
|
||||
.UseUrls("http://localhost:" + webPort);
|
||||
.UseUrls($"http://{host}:{webPort}");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = builder.UseUrls("http://localhost:" + webPort);
|
||||
builder = builder.UseUrls($"http://{host}:{webPort}");
|
||||
}
|
||||
|
||||
builder = builder.ConfigureServices(services =>
|
||||
builder = builder.ConfigureServices((context, services) =>
|
||||
{
|
||||
services.AddTransient<IStartupFilter, ServerReadyStartupFilter>();
|
||||
services.AddSingleton<AspNetLifetimeAdapter>();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Import Project="..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
|
||||
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||
<PackageId>$(PackageNamePrefix).AspNet</PackageId>
|
||||
<Title>$(PackageId)</Title>
|
||||
@@ -13,23 +13,6 @@
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<Nullable>disable</Nullable>
|
||||
<RootNamespace>ElectronNET</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net10.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
namespace ElectronNET.AspNet.Middleware
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ElectronNET.AspNet.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Middleware that validates authentication for all Electron requests.
|
||||
/// Checks for authentication cookie or token query parameter on first request.
|
||||
/// Sets HttpOnly cookie for subsequent requests.
|
||||
///
|
||||
/// Security Model:
|
||||
/// - First request includes token as query parameter (?token=guid)
|
||||
/// - Middleware validates token and sets secure HttpOnly cookie
|
||||
/// - Subsequent requests use cookie (no token in URL)
|
||||
/// - HTTP endpoints protected
|
||||
/// </summary>
|
||||
public class ElectronAuthenticationMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IElectronAuthenticationService _authService;
|
||||
private readonly ILogger<ElectronAuthenticationMiddleware> _logger;
|
||||
private const string AuthCookieName = "ElectronAuth";
|
||||
|
||||
public ElectronAuthenticationMiddleware(
|
||||
RequestDelegate next,
|
||||
IElectronAuthenticationService authService,
|
||||
ILogger<ElectronAuthenticationMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_authService = authService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
var path = context.Request.Path.Value;
|
||||
|
||||
// Check if authentication cookie exists
|
||||
var authCookie = context.Request.Cookies[AuthCookieName];
|
||||
|
||||
if (!string.IsNullOrEmpty(authCookie))
|
||||
{
|
||||
// Cookie present - validate it
|
||||
if (_authService.ValidateToken(authCookie))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid cookie - reject
|
||||
_logger.LogWarning("Authentication failed: Invalid cookie for path {Path} from {RemoteIp}", path, context.Connection.RemoteIpAddress);
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("Unauthorized: Invalid authentication");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No cookie - check for token in query string (first-time authentication)
|
||||
var token = context.Request.Query["token"].ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
if (_authService.ValidateToken(token))
|
||||
{
|
||||
// Valid token - set cookie for future requests
|
||||
_logger.LogInformation("Authentication successful: Setting cookie for path {Path}", path);
|
||||
|
||||
context.Response.Cookies.Append(AuthCookieName, token, new CookieOptions
|
||||
{
|
||||
HttpOnly = true, // Prevent JavaScript access (XSS protection)
|
||||
SameSite = SameSiteMode.Strict, // CSRF protection
|
||||
Path = "/", // Valid for all routes
|
||||
Secure = false, // False because localhost is HTTP
|
||||
IsEssential = true // Required for app to function
|
||||
});
|
||||
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid token - reject
|
||||
_logger.LogWarning("Authentication failed: Invalid token (prefix: {TokenPrefix}...) for path {Path} from {RemoteIp}", token.Length > 8 ? token.Substring(0, 8) : token, path, context.Connection.RemoteIpAddress);
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("Unauthorized: Invalid authentication");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Neither cookie nor valid token present - reject
|
||||
_logger.LogWarning("Authentication failed: No cookie or token provided for path {Path} from {RemoteIp}", path, context.Connection.RemoteIpAddress);
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("Unauthorized: Authentication required");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.AspNet.Services;
|
||||
using ElectronNET.Common;
|
||||
using ElectronNET.Runtime.Controllers;
|
||||
using ElectronNET.Runtime.Data;
|
||||
@@ -10,12 +15,18 @@
|
||||
|
||||
internal abstract class RuntimeControllerAspNetBase : RuntimeControllerBase
|
||||
{
|
||||
private readonly IServer server;
|
||||
private readonly AspNetLifetimeAdapter aspNetLifetimeAdapter;
|
||||
private readonly IAppReadyCallbackResolver callbackResolver;
|
||||
private readonly IElectronAuthenticationService authenticationService;
|
||||
private SocketBridgeService socketBridge;
|
||||
|
||||
protected RuntimeControllerAspNetBase(AspNetLifetimeAdapter aspNetLifetimeAdapter)
|
||||
protected RuntimeControllerAspNetBase(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IAppReadyCallbackResolver callbackResolver, IElectronAuthenticationService authenticationService = null)
|
||||
{
|
||||
this.server = server;
|
||||
this.aspNetLifetimeAdapter = aspNetLifetimeAdapter;
|
||||
this.callbackResolver = callbackResolver;
|
||||
this.authenticationService = authenticationService;
|
||||
this.aspNetLifetimeAdapter.Ready += this.AspNetLifetimeAdapter_Ready;
|
||||
this.aspNetLifetimeAdapter.Stopping += this.AspNetLifetimeAdapter_Stopping;
|
||||
this.aspNetLifetimeAdapter.Stopped += this.AspNetLifetimeAdapter_Stopped;
|
||||
@@ -25,7 +36,7 @@
|
||||
|
||||
internal override SocketBridgeService SocketBridge => this.socketBridge;
|
||||
|
||||
internal override SocketIoFacade Socket
|
||||
internal override ISocketConnection Socket
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -38,9 +49,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateSocketBridge(int port)
|
||||
protected void CreateSocketBridge(int port, string authorization)
|
||||
{
|
||||
this.socketBridge = new SocketBridgeService(port);
|
||||
this.socketBridge = new SocketBridgeService(port, authorization);
|
||||
this.socketBridge.Ready += this.SocketBridge_Ready;
|
||||
this.socketBridge.Stopped += this.SocketBridge_Stopped;
|
||||
this.socketBridge.Start();
|
||||
@@ -52,6 +63,15 @@
|
||||
this.ElectronProcess.IsReady() &&
|
||||
this.aspNetLifetimeAdapter.IsReady())
|
||||
{
|
||||
var token = ElectronNetRuntime.ElectronAuthToken;
|
||||
var serverAddressesFeature = this.server.Features.Get<IServerAddressesFeature>();
|
||||
var address = serverAddressesFeature.Addresses.First();
|
||||
var uri = new Uri(address);
|
||||
|
||||
// Only if somebody registered an IElectronAuthenticationService service - otherwise we do not care
|
||||
this.authenticationService?.SetExpectedToken(token);
|
||||
ElectronNetRuntime.AspNetWebPort = uri.Port;
|
||||
|
||||
this.TransitionState(LifetimeState.Ready);
|
||||
Task.Run(this.RunReadyCallback);
|
||||
}
|
||||
@@ -112,20 +132,20 @@
|
||||
|
||||
private async Task RunReadyCallback()
|
||||
{
|
||||
if (ElectronNetRuntime.OnAppReadyCallback == null)
|
||||
if (!callbackResolver.HasCallback)
|
||||
{
|
||||
Console.WriteLine("Warning: Non OnReadyCallback provided in UseElectron() setup.");
|
||||
Console.WriteLine("Warning: No OnReadyCallback provided in UseElectron() setup.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ElectronNetRuntime.OnAppReadyCallback().ConfigureAwait(false);
|
||||
await callbackResolver.Invoke().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Exception while executing OnAppReadyCallback. Stopping...\n" + ex);
|
||||
this.Stop();
|
||||
_ = this.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using ElectronNET.AspNet.Services;
|
||||
using ElectronNET.Common;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Helpers;
|
||||
@@ -10,9 +13,8 @@
|
||||
internal class RuntimeControllerAspNetDotnetFirst : RuntimeControllerAspNetBase
|
||||
{
|
||||
private ElectronProcessBase electronProcess;
|
||||
private int? port;
|
||||
|
||||
public RuntimeControllerAspNetDotnetFirst(AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(aspNetLifetimeAdapter)
|
||||
public RuntimeControllerAspNetDotnetFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IAppReadyCallbackResolver callbackResolver, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter, callbackResolver, authenticationService)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -23,15 +25,9 @@
|
||||
var isUnPacked = ElectronNetRuntime.StartupMethod.IsUnpackaged();
|
||||
var electronBinaryName = ElectronNetRuntime.ElectronExecutable;
|
||||
var args = Environment.CommandLine;
|
||||
this.port = ElectronNetRuntime.ElectronSocketPort;
|
||||
var port = ElectronNetRuntime.ElectronSocketPort ?? 0;
|
||||
|
||||
if (!this.port.HasValue)
|
||||
{
|
||||
this.port = PortHelper.GetFreePort(ElectronNetRuntime.DefaultSocketPort);
|
||||
ElectronNetRuntime.ElectronSocketPort = this.port;
|
||||
}
|
||||
|
||||
this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, this.port.Value);
|
||||
this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, port);
|
||||
this.electronProcess.Ready += this.ElectronProcess_Ready;
|
||||
this.electronProcess.Stopped += this.ElectronProcess_Stopped;
|
||||
|
||||
@@ -46,8 +42,10 @@
|
||||
|
||||
private void ElectronProcess_Ready(object sender, EventArgs e)
|
||||
{
|
||||
var port = ElectronNetRuntime.ElectronSocketPort.Value;
|
||||
var token = ElectronNetRuntime.ElectronAuthToken;
|
||||
this.TransitionState(LifetimeState.Started);
|
||||
this.CreateSocketBridge(this.port!.Value);
|
||||
this.CreateSocketBridge(port, token);
|
||||
}
|
||||
|
||||
private void ElectronProcess_Stopped(object sender, EventArgs e)
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using ElectronNET.AspNet.Services;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Services.ElectronProcess;
|
||||
|
||||
internal class RuntimeControllerAspNetElectronFirst : RuntimeControllerAspNetBase
|
||||
{
|
||||
private ElectronProcessBase electronProcess;
|
||||
private int? port;
|
||||
|
||||
public RuntimeControllerAspNetElectronFirst(AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(aspNetLifetimeAdapter)
|
||||
public RuntimeControllerAspNetElectronFirst(IServer server, AspNetLifetimeAdapter aspNetLifetimeAdapter, IAppReadyCallbackResolver callbackResolver, IElectronAuthenticationService authenticationService = null) : base(server, aspNetLifetimeAdapter, callbackResolver, authenticationService)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -18,19 +19,15 @@
|
||||
|
||||
protected override Task StartCore()
|
||||
{
|
||||
this.port = ElectronNetRuntime.ElectronSocketPort;
|
||||
|
||||
if (!this.port.HasValue)
|
||||
{
|
||||
throw new Exception("No port has been specified by Electron!");
|
||||
}
|
||||
var port = ElectronNetRuntime.ElectronSocketPort.Value;
|
||||
var token = ElectronNetRuntime.ElectronAuthToken;
|
||||
|
||||
if (!ElectronNetRuntime.ElectronProcessId.HasValue)
|
||||
{
|
||||
throw new Exception("No electronPID has been specified by Electron!");
|
||||
}
|
||||
|
||||
this.CreateSocketBridge(this.port!.Value);
|
||||
this.CreateSocketBridge(port, token);
|
||||
|
||||
this.electronProcess = new ElectronProcessPassive(ElectronNetRuntime.ElectronProcessId.Value);
|
||||
this.electronProcess.Stopped += this.ElectronProcess_Stopped;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
internal class AppReadyCallbackResolver : IAppReadyCallbackResolver
|
||||
{
|
||||
private readonly Func<Task> _callback;
|
||||
|
||||
public AppReadyCallbackResolver()
|
||||
{ }
|
||||
|
||||
public AppReadyCallbackResolver(Func<Task> callback)
|
||||
{
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
public AppReadyCallbackResolver(string[] args, Func<string[], Task> callback)
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
_callback = () => callback.Invoke(args);
|
||||
}
|
||||
}
|
||||
|
||||
public AppReadyCallbackResolver(IServiceProvider serviceProvider, Func<IServiceProvider, Task> callback)
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
_callback = () => callback.Invoke(serviceProvider);
|
||||
}
|
||||
}
|
||||
|
||||
public AppReadyCallbackResolver(IServiceProvider serviceProvider, string[] args, Func<IServiceProvider, string[], Task> callback)
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
_callback = () => callback.Invoke(serviceProvider, args);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasCallback => _callback != null;
|
||||
|
||||
public Task Invoke() => _callback?.Invoke() ?? Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
internal interface IAppReadyCallbackResolver
|
||||
{
|
||||
public bool HasCallback { get; }
|
||||
|
||||
public Task Invoke();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
namespace ElectronNET.AspNet.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of authentication service for Electron clients.
|
||||
/// Stores and validates the authentication token to ensure only the spawned Electron process can connect.
|
||||
/// </summary>
|
||||
public class ElectronAuthenticationService : IElectronAuthenticationService
|
||||
{
|
||||
private string _expectedToken;
|
||||
private readonly object _lock = new object();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetExpectedToken(string token)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_expectedToken = token;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ValidateToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return false;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_expectedToken))
|
||||
return false;
|
||||
|
||||
// Constant-time comparison to prevent timing attacks
|
||||
return ConstantTimeEquals(token, _expectedToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs constant-time string comparison to prevent timing attacks.
|
||||
/// </summary>
|
||||
private static bool ConstantTimeEquals(string a, string b)
|
||||
{
|
||||
if (a == null || b == null || a.Length != b.Length)
|
||||
return false;
|
||||
|
||||
var result = 0;
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
result |= a[i] ^ b[i];
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace ElectronNET.AspNet.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for validating authentication tokens from Electron clients.
|
||||
/// Used to ensure only the Electron process spawned by this .NET instance can connect.
|
||||
/// </summary>
|
||||
public interface IElectronAuthenticationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the expected authentication token for this instance.
|
||||
/// Should be called when launching Electron with the generated token.
|
||||
/// </summary>
|
||||
/// <param name="token">The authentication token</param>
|
||||
void SetExpectedToken(string token);
|
||||
|
||||
/// <summary>
|
||||
/// Validates an incoming token against the expected token.
|
||||
/// Uses constant-time comparison to prevent timing attacks.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to validate</param>
|
||||
/// <returns>True if token is valid, false otherwise</returns>
|
||||
bool ValidateToken(string token);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@
|
||||
<ProjectReference Include="..\ElectronNET.API\ElectronNET.API.csproj" Condition="$(ElectronNetDevMode)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.4.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\ElectronNET\build\ElectronNET.Core.targets" Condition="$(ElectronNetDevMode)" />
|
||||
|
||||
@@ -39,6 +39,15 @@
|
||||
"undici-types": "~7.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -101,20 +110,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
|
||||
"integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
|
||||
"version": "6.6.8",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.8.tgz",
|
||||
"integrity": "sha512-2agL3ueZhqxoVrfmntO8yuVj+uNSlIOnhykYHk3Cq0ShYPdUjjUiSJrQvXjq01I9jAuI0Zl2YO8Evv5Mqytm5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1"
|
||||
"ws": "~8.20.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
@@ -129,6 +139,23 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -193,28 +220,62 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.7.tgz",
|
||||
"integrity": "sha512-e0LyK91f3cUxTmv95/KzoLg47+zF+s/sbxRGDNsyG4dmIP8ZSX8ax6byOxfJXeNNtS/8AZlfD+uP7gBeR7DLlg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.4",
|
||||
"ws": "~8.17.1"
|
||||
"debug": "~4.4.1",
|
||||
"ws": "~8.20.1"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"version": "4.2.7",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
|
||||
"integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
"debug": "~4.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser/node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
@@ -245,9 +306,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"version": "8.20.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz",
|
||||
"integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.4110890">
|
||||
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.6537497">
|
||||
<ItemGroup>
|
||||
<None Include=".vscode\tasks.json" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
"use strict";
|
||||
let isQuitWindowAllClosed = true, electronSocket;
|
||||
let isQuitWindowAllClosed = true;
|
||||
let electronSocket;
|
||||
let appWindowAllClosedEventId;
|
||||
module.exports = (socket, app) => {
|
||||
electronSocket = socket;
|
||||
// By default, quit when all windows are closed
|
||||
app.on('window-all-closed', () => {
|
||||
app.on("window-all-closed", () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin' && isQuitWindowAllClosed) {
|
||||
if (process.platform !== "darwin" && isQuitWindowAllClosed) {
|
||||
app.quit();
|
||||
}
|
||||
else if (appWindowAllClosedEventId) {
|
||||
@@ -15,220 +16,224 @@ module.exports = (socket, app) => {
|
||||
// - OR -
|
||||
// If the user has indicated NOT to quit when all windows are closed,
|
||||
// emit the event.
|
||||
electronSocket.emit('app-window-all-closed' + appWindowAllClosedEventId);
|
||||
electronSocket.emit("app-window-all-closed" + appWindowAllClosedEventId);
|
||||
}
|
||||
});
|
||||
socket.on('quit-app-window-all-closed', (quit) => {
|
||||
socket.on("quit-app-window-all-closed", (quit) => {
|
||||
isQuitWindowAllClosed = quit;
|
||||
});
|
||||
socket.on('register-app-window-all-closed', (id) => {
|
||||
socket.on("register-app-window-all-closed", (id) => {
|
||||
appWindowAllClosedEventId = id;
|
||||
});
|
||||
socket.on('register-app-before-quit', (id) => {
|
||||
app.on('before-quit', (event) => {
|
||||
socket.on("register-app-before-quit", (id) => {
|
||||
app.on("before-quit", (event) => {
|
||||
event.preventDefault();
|
||||
electronSocket.emit('app-before-quit' + id);
|
||||
electronSocket.emit("app-before-quit" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-app-will-quit', (id) => {
|
||||
app.on('will-quit', (event) => {
|
||||
socket.on("register-app-will-quit", (id) => {
|
||||
app.on("will-quit", (event) => {
|
||||
event.preventDefault();
|
||||
electronSocket.emit('app-will-quit' + id);
|
||||
electronSocket.emit("app-will-quit" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-app-browser-window-blur', (id) => {
|
||||
app.on('browser-window-blur', () => {
|
||||
electronSocket.emit('app-browser-window-blur' + id);
|
||||
socket.on("register-app-browser-window-blur", (id) => {
|
||||
app.on("browser-window-blur", () => {
|
||||
electronSocket.emit("app-browser-window-blur" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-app-browser-window-focus', (id) => {
|
||||
app.on('browser-window-focus', () => {
|
||||
electronSocket.emit('app-browser-window-focus' + id);
|
||||
socket.on("register-app-browser-window-focus", (id) => {
|
||||
app.on("browser-window-focus", () => {
|
||||
electronSocket.emit("app-browser-window-focus" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-app-browser-window-created', (id) => {
|
||||
app.on('browser-window-created', () => {
|
||||
electronSocket.emit('app-browser-window-created' + id);
|
||||
socket.on("register-app-browser-window-created", (id) => {
|
||||
app.on("browser-window-created", () => {
|
||||
electronSocket.emit("app-browser-window-created" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-app-web-contents-created', (id) => {
|
||||
app.on('web-contents-created', () => {
|
||||
electronSocket.emit('app-web-contents-created' + id);
|
||||
socket.on("register-app-web-contents-created", (id) => {
|
||||
app.on("web-contents-created", () => {
|
||||
electronSocket.emit("app-web-contents-created" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-app-accessibility-support-changed', (id) => {
|
||||
app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => {
|
||||
electronSocket.emit('app-accessibility-support-changed' + id, accessibilitySupportEnabled);
|
||||
socket.on("register-app-accessibility-support-changed", (id) => {
|
||||
app.on("accessibility-support-changed", (event, accessibilitySupportEnabled) => {
|
||||
electronSocket.emit("app-accessibility-support-changed" + id, accessibilitySupportEnabled);
|
||||
});
|
||||
});
|
||||
socket.on('appQuit', () => {
|
||||
socket.on("appQuit", () => {
|
||||
app.quit();
|
||||
});
|
||||
socket.on('appExit', (exitCode = 0) => {
|
||||
socket.on("appExit", (exitCode = 0) => {
|
||||
app.exit(exitCode);
|
||||
});
|
||||
socket.on('appRelaunch', (options) => {
|
||||
socket.on("appRelaunch", (options) => {
|
||||
app.relaunch(options);
|
||||
});
|
||||
socket.on('appFocus', (options) => {
|
||||
socket.on("appFocus", (options) => {
|
||||
app.focus(options);
|
||||
});
|
||||
socket.on('appHide', () => {
|
||||
socket.on("appHide", () => {
|
||||
app.hide();
|
||||
});
|
||||
socket.on('appShow', () => {
|
||||
socket.on("appShow", () => {
|
||||
app.show();
|
||||
});
|
||||
socket.on('appGetAppPath', () => {
|
||||
socket.on("appGetAppPath", () => {
|
||||
const path = app.getAppPath();
|
||||
electronSocket.emit('appGetAppPathCompleted', path);
|
||||
electronSocket.emit("appGetAppPathCompleted", path);
|
||||
});
|
||||
socket.on('appSetAppLogsPath', (path) => {
|
||||
socket.on("appSetAppLogsPath", (path) => {
|
||||
app.setAppLogsPath(path);
|
||||
});
|
||||
socket.on('appGetPath', (name) => {
|
||||
socket.on("appGetPath", (name) => {
|
||||
const path = app.getPath(name);
|
||||
electronSocket.emit('appGetPathCompleted', path);
|
||||
electronSocket.emit("appGetPathCompleted", path);
|
||||
});
|
||||
socket.on('appGetFileIcon', async (path, options) => {
|
||||
socket.on("appGetFileIcon", async (path, options) => {
|
||||
let error = {};
|
||||
if (options) {
|
||||
const nativeImage = await app.getFileIcon(path, options).catch((errorFileIcon) => error = errorFileIcon);
|
||||
electronSocket.emit('appGetFileIconCompleted', [error, nativeImage]);
|
||||
const nativeImage = await app
|
||||
.getFileIcon(path, options)
|
||||
.catch((errorFileIcon) => (error = errorFileIcon));
|
||||
electronSocket.emit("appGetFileIconCompleted", [error, nativeImage]);
|
||||
}
|
||||
else {
|
||||
const nativeImage = await app.getFileIcon(path).catch((errorFileIcon) => error = errorFileIcon);
|
||||
electronSocket.emit('appGetFileIconCompleted', [error, nativeImage]);
|
||||
const nativeImage = await app
|
||||
.getFileIcon(path)
|
||||
.catch((errorFileIcon) => (error = errorFileIcon));
|
||||
electronSocket.emit("appGetFileIconCompleted", [error, nativeImage]);
|
||||
}
|
||||
});
|
||||
socket.on('appSetPath', (name, path) => {
|
||||
socket.on("appSetPath", (name, path) => {
|
||||
app.setPath(name, path);
|
||||
});
|
||||
socket.on('appGetVersion', () => {
|
||||
socket.on("appGetVersion", () => {
|
||||
const version = app.getVersion();
|
||||
electronSocket.emit('appGetVersionCompleted', version);
|
||||
electronSocket.emit("appGetVersionCompleted", version);
|
||||
});
|
||||
socket.on('appGetName', () => {
|
||||
electronSocket.emit('appGetNameCompleted', app.name);
|
||||
socket.on("appGetName", () => {
|
||||
electronSocket.emit("appGetNameCompleted", app.name);
|
||||
});
|
||||
socket.on('appSetName', (name) => {
|
||||
socket.on("appSetName", (name) => {
|
||||
app.name = name;
|
||||
});
|
||||
socket.on('appGetLocale', () => {
|
||||
socket.on("appGetLocale", () => {
|
||||
const locale = app.getLocale();
|
||||
electronSocket.emit('appGetLocaleCompleted', locale);
|
||||
electronSocket.emit("appGetLocaleCompleted", locale);
|
||||
});
|
||||
socket.on('appAddRecentDocument', (path) => {
|
||||
socket.on("appAddRecentDocument", (path) => {
|
||||
app.addRecentDocument(path);
|
||||
});
|
||||
socket.on('appClearRecentDocuments', () => {
|
||||
socket.on("appClearRecentDocuments", () => {
|
||||
app.clearRecentDocuments();
|
||||
});
|
||||
socket.on('appSetAsDefaultProtocolClient', (protocol, path, args) => {
|
||||
socket.on("appSetAsDefaultProtocolClient", (protocol, path, args) => {
|
||||
const success = app.setAsDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit('appSetAsDefaultProtocolClientCompleted', success);
|
||||
electronSocket.emit("appSetAsDefaultProtocolClientCompleted", success);
|
||||
});
|
||||
socket.on('appRemoveAsDefaultProtocolClient', (protocol, path, args) => {
|
||||
socket.on("appRemoveAsDefaultProtocolClient", (protocol, path, args) => {
|
||||
const success = app.removeAsDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit('appRemoveAsDefaultProtocolClientCompleted', success);
|
||||
electronSocket.emit("appRemoveAsDefaultProtocolClientCompleted", success);
|
||||
});
|
||||
socket.on('appIsDefaultProtocolClient', (protocol, path, args) => {
|
||||
socket.on("appIsDefaultProtocolClient", (protocol, path, args) => {
|
||||
const success = app.isDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit('appIsDefaultProtocolClientCompleted', success);
|
||||
electronSocket.emit("appIsDefaultProtocolClientCompleted", success);
|
||||
});
|
||||
socket.on('appSetUserTasks', (tasks) => {
|
||||
socket.on("appSetUserTasks", (tasks) => {
|
||||
const success = app.setUserTasks(tasks);
|
||||
electronSocket.emit('appSetUserTasksCompleted', success);
|
||||
electronSocket.emit("appSetUserTasksCompleted", success);
|
||||
});
|
||||
socket.on('appGetJumpListSettings', () => {
|
||||
socket.on("appGetJumpListSettings", () => {
|
||||
const jumpListSettings = app.getJumpListSettings();
|
||||
electronSocket.emit('appGetJumpListSettingsCompleted', jumpListSettings);
|
||||
electronSocket.emit("appGetJumpListSettingsCompleted", jumpListSettings);
|
||||
});
|
||||
socket.on('appSetJumpList', (categories) => {
|
||||
socket.on("appSetJumpList", (categories) => {
|
||||
app.setJumpList(categories);
|
||||
});
|
||||
socket.on('appRequestSingleInstanceLock', () => {
|
||||
socket.on("appRequestSingleInstanceLock", () => {
|
||||
const success = app.requestSingleInstanceLock();
|
||||
electronSocket.emit('appRequestSingleInstanceLockCompleted', success);
|
||||
app.on('second-instance', (event, args = [], workingDirectory = '') => {
|
||||
electronSocket.emit('secondInstance', [args, workingDirectory]);
|
||||
electronSocket.emit("appRequestSingleInstanceLockCompleted", success);
|
||||
app.on("second-instance", (event, args = [], workingDirectory = "") => {
|
||||
electronSocket.emit("secondInstance", [args, workingDirectory]);
|
||||
});
|
||||
});
|
||||
socket.on('appHasSingleInstanceLock', () => {
|
||||
socket.on("appHasSingleInstanceLock", () => {
|
||||
const hasLock = app.hasSingleInstanceLock();
|
||||
electronSocket.emit('appHasSingleInstanceLockCompleted', hasLock);
|
||||
electronSocket.emit("appHasSingleInstanceLockCompleted", hasLock);
|
||||
});
|
||||
socket.on('appReleaseSingleInstanceLock', () => {
|
||||
socket.on("appReleaseSingleInstanceLock", () => {
|
||||
app.releaseSingleInstanceLock();
|
||||
});
|
||||
socket.on('appSetUserActivity', (type, userInfo, webpageUrl) => {
|
||||
socket.on("appSetUserActivity", (type, userInfo, webpageUrl) => {
|
||||
app.setUserActivity(type, userInfo, webpageUrl);
|
||||
});
|
||||
socket.on('appGetCurrentActivityType', () => {
|
||||
socket.on("appGetCurrentActivityType", () => {
|
||||
const activityType = app.getCurrentActivityType();
|
||||
electronSocket.emit('appGetCurrentActivityTypeCompleted', activityType);
|
||||
electronSocket.emit("appGetCurrentActivityTypeCompleted", activityType);
|
||||
});
|
||||
socket.on('appInvalidateCurrentActivity', () => {
|
||||
socket.on("appInvalidateCurrentActivity", () => {
|
||||
app.invalidateCurrentActivity();
|
||||
});
|
||||
socket.on('appResignCurrentActivity', () => {
|
||||
socket.on("appResignCurrentActivity", () => {
|
||||
app.resignCurrentActivity();
|
||||
});
|
||||
socket.on('appSetAppUserModelId', (id) => {
|
||||
socket.on("appSetAppUserModelId", (id) => {
|
||||
app.setAppUserModelId(id);
|
||||
});
|
||||
socket.on('appImportCertificate', (options) => {
|
||||
socket.on("appImportCertificate", (options) => {
|
||||
app.importCertificate(options, (result) => {
|
||||
electronSocket.emit('appImportCertificateCompleted', result);
|
||||
electronSocket.emit("appImportCertificateCompleted", result);
|
||||
});
|
||||
});
|
||||
socket.on('appGetAppMetrics', () => {
|
||||
socket.on("appGetAppMetrics", () => {
|
||||
const processMetrics = app.getAppMetrics();
|
||||
electronSocket.emit('appGetAppMetricsCompleted', processMetrics);
|
||||
electronSocket.emit("appGetAppMetricsCompleted", processMetrics);
|
||||
});
|
||||
socket.on('appGetGpuFeatureStatus', () => {
|
||||
socket.on("appGetGpuFeatureStatus", () => {
|
||||
const gpuFeatureStatus = app.getGPUFeatureStatus();
|
||||
electronSocket.emit('appGetGpuFeatureStatusCompleted', gpuFeatureStatus);
|
||||
electronSocket.emit("appGetGpuFeatureStatusCompleted", gpuFeatureStatus);
|
||||
});
|
||||
socket.on('appSetBadgeCount', (count) => {
|
||||
socket.on("appSetBadgeCount", (count) => {
|
||||
const success = app.setBadgeCount(count);
|
||||
electronSocket.emit('appSetBadgeCountCompleted', success);
|
||||
electronSocket.emit("appSetBadgeCountCompleted", success);
|
||||
});
|
||||
socket.on('appGetBadgeCount', () => {
|
||||
socket.on("appGetBadgeCount", () => {
|
||||
const count = app.getBadgeCount();
|
||||
electronSocket.emit('appGetBadgeCountCompleted', count);
|
||||
electronSocket.emit("appGetBadgeCountCompleted", count);
|
||||
});
|
||||
socket.on('appIsUnityRunning', () => {
|
||||
socket.on("appIsUnityRunning", () => {
|
||||
const isUnityRunning = app.isUnityRunning();
|
||||
electronSocket.emit('appIsUnityRunningCompleted', isUnityRunning);
|
||||
electronSocket.emit("appIsUnityRunningCompleted", isUnityRunning);
|
||||
});
|
||||
socket.on('appGetLoginItemSettings', (options) => {
|
||||
socket.on("appGetLoginItemSettings", (options) => {
|
||||
const loginItemSettings = app.getLoginItemSettings(options);
|
||||
electronSocket.emit('appGetLoginItemSettingsCompleted', loginItemSettings);
|
||||
electronSocket.emit("appGetLoginItemSettingsCompleted", loginItemSettings);
|
||||
});
|
||||
socket.on('appSetLoginItemSettings', (settings) => {
|
||||
socket.on("appSetLoginItemSettings", (settings) => {
|
||||
app.setLoginItemSettings(settings);
|
||||
});
|
||||
socket.on('appIsAccessibilitySupportEnabled', () => {
|
||||
socket.on("appIsAccessibilitySupportEnabled", () => {
|
||||
const isAccessibilitySupportEnabled = app.isAccessibilitySupportEnabled();
|
||||
electronSocket.emit('appIsAccessibilitySupportEnabledCompleted', isAccessibilitySupportEnabled);
|
||||
electronSocket.emit("appIsAccessibilitySupportEnabledCompleted", isAccessibilitySupportEnabled);
|
||||
});
|
||||
socket.on('appSetAccessibilitySupportEnabled', (enabled) => {
|
||||
socket.on("appSetAccessibilitySupportEnabled", (enabled) => {
|
||||
app.setAccessibilitySupportEnabled(enabled);
|
||||
});
|
||||
socket.on('appShowAboutPanel', () => {
|
||||
socket.on("appShowAboutPanel", () => {
|
||||
app.showAboutPanel();
|
||||
});
|
||||
socket.on('appSetAboutPanelOptions', (options) => {
|
||||
socket.on("appSetAboutPanelOptions", (options) => {
|
||||
app.setAboutPanelOptions(options);
|
||||
});
|
||||
socket.on('appGetUserAgentFallback', () => {
|
||||
electronSocket.emit('appGetUserAgentFallbackCompleted', app.userAgentFallback);
|
||||
socket.on("appGetUserAgentFallback", () => {
|
||||
electronSocket.emit("appGetUserAgentFallbackCompleted", app.userAgentFallback);
|
||||
});
|
||||
socket.on('appSetUserAgentFallback', (userAgent) => {
|
||||
socket.on("appSetUserAgentFallback", (userAgent) => {
|
||||
app.userAgentFallback = userAgent;
|
||||
});
|
||||
socket.on('register-app-on-event', (eventName, listenerName) => {
|
||||
socket.on("register-app-on-event", (eventName, listenerName) => {
|
||||
app.on(eventName, (...args) => {
|
||||
if (args.length > 1) {
|
||||
electronSocket.emit(listenerName, args[1]);
|
||||
@@ -238,7 +243,7 @@ module.exports = (socket, app) => {
|
||||
}
|
||||
});
|
||||
});
|
||||
socket.on('register-app-once-event', (eventName, listenerName) => {
|
||||
socket.on("register-app-once-event", (eventName, listenerName) => {
|
||||
app.once(eventName, (...args) => {
|
||||
if (args.length > 1) {
|
||||
electronSocket.emit(listenerName, args[1]);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,313 +1,338 @@
|
||||
import { RelaunchOptions, LoginItemSettingsOptions, Settings, AboutPanelOptionsOptions } from "electron";
|
||||
import { Socket } from "net";
|
||||
import type { Socket } from "net";
|
||||
import {
|
||||
RelaunchOptions,
|
||||
LoginItemSettingsOptions,
|
||||
Settings,
|
||||
AboutPanelOptionsOptions,
|
||||
} from "electron";
|
||||
|
||||
let isQuitWindowAllClosed = true;
|
||||
let electronSocket: Socket;
|
||||
let appWindowAllClosedEventId: string;
|
||||
|
||||
let isQuitWindowAllClosed = true, electronSocket;
|
||||
let appWindowAllClosedEventId;
|
||||
export = (socket: Socket, app: Electron.App) => {
|
||||
electronSocket = socket;
|
||||
electronSocket = socket;
|
||||
|
||||
// By default, quit when all windows are closed
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin' && isQuitWindowAllClosed) {
|
||||
app.quit();
|
||||
} else if (appWindowAllClosedEventId) {
|
||||
// If the user is on macOS
|
||||
// - OR -
|
||||
// If the user has indicated NOT to quit when all windows are closed,
|
||||
// emit the event.
|
||||
electronSocket.emit('app-window-all-closed' + appWindowAllClosedEventId);
|
||||
}
|
||||
// By default, quit when all windows are closed
|
||||
app.on("window-all-closed", () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== "darwin" && isQuitWindowAllClosed) {
|
||||
app.quit();
|
||||
} else if (appWindowAllClosedEventId) {
|
||||
// If the user is on macOS
|
||||
// - OR -
|
||||
// If the user has indicated NOT to quit when all windows are closed,
|
||||
// emit the event.
|
||||
electronSocket.emit("app-window-all-closed" + appWindowAllClosedEventId);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("quit-app-window-all-closed", (quit) => {
|
||||
isQuitWindowAllClosed = quit;
|
||||
});
|
||||
|
||||
socket.on("register-app-window-all-closed", (id) => {
|
||||
appWindowAllClosedEventId = id;
|
||||
});
|
||||
|
||||
socket.on("register-app-before-quit", (id) => {
|
||||
app.on("before-quit", (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
electronSocket.emit("app-before-quit" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('quit-app-window-all-closed', (quit) => {
|
||||
isQuitWindowAllClosed = quit;
|
||||
socket.on("register-app-will-quit", (id) => {
|
||||
app.on("will-quit", (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
electronSocket.emit("app-will-quit" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-window-all-closed', (id) => {
|
||||
appWindowAllClosedEventId = id;
|
||||
socket.on("register-app-browser-window-blur", (id) => {
|
||||
app.on("browser-window-blur", () => {
|
||||
electronSocket.emit("app-browser-window-blur" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-before-quit', (id) => {
|
||||
app.on('before-quit', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
electronSocket.emit('app-before-quit' + id);
|
||||
});
|
||||
socket.on("register-app-browser-window-focus", (id) => {
|
||||
app.on("browser-window-focus", () => {
|
||||
electronSocket.emit("app-browser-window-focus" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-will-quit', (id) => {
|
||||
app.on('will-quit', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
electronSocket.emit('app-will-quit' + id);
|
||||
});
|
||||
socket.on("register-app-browser-window-created", (id) => {
|
||||
app.on("browser-window-created", () => {
|
||||
electronSocket.emit("app-browser-window-created" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-browser-window-blur', (id) => {
|
||||
app.on('browser-window-blur', () => {
|
||||
electronSocket.emit('app-browser-window-blur' + id);
|
||||
});
|
||||
socket.on("register-app-web-contents-created", (id) => {
|
||||
app.on("web-contents-created", () => {
|
||||
electronSocket.emit("app-web-contents-created" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-browser-window-focus', (id) => {
|
||||
app.on('browser-window-focus', () => {
|
||||
electronSocket.emit('app-browser-window-focus' + id);
|
||||
});
|
||||
socket.on("register-app-accessibility-support-changed", (id) => {
|
||||
app.on(
|
||||
"accessibility-support-changed",
|
||||
(event, accessibilitySupportEnabled) => {
|
||||
electronSocket.emit(
|
||||
"app-accessibility-support-changed" + id,
|
||||
accessibilitySupportEnabled,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
socket.on("appQuit", () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
socket.on("appExit", (exitCode = 0) => {
|
||||
app.exit(exitCode);
|
||||
});
|
||||
|
||||
socket.on("appRelaunch", (options) => {
|
||||
app.relaunch(options as RelaunchOptions);
|
||||
});
|
||||
|
||||
socket.on("appFocus", (options) => {
|
||||
app.focus(options);
|
||||
});
|
||||
|
||||
socket.on("appHide", () => {
|
||||
app.hide();
|
||||
});
|
||||
|
||||
socket.on("appShow", () => {
|
||||
app.show();
|
||||
});
|
||||
|
||||
socket.on("appGetAppPath", () => {
|
||||
const path = app.getAppPath();
|
||||
electronSocket.emit("appGetAppPathCompleted", path);
|
||||
});
|
||||
|
||||
socket.on("appSetAppLogsPath", (path) => {
|
||||
app.setAppLogsPath(path);
|
||||
});
|
||||
|
||||
socket.on("appGetPath", (name) => {
|
||||
const path = app.getPath(name);
|
||||
electronSocket.emit("appGetPathCompleted", path);
|
||||
});
|
||||
|
||||
socket.on("appGetFileIcon", async (path, options) => {
|
||||
let error = {};
|
||||
|
||||
if (options) {
|
||||
const nativeImage = await app
|
||||
.getFileIcon(path, options)
|
||||
.catch((errorFileIcon) => (error = errorFileIcon));
|
||||
|
||||
electronSocket.emit("appGetFileIconCompleted", [error, nativeImage]);
|
||||
} else {
|
||||
const nativeImage = await app
|
||||
.getFileIcon(path)
|
||||
.catch((errorFileIcon) => (error = errorFileIcon));
|
||||
|
||||
electronSocket.emit("appGetFileIconCompleted", [error, nativeImage]);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("appSetPath", (name, path) => {
|
||||
app.setPath(name, path);
|
||||
});
|
||||
|
||||
socket.on("appGetVersion", () => {
|
||||
const version = app.getVersion();
|
||||
electronSocket.emit("appGetVersionCompleted", version);
|
||||
});
|
||||
|
||||
socket.on("appGetName", () => {
|
||||
electronSocket.emit("appGetNameCompleted", app.name);
|
||||
});
|
||||
|
||||
socket.on("appSetName", (name) => {
|
||||
app.name = name;
|
||||
});
|
||||
|
||||
socket.on("appGetLocale", () => {
|
||||
const locale = app.getLocale();
|
||||
electronSocket.emit("appGetLocaleCompleted", locale);
|
||||
});
|
||||
|
||||
socket.on("appAddRecentDocument", (path) => {
|
||||
app.addRecentDocument(path);
|
||||
});
|
||||
|
||||
socket.on("appClearRecentDocuments", () => {
|
||||
app.clearRecentDocuments();
|
||||
});
|
||||
|
||||
socket.on("appSetAsDefaultProtocolClient", (protocol, path, args) => {
|
||||
const success = app.setAsDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit("appSetAsDefaultProtocolClientCompleted", success);
|
||||
});
|
||||
|
||||
socket.on("appRemoveAsDefaultProtocolClient", (protocol, path, args) => {
|
||||
const success = app.removeAsDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit("appRemoveAsDefaultProtocolClientCompleted", success);
|
||||
});
|
||||
|
||||
socket.on("appIsDefaultProtocolClient", (protocol, path, args) => {
|
||||
const success = app.isDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit("appIsDefaultProtocolClientCompleted", success);
|
||||
});
|
||||
|
||||
socket.on("appSetUserTasks", (tasks) => {
|
||||
const success = app.setUserTasks(tasks);
|
||||
electronSocket.emit("appSetUserTasksCompleted", success);
|
||||
});
|
||||
|
||||
socket.on("appGetJumpListSettings", () => {
|
||||
const jumpListSettings = app.getJumpListSettings();
|
||||
electronSocket.emit("appGetJumpListSettingsCompleted", jumpListSettings);
|
||||
});
|
||||
|
||||
socket.on("appSetJumpList", (categories) => {
|
||||
app.setJumpList(categories);
|
||||
});
|
||||
|
||||
socket.on("appRequestSingleInstanceLock", () => {
|
||||
const success = app.requestSingleInstanceLock();
|
||||
electronSocket.emit("appRequestSingleInstanceLockCompleted", success);
|
||||
|
||||
app.on("second-instance", (event, args = [], workingDirectory = "") => {
|
||||
electronSocket.emit("secondInstance", [args, workingDirectory]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-browser-window-created', (id) => {
|
||||
app.on('browser-window-created', () => {
|
||||
electronSocket.emit('app-browser-window-created' + id);
|
||||
});
|
||||
socket.on("appHasSingleInstanceLock", () => {
|
||||
const hasLock = app.hasSingleInstanceLock();
|
||||
|
||||
electronSocket.emit("appHasSingleInstanceLockCompleted", hasLock);
|
||||
});
|
||||
|
||||
socket.on("appReleaseSingleInstanceLock", () => {
|
||||
app.releaseSingleInstanceLock();
|
||||
});
|
||||
|
||||
socket.on("appSetUserActivity", (type, userInfo, webpageUrl) => {
|
||||
app.setUserActivity(type, userInfo, webpageUrl);
|
||||
});
|
||||
|
||||
socket.on("appGetCurrentActivityType", () => {
|
||||
const activityType = app.getCurrentActivityType();
|
||||
electronSocket.emit("appGetCurrentActivityTypeCompleted", activityType);
|
||||
});
|
||||
|
||||
socket.on("appInvalidateCurrentActivity", () => {
|
||||
app.invalidateCurrentActivity();
|
||||
});
|
||||
|
||||
socket.on("appResignCurrentActivity", () => {
|
||||
app.resignCurrentActivity();
|
||||
});
|
||||
|
||||
socket.on("appSetAppUserModelId", (id) => {
|
||||
app.setAppUserModelId(id);
|
||||
});
|
||||
|
||||
socket.on("appImportCertificate", (options) => {
|
||||
app.importCertificate(options, (result) => {
|
||||
electronSocket.emit("appImportCertificateCompleted", result);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-web-contents-created', (id) => {
|
||||
app.on('web-contents-created', () => {
|
||||
electronSocket.emit('app-web-contents-created' + id);
|
||||
});
|
||||
socket.on("appGetAppMetrics", () => {
|
||||
const processMetrics = app.getAppMetrics();
|
||||
electronSocket.emit("appGetAppMetricsCompleted", processMetrics);
|
||||
});
|
||||
|
||||
socket.on("appGetGpuFeatureStatus", () => {
|
||||
const gpuFeatureStatus = app.getGPUFeatureStatus();
|
||||
electronSocket.emit("appGetGpuFeatureStatusCompleted", gpuFeatureStatus);
|
||||
});
|
||||
|
||||
socket.on("appSetBadgeCount", (count) => {
|
||||
const success = app.setBadgeCount(count);
|
||||
electronSocket.emit("appSetBadgeCountCompleted", success);
|
||||
});
|
||||
|
||||
socket.on("appGetBadgeCount", () => {
|
||||
const count = app.getBadgeCount();
|
||||
electronSocket.emit("appGetBadgeCountCompleted", count);
|
||||
});
|
||||
|
||||
socket.on("appIsUnityRunning", () => {
|
||||
const isUnityRunning = app.isUnityRunning();
|
||||
electronSocket.emit("appIsUnityRunningCompleted", isUnityRunning);
|
||||
});
|
||||
|
||||
socket.on("appGetLoginItemSettings", (options) => {
|
||||
const loginItemSettings = app.getLoginItemSettings(
|
||||
options as LoginItemSettingsOptions,
|
||||
);
|
||||
electronSocket.emit("appGetLoginItemSettingsCompleted", loginItemSettings);
|
||||
});
|
||||
|
||||
socket.on("appSetLoginItemSettings", (settings) => {
|
||||
app.setLoginItemSettings(settings as Settings);
|
||||
});
|
||||
|
||||
socket.on("appIsAccessibilitySupportEnabled", () => {
|
||||
const isAccessibilitySupportEnabled = app.isAccessibilitySupportEnabled();
|
||||
electronSocket.emit(
|
||||
"appIsAccessibilitySupportEnabledCompleted",
|
||||
isAccessibilitySupportEnabled,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on("appSetAccessibilitySupportEnabled", (enabled) => {
|
||||
app.setAccessibilitySupportEnabled(enabled);
|
||||
});
|
||||
|
||||
socket.on("appShowAboutPanel", () => {
|
||||
app.showAboutPanel();
|
||||
});
|
||||
|
||||
socket.on("appSetAboutPanelOptions", (options) => {
|
||||
app.setAboutPanelOptions(options as AboutPanelOptionsOptions);
|
||||
});
|
||||
|
||||
socket.on("appGetUserAgentFallback", () => {
|
||||
electronSocket.emit(
|
||||
"appGetUserAgentFallbackCompleted",
|
||||
app.userAgentFallback,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on("appSetUserAgentFallback", (userAgent) => {
|
||||
app.userAgentFallback = userAgent;
|
||||
});
|
||||
|
||||
socket.on("register-app-on-event", (eventName, listenerName) => {
|
||||
app.on(eventName, (...args) => {
|
||||
if (args.length > 1) {
|
||||
electronSocket.emit(listenerName, args[1]);
|
||||
} else {
|
||||
electronSocket.emit(listenerName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-accessibility-support-changed', (id) => {
|
||||
app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => {
|
||||
electronSocket.emit('app-accessibility-support-changed' + id, accessibilitySupportEnabled);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('appQuit', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
socket.on('appExit', (exitCode = 0) => {
|
||||
app.exit(exitCode);
|
||||
});
|
||||
|
||||
socket.on('appRelaunch', (options) => {
|
||||
app.relaunch(options as RelaunchOptions);
|
||||
});
|
||||
|
||||
socket.on('appFocus', (options) => {
|
||||
app.focus(options);
|
||||
});
|
||||
|
||||
socket.on('appHide', () => {
|
||||
app.hide();
|
||||
});
|
||||
|
||||
socket.on('appShow', () => {
|
||||
app.show();
|
||||
});
|
||||
|
||||
socket.on('appGetAppPath', () => {
|
||||
const path = app.getAppPath();
|
||||
electronSocket.emit('appGetAppPathCompleted', path);
|
||||
});
|
||||
|
||||
socket.on('appSetAppLogsPath', (path) => {
|
||||
app.setAppLogsPath(path);
|
||||
});
|
||||
|
||||
socket.on('appGetPath', (name) => {
|
||||
const path = app.getPath(name);
|
||||
electronSocket.emit('appGetPathCompleted', path);
|
||||
});
|
||||
|
||||
socket.on('appGetFileIcon', async (path, options) => {
|
||||
let error = {};
|
||||
|
||||
if (options) {
|
||||
const nativeImage = await app.getFileIcon(path, options).catch((errorFileIcon) => error = errorFileIcon);
|
||||
|
||||
electronSocket.emit('appGetFileIconCompleted', [error, nativeImage]);
|
||||
} else {
|
||||
const nativeImage = await app.getFileIcon(path).catch((errorFileIcon) => error = errorFileIcon);
|
||||
|
||||
electronSocket.emit('appGetFileIconCompleted', [error, nativeImage]);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('appSetPath', (name, path) => {
|
||||
app.setPath(name, path);
|
||||
});
|
||||
|
||||
socket.on('appGetVersion', () => {
|
||||
const version = app.getVersion();
|
||||
electronSocket.emit('appGetVersionCompleted', version);
|
||||
});
|
||||
|
||||
socket.on('appGetName', () => {
|
||||
electronSocket.emit('appGetNameCompleted', app.name);
|
||||
});
|
||||
|
||||
socket.on('appSetName', (name) => {
|
||||
app.name = name;
|
||||
});
|
||||
|
||||
socket.on('appGetLocale', () => {
|
||||
const locale = app.getLocale();
|
||||
electronSocket.emit('appGetLocaleCompleted', locale);
|
||||
});
|
||||
|
||||
socket.on('appAddRecentDocument', (path) => {
|
||||
app.addRecentDocument(path);
|
||||
});
|
||||
|
||||
socket.on('appClearRecentDocuments', () => {
|
||||
app.clearRecentDocuments();
|
||||
});
|
||||
|
||||
socket.on('appSetAsDefaultProtocolClient', (protocol, path, args) => {
|
||||
const success = app.setAsDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit('appSetAsDefaultProtocolClientCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('appRemoveAsDefaultProtocolClient', (protocol, path, args) => {
|
||||
const success = app.removeAsDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit('appRemoveAsDefaultProtocolClientCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('appIsDefaultProtocolClient', (protocol, path, args) => {
|
||||
const success = app.isDefaultProtocolClient(protocol, path, args);
|
||||
electronSocket.emit('appIsDefaultProtocolClientCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('appSetUserTasks', (tasks) => {
|
||||
const success = app.setUserTasks(tasks);
|
||||
electronSocket.emit('appSetUserTasksCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('appGetJumpListSettings', () => {
|
||||
const jumpListSettings = app.getJumpListSettings();
|
||||
electronSocket.emit('appGetJumpListSettingsCompleted', jumpListSettings);
|
||||
});
|
||||
|
||||
socket.on('appSetJumpList', (categories) => {
|
||||
app.setJumpList(categories);
|
||||
});
|
||||
|
||||
socket.on('appRequestSingleInstanceLock', () => {
|
||||
const success = app.requestSingleInstanceLock();
|
||||
electronSocket.emit('appRequestSingleInstanceLockCompleted', success);
|
||||
|
||||
app.on('second-instance', (event, args = [], workingDirectory = '') => {
|
||||
electronSocket.emit('secondInstance', [args, workingDirectory]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('appHasSingleInstanceLock', () => {
|
||||
const hasLock = app.hasSingleInstanceLock();
|
||||
|
||||
electronSocket.emit('appHasSingleInstanceLockCompleted', hasLock);
|
||||
});
|
||||
|
||||
socket.on('appReleaseSingleInstanceLock', () => {
|
||||
app.releaseSingleInstanceLock();
|
||||
});
|
||||
|
||||
socket.on('appSetUserActivity', (type, userInfo, webpageUrl) => {
|
||||
app.setUserActivity(type, userInfo, webpageUrl);
|
||||
});
|
||||
|
||||
socket.on('appGetCurrentActivityType', () => {
|
||||
const activityType = app.getCurrentActivityType();
|
||||
electronSocket.emit('appGetCurrentActivityTypeCompleted', activityType);
|
||||
});
|
||||
|
||||
socket.on('appInvalidateCurrentActivity', () => {
|
||||
app.invalidateCurrentActivity();
|
||||
});
|
||||
|
||||
socket.on('appResignCurrentActivity', () => {
|
||||
app.resignCurrentActivity();
|
||||
});
|
||||
|
||||
socket.on('appSetAppUserModelId', (id) => {
|
||||
app.setAppUserModelId(id);
|
||||
});
|
||||
|
||||
socket.on('appImportCertificate', (options) => {
|
||||
app.importCertificate(options, (result) => {
|
||||
electronSocket.emit('appImportCertificateCompleted', result);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('appGetAppMetrics', () => {
|
||||
const processMetrics = app.getAppMetrics();
|
||||
electronSocket.emit('appGetAppMetricsCompleted', processMetrics);
|
||||
});
|
||||
|
||||
socket.on('appGetGpuFeatureStatus', () => {
|
||||
const gpuFeatureStatus = app.getGPUFeatureStatus();
|
||||
electronSocket.emit('appGetGpuFeatureStatusCompleted', gpuFeatureStatus);
|
||||
});
|
||||
|
||||
socket.on('appSetBadgeCount', (count) => {
|
||||
const success = app.setBadgeCount(count);
|
||||
electronSocket.emit('appSetBadgeCountCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('appGetBadgeCount', () => {
|
||||
const count = app.getBadgeCount();
|
||||
electronSocket.emit('appGetBadgeCountCompleted', count);
|
||||
});
|
||||
|
||||
socket.on('appIsUnityRunning', () => {
|
||||
const isUnityRunning = app.isUnityRunning();
|
||||
electronSocket.emit('appIsUnityRunningCompleted', isUnityRunning);
|
||||
});
|
||||
|
||||
socket.on('appGetLoginItemSettings', (options) => {
|
||||
const loginItemSettings = app.getLoginItemSettings(options as LoginItemSettingsOptions);
|
||||
electronSocket.emit('appGetLoginItemSettingsCompleted', loginItemSettings);
|
||||
});
|
||||
|
||||
socket.on('appSetLoginItemSettings', (settings) => {
|
||||
app.setLoginItemSettings(settings as Settings);
|
||||
});
|
||||
|
||||
socket.on('appIsAccessibilitySupportEnabled', () => {
|
||||
const isAccessibilitySupportEnabled = app.isAccessibilitySupportEnabled();
|
||||
electronSocket.emit('appIsAccessibilitySupportEnabledCompleted', isAccessibilitySupportEnabled);
|
||||
});
|
||||
|
||||
socket.on('appSetAccessibilitySupportEnabled', (enabled) => {
|
||||
app.setAccessibilitySupportEnabled(enabled);
|
||||
});
|
||||
|
||||
socket.on('appShowAboutPanel', () => {
|
||||
app.showAboutPanel();
|
||||
});
|
||||
|
||||
socket.on('appSetAboutPanelOptions', (options) => {
|
||||
app.setAboutPanelOptions(options as AboutPanelOptionsOptions);
|
||||
});
|
||||
|
||||
socket.on('appGetUserAgentFallback', () => {
|
||||
electronSocket.emit('appGetUserAgentFallbackCompleted', app.userAgentFallback);
|
||||
});
|
||||
|
||||
socket.on('appSetUserAgentFallback', (userAgent) => {
|
||||
app.userAgentFallback = userAgent;
|
||||
});
|
||||
|
||||
socket.on('register-app-on-event', (eventName, listenerName) => {
|
||||
app.on(eventName, (...args) => {
|
||||
if (args.length > 1) {
|
||||
electronSocket.emit(listenerName, args[1]);
|
||||
} else {
|
||||
electronSocket.emit(listenerName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-app-once-event', (eventName, listenerName) => {
|
||||
app.once(eventName, (...args) => {
|
||||
if (args.length > 1) {
|
||||
electronSocket.emit(listenerName, args[1]);
|
||||
} else {
|
||||
electronSocket.emit(listenerName);
|
||||
}
|
||||
});
|
||||
socket.on("register-app-once-event", (eventName, listenerName) => {
|
||||
app.once(eventName, (...args) => {
|
||||
if (args.length > 1) {
|
||||
electronSocket.emit(listenerName, args[1]);
|
||||
} else {
|
||||
electronSocket.emit(listenerName);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,114 +1,133 @@
|
||||
"use strict";
|
||||
const electron_updater_1 = require("electron-updater");
|
||||
let electronSocket;
|
||||
function normalize(updateInfo) {
|
||||
if (typeof updateInfo?.releaseNotes === "string") {
|
||||
updateInfo.releaseNotes = [{ note: updateInfo.releaseNotes }];
|
||||
}
|
||||
else if (Array.isArray(updateInfo?.releaseNotes)) {
|
||||
updateInfo.releaseNotes = updateInfo.releaseNotes.map((entry) => typeof entry === "string" ? { note: entry } : entry);
|
||||
}
|
||||
}
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('register-autoUpdater-error', (id) => {
|
||||
electron_updater_1.autoUpdater.on('error', (error) => {
|
||||
electronSocket.emit('autoUpdater-error' + id, error.message);
|
||||
socket.on("register-autoUpdater-error", (id) => {
|
||||
electron_updater_1.autoUpdater.on("error", (error) => {
|
||||
electronSocket.emit("autoUpdater-error" + id, error.message);
|
||||
});
|
||||
});
|
||||
socket.on('register-autoUpdater-checking-for-update', (id) => {
|
||||
electron_updater_1.autoUpdater.on('checking-for-update', () => {
|
||||
electronSocket.emit('autoUpdater-checking-for-update' + id);
|
||||
socket.on("register-autoUpdater-checking-for-update", (id) => {
|
||||
electron_updater_1.autoUpdater.on("checking-for-update", () => {
|
||||
electronSocket.emit("autoUpdater-checking-for-update" + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-autoUpdater-update-available', (id) => {
|
||||
electron_updater_1.autoUpdater.on('update-available', (updateInfo) => {
|
||||
electronSocket.emit('autoUpdater-update-available' + id, updateInfo);
|
||||
socket.on("register-autoUpdater-update-available", (id) => {
|
||||
electron_updater_1.autoUpdater.on("update-available", (updateInfo) => {
|
||||
normalize(updateInfo);
|
||||
electronSocket.emit("autoUpdater-update-available" + id, updateInfo);
|
||||
});
|
||||
});
|
||||
socket.on('register-autoUpdater-update-not-available', (id) => {
|
||||
electron_updater_1.autoUpdater.on('update-not-available', (updateInfo) => {
|
||||
electronSocket.emit('autoUpdater-update-not-available' + id, updateInfo);
|
||||
socket.on("register-autoUpdater-update-not-available", (id) => {
|
||||
electron_updater_1.autoUpdater.on("update-not-available", (updateInfo) => {
|
||||
normalize(updateInfo);
|
||||
electronSocket.emit("autoUpdater-update-not-available" + id, updateInfo);
|
||||
});
|
||||
});
|
||||
socket.on('register-autoUpdater-download-progress', (id) => {
|
||||
electron_updater_1.autoUpdater.on('download-progress', (progressInfo) => {
|
||||
electronSocket.emit('autoUpdater-download-progress' + id, progressInfo);
|
||||
socket.on("register-autoUpdater-download-progress", (id) => {
|
||||
electron_updater_1.autoUpdater.on("download-progress", (progressInfo) => {
|
||||
electronSocket.emit("autoUpdater-download-progress" + id, progressInfo);
|
||||
});
|
||||
});
|
||||
socket.on('register-autoUpdater-update-downloaded', (id) => {
|
||||
electron_updater_1.autoUpdater.on('update-downloaded', (updateInfo) => {
|
||||
electronSocket.emit('autoUpdater-update-downloaded' + id, updateInfo);
|
||||
socket.on("register-autoUpdater-update-downloaded", (id) => {
|
||||
electron_updater_1.autoUpdater.on("update-downloaded", (updateInfo) => {
|
||||
normalize(updateInfo);
|
||||
electronSocket.emit("autoUpdater-update-downloaded" + id, updateInfo);
|
||||
});
|
||||
});
|
||||
// Properties *****
|
||||
socket.on('autoUpdater-autoDownload', () => {
|
||||
electronSocket.emit('autoUpdater-autoDownload-completed', electron_updater_1.autoUpdater.autoDownload);
|
||||
socket.on("autoUpdater-autoDownload", () => {
|
||||
electronSocket.emit("autoUpdater-autoDownload-completed", electron_updater_1.autoUpdater.autoDownload);
|
||||
});
|
||||
socket.on('autoUpdater-autoDownload-set', (value) => {
|
||||
socket.on("autoUpdater-autoDownload-set", (value) => {
|
||||
electron_updater_1.autoUpdater.autoDownload = value;
|
||||
});
|
||||
socket.on('autoUpdater-autoInstallOnAppQuit', () => {
|
||||
electronSocket.emit('autoUpdater-autoInstallOnAppQuit-completed', electron_updater_1.autoUpdater.autoInstallOnAppQuit);
|
||||
socket.on("autoUpdater-autoInstallOnAppQuit", () => {
|
||||
electronSocket.emit("autoUpdater-autoInstallOnAppQuit-completed", electron_updater_1.autoUpdater.autoInstallOnAppQuit);
|
||||
});
|
||||
socket.on('autoUpdater-autoInstallOnAppQuit-set', (value) => {
|
||||
socket.on("autoUpdater-autoInstallOnAppQuit-set", (value) => {
|
||||
electron_updater_1.autoUpdater.autoInstallOnAppQuit = value;
|
||||
});
|
||||
socket.on('autoUpdater-allowPrerelease', () => {
|
||||
electronSocket.emit('autoUpdater-allowPrerelease-completed', electron_updater_1.autoUpdater.allowPrerelease);
|
||||
socket.on("autoUpdater-allowPrerelease", () => {
|
||||
electronSocket.emit("autoUpdater-allowPrerelease-completed", electron_updater_1.autoUpdater.allowPrerelease);
|
||||
});
|
||||
socket.on('autoUpdater-allowPrerelease-set', (value) => {
|
||||
socket.on("autoUpdater-allowPrerelease-set", (value) => {
|
||||
electron_updater_1.autoUpdater.allowPrerelease = value;
|
||||
});
|
||||
socket.on('autoUpdater-fullChangelog', () => {
|
||||
electronSocket.emit('autoUpdater-fullChangelog-completed', electron_updater_1.autoUpdater.fullChangelog);
|
||||
socket.on("autoUpdater-fullChangelog", () => {
|
||||
electronSocket.emit("autoUpdater-fullChangelog-completed", electron_updater_1.autoUpdater.fullChangelog);
|
||||
});
|
||||
socket.on('autoUpdater-fullChangelog-set', (value) => {
|
||||
socket.on("autoUpdater-fullChangelog-set", (value) => {
|
||||
electron_updater_1.autoUpdater.fullChangelog = value;
|
||||
});
|
||||
socket.on('autoUpdater-allowDowngrade', () => {
|
||||
electronSocket.emit('autoUpdater-allowDowngrade-completed', electron_updater_1.autoUpdater.allowDowngrade);
|
||||
socket.on("autoUpdater-allowDowngrade", () => {
|
||||
electronSocket.emit("autoUpdater-allowDowngrade-completed", electron_updater_1.autoUpdater.allowDowngrade);
|
||||
});
|
||||
socket.on('autoUpdater-allowDowngrade-set', (value) => {
|
||||
socket.on("autoUpdater-allowDowngrade-set", (value) => {
|
||||
electron_updater_1.autoUpdater.allowDowngrade = value;
|
||||
});
|
||||
socket.on('autoUpdater-updateConfigPath', () => {
|
||||
electronSocket.emit('autoUpdater-updateConfigPath-completed', electron_updater_1.autoUpdater.updateConfigPath || '');
|
||||
socket.on("autoUpdater-updateConfigPath", () => {
|
||||
electronSocket.emit("autoUpdater-updateConfigPath-completed", electron_updater_1.autoUpdater.updateConfigPath || "");
|
||||
});
|
||||
socket.on('autoUpdater-updateConfigPath-set', (value) => {
|
||||
socket.on("autoUpdater-updateConfigPath-set", (value) => {
|
||||
electron_updater_1.autoUpdater.updateConfigPath = value;
|
||||
});
|
||||
socket.on('autoUpdater-currentVersion', () => {
|
||||
electronSocket.emit('autoUpdater-currentVersion-completed', electron_updater_1.autoUpdater.currentVersion);
|
||||
socket.on("autoUpdater-currentVersion", () => {
|
||||
electronSocket.emit("autoUpdater-currentVersion-completed", electron_updater_1.autoUpdater.currentVersion);
|
||||
});
|
||||
socket.on('autoUpdater-channel', () => {
|
||||
electronSocket.emit('autoUpdater-channel-completed', electron_updater_1.autoUpdater.channel || '');
|
||||
socket.on("autoUpdater-channel", () => {
|
||||
electronSocket.emit("autoUpdater-channel-completed", electron_updater_1.autoUpdater.channel || "");
|
||||
});
|
||||
socket.on('autoUpdater-channel-set', (value) => {
|
||||
socket.on("autoUpdater-channel-set", (value) => {
|
||||
electron_updater_1.autoUpdater.channel = value;
|
||||
});
|
||||
socket.on('autoUpdater-requestHeaders', () => {
|
||||
electronSocket.emit('autoUpdater-requestHeaders-completed', electron_updater_1.autoUpdater.requestHeaders);
|
||||
socket.on("autoUpdater-requestHeaders", () => {
|
||||
electronSocket.emit("autoUpdater-requestHeaders-completed", electron_updater_1.autoUpdater.requestHeaders);
|
||||
});
|
||||
socket.on('autoUpdater-requestHeaders-set', (value) => {
|
||||
socket.on("autoUpdater-requestHeaders-set", (value) => {
|
||||
electron_updater_1.autoUpdater.requestHeaders = value;
|
||||
});
|
||||
socket.on('autoUpdater-checkForUpdatesAndNotify', async (guid) => {
|
||||
electron_updater_1.autoUpdater.checkForUpdatesAndNotify().then((updateCheckResult) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdatesAndNotify-completed' + guid, updateCheckResult);
|
||||
}).catch((error) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdatesAndNotifyError' + guid, error);
|
||||
socket.on("autoUpdater-checkForUpdatesAndNotify", async (guid) => {
|
||||
electron_updater_1.autoUpdater
|
||||
.checkForUpdatesAndNotify()
|
||||
.then((updateCheckResult) => {
|
||||
normalize(updateCheckResult?.updateInfo);
|
||||
electronSocket.emit("autoUpdater-checkForUpdatesAndNotify-completed" + guid, updateCheckResult);
|
||||
})
|
||||
.catch((error) => {
|
||||
electronSocket.emit("autoUpdater-checkForUpdatesAndNotifyError" + guid, error);
|
||||
});
|
||||
});
|
||||
socket.on('autoUpdater-checkForUpdates', async (guid) => {
|
||||
electron_updater_1.autoUpdater.checkForUpdates().then((updateCheckResult) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdates-completed' + guid, updateCheckResult);
|
||||
}).catch((error) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdatesError' + guid, error);
|
||||
socket.on("autoUpdater-checkForUpdates", async (guid) => {
|
||||
electron_updater_1.autoUpdater
|
||||
.checkForUpdates()
|
||||
.then((updateCheckResult) => {
|
||||
normalize(updateCheckResult?.updateInfo);
|
||||
electronSocket.emit("autoUpdater-checkForUpdates-completed" + guid, updateCheckResult);
|
||||
})
|
||||
.catch((error) => {
|
||||
electronSocket.emit("autoUpdater-checkForUpdatesError" + guid, error);
|
||||
});
|
||||
});
|
||||
socket.on('autoUpdater-quitAndInstall', async (isSilent, isForceRunAfter) => {
|
||||
socket.on("autoUpdater-quitAndInstall", async (isSilent, isForceRunAfter) => {
|
||||
electron_updater_1.autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
});
|
||||
socket.on('autoUpdater-downloadUpdate', async (guid) => {
|
||||
socket.on("autoUpdater-downloadUpdate", async (guid) => {
|
||||
const downloadedPath = await electron_updater_1.autoUpdater.downloadUpdate();
|
||||
electronSocket.emit('autoUpdater-downloadUpdate-completed' + guid, downloadedPath);
|
||||
electronSocket.emit("autoUpdater-downloadUpdate-completed" + guid, downloadedPath);
|
||||
});
|
||||
socket.on('autoUpdater-getFeedURL', async (guid) => {
|
||||
socket.on("autoUpdater-getFeedURL", async (guid) => {
|
||||
const feedUrl = await electron_updater_1.autoUpdater.getFeedURL();
|
||||
electronSocket.emit('autoUpdater-getFeedURL-completed' + guid, feedUrl || '');
|
||||
electronSocket.emit("autoUpdater-getFeedURL-completed" + guid, feedUrl || "");
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=autoUpdater.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,143 +1,207 @@
|
||||
import { Socket } from 'net';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
let electronSocket;
|
||||
import type { Socket } from "net";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
function normalize(updateInfo) {
|
||||
if (typeof updateInfo?.releaseNotes === "string") {
|
||||
updateInfo.releaseNotes = [{ note: updateInfo.releaseNotes }];
|
||||
} else if (Array.isArray(updateInfo?.releaseNotes)) {
|
||||
updateInfo.releaseNotes = updateInfo.releaseNotes.map((entry) =>
|
||||
typeof entry === "string" ? { note: entry } : entry,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
electronSocket = socket;
|
||||
|
||||
socket.on('register-autoUpdater-error', (id) => {
|
||||
autoUpdater.on('error', (error) => {
|
||||
electronSocket.emit('autoUpdater-error' + id, error.message);
|
||||
});
|
||||
socket.on("register-autoUpdater-error", (id) => {
|
||||
autoUpdater.on("error", (error) => {
|
||||
electronSocket.emit("autoUpdater-error" + id, error.message);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-autoUpdater-checking-for-update', (id) => {
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
electronSocket.emit('autoUpdater-checking-for-update' + id);
|
||||
});
|
||||
socket.on("register-autoUpdater-checking-for-update", (id) => {
|
||||
autoUpdater.on("checking-for-update", () => {
|
||||
electronSocket.emit("autoUpdater-checking-for-update" + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-autoUpdater-update-available', (id) => {
|
||||
autoUpdater.on('update-available', (updateInfo) => {
|
||||
electronSocket.emit('autoUpdater-update-available' + id, updateInfo);
|
||||
});
|
||||
socket.on("register-autoUpdater-update-available", (id) => {
|
||||
autoUpdater.on("update-available", (updateInfo) => {
|
||||
normalize(updateInfo);
|
||||
electronSocket.emit("autoUpdater-update-available" + id, updateInfo);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-autoUpdater-update-not-available', (id) => {
|
||||
autoUpdater.on('update-not-available', (updateInfo) => {
|
||||
electronSocket.emit('autoUpdater-update-not-available' + id, updateInfo);
|
||||
});
|
||||
socket.on("register-autoUpdater-update-not-available", (id) => {
|
||||
autoUpdater.on("update-not-available", (updateInfo) => {
|
||||
normalize(updateInfo);
|
||||
electronSocket.emit("autoUpdater-update-not-available" + id, updateInfo);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-autoUpdater-download-progress', (id) => {
|
||||
autoUpdater.on('download-progress', (progressInfo) => {
|
||||
electronSocket.emit('autoUpdater-download-progress' + id, progressInfo);
|
||||
});
|
||||
socket.on("register-autoUpdater-download-progress", (id) => {
|
||||
autoUpdater.on("download-progress", (progressInfo) => {
|
||||
electronSocket.emit("autoUpdater-download-progress" + id, progressInfo);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-autoUpdater-update-downloaded', (id) => {
|
||||
autoUpdater.on('update-downloaded', (updateInfo) => {
|
||||
electronSocket.emit('autoUpdater-update-downloaded' + id, updateInfo);
|
||||
});
|
||||
socket.on("register-autoUpdater-update-downloaded", (id) => {
|
||||
autoUpdater.on("update-downloaded", (updateInfo) => {
|
||||
normalize(updateInfo);
|
||||
electronSocket.emit("autoUpdater-update-downloaded" + id, updateInfo);
|
||||
});
|
||||
});
|
||||
|
||||
// Properties *****
|
||||
// Properties *****
|
||||
|
||||
socket.on('autoUpdater-autoDownload', () => {
|
||||
electronSocket.emit('autoUpdater-autoDownload-completed', autoUpdater.autoDownload);
|
||||
});
|
||||
socket.on("autoUpdater-autoDownload", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-autoDownload-completed",
|
||||
autoUpdater.autoDownload,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-autoDownload-set', (value) => {
|
||||
autoUpdater.autoDownload = value;
|
||||
});
|
||||
socket.on("autoUpdater-autoDownload-set", (value) => {
|
||||
autoUpdater.autoDownload = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-autoInstallOnAppQuit', () => {
|
||||
electronSocket.emit('autoUpdater-autoInstallOnAppQuit-completed', autoUpdater.autoInstallOnAppQuit);
|
||||
});
|
||||
socket.on("autoUpdater-autoInstallOnAppQuit", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-autoInstallOnAppQuit-completed",
|
||||
autoUpdater.autoInstallOnAppQuit,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-autoInstallOnAppQuit-set', (value) => {
|
||||
autoUpdater.autoInstallOnAppQuit = value;
|
||||
});
|
||||
socket.on("autoUpdater-autoInstallOnAppQuit-set", (value) => {
|
||||
autoUpdater.autoInstallOnAppQuit = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-allowPrerelease', () => {
|
||||
electronSocket.emit('autoUpdater-allowPrerelease-completed', autoUpdater.allowPrerelease);
|
||||
});
|
||||
socket.on("autoUpdater-allowPrerelease", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-allowPrerelease-completed",
|
||||
autoUpdater.allowPrerelease,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-allowPrerelease-set', (value) => {
|
||||
autoUpdater.allowPrerelease = value;
|
||||
});
|
||||
socket.on("autoUpdater-allowPrerelease-set", (value) => {
|
||||
autoUpdater.allowPrerelease = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-fullChangelog', () => {
|
||||
electronSocket.emit('autoUpdater-fullChangelog-completed', autoUpdater.fullChangelog);
|
||||
});
|
||||
socket.on("autoUpdater-fullChangelog", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-fullChangelog-completed",
|
||||
autoUpdater.fullChangelog,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-fullChangelog-set', (value) => {
|
||||
autoUpdater.fullChangelog = value;
|
||||
});
|
||||
socket.on("autoUpdater-fullChangelog-set", (value) => {
|
||||
autoUpdater.fullChangelog = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-allowDowngrade', () => {
|
||||
electronSocket.emit('autoUpdater-allowDowngrade-completed', autoUpdater.allowDowngrade);
|
||||
});
|
||||
socket.on("autoUpdater-allowDowngrade", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-allowDowngrade-completed",
|
||||
autoUpdater.allowDowngrade,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-allowDowngrade-set', (value) => {
|
||||
autoUpdater.allowDowngrade = value;
|
||||
});
|
||||
socket.on("autoUpdater-allowDowngrade-set", (value) => {
|
||||
autoUpdater.allowDowngrade = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-updateConfigPath', () => {
|
||||
electronSocket.emit('autoUpdater-updateConfigPath-completed', autoUpdater.updateConfigPath || '');
|
||||
});
|
||||
socket.on("autoUpdater-updateConfigPath", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-updateConfigPath-completed",
|
||||
autoUpdater.updateConfigPath || "",
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-updateConfigPath-set', (value) => {
|
||||
autoUpdater.updateConfigPath = value;
|
||||
});
|
||||
socket.on("autoUpdater-updateConfigPath-set", (value) => {
|
||||
autoUpdater.updateConfigPath = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-currentVersion', () => {
|
||||
electronSocket.emit('autoUpdater-currentVersion-completed', autoUpdater.currentVersion);
|
||||
});
|
||||
socket.on("autoUpdater-currentVersion", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-currentVersion-completed",
|
||||
autoUpdater.currentVersion,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-channel', () => {
|
||||
electronSocket.emit('autoUpdater-channel-completed', autoUpdater.channel || '');
|
||||
});
|
||||
socket.on("autoUpdater-channel", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-channel-completed",
|
||||
autoUpdater.channel || "",
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-channel-set', (value) => {
|
||||
autoUpdater.channel = value;
|
||||
});
|
||||
socket.on("autoUpdater-channel-set", (value) => {
|
||||
autoUpdater.channel = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-requestHeaders', () => {
|
||||
electronSocket.emit('autoUpdater-requestHeaders-completed', autoUpdater.requestHeaders);
|
||||
});
|
||||
socket.on("autoUpdater-requestHeaders", () => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-requestHeaders-completed",
|
||||
autoUpdater.requestHeaders,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-requestHeaders-set', (value) => {
|
||||
autoUpdater.requestHeaders = value;
|
||||
});
|
||||
socket.on("autoUpdater-requestHeaders-set", (value) => {
|
||||
autoUpdater.requestHeaders = value;
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-checkForUpdatesAndNotify', async (guid) => {
|
||||
autoUpdater.checkForUpdatesAndNotify().then((updateCheckResult) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdatesAndNotify-completed' + guid, updateCheckResult);
|
||||
}).catch((error) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdatesAndNotifyError' + guid, error);
|
||||
});
|
||||
});
|
||||
socket.on("autoUpdater-checkForUpdatesAndNotify", async (guid) => {
|
||||
autoUpdater
|
||||
.checkForUpdatesAndNotify()
|
||||
.then((updateCheckResult) => {
|
||||
normalize(updateCheckResult?.updateInfo);
|
||||
electronSocket.emit(
|
||||
"autoUpdater-checkForUpdatesAndNotify-completed" + guid,
|
||||
updateCheckResult,
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
electronSocket.emit(
|
||||
"autoUpdater-checkForUpdatesAndNotifyError" + guid,
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-checkForUpdates', async (guid) => {
|
||||
autoUpdater.checkForUpdates().then((updateCheckResult) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdates-completed' + guid, updateCheckResult);
|
||||
}).catch((error) => {
|
||||
electronSocket.emit('autoUpdater-checkForUpdatesError' + guid, error);
|
||||
});
|
||||
});
|
||||
socket.on("autoUpdater-checkForUpdates", async (guid) => {
|
||||
autoUpdater
|
||||
.checkForUpdates()
|
||||
.then((updateCheckResult) => {
|
||||
normalize(updateCheckResult?.updateInfo);
|
||||
electronSocket.emit(
|
||||
"autoUpdater-checkForUpdates-completed" + guid,
|
||||
updateCheckResult,
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
electronSocket.emit("autoUpdater-checkForUpdatesError" + guid, error);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-quitAndInstall', async (isSilent, isForceRunAfter) => {
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
});
|
||||
socket.on("autoUpdater-quitAndInstall", async (isSilent, isForceRunAfter) => {
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-downloadUpdate', async (guid) => {
|
||||
const downloadedPath = await autoUpdater.downloadUpdate();
|
||||
electronSocket.emit('autoUpdater-downloadUpdate-completed' + guid, downloadedPath);
|
||||
});
|
||||
socket.on("autoUpdater-downloadUpdate", async (guid) => {
|
||||
const downloadedPath = await autoUpdater.downloadUpdate();
|
||||
electronSocket.emit(
|
||||
"autoUpdater-downloadUpdate-completed" + guid,
|
||||
downloadedPath,
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('autoUpdater-getFeedURL', async (guid) => {
|
||||
const feedUrl = await autoUpdater.getFeedURL();
|
||||
electronSocket.emit('autoUpdater-getFeedURL-completed' + guid, feedUrl || '');
|
||||
});
|
||||
socket.on("autoUpdater-getFeedURL", async (guid) => {
|
||||
const feedUrl = await autoUpdater.getFeedURL();
|
||||
electronSocket.emit(
|
||||
"autoUpdater-getFeedURL-completed" + guid,
|
||||
feedUrl || "",
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.browserViewMediateService = exports.browserViewApi = void 0;
|
||||
const electron_1 = require("electron");
|
||||
const browserViews = (global['browserViews'] = global['browserViews'] || []);
|
||||
let browserView, electronSocket;
|
||||
const proxyToCredentialsMap = (global['proxyToCredentialsMap'] = global['proxyToCredentialsMap'] || []);
|
||||
const browserViews = (global["browserViews"] =
|
||||
global["browserViews"] || []);
|
||||
const proxyToCredentialsMap = (global["proxyToCredentialsMap"] = global["proxyToCredentialsMap"] || []);
|
||||
let browserView;
|
||||
let electronSocket;
|
||||
const browserViewApi = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('createBrowserView', (options) => {
|
||||
if (!hasOwnChildreen(options, 'webPreferences', 'nodeIntegration')) {
|
||||
options = { ...options, webPreferences: { nodeIntegration: true, contextIsolation: false } };
|
||||
socket.on("createBrowserView", (options) => {
|
||||
if (!hasOwnChildreen(options, "webPreferences", "nodeIntegration")) {
|
||||
options = {
|
||||
...options,
|
||||
webPreferences: { nodeIntegration: true, contextIsolation: false },
|
||||
};
|
||||
}
|
||||
browserView = new electron_1.BrowserView(options);
|
||||
browserView['id'] = browserViews.length + 1;
|
||||
browserView["id"] = browserViews.length + 1;
|
||||
if (options.proxy) {
|
||||
browserView.webContents.session.setProxy({ proxyRules: options.proxy });
|
||||
}
|
||||
@@ -20,19 +25,19 @@ const browserViewApi = (socket) => {
|
||||
proxyToCredentialsMap[options.proxy] = options.proxyCredentials;
|
||||
}
|
||||
browserViews.push(browserView);
|
||||
electronSocket.emit('BrowserViewCreated', browserView['id']);
|
||||
electronSocket.emit("BrowserViewCreated", browserView["id"]);
|
||||
});
|
||||
socket.on('browserView-bounds', (id) => {
|
||||
socket.on("browserView-bounds", (id) => {
|
||||
const bounds = getBrowserViewById(id).getBounds();
|
||||
electronSocket.emit('browserView-bounds-completed', bounds);
|
||||
electronSocket.emit("browserView-bounds-completed", bounds);
|
||||
});
|
||||
socket.on('browserView-bounds-set', (id, bounds) => {
|
||||
socket.on("browserView-bounds-set", (id, bounds) => {
|
||||
getBrowserViewById(id).setBounds(bounds);
|
||||
});
|
||||
socket.on('browserView-setAutoResize', (id, options) => {
|
||||
socket.on("browserView-setAutoResize", (id, options) => {
|
||||
getBrowserViewById(id).setAutoResize(options);
|
||||
});
|
||||
socket.on('browserView-setBackgroundColor', (id, color) => {
|
||||
socket.on("browserView-setBackgroundColor", (id, color) => {
|
||||
getBrowserViewById(id).setBackgroundColor(color);
|
||||
});
|
||||
function hasOwnChildreen(obj, ...childNames) {
|
||||
@@ -53,7 +58,7 @@ exports.browserViewMediateService = browserViewMediateService;
|
||||
function getBrowserViewById(id) {
|
||||
for (let index = 0; index < browserViews.length; index++) {
|
||||
const browserViewItem = browserViews[index];
|
||||
if (browserViewItem['id'] === id) {
|
||||
if (browserViewItem["id"] === id) {
|
||||
return browserViewItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"browserView.js","sourceRoot":"","sources":["browserView.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"browserView.js","sourceRoot":"","sources":["browserView.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,74 +1,83 @@
|
||||
import { Socket } from 'net';
|
||||
import { BrowserView } from 'electron';
|
||||
const browserViews: BrowserView[] = (global['browserViews'] = global['browserViews'] || []) as BrowserView[];
|
||||
let browserView: BrowserView, electronSocket;
|
||||
const proxyToCredentialsMap: { [proxy: string]: string } = (global['proxyToCredentialsMap'] = global['proxyToCredentialsMap'] || []) as { [proxy: string]: string };
|
||||
import type { Socket } from "net";
|
||||
import { BrowserView } from "electron";
|
||||
|
||||
const browserViews: BrowserView[] = (global["browserViews"] =
|
||||
global["browserViews"] || []) as BrowserView[];
|
||||
const proxyToCredentialsMap: { [proxy: string]: string } = (global[
|
||||
"proxyToCredentialsMap"
|
||||
] = global["proxyToCredentialsMap"] || []) as { [proxy: string]: string };
|
||||
|
||||
let browserView: BrowserView;
|
||||
let electronSocket: Socket;
|
||||
|
||||
const browserViewApi = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
electronSocket = socket;
|
||||
|
||||
socket.on('createBrowserView', (options) => {
|
||||
if (!hasOwnChildreen(options, 'webPreferences', 'nodeIntegration')) {
|
||||
options = { ...options, webPreferences: { nodeIntegration: true, contextIsolation: false } };
|
||||
}
|
||||
|
||||
browserView = new BrowserView(options);
|
||||
browserView['id'] = browserViews.length + 1;
|
||||
|
||||
if (options.proxy) {
|
||||
browserView.webContents.session.setProxy({proxyRules: options.proxy});
|
||||
}
|
||||
|
||||
if (options.proxy && options.proxyCredentials) {
|
||||
proxyToCredentialsMap[options.proxy] = options.proxyCredentials;
|
||||
}
|
||||
|
||||
browserViews.push(browserView);
|
||||
|
||||
electronSocket.emit('BrowserViewCreated', browserView['id']);
|
||||
});
|
||||
|
||||
socket.on('browserView-bounds', (id) => {
|
||||
const bounds = getBrowserViewById(id).getBounds();
|
||||
|
||||
electronSocket.emit('browserView-bounds-completed', bounds);
|
||||
});
|
||||
|
||||
socket.on('browserView-bounds-set', (id, bounds) => {
|
||||
getBrowserViewById(id).setBounds(bounds);
|
||||
});
|
||||
|
||||
socket.on('browserView-setAutoResize', (id, options) => {
|
||||
getBrowserViewById(id).setAutoResize(options);
|
||||
});
|
||||
|
||||
socket.on('browserView-setBackgroundColor', (id, color) => {
|
||||
getBrowserViewById(id).setBackgroundColor(color);
|
||||
});
|
||||
|
||||
function hasOwnChildreen(obj, ...childNames) {
|
||||
for (let i = 0; i < childNames.length; i++) {
|
||||
if (!obj || !obj.hasOwnProperty(childNames[i])) {
|
||||
return false;
|
||||
}
|
||||
obj = obj[childNames[i]];
|
||||
}
|
||||
|
||||
return true;
|
||||
socket.on("createBrowserView", (options) => {
|
||||
if (!hasOwnChildreen(options, "webPreferences", "nodeIntegration")) {
|
||||
options = {
|
||||
...options,
|
||||
webPreferences: { nodeIntegration: true, contextIsolation: false },
|
||||
};
|
||||
}
|
||||
|
||||
browserView = new BrowserView(options);
|
||||
browserView["id"] = browserViews.length + 1;
|
||||
|
||||
if (options.proxy) {
|
||||
browserView.webContents.session.setProxy({ proxyRules: options.proxy });
|
||||
}
|
||||
|
||||
if (options.proxy && options.proxyCredentials) {
|
||||
proxyToCredentialsMap[options.proxy] = options.proxyCredentials;
|
||||
}
|
||||
|
||||
browserViews.push(browserView);
|
||||
|
||||
electronSocket.emit("BrowserViewCreated", browserView["id"]);
|
||||
});
|
||||
|
||||
socket.on("browserView-bounds", (id) => {
|
||||
const bounds = getBrowserViewById(id).getBounds();
|
||||
|
||||
electronSocket.emit("browserView-bounds-completed", bounds);
|
||||
});
|
||||
|
||||
socket.on("browserView-bounds-set", (id, bounds) => {
|
||||
getBrowserViewById(id).setBounds(bounds);
|
||||
});
|
||||
|
||||
socket.on("browserView-setAutoResize", (id, options) => {
|
||||
getBrowserViewById(id).setAutoResize(options);
|
||||
});
|
||||
|
||||
socket.on("browserView-setBackgroundColor", (id, color) => {
|
||||
getBrowserViewById(id).setBackgroundColor(color);
|
||||
});
|
||||
|
||||
function hasOwnChildreen(obj, ...childNames) {
|
||||
for (let i = 0; i < childNames.length; i++) {
|
||||
if (!obj || !obj.hasOwnProperty(childNames[i])) {
|
||||
return false;
|
||||
}
|
||||
obj = obj[childNames[i]];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const browserViewMediateService = (browserViewId: number): BrowserView => {
|
||||
return getBrowserViewById(browserViewId);
|
||||
return getBrowserViewById(browserViewId);
|
||||
};
|
||||
|
||||
function getBrowserViewById(id: number) {
|
||||
for (let index = 0; index < browserViews.length; index++) {
|
||||
const browserViewItem = browserViews[index];
|
||||
if (browserViewItem['id'] === id) {
|
||||
return browserViewItem;
|
||||
}
|
||||
for (let index = 0; index < browserViews.length; index++) {
|
||||
const browserViewItem = browserViews[index];
|
||||
if (browserViewItem["id"] === id) {
|
||||
return browserViewItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { browserViewApi, browserViewMediateService };
|
||||
|
||||
@@ -1,12 +1,45 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
const path = __importStar(require("path"));
|
||||
const electron_1 = require("electron");
|
||||
const browserView_1 = require("./browserView");
|
||||
const path = require("path");
|
||||
const windows = (global["browserWindows"] =
|
||||
global["browserWindows"] || []);
|
||||
let readyToShowWindowsIds = [];
|
||||
let window, lastOptions, electronSocket;
|
||||
let mainWindowURL;
|
||||
let window;
|
||||
let electronSocket;
|
||||
const proxyToCredentialsMap = (global["proxyToCredentialsMap"] = global["proxyToCredentialsMap"] || []);
|
||||
module.exports = (socket, app) => {
|
||||
electronSocket = socket;
|
||||
@@ -112,6 +145,12 @@ module.exports = (socket, app) => {
|
||||
electronSocket.emit("browserWindow-move" + id);
|
||||
});
|
||||
});
|
||||
socket.on("register-browserWindow-bounds-changed", (id) => {
|
||||
const window = getWindowById(id);
|
||||
const cb = () => electronSocket.emit("browserWindow-bounds-changed" + id, window.getBounds());
|
||||
window.on("resize", cb);
|
||||
window.on("move", cb);
|
||||
});
|
||||
socket.on("register-browserWindow-moved", (id) => {
|
||||
getWindowById(id).on("moved", () => {
|
||||
electronSocket.emit("browserWindow-moved" + id);
|
||||
@@ -163,6 +202,18 @@ module.exports = (socket, app) => {
|
||||
});
|
||||
});
|
||||
socket.on("createBrowserWindow", (options, loadUrl) => {
|
||||
createWindow(options, loadUrl, "BrowserWindowCreated");
|
||||
});
|
||||
// Allows the host (main.js) to bring the main window back when the app is activated
|
||||
// again on macOS after all windows have been closed.
|
||||
global["recreateMainWindow"] = () => {
|
||||
if (electron_1.BrowserWindow.getAllWindows().length > 0 || !app["mainWindowOptions"]) {
|
||||
return false;
|
||||
}
|
||||
createWindow(app["mainWindowOptions"], app["mainWindowURL"], "BrowserWindowRecreated");
|
||||
return true;
|
||||
};
|
||||
function createWindow(options, loadUrl, createdEventName) {
|
||||
if (options.webPreferences &&
|
||||
!("nodeIntegration" in options.webPreferences)) {
|
||||
options = {
|
||||
@@ -186,18 +237,19 @@ module.exports = (socket, app) => {
|
||||
delete options.isRunningBlazor;
|
||||
// we dont want to recreate the window when watch is ready.
|
||||
if (app.commandLine.hasSwitch("watch") &&
|
||||
app["mainWindowURL"] === loadUrl) {
|
||||
app["mainWindowURL"] === loadUrl &&
|
||||
app["mainWindow"] &&
|
||||
!app["mainWindow"].isDestroyed()) {
|
||||
window = app["mainWindow"];
|
||||
if (window) {
|
||||
window.reload();
|
||||
window.reload();
|
||||
synchronizeWindowRegistry();
|
||||
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
|
||||
windows.push(window);
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
return;
|
||||
}
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
window = new electron_1.BrowserWindow(options);
|
||||
}
|
||||
window = new electron_1.BrowserWindow(options);
|
||||
if (options.proxy) {
|
||||
window.webContents.session.setProxy({ proxyRules: options.proxy });
|
||||
}
|
||||
@@ -212,32 +264,32 @@ module.exports = (socket, app) => {
|
||||
readyToShowWindowsIds.push(window.id);
|
||||
}
|
||||
});
|
||||
lastOptions = options;
|
||||
window.on("closed", (sender) => {
|
||||
for (let index = 0; index < windows.length; index++) {
|
||||
const windowItem = windows[index];
|
||||
try {
|
||||
windowItem.id;
|
||||
}
|
||||
catch (error) {
|
||||
if (error.message === "Object has been destroyed") {
|
||||
windows.splice(index, 1);
|
||||
const ids = [];
|
||||
windows.forEach((x) => ids.push(x.id));
|
||||
electronSocket.emit("BrowserWindowClosed", ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
app.on("activate", () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (window === null && lastOptions) {
|
||||
window = new electron_1.BrowserWindow(lastOptions);
|
||||
}
|
||||
window.on("closed", () => {
|
||||
synchronizeWindowRegistry();
|
||||
emitBrowserWindowClosed();
|
||||
});
|
||||
if (loadUrl) {
|
||||
window.loadURL(loadUrl);
|
||||
// Append authentication token to initial URL if available
|
||||
const token = global["authToken"];
|
||||
if (token) {
|
||||
try {
|
||||
const url = new URL(loadUrl);
|
||||
const isLocal = url.hostname === "localhost" ||
|
||||
url.hostname === "127.0.0.1" ||
|
||||
url.hostname === "::1";
|
||||
if (isLocal) {
|
||||
url.searchParams.set("token", token);
|
||||
}
|
||||
window.loadURL(url.toString());
|
||||
}
|
||||
catch {
|
||||
// Handle invalid URLs or file:// URLs if needed
|
||||
window.loadURL(loadUrl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
window.loadURL(loadUrl);
|
||||
}
|
||||
}
|
||||
if (app.commandLine.hasSwitch("clear-cache") &&
|
||||
app.commandLine.getSwitchValue("clear-cache")) {
|
||||
@@ -248,10 +300,14 @@ module.exports = (socket, app) => {
|
||||
if (app["mainWindowURL"] == undefined || app["mainWindowURL"] == "") {
|
||||
app["mainWindowURL"] = loadUrl;
|
||||
app["mainWindow"] = window;
|
||||
app["mainWindowOptions"] = options;
|
||||
}
|
||||
else if (app["mainWindowURL"] === loadUrl) {
|
||||
app["mainWindow"] = window;
|
||||
}
|
||||
windows.push(window);
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
});
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
}
|
||||
socket.on("browserWindowDestroy", (id) => {
|
||||
getWindowById(id).destroy();
|
||||
});
|
||||
@@ -643,12 +699,47 @@ module.exports = (socket, app) => {
|
||||
getWindowById(id).setBrowserView((0, browserView_1.browserViewMediateService)(browserViewId));
|
||||
});
|
||||
function getWindowById(id) {
|
||||
const runtimeWindow = electron_1.BrowserWindow.fromId(id);
|
||||
if (runtimeWindow) {
|
||||
return runtimeWindow;
|
||||
}
|
||||
synchronizeWindowRegistry();
|
||||
for (let index = 0; index < windows.length; index++) {
|
||||
const element = windows[index];
|
||||
if (element.id === id) {
|
||||
if (tryGetWindowId(element) === id) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
throw new Error(`BrowserWindow with id '${id}' was not found.`);
|
||||
}
|
||||
function tryGetWindowId(element) {
|
||||
try {
|
||||
return element.id;
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function synchronizeWindowRegistry() {
|
||||
const runtimeWindows = electron_1.BrowserWindow.getAllWindows();
|
||||
const runtimeWindowIds = new Set(runtimeWindows.map((entry) => entry.id));
|
||||
for (let index = windows.length - 1; index >= 0; index--) {
|
||||
const windowId = tryGetWindowId(windows[index]);
|
||||
if (windowId === null || !runtimeWindowIds.has(windowId)) {
|
||||
windows.splice(index, 1);
|
||||
}
|
||||
}
|
||||
readyToShowWindowsIds = readyToShowWindowsIds.filter((entryId) => runtimeWindowIds.has(entryId));
|
||||
}
|
||||
function emitBrowserWindowClosed() {
|
||||
const ids = [];
|
||||
for (const entry of windows) {
|
||||
const windowId = tryGetWindowId(entry);
|
||||
if (windowId !== null) {
|
||||
ids.push(windowId);
|
||||
}
|
||||
}
|
||||
electronSocket.emit("BrowserWindowClosed", ids);
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=browserWindows.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,12 +1,17 @@
|
||||
import { Socket } from "net";
|
||||
import { BrowserWindow, Menu, nativeImage } from "electron";
|
||||
import * as path from "path";
|
||||
import type { Socket } from "net";
|
||||
import { BrowserWindow, Menu } from "electron";
|
||||
|
||||
import { browserViewMediateService } from "./browserView";
|
||||
const path = require("path");
|
||||
|
||||
const windows: Electron.BrowserWindow[] = (global["browserWindows"] =
|
||||
global["browserWindows"] || []) as Electron.BrowserWindow[];
|
||||
|
||||
let readyToShowWindowsIds: number[] = [];
|
||||
let window, lastOptions, electronSocket;
|
||||
let mainWindowURL;
|
||||
|
||||
let window;
|
||||
let electronSocket;
|
||||
|
||||
const proxyToCredentialsMap: { [proxy: string]: string } = (global[
|
||||
"proxyToCredentialsMap"
|
||||
] = global["proxyToCredentialsMap"] || []) as { [proxy: string]: string };
|
||||
@@ -32,7 +37,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
socket.on("register-browserWindow-ready-to-show", (id) => {
|
||||
if (readyToShowWindowsIds.includes(id)) {
|
||||
readyToShowWindowsIds = readyToShowWindowsIds.filter(
|
||||
(value) => value !== id
|
||||
(value) => value !== id,
|
||||
);
|
||||
electronSocket.emit("browserWindow-ready-to-show" + id);
|
||||
}
|
||||
@@ -139,6 +144,17 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("register-browserWindow-bounds-changed", (id) => {
|
||||
const window = getWindowById(id);
|
||||
const cb = () =>
|
||||
electronSocket.emit(
|
||||
"browserWindow-bounds-changed" + id,
|
||||
window.getBounds(),
|
||||
);
|
||||
window.on("resize", cb);
|
||||
window.on("move", cb);
|
||||
});
|
||||
|
||||
socket.on("register-browserWindow-moved", (id) => {
|
||||
getWindowById(id).on("moved", () => {
|
||||
electronSocket.emit("browserWindow-moved" + id);
|
||||
@@ -200,6 +216,26 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
});
|
||||
|
||||
socket.on("createBrowserWindow", (options, loadUrl) => {
|
||||
createWindow(options, loadUrl, "BrowserWindowCreated");
|
||||
});
|
||||
|
||||
// Allows the host (main.js) to bring the main window back when the app is activated
|
||||
// again on macOS after all windows have been closed.
|
||||
global["recreateMainWindow"] = () => {
|
||||
if (BrowserWindow.getAllWindows().length > 0 || !app["mainWindowOptions"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
createWindow(
|
||||
app["mainWindowOptions"],
|
||||
app["mainWindowURL"],
|
||||
"BrowserWindowRecreated",
|
||||
);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
function createWindow(options, loadUrl, createdEventName: string) {
|
||||
if (
|
||||
options.webPreferences &&
|
||||
!("nodeIntegration" in options.webPreferences)
|
||||
@@ -224,7 +260,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
__dirname,
|
||||
"..",
|
||||
"scripts",
|
||||
"blazor-preload.js"
|
||||
"blazor-preload.js",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -233,19 +269,24 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
// we dont want to recreate the window when watch is ready.
|
||||
if (
|
||||
app.commandLine.hasSwitch("watch") &&
|
||||
app["mainWindowURL"] === loadUrl
|
||||
app["mainWindowURL"] === loadUrl &&
|
||||
app["mainWindow"] &&
|
||||
!app["mainWindow"].isDestroyed()
|
||||
) {
|
||||
window = app["mainWindow"];
|
||||
if (window) {
|
||||
window.reload();
|
||||
window.reload();
|
||||
synchronizeWindowRegistry();
|
||||
|
||||
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
|
||||
windows.push(window);
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
window = new BrowserWindow(options);
|
||||
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
return;
|
||||
}
|
||||
|
||||
window = new BrowserWindow(options);
|
||||
|
||||
if (options.proxy) {
|
||||
window.webContents.session.setProxy({ proxyRules: options.proxy });
|
||||
}
|
||||
@@ -257,42 +298,43 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
window.on("ready-to-show", () => {
|
||||
if (readyToShowWindowsIds.includes(window.id)) {
|
||||
readyToShowWindowsIds = readyToShowWindowsIds.filter(
|
||||
(value) => value !== window.id
|
||||
(value) => value !== window.id,
|
||||
);
|
||||
} else {
|
||||
readyToShowWindowsIds.push(window.id);
|
||||
}
|
||||
});
|
||||
|
||||
lastOptions = options;
|
||||
|
||||
window.on("closed", (sender) => {
|
||||
for (let index = 0; index < windows.length; index++) {
|
||||
const windowItem = windows[index];
|
||||
try {
|
||||
windowItem.id;
|
||||
} catch (error) {
|
||||
if (error.message === "Object has been destroyed") {
|
||||
windows.splice(index, 1);
|
||||
|
||||
const ids = [];
|
||||
windows.forEach((x) => ids.push(x.id));
|
||||
electronSocket.emit("BrowserWindowClosed", ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (window === null && lastOptions) {
|
||||
window = new BrowserWindow(lastOptions);
|
||||
}
|
||||
window.on("closed", () => {
|
||||
synchronizeWindowRegistry();
|
||||
emitBrowserWindowClosed();
|
||||
});
|
||||
|
||||
if (loadUrl) {
|
||||
window.loadURL(loadUrl);
|
||||
// Append authentication token to initial URL if available
|
||||
const token = global["authToken"];
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const url = new URL(loadUrl);
|
||||
|
||||
const isLocal =
|
||||
url.hostname === "localhost" ||
|
||||
url.hostname === "127.0.0.1" ||
|
||||
url.hostname === "::1";
|
||||
|
||||
if (isLocal) {
|
||||
url.searchParams.set("token", token);
|
||||
}
|
||||
|
||||
window.loadURL(url.toString());
|
||||
} catch {
|
||||
// Handle invalid URLs or file:// URLs if needed
|
||||
window.loadURL(loadUrl);
|
||||
}
|
||||
} else {
|
||||
window.loadURL(loadUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -307,11 +349,14 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
if (app["mainWindowURL"] == undefined || app["mainWindowURL"] == "") {
|
||||
app["mainWindowURL"] = loadUrl;
|
||||
app["mainWindow"] = window;
|
||||
app["mainWindowOptions"] = options;
|
||||
} else if (app["mainWindowURL"] === loadUrl) {
|
||||
app["mainWindow"] = window;
|
||||
}
|
||||
|
||||
windows.push(window);
|
||||
electronSocket.emit("BrowserWindowCreated", window.id);
|
||||
});
|
||||
electronSocket.emit(createdEventName, window.id);
|
||||
}
|
||||
|
||||
socket.on("browserWindowDestroy", (id) => {
|
||||
getWindowById(id).destroy();
|
||||
@@ -524,7 +569,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
|
||||
electronSocket.emit(
|
||||
"browserWindow-isFullScreenable-completed",
|
||||
fullscreenable
|
||||
fullscreenable,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -609,7 +654,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
.toString(16);
|
||||
electronSocket.emit(
|
||||
"browserWindow-getNativeWindowHandle-completed",
|
||||
nativeWindowHandle
|
||||
nativeWindowHandle,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -622,7 +667,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"setRepresentedFilename failed (likely unsupported platform):",
|
||||
e
|
||||
e,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -637,12 +682,12 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"getRepresentedFilename failed (likely unsupported platform):",
|
||||
e
|
||||
e,
|
||||
);
|
||||
}
|
||||
electronSocket.emit(
|
||||
"browserWindow-getRepresentedFilename-completed",
|
||||
pathname
|
||||
pathname,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -734,7 +779,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
imagePath = path.join(
|
||||
__dirname.replace("api", ""),
|
||||
"bin",
|
||||
originalIconPath
|
||||
originalIconPath,
|
||||
);
|
||||
}
|
||||
const { nativeImage } = require("electron");
|
||||
@@ -751,7 +796,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
|
||||
const success = getWindowById(id).setThumbarButtons(thumbarButtons);
|
||||
electronSocket.emit("browserWindowSetThumbarButtons-completed", success);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
socket.on("browserWindowSetThumbnailClip", (id, rectangle) => {
|
||||
@@ -779,7 +824,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
|
||||
electronSocket.emit(
|
||||
"browserWindow-isMenuBarAutoHide-completed",
|
||||
isMenuBarAutoHide
|
||||
isMenuBarAutoHide,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -792,7 +837,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
|
||||
electronSocket.emit(
|
||||
"browserWindow-isMenuBarVisible-completed",
|
||||
isMenuBarVisible
|
||||
isMenuBarVisible,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -806,7 +851,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
|
||||
electronSocket.emit(
|
||||
"browserWindow-isVisibleOnAllWorkspaces-completed",
|
||||
isVisibleOnAllWorkspaces
|
||||
isVisibleOnAllWorkspaces,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -838,7 +883,7 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
|
||||
electronSocket.emit(
|
||||
"browserWindow-getParentWindow-completed",
|
||||
browserWindow.id
|
||||
browserWindow.id,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -867,11 +912,57 @@ export = (socket: Socket, app: Electron.App) => {
|
||||
});
|
||||
|
||||
function getWindowById(id: number): Electron.BrowserWindow {
|
||||
const runtimeWindow = BrowserWindow.fromId(id);
|
||||
if (runtimeWindow) {
|
||||
return runtimeWindow;
|
||||
}
|
||||
|
||||
synchronizeWindowRegistry();
|
||||
|
||||
for (let index = 0; index < windows.length; index++) {
|
||||
const element = windows[index];
|
||||
if (element.id === id) {
|
||||
if (tryGetWindowId(element) === id) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`BrowserWindow with id '${id}' was not found.`);
|
||||
}
|
||||
|
||||
function tryGetWindowId(element: Electron.BrowserWindow): number | null {
|
||||
try {
|
||||
return element.id;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function synchronizeWindowRegistry(): void {
|
||||
const runtimeWindows = BrowserWindow.getAllWindows();
|
||||
const runtimeWindowIds = new Set(runtimeWindows.map((entry) => entry.id));
|
||||
|
||||
for (let index = windows.length - 1; index >= 0; index--) {
|
||||
const windowId = tryGetWindowId(windows[index]);
|
||||
if (windowId === null || !runtimeWindowIds.has(windowId)) {
|
||||
windows.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
readyToShowWindowsIds = readyToShowWindowsIds.filter((entryId) =>
|
||||
runtimeWindowIds.has(entryId),
|
||||
);
|
||||
}
|
||||
|
||||
function emitBrowserWindowClosed(): void {
|
||||
const ids: number[] = [];
|
||||
|
||||
for (const entry of windows) {
|
||||
const windowId = tryGetWindowId(entry);
|
||||
if (windowId !== null) {
|
||||
ids.push(windowId);
|
||||
}
|
||||
}
|
||||
|
||||
electronSocket.emit("BrowserWindowClosed", ids);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,63 +3,65 @@ const electron_1 = require("electron");
|
||||
let electronSocket;
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('clipboard-readText', (type) => {
|
||||
socket.on("clipboard-readText", (type) => {
|
||||
const text = electron_1.clipboard.readText(type);
|
||||
electronSocket.emit('clipboard-readText-completed', text);
|
||||
electronSocket.emit("clipboard-readText-completed", text);
|
||||
});
|
||||
socket.on('clipboard-writeText', (text, type) => {
|
||||
socket.on("clipboard-writeText", (text, type) => {
|
||||
electron_1.clipboard.writeText(text, type);
|
||||
});
|
||||
socket.on('clipboard-readHTML', (type) => {
|
||||
socket.on("clipboard-readHTML", (type) => {
|
||||
const content = electron_1.clipboard.readHTML(type);
|
||||
electronSocket.emit('clipboard-readHTML-completed', content);
|
||||
electronSocket.emit("clipboard-readHTML-completed", content);
|
||||
});
|
||||
socket.on('clipboard-writeHTML', (markup, type) => {
|
||||
socket.on("clipboard-writeHTML", (markup, type) => {
|
||||
electron_1.clipboard.writeHTML(markup, type);
|
||||
});
|
||||
socket.on('clipboard-readRTF', (type) => {
|
||||
socket.on("clipboard-readRTF", (type) => {
|
||||
const content = electron_1.clipboard.readRTF(type);
|
||||
electronSocket.emit('clipboard-readRTF-completed', content);
|
||||
electronSocket.emit("clipboard-readRTF-completed", content);
|
||||
});
|
||||
socket.on('clipboard-writeRTF', (text, type) => {
|
||||
socket.on("clipboard-writeRTF", (text, type) => {
|
||||
electron_1.clipboard.writeHTML(text, type);
|
||||
});
|
||||
socket.on('clipboard-readBookmark', () => {
|
||||
socket.on("clipboard-readBookmark", () => {
|
||||
const bookmark = electron_1.clipboard.readBookmark();
|
||||
electronSocket.emit('clipboard-readBookmark-completed', bookmark);
|
||||
electronSocket.emit("clipboard-readBookmark-completed", bookmark);
|
||||
});
|
||||
socket.on('clipboard-writeBookmark', (title, url, type) => {
|
||||
socket.on("clipboard-writeBookmark", (title, url, type) => {
|
||||
electron_1.clipboard.writeBookmark(title, url, type);
|
||||
});
|
||||
socket.on('clipboard-readFindText', () => {
|
||||
socket.on("clipboard-readFindText", () => {
|
||||
const content = electron_1.clipboard.readFindText();
|
||||
electronSocket.emit('clipboard-readFindText-completed', content);
|
||||
electronSocket.emit("clipboard-readFindText-completed", content);
|
||||
});
|
||||
socket.on('clipboard-writeFindText', (text) => {
|
||||
socket.on("clipboard-writeFindText", (text) => {
|
||||
electron_1.clipboard.writeFindText(text);
|
||||
});
|
||||
socket.on('clipboard-clear', (type) => {
|
||||
socket.on("clipboard-clear", (type) => {
|
||||
electron_1.clipboard.clear(type);
|
||||
});
|
||||
socket.on('clipboard-availableFormats', (type) => {
|
||||
socket.on("clipboard-availableFormats", (type) => {
|
||||
const formats = electron_1.clipboard.availableFormats(type);
|
||||
electronSocket.emit('clipboard-availableFormats-completed', formats);
|
||||
electronSocket.emit("clipboard-availableFormats-completed", formats);
|
||||
});
|
||||
socket.on('clipboard-write', (data, type) => {
|
||||
socket.on("clipboard-write", (data, type) => {
|
||||
electron_1.clipboard.write(data, type);
|
||||
});
|
||||
socket.on('clipboard-readImage', (type) => {
|
||||
socket.on("clipboard-readImage", (type) => {
|
||||
const image = electron_1.clipboard.readImage(type);
|
||||
electronSocket.emit('clipboard-readImage-completed', { 1: image.toPNG().toString('base64') });
|
||||
electronSocket.emit("clipboard-readImage-completed", {
|
||||
1: image.toPNG().toString("base64"),
|
||||
});
|
||||
});
|
||||
socket.on('clipboard-writeImage', (data, type) => {
|
||||
socket.on("clipboard-writeImage", (data, type) => {
|
||||
const dataContent = JSON.parse(data);
|
||||
const image = electron_1.nativeImage.createEmpty();
|
||||
// tslint:disable-next-line: forin
|
||||
for (const key in dataContent) {
|
||||
const scaleFactor = key;
|
||||
const bytes = data[key];
|
||||
const buffer = Buffer.from(bytes, 'base64');
|
||||
const buffer = Buffer.from(bytes, "base64");
|
||||
image.addRepresentation({ scaleFactor: +scaleFactor, buffer: buffer });
|
||||
}
|
||||
electron_1.clipboard.writeImage(image, type);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["clipboard.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["clipboard.ts"],"names":[],"mappings":";AACA,uCAAkD;AAElD,IAAI,cAAsB,CAAC;AAE3B,iBAAS,CAAC,MAAc,EAAE,EAAE;IAC1B,cAAc,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;QACvC,MAAM,IAAI,GAAG,oBAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtC,cAAc,CAAC,IAAI,CAAC,8BAA8B,EAAE,IAAI,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC9C,oBAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;QACvC,MAAM,OAAO,GAAG,oBAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACzC,cAAc,CAAC,IAAI,CAAC,8BAA8B,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QAChD,oBAAS,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,IAAI,EAAE,EAAE;QACtC,MAAM,OAAO,GAAG,oBAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,cAAc,CAAC,IAAI,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC7C,oBAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACvC,MAAM,QAAQ,GAAG,oBAAS,CAAC,YAAY,EAAE,CAAC;QAC1C,cAAc,CAAC,IAAI,CAAC,kCAAkC,EAAE,QAAQ,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxD,oBAAS,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG,oBAAS,CAAC,YAAY,EAAE,CAAC;QACzC,cAAc,CAAC,IAAI,CAAC,kCAAkC,EAAE,OAAO,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,IAAI,EAAE,EAAE;QAC5C,oBAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE;QACpC,oBAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,4BAA4B,EAAE,CAAC,IAAI,EAAE,EAAE;QAC/C,MAAM,OAAO,GAAG,oBAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACjD,cAAc,CAAC,IAAI,CAAC,sCAAsC,EAAE,OAAO,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC1C,oBAAS,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,IAAI,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,oBAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACxC,cAAc,CAAC,IAAI,CAAC,+BAA+B,EAAE;YACnD,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,sBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,sBAAW,CAAC,WAAW,EAAE,CAAC;QAExC,kCAAkC;QAClC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC9B,MAAM,WAAW,GAAG,GAAG,CAAC;YACxB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC5C,KAAK,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,oBAAS,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
||||
@@ -1,84 +1,87 @@
|
||||
import { Socket } from 'net';
|
||||
import { clipboard, nativeImage } from 'electron';
|
||||
let electronSocket;
|
||||
import type { Socket } from "net";
|
||||
import { clipboard, nativeImage } from "electron";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
export = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('clipboard-readText', (type) => {
|
||||
const text = clipboard.readText(type);
|
||||
electronSocket.emit('clipboard-readText-completed', text);
|
||||
electronSocket = socket;
|
||||
socket.on("clipboard-readText", (type) => {
|
||||
const text = clipboard.readText(type);
|
||||
electronSocket.emit("clipboard-readText-completed", text);
|
||||
});
|
||||
|
||||
socket.on("clipboard-writeText", (text, type) => {
|
||||
clipboard.writeText(text, type);
|
||||
});
|
||||
|
||||
socket.on("clipboard-readHTML", (type) => {
|
||||
const content = clipboard.readHTML(type);
|
||||
electronSocket.emit("clipboard-readHTML-completed", content);
|
||||
});
|
||||
|
||||
socket.on("clipboard-writeHTML", (markup, type) => {
|
||||
clipboard.writeHTML(markup, type);
|
||||
});
|
||||
|
||||
socket.on("clipboard-readRTF", (type) => {
|
||||
const content = clipboard.readRTF(type);
|
||||
electronSocket.emit("clipboard-readRTF-completed", content);
|
||||
});
|
||||
|
||||
socket.on("clipboard-writeRTF", (text, type) => {
|
||||
clipboard.writeHTML(text, type);
|
||||
});
|
||||
|
||||
socket.on("clipboard-readBookmark", () => {
|
||||
const bookmark = clipboard.readBookmark();
|
||||
electronSocket.emit("clipboard-readBookmark-completed", bookmark);
|
||||
});
|
||||
|
||||
socket.on("clipboard-writeBookmark", (title, url, type) => {
|
||||
clipboard.writeBookmark(title, url, type);
|
||||
});
|
||||
|
||||
socket.on("clipboard-readFindText", () => {
|
||||
const content = clipboard.readFindText();
|
||||
electronSocket.emit("clipboard-readFindText-completed", content);
|
||||
});
|
||||
|
||||
socket.on("clipboard-writeFindText", (text) => {
|
||||
clipboard.writeFindText(text);
|
||||
});
|
||||
|
||||
socket.on("clipboard-clear", (type) => {
|
||||
clipboard.clear(type);
|
||||
});
|
||||
|
||||
socket.on("clipboard-availableFormats", (type) => {
|
||||
const formats = clipboard.availableFormats(type);
|
||||
electronSocket.emit("clipboard-availableFormats-completed", formats);
|
||||
});
|
||||
|
||||
socket.on("clipboard-write", (data, type) => {
|
||||
clipboard.write(data, type);
|
||||
});
|
||||
|
||||
socket.on("clipboard-readImage", (type) => {
|
||||
const image = clipboard.readImage(type);
|
||||
electronSocket.emit("clipboard-readImage-completed", {
|
||||
1: image.toPNG().toString("base64"),
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('clipboard-writeText', (text, type) => {
|
||||
clipboard.writeText(text, type);
|
||||
});
|
||||
socket.on("clipboard-writeImage", (data, type) => {
|
||||
const dataContent = JSON.parse(data);
|
||||
const image = nativeImage.createEmpty();
|
||||
|
||||
socket.on('clipboard-readHTML', (type) => {
|
||||
const content = clipboard.readHTML(type);
|
||||
electronSocket.emit('clipboard-readHTML-completed', content);
|
||||
});
|
||||
// tslint:disable-next-line: forin
|
||||
for (const key in dataContent) {
|
||||
const scaleFactor = key;
|
||||
const bytes = data[key];
|
||||
const buffer = Buffer.from(bytes, "base64");
|
||||
image.addRepresentation({ scaleFactor: +scaleFactor, buffer: buffer });
|
||||
}
|
||||
|
||||
socket.on('clipboard-writeHTML', (markup, type) => {
|
||||
clipboard.writeHTML(markup, type);
|
||||
});
|
||||
|
||||
socket.on('clipboard-readRTF', (type) => {
|
||||
const content = clipboard.readRTF(type);
|
||||
electronSocket.emit('clipboard-readRTF-completed', content);
|
||||
});
|
||||
|
||||
socket.on('clipboard-writeRTF', (text, type) => {
|
||||
clipboard.writeHTML(text, type);
|
||||
});
|
||||
|
||||
socket.on('clipboard-readBookmark', () => {
|
||||
const bookmark = clipboard.readBookmark();
|
||||
electronSocket.emit('clipboard-readBookmark-completed', bookmark);
|
||||
});
|
||||
|
||||
socket.on('clipboard-writeBookmark', (title, url, type) => {
|
||||
clipboard.writeBookmark(title, url, type);
|
||||
});
|
||||
|
||||
socket.on('clipboard-readFindText', () => {
|
||||
const content = clipboard.readFindText();
|
||||
electronSocket.emit('clipboard-readFindText-completed', content);
|
||||
});
|
||||
|
||||
socket.on('clipboard-writeFindText', (text) => {
|
||||
clipboard.writeFindText(text);
|
||||
});
|
||||
|
||||
socket.on('clipboard-clear', (type) => {
|
||||
clipboard.clear(type);
|
||||
});
|
||||
|
||||
socket.on('clipboard-availableFormats', (type) => {
|
||||
const formats = clipboard.availableFormats(type);
|
||||
electronSocket.emit('clipboard-availableFormats-completed', formats);
|
||||
});
|
||||
|
||||
socket.on('clipboard-write', (data, type) => {
|
||||
clipboard.write(data, type);
|
||||
});
|
||||
|
||||
socket.on('clipboard-readImage', (type) => {
|
||||
const image = clipboard.readImage(type);
|
||||
electronSocket.emit('clipboard-readImage-completed', { 1: image.toPNG().toString('base64') });
|
||||
});
|
||||
|
||||
socket.on('clipboard-writeImage', (data, type) => {
|
||||
const dataContent = JSON.parse(data);
|
||||
const image = nativeImage.createEmpty();
|
||||
|
||||
// tslint:disable-next-line: forin
|
||||
for (const key in dataContent) {
|
||||
const scaleFactor = key;
|
||||
const bytes = data[key];
|
||||
const buffer = Buffer.from(bytes, 'base64');
|
||||
image.addRepresentation({ scaleFactor: +scaleFactor, buffer: buffer });
|
||||
}
|
||||
|
||||
clipboard.writeImage(image, type);
|
||||
});
|
||||
clipboard.writeImage(image, type);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
let electronSocket;
|
||||
module.exports = (socket, app) => {
|
||||
electronSocket = socket;
|
||||
socket.on('appCommandLineAppendSwitch', (the_switch, value) => {
|
||||
socket.on("appCommandLineAppendSwitch", (the_switch, value) => {
|
||||
app.commandLine.appendSwitch(the_switch, value);
|
||||
});
|
||||
socket.on('appCommandLineAppendArgument', (value) => {
|
||||
socket.on("appCommandLineAppendArgument", (value) => {
|
||||
app.commandLine.appendArgument(value);
|
||||
});
|
||||
socket.on('appCommandLineHasSwitch', (value) => {
|
||||
socket.on("appCommandLineHasSwitch", (value) => {
|
||||
const hasSwitch = app.commandLine.hasSwitch(value);
|
||||
electronSocket.emit('appCommandLineHasSwitchCompleted', hasSwitch);
|
||||
electronSocket.emit("appCommandLineHasSwitchCompleted", hasSwitch);
|
||||
});
|
||||
socket.on('appCommandLineGetSwitchValue', (the_switch) => {
|
||||
socket.on("appCommandLineGetSwitchValue", (the_switch) => {
|
||||
const value = app.commandLine.getSwitchValue(the_switch);
|
||||
electronSocket.emit('appCommandLineGetSwitchValueCompleted', value);
|
||||
electronSocket.emit("appCommandLineGetSwitchValueCompleted", value);
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=commandLine.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"commandLine.js","sourceRoot":"","sources":["commandLine.ts"],"names":[],"mappings":";AACA,IAAI,cAAc,CAAC;AAEnB,iBAAS,CAAC,MAAc,EAAE,GAAiB,EAAE,EAAE;IAC3C,cAAc,GAAG,MAAM,CAAC;IAExB,MAAM,CAAC,EAAE,CAAC,4BAA4B,EAAE,CAAC,UAAkB,EAAE,KAAa,EAAE,EAAE;QAC1E,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,CAAC,KAAa,EAAE,EAAE;QACxD,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,KAAa,EAAE,EAAE;QACnD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnD,cAAc,CAAC,IAAI,CAAC,kCAAkC,EAAE,SAAS,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,CAAC,UAAkB,EAAE,EAAE;QAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACzD,cAAc,CAAC,IAAI,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC"}
|
||||
{"version":3,"file":"commandLine.js","sourceRoot":"","sources":["commandLine.ts"],"names":[],"mappings":";AAEA,IAAI,cAAsB,CAAC;AAE3B,iBAAS,CAAC,MAAc,EAAE,GAAiB,EAAE,EAAE;IAC7C,cAAc,GAAG,MAAM,CAAC;IAExB,MAAM,CAAC,EAAE,CACP,4BAA4B,EAC5B,CAAC,UAAkB,EAAE,KAAa,EAAE,EAAE;QACpC,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1D,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,KAAa,EAAE,EAAE;QACrD,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnD,cAAc,CAAC,IAAI,CAAC,kCAAkC,EAAE,SAAS,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,CAAC,UAAkB,EAAE,EAAE;QAC/D,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACzD,cAAc,CAAC,IAAI,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
||||
@@ -1,24 +1,28 @@
|
||||
import { Socket } from 'net';
|
||||
let electronSocket;
|
||||
import type { Socket } from "net";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
export = (socket: Socket, app: Electron.App) => {
|
||||
electronSocket = socket;
|
||||
electronSocket = socket;
|
||||
|
||||
socket.on('appCommandLineAppendSwitch', (the_switch: string, value: string) => {
|
||||
app.commandLine.appendSwitch(the_switch, value);
|
||||
});
|
||||
socket.on(
|
||||
"appCommandLineAppendSwitch",
|
||||
(the_switch: string, value: string) => {
|
||||
app.commandLine.appendSwitch(the_switch, value);
|
||||
},
|
||||
);
|
||||
|
||||
socket.on('appCommandLineAppendArgument', (value: string) => {
|
||||
app.commandLine.appendArgument(value);
|
||||
});
|
||||
socket.on("appCommandLineAppendArgument", (value: string) => {
|
||||
app.commandLine.appendArgument(value);
|
||||
});
|
||||
|
||||
socket.on('appCommandLineHasSwitch', (value: string) => {
|
||||
const hasSwitch = app.commandLine.hasSwitch(value);
|
||||
electronSocket.emit('appCommandLineHasSwitchCompleted', hasSwitch);
|
||||
});
|
||||
socket.on("appCommandLineHasSwitch", (value: string) => {
|
||||
const hasSwitch = app.commandLine.hasSwitch(value);
|
||||
electronSocket.emit("appCommandLineHasSwitchCompleted", hasSwitch);
|
||||
});
|
||||
|
||||
socket.on('appCommandLineGetSwitchValue', (the_switch: string) => {
|
||||
const value = app.commandLine.getSwitchValue(the_switch);
|
||||
electronSocket.emit('appCommandLineGetSwitchValueCompleted', value);
|
||||
});
|
||||
socket.on("appCommandLineGetSwitchValue", (the_switch: string) => {
|
||||
const value = app.commandLine.getSwitchValue(the_switch);
|
||||
electronSocket.emit("appCommandLineGetSwitchValueCompleted", value);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,35 +3,41 @@ const electron_1 = require("electron");
|
||||
let electronSocket;
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('showMessageBox', async (browserWindow, options, guid) => {
|
||||
if ('id' in browserWindow) {
|
||||
socket.on("showMessageBox", async (browserWindow, options, guid) => {
|
||||
if ("id" in browserWindow) {
|
||||
const window = electron_1.BrowserWindow.fromId(browserWindow.id);
|
||||
const messageBoxReturnValue = await electron_1.dialog.showMessageBox(window, options);
|
||||
electronSocket.emit('showMessageBoxComplete' + guid, [messageBoxReturnValue.response, messageBoxReturnValue.checkboxChecked]);
|
||||
electronSocket.emit("showMessageBoxComplete" + guid, [
|
||||
messageBoxReturnValue.response,
|
||||
messageBoxReturnValue.checkboxChecked,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
const id = guid || options;
|
||||
const messageBoxReturnValue = await electron_1.dialog.showMessageBox(browserWindow);
|
||||
electronSocket.emit('showMessageBoxComplete' + id, [messageBoxReturnValue.response, messageBoxReturnValue.checkboxChecked]);
|
||||
electronSocket.emit("showMessageBoxComplete" + id, [
|
||||
messageBoxReturnValue.response,
|
||||
messageBoxReturnValue.checkboxChecked,
|
||||
]);
|
||||
}
|
||||
});
|
||||
socket.on('showOpenDialog', async (browserWindow, options, guid) => {
|
||||
socket.on("showOpenDialog", async (browserWindow, options, guid) => {
|
||||
const window = electron_1.BrowserWindow.fromId(browserWindow.id);
|
||||
const openDialogReturnValue = await electron_1.dialog.showOpenDialog(window, options);
|
||||
electronSocket.emit('showOpenDialogComplete' + guid, openDialogReturnValue.filePaths || []);
|
||||
electronSocket.emit("showOpenDialogComplete" + guid, openDialogReturnValue.filePaths || []);
|
||||
});
|
||||
socket.on('showSaveDialog', async (browserWindow, options, guid) => {
|
||||
socket.on("showSaveDialog", async (browserWindow, options, guid) => {
|
||||
const window = electron_1.BrowserWindow.fromId(browserWindow.id);
|
||||
const saveDialogReturnValue = await electron_1.dialog.showSaveDialog(window, options);
|
||||
electronSocket.emit('showSaveDialogComplete' + guid, saveDialogReturnValue.filePath || '');
|
||||
electronSocket.emit("showSaveDialogComplete" + guid, saveDialogReturnValue.filePath || "");
|
||||
});
|
||||
socket.on('showErrorBox', (title, content) => {
|
||||
socket.on("showErrorBox", (title, content) => {
|
||||
electron_1.dialog.showErrorBox(title, content);
|
||||
});
|
||||
socket.on('showCertificateTrustDialog', async (browserWindow, options, guid) => {
|
||||
socket.on("showCertificateTrustDialog", async (browserWindow, options, guid) => {
|
||||
const window = electron_1.BrowserWindow.fromId(browserWindow.id);
|
||||
await electron_1.dialog.showCertificateTrustDialog(window, options);
|
||||
electronSocket.emit('showCertificateTrustDialogComplete' + guid);
|
||||
electronSocket.emit("showCertificateTrustDialogComplete" + guid);
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=dialog.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";AACA,uCAAiD;AACjD,IAAI,cAAsB,CAAC;AAE3B,iBAAS,CAAC,MAAc,EAAE,EAAE;IACxB,cAAc,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC/D,IAAI,IAAI,IAAI,aAAa,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEtD,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC3E,cAAc,CAAC,IAAI,CAAC,wBAAwB,GAAG,IAAI,EAAE,CAAC,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAC,CAAC;QAClI,CAAC;aAAM,CAAC;YACJ,MAAM,EAAE,GAAG,IAAI,IAAI,OAAO,CAAC;YAC3B,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YAEzE,cAAc,CAAC,IAAI,CAAC,wBAAwB,GAAG,EAAE,EAAE,CAAC,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAC,CAAC;QAChI,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC/D,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAE3E,cAAc,CAAC,IAAI,CAAC,wBAAwB,GAAG,IAAI,EAAE,qBAAqB,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC/D,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAE3E,cAAc,CAAC,IAAI,CAAC,wBAAwB,GAAG,IAAI,EAAE,qBAAqB,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACzC,iBAAM,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,4BAA4B,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC3E,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,iBAAM,CAAC,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAEzD,cAAc,CAAC,IAAI,CAAC,oCAAoC,GAAG,IAAI,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC"}
|
||||
{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";AACA,uCAAiD;AAEjD,IAAI,cAAsB,CAAC;AAE3B,iBAAS,CAAC,MAAc,EAAE,EAAE;IAC1B,cAAc,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QACjE,IAAI,IAAI,IAAI,aAAa,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEtD,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CACvD,MAAM,EACN,OAAO,CACR,CAAC;YACF,cAAc,CAAC,IAAI,CAAC,wBAAwB,GAAG,IAAI,EAAE;gBACnD,qBAAqB,CAAC,QAAQ;gBAC9B,qBAAqB,CAAC,eAAe;aACtC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,GAAG,IAAI,IAAI,OAAO,CAAC;YAC3B,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YAEzE,cAAc,CAAC,IAAI,CAAC,wBAAwB,GAAG,EAAE,EAAE;gBACjD,qBAAqB,CAAC,QAAQ;gBAC9B,qBAAqB,CAAC,eAAe;aACtC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QACjE,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAE3E,cAAc,CAAC,IAAI,CACjB,wBAAwB,GAAG,IAAI,EAC/B,qBAAqB,CAAC,SAAS,IAAI,EAAE,CACtC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QACjE,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,qBAAqB,GAAG,MAAM,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAE3E,cAAc,CAAC,IAAI,CACjB,wBAAwB,GAAG,IAAI,EAC/B,qBAAqB,CAAC,QAAQ,IAAI,EAAE,CACrC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC3C,iBAAM,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CACP,4BAA4B,EAC5B,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtD,MAAM,iBAAM,CAAC,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAEzD,cAAc,CAAC,IAAI,CAAC,oCAAoC,GAAG,IAAI,CAAC,CAAC;IACnE,CAAC,CACF,CAAC;AACJ,CAAC,CAAC"}
|
||||
@@ -1,45 +1,64 @@
|
||||
import { Socket } from 'net';
|
||||
import { BrowserWindow, dialog } from 'electron';
|
||||
import type { Socket } from "net";
|
||||
import { BrowserWindow, dialog } from "electron";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
export = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('showMessageBox', async (browserWindow, options, guid) => {
|
||||
if ('id' in browserWindow) {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
electronSocket = socket;
|
||||
socket.on("showMessageBox", async (browserWindow, options, guid) => {
|
||||
if ("id" in browserWindow) {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
|
||||
const messageBoxReturnValue = await dialog.showMessageBox(window, options);
|
||||
electronSocket.emit('showMessageBoxComplete' + guid, [messageBoxReturnValue.response, messageBoxReturnValue.checkboxChecked]);
|
||||
} else {
|
||||
const id = guid || options;
|
||||
const messageBoxReturnValue = await dialog.showMessageBox(browserWindow);
|
||||
const messageBoxReturnValue = await dialog.showMessageBox(
|
||||
window,
|
||||
options,
|
||||
);
|
||||
electronSocket.emit("showMessageBoxComplete" + guid, [
|
||||
messageBoxReturnValue.response,
|
||||
messageBoxReturnValue.checkboxChecked,
|
||||
]);
|
||||
} else {
|
||||
const id = guid || options;
|
||||
const messageBoxReturnValue = await dialog.showMessageBox(browserWindow);
|
||||
|
||||
electronSocket.emit('showMessageBoxComplete' + id, [messageBoxReturnValue.response, messageBoxReturnValue.checkboxChecked]);
|
||||
}
|
||||
});
|
||||
electronSocket.emit("showMessageBoxComplete" + id, [
|
||||
messageBoxReturnValue.response,
|
||||
messageBoxReturnValue.checkboxChecked,
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('showOpenDialog', async (browserWindow, options, guid) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
const openDialogReturnValue = await dialog.showOpenDialog(window, options);
|
||||
socket.on("showOpenDialog", async (browserWindow, options, guid) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
const openDialogReturnValue = await dialog.showOpenDialog(window, options);
|
||||
|
||||
electronSocket.emit('showOpenDialogComplete' + guid, openDialogReturnValue.filePaths || []);
|
||||
});
|
||||
electronSocket.emit(
|
||||
"showOpenDialogComplete" + guid,
|
||||
openDialogReturnValue.filePaths || [],
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('showSaveDialog', async (browserWindow, options, guid) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
const saveDialogReturnValue = await dialog.showSaveDialog(window, options);
|
||||
socket.on("showSaveDialog", async (browserWindow, options, guid) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
const saveDialogReturnValue = await dialog.showSaveDialog(window, options);
|
||||
|
||||
electronSocket.emit('showSaveDialogComplete' + guid, saveDialogReturnValue.filePath || '');
|
||||
});
|
||||
electronSocket.emit(
|
||||
"showSaveDialogComplete" + guid,
|
||||
saveDialogReturnValue.filePath || "",
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('showErrorBox', (title, content) => {
|
||||
dialog.showErrorBox(title, content);
|
||||
});
|
||||
socket.on("showErrorBox", (title, content) => {
|
||||
dialog.showErrorBox(title, content);
|
||||
});
|
||||
|
||||
socket.on('showCertificateTrustDialog', async (browserWindow, options, guid) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
await dialog.showCertificateTrustDialog(window, options);
|
||||
socket.on(
|
||||
"showCertificateTrustDialog",
|
||||
async (browserWindow, options, guid) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
await dialog.showCertificateTrustDialog(window, options);
|
||||
|
||||
electronSocket.emit('showCertificateTrustDialogComplete' + guid);
|
||||
});
|
||||
electronSocket.emit("showCertificateTrustDialogComplete" + guid);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,49 +3,49 @@ const electron_1 = require("electron");
|
||||
let electronSocket;
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('dock-bounce', (type) => {
|
||||
socket.on("dock-bounce", (type) => {
|
||||
const id = electron_1.app.dock.bounce(type);
|
||||
electronSocket.emit('dock-bounce-completed', id);
|
||||
electronSocket.emit("dock-bounce-completed", id);
|
||||
});
|
||||
socket.on('dock-cancelBounce', (id) => {
|
||||
socket.on("dock-cancelBounce", (id) => {
|
||||
electron_1.app.dock.cancelBounce(id);
|
||||
});
|
||||
socket.on('dock-downloadFinished', (filePath) => {
|
||||
socket.on("dock-downloadFinished", (filePath) => {
|
||||
electron_1.app.dock.downloadFinished(filePath);
|
||||
});
|
||||
socket.on('dock-setBadge', (text) => {
|
||||
socket.on("dock-setBadge", (text) => {
|
||||
electron_1.app.dock.setBadge(text);
|
||||
});
|
||||
socket.on('dock-getBadge', () => {
|
||||
socket.on("dock-getBadge", () => {
|
||||
const text = electron_1.app.dock.getBadge();
|
||||
electronSocket.emit('dock-getBadge-completed', text);
|
||||
electronSocket.emit("dock-getBadge-completed", text);
|
||||
});
|
||||
socket.on('dock-hide', () => {
|
||||
socket.on("dock-hide", () => {
|
||||
electron_1.app.dock.hide();
|
||||
});
|
||||
socket.on('dock-show', () => {
|
||||
socket.on("dock-show", () => {
|
||||
electron_1.app.dock.show();
|
||||
});
|
||||
socket.on('dock-isVisible', () => {
|
||||
socket.on("dock-isVisible", () => {
|
||||
const isVisible = electron_1.app.dock.isVisible();
|
||||
electronSocket.emit('dock-isVisible-completed', isVisible);
|
||||
electronSocket.emit("dock-isVisible-completed", isVisible);
|
||||
});
|
||||
socket.on('dock-setMenu', (menuItems) => {
|
||||
socket.on("dock-setMenu", (menuItems) => {
|
||||
let menu = null;
|
||||
if (menuItems) {
|
||||
menu = electron_1.Menu.buildFromTemplate(menuItems);
|
||||
addMenuItemClickConnector(menu.items, (id) => {
|
||||
electronSocket.emit('dockMenuItemClicked', id);
|
||||
electronSocket.emit("dockMenuItemClicked", id);
|
||||
});
|
||||
}
|
||||
electron_1.app.dock.setMenu(menu);
|
||||
});
|
||||
// TODO: Menu (macOS) still to be implemented
|
||||
socket.on('dock-getMenu', () => {
|
||||
socket.on("dock-getMenu", () => {
|
||||
const menu = electron_1.app.dock.getMenu();
|
||||
electronSocket.emit('dock-getMenu-completed', menu);
|
||||
electronSocket.emit("dock-getMenu-completed", menu);
|
||||
});
|
||||
socket.on('dock-setIcon', (image) => {
|
||||
socket.on("dock-setIcon", (image) => {
|
||||
electron_1.app.dock.setIcon(image);
|
||||
});
|
||||
function addMenuItemClickConnector(menuItems, callback) {
|
||||
@@ -53,8 +53,10 @@ module.exports = (socket) => {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addMenuItemClickConnector(item.submenu.items, callback);
|
||||
}
|
||||
if ('id' in item && item.id) {
|
||||
item.click = () => { callback(item.id); };
|
||||
if ("id" in item && item.id) {
|
||||
item.click = () => {
|
||||
callback(item.id);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"dock.js","sourceRoot":"","sources":["dock.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"dock.js","sourceRoot":"","sources":["dock.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,78 +1,81 @@
|
||||
import { Socket } from 'net';
|
||||
import { app, Menu } from 'electron';
|
||||
let electronSocket;
|
||||
import type { Socket } from "net";
|
||||
import { app, Menu } from "electron";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
export = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
electronSocket = socket;
|
||||
|
||||
socket.on('dock-bounce', (type) => {
|
||||
const id = app.dock.bounce(type);
|
||||
electronSocket.emit('dock-bounce-completed', id);
|
||||
});
|
||||
socket.on("dock-bounce", (type) => {
|
||||
const id = app.dock.bounce(type);
|
||||
electronSocket.emit("dock-bounce-completed", id);
|
||||
});
|
||||
|
||||
socket.on('dock-cancelBounce', (id) => {
|
||||
app.dock.cancelBounce(id);
|
||||
});
|
||||
socket.on("dock-cancelBounce", (id) => {
|
||||
app.dock.cancelBounce(id);
|
||||
});
|
||||
|
||||
socket.on('dock-downloadFinished', (filePath) => {
|
||||
app.dock.downloadFinished(filePath);
|
||||
});
|
||||
socket.on("dock-downloadFinished", (filePath) => {
|
||||
app.dock.downloadFinished(filePath);
|
||||
});
|
||||
|
||||
socket.on('dock-setBadge', (text) => {
|
||||
app.dock.setBadge(text);
|
||||
});
|
||||
socket.on("dock-setBadge", (text) => {
|
||||
app.dock.setBadge(text);
|
||||
});
|
||||
|
||||
socket.on('dock-getBadge', () => {
|
||||
const text = app.dock.getBadge();
|
||||
electronSocket.emit('dock-getBadge-completed', text);
|
||||
});
|
||||
socket.on("dock-getBadge", () => {
|
||||
const text = app.dock.getBadge();
|
||||
electronSocket.emit("dock-getBadge-completed", text);
|
||||
});
|
||||
|
||||
socket.on('dock-hide', () => {
|
||||
app.dock.hide();
|
||||
});
|
||||
socket.on("dock-hide", () => {
|
||||
app.dock.hide();
|
||||
});
|
||||
|
||||
socket.on('dock-show', () => {
|
||||
app.dock.show();
|
||||
});
|
||||
socket.on("dock-show", () => {
|
||||
app.dock.show();
|
||||
});
|
||||
|
||||
socket.on('dock-isVisible', () => {
|
||||
const isVisible = app.dock.isVisible();
|
||||
electronSocket.emit('dock-isVisible-completed', isVisible);
|
||||
});
|
||||
socket.on("dock-isVisible", () => {
|
||||
const isVisible = app.dock.isVisible();
|
||||
electronSocket.emit("dock-isVisible-completed", isVisible);
|
||||
});
|
||||
|
||||
socket.on('dock-setMenu', (menuItems) => {
|
||||
let menu = null;
|
||||
socket.on("dock-setMenu", (menuItems) => {
|
||||
let menu = null;
|
||||
|
||||
if (menuItems) {
|
||||
menu = Menu.buildFromTemplate(menuItems);
|
||||
if (menuItems) {
|
||||
menu = Menu.buildFromTemplate(menuItems);
|
||||
|
||||
addMenuItemClickConnector(menu.items, (id) => {
|
||||
electronSocket.emit('dockMenuItemClicked', id);
|
||||
});
|
||||
}
|
||||
|
||||
app.dock.setMenu(menu);
|
||||
});
|
||||
|
||||
// TODO: Menu (macOS) still to be implemented
|
||||
socket.on('dock-getMenu', () => {
|
||||
const menu = app.dock.getMenu();
|
||||
electronSocket.emit('dock-getMenu-completed', menu);
|
||||
});
|
||||
|
||||
socket.on('dock-setIcon', (image) => {
|
||||
app.dock.setIcon(image);
|
||||
});
|
||||
|
||||
function addMenuItemClickConnector(menuItems, callback) {
|
||||
menuItems.forEach((item) => {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addMenuItemClickConnector(item.submenu.items, callback);
|
||||
}
|
||||
|
||||
if ('id' in item && item.id) {
|
||||
item.click = () => { callback(item.id); };
|
||||
}
|
||||
});
|
||||
addMenuItemClickConnector(menu.items, (id) => {
|
||||
electronSocket.emit("dockMenuItemClicked", id);
|
||||
});
|
||||
}
|
||||
|
||||
app.dock.setMenu(menu);
|
||||
});
|
||||
|
||||
// TODO: Menu (macOS) still to be implemented
|
||||
socket.on("dock-getMenu", () => {
|
||||
const menu = app.dock.getMenu();
|
||||
electronSocket.emit("dock-getMenu-completed", menu);
|
||||
});
|
||||
|
||||
socket.on("dock-setIcon", (image) => {
|
||||
app.dock.setIcon(image);
|
||||
});
|
||||
|
||||
function addMenuItemClickConnector(menuItems, callback) {
|
||||
menuItems.forEach((item) => {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addMenuItemClickConnector(item.submenu.items, callback);
|
||||
}
|
||||
|
||||
if ("id" in item && item.id) {
|
||||
item.click = () => {
|
||||
callback(item.id);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,19 +3,19 @@ const electron_1 = require("electron");
|
||||
let electronSocket;
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('globalShortcut-register', (accelerator) => {
|
||||
socket.on("globalShortcut-register", (accelerator) => {
|
||||
electron_1.globalShortcut.register(accelerator, () => {
|
||||
electronSocket.emit('globalShortcut-pressed', accelerator);
|
||||
electronSocket.emit("globalShortcut-pressed", accelerator);
|
||||
});
|
||||
});
|
||||
socket.on('globalShortcut-isRegistered', (accelerator) => {
|
||||
socket.on("globalShortcut-isRegistered", (accelerator) => {
|
||||
const isRegistered = electron_1.globalShortcut.isRegistered(accelerator);
|
||||
electronSocket.emit('globalShortcut-isRegisteredCompleted', isRegistered);
|
||||
electronSocket.emit("globalShortcut-isRegisteredCompleted", isRegistered);
|
||||
});
|
||||
socket.on('globalShortcut-unregister', (accelerator) => {
|
||||
socket.on("globalShortcut-unregister", (accelerator) => {
|
||||
electron_1.globalShortcut.unregister(accelerator);
|
||||
});
|
||||
socket.on('globalShortcut-unregisterAll', () => {
|
||||
socket.on("globalShortcut-unregisterAll", () => {
|
||||
try {
|
||||
electron_1.globalShortcut.unregisterAll();
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"globalShortcut.js","sourceRoot":"","sources":["globalShortcut.ts"],"names":[],"mappings":";AAAA,uCAA0C;AAE1C,IAAI,cAAc,CAAC;AAEnB,iBAAS,CAAC,MAAc,EAAE,EAAE;IACxB,cAAc,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,WAAW,EAAE,EAAE;QACjD,yBAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;YACtC,cAAc,CAAC,IAAI,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,6BAA6B,EAAE,CAAC,WAAW,EAAE,EAAE;QACrD,MAAM,YAAY,GAAG,yBAAc,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAE9D,cAAc,CAAC,IAAI,CAAC,sCAAsC,EAAE,YAAY,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,2BAA2B,EAAE,CAAC,WAAW,EAAE,EAAE;QACnD,yBAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC3C,IAAI,CAAC;YACD,yBAAc,CAAC,aAAa,EAAE,CAAC;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC,CAAC"}
|
||||
{"version":3,"file":"globalShortcut.js","sourceRoot":"","sources":["globalShortcut.ts"],"names":[],"mappings":";AACA,uCAA0C;AAE1C,IAAI,cAAsB,CAAC;AAE3B,iBAAS,CAAC,MAAc,EAAE,EAAE;IAC1B,cAAc,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,WAAW,EAAE,EAAE;QACnD,yBAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;YACxC,cAAc,CAAC,IAAI,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,6BAA6B,EAAE,CAAC,WAAW,EAAE,EAAE;QACvD,MAAM,YAAY,GAAG,yBAAc,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAE9D,cAAc,CAAC,IAAI,CAAC,sCAAsC,EAAE,YAAY,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,2BAA2B,EAAE,CAAC,WAAW,EAAE,EAAE;QACrD,yBAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC7C,IAAI,CAAC;YACH,yBAAc,CAAC,aAAa,EAAE,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC,CAAA,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
||||
@@ -1,28 +1,29 @@
|
||||
import { globalShortcut } from 'electron';
|
||||
import { Socket } from 'net';
|
||||
let electronSocket;
|
||||
import type { Socket } from "net";
|
||||
import { globalShortcut } from "electron";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
export = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('globalShortcut-register', (accelerator) => {
|
||||
globalShortcut.register(accelerator, () => {
|
||||
electronSocket.emit('globalShortcut-pressed', accelerator);
|
||||
});
|
||||
electronSocket = socket;
|
||||
socket.on("globalShortcut-register", (accelerator) => {
|
||||
globalShortcut.register(accelerator, () => {
|
||||
electronSocket.emit("globalShortcut-pressed", accelerator);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('globalShortcut-isRegistered', (accelerator) => {
|
||||
const isRegistered = globalShortcut.isRegistered(accelerator);
|
||||
socket.on("globalShortcut-isRegistered", (accelerator) => {
|
||||
const isRegistered = globalShortcut.isRegistered(accelerator);
|
||||
|
||||
electronSocket.emit('globalShortcut-isRegisteredCompleted', isRegistered);
|
||||
});
|
||||
electronSocket.emit("globalShortcut-isRegisteredCompleted", isRegistered);
|
||||
});
|
||||
|
||||
socket.on('globalShortcut-unregister', (accelerator) => {
|
||||
globalShortcut.unregister(accelerator);
|
||||
});
|
||||
socket.on("globalShortcut-unregister", (accelerator) => {
|
||||
globalShortcut.unregister(accelerator);
|
||||
});
|
||||
|
||||
socket.on('globalShortcut-unregisterAll', () => {
|
||||
try {
|
||||
globalShortcut.unregisterAll();
|
||||
} catch (error) { }
|
||||
});
|
||||
socket.on("globalShortcut-unregisterAll", () => {
|
||||
try {
|
||||
globalShortcut.unregisterAll();
|
||||
} catch (error) {}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,40 +3,41 @@ const electron_1 = require("electron");
|
||||
let electronSocket;
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('registerIpcMainChannel', (channel) => {
|
||||
socket.on("registerIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.on(channel, (event, args) => {
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
socket.on('registerSyncIpcMainChannel', (channel) => {
|
||||
socket.on("registerSyncIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.on(channel, (event, args) => {
|
||||
const x = socket;
|
||||
x.removeAllListeners(channel + 'Sync');
|
||||
socket.on(channel + 'Sync', (result) => {
|
||||
x.removeAllListeners(channel + "Sync");
|
||||
socket.on(channel + "Sync", (result) => {
|
||||
event.returnValue = result;
|
||||
});
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
socket.on('registerOnceIpcMainChannel', (channel) => {
|
||||
socket.on("registerOnceIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.once(channel, (event, args) => {
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
socket.on('removeAllListenersIpcMainChannel', (channel) => {
|
||||
socket.on("removeAllListenersIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.removeAllListeners(channel);
|
||||
});
|
||||
socket.on('sendToIpcRenderer', (browserWindow, channel, data) => {
|
||||
socket.on("sendToIpcRenderer", (browserWindow, channel, data) => {
|
||||
const window = electron_1.BrowserWindow.fromId(browserWindow.id);
|
||||
if (window) {
|
||||
window.webContents.send(channel, ...data);
|
||||
}
|
||||
});
|
||||
socket.on('sendToIpcRendererBrowserView', (id, channel, data) => {
|
||||
const browserViews = (global['browserViews'] = global['browserViews'] || []);
|
||||
socket.on("sendToIpcRendererBrowserView", (id, channel, data) => {
|
||||
const browserViews = (global["browserViews"] =
|
||||
global["browserViews"] || []);
|
||||
let view = null;
|
||||
for (let i = 0; i < browserViews.length; i++) {
|
||||
if (browserViews[i]['id'] === id) {
|
||||
if (browserViews[i]["id"] === id) {
|
||||
view = browserViews[i];
|
||||
break;
|
||||
}
|
||||
@@ -45,29 +46,58 @@ module.exports = (socket) => {
|
||||
view.webContents.send(channel, ...data);
|
||||
}
|
||||
});
|
||||
socket.on("registerHandleIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.handle(channel, (event, args) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
socket.removeAllListeners(channel + "Handle");
|
||||
socket.on(channel + "Handle", (result) => {
|
||||
resolve(result);
|
||||
});
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
});
|
||||
socket.on("registerHandleOnceIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.handleOnce(channel, (event, args) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
socket.removeAllListeners(channel + "HandleOnce");
|
||||
socket.once(channel + "HandleOnce", (result) => {
|
||||
resolve(result);
|
||||
});
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
});
|
||||
socket.on("removeHandlerIpcMainChannel", (channel) => {
|
||||
electron_1.ipcMain.removeHandler(channel);
|
||||
});
|
||||
// Integration helpers: programmatically click menu items from renderer tests
|
||||
electron_1.ipcMain.on('integration-click-application-menu', (event, id) => {
|
||||
electron_1.ipcMain.on("integration-click-application-menu", (event, id) => {
|
||||
try {
|
||||
const menu = electron_1.Menu.getApplicationMenu();
|
||||
const mi = menu ? menu.getMenuItemById(id) : null;
|
||||
if (mi && typeof mi.click === 'function') {
|
||||
if (mi && typeof mi.click === "function") {
|
||||
const bw = electron_1.BrowserWindow.fromWebContents(event.sender);
|
||||
mi.click(undefined, bw, undefined);
|
||||
}
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
catch {
|
||||
/* ignore */
|
||||
}
|
||||
});
|
||||
electron_1.ipcMain.on('integration-click-context-menu', (event, windowId, id) => {
|
||||
electron_1.ipcMain.on("integration-click-context-menu", (event, windowId, id) => {
|
||||
try {
|
||||
const entries = global['contextMenuItems'] || [];
|
||||
const entries = global["contextMenuItems"] || [];
|
||||
const entry = entries.find((x) => x.browserWindowId === windowId);
|
||||
const mi = entry?.menu?.items?.find((i) => i.id === id);
|
||||
if (mi && typeof mi.click === 'function') {
|
||||
if (mi && typeof mi.click === "function") {
|
||||
const bw = electron_1.BrowserWindow.fromId(windowId);
|
||||
mi.click(undefined, bw, undefined);
|
||||
}
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
catch {
|
||||
/* ignore */
|
||||
}
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=ipc.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"ipc.js","sourceRoot":"","sources":["ipc.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"ipc.js","sourceRoot":"","sources":["ipc.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,81 +1,118 @@
|
||||
import { ipcMain, BrowserWindow, BrowserView, Menu } from 'electron';
|
||||
import { Socket } from 'net';
|
||||
let electronSocket;
|
||||
import type { Socket } from "net";
|
||||
import { ipcMain, BrowserWindow, BrowserView, Menu } from "electron";
|
||||
|
||||
let electronSocket: Socket;
|
||||
|
||||
export = (socket: Socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('registerIpcMainChannel', (channel) => {
|
||||
ipcMain.on(channel, (event, args) => {
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
electronSocket = socket;
|
||||
socket.on("registerIpcMainChannel", (channel) => {
|
||||
ipcMain.on(channel, (event, args) => {
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("registerSyncIpcMainChannel", (channel) => {
|
||||
ipcMain.on(channel, (event, args) => {
|
||||
const x = <any>socket;
|
||||
x.removeAllListeners(channel + "Sync");
|
||||
socket.on(channel + "Sync", (result) => {
|
||||
event.returnValue = result;
|
||||
});
|
||||
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("registerOnceIpcMainChannel", (channel) => {
|
||||
ipcMain.once(channel, (event, args) => {
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("removeAllListenersIpcMainChannel", (channel) => {
|
||||
ipcMain.removeAllListeners(channel);
|
||||
});
|
||||
|
||||
socket.on("sendToIpcRenderer", (browserWindow, channel, data) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
|
||||
if (window) {
|
||||
window.webContents.send(channel, ...data);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("sendToIpcRendererBrowserView", (id, channel, data) => {
|
||||
const browserViews: BrowserView[] = (global["browserViews"] =
|
||||
global["browserViews"] || []) as BrowserView[];
|
||||
let view: BrowserView = null;
|
||||
for (let i = 0; i < browserViews.length; i++) {
|
||||
if (browserViews[i]["id"] === id) {
|
||||
view = browserViews[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (view) {
|
||||
view.webContents.send(channel, ...data);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("registerHandleIpcMainChannel", (channel) => {
|
||||
ipcMain.handle(channel, (event, args) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
socket.removeAllListeners(channel + "Handle");
|
||||
socket.on(channel + "Handle", (result) => {
|
||||
resolve(result);
|
||||
});
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('registerSyncIpcMainChannel', (channel) => {
|
||||
ipcMain.on(channel, (event, args) => {
|
||||
const x = <any>socket;
|
||||
x.removeAllListeners(channel + 'Sync');
|
||||
socket.on(channel + 'Sync', (result) => {
|
||||
event.returnValue = result;
|
||||
});
|
||||
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
socket.on("registerHandleOnceIpcMainChannel", (channel) => {
|
||||
ipcMain.handleOnce(channel, (event, args) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
socket.removeAllListeners(channel + "HandleOnce");
|
||||
socket.once(channel + "HandleOnce", (result) => {
|
||||
resolve(result);
|
||||
});
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('registerOnceIpcMainChannel', (channel) => {
|
||||
ipcMain.once(channel, (event, args) => {
|
||||
electronSocket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
socket.on("removeHandlerIpcMainChannel", (channel) => {
|
||||
ipcMain.removeHandler(channel);
|
||||
});
|
||||
|
||||
socket.on('removeAllListenersIpcMainChannel', (channel) => {
|
||||
ipcMain.removeAllListeners(channel);
|
||||
});
|
||||
// Integration helpers: programmatically click menu items from renderer tests
|
||||
ipcMain.on("integration-click-application-menu", (event, id: string) => {
|
||||
try {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
const mi = menu ? menu.getMenuItemById(id) : null;
|
||||
if (mi && typeof (mi as any).click === "function") {
|
||||
const bw = BrowserWindow.fromWebContents(event.sender);
|
||||
(mi as any).click(undefined, bw, undefined);
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('sendToIpcRenderer', (browserWindow, channel, data) => {
|
||||
const window = BrowserWindow.fromId(browserWindow.id);
|
||||
|
||||
if (window) {
|
||||
window.webContents.send(channel, ...data);
|
||||
ipcMain.on(
|
||||
"integration-click-context-menu",
|
||||
(event, windowId: number, id: string) => {
|
||||
try {
|
||||
const entries = (global as any)["contextMenuItems"] || [];
|
||||
const entry = entries.find((x: any) => x.browserWindowId === windowId);
|
||||
const mi = entry?.menu?.items?.find((i: any) => i.id === id);
|
||||
if (mi && typeof (mi as any).click === "function") {
|
||||
const bw = BrowserWindow.fromId(windowId);
|
||||
(mi as any).click(undefined, bw, undefined);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('sendToIpcRendererBrowserView', (id, channel, data) => {
|
||||
const browserViews: BrowserView[] = (global['browserViews'] = global['browserViews'] || []) as BrowserView[];
|
||||
let view: BrowserView = null;
|
||||
for (let i = 0; i < browserViews.length; i++) {
|
||||
if (browserViews[i]['id'] === id) {
|
||||
view = browserViews[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (view) {
|
||||
view.webContents.send(channel, ...data);
|
||||
}
|
||||
});
|
||||
|
||||
// Integration helpers: programmatically click menu items from renderer tests
|
||||
ipcMain.on('integration-click-application-menu', (event, id: string) => {
|
||||
try {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
const mi = menu ? menu.getMenuItemById(id) : null;
|
||||
if (mi && typeof (mi as any).click === 'function') {
|
||||
const bw = BrowserWindow.fromWebContents(event.sender);
|
||||
(mi as any).click(undefined, bw, undefined);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
});
|
||||
|
||||
ipcMain.on('integration-click-context-menu', (event, windowId: number, id: string) => {
|
||||
try {
|
||||
const entries = (global as any)['contextMenuItems'] || [];
|
||||
const entry = entries.find((x: any) => x.browserWindowId === windowId);
|
||||
const mi = entry?.menu?.items?.find((i: any) => i.id === id);
|
||||
if (mi && typeof (mi as any).click === 'function') {
|
||||
const bw = BrowserWindow.fromId(windowId);
|
||||
(mi as any).click(undefined, bw, undefined);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
});
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
"use strict";
|
||||
const electron_1 = require("electron");
|
||||
const contextMenuItems = (global['contextMenuItems'] = global['contextMenuItems'] || []);
|
||||
const contextMenuItems = (global["contextMenuItems"] =
|
||||
global["contextMenuItems"] || []);
|
||||
let electronSocket;
|
||||
module.exports = (socket) => {
|
||||
electronSocket = socket;
|
||||
socket.on('menu-setContextMenu', (browserWindowId, menuItems) => {
|
||||
socket.on("menu-setContextMenu", (browserWindowId, menuItems) => {
|
||||
const menu = electron_1.Menu.buildFromTemplate(menuItems);
|
||||
addContextMenuItemClickConnector(menu.items, browserWindowId, (id, windowId) => {
|
||||
electronSocket.emit('contextMenuItemClicked', [id, windowId]);
|
||||
electronSocket.emit("contextMenuItemClicked", [id, windowId]);
|
||||
});
|
||||
const index = contextMenuItems.findIndex(contextMenu => contextMenu.browserWindowId === browserWindowId);
|
||||
const index = contextMenuItems.findIndex((contextMenu) => contextMenu.browserWindowId === browserWindowId);
|
||||
const contextMenuItem = {
|
||||
menu: menu,
|
||||
browserWindowId: browserWindowId
|
||||
browserWindowId: browserWindowId,
|
||||
};
|
||||
if (index === -1) {
|
||||
contextMenuItems.push(contextMenuItem);
|
||||
@@ -26,23 +27,25 @@ module.exports = (socket) => {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addContextMenuItemClickConnector(item.submenu.items, browserWindowId, callback);
|
||||
}
|
||||
if ('id' in item && item.id) {
|
||||
item.click = () => { callback(item.id, browserWindowId); };
|
||||
if ("id" in item && item.id) {
|
||||
item.click = () => {
|
||||
callback(item.id, browserWindowId);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
socket.on('menu-contextMenuPopup', (browserWindowId) => {
|
||||
contextMenuItems.forEach(x => {
|
||||
socket.on("menu-contextMenuPopup", (browserWindowId) => {
|
||||
contextMenuItems.forEach((x) => {
|
||||
if (x.browserWindowId === browserWindowId) {
|
||||
const browserWindow = electron_1.BrowserWindow.fromId(browserWindowId);
|
||||
x.menu.popup(browserWindow);
|
||||
}
|
||||
});
|
||||
});
|
||||
socket.on('menu-setApplicationMenu', (menuItems) => {
|
||||
socket.on("menu-setApplicationMenu", (menuItems) => {
|
||||
const menu = electron_1.Menu.buildFromTemplate(menuItems);
|
||||
addMenuItemClickConnector(menu.items, (id) => {
|
||||
electronSocket.emit('menuItemClicked', id);
|
||||
electronSocket.emit("menuItemClicked", id);
|
||||
});
|
||||
electron_1.Menu.setApplicationMenu(menu);
|
||||
});
|
||||
@@ -51,8 +54,10 @@ module.exports = (socket) => {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addMenuItemClickConnector(item.submenu.items, callback);
|
||||
}
|
||||
if ('id' in item && item.id) {
|
||||
item.click = () => { callback(item.id); };
|
||||
if ("id" in item && item.id) {
|
||||
item.click = () => {
|
||||
callback(item.id);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"menu.js","sourceRoot":"","sources":["menu.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"menu.js","sourceRoot":"","sources":["menu.ts"],"names":[],"mappings":"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"}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user