mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-10-18 23:07:27 +02:00
feat(clipboard-manager): add support for writing secrets (#3376)
* feat(clipboard-manager): add support for writing secrets add `write_secret` command that writes a text into the clipboard and hints clipboard managers to not save the text. uses `arboard`'s `SetExtLinux`, `SetExtWindows`, and `SetExtApple` extensions for each platform. * fix(clipboard-manager): require arboard 3.6 * chore(clipboard-manager): add license header to secret.rs * fix(clipboard-manager): only exclude from monitoring on Windows * refactor(clipboard-manager): replace ExcludeSecret trait with a function * chore(clipboard-manager): drop redundant cfg(desktop) in desktop.rs * refactor(clipboard-manager): share clipboard access in a helper * feat(clipboard-manager): implement write_secret on Android and iOS * docs(clipboard-manager): document writeSecret * chore(clipboard-manager): add change file for writeSecret * test(clipboard-manager): cover writeSecret in e2e --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
1 parent
1ab930465f
commit
57297f3fb1
21 files changed
+247
-72
No files matched your search
@@ -0,0 +1,6 @@
|
||||
---
|
||||
clipboard-manager: minor
|
||||
clipboard-manager-js: minor
|
||||
---
|
||||
|
||||
Added `writeSecret` (`Clipboard::write_secret` in Rust) to write sensitive text, such as passwords, to the clipboard. Clipboard managers are asked to keep it out of their history on Windows, macOS and Linux. On Android the clip is flagged as sensitive, and on iOS it is not synced to other devices.
|
||||
Generated
+3
-3
@@ -297,9 +297,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.5.0"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70"
|
||||
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"image",
|
||||
@@ -311,7 +311,7 @@ dependencies = [
|
||||
"objc2-foundation 0.3.2",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
"clipboard-manager:allow-write-image",
|
||||
"clipboard-manager:allow-write-html",
|
||||
"clipboard-manager:allow-clear",
|
||||
"clipboard-manager:allow-write-secret",
|
||||
"fs:default",
|
||||
"fs:read-meta",
|
||||
"fs:allow-open",
|
||||
|
||||
@@ -35,6 +35,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
function writeSecret() {
|
||||
clipboard.writeSecret(text).then(() => {
|
||||
onMessage("Wrote secret to the clipboard")
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
async function read() {
|
||||
try {
|
||||
const image = await clipboard.readImage()
|
||||
@@ -63,6 +70,7 @@
|
||||
bind:value={text}
|
||||
/>
|
||||
<button class="btn" type="button" on:click={writeText}>Write</button>
|
||||
<button class="btn" type="button" on:click={writeSecret}>Write Secret</button>
|
||||
<button class="btn" type="button" on:click={writeImage}>Pick Image</button>
|
||||
<button class="btn" type="button" on:click={read}>Read</button>
|
||||
</div>
|
||||
@@ -68,7 +68,7 @@ skipped on desktop, and the rest run everywhere with the odd test gated.
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| `autostart` | enable/disable/isEnabled, restoring the host's original state. | Skipped — desktop-only plugin. |
|
||||
| `cli` | `getMatches` shape for an argument-less launch. | Skipped — desktop-only plugin. |
|
||||
| `clipboard-manager` | text, HTML and image round-trips, `clear`, error paths. | Text only; HTML and images are unsupported there. |
|
||||
| `clipboard-manager` | text, secret, HTML and image round-trips, `clear`, error paths. | Text only; HTML and images are unsupported there. |
|
||||
| `deep-link` | `getCurrent` on a plain launch, `onOpenUrl` delivery, runtime `register`/`isRegistered`/`unregister` on Linux and Windows. | Only that runtime registration is unsupported. |
|
||||
| `dialog` | Only that the API is registered — every dialog blocks on native UI the driver cannot operate. | Same. |
|
||||
| `fs` | read/write/stat/copy/rename/remove/truncate, `FileHandle`, line iteration, watchers, scope. | All but the watchers (`fs:allow-watch` is desktop-only here). |
|
||||
|
||||
@@ -34,6 +34,18 @@ describePlugin('clipboard-manager', () => {
|
||||
).toBe('second')
|
||||
})
|
||||
|
||||
it('writeSecret writes text the app can read back', async () => {
|
||||
// the history/sync exclusion hints aren't observable from the app itself
|
||||
const text = 'secret from e2e — ✓'
|
||||
expect(
|
||||
await tauri(async (api, text) => {
|
||||
await api.clipboardManager.writeText('not the secret')
|
||||
await api.clipboardManager.writeSecret(text)
|
||||
return api.clipboardManager.readText()
|
||||
}, text)
|
||||
).toBe(text)
|
||||
})
|
||||
|
||||
itDesktop('writeHtml exposes the alt text as plain text', async () => {
|
||||
expect(
|
||||
await tauri(async (api) => {
|
||||
|
||||
@@ -30,7 +30,8 @@ const commonSurface: Surface<CommonPluginApi> = {
|
||||
'writeHtml',
|
||||
'clear',
|
||||
'readImage',
|
||||
'writeImage'
|
||||
'writeImage',
|
||||
'writeSecret'
|
||||
],
|
||||
deepLink: [
|
||||
'getCurrent',
|
||||
|
||||
@@ -42,4 +42,4 @@ thiserror = { workspace = true }
|
||||
tauri = { workspace = true, features = ["wry"] }
|
||||
|
||||
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
arboard = { version = "3", features = ["wayland-data-control"] }
|
||||
arboard = { version = "3.6", features = ["wayland-data-control"] }
|
||||
@@ -10,6 +10,7 @@ import android.content.ClipDescription
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.PersistableBundle
|
||||
import app.tauri.annotation.Command
|
||||
import app.tauri.annotation.InvokeArg
|
||||
import app.tauri.annotation.TauriPlugin
|
||||
@@ -37,6 +38,11 @@ sealed class WriteOptions {
|
||||
}
|
||||
}
|
||||
|
||||
@InvokeArg
|
||||
class WriteSecretArgs {
|
||||
lateinit var text: String
|
||||
}
|
||||
|
||||
@JsonSerialize(using = ReadClipDataSerializer::class)
|
||||
sealed class ReadClipData {
|
||||
class PlainText: ReadClipData() {
|
||||
@@ -107,6 +113,25 @@ class ClipboardPlugin(private val activity: Activity) : Plugin(activity) {
|
||||
invoke.resolve()
|
||||
}
|
||||
|
||||
@Command
|
||||
fun writeSecret(invoke: Invoke) {
|
||||
val args = invoke.parseArgs(WriteSecretArgs::class.java)
|
||||
|
||||
val clipData = ClipData.newPlainText(null, args.text)
|
||||
val isSensitiveKey = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ClipDescription.EXTRA_IS_SENSITIVE
|
||||
} else {
|
||||
"android.content.extra.IS_SENSITIVE"
|
||||
}
|
||||
clipData.description.extras = PersistableBundle().apply {
|
||||
putBoolean(isSensitiveKey, true)
|
||||
}
|
||||
|
||||
manager.setPrimaryClip(clipData)
|
||||
|
||||
invoke.resolve()
|
||||
}
|
||||
|
||||
@Command
|
||||
fun readText(invoke: Invoke) {
|
||||
val data = if (manager.hasPrimaryClip()) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(a,e,n){"use strict";return a.clear=async function(){await e.invoke("plugin:clipboard-manager|clear")},a.readImage=async function(){return await e.invoke("plugin:clipboard-manager|read_image").then(a=>new n.Image(a))},a.readText=async function(){return await e.invoke("plugin:clipboard-manager|read_text")},a.writeHtml=async function(a,n){await e.invoke("plugin:clipboard-manager|write_html",{html:a,altText:n})},a.writeImage=async function(a){await e.invoke("plugin:clipboard-manager|write_image",{image:n.transformImage(a)})},a.writeText=async function(a,n){await e.invoke("plugin:clipboard-manager|write_text",{label:n?.label,text:a})},a}({},window.__TAURI__.core,window.__TAURI__.image);Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
|
||||
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(a,e,i){"use strict";return a.clear=async function(){await e.invoke("plugin:clipboard-manager|clear")},a.readImage=async function(){return await e.invoke("plugin:clipboard-manager|read_image").then(a=>new i.Image(a))},a.readText=async function(){return await e.invoke("plugin:clipboard-manager|read_text")},a.writeHtml=async function(a,i){await e.invoke("plugin:clipboard-manager|write_html",{html:a,altText:i})},a.writeImage=async function(a){await e.invoke("plugin:clipboard-manager|write_image",{image:i.transformImage(a)})},a.writeSecret=async function(a){await e.invoke("plugin:clipboard-manager|write_secret",{text:a})},a.writeText=async function(a,i){await e.invoke("plugin:clipboard-manager|write_text",{label:i?.label,text:a})},a}({},window.__TAURI__.core,window.__TAURI__.image);Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
|
||||
@@ -9,6 +9,7 @@ const COMMANDS: &[&str] = &[
|
||||
"read_image",
|
||||
"write_html",
|
||||
"clear",
|
||||
"write_secret",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -156,4 +156,35 @@ async function clear(): Promise<void> {
|
||||
await invoke('plugin:clipboard-manager|clear')
|
||||
}
|
||||
|
||||
export { writeText, readText, writeHtml, clear, readImage, writeImage }
|
||||
/**
|
||||
* Writes sensitive plain text, such as a password, to the clipboard.
|
||||
*
|
||||
* The text is marked so the system doesn't keep or share it: on Windows it is excluded from
|
||||
* clipboard history and cloud sync, on macOS and Linux clipboard managers are asked to skip it,
|
||||
* on Android it is flagged as sensitive so the clipboard preview hides it,
|
||||
* and on iOS it is not synced to other devices.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { writeSecret } from '@tauri-apps/plugin-clipboard-manager';
|
||||
* await writeSecret('my-password');
|
||||
* ```
|
||||
*
|
||||
* @param text The plain text to write to the clipboard.
|
||||
* @returns A promise indicating the success or failure of the operation.
|
||||
*
|
||||
* @since 2.5.0
|
||||
*/
|
||||
async function writeSecret(text: string): Promise<void> {
|
||||
await invoke('plugin:clipboard-manager|write_secret', { text })
|
||||
}
|
||||
|
||||
export {
|
||||
writeText,
|
||||
readText,
|
||||
writeHtml,
|
||||
clear,
|
||||
readImage,
|
||||
writeImage,
|
||||
writeSecret
|
||||
}
|
||||
@@ -11,6 +11,10 @@ enum WriteOptions: Codable {
|
||||
case plainText(text: String)
|
||||
}
|
||||
|
||||
struct WriteSecretArgs: Decodable {
|
||||
let text: String
|
||||
}
|
||||
|
||||
enum ReadClipData: Codable {
|
||||
case plainText(text: String)
|
||||
}
|
||||
@@ -30,6 +34,15 @@ class ClipboardPlugin: Plugin {
|
||||
|
||||
}
|
||||
|
||||
@objc public func writeSecret(_ invoke: Invoke) throws {
|
||||
let args = try invoke.parseArgs(WriteSecretArgs.self)
|
||||
UIPasteboard.general.setItems(
|
||||
[["public.utf8-plain-text": args.text]],
|
||||
options: [.localOnly: true]
|
||||
)
|
||||
invoke.resolve()
|
||||
}
|
||||
|
||||
@objc public func readText(_ invoke: Invoke) throws {
|
||||
let clipboard = UIPasteboard.general
|
||||
if let text = clipboard.string {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-write-secret"
|
||||
description = "Enables the write_secret command without any pre-configured scope."
|
||||
commands.allow = ["write_secret"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-write-secret"
|
||||
description = "Denies the write_secret command without any pre-configured scope."
|
||||
commands.deny = ["write_secret"]
|
||||
@@ -148,6 +148,32 @@ Denies the write_image command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`clipboard-manager:allow-write-secret`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the write_secret command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`clipboard-manager:deny-write-secret`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the write_secret command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`clipboard-manager:allow-write-text`
|
||||
|
||||
</td>
|
||||
|
||||
@@ -354,6 +354,18 @@
|
||||
"const": "deny-write-image",
|
||||
"markdownDescription": "Denies the write_image command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the write_secret command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-write-secret",
|
||||
"markdownDescription": "Enables the write_secret command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the write_secret command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-write-secret",
|
||||
"markdownDescription": "Denies the write_secret command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the write_text command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
@@ -78,3 +78,12 @@ pub(crate) async fn clear<R: Runtime>(
|
||||
) -> Result<()> {
|
||||
clipboard.clear()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn write_secret<R: Runtime>(
|
||||
_app: AppHandle<R>,
|
||||
clipboard: State<'_, Clipboard<R>>,
|
||||
text: &str,
|
||||
) -> Result<()> {
|
||||
clipboard.write_secret(text)
|
||||
}
|
||||
@@ -28,6 +28,16 @@ pub struct Clipboard<R: Runtime> {
|
||||
}
|
||||
|
||||
impl<R: Runtime> Clipboard<R> {
|
||||
fn with_clipboard<T>(
|
||||
&self,
|
||||
f: impl FnOnce(&mut arboard::Clipboard) -> Result<T, arboard::Error>,
|
||||
) -> crate::Result<T> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => f(clipboard.lock().unwrap().as_mut().unwrap()).map_err(Into::into),
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes plain text to the system clipboard.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -35,16 +45,7 @@ impl<R: Runtime> Clipboard<R> {
|
||||
/// Returns [`crate::Error::Clipboard`] if the clipboard could not be initialized or the
|
||||
/// underlying [`arboard`] operation fails.
|
||||
pub fn write_text<'a, T: Into<Cow<'a, str>>>(&self, text: T) -> crate::Result<()> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => clipboard
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_text(text)
|
||||
.map_err(Into::into),
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
self.with_clipboard(|c| c.set_text(text))
|
||||
}
|
||||
|
||||
/// Writes an image to the system clipboard as RGBA data.
|
||||
@@ -54,31 +55,18 @@ impl<R: Runtime> Clipboard<R> {
|
||||
/// Returns [`crate::Error::Clipboard`] if the clipboard could not be initialized or the
|
||||
/// underlying [`arboard`] operation fails.
|
||||
pub fn write_image(&self, image: &Image<'_>) -> crate::Result<()> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => clipboard
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_image(ImageData {
|
||||
bytes: Cow::Borrowed(image.rgba()),
|
||||
width: image.width() as usize,
|
||||
height: image.height() as usize,
|
||||
})
|
||||
.map_err(Into::into),
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
self.with_clipboard(|c| {
|
||||
c.set_image(ImageData {
|
||||
bytes: Cow::Borrowed(image.rgba()),
|
||||
width: image.width() as usize,
|
||||
height: image.height() as usize,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
|
||||
pub fn read_text(&self) -> crate::Result<String> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => {
|
||||
let text = clipboard.lock().unwrap().as_mut().unwrap().get_text()?;
|
||||
Ok(text)
|
||||
}
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
self.with_clipboard(|c| c.get_text())
|
||||
}
|
||||
|
||||
/// Writes HTML to the system clipboard, with an optional plain text fallback for targets
|
||||
@@ -93,16 +81,7 @@ impl<R: Runtime> Clipboard<R> {
|
||||
html: T,
|
||||
alt_text: Option<T>,
|
||||
) -> crate::Result<()> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => clipboard
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_html(html, alt_text)
|
||||
.map_err(Into::into),
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
self.with_clipboard(|c| c.set_html(html, alt_text))
|
||||
}
|
||||
|
||||
/// Clears the system clipboard.
|
||||
@@ -112,32 +91,17 @@ impl<R: Runtime> Clipboard<R> {
|
||||
/// Returns [`crate::Error::Clipboard`] if the clipboard could not be initialized or the
|
||||
/// underlying [`arboard`] operation fails.
|
||||
pub fn clear(&self) -> crate::Result<()> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => clipboard
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.clear()
|
||||
.map_err(Into::into),
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
self.with_clipboard(|c| c.clear())
|
||||
}
|
||||
|
||||
/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
|
||||
pub fn read_image(&self) -> crate::Result<Image<'_>> {
|
||||
match &self.clipboard {
|
||||
Ok(clipboard) => {
|
||||
let image = clipboard.lock().unwrap().as_mut().unwrap().get_image()?;
|
||||
let image = Image::new_owned(
|
||||
image.bytes.to_vec(),
|
||||
image.width as u32,
|
||||
image.height as u32,
|
||||
);
|
||||
Ok(image)
|
||||
}
|
||||
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
|
||||
}
|
||||
let image = self.with_clipboard(|c| c.get_image())?;
|
||||
Ok(Image::new_owned(
|
||||
image.bytes.to_vec(),
|
||||
image.width as u32,
|
||||
image.height as u32,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn cleanup(&self) {
|
||||
@@ -145,4 +109,9 @@ impl<R: Runtime> Clipboard<R> {
|
||||
clipboard.lock().unwrap().take();
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the same as write_text but it will set hints using `arboard`'s `SetExt*` extensions so clipboard managers exclude the text from history.
|
||||
pub fn write_secret<'a, T: Into<Cow<'a, str>>>(&self, text: T) -> crate::Result<()> {
|
||||
self.with_clipboard(|c| crate::secret::exclude_secret(c.set()).text(text))
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ mod mobile;
|
||||
mod commands;
|
||||
mod error;
|
||||
|
||||
#[cfg(desktop)]
|
||||
mod secret;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
|
||||
#[cfg(desktop)]
|
||||
@@ -50,7 +53,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
commands::read_image,
|
||||
commands::write_image,
|
||||
commands::write_html,
|
||||
commands::clear
|
||||
commands::clear,
|
||||
commands::write_secret,
|
||||
])
|
||||
.setup(|app, api| {
|
||||
#[cfg(mobile)]
|
||||
|
||||
@@ -123,6 +123,22 @@ impl<R: Runtime> Clipboard<R> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Writes plain text to the system clipboard, marked as sensitive.
|
||||
///
|
||||
/// On Android the clip is flagged with `ClipDescription.EXTRA_IS_SENSITIVE`, which hides it
|
||||
/// from the clipboard preview. On iOS the text is written as local only, so it is not
|
||||
/// synced to other devices through Universal Clipboard.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`crate::Error::PluginInvoke`] if the underlying mobile plugin call fails.
|
||||
pub fn write_secret<'a, T: Into<Cow<'a, str>>>(&self, text: T) -> crate::Result<()> {
|
||||
let text = text.into().to_string();
|
||||
self.0
|
||||
.run_mobile_plugin("writeSecret", WriteSecretArgs { text })
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Clears the system clipboard.
|
||||
///
|
||||
/// On Android this only works on SDK 28 and above; on older versions the clipboard is
|
||||
@@ -142,6 +158,11 @@ enum ClipKind {
|
||||
PlainText { label: Option<String>, text: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct WriteSecretArgs {
|
||||
text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum ClipboardContents {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(windows)]
|
||||
use arboard::SetExtWindows;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use arboard::SetExtApple;
|
||||
|
||||
#[cfg(not(any(windows, target_os = "macos")))]
|
||||
use arboard::SetExtLinux;
|
||||
|
||||
/// Hints clipboard managers to exclude the data from history.
|
||||
///
|
||||
/// On Windows, it calls [`arboard::SetExtWindows::exclude_from_monitoring`], which also covers history and cloud sync.
|
||||
/// On macOS and Linux, it calls `exclude_from_history`.
|
||||
pub(crate) fn exclude_secret(set: arboard::Set<'_>) -> arboard::Set<'_> {
|
||||
#[cfg(windows)]
|
||||
return set.exclude_from_monitoring();
|
||||
#[cfg(not(windows))]
|
||||
return set.exclude_from_history();
|
||||
}
|
||||
Reference in new issue
Block a user