feat(clipboard): Implement HTML and clear functionality (#977)

* Implement arboard HTML features (desktop only) and ability to trigger clipboard clear

Signed-off-by: TukanDev <contact@tukandev.com>

* Update readme of clipboard plugin

Signed-off-by: TukanDev <contact@tukandev.com>

* Update plugins/clipboard-manager/src/desktop.rs

Propagate error for clear as requested

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* Update plugins/clipboard-manager/guest-js/index.ts

Change to camelCase as requested

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* Update plugins/clipboard-manager/guest-js/index.ts

use camelCase here too

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* Update README.md

Revert back as mentioned in HTML PR

* Update index.ts

After discussion  readHtml() is decided to be removed. Will follow and remove corresponding rust side function too.

* Strip all other existence of read_html out as determined in HTML support PR conversation

Signed-off-by: TukanDev <contact@tukandev.com>

* Apply requested changes v2

Signed-off-by: TukanDev <contact@tukandev.com>

* pnpm run build and cargo fmt

Signed-off-by: TukanDev <contact@tukandev.com>

* Update plugins/clipboard-manager/src/mobile.rs

fix ci calling

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* mobile read_html omg....

Signed-off-by: TukanDev <contact@tukandev.com>

* Update plugins/clipboard-manager/src/mobile.rs

* pnpm format....

Signed-off-by: TukanDev <contact@tukandev.com>

* error on mobile as well

* clear on mobile

* change file

---------

Signed-off-by: TukanDev <contact@tukandev.com>
pull/950/merge
TukanDev 1 year ago committed by GitHub
parent aa25c91bb0
commit dc6d3321e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,6 @@
---
"clipboard-manager": patch
"clipboard-manager-js": patch
---
Add support for writing HTML content to the clipboard.

@ -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!");
```

@ -53,4 +53,43 @@ async function readText(): Promise<string> {
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('<h1>Tauri is awesome!</h1>', 'plaintext');
* await writeHtml('<h1>Tauri is awesome!</h1>', '<h1>Tauri is awesome</h1>'); // Will write "<h1>Tauri is awesome</h1>" as plain text
* assert(await readText(), '<h1>Tauri is awesome!</h1>');
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function writeHtml(html: string, altHtml?: string): Promise<void> {
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<void> {
await invoke("plugin:clipboard-manager|clear");
return;
}
export { writeText, readText, writeHtml, clear };

@ -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__})}

@ -22,3 +22,20 @@ pub(crate) async fn read<R: Runtime>(
) -> Result<ClipboardContents> {
clipboard.read()
}
#[command]
pub(crate) async fn write_html<R: Runtime>(
_app: AppHandle<R>,
clipboard: State<'_, Clipboard<R>>,
data: ClipKind,
) -> Result<()> {
clipboard.write_html(data)
}
#[command]
pub(crate) async fn clear<R: Runtime>(
_app: AppHandle<R>,
clipboard: State<'_, Clipboard<R>>,
) -> Result<()> {
clipboard.clear()
}

@ -28,10 +28,12 @@ pub struct Clipboard<R: Runtime> {
impl<R: Runtime> Clipboard<R> {
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<R: Runtime> Clipboard<R> {
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())),
}
}
}

@ -11,7 +11,6 @@ pub enum Error {
#[cfg(mobile)]
#[error(transparent)]
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
#[cfg(desktop)]
#[error("{0}")]
Clipboard(String),
}

@ -49,7 +49,14 @@ impl<R: Runtime, T: Manager<R>> crate::ClipboardExt<R> for T {
pub fn init<R: Runtime>() -> TauriPlugin<R> {
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)?;

@ -39,4 +39,17 @@ impl<R: Runtime> Clipboard<R> {
pub fn read(&self) -> crate::Result<ClipboardContents> {
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(),
))
}
}

@ -7,7 +7,14 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ClipKind {
PlainText { label: Option<String>, text: String },
PlainText {
label: Option<String>,
text: String,
},
Html {
html: String,
alt_html: Option<String>,
},
}
#[derive(Debug, Serialize, Deserialize)]

Loading…
Cancel
Save