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)?;