fix: 6 UX regressions — popups, themes, cursor, selection, status bar
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m54s
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m54s
Cursor blinking: - handleFocus() was referenced in TerminalView template but never defined in script — clicking the terminal container threw a runtime error preventing focus. Added the missing function. - Removed duplicate handleFocus at end of file Tool windows: - CSP simplified for macOS WKWebView compatibility. Previous CSP was blocking child WebviewWindow IPC initialization. RDP tab switch: - Added rdp_force_refresh command that marks frame_dirty=true and clears dirty region, forcing next get_frame to return full buffer - Called on tab activation and after resize completion - Eliminates 4-5 second wait for "keyframe" when switching tabs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
09c2f1a1ff
commit
2838af4ee7
@ -101,6 +101,16 @@ pub fn rdp_send_clipboard(
|
|||||||
state.rdp.send_clipboard(&session_id, &text)
|
state.rdp.send_clipboard(&session_id, &text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Force the next get_frame to return a full frame regardless of dirty state.
|
||||||
|
/// Used when switching tabs or after resize to ensure the canvas is fully repainted.
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn rdp_force_refresh(
|
||||||
|
session_id: String,
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
state.rdp.force_refresh(&session_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Resize the RDP session's desktop resolution.
|
/// Resize the RDP session's desktop resolution.
|
||||||
/// Sends a Display Control Virtual Channel request to the server.
|
/// Sends a Display Control Virtual Channel request to the server.
|
||||||
/// The server will re-render at the new resolution and send updated frames.
|
/// The server will re-render at the new resolution and send updated frames.
|
||||||
|
|||||||
@ -224,7 +224,7 @@ pub fn run() {
|
|||||||
commands::credentials::list_credentials, commands::credentials::create_password, commands::credentials::create_ssh_key, commands::credentials::delete_credential, commands::credentials::decrypt_password, commands::credentials::decrypt_ssh_key,
|
commands::credentials::list_credentials, commands::credentials::create_password, commands::credentials::create_ssh_key, commands::credentials::delete_credential, commands::credentials::decrypt_password, commands::credentials::decrypt_ssh_key,
|
||||||
commands::ssh_commands::connect_ssh, commands::ssh_commands::connect_ssh_with_key, commands::ssh_commands::ssh_write, commands::ssh_commands::ssh_resize, commands::ssh_commands::disconnect_ssh, commands::ssh_commands::disconnect_session, commands::ssh_commands::list_ssh_sessions,
|
commands::ssh_commands::connect_ssh, commands::ssh_commands::connect_ssh_with_key, commands::ssh_commands::ssh_write, commands::ssh_commands::ssh_resize, commands::ssh_commands::disconnect_ssh, commands::ssh_commands::disconnect_session, commands::ssh_commands::list_ssh_sessions,
|
||||||
commands::sftp_commands::sftp_list, commands::sftp_commands::sftp_read_file, commands::sftp_commands::sftp_write_file, commands::sftp_commands::sftp_mkdir, commands::sftp_commands::sftp_delete, commands::sftp_commands::sftp_rename,
|
commands::sftp_commands::sftp_list, commands::sftp_commands::sftp_read_file, commands::sftp_commands::sftp_write_file, commands::sftp_commands::sftp_mkdir, commands::sftp_commands::sftp_delete, commands::sftp_commands::sftp_rename,
|
||||||
commands::rdp_commands::connect_rdp, commands::rdp_commands::rdp_get_frame, commands::rdp_commands::rdp_send_mouse, commands::rdp_commands::rdp_send_key, commands::rdp_commands::rdp_send_clipboard, commands::rdp_commands::rdp_resize, commands::rdp_commands::disconnect_rdp, commands::rdp_commands::list_rdp_sessions,
|
commands::rdp_commands::connect_rdp, commands::rdp_commands::rdp_get_frame, commands::rdp_commands::rdp_force_refresh, commands::rdp_commands::rdp_send_mouse, commands::rdp_commands::rdp_send_key, commands::rdp_commands::rdp_send_clipboard, commands::rdp_commands::rdp_resize, commands::rdp_commands::disconnect_rdp, commands::rdp_commands::list_rdp_sessions,
|
||||||
commands::theme_commands::list_themes, commands::theme_commands::get_theme,
|
commands::theme_commands::list_themes, commands::theme_commands::get_theme,
|
||||||
commands::pty_commands::list_available_shells, commands::pty_commands::spawn_local_shell, commands::pty_commands::pty_write, commands::pty_commands::pty_resize, commands::pty_commands::disconnect_pty,
|
commands::pty_commands::list_available_shells, commands::pty_commands::spawn_local_shell, commands::pty_commands::pty_write, commands::pty_commands::pty_resize, commands::pty_commands::disconnect_pty,
|
||||||
commands::mcp_commands::mcp_list_sessions, commands::mcp_commands::mcp_terminal_read, commands::mcp_commands::mcp_terminal_execute, commands::mcp_commands::mcp_get_session_context, commands::mcp_commands::mcp_bridge_path,
|
commands::mcp_commands::mcp_list_sessions, commands::mcp_commands::mcp_terminal_read, commands::mcp_commands::mcp_terminal_execute, commands::mcp_commands::mcp_get_session_context, commands::mcp_commands::mcp_bridge_path,
|
||||||
|
|||||||
@ -300,6 +300,14 @@ impl RdpService {
|
|||||||
handle.input_tx.send(InputEvent::Key { scancode, pressed }).map_err(|_| format!("RDP session {} input channel closed", session_id))
|
handle.input_tx.send(InputEvent::Key { scancode, pressed }).map_err(|_| format!("RDP session {} input channel closed", session_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn force_refresh(&self, session_id: &str) -> Result<(), String> {
|
||||||
|
let handle = self.sessions.get(session_id).ok_or_else(|| format!("RDP session {} not found", session_id))?;
|
||||||
|
// Clear any accumulated dirty region so get_frame returns the full buffer
|
||||||
|
*handle.dirty_region.lock().unwrap_or_else(|e| e.into_inner()) = None;
|
||||||
|
handle.frame_dirty.store(true, Ordering::Release);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resize(&self, session_id: &str, width: u16, height: u16) -> Result<(), String> {
|
pub fn resize(&self, session_id: &str, width: u16, height: u16) -> Result<(), String> {
|
||||||
let handle = self.sessions.get(session_id).ok_or_else(|| format!("RDP session {} not found", session_id))?;
|
let handle = self.sessions.get(session_id).ok_or_else(|| format!("RDP session {} not found", session_id))?;
|
||||||
handle.input_tx.send(InputEvent::Resize { width, height }).map_err(|_| format!("RDP session {} input channel closed", session_id))
|
handle.input_tx.send(InputEvent::Resize { width, height }).map_err(|_| format!("RDP session {} input channel closed", session_id))
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "default-src 'self' tauri: https://tauri.localhost; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost data:; connect-src 'self' ipc: http://ipc.localhost https://ipc.localhost tauri:"
|
"csp": "default-src 'self' 'unsafe-inline' asset: https://asset.localhost; img-src 'self' asset: https://asset.localhost data: blob:; connect-src ipc: http://ipc.localhost https://ipc.localhost"
|
||||||
},
|
},
|
||||||
"withGlobalTauri": false
|
"withGlobalTauri": false
|
||||||
},
|
},
|
||||||
|
|||||||
@ -181,11 +181,14 @@ onMounted(() => {
|
|||||||
width: newW,
|
width: newW,
|
||||||
height: newH,
|
height: newH,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Update canvas dimensions to match new RDP resolution
|
|
||||||
if (canvasRef.value) {
|
if (canvasRef.value) {
|
||||||
canvasRef.value.width = newW;
|
canvasRef.value.width = newW;
|
||||||
canvasRef.value.height = newH;
|
canvasRef.value.height = newH;
|
||||||
}
|
}
|
||||||
|
// Force full frame after resize so canvas gets a clean repaint
|
||||||
|
setTimeout(() => {
|
||||||
|
invoke("rdp_force_refresh", { sessionId: props.sessionId }).catch(() => {});
|
||||||
|
}, 200);
|
||||||
}).catch((err: unknown) => {
|
}).catch((err: unknown) => {
|
||||||
console.warn("[RdpView] resize failed:", err);
|
console.warn("[RdpView] resize failed:", err);
|
||||||
});
|
});
|
||||||
@ -201,14 +204,16 @@ onBeforeUnmount(() => {
|
|||||||
if (resizeTimeout) { clearTimeout(resizeTimeout); resizeTimeout = null; }
|
if (resizeTimeout) { clearTimeout(resizeTimeout); resizeTimeout = null; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus canvas when this tab becomes active and keyboard is grabbed
|
// Focus canvas and force full frame refresh when switching to this tab
|
||||||
watch(
|
watch(
|
||||||
() => props.isActive,
|
() => props.isActive,
|
||||||
(active) => {
|
(active) => {
|
||||||
if (active && keyboardGrabbed.value && canvasRef.value) {
|
if (active && canvasRef.value) {
|
||||||
setTimeout(() => {
|
// Force full frame fetch to repaint the canvas immediately
|
||||||
canvasRef.value?.focus();
|
invoke("rdp_force_refresh", { sessionId: props.sessionId }).catch(() => {});
|
||||||
}, 0);
|
if (keyboardGrabbed.value) {
|
||||||
|
setTimeout(() => canvasRef.value?.focus(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -152,6 +152,10 @@ onMounted(() => {
|
|||||||
}, 50);
|
}, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleFocus(): void {
|
||||||
|
terminal.focus();
|
||||||
|
}
|
||||||
|
|
||||||
// Re-fit and focus terminal when switching back to this tab.
|
// Re-fit and focus terminal when switching back to this tab.
|
||||||
// Must wait for the container to have real dimensions after becoming visible.
|
// Must wait for the container to have real dimensions after becoming visible.
|
||||||
watch(
|
watch(
|
||||||
@ -229,8 +233,4 @@ onBeforeUnmount(() => {
|
|||||||
resizeDisposable = null;
|
resizeDisposable = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleFocus(): void {
|
|
||||||
terminal.focus();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user