diff --git a/__test__/index.spec.ts b/__test__/index.spec.ts
index 3148405..56f951b 100644
--- a/__test__/index.spec.ts
+++ b/__test__/index.spec.ts
@@ -11,6 +11,7 @@ import {
hasRtf,
hasText,
setBuffer,
+ setContents,
setHtml,
setText,
startWatch,
@@ -68,6 +69,30 @@ test.serial('setBuffer / getBuffer round-trips with custom format', async (t) =>
t.deepEqual(Array.from(result), data)
})
+// ── Multiple formats ─────────────────────────────────────────────────
+
+test.serial('setContents writes multiple clipboard representations at once', async (t) => {
+ const format = 'com.ava.test-set-contents'
+ const data = [0x48, 0x65, 0x6c, 0x6c, 0x6f]
+
+ await setContents({
+ text: 'hello',
+ html: '
hello
',
+ rtf: '{\\rtf1\\ansi hello}',
+ buffers: [{ format, buffer: data }],
+ })
+
+ t.is(await getText(), 'hello')
+ t.true(hasHtml())
+ t.true((await getHtml()).includes('hello'))
+ t.true(hasRtf())
+ t.deepEqual(Array.from(await getBuffer(format)), data)
+})
+
+test.serial('setContents rejects empty content', async (t) => {
+ await t.throwsAsync(() => setContents({}), { message: /contents is empty/ })
+})
+
// ── Clear ────────────────────────────────────────────────────────────
test.serial('clear removes clipboard content', async (t) => {
diff --git a/index.d.ts b/index.d.ts
index c514fa1..06e52d0 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -9,6 +9,18 @@ export declare function availableFormats(): Array
export declare function clear(): Promise
+export interface ClipboardBufferContent {
+ format: string
+ buffer: Array
+}
+
+export interface ClipboardContents {
+ text?: string
+ html?: string
+ rtf?: string
+ buffers?: Array
+}
+
export declare function getBuffer(format: string): Promise>
export declare function getFiles(): Promise>
@@ -35,6 +47,9 @@ export declare function hasText(): boolean
export declare function setBuffer(format: string, buffer: Array): Promise
+/** Write multiple representations of the same clipboard content in a single operation. */
+export declare function setContents(contents: ClipboardContents): Promise
+
export declare function setFiles(files: Array): Promise
export declare function setHtml(html: string): Promise
diff --git a/index.js b/index.js
index fd95a3a..0cba697 100644
--- a/index.js
+++ b/index.js
@@ -592,6 +592,7 @@ module.exports.hasImage = nativeBinding.hasImage
module.exports.hasRtf = nativeBinding.hasRtf
module.exports.hasText = nativeBinding.hasText
module.exports.setBuffer = nativeBinding.setBuffer
+module.exports.setContents = nativeBinding.setContents
module.exports.setFiles = nativeBinding.setFiles
module.exports.setHtml = nativeBinding.setHtml
module.exports.setImageBase64 = nativeBinding.setImageBase64
diff --git a/src/lib.rs b/src/lib.rs
index 667152c..8eda540 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,8 @@
#![deny(clippy::all)]
use base64::{engine::general_purpose, Engine as _};
use clipboard_rs::{
- common::RustImage, Clipboard, ClipboardContext, ClipboardHandler, ClipboardWatcher,
- ClipboardWatcherContext, ContentFormat, RustImageData, WatcherShutdown,
+ common::RustImage, Clipboard, ClipboardContent, ClipboardContext, ClipboardHandler,
+ ClipboardWatcher, ClipboardWatcherContext, ContentFormat, RustImageData, WatcherShutdown,
};
use napi::bindgen_prelude::*;
use napi::threadsafe_function::{ThreadsafeCallContext, ThreadsafeFunctionCallMode};
@@ -139,6 +139,53 @@ pub async fn set_buffer(format: String, buffer: Vec) -> Result<()> {
ctx.set_buffer(&format, buffer).map_err(cb_err)
}
+#[napi(object)]
+pub struct ClipboardBufferContent {
+ pub format: String,
+ pub buffer: Vec,
+}
+
+#[napi(object)]
+pub struct ClipboardContents {
+ pub text: Option,
+ pub html: Option,
+ pub rtf: Option,
+ pub buffers: Option>,
+}
+
+/// Write multiple representations of the same clipboard content in a single operation.
+#[napi]
+pub async fn set_contents(contents: ClipboardContents) -> Result<()> {
+ let ctx = ClipboardContext::new().map_err(cb_err)?;
+ let mut clipboard_contents = Vec::new();
+
+ if let Some(text) = contents.text {
+ clipboard_contents.push(ClipboardContent::Text(text));
+ }
+ if let Some(html) = contents.html {
+ clipboard_contents.push(ClipboardContent::Html(html));
+ }
+ if let Some(rtf) = contents.rtf {
+ clipboard_contents.push(ClipboardContent::Rtf(rtf));
+ }
+ if let Some(buffers) = contents.buffers {
+ clipboard_contents.extend(
+ buffers
+ .into_iter()
+ .map(|entry| ClipboardContent::Other(entry.format, entry.buffer)),
+ );
+ }
+
+ if clipboard_contents.is_empty() {
+ return Err(Error::new(
+ Status::InvalidArg,
+ "contents is empty; use clear() to clear the clipboard",
+ ));
+ }
+
+ ctx.set(clipboard_contents).map_err(cb_err)
+}
+
#[napi]
pub async fn clear() -> Result<()> {
let ctx = ClipboardContext::new().map_err(cb_err)?;