Compare commits

..

2 Commits

Author SHA1 Message Date
Charles Kerr
4d1a32dab0 chore: make linter happy 2026-03-12 18:16:26 -05:00
Shelley Vohr
9774cbd508 feat: support notification priority on Windows
Add Windows notifications support urgency/priority levels.
This maps the existing `urgency` option (previously Linux-only) to
Windows toast notification priorities:

- 'critical' maps to ToastNotificationPriority_High, which sorts the
  notification above default-priority items in Action Center.
- 'normal' and 'low' both map to ToastNotificationPriority_Default.

Note that on Windows, 'critical' priority does not prevent the toast
from being auto-dismissed. Users should additionally set `timeoutType`
to 'never' for that behavior.
2026-03-12 16:29:24 +01:00
17 changed files with 42 additions and 334 deletions

View File

@@ -192,7 +192,6 @@ gh label list --repo electron/electron --search target/ --json name,color --jq '
```bash
npm run lint # Run all linters
npm run lint:clang-format # C++ formatting
npm run lint:api-history # Validate API history YAML blocks in docs
```
## Key Files

View File

@@ -1,58 +0,0 @@
# Electron Documentation Guide
## API History Migration
Guide: `docs/development/api-history-migration-guide.md`
Style rules: `docs/development/style-guide.md` (see "API History" section)
Schema: `docs/api-history.schema.json`
Lint: `npm run lint:api-history`
### Format
Place YAML history block directly after the Markdown header, before parameters:
````md
### `module.method(args)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/XXXXX
```
-->
* `arg` type - Description.
````
### Finding when an API was added
- `git log --all --reverse --oneline -S "methodName" -- docs/api/file.md` — find first commit adding a method name
- `git log --reverse -L :FunctionName:path/to/source.cc` — trace C++ implementation history
- `git log --grep="keyword" --oneline` — find merge commits referencing PRs
- `gh pr view <number> --repo electron/electron --json baseRefName` — verify PR targets main (not a backport)
- Always use the main-branch PR URL in history blocks, not backport PRs
### Cross-referencing breaking changes
- Search `docs/breaking-changes.md` for the API name to find deprecations/removals
- Use `git blame` on the breaking-changes entry to find the associated PR
- Add `breaking-changes-header` field using the heading ID from breaking-changes.md
### Placement rules
- Only add blocks to actual API entries (methods, events, properties with backtick signatures)
- Do NOT add blocks to section headers like `## Methods`, `### Instance Methods`, `## Events`
- Module-level blocks go after the `# moduleName` heading, before the module description quote
- For changes affecting multiple APIs, add a block under each affected top-level heading (see style guide "Change affecting multiple APIs")
### Key details
- `added` and `deprecated` arrays have `maxItems: 1`; `changes` can have multiple items
- `changes` entries require a `description` field; `added`/`deprecated` do not
- Wrap descriptions in double quotes to avoid YAML parsing issues with special characters
- Early Electron APIs (pre-2015) use merge-commit PRs (e.g., `Merge pull request #534`)
- Very early APIs (2013-2014, e.g., `ipcMain.on`, `ipcRenderer.send`) predate GitHub PRs — skip history blocks for these
- When multiple APIs were added in the same PR, they all reference the same PR URL
- Promisification PRs (e.g., #17355) count as `changes` entries with a description
- These PRs are breaking changes and should have their notes as "This method now returns a Promise instead of using a callback function."
- APIs that were deprecated and then removed from docs don't need history blocks (the removal is in `breaking-changes.md`)

View File

@@ -55,13 +55,6 @@ The `globalShortcut` module has the following methods:
### `globalShortcut.register(accelerator, callback)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
* `accelerator` string - An [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcut.
* `callback` Function
@@ -84,13 +77,6 @@ the app has been authorized as a [trusted accessibility client](https://develope
### `globalShortcut.registerAll(accelerators, callback)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/15542
```
-->
* `accelerators` string[] - An array of [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcuts.
* `callback` Function
@@ -110,13 +96,6 @@ the app has been authorized as a [trusted accessibility client](https://develope
### `globalShortcut.isRegistered(accelerator)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
* `accelerator` string - An [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcut.
Returns `boolean` - Whether this application has registered `accelerator`.
@@ -127,24 +106,10 @@ don't want applications to fight for global shortcuts.
### `globalShortcut.unregister(accelerator)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
* `accelerator` string - An [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcut.
Unregisters the global shortcut of `accelerator`.
### `globalShortcut.unregisterAll()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
Unregisters all of the global shortcuts.

View File

@@ -35,13 +35,6 @@ webContentsView.webContents.loadURL('https://electronjs.org')
## Class: ImageView extends `View`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/46760
```
-->
> A View that displays an image.
Process: [Main](../glossary.md#main-process)
@@ -56,13 +49,6 @@ Process: [Main](../glossary.md#main-process)
### `new ImageView()` _Experimental_
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/46760
```
-->
Creates an ImageView.
### Instance Methods
@@ -72,13 +58,6 @@ addition to those inherited from [View](view.md):
#### `image.setImage(image)` _Experimental_
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/46760
```
-->
* `image` NativeImage
Sets the image for this `ImageView`. Note that only image formats supported by

View File

@@ -1,12 +1,5 @@
# inAppPurchase
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
> In-app purchases on Mac App Store.
Process: [Main](../glossary.md#main-process)
@@ -17,13 +10,6 @@ The `inAppPurchase` module emits the following events:
### Event: 'transactions-updated'
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
Returns:
* `event` Event
@@ -37,19 +23,6 @@ The `inAppPurchase` module has the following methods:
### `inAppPurchase.purchaseProduct(productID[, opts])`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
changes:
- pr-url: https://github.com/electron/electron/pull/17355
description: "This method now returns a Promise instead of using a callback function."
breaking-changes-header: api-changed-callback-based-versions-of-promisified-apis
- pr-url: https://github.com/electron/electron/pull/35902
description: "Added `username` option to `opts` parameter."
```
-->
* `productID` string
* `opts` Integer | Object (optional) - If specified as an integer, defines the quantity.
* `quantity` Integer (optional) - The number of items the user wants to purchase.
@@ -61,17 +34,6 @@ You should listen for the `transactions-updated` event as soon as possible and c
### `inAppPurchase.getProducts(productIDs)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/12464
changes:
- pr-url: https://github.com/electron/electron/pull/17355
description: "This method now returns a Promise instead of using a callback function."
breaking-changes-header: api-changed-callback-based-versions-of-promisified-apis
```
-->
* `productIDs` string[] - The identifiers of the products to get.
Returns `Promise<Product[]>` - Resolves with an array of [`Product`](structures/product.md) objects.
@@ -80,59 +42,24 @@ Retrieves the product descriptions.
### `inAppPurchase.canMakePayments()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
Returns `boolean` - whether a user can make a payment.
### `inAppPurchase.restoreCompletedTransactions()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/21461
```
-->
Restores finished transactions. This method can be called either to install purchases on additional devices, or to restore purchases for an application that the user deleted and reinstalled.
[The payment queue](https://developer.apple.com/documentation/storekit/skpaymentqueue?language=objc) delivers a new transaction for each previously completed transaction that can be restored. Each transaction includes a copy of the original transaction.
### `inAppPurchase.getReceiptURL()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
Returns `string` - the path to the receipt.
### `inAppPurchase.finishAllTransactions()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/12464
```
-->
Completes all pending transactions.
### `inAppPurchase.finishTransactionByDate(date)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/12464
```
-->
* `date` string - The ISO formatted date of the transaction to finish.
Completes the pending transactions corresponding to the date.

View File

@@ -46,13 +46,6 @@ Listens to `channel`, when a new message arrives `listener` would be called with
### `ipcMain.off(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/44651
```
-->
* `channel` string
* `listener` Function
* `event` [IpcMainEvent][ipc-main-event]
@@ -96,13 +89,6 @@ Removes all listeners from the specified `channel`. Removes all listeners from a
### `ipcMain.handle(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
* `listener` Function\<Promise\<any\> | any\>
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
@@ -140,13 +126,6 @@ provided to the renderer process. Please refer to
### `ipcMain.handleOnce(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
* `listener` Function\<Promise\<any\> | any\>
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
@@ -157,13 +136,6 @@ Handles a single `invoke`able IPC message, then removes the listener. See
### `ipcMain.removeHandler(channel)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
Removes any handler for `channel`, if present.

View File

@@ -59,13 +59,6 @@ for more info.
### `ipcRenderer.off(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/39816
```
-->
* `channel` string
* `listener` Function
* `event` [IpcRendererEvent][ipc-renderer-event]
@@ -86,13 +79,6 @@ only the next time a message is sent to `channel`, after which it is removed.
### `ipcRenderer.addListener(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/39816
```
-->
* `channel` string
* `listener` Function
* `event` [IpcRendererEvent][ipc-renderer-event]
@@ -143,13 +129,6 @@ If you want to receive a single response from the main process, like the result
### `ipcRenderer.invoke(channel, ...args)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
* `...args` any[]
@@ -230,13 +209,6 @@ and replies by setting `event.returnValue`.
### `ipcRenderer.postMessage(channel, message, [transfer])`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/22404
```
-->
* `channel` string
* `message` any
* `transfer` MessagePort[] (optional)

View File

@@ -88,11 +88,15 @@ app.whenReady().then(() => {
* `timeoutType` string (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
* `replyPlaceholder` string (optional) _macOS_ - The placeholder to write in the inline reply input field.
* `sound` string (optional) _macOS_ - The name of the sound file to play when the notification is shown.
* `urgency` string (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
* `urgency` string (optional) _Linux_ _Windows_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
* `closeButtonText` string (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
* `toastXml` string (optional) _Windows_ - A custom description of the Notification on Windows superseding all properties above. Provides full customization of design and behavior of the notification.
> [!NOTE]
> On Windows, `urgency` type 'critical' sorts the notification higher in Action Center (above default priority notifications), but does not prevent auto-dismissal. To prevent auto-dismissal, you should also set
> `timeoutType` to 'never'.
### Instance Events
Objects created with `new Notification` emit the following events:

View File

@@ -59,17 +59,6 @@ npm install electron --save-dev --ignore-scripts
npx install-electron --no
```
If you need to test changes across platforms or architectures, you should now use the
`ELECTRON_INSTALL_ARCH` and `ELECTRON_INSTALL_PLATFORM` environment variables.
```sh
# before: pass npm config flag on install command
npm install --platform=mas electron --save-dev
# after: add env var when you first run the Electron command
npm install electron --save-dev
ELECTRON_INSTALL_PLATFORM=mas npx electron . --no
```
### Removed: `quotas` object from `Session.clearStorageData(options)`
When calling `Session.clearStorageData(options)`, the `options.quotas` object is no longer supported because it has been

View File

@@ -51,40 +51,20 @@ any dependencies in your app will not be installed.
## Customization
If you want to change the architecture that is downloaded (e.g., `x64` on an
`arm64` machine), you can set the `ELECTRON_INSTALL_ARCH` environment variable:
`arm64` machine), you can use the `--arch` flag with npm install or set the
`npm_config_arch` environment variable:
```sh
# Inside an npm script or with npx
ELECTRON_INSTALL_ARCH=x64 electron .
```shell
npm install --arch=x64 electron
```
Supported architectures are a subset of Node.js [`process.arch`](https://nodejs.org/api/process.html#processarch)
values, and include:
* `x64` (Intel Mac and 64-bit Windows)
* `ia32` (32-bit Windows)
* `arm64` (Apple silicon, Windows on ARM, ARM64 Linux)
* `arm` (32-bit ARM)
In addition to changing the architecture, you can also specify the platform
(e.g., `win32`, `linux`, etc.) using the `--platform` flag:
```sh
# Inside an npm script or with npx
ELECTRON_INSTALL_PLATFORM=mas electron .
```shell
npm install --platform=win32 electron
```
Supported platforms are Node-like [platform strings](https://nodejs.org/api/process.html#processplatform):
* `darwin`
* `mas` ([Mac App Store](./mac-app-store-submission-guide.md))
* `win32`
* `linux`
> [!TIP]
> To see all available platform/architecture combinations for a particular release, see the artifacts
> on [Electron's GitHub Releases](https://github.com/electron/electron/releases).
## Proxies
If you need to use an HTTP proxy, you need to set the `ELECTRON_GET_USE_PROXY` variable to any

View File

@@ -78,7 +78,6 @@ auto_filenames = {
"docs/api/web-utils.md",
"docs/api/webview-tag.md",
"docs/api/window-open.md",
"docs/api/structures/activation-arguments.md",
"docs/api/structures/base-window-options.md",
"docs/api/structures/bluetooth-device.md",
"docs/api/structures/browser-window-options.md",

View File

@@ -21,11 +21,8 @@ if (isInstalled()) {
process.exit(0);
}
const platform = process.env.ELECTRON_INSTALL_PLATFORM || process.env.npm_config_platform || process.platform;
let arch =
process.env.ELECTRON_INSTALL_ARCH ||
process.env.npm_config_arch ||
process.arch;
const platform = process.env.npm_config_platform || process.platform;
let arch = process.env.npm_config_arch || process.arch;
if (platform === 'darwin' && process.platform === 'darwin' && arch === 'x64' &&
process.env.npm_config_arch === undefined) {

View File

@@ -45,7 +45,7 @@ struct NotificationOptions {
std::u16string timeout_type;
std::u16string reply_placeholder;
std::u16string sound;
std::u16string urgency; // Linux
std::u16string urgency; // Linux/Windows
std::vector<NotificationAction> actions;
std::u16string close_button_text;
std::u16string toast_xml;

View File

@@ -280,8 +280,9 @@ void WindowsToastNotification::CreateToastNotificationOnBackgroundThread(
// Continue to create the toast notification
ComPtr<ABI::Windows::UI::Notifications::IToastNotification>
toast_notification;
if (!CreateToastNotification(toast_xml, notification_id, weak_notification,
ui_task_runner, &toast_notification)) {
if (!CreateToastNotification(toast_xml, options, notification_id,
weak_notification, ui_task_runner,
&toast_notification)) {
return; // Error already posted to UI thread
}
@@ -349,6 +350,7 @@ bool WindowsToastNotification::CreateToastXmlDocument(
// returns the created notification via out parameter.
bool WindowsToastNotification::CreateToastNotification(
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
const NotificationOptions& options,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -416,6 +418,27 @@ bool WindowsToastNotification::CreateToastNotification(
return false;
}
ComPtr<winui::Notifications::IToastNotification4> toast4;
hr = (*toast_notification)->QueryInterface(IID_PPV_ARGS(&toast4));
if (SUCCEEDED(hr)) {
winui::Notifications::ToastNotificationPriority priority =
winui::Notifications::ToastNotificationPriority::
ToastNotificationPriority_Default;
if (options.urgency == u"critical") {
priority = winui::Notifications::ToastNotificationPriority::
ToastNotificationPriority_High;
}
hr = toast4->put_Priority(priority);
if (FAILED(hr)) {
std::string err = base::StrCat({"WinAPI: Setting priority failed, ERROR ",
FailureResultToString(hr)});
DebugLog(err);
PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
return false;
}
}
return true;
}

View File

@@ -94,6 +94,7 @@ class WindowsToastNotification : public Notification {
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
static bool CreateToastNotification(
ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
const NotificationOptions& options,
const std::string& notification_id,
base::WeakPtr<Notification> weak_notification,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,

View File

@@ -4,8 +4,6 @@
#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h"
#include <memory>
#include "base/win/windows_version.h"
#include "electron/buildflags/buildflags.h"
#include "shell/browser/api/electron_api_web_contents.h"
@@ -13,7 +11,6 @@
#include "shell/browser/ui/views/win_frame_view.h"
#include "shell/browser/win/dark_mode.h"
#include "ui/base/win/hwnd_metrics.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace electron {
@@ -153,25 +150,6 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) {
return views::DesktopWindowTreeHostWin::HandleMouseEvent(event);
}
void ElectronDesktopWindowTreeHostWin::HandleKeyEvent(ui::KeyEvent* event) {
// Chromium's base implementation intentionally drops ALT+SPACE keydown
// events so that the subsequent WM_SYSCHAR can open the system menu.
// We defer the event instead, so that if the system-context-menu event
// is prevented by the user, we can replay it to the renderer.
if ((event->type() == ui::EventType::kKeyPressed) &&
(event->key_code() == ui::VKEY_SPACE) &&
(event->flags() & ui::EF_ALT_DOWN) &&
!(event->flags() & ui::EF_CONTROL_DOWN)) {
if (views::Widget* widget = GetWidget();
widget && widget->non_client_view()) {
deferred_alt_space_event_ = std::make_unique<ui::KeyEvent>(*event);
return;
}
}
views::DesktopWindowTreeHostWin::HandleKeyEvent(event);
}
bool ElectronDesktopWindowTreeHostWin::HandleIMEMessage(UINT message,
WPARAM w_param,
LPARAM l_param,
@@ -187,20 +165,8 @@ bool ElectronDesktopWindowTreeHostWin::HandleIMEMessage(UINT message,
native_window_view_->NotifyWindowSystemContextMenu(
location.x(), location.y(), &prevent_default);
if (prevent_default) {
// The system menu was suppressed. Replay the deferred ALT+SPACE
// keydown so it reaches before-input-event and the renderer.
if (deferred_alt_space_event_) {
SendEventToSink(deferred_alt_space_event_.get());
deferred_alt_space_event_.reset();
}
return true;
}
// System menu not prevented — discard the deferred event and let
// the default WM_SYSCHAR handling open the system menu.
deferred_alt_space_event_.reset();
return views::DesktopWindowTreeHostWin::HandleIMEMessage(message, w_param,
return prevent_default ||
views::DesktopWindowTreeHostWin::HandleIMEMessage(message, w_param,
l_param, result);
}
}

View File

@@ -7,10 +7,8 @@
#include <windows.h>
#include <memory>
#include <optional>
#include "ui/events/event.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
namespace electron {
@@ -46,7 +44,6 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
int frame_thickness) const override;
bool HandleMouseEventForCaption(UINT message) const override;
bool HandleMouseEvent(ui::MouseEvent* event) override;
void HandleKeyEvent(ui::KeyEvent* event) override;
bool HandleIMEMessage(UINT message,
WPARAM w_param,
LPARAM l_param,
@@ -66,10 +63,6 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
std::optional<bool> force_should_paint_as_active_;
bool allow_screenshots_ = true;
bool widget_init_done_ = false;
// Stores the ALT+SPACE key event deferred from HandleKeyEvent so it can be
// replayed in HandleIMEMessage if the system-context-menu is prevented.
std::unique_ptr<ui::KeyEvent> deferred_alt_space_event_;
};
} // namespace electron