Files
shopify-ai-backup/opencode/packages/desktop/src-tauri/src/windows.rs
2026-02-07 20:54:46 +00:00

141 lines
4.0 KiB
Rust

use crate::constants::{UPDATER_ENABLED, window_state_flags};
use std::{ops::Deref, time::Duration};
use tauri::{AppHandle, Manager, Runtime, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
use tauri_plugin_window_state::AppHandleExt;
use tokio::sync::mpsc;
pub struct MainWindow(WebviewWindow);
impl Deref for MainWindow {
type Target = WebviewWindow;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl MainWindow {
pub const LABEL: &str = "main";
pub fn create(app: &AppHandle) -> Result<Self, tauri::Error> {
if let Some(window) = app.get_webview_window(Self::LABEL) {
return Ok(Self(window));
}
let window_builder = base_window_config(
WebviewWindowBuilder::new(app, Self::LABEL, WebviewUrl::App("/".into())),
app,
)
.title("OpenCode")
.decorations(true)
.disable_drag_drop_handler()
.zoom_hotkeys_enabled(false)
.visible(true)
.maximized(true)
.initialization_script(format!(
r#"
window.__OPENCODE__ ??= {{}};
window.__OPENCODE__.updaterEnabled = {UPDATER_ENABLED};
"#
));
let window = window_builder.build()?;
setup_window_state_listener(app, &window);
#[cfg(windows)]
{
use tauri_plugin_decorum::WebviewWindowExt;
let _ = window.create_overlay_titlebar();
}
Ok(Self(window))
}
}
fn setup_window_state_listener(app: &AppHandle, window: &WebviewWindow) {
let (tx, mut rx) = mpsc::channel::<()>(1);
window.on_window_event(move |event| {
use tauri::WindowEvent;
if !matches!(event, WindowEvent::Moved(_) | WindowEvent::Resized(_)) {
return;
}
let _ = tx.try_send(());
});
tokio::spawn({
let app = app.clone();
async move {
let save = || {
let handle = app.clone();
let app = app.clone();
let _ = handle.run_on_main_thread(move || {
let _ = app.save_window_state(window_state_flags());
});
};
while rx.recv().await.is_some() {
tokio::time::sleep(Duration::from_millis(200)).await;
save();
}
}
});
}
pub struct LoadingWindow(WebviewWindow);
impl Deref for LoadingWindow {
type Target = WebviewWindow;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl LoadingWindow {
pub const LABEL: &str = "loading";
pub fn create(app: &AppHandle) -> Result<Self, tauri::Error> {
let window_builder = base_window_config(
WebviewWindowBuilder::new(app, Self::LABEL, tauri::WebviewUrl::App("/loading".into())),
app,
)
.center()
.resizable(false)
.inner_size(640.0, 480.0)
.visible(true);
Ok(Self(window_builder.build()?))
}
}
fn base_window_config<'a, R: Runtime, M: Manager<R>>(
window_builder: WebviewWindowBuilder<'a, R, M>,
_app: &AppHandle,
) -> WebviewWindowBuilder<'a, R, M> {
let window_builder = window_builder.decorations(true);
#[cfg(windows)]
let window_builder = window_builder
// Some VPNs set a global/system proxy that WebView2 applies even for loopback
// connections, which breaks the app's localhost sidecar server.
// Note: when setting additional args, we must re-apply wry's default
// `--disable-features=...` flags.
.additional_browser_args(
"--proxy-bypass-list=<-loopback> --disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection",
)
.data_directory(_app.path().config_dir().expect("Failed to get config dir").join(_app.config().product_name.clone().unwrap()))
.decorations(false);
#[cfg(target_os = "macos")]
let window_builder = window_builder
.title_bar_style(tauri::TitleBarStyle::Overlay)
.hidden_title(true)
.traffic_light_position(tauri::LogicalPosition::new(12.0, 18.0));
window_builder
}