@crosscopy/clipboard 0.2.3 → 0.2.5
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/Cargo.toml +1 -0
- package/README.md +46 -0
- package/exp.ts +7 -11
- package/index.d.ts +17 -6
- package/index.js +14 -3
- package/package.json +9 -9
- package/src/lib.rs +154 -15
package/Cargo.toml
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Clipboard
|
|
2
|
+
|
|
3
|
+
npm package: https://www.npmjs.com/package/@crosscopy/clipboard
|
|
4
|
+
GitHub: https://github.com/crosscopy/clipboard
|
|
5
|
+
|
|
6
|
+
> This is a clipboard API npm package that allows you to copy and paste data to and from the clipboard.
|
|
7
|
+
> There doesn't seem to be a good clipboard package for node.js (that supports data format beyond text), so I decided to make one.
|
|
8
|
+
> Data Format Supported
|
|
9
|
+
>
|
|
10
|
+
> - Text
|
|
11
|
+
> - Image
|
|
12
|
+
> - Rich Text Format
|
|
13
|
+
> - Files
|
|
14
|
+
> - HTML
|
|
15
|
+
|
|
16
|
+
## Acknowledgements
|
|
17
|
+
|
|
18
|
+
- [ChurchTao/clipboard-rs](https://github.com/ChurchTao/clipboard-rs) is written in rust, which is used to provide the native clipboard support for this package across Linux, Windows and MacOS. This package is basically a wrapper around this rust package.
|
|
19
|
+
- https://crates.io/crates/clipboard-rs
|
|
20
|
+
- [napi.rs](https://napi.rs/) was used to create the node.js addon for this package, so that API calls written in rust can be called from node.js.
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
Detailed API function declarations can be found in the [index.d.ts](./index.d.ts).
|
|
25
|
+
|
|
26
|
+
Or you can refer to the source code in [src/lib.rs](./src/lib.rs).
|
|
27
|
+
|
|
28
|
+
## Sample
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import Clipboard from "@crosscopy/clipboard";
|
|
32
|
+
|
|
33
|
+
console.log(await Clipboard.getText());
|
|
34
|
+
|
|
35
|
+
console.log(await Clipboard.getHtml());
|
|
36
|
+
|
|
37
|
+
if (await Clipboard.hasImage()) {
|
|
38
|
+
console.log(await Clipboard.getImageBase64());
|
|
39
|
+
} else {
|
|
40
|
+
console.log("No Image");
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Plan
|
|
45
|
+
|
|
46
|
+
A clipboard listener will be added soon for monitoring clipboard changes and get notified when the clipboard content changes.
|
package/exp.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { getText, getImageBase64, Animal, asyncGetText } from "./index.js";
|
|
2
1
|
import Clipboard from "./index.js";
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
console.log(await Clipboard.getText());
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
console.log(await Clipboard.getHtml());
|
|
7
6
|
|
|
8
|
-
// const dog = new Animal("dog", 5);
|
|
9
|
-
// console.log(dog.name);
|
|
10
|
-
// dog.changeName("dog 2");
|
|
11
|
-
// console.log(dog.name);
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
if (await Clipboard.hasImage()) {
|
|
9
|
+
console.log(await Clipboard.getImageBase64());
|
|
10
|
+
} else {
|
|
11
|
+
console.log("No Image");
|
|
12
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -3,10 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
-
export function sum(a: number, b: number): number
|
|
7
6
|
export function availableFormats(): Array<string>
|
|
8
|
-
export function getText(): string
|
|
9
|
-
export function
|
|
10
|
-
export function
|
|
11
|
-
export function
|
|
12
|
-
export function
|
|
7
|
+
export function getText(): Promise<string>
|
|
8
|
+
export function setText(text: string): Promise<void>
|
|
9
|
+
export function hasText(): boolean
|
|
10
|
+
export function getImageBinary(): Promise<Array<number>>
|
|
11
|
+
export function getImageBase64(): Promise<string>
|
|
12
|
+
export function setImageBinary(imageBytes: Array<number>): Promise<void>
|
|
13
|
+
export function setImageBase64(base64Str: string): Promise<void>
|
|
14
|
+
export function hasImage(): boolean
|
|
15
|
+
export function getHtml(): Promise<string>
|
|
16
|
+
export function setHtml(html: string): Promise<void>
|
|
17
|
+
export function hasHtml(): boolean
|
|
18
|
+
export function getRtf(): Promise<string>
|
|
19
|
+
export function setRtf(rtf: string): Promise<void>
|
|
20
|
+
export function hasRtf(): boolean
|
|
21
|
+
export function clear(): Promise<void>
|
|
22
|
+
export function watch(): void
|
|
23
|
+
export function callThreadsafeFunction(callback: (...args: any[]) => any): void
|
package/index.js
CHANGED
|
@@ -295,12 +295,23 @@ if (!nativeBinding) {
|
|
|
295
295
|
throw new Error(`Failed to load native binding`)
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
const {
|
|
298
|
+
const { availableFormats, getText, setText, hasText, getImageBinary, getImageBase64, setImageBinary, setImageBase64, hasImage, getHtml, setHtml, hasHtml, getRtf, setRtf, hasRtf, clear, watch, callThreadsafeFunction } = nativeBinding
|
|
299
299
|
|
|
300
|
-
module.exports.sum = sum
|
|
301
300
|
module.exports.availableFormats = availableFormats
|
|
302
301
|
module.exports.getText = getText
|
|
303
|
-
module.exports.getTextAsync = getTextAsync
|
|
304
302
|
module.exports.setText = setText
|
|
303
|
+
module.exports.hasText = hasText
|
|
304
|
+
module.exports.getImageBinary = getImageBinary
|
|
305
305
|
module.exports.getImageBase64 = getImageBase64
|
|
306
|
+
module.exports.setImageBinary = setImageBinary
|
|
306
307
|
module.exports.setImageBase64 = setImageBase64
|
|
308
|
+
module.exports.hasImage = hasImage
|
|
309
|
+
module.exports.getHtml = getHtml
|
|
310
|
+
module.exports.setHtml = setHtml
|
|
311
|
+
module.exports.hasHtml = hasHtml
|
|
312
|
+
module.exports.getRtf = getRtf
|
|
313
|
+
module.exports.setRtf = setRtf
|
|
314
|
+
module.exports.hasRtf = hasRtf
|
|
315
|
+
module.exports.clear = clear
|
|
316
|
+
module.exports.watch = watch
|
|
317
|
+
module.exports.callThreadsafeFunction = callThreadsafeFunction
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crosscopy/clipboard",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"napi": {
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
"version": "napi version"
|
|
41
41
|
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
|
-
"@crosscopy/clipboard-win32-x64-msvc": "0.2.
|
|
44
|
-
"@crosscopy/clipboard-darwin-x64": "0.2.
|
|
45
|
-
"@crosscopy/clipboard-linux-x64-gnu": "0.2.
|
|
46
|
-
"@crosscopy/clipboard-darwin-arm64": "0.2.
|
|
47
|
-
"@crosscopy/clipboard-linux-arm64-gnu": "0.2.
|
|
48
|
-
"@crosscopy/clipboard-win32-arm64-msvc": "0.2.
|
|
49
|
-
"@crosscopy/clipboard-darwin-universal": "0.2.
|
|
50
|
-
"@crosscopy/clipboard-linux-riscv64-gnu": "0.2.
|
|
43
|
+
"@crosscopy/clipboard-win32-x64-msvc": "0.2.5",
|
|
44
|
+
"@crosscopy/clipboard-darwin-x64": "0.2.5",
|
|
45
|
+
"@crosscopy/clipboard-linux-x64-gnu": "0.2.5",
|
|
46
|
+
"@crosscopy/clipboard-darwin-arm64": "0.2.5",
|
|
47
|
+
"@crosscopy/clipboard-linux-arm64-gnu": "0.2.5",
|
|
48
|
+
"@crosscopy/clipboard-win32-arm64-msvc": "0.2.5",
|
|
49
|
+
"@crosscopy/clipboard-darwin-universal": "0.2.5",
|
|
50
|
+
"@crosscopy/clipboard-linux-riscv64-gnu": "0.2.5"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/lib.rs
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#![deny(clippy::all)]
|
|
2
2
|
use base64::{engine::general_purpose, Engine as _};
|
|
3
|
-
use clipboard_rs::{
|
|
3
|
+
use clipboard_rs::{
|
|
4
|
+
common::RustImage, Clipboard, ClipboardContext, ClipboardHandler, ClipboardWatcher,
|
|
5
|
+
ClipboardWatcherContext, ContentFormat, RustImageData,
|
|
6
|
+
};
|
|
7
|
+
use napi::{
|
|
8
|
+
bindgen_prelude::*,
|
|
9
|
+
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
|
10
|
+
CallContext, JsFunction, JsString,
|
|
11
|
+
};
|
|
12
|
+
use std::{thread, time::Duration};
|
|
4
13
|
|
|
5
14
|
#[macro_use]
|
|
6
15
|
extern crate napi_derive;
|
|
7
16
|
|
|
8
|
-
#[napi]
|
|
9
|
-
pub fn sum(a: i32, b: i32) -> i32 {
|
|
10
|
-
a + b
|
|
11
|
-
}
|
|
12
|
-
|
|
13
17
|
#[napi]
|
|
14
18
|
pub fn available_formats() -> Vec<String> {
|
|
15
19
|
let ctx = ClipboardContext::new().unwrap();
|
|
@@ -18,36 +22,171 @@ pub fn available_formats() -> Vec<String> {
|
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
#[napi]
|
|
21
|
-
pub fn get_text() -> String {
|
|
25
|
+
pub async fn get_text() -> String {
|
|
22
26
|
let ctx = ClipboardContext::new().unwrap();
|
|
23
27
|
ctx.get_text().unwrap()
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
#[napi]
|
|
27
|
-
async fn
|
|
31
|
+
pub async fn set_text(text: String) {
|
|
28
32
|
let ctx = ClipboardContext::new().unwrap();
|
|
29
|
-
ctx.
|
|
33
|
+
ctx.set_text(text).unwrap()
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
#[napi]
|
|
33
|
-
pub fn
|
|
37
|
+
pub fn has_text() -> bool {
|
|
34
38
|
let ctx = ClipboardContext::new().unwrap();
|
|
35
|
-
ctx.
|
|
39
|
+
ctx.has(ContentFormat::Text)
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
#[napi]
|
|
39
|
-
pub fn
|
|
43
|
+
pub async fn get_image_binary() -> Vec<u8> {
|
|
40
44
|
let ctx = ClipboardContext::new().unwrap();
|
|
41
45
|
let image = ctx.get_image().unwrap();
|
|
42
46
|
let image_bytes = image.to_png().unwrap().get_bytes().to_vec();
|
|
47
|
+
image_bytes
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[napi]
|
|
51
|
+
pub async fn get_image_base64() -> String {
|
|
52
|
+
let image_bytes = get_image_binary().await;
|
|
43
53
|
let base64_str = general_purpose::STANDARD_NO_PAD.encode(&image_bytes);
|
|
44
54
|
base64_str
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
#[napi]
|
|
48
|
-
pub fn
|
|
58
|
+
pub async fn set_image_binary(image_bytes: Vec<u8>) {
|
|
49
59
|
let ctx = ClipboardContext::new().unwrap();
|
|
50
|
-
let
|
|
51
|
-
let img = RustImageData::from_bytes(&decoded).unwrap();
|
|
60
|
+
let img = RustImageData::from_bytes(&image_bytes).unwrap();
|
|
52
61
|
ctx.set_image(img).unwrap()
|
|
53
62
|
}
|
|
63
|
+
|
|
64
|
+
#[napi]
|
|
65
|
+
pub async fn set_image_base64(base64_str: String) {
|
|
66
|
+
let decoded: Vec<u8> = general_purpose::STANDARD_NO_PAD.decode(base64_str).unwrap();
|
|
67
|
+
set_image_binary(decoded).await;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[napi]
|
|
71
|
+
pub fn has_image() -> bool {
|
|
72
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
73
|
+
ctx.has(ContentFormat::Image)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#[napi]
|
|
77
|
+
pub async fn get_html() -> String {
|
|
78
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
79
|
+
ctx.get_html().unwrap()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[napi]
|
|
83
|
+
pub async fn set_html(html: String) {
|
|
84
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
85
|
+
ctx.set_html(html).unwrap()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#[napi]
|
|
89
|
+
fn has_html() -> bool {
|
|
90
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
91
|
+
ctx.has(ContentFormat::Html)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[napi]
|
|
95
|
+
pub async fn get_rtf() -> String {
|
|
96
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
97
|
+
ctx.get_rich_text().unwrap()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[napi]
|
|
101
|
+
pub async fn set_rtf(rtf: String) {
|
|
102
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
103
|
+
ctx.set_rich_text(rtf).unwrap()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#[napi]
|
|
107
|
+
pub fn has_rtf() -> bool {
|
|
108
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
109
|
+
ctx.has(ContentFormat::Rtf)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#[napi]
|
|
113
|
+
pub async fn clear() {
|
|
114
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
115
|
+
ctx.clear().unwrap()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
struct Manager {
|
|
119
|
+
ctx: ClipboardContext,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
impl Manager {
|
|
123
|
+
pub fn new() -> Self {
|
|
124
|
+
let ctx = ClipboardContext::new().unwrap();
|
|
125
|
+
Manager { ctx }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
impl ClipboardHandler for Manager {
|
|
130
|
+
fn on_clipboard_change(&mut self) {
|
|
131
|
+
println!(
|
|
132
|
+
"on_clipboard_change, txt = {}",
|
|
133
|
+
self.ctx.get_text().unwrap()
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#[napi]
|
|
139
|
+
pub fn watch() {
|
|
140
|
+
let manager = Manager::new();
|
|
141
|
+
|
|
142
|
+
let mut watcher = ClipboardWatcherContext::new().unwrap();
|
|
143
|
+
|
|
144
|
+
let watcher_shutdown = watcher.add_handler(manager).get_shutdown_channel();
|
|
145
|
+
|
|
146
|
+
thread::spawn(move || {
|
|
147
|
+
thread::sleep(Duration::from_secs(5));
|
|
148
|
+
println!("stop watch!");
|
|
149
|
+
watcher_shutdown.stop();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
println!("start watch!");
|
|
153
|
+
watcher.start_watch();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
#[napi]
|
|
158
|
+
pub fn call_threadsafe_function(callback: JsFunction) -> Result<()> {
|
|
159
|
+
let tsfn: ThreadsafeFunction<u32, ErrorStrategy::CalleeHandled> = callback
|
|
160
|
+
.create_threadsafe_function(0, |ctx| {
|
|
161
|
+
ctx.env.create_uint32(ctx.value + 1).map(|v| vec![v])
|
|
162
|
+
})?;
|
|
163
|
+
for n in 0..10 {
|
|
164
|
+
let tsfn = tsfn.clone();
|
|
165
|
+
thread::spawn(move || {
|
|
166
|
+
tsfn.call(Ok(n), ThreadsafeFunctionCallMode::Blocking);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
Ok(())
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// #[js_function(1)]
|
|
173
|
+
// fn hello(ctx: CallContext) -> Result<JsString, String> {
|
|
174
|
+
// let argument_one = ctx
|
|
175
|
+
// .get::<JsString>(0)
|
|
176
|
+
// .map_err(|err| err.to_string())?
|
|
177
|
+
// .into_utf8()
|
|
178
|
+
// .map_err(|err| err.to_string())?;
|
|
179
|
+
// ctx
|
|
180
|
+
// .env
|
|
181
|
+
// .create_string_from_std(format!("{} world!", argument_one.as_str()?))
|
|
182
|
+
// }
|
|
183
|
+
|
|
184
|
+
#[cfg(test)]
|
|
185
|
+
mod tests {
|
|
186
|
+
use super::*;
|
|
187
|
+
|
|
188
|
+
#[test]
|
|
189
|
+
fn it_works() {
|
|
190
|
+
watch();
|
|
191
|
+
}
|
|
192
|
+
}
|