Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Features:

Fixes:

- WSL: converts `icon` paths on Windows drives (`/mnt/c/...`) and skips other icons instead of showing an empty "SnoreToast" notification ([#354](https://github.com/mikaelbr/node-notifier/issues/354)).
- Windows: `WindowsToaster` now accepts `remove` without a `message`, the same as `close` ([#431](https://github.com/mikaelbr/node-notifier/issues/431)).
- Windows: updates the bundled 64-bit SnoreToast to 0.9.1, so clicks and action buttons are reported back when using a custom `appID` ([#326](https://github.com/mikaelbr/node-notifier/issues/326), [#332](https://github.com/mikaelbr/node-notifier/issues/332), [#375](https://github.com/mikaelbr/node-notifier/pull/375)). The binary is KDE's own build of the official release. The 32-bit binary stays on 0.7.0, as KDE publishes no 32-bit build. Note that the new binary is not code-signed.
- Windows: fixes `notify` throwing in `cluster` worker processes ([#410](https://github.com/mikaelbr/node-notifier/issues/410)).
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ _**Short answer:** update your `appID`._
If you don't see notifications within WSL2, you might have to change permission of exe vendor files (snoreToast).
[See issue for more info](https://github.com/mikaelbr/node-notifier/issues/353)

SnoreToast can only show icons from a Windows drive. Under WSL, an `icon` on a mounted drive (e.g. `/mnt/c/Users/me/icon.png`) is converted to its Windows path, and other icons (e.g. in your Linux home folder) are skipped.

### WSL: Use Linux notifications (`notify-send`)

Under WSL, the default notifier is `WindowsToaster`, which shows the notification in Windows. If you run an X server and a notification daemon and want Linux notifications instead, set `NODE_NOTIFIER_WSL_NOTIFIER=linux`:
Expand Down
30 changes: 24 additions & 6 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,18 +360,36 @@ const allowedToasterFlags = [
const toasterSoundPrefix = 'Notification.';
const toasterDefaultSound = 'Notification.Default';

// SnoreToast only reads local Windows files. Under WSL, maps /mnt/<drive>/...
// to <DRIVE>:\... and returns undefined for paths it can't map, as passing
// a Linux path makes SnoreToast show an empty notification (#354).
function toWslIconPath(icon) {
// Already a Windows path, e.g. C:\icon.png, or /C:/icon.png from a file: URL
if (/^\/?[a-zA-Z]:[\\/]/.test(icon)) {
return icon.replace(/^\//, '').replace(/\//g, '\\');
}
const match = /^\/mnt\/([a-z])\/(.+)$/.exec(icon);
if (!match || match[2].split('/').includes('..')) {
return undefined;
}
return `${match[1].toUpperCase()}:\\${match[2].replace(/\//g, '\\')}`;
}

function mapToWin8(options) {
options = mapAppIcon(options);
options = mapText(options);

if (options.icon) {
if (/^file:\/+/.test(options.icon)) {
// should parse file protocol URL to path
options.p = new URL(options.icon).pathname
.replace(/^\/(\w:\/)/, '$1')
.replace(/\//g, '\\');
const isFileUrl = /^file:\/+/.test(options.icon);
// should parse file protocol URL to path
const icon = isFileUrl ? new URL(options.icon).pathname : options.icon;
if (utils.isWSL()) {
const wslIcon = toWslIconPath(icon);
if (wslIcon) options.p = wslIcon;
} else if (isFileUrl) {
options.p = icon.replace(/^\/(\w:\/)/, '$1').replace(/\//g, '\\');
} else {
options.p = options.icon;
options.p = icon;
}
delete options.icon;
}
Expand Down
62 changes: 62 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,66 @@ describe('utils', function () {
});
});
});

describe('mapToWin8 icon', function () {
const isWSL = _.isWSL;
afterEach(function () {
_.isWSL = isWSL;
});

function iconFor(icon, wsl) {
_.isWSL = () => wsl;
const mapped = _.mapToWin8({ title: 'Foo', message: 'Bar', icon });
expect(mapped.icon).toBeUndefined();
return mapped.p;
}

it('should convert /mnt/<drive>/ paths under WSL', function () {
expect(iconFor('/mnt/c/Users/me/icon.png', true)).toBe(
'C:\\Users\\me\\icon.png'
);
});

it('should convert file:///mnt/<drive>/ URLs under WSL', function () {
expect(iconFor('file:///mnt/d/icons/icon.png', true)).toBe(
'D:\\icons\\icon.png'
);
});

it('should keep Windows paths under WSL', function () {
expect(iconFor('C:\\icon.png', true)).toBe('C:\\icon.png');
expect(iconFor('file:///C:/icons/icon.png', true)).toBe(
'C:\\icons\\icon.png'
);
});

it('should drop icons not on a Windows drive under WSL', function () {
for (const icon of [
'/home/me/icon.png',
'icon.png',
'./icon.png',
'http://example.com/icon.png',
'file:///home/me/icon.png',
'/mnt/c',
'/mnt/c/',
'/mnt/cdrom/icon.png',
'/mnt/C/icon.png',
'/mnt/c/../../etc/icon.png',
'file:///mnt/c/../../home/me/icon.png',
'\\\\wsl$\\Ubuntu\\home\\me\\icon.png'
]) {
expect(iconFor(icon, true), icon).toBeUndefined();
}
});

it('should not change icons outside WSL', function () {
expect(iconFor('/mnt/c/Users/me/icon.png', false)).toBe(
'/mnt/c/Users/me/icon.png'
);
expect(iconFor('/home/me/icon.png', false)).toBe('/home/me/icon.png');
expect(iconFor('file:///C:/icons/icon.png', false)).toBe(
'C:\\icons\\icon.png'
);
});
});
});
Loading