diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
index bf1e8ddf..83083278 100644
--- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
+++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
@@ -165,7 +165,7 @@ public abstract partial class AssistantBase : ComponentBase
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)
diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
index f411b075..8bb48b4a 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
@@ -100,7 +100,7 @@ public partial class ContentBlockComponent : ComponentBase
{
case ContentType.TEXT:
var textContent = (ContentText) this.Content;
- await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, textContent.Text);
+ await this.Rust.CopyText2Clipboard(this.Snackbar, textContent.Text);
break;
default:
diff --git a/app/MindWork AI Studio/Tools/Rust.cs b/app/MindWork AI Studio/Tools/Rust.cs
index b7d158bd..d78f8a0f 100644
--- a/app/MindWork AI Studio/Tools/Rust.cs
+++ b/app/MindWork AI Studio/Tools/Rust.cs
@@ -78,36 +78,44 @@ public sealed class Rust(string apiPort) : IDisposable
///
/// Tries to copy the given text to the clipboard.
///
- /// The JS runtime to access the Rust code.
/// The snackbar to show the result.
/// The text to copy to the clipboard.
- public async Task CopyText2Clipboard(IJSRuntime jsRuntime, ISnackbar snackbar, string text)
+ public async Task CopyText2Clipboard(ISnackbar snackbar, string text)
{
- var response = await jsRuntime.InvokeAsync("window.__TAURI__.invoke", "set_clipboard", new SetClipboardText(text));
- var msg = response.Success switch
+ var message = "Successfully copied the text to your clipboard";
+ var iconColor = Color.Error;
+ var severity = Severity.Error;
+ try
{
- true => "Successfully copied text to clipboard!",
- false => $"Failed to copy text to clipboard: {response.Issue}",
- };
-
- 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
+ var response = await this.http.PostAsync("/clipboard/set", new StringContent(await text.Encrypt(this.encryptor!)));
+ if (!response.IsSuccessStatusCode)
{
- Severity.Success => Color.Success,
- Severity.Error => Color.Error,
-
- _ => Color.Default,
- };
- });
+ this.logger!.LogError($"Failed to copy the text to the clipboard due to an network error: '{response.StatusCode}'");
+ message = "Failed to copy the text to your clipboard.";
+ return;
+ }
+
+ var state = await response.Content.ReadFromJsonAsync();
+ 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 CheckForUpdate(IJSRuntime jsRuntime)
diff --git a/app/MindWork AI Studio/Tools/Services/MarkdownClipboardService.cs b/app/MindWork AI Studio/Tools/Services/MarkdownClipboardService.cs
index 2684b2a9..4b566ab4 100644
--- a/app/MindWork AI Studio/Tools/Services/MarkdownClipboardService.cs
+++ b/app/MindWork AI Studio/Tools/Services/MarkdownClipboardService.cs
@@ -6,10 +6,8 @@ namespace AIStudio.Tools.Services;
/// Wire up the clipboard service to copy Markdown to the clipboard.
/// We use our own Rust-based clipboard service for this.
///
-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 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.
///
/// The Markdown text to copy.
- 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);
}
\ No newline at end of file
diff --git a/runtime/src/main.rs b/runtime/src/main.rs
index 9116e5ff..aa14fc60 100644
--- a/runtime/src/main.rs
+++ b/runtime/src/main.rs
@@ -194,7 +194,7 @@ async fn main() {
//
tauri::async_runtime::spawn(async move {
_ = rocket::custom(figment)
- .mount("/", routes![dotnet_port, dotnet_ready])
+ .mount("/", routes![dotnet_port, dotnet_ready, set_clipboard])
.ignite().await.unwrap()
.launch().await.unwrap();
});
@@ -312,7 +312,7 @@ async fn main() {
})
.plugin(tauri_plugin_window_state::Builder::default().build())
.invoke_handler(tauri::generate_handler![
- store_secret, get_secret, delete_secret, set_clipboard,
+ store_secret, get_secret, delete_secret,
check_for_update, install_update
])
.build(tauri::generate_context!())
@@ -848,36 +848,49 @@ struct DeleteSecretResponse {
issue: String,
}
-#[tauri::command]
-fn set_clipboard(text: String) -> SetClipboardResponse {
+#[post("/clipboard/set", data = "")]
+fn set_clipboard(encrypted_text: EncryptedText) -> Json {
+
+ // 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 mut clipboard = match clipboard_result {
Ok(clipboard) => clipboard,
Err(e) => {
error!(Source = "Clipboard"; "Failed to get the clipboard instance: {e}.");
- return SetClipboardResponse {
+ return Json(SetClipboardResponse {
success: false,
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 {
Ok(_) => {
debug!(Source = "Clipboard"; "Text was set to the clipboard successfully.");
- SetClipboardResponse {
+ Json(SetClipboardResponse {
success: true,
issue: String::from(""),
- }
+ })
},
Err(e) => {
error!(Source = "Clipboard"; "Failed to set text to the clipboard: {e}.");
- SetClipboardResponse {
+ Json(SetClipboardResponse {
success: false,
issue: e.to_string(),
- }
+ })
},
}
}