feat(ui): enable drag-and-drop attachments and image previews in chat (#390)
* feat(BottomTerminalDock): add close button next to the fullscreen toggle in the dock * style: replace hardcoded gradient with theme value in shine text variant * fix(header): adapt instance button for desktop only * refactor(chat): polish sticky turn UX and message action rows for better readability Switch to stable sticky-only turn behavior and redesign user/assistant action controls (placement, hover rules, ordering, spacing, selection-safe clamp) to reduce visual noise and improve interaction flow. * feat(chat/message): refactor buttons in messages footer * feat: enhance image preview functionality in chat messages - Added a new ImagePreviewDialog component to handle image previews with navigation support. - Updated ToolOutputDialog to utilize the new ImagePreviewDialog for displaying images. - Modified the ToolPopupContent type to include a gallery of images and an index for the current image. - Removed the old inline image display logic from ToolOutputDialog. - Improved file handling in the file store, including better MIME type guessing and handling of server paths. - Introduced a new API endpoint for handling large session message payloads, allowing for better management of multi-file attachments. - Updated the VSCode bridge to support session message requests with appropriate headers and body handling. * feat(proxy): implement SSE forwarding and enhance generic API request handling * feat(chat): support submitting only queued messages * feat: add image preview transition state * fix: default VSCode view to draft and fixed sessions list regression
This commit is contained in:
committed by
GitHub
parent
39e625d8ec
commit
844562749d
@@ -1843,6 +1843,65 @@ fn desktop_new_window_at_url(app: tauri::AppHandle, url: String) -> Result<(), S
|
||||
create_window(&app, &url, &local_origin).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Read a file and return its content as base64 with mime type detection.
|
||||
/// Used for drag-drop file attachments in desktop app.
|
||||
#[tauri::command]
|
||||
fn desktop_read_file(path: String) -> Result<FileContent, String> {
|
||||
use std::path::Path;
|
||||
|
||||
let path = Path::new(&path);
|
||||
|
||||
// Check file size (max 50MB)
|
||||
let metadata = std::fs::metadata(path).map_err(|e| format!("Failed to read file metadata: {e}"))?;
|
||||
let size = metadata.len();
|
||||
if size > 50 * 1024 * 1024 {
|
||||
return Err("File is too large. Maximum size is 50MB.".to_string());
|
||||
}
|
||||
|
||||
// Read file bytes
|
||||
let bytes = std::fs::read(path).map_err(|e| format!("Failed to read file: {e}"))?;
|
||||
|
||||
// Detect mime type from extension
|
||||
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("").to_lowercase();
|
||||
let mime = match ext.as_str() {
|
||||
"png" => "image/png",
|
||||
"jpg" | "jpeg" => "image/jpeg",
|
||||
"gif" => "image/gif",
|
||||
"webp" => "image/webp",
|
||||
"svg" => "image/svg+xml",
|
||||
"bmp" => "image/bmp",
|
||||
"ico" => "image/x-icon",
|
||||
"pdf" => "application/pdf",
|
||||
"txt" => "text/plain",
|
||||
"md" => "text/markdown",
|
||||
"json" => "application/json",
|
||||
"js" => "text/javascript",
|
||||
"ts" => "text/typescript",
|
||||
"tsx" => "text/typescript-jsx",
|
||||
"jsx" => "text/javascript-jsx",
|
||||
"html" => "text/html",
|
||||
"css" => "text/css",
|
||||
"py" => "text/x-python",
|
||||
_ => "application/octet-stream",
|
||||
};
|
||||
|
||||
// Encode as base64
|
||||
let base64 = general_purpose::STANDARD.encode(&bytes);
|
||||
|
||||
Ok(FileContent {
|
||||
mime: mime.to_string(),
|
||||
base64,
|
||||
size: bytes.len(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FileContent {
|
||||
mime: String,
|
||||
base64: String,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn macos_major_version() -> Option<u32> {
|
||||
fn cmd_stdout(cmd: &str, args: &[&str]) -> Option<String> {
|
||||
@@ -2222,6 +2281,7 @@ fn main() {
|
||||
desktop_hosts_get,
|
||||
desktop_hosts_set,
|
||||
desktop_host_probe,
|
||||
desktop_read_file,
|
||||
])
|
||||
.setup(|app| {
|
||||
let handle = app.handle().clone();
|
||||
|
||||
Reference in New Issue
Block a user