mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-10-25 17:20:21 +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() | ||||
|     { | ||||
|         await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, this.Result2Copy()); | ||||
|         await this.Rust.CopyText2Clipboard(this.Snackbar, this.Result2Copy()); | ||||
|     } | ||||
|      | ||||
|     private static string? GetButtonIcon(string icon) | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -78,36 +78,44 @@ public sealed class Rust(string apiPort) : IDisposable | ||||
|     /// <summary> | ||||
|     /// Tries to copy the given text to the clipboard. | ||||
|     /// </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="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 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<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) | ||||
|  | ||||
| @ -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. | ||||
| /// </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 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. | ||||
|     /// </summary> | ||||
|     /// <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 { | ||||
|         _ = 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 = "<encrypted_text>")] | ||||
| 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 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(), | ||||
|             } | ||||
|             }) | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user