clipboardy 4.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +2 -12
- package/index.js +27 -16
- package/lib/linux.js +7 -5
- package/lib/termux.js +1 -1
- package/lib/wayland.js +70 -0
- package/lib/windows.js +76 -4
- package/package.json +9 -8
- package/readme.md +37 -4
package/index.d.ts
CHANGED
|
@@ -9,9 +9,6 @@ declare const clipboard: {
|
|
|
9
9
|
import clipboard from 'clipboardy';
|
|
10
10
|
|
|
11
11
|
await clipboard.write('🦄');
|
|
12
|
-
|
|
13
|
-
await clipboard.read();
|
|
14
|
-
//=> '🦄'
|
|
15
12
|
```
|
|
16
13
|
*/
|
|
17
14
|
write(text: string): Promise<void>;
|
|
@@ -23,9 +20,7 @@ declare const clipboard: {
|
|
|
23
20
|
```
|
|
24
21
|
import clipboard from 'clipboardy';
|
|
25
22
|
|
|
26
|
-
await clipboard.
|
|
27
|
-
|
|
28
|
-
await clipboard.read();
|
|
23
|
+
const content = await clipboard.read();
|
|
29
24
|
//=> '🦄'
|
|
30
25
|
```
|
|
31
26
|
*/
|
|
@@ -43,9 +38,6 @@ declare const clipboard: {
|
|
|
43
38
|
import clipboard from 'clipboardy';
|
|
44
39
|
|
|
45
40
|
clipboard.writeSync('🦄');
|
|
46
|
-
|
|
47
|
-
clipboard.readSync();
|
|
48
|
-
//=> '🦄'
|
|
49
41
|
```
|
|
50
42
|
*/
|
|
51
43
|
writeSync(text: string): void;
|
|
@@ -59,9 +51,7 @@ declare const clipboard: {
|
|
|
59
51
|
```
|
|
60
52
|
import clipboard from 'clipboardy';
|
|
61
53
|
|
|
62
|
-
clipboard.
|
|
63
|
-
|
|
64
|
-
clipboard.readSync();
|
|
54
|
+
const content = clipboard.readSync();
|
|
65
55
|
//=> '🦄'
|
|
66
56
|
```
|
|
67
57
|
*/
|
package/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import isWSL from 'is-wsl';
|
|
3
|
+
import isWayland from 'is-wayland';
|
|
3
4
|
import termux from './lib/termux.js';
|
|
4
5
|
import linux from './lib/linux.js';
|
|
6
|
+
import wayland from './lib/wayland.js';
|
|
5
7
|
import macos from './lib/macos.js';
|
|
6
8
|
import windows from './lib/windows.js';
|
|
7
9
|
|
|
@@ -16,7 +18,7 @@ const platformLib = (() => {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
case 'android': {
|
|
19
|
-
if (process.env.
|
|
21
|
+
if (process.env.TERMUX__PREFIX === undefined) {
|
|
20
22
|
throw new Error('You need to install Termux for this module to work on Android: https://termux.com');
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -29,31 +31,40 @@ const platformLib = (() => {
|
|
|
29
31
|
return windows;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
// Check for Wayland session on Linux
|
|
35
|
+
if (isWayland()) {
|
|
36
|
+
return wayland;
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
return linux;
|
|
33
40
|
}
|
|
34
41
|
}
|
|
35
42
|
})();
|
|
36
43
|
|
|
37
|
-
const clipboard = {
|
|
44
|
+
const clipboard = {
|
|
45
|
+
async write(text) {
|
|
46
|
+
if (typeof text !== 'string') {
|
|
47
|
+
throw new TypeError(`Expected a string, got ${typeof text}`);
|
|
48
|
+
}
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
throw new TypeError(`Expected a string, got ${typeof text}`);
|
|
42
|
-
}
|
|
50
|
+
await platformLib.copy({input: text});
|
|
51
|
+
},
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
};
|
|
53
|
+
async read() {
|
|
54
|
+
return platformLib.paste({stripFinalNewline: false});
|
|
55
|
+
},
|
|
46
56
|
|
|
47
|
-
|
|
57
|
+
writeSync(text) {
|
|
58
|
+
if (typeof text !== 'string') {
|
|
59
|
+
throw new TypeError(`Expected a string, got ${typeof text}`);
|
|
60
|
+
}
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
throw new TypeError(`Expected a string, got ${typeof text}`);
|
|
52
|
-
}
|
|
62
|
+
platformLib.copySync({input: text});
|
|
63
|
+
},
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
readSync() {
|
|
66
|
+
return platformLib.pasteSync({stripFinalNewline: false});
|
|
67
|
+
},
|
|
55
68
|
};
|
|
56
69
|
|
|
57
|
-
clipboard.readSync = () => platformLib.pasteSync({stripFinalNewline: false});
|
|
58
|
-
|
|
59
70
|
export default clipboard;
|
package/lib/linux.js
CHANGED
|
@@ -11,11 +11,13 @@ const copyArguments = ['--clipboard', '--input'];
|
|
|
11
11
|
const pasteArguments = ['--clipboard', '--output'];
|
|
12
12
|
|
|
13
13
|
const makeError = (xselError, fallbackError) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const message = xselError.code === 'ENOENT'
|
|
15
|
+
? 'Couldn\'t find the `xsel` binary and fallback didn\'t work. On Debian/Ubuntu you can install xsel with: sudo apt install xsel'
|
|
16
|
+
: 'Both xsel and fallback failed';
|
|
17
|
+
|
|
18
|
+
const error = new Error(message);
|
|
19
|
+
|
|
20
|
+
if (xselError.code !== 'ENOENT') {
|
|
19
21
|
error.xselError = xselError;
|
|
20
22
|
}
|
|
21
23
|
|
package/lib/termux.js
CHANGED
|
@@ -2,7 +2,7 @@ import {execa, execaSync} from 'execa';
|
|
|
2
2
|
|
|
3
3
|
const handler = error => {
|
|
4
4
|
if (error.code === 'ENOENT') {
|
|
5
|
-
throw new Error('Couldn\'t find the termux-api scripts. You can install them with: apt install termux-api');
|
|
5
|
+
throw new Error('Couldn\'t find the `termux-api` scripts. You can install them with: apt install termux-api');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
throw error;
|
package/lib/wayland.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {execa, execaSync} from 'execa';
|
|
2
|
+
import linux from './linux.js';
|
|
3
|
+
|
|
4
|
+
// Common arguments for text clipboard operations
|
|
5
|
+
const textArgs = ['--type', 'text/plain'];
|
|
6
|
+
|
|
7
|
+
const makeError = (command, error) => {
|
|
8
|
+
if (error.code === 'ENOENT') {
|
|
9
|
+
return new Error(`Couldn't find the \`${command}\` binary. On Debian/Ubuntu you can install wl-clipboard with: sudo apt install wl-clipboard`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return new Error(`Command \`${command}\` failed: ${error.message}`);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const tryWaylandWithFallback = async (command, arguments_, options, fallbackMethod) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await execa(command, arguments_, options);
|
|
18
|
+
return result.stdout;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// Handle empty clipboard on wl-paste
|
|
21
|
+
if (command === 'wl-paste' && /nothing is copied|no selection|selection owner/i.test(error.stderr || '')) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Fall back to X11 if wl-clipboard not found OR Wayland not available
|
|
26
|
+
if (error.code === 'ENOENT'
|
|
27
|
+
|| /wayland|wayland_display|failed to connect|display/i.test(error.stderr || '')) {
|
|
28
|
+
return fallbackMethod(options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw makeError(command, error);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const tryWaylandWithFallbackSync = (command, arguments_, options, fallbackMethod) => {
|
|
36
|
+
try {
|
|
37
|
+
const result = execaSync(command, arguments_, options);
|
|
38
|
+
return result.stdout;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// Handle empty clipboard on wl-paste
|
|
41
|
+
if (command === 'wl-paste' && /nothing is copied|no selection|selection owner/i.test(error.stderr || '')) {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fall back to X11 if wl-clipboard not found OR Wayland not available
|
|
46
|
+
if (error.code === 'ENOENT'
|
|
47
|
+
|| /wayland|wayland_display|failed to connect|display/i.test(error.stderr || '')) {
|
|
48
|
+
return fallbackMethod(options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw makeError(command, error);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const clipboard = {
|
|
56
|
+
async copy(options) {
|
|
57
|
+
await tryWaylandWithFallback('wl-copy', textArgs, options, linux.copy);
|
|
58
|
+
},
|
|
59
|
+
copySync(options) {
|
|
60
|
+
tryWaylandWithFallbackSync('wl-copy', textArgs, options, linux.copySync);
|
|
61
|
+
},
|
|
62
|
+
async paste(options) {
|
|
63
|
+
return tryWaylandWithFallback('wl-paste', [...textArgs, '--no-newline'], options, linux.paste);
|
|
64
|
+
},
|
|
65
|
+
pasteSync(options) {
|
|
66
|
+
return tryWaylandWithFallbackSync('wl-paste', [...textArgs, '--no-newline'], options, linux.pasteSync);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default clipboard;
|
package/lib/windows.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {Buffer} from 'node:buffer';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import {fileURLToPath} from 'node:url';
|
|
3
4
|
import {execa, execaSync} from 'execa';
|
|
@@ -10,14 +11,85 @@ const binarySuffix = is64bitSync() ? 'x86_64' : 'i686';
|
|
|
10
11
|
// Binaries from: https://github.com/sindresorhus/win-clipboard
|
|
11
12
|
const windowBinaryPath = path.join(__dirname, `../fallbacks/windows/clipboard_${binarySuffix}.exe`);
|
|
12
13
|
|
|
14
|
+
const powershellPath = 'powershell.exe';
|
|
15
|
+
|
|
16
|
+
const psCommonArgs = [
|
|
17
|
+
'-NoProfile',
|
|
18
|
+
'-NonInteractive',
|
|
19
|
+
'-ExecutionPolicy',
|
|
20
|
+
'Bypass',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
// Use -EncodedCommand for better safety and Unicode handling
|
|
24
|
+
const createEncodedCommand = script => {
|
|
25
|
+
const encodedCommand = Buffer.from(script, 'utf16le').toString('base64');
|
|
26
|
+
return [...psCommonArgs, '-EncodedCommand', encodedCommand];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Robust PowerShell commands with error handling
|
|
30
|
+
const psCopyScript = `
|
|
31
|
+
try {
|
|
32
|
+
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
33
|
+
$input = [Console]::In.ReadToEnd()
|
|
34
|
+
if ($input -eq $null) { $input = '' }
|
|
35
|
+
Set-Clipboard -Value $input
|
|
36
|
+
} catch {
|
|
37
|
+
Write-Error "Failed to set clipboard: $($_.Exception.Message)"
|
|
38
|
+
exit 1
|
|
39
|
+
}
|
|
40
|
+
`.trim();
|
|
41
|
+
|
|
42
|
+
const psPasteScript = `
|
|
43
|
+
try {
|
|
44
|
+
$content = Get-Clipboard -Raw -ErrorAction Stop
|
|
45
|
+
if ($content -eq $null) { $content = '' }
|
|
46
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
47
|
+
[Console]::Out.Write($content)
|
|
48
|
+
} catch {
|
|
49
|
+
Write-Error "Failed to get clipboard: $($_.Exception.Message)"
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
`.trim();
|
|
53
|
+
|
|
54
|
+
const executeWithFallback = async (primaryCommand, fallbackCommand) => {
|
|
55
|
+
try {
|
|
56
|
+
return await primaryCommand();
|
|
57
|
+
} catch {
|
|
58
|
+
return fallbackCommand();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const executeWithFallbackSync = (primaryCommand, fallbackCommand) => {
|
|
63
|
+
try {
|
|
64
|
+
return primaryCommand();
|
|
65
|
+
} catch {
|
|
66
|
+
return fallbackCommand();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
13
70
|
const clipboard = {
|
|
14
|
-
copy: async options =>
|
|
71
|
+
copy: async options => executeWithFallback(
|
|
72
|
+
async () => execa(powershellPath, createEncodedCommand(psCopyScript), options),
|
|
73
|
+
async () => execa(windowBinaryPath, ['--copy'], options),
|
|
74
|
+
),
|
|
75
|
+
|
|
15
76
|
async paste(options) {
|
|
16
|
-
const {stdout} = await
|
|
77
|
+
const {stdout} = await executeWithFallback(
|
|
78
|
+
async () => execa(powershellPath, createEncodedCommand(psPasteScript), options),
|
|
79
|
+
async () => execa(windowBinaryPath, ['--paste'], options),
|
|
80
|
+
);
|
|
17
81
|
return stdout;
|
|
18
82
|
},
|
|
19
|
-
|
|
20
|
-
|
|
83
|
+
|
|
84
|
+
copySync: options => executeWithFallbackSync(
|
|
85
|
+
() => execaSync(powershellPath, createEncodedCommand(psCopyScript), options),
|
|
86
|
+
() => execaSync(windowBinaryPath, ['--copy'], options),
|
|
87
|
+
),
|
|
88
|
+
|
|
89
|
+
pasteSync: options => executeWithFallbackSync(
|
|
90
|
+
() => execaSync(powershellPath, createEncodedCommand(psPasteScript), options),
|
|
91
|
+
() => execaSync(windowBinaryPath, ['--paste'], options),
|
|
92
|
+
).stdout,
|
|
21
93
|
};
|
|
22
94
|
|
|
23
95
|
export default clipboard;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clipboardy",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Access the system clipboard (copy/paste)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/clipboardy",
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
"node": "./index.js",
|
|
17
17
|
"default": "./browser.js"
|
|
18
18
|
},
|
|
19
|
+
"sideEffects": false,
|
|
19
20
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
21
|
+
"node": ">=20"
|
|
21
22
|
},
|
|
22
|
-
"sideEffects": false,
|
|
23
23
|
"scripts": {
|
|
24
|
-
"test": "xo && ava
|
|
24
|
+
"//test": "xo && ava",
|
|
25
|
+
"test": "ava"
|
|
25
26
|
},
|
|
26
27
|
"files": [
|
|
27
28
|
"index.js",
|
|
@@ -44,14 +45,14 @@
|
|
|
44
45
|
"xsel"
|
|
45
46
|
],
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"execa": "^
|
|
48
|
+
"execa": "^9.6.0",
|
|
49
|
+
"is-wayland": "^0.1.0",
|
|
48
50
|
"is-wsl": "^3.1.0",
|
|
49
51
|
"is64bit": "^2.0.0"
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
|
-
"ava": "^
|
|
53
|
-
"
|
|
54
|
-
"xo": "^0.56.0"
|
|
54
|
+
"ava": "^6.4.1",
|
|
55
|
+
"xo": "^1.2.2"
|
|
55
56
|
},
|
|
56
57
|
"ava": {
|
|
57
58
|
"serial": true
|
package/readme.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Access the system clipboard (copy/paste)
|
|
4
4
|
|
|
5
|
-
Cross-platform. Supports: macOS, Windows, Linux, OpenBSD, FreeBSD, Android with [Termux](https://termux.com/), and [modern browsers](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#Browser_compatibility).
|
|
5
|
+
Cross-platform. Supports: macOS, Windows, Linux (including Wayland), OpenBSD, FreeBSD, Android with [Termux](https://termux.com/), and [modern browsers](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#Browser_compatibility).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -15,6 +15,12 @@ npm install clipboardy
|
|
|
15
15
|
```js
|
|
16
16
|
import clipboard from 'clipboardy';
|
|
17
17
|
|
|
18
|
+
await clipboard.write('🦄');
|
|
19
|
+
|
|
20
|
+
await clipboard.read();
|
|
21
|
+
//=> '🦄'
|
|
22
|
+
|
|
23
|
+
// Or use the synchronous API
|
|
18
24
|
clipboard.writeSync('🦄');
|
|
19
25
|
|
|
20
26
|
clipboard.readSync();
|
|
@@ -23,7 +29,7 @@ clipboard.readSync();
|
|
|
23
29
|
|
|
24
30
|
## API
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
**Browser usage:** Requires a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS). Synchronous methods are not available in browsers.
|
|
27
33
|
|
|
28
34
|
### clipboard
|
|
29
35
|
|
|
@@ -31,7 +37,7 @@ In the browser, it requires a [secure context](https://developer.mozilla.org/en-
|
|
|
31
37
|
|
|
32
38
|
Write (copy) to the clipboard asynchronously.
|
|
33
39
|
|
|
34
|
-
Returns a `Promise
|
|
40
|
+
Returns a `Promise<void>`.
|
|
35
41
|
|
|
36
42
|
##### text
|
|
37
43
|
|
|
@@ -39,11 +45,20 @@ Type: `string`
|
|
|
39
45
|
|
|
40
46
|
The text to write to the clipboard.
|
|
41
47
|
|
|
48
|
+
```js
|
|
49
|
+
await clipboard.write('🦄');
|
|
50
|
+
```
|
|
51
|
+
|
|
42
52
|
#### .read()
|
|
43
53
|
|
|
44
54
|
Read (paste) from the clipboard asynchronously.
|
|
45
55
|
|
|
46
|
-
Returns a `Promise
|
|
56
|
+
Returns a `Promise<string>`.
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const content = await clipboard.read();
|
|
60
|
+
//=> '🦄'
|
|
61
|
+
```
|
|
47
62
|
|
|
48
63
|
#### .writeSync(text)
|
|
49
64
|
|
|
@@ -57,19 +72,37 @@ Type: `string`
|
|
|
57
72
|
|
|
58
73
|
The text to write to the clipboard.
|
|
59
74
|
|
|
75
|
+
```js
|
|
76
|
+
clipboard.writeSync('🦄');
|
|
77
|
+
```
|
|
78
|
+
|
|
60
79
|
#### .readSync()
|
|
61
80
|
|
|
62
81
|
Read (paste) from the clipboard synchronously.
|
|
63
82
|
|
|
83
|
+
Returns a `string`.
|
|
84
|
+
|
|
64
85
|
**Doesn't work in browsers.**
|
|
65
86
|
|
|
87
|
+
```js
|
|
88
|
+
const content = clipboard.readSync();
|
|
89
|
+
//=> '🦄'
|
|
90
|
+
```
|
|
91
|
+
|
|
66
92
|
## FAQ
|
|
67
93
|
|
|
68
94
|
#### Where can I find the source of the bundled binaries?
|
|
69
95
|
|
|
70
96
|
The [Linux binary](fallbacks/linux/xsel) is just a bundled version of [`xsel`](https://linux.die.net/man/1/xsel). The source for the [Windows binary](fallbacks/windows/clipboard_x86_64.exe) can be found [here](https://github.com/sindresorhus/win-clipboard).
|
|
71
97
|
|
|
98
|
+
On Windows, clipboardy first tries the native PowerShell cmdlets (`Set-Clipboard`/`Get-Clipboard`) and falls back to the bundled binary if PowerShell is unavailable or restricted.
|
|
99
|
+
|
|
100
|
+
#### Does this work on Wayland?
|
|
101
|
+
|
|
102
|
+
Yes. On Linux, clipboardy automatically detects Wayland sessions and uses [`wl-clipboard`](https://github.com/bugaevc/wl-clipboard) when available. If not, it gracefully falls back to X11 tools. Also works with WSLg (Windows Subsystem for Linux GUI). Install `wl-clipboard` using your distribution's package manager.
|
|
103
|
+
|
|
72
104
|
## Related
|
|
73
105
|
|
|
74
106
|
- [clipboard-cli](https://github.com/sindresorhus/clipboard-cli) - CLI for this module
|
|
107
|
+
- [clipboard-image](https://github.com/sindresorhus/clipboard-image) - Get and set images on the clipboard
|
|
75
108
|
- [copy-text-to-clipboard](https://github.com/sindresorhus/copy-text-to-clipboard) - Copy text to the clipboard in the browser
|