Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changeset/windows-hotfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Bug Fixes
- On Windows, File > New window no longer freezes Stroke. The window opens, and the app closes normally
- On Windows, psql tables in the Terminal tab no longer show garbled borders like `│`, and NULL shows as `(null)`
18 changes: 16 additions & 2 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,13 @@ pub async fn read_file(path: String) -> Result<String, String> {
/// The chrome is built to match the main window exactly: frameless everywhere,
/// macOS keeping its native traffic lights, and the dark base colour painted
/// before the first frame so no white flash escapes while the frontend boots.
///
/// Async on purpose. A synchronous command runs on the main thread, and
/// building a WebView2 window there deadlocks on Windows (tauri-apps/wry#583):
/// the window never appeared, and every other window froze with it, so the
/// app could not even be closed.
#[tauri::command]
pub fn open_new_window(app: tauri::AppHandle) -> Result<(), String> {
pub async fn open_new_window(app: tauri::AppHandle) -> Result<(), String> {
// Labels must be unique and stable-ish; the counter restarts with the app,
// and a closed label is free to reuse, so probe for the first gap.
let label = (2..64)
Expand All @@ -290,7 +295,12 @@ pub fn open_new_window(app: tauri::AppHandle) -> Result<(), String> {
.resizable(true)
// Hidden until its page reveals itself, same as the main window.
.visible(false)
.background_color(tauri::window::Color(8, 8, 8, 255));
.background_color(tauri::window::Color(8, 8, 8, 255))
// The rest of the main window's settings (lib.rs): inspector in debug
// builds only, no injected zoom polyfill, no navigating away from the app.
.devtools(cfg!(debug_assertions))
.zoom_hotkeys_enabled(false)
.on_navigation(crate::navigation_allowed);

#[cfg(target_os = "macos")]
{
Expand All @@ -304,6 +314,10 @@ pub fn open_new_window(app: tauri::AppHandle) -> Result<(), String> {
}

let window = builder.build().map_err(|e| e.to_string())?;
// The surface on the OS's side of light and dark before the first frame.
let surface = crate::surface_for_theme(window.theme().unwrap_or(tauri::Theme::Dark));
let _ = window.set_background_color(Some(surface));
crate::set_webview_backdrop(&window, surface);
crate::arm_reveal_failsafe(&window);
Ok(())
}
Expand Down
32 changes: 28 additions & 4 deletions src-tauri/src/db/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,21 @@ const PSQL_PROMPT1: &str =
"%[%033]133;A%007%]%[%033[1;34m%]%/%[%033[0m%]%R%[%033[33m%]%x%[%033[0m%]%# %[%033]133;B%007%]";
const PSQL_PROMPT2: &str = "%[%033]133;A;k=s%007%]%[%033[2m%]%/%R%x%#%[%033[0m%] %[%033]133;B%007%]";

/// Table borders and the NULL marker. Unicode borders and `∅` everywhere but
/// Windows: psql writes those borders as UTF-8 whatever its client encoding,
/// and the Windows console reads its output in the ANSI code page (1252), so
/// every `│` came out as `│`; `∅` reached it through a 1252 argv as `?`. There
/// psql keeps its own ASCII borders, and NULL shows as `(null)`, which read
/// the same in any code page.
fn psql_display_args(windows: bool) -> Vec<String> {
if windows {
vec!["--pset=null=(null)".to_string()]
} else {
// NULL apart from the empty string.
vec!["--pset=linestyle=unicode".to_string(), "--pset=null=\u{2205}".to_string()]
}
}

async fn pg_launch(config: PgConfig) -> Result<Launch, String> {
let (c, tunnel) = resolve_pg_ssh(config).await?;
let mut env = vec![
Expand Down Expand Up @@ -355,10 +370,10 @@ async fn pg_launch(config: PgConfig) -> Result<Launch, String> {
// and narrow results stay plain tables. (`format=wrapped` was tried
// and dropped: it squeezes columns and breaks names mid-word.)
"--pset=expanded=auto".to_string(),
"--pset=linestyle=unicode".to_string(),
// NULL apart from the empty string.
"--pset=null=\u{2205}".to_string(),
],
]
.into_iter()
.chain(psql_display_args(cfg!(windows)))
.collect(),
env,
unset: &["PGSERVICE", "PGPASSWORD", "PGSSLMODE", "PGSSLROOTCERT", "PGTZ", "PGOPTIONS"],
tunnel,
Expand Down Expand Up @@ -985,6 +1000,15 @@ mod tests {
assert!(!env.contains_key("PGTZ"));
}

#[test]
fn psql_keeps_ascii_borders_on_windows() {
let unix = psql_display_args(false);
assert!(unix.contains(&"--pset=linestyle=unicode".to_string()));
let windows = psql_display_args(true);
assert!(windows.iter().all(|a| a.is_ascii() && !a.contains("linestyle")), "{windows:?}");
assert!(windows.contains(&"--pset=null=(null)".to_string()));
}

#[test]
fn sqlite_gets_box_mode_by_version() {
let c = SqliteConfig { name: "t".into(), file_path: "/tmp/x.db".into() };
Expand Down
23 changes: 13 additions & 10 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use tauri::Manager;
const DARK_SURFACE: tauri::window::Color = tauri::window::Color(8, 8, 8, 255);
const LIGHT_SURFACE: tauri::window::Color = tauri::window::Color(247, 247, 247, 255);

fn surface_for_theme(theme: tauri::Theme) -> tauri::window::Color {
pub(crate) fn surface_for_theme(theme: tauri::Theme) -> tauri::window::Color {
match theme {
tauri::Theme::Light => LIGHT_SURFACE,
_ => DARK_SURFACE,
Expand All @@ -58,6 +58,16 @@ fn surface_for_theme(theme: tauri::Theme) -> tauri::window::Color {
/// never loads), which would otherwise leave a live process with no window.
const REVEAL_FAILSAFE: std::time::Duration = std::time::Duration::from_secs(4);

/// Whether a window may navigate to `url`: only the app's own pages. A link
/// to anywhere else opens in the browser instead of replacing the app.
pub(crate) fn navigation_allowed(url: &tauri::Url) -> bool {
if matches!(url.scheme(), "tauri" | "ipc") {
return true;
}
let host = url.host_str().unwrap_or("");
host == "localhost" || host == "tauri.localhost" || host == "127.0.0.1"
}

pub(crate) fn arm_reveal_failsafe(window: &tauri::WebviewWindow) {
let window = window.clone();
std::thread::spawn(move || {
Expand All @@ -82,7 +92,7 @@ pub(crate) fn arm_reveal_failsafe(window: &tauri::WebviewWindow) {
/// - WKWebView (macOS): `underPageBackgroundColor`
/// - WebView2 (Windows): `ICoreWebView2Controller2::DefaultBackgroundColor`
/// - WebKitGTK (Linux): `webkit_web_view_set_background_color`
fn set_webview_backdrop(window: &tauri::WebviewWindow, color: tauri::window::Color) {
pub(crate) fn set_webview_backdrop(window: &tauri::WebviewWindow, color: tauri::window::Color) {
#[cfg(target_os = "macos")]
set_macos_webview_backdrop(window, color);
#[cfg(target_os = "windows")]
Expand Down Expand Up @@ -397,14 +407,7 @@ pub fn run() {
// a stray trackpad pinch near a column resize handle would then page-zoom
// the whole webview (devicePixelRatio jumps, canvas renders blurry).
.zoom_hotkeys_enabled(false)
.on_navigation(|url| {
let scheme = url.scheme();
if matches!(scheme, "tauri" | "ipc") {
return true;
}
let host = url.host_str().unwrap_or("");
host == "localhost" || host == "tauri.localhost" || host == "127.0.0.1"
})
.on_navigation(navigation_allowed)
.build()?;

// Match the surface to the OS appearance before the first frame. Both
Expand Down
Loading