diff --git a/.changes/clipboard-html.md b/.changes/clipboard-html.md new file mode 100644 index 00000000..df19abfd --- /dev/null +++ b/.changes/clipboard-html.md @@ -0,0 +1,6 @@ +--- +"clipboard-manager": patch +"clipboard-manager-js": patch +--- + +Add support for writing HTML content to the clipboard. \ No newline at end of file diff --git a/plugins/clipboard-manager/README.md b/plugins/clipboard-manager/README.md index 02b26b3c..cd7f5216 100644 --- a/plugins/clipboard-manager/README.md +++ b/plugins/clipboard-manager/README.md @@ -60,7 +60,7 @@ fn main() { Afterwards all the plugin's APIs are available through the JavaScript guest bindings: ```javascript -import { writeText, readText } from "@tauri-apps/plugin-clipboard-manager"; +import { writeText, readText, writeHtml, readHtml, clear } from "@tauri-apps/plugin-clipboard-manager"; await writeText("Tauri is awesome!"); assert(await readText(), "Tauri is awesome!"); ``` diff --git a/plugins/clipboard-manager/guest-js/index.ts b/plugins/clipboard-manager/guest-js/index.ts index 4a9d10a0..c812ad53 100644 --- a/plugins/clipboard-manager/guest-js/index.ts +++ b/plugins/clipboard-manager/guest-js/index.ts @@ -53,4 +53,43 @@ async function readText(): Promise { return kind.plainText.text; } -export { writeText, readText }; +/** + * Writes HTML or fallbacks to write provided plain text to the clipboard. + * @example + * ```typescript + * import { writeHtml, readHtml } from '@tauri-apps/plugin-clipboard-manager'; + * await writeHtml('

Tauri is awesome!

', 'plaintext'); + * await writeHtml('

Tauri is awesome!

', '

Tauri is awesome

'); // Will write "

Tauri is awesome

" as plain text + * assert(await readText(), '

Tauri is awesome!

'); + * ``` + * + * @returns A promise indicating the success or failure of the operation. + * + * @since 2.0.0 + */ +async function writeHtml(html: string, altHtml?: string): Promise { + return invoke("plugin:clipboard-manager|write_html", { + data: { + html: { + html, + altHtml, + }, + }, + }); +} + +/** + * Clears the clipboard. + * @example + * ```typescript + * import { clear } from '@tauri-apps/plugin-clipboard-manager'; + * await clear(); + * ``` + * @since 2.0.0 + */ +async function clear(): Promise { + await invoke("plugin:clipboard-manager|clear"); + return; +} + +export { writeText, readText, writeHtml, clear }; diff --git a/plugins/clipboard-manager/src/api-iife.js b/plugins/clipboard-manager/src/api-iife.js index 8dc51655..bbf9cff6 100644 --- a/plugins/clipboard-manager/src/api-iife.js +++ b/plugins/clipboard-manager/src/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARDMANAGER__=function(e){"use strict";async function n(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}return"function"==typeof SuppressedError&&SuppressedError,e.readText=async function(){return(await n("plugin:clipboard-manager|read")).plainText.text},e.writeText=async function(e,r){return n("plugin:clipboard-manager|write",{data:{plainText:{label:r?.label,text:e}}})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARDMANAGER__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARDMANAGER__=function(n){"use strict";async function r(n,r={},a){return window.__TAURI_INTERNALS__.invoke(n,r,a)}return"function"==typeof SuppressedError&&SuppressedError,n.clear=async function(){await r("plugin:clipboard-manager|clear")},n.readText=async function(){return(await r("plugin:clipboard-manager|read")).plainText.text},n.writeHtml=async function(n,a){return r("plugin:clipboard-manager|write_html",{data:{html:{html:n,altHtml:a}}})},n.writeText=async function(n,a){return r("plugin:clipboard-manager|write",{data:{plainText:{label:a?.label,text:n}}})},n}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARDMANAGER__})} diff --git a/plugins/clipboard-manager/src/commands.rs b/plugins/clipboard-manager/src/commands.rs index eec868a8..d7fb0688 100644 --- a/plugins/clipboard-manager/src/commands.rs +++ b/plugins/clipboard-manager/src/commands.rs @@ -22,3 +22,20 @@ pub(crate) async fn read( ) -> Result { clipboard.read() } + +#[command] +pub(crate) async fn write_html( + _app: AppHandle, + clipboard: State<'_, Clipboard>, + data: ClipKind, +) -> Result<()> { + clipboard.write_html(data) +} + +#[command] +pub(crate) async fn clear( + _app: AppHandle, + clipboard: State<'_, Clipboard>, +) -> Result<()> { + clipboard.clear() +} diff --git a/plugins/clipboard-manager/src/desktop.rs b/plugins/clipboard-manager/src/desktop.rs index 555321dc..acf63c80 100644 --- a/plugins/clipboard-manager/src/desktop.rs +++ b/plugins/clipboard-manager/src/desktop.rs @@ -28,10 +28,12 @@ pub struct Clipboard { impl Clipboard { pub fn write(&self, kind: ClipKind) -> crate::Result<()> { - let ClipKind::PlainText { text, .. } = kind; - match &self.clipboard { - Ok(clipboard) => clipboard.lock().unwrap().set_text(text).map_err(Into::into), - Err(e) => Err(crate::Error::Clipboard(e.to_string())), + match kind { + ClipKind::PlainText { text, .. } => match &self.clipboard { + Ok(clipboard) => clipboard.lock().unwrap().set_text(text).map_err(Into::into), + Err(e) => Err(crate::Error::Clipboard(e.to_string())), + }, + _ => Err(crate::Error::Clipboard("Invalid clip kind!".to_string())), } } @@ -44,4 +46,25 @@ impl Clipboard { Err(e) => Err(crate::Error::Clipboard(e.to_string())), } } + + pub fn write_html(&self, kind: ClipKind) -> crate::Result<()> { + match kind { + ClipKind::Html { html, alt_html, .. } => match &self.clipboard { + Ok(clipboard) => clipboard + .lock() + .unwrap() + .set_html(html, alt_html) + .map_err(Into::into), + Err(e) => Err(crate::Error::Clipboard(e.to_string())), + }, + _ => Err(crate::Error::Clipboard("Invalid clip kind!".to_string())), + } + } + + pub fn clear(&self) -> crate::Result<()> { + match &self.clipboard { + Ok(clipboard) => clipboard.lock().unwrap().clear().map_err(Into::into), + Err(e) => Err(crate::Error::Clipboard(e.to_string())), + } + } } diff --git a/plugins/clipboard-manager/src/error.rs b/plugins/clipboard-manager/src/error.rs index bf71802f..03da585b 100644 --- a/plugins/clipboard-manager/src/error.rs +++ b/plugins/clipboard-manager/src/error.rs @@ -11,7 +11,6 @@ pub enum Error { #[cfg(mobile)] #[error(transparent)] PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), - #[cfg(desktop)] #[error("{0}")] Clipboard(String), } diff --git a/plugins/clipboard-manager/src/lib.rs b/plugins/clipboard-manager/src/lib.rs index 2b7934ab..bbfe5918 100644 --- a/plugins/clipboard-manager/src/lib.rs +++ b/plugins/clipboard-manager/src/lib.rs @@ -49,7 +49,14 @@ impl> crate::ClipboardExt for T { pub fn init() -> TauriPlugin { Builder::new("clipboard-manager") .js_init_script(include_str!("api-iife.js").to_string()) - .invoke_handler(tauri::generate_handler![commands::write, commands::read]) + .invoke_handler(tauri::generate_handler![ + commands::write, + commands::read, + #[cfg(desktop)] + commands::write_html, + #[cfg(desktop)] + commands::clear + ]) .setup(|app, api| { #[cfg(mobile)] let clipboard = mobile::init(app, api)?; diff --git a/plugins/clipboard-manager/src/mobile.rs b/plugins/clipboard-manager/src/mobile.rs index 07964711..8ba8c809 100644 --- a/plugins/clipboard-manager/src/mobile.rs +++ b/plugins/clipboard-manager/src/mobile.rs @@ -39,4 +39,17 @@ impl Clipboard { pub fn read(&self) -> crate::Result { self.0.run_mobile_plugin("read", ()).map_err(Into::into) } + + // Treat HTML as unsupported on mobile until tested + pub fn write_html(&self, _kind: ClipKind) -> crate::Result<()> { + Err(crate::Error::Clipboard( + "Unsupported on this platform".to_string(), + )) + } + + pub fn clear(&self) -> crate::Result<()> { + Err(crate::Error::Clipboard( + "Unsupported on this platform".to_string(), + )) + } } diff --git a/plugins/clipboard-manager/src/models.rs b/plugins/clipboard-manager/src/models.rs index a223a679..bb8c9b54 100644 --- a/plugins/clipboard-manager/src/models.rs +++ b/plugins/clipboard-manager/src/models.rs @@ -7,7 +7,14 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum ClipKind { - PlainText { label: Option, text: String }, + PlainText { + label: Option, + text: String, + }, + Html { + html: String, + alt_html: Option, + }, } #[derive(Debug, Serialize, Deserialize)]