windows.rs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. use crate::constants::{UPDATER_ENABLED, window_state_flags};
  2. use std::{ops::Deref, time::Duration};
  3. use tauri::{AppHandle, Manager, Runtime, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
  4. use tauri_plugin_window_state::AppHandleExt;
  5. use tokio::sync::mpsc;
  6. pub struct MainWindow(WebviewWindow);
  7. impl Deref for MainWindow {
  8. type Target = WebviewWindow;
  9. fn deref(&self) -> &Self::Target {
  10. &self.0
  11. }
  12. }
  13. impl MainWindow {
  14. pub const LABEL: &str = "main";
  15. pub fn create(app: &AppHandle) -> Result<Self, tauri::Error> {
  16. if let Some(window) = app.get_webview_window(Self::LABEL) {
  17. return Ok(Self(window));
  18. }
  19. let window_builder = base_window_config(
  20. WebviewWindowBuilder::new(app, Self::LABEL, WebviewUrl::App("/".into())),
  21. app,
  22. )
  23. .title("OpenCode")
  24. .decorations(true)
  25. .disable_drag_drop_handler()
  26. .zoom_hotkeys_enabled(false)
  27. .visible(true)
  28. .maximized(true)
  29. .initialization_script(format!(
  30. r#"
  31. window.__OPENCODE__ ??= {{}};
  32. window.__OPENCODE__.updaterEnabled = {UPDATER_ENABLED};
  33. "#
  34. ));
  35. let window = window_builder.build()?;
  36. setup_window_state_listener(app, &window);
  37. #[cfg(windows)]
  38. {
  39. use tauri_plugin_decorum::WebviewWindowExt;
  40. let _ = window.create_overlay_titlebar();
  41. }
  42. Ok(Self(window))
  43. }
  44. }
  45. fn setup_window_state_listener(app: &AppHandle, window: &WebviewWindow) {
  46. let (tx, mut rx) = mpsc::channel::<()>(1);
  47. window.on_window_event(move |event| {
  48. use tauri::WindowEvent;
  49. if !matches!(event, WindowEvent::Moved(_) | WindowEvent::Resized(_)) {
  50. return;
  51. }
  52. let _ = tx.try_send(());
  53. });
  54. tokio::spawn({
  55. let app = app.clone();
  56. async move {
  57. let save = || {
  58. let handle = app.clone();
  59. let app = app.clone();
  60. let _ = handle.run_on_main_thread(move || {
  61. let _ = app.save_window_state(window_state_flags());
  62. });
  63. };
  64. while rx.recv().await.is_some() {
  65. tokio::time::sleep(Duration::from_millis(200)).await;
  66. save();
  67. }
  68. }
  69. });
  70. }
  71. pub struct LoadingWindow(WebviewWindow);
  72. impl Deref for LoadingWindow {
  73. type Target = WebviewWindow;
  74. fn deref(&self) -> &Self::Target {
  75. &self.0
  76. }
  77. }
  78. impl LoadingWindow {
  79. pub const LABEL: &str = "loading";
  80. pub fn create(app: &AppHandle) -> Result<Self, tauri::Error> {
  81. let window_builder = base_window_config(
  82. WebviewWindowBuilder::new(app, Self::LABEL, tauri::WebviewUrl::App("/loading".into())),
  83. app,
  84. )
  85. .center()
  86. .resizable(false)
  87. .inner_size(640.0, 480.0)
  88. .visible(true);
  89. Ok(Self(window_builder.build()?))
  90. }
  91. }
  92. fn base_window_config<'a, R: Runtime, M: Manager<R>>(
  93. window_builder: WebviewWindowBuilder<'a, R, M>,
  94. _app: &AppHandle,
  95. ) -> WebviewWindowBuilder<'a, R, M> {
  96. let window_builder = window_builder.decorations(true);
  97. #[cfg(windows)]
  98. let window_builder = window_builder
  99. // Some VPNs set a global/system proxy that WebView2 applies even for loopback
  100. // connections, which breaks the app's localhost sidecar server.
  101. // Note: when setting additional args, we must re-apply wry's default
  102. // `--disable-features=...` flags.
  103. .additional_browser_args(
  104. "--proxy-bypass-list=<-loopback> --disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection",
  105. )
  106. .data_directory(_app.path().config_dir().expect("Failed to get config dir").join(_app.config().product_name.clone().unwrap()))
  107. .decorations(false);
  108. #[cfg(target_os = "macos")]
  109. let window_builder = window_builder
  110. .title_bar_style(tauri::TitleBarStyle::Overlay)
  111. .hidden_title(true)
  112. .traffic_light_position(tauri::LogicalPosition::new(12.0, 18.0));
  113. window_builder
  114. }