Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 19 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/clipboard-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
```
Expand Down
46 changes: 45 additions & 1 deletion plugins/clipboard-manager/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import { invoke } from "@tauri-apps/api/core";

type ClipResponse = Record<"plainText", { text: string }>;
// Commented this type out as it can come in handy to stay
//type ClipHtmlResponse = Record<"html", { html: string, altHtml: string }>;
TukanDev marked this conversation as resolved.
Show resolved Hide resolved

/**
* Writes plain text to the clipboard.
Expand Down Expand Up @@ -53,4 +55,46 @@ 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 readHtml(), '<h1>Tauri is awesome!</h1>');
TukanDev marked this conversation as resolved.
Show resolved Hide resolved
* ```
*
* @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
},
},
});
}

/**
* Gets the clipboard content as HTML text.
* @example
* ```typescript
* import { readHtml } from '@tauri-apps/plugin-clipboard-manager';
* const clipboardHtml = await readHtml();
* ```
TukanDev marked this conversation as resolved.
Show resolved Hide resolved
* @since 2.0.0
*/
async function clear(): Promise<void> {
await invoke("plugin:clipboard-manager|clear");
return;
}

export { writeText, readText, writeHtml, clear };
17 changes: 17 additions & 0 deletions plugins/clipboard-manager/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
31 changes: 27 additions & 4 deletions plugins/clipboard-manager/src/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ 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())),
}
}

Expand All @@ -44,4 +48,23 @@ 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 changes: 10 additions & 1 deletion plugins/clipboard-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,16 @@ 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::read_html,
#[cfg(desktop)]
commands::clear
])
.setup(|app, api| {
#[cfg(mobile)]
let clipboard = mobile::init(app, api)?;
Expand Down
13 changes: 13 additions & 0 deletions plugins/clipboard-manager/src/mobile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) -> crate::Result<ClipboardContents> {
TukanDev marked this conversation as resolved.
Show resolved Hide resolved
Err(crate::Error::Clipboard(
"Unsupported on this platform".to_string(),
))
}

pub fn read_html(&self) -> crate::Result<ClipboardContents> {
Err(crate::Error::Clipboard(
"Unsupported on this platform".to_string(),
))
}
TukanDev marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 2 additions & 0 deletions plugins/clipboard-manager/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ use serde::{Deserialize, Serialize};
#[serde(rename_all = "camelCase")]
pub enum ClipKind {
PlainText { label: Option<String>, text: String },
Html { html: String, alt_html: Option<String> }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ClipboardContents {
PlainText { text: String },
Html { html: String, alt_html: Option<String> }
TukanDev marked this conversation as resolved.
Show resolved Hide resolved
}