mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-04-28 21:39:46 +00:00
Migrated the clipboard function from Tauri JS to the runtime API using encryption
This commit is contained in:
parent
79afd42195
commit
1d12facd58
@ -165,7 +165,7 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
|
|
||||||
protected async Task CopyToClipboard()
|
protected async Task CopyToClipboard()
|
||||||
{
|
{
|
||||||
await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, this.Result2Copy());
|
await this.Rust.CopyText2Clipboard(this.Snackbar, this.Result2Copy());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? GetButtonIcon(string icon)
|
private static string? GetButtonIcon(string icon)
|
||||||
|
@ -100,7 +100,7 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
{
|
{
|
||||||
case ContentType.TEXT:
|
case ContentType.TEXT:
|
||||||
var textContent = (ContentText) this.Content;
|
var textContent = (ContentText) this.Content;
|
||||||
await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, textContent.Text);
|
await this.Rust.CopyText2Clipboard(this.Snackbar, textContent.Text);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -78,36 +78,44 @@ public sealed class Rust(string apiPort) : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to copy the given text to the clipboard.
|
/// Tries to copy the given text to the clipboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
|
|
||||||
/// <param name="snackbar">The snackbar to show the result.</param>
|
/// <param name="snackbar">The snackbar to show the result.</param>
|
||||||
/// <param name="text">The text to copy to the clipboard.</param>
|
/// <param name="text">The text to copy to the clipboard.</param>
|
||||||
public async Task CopyText2Clipboard(IJSRuntime jsRuntime, ISnackbar snackbar, string text)
|
public async Task CopyText2Clipboard(ISnackbar snackbar, string text)
|
||||||
{
|
{
|
||||||
var response = await jsRuntime.InvokeAsync<SetClipboardResponse>("window.__TAURI__.invoke", "set_clipboard", new SetClipboardText(text));
|
var message = "Successfully copied the text to your clipboard";
|
||||||
var msg = response.Success switch
|
var iconColor = Color.Error;
|
||||||
|
var severity = Severity.Error;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
true => "Successfully copied text to clipboard!",
|
var response = await this.http.PostAsync("/clipboard/set", new StringContent(await text.Encrypt(this.encryptor!)));
|
||||||
false => $"Failed to copy text to clipboard: {response.Issue}",
|
if (!response.IsSuccessStatusCode)
|
||||||
};
|
|
||||||
|
|
||||||
var severity = response.Success switch
|
|
||||||
{
|
|
||||||
true => Severity.Success,
|
|
||||||
false => Severity.Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
snackbar.Add(msg, severity, config =>
|
|
||||||
{
|
|
||||||
config.Icon = Icons.Material.Filled.ContentCopy;
|
|
||||||
config.IconSize = Size.Large;
|
|
||||||
config.IconColor = severity switch
|
|
||||||
{
|
{
|
||||||
Severity.Success => Color.Success,
|
this.logger!.LogError($"Failed to copy the text to the clipboard due to an network error: '{response.StatusCode}'");
|
||||||
Severity.Error => Color.Error,
|
message = "Failed to copy the text to your clipboard.";
|
||||||
|
return;
|
||||||
_ => Color.Default,
|
}
|
||||||
};
|
|
||||||
});
|
var state = await response.Content.ReadFromJsonAsync<SetClipboardResponse>();
|
||||||
|
if (!state.Success)
|
||||||
|
{
|
||||||
|
this.logger!.LogError("Failed to copy the text to the clipboard.");
|
||||||
|
message = "Failed to copy the text to your clipboard.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconColor = Color.Success;
|
||||||
|
severity = Severity.Success;
|
||||||
|
this.logger!.LogDebug("Successfully copied the text to the clipboard.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
snackbar.Add(message, severity, config =>
|
||||||
|
{
|
||||||
|
config.Icon = Icons.Material.Filled.ContentCopy;
|
||||||
|
config.IconSize = Size.Large;
|
||||||
|
config.IconColor = iconColor;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UpdateResponse> CheckForUpdate(IJSRuntime jsRuntime)
|
public async Task<UpdateResponse> CheckForUpdate(IJSRuntime jsRuntime)
|
||||||
|
@ -6,10 +6,8 @@ namespace AIStudio.Tools.Services;
|
|||||||
/// Wire up the clipboard service to copy Markdown to the clipboard.
|
/// Wire up the clipboard service to copy Markdown to the clipboard.
|
||||||
/// We use our own Rust-based clipboard service for this.
|
/// We use our own Rust-based clipboard service for this.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MarkdownClipboardService(Rust rust, IJSRuntime jsRuntime, ISnackbar snackbar) : IMudMarkdownClipboardService
|
public sealed class MarkdownClipboardService(Rust rust, ISnackbar snackbar) : IMudMarkdownClipboardService
|
||||||
{
|
{
|
||||||
private IJSRuntime JsRuntime { get; } = jsRuntime;
|
|
||||||
|
|
||||||
private ISnackbar Snackbar { get; } = snackbar;
|
private ISnackbar Snackbar { get; } = snackbar;
|
||||||
|
|
||||||
private Rust Rust { get; } = rust;
|
private Rust Rust { get; } = rust;
|
||||||
@ -18,5 +16,5 @@ public sealed class MarkdownClipboardService(Rust rust, IJSRuntime jsRuntime, IS
|
|||||||
/// Gets called when the user wants to copy the Markdown to the clipboard.
|
/// Gets called when the user wants to copy the Markdown to the clipboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The Markdown text to copy.</param>
|
/// <param name="text">The Markdown text to copy.</param>
|
||||||
public async ValueTask CopyToClipboardAsync(string text) => await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, text);
|
public async ValueTask CopyToClipboardAsync(string text) => await this.Rust.CopyText2Clipboard(this.Snackbar, text);
|
||||||
}
|
}
|
@ -194,7 +194,7 @@ async fn main() {
|
|||||||
//
|
//
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
_ = rocket::custom(figment)
|
_ = rocket::custom(figment)
|
||||||
.mount("/", routes![dotnet_port, dotnet_ready])
|
.mount("/", routes![dotnet_port, dotnet_ready, set_clipboard])
|
||||||
.ignite().await.unwrap()
|
.ignite().await.unwrap()
|
||||||
.launch().await.unwrap();
|
.launch().await.unwrap();
|
||||||
});
|
});
|
||||||
@ -312,7 +312,7 @@ async fn main() {
|
|||||||
})
|
})
|
||||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
store_secret, get_secret, delete_secret, set_clipboard,
|
store_secret, get_secret, delete_secret,
|
||||||
check_for_update, install_update
|
check_for_update, install_update
|
||||||
])
|
])
|
||||||
.build(tauri::generate_context!())
|
.build(tauri::generate_context!())
|
||||||
@ -848,36 +848,49 @@ struct DeleteSecretResponse {
|
|||||||
issue: String,
|
issue: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[post("/clipboard/set", data = "<encrypted_text>")]
|
||||||
fn set_clipboard(text: String) -> SetClipboardResponse {
|
fn set_clipboard(encrypted_text: EncryptedText) -> Json<SetClipboardResponse> {
|
||||||
|
|
||||||
|
// Decrypt this text first:
|
||||||
|
let decrypted_text = match ENCRYPTION.decrypt(&encrypted_text) {
|
||||||
|
Ok(text) => text,
|
||||||
|
Err(e) => {
|
||||||
|
error!(Source = "Clipboard"; "Failed to decrypt the text: {e}.");
|
||||||
|
return Json(SetClipboardResponse {
|
||||||
|
success: false,
|
||||||
|
issue: e,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let clipboard_result = Clipboard::new();
|
let clipboard_result = Clipboard::new();
|
||||||
let mut clipboard = match clipboard_result {
|
let mut clipboard = match clipboard_result {
|
||||||
Ok(clipboard) => clipboard,
|
Ok(clipboard) => clipboard,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(Source = "Clipboard"; "Failed to get the clipboard instance: {e}.");
|
error!(Source = "Clipboard"; "Failed to get the clipboard instance: {e}.");
|
||||||
return SetClipboardResponse {
|
return Json(SetClipboardResponse {
|
||||||
success: false,
|
success: false,
|
||||||
issue: e.to_string(),
|
issue: e.to_string(),
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let set_text_result = clipboard.set_text(text);
|
let set_text_result = clipboard.set_text(decrypted_text);
|
||||||
match set_text_result {
|
match set_text_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
debug!(Source = "Clipboard"; "Text was set to the clipboard successfully.");
|
debug!(Source = "Clipboard"; "Text was set to the clipboard successfully.");
|
||||||
SetClipboardResponse {
|
Json(SetClipboardResponse {
|
||||||
success: true,
|
success: true,
|
||||||
issue: String::from(""),
|
issue: String::from(""),
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(Source = "Clipboard"; "Failed to set text to the clipboard: {e}.");
|
error!(Source = "Clipboard"; "Failed to set text to the clipboard: {e}.");
|
||||||
SetClipboardResponse {
|
Json(SetClipboardResponse {
|
||||||
success: false,
|
success: false,
|
||||||
issue: e.to_string(),
|
issue: e.to_string(),
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user