mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-07-04 20:11:48 +02:00
Add CEF runtime to Linux builds (#494)
This commit is contained in:
@@ -13,6 +13,8 @@ crate-type = ["staticlib", "cdylib", "lib"]
|
||||
[features]
|
||||
cargo-clippy = []
|
||||
default = []
|
||||
cef = ["tauri/cef"]
|
||||
wry = ["tauri/wry", "tauri/x11", "tauri/dbus"]
|
||||
updater = []
|
||||
license = ["yaak-license"]
|
||||
|
||||
@@ -82,6 +84,7 @@ yaak-mac-window = { workspace = true }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-plugins = { workspace = true }
|
||||
yaak-sse = { workspace = true }
|
||||
yaak-system-appearance = { workspace = true }
|
||||
yaak-sync = { workspace = true }
|
||||
yaak-templates = { workspace = true }
|
||||
yaak-tls = { workspace = true }
|
||||
|
||||
@@ -82,6 +82,14 @@ mod uri_scheme;
|
||||
mod window_menu;
|
||||
mod ws_ext;
|
||||
|
||||
#[cfg(not(any(feature = "cef", feature = "wry")))]
|
||||
compile_error!("Enable one Tauri runtime feature: `cef` or `wry`.");
|
||||
|
||||
#[cfg(feature = "cef")]
|
||||
type TauriRuntime = tauri::Cef;
|
||||
#[cfg(all(not(feature = "cef"), feature = "wry"))]
|
||||
type TauriRuntime = tauri::Wry;
|
||||
|
||||
fn setup_window_menu<R: Runtime>(win: &WebviewWindow<R>) -> Result<()> {
|
||||
#[allow(unused_variables)]
|
||||
let menu = window_menu::app_menu(win.app_handle())?;
|
||||
@@ -150,6 +158,22 @@ fn setup_window_menu<R: Runtime>(win: &WebviewWindow<R>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initial_appearance_script<R: Runtime>(app_handle: &AppHandle<R>) -> Option<String> {
|
||||
use yaak_system_appearance::{Appearance, InitialAppearanceSource};
|
||||
|
||||
let settings = app_handle.db().get_settings();
|
||||
let (appearance, source) = match settings.appearance.as_str() {
|
||||
"dark" => (Appearance::Dark, InitialAppearanceSource::Settings),
|
||||
"light" => (Appearance::Light, InitialAppearanceSource::Settings),
|
||||
_ => (
|
||||
yaak_system_appearance::system_appearance()?,
|
||||
InitialAppearanceSource::LinuxSystem,
|
||||
),
|
||||
};
|
||||
|
||||
Some(yaak_system_appearance::initialization_script(appearance, source))
|
||||
}
|
||||
|
||||
/// Extension trait for easily creating a PluginContext from a WebviewWindow
|
||||
pub trait PluginContextExt<R: Runtime> {
|
||||
fn plugin_context(&self) -> PluginContext;
|
||||
@@ -177,7 +201,7 @@ struct AppMetaData {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_metadata(app_handle: AppHandle) -> YaakResult<AppMetaData> {
|
||||
async fn cmd_metadata<R: Runtime>(app_handle: AppHandle<R>) -> YaakResult<AppMetaData> {
|
||||
let app_data_dir = app_handle.path().app_data_dir()?;
|
||||
let app_log_dir = app_handle.path().app_log_dir()?;
|
||||
let vendored_plugin_dir =
|
||||
@@ -962,7 +986,7 @@ async fn cmd_send_ephemeral_request<R: Runtime>(
|
||||
mut request: HttpRequest,
|
||||
environment_id: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
window: WebviewWindow,
|
||||
window: WebviewWindow<R>,
|
||||
app_handle: AppHandle<R>,
|
||||
) -> YaakResult<HttpResponse> {
|
||||
let response = HttpResponse::default();
|
||||
@@ -1588,20 +1612,22 @@ async fn cmd_get_workspace_meta<R: Runtime>(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_new_child_window(
|
||||
parent_window: WebviewWindow,
|
||||
async fn cmd_new_child_window<R: Runtime>(
|
||||
parent_window: WebviewWindow<R>,
|
||||
url: &str,
|
||||
label: &str,
|
||||
title: &str,
|
||||
inner_size: (f64, f64),
|
||||
) -> YaakResult<()> {
|
||||
let use_native_titlebar = parent_window.app_handle().db().get_settings().use_native_titlebar;
|
||||
let initialization_script = initial_appearance_script(&parent_window.app_handle());
|
||||
let win = yaak_window::window::create_child_window(
|
||||
&parent_window,
|
||||
url,
|
||||
label,
|
||||
title,
|
||||
inner_size,
|
||||
initialization_script,
|
||||
use_native_titlebar,
|
||||
)?;
|
||||
setup_window_menu(&win)?;
|
||||
@@ -1609,9 +1635,15 @@ async fn cmd_new_child_window(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_new_main_window(app_handle: AppHandle, url: &str) -> YaakResult<()> {
|
||||
async fn cmd_new_main_window<R: Runtime>(app_handle: AppHandle<R>, url: &str) -> YaakResult<()> {
|
||||
let use_native_titlebar = app_handle.db().get_settings().use_native_titlebar;
|
||||
let win = yaak_window::window::create_main_window(&app_handle, url, use_native_titlebar)?;
|
||||
let initialization_script = initial_appearance_script(&app_handle);
|
||||
let win = yaak_window::window::create_main_window(
|
||||
&app_handle,
|
||||
url,
|
||||
initialization_script,
|
||||
use_native_titlebar,
|
||||
)?;
|
||||
setup_window_menu(&win)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1631,8 +1663,9 @@ async fn cmd_check_for_updates<R: Runtime>(
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
#[cfg_attr(feature = "cef", tauri::cef_entry_point)]
|
||||
pub fn run() {
|
||||
let mut builder = tauri::Builder::default().plugin(
|
||||
let mut builder = tauri::Builder::<TauriRuntime>::default().plugin(
|
||||
Builder::default()
|
||||
.targets([
|
||||
Target::new(TargetKind::Stdout),
|
||||
@@ -1706,6 +1739,10 @@ pub fn run() {
|
||||
app.state::<yaak_models::query_manager::QueryManager>().inner().clone();
|
||||
let app_id = app.config().identifier.to_string();
|
||||
app.manage(yaak_crypto::manager::EncryptionManager::new(query_manager, app_id));
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(state) = yaak_system_appearance::watch(app.app_handle().clone()) {
|
||||
app.manage(state);
|
||||
}
|
||||
|
||||
{
|
||||
let app_handle = app.app_handle().clone();
|
||||
@@ -1894,9 +1931,11 @@ pub fn run() {
|
||||
match event {
|
||||
RunEvent::Ready => {
|
||||
let use_native_titlebar = app_handle.db().get_settings().use_native_titlebar;
|
||||
let initialization_script = initial_appearance_script(app_handle);
|
||||
if let Ok(win) = yaak_window::window::create_main_window(
|
||||
app_handle,
|
||||
"/",
|
||||
initialization_script,
|
||||
use_native_titlebar,
|
||||
) {
|
||||
let _ = setup_window_menu(&win);
|
||||
@@ -1917,6 +1956,13 @@ pub fn run() {
|
||||
});
|
||||
}
|
||||
RunEvent::WindowEvent { event: WindowEvent::Focused(true), label, .. } => {
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(state) =
|
||||
app_handle.try_state::<yaak_system_appearance::SystemAppearanceState>()
|
||||
{
|
||||
yaak_system_appearance::emit_change(app_handle, &state);
|
||||
}
|
||||
|
||||
if cfg!(feature = "updater") {
|
||||
// Run update check whenever the window is focused
|
||||
let w = app_handle.get_webview_window(&label).unwrap();
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"beforeBuildCommand": "npm --prefix ../.. run client:tauri-before-build",
|
||||
"beforeDevCommand": "npm --prefix ../.. run client:tauri-before-dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"frontendDist": "../../dist/apps/yaak-client"
|
||||
"frontendDist": "../../dist/apps/yaak-client",
|
||||
"features": ["wry"]
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": false,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
{
|
||||
"build": {
|
||||
"features": ["updater", "license"]
|
||||
},
|
||||
"app": {
|
||||
"security": {
|
||||
"capabilities": [
|
||||
|
||||
@@ -12,6 +12,10 @@ crate-type = ["staticlib", "cdylib", "lib"]
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.6.1", features = [] }
|
||||
|
||||
[features]
|
||||
default = ["wry"]
|
||||
wry = ["tauri/wry", "tauri/x11", "tauri/dbus"]
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "yaak-system-appearance"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
dark-light = "2.0.0"
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
tauri = { workspace = true }
|
||||
@@ -0,0 +1,151 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::{debug, warn};
|
||||
use tauri::{AppHandle, Emitter, Runtime};
|
||||
|
||||
pub const INITIAL_APPEARANCE_GLOBAL: &str = "__YAAK_INITIAL_APPEARANCE__";
|
||||
pub const INITIAL_APPEARANCE_SOURCE_GLOBAL: &str = "__YAAK_INITIAL_APPEARANCE_SOURCE__";
|
||||
pub const SYSTEM_APPEARANCE_CHANGE_EVENT: &str = "system_appearance_change";
|
||||
|
||||
const SYSTEM_APPEARANCE_POLL_INTERVAL: Duration = Duration::from_secs(1);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Appearance {
|
||||
Dark,
|
||||
Light,
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Dark => "dark",
|
||||
Self::Light => "light",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum InitialAppearanceSource {
|
||||
Settings,
|
||||
LinuxSystem,
|
||||
}
|
||||
|
||||
impl InitialAppearanceSource {
|
||||
fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Settings => "settings",
|
||||
Self::LinuxSystem => "linux-system",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SystemAppearanceState {
|
||||
last_appearance: Arc<Mutex<Option<Appearance>>>,
|
||||
}
|
||||
|
||||
pub fn initialization_script(appearance: Appearance, source: InitialAppearanceSource) -> String {
|
||||
let appearance = appearance.as_str();
|
||||
let source = source.as_str();
|
||||
format!(
|
||||
"window.{INITIAL_APPEARANCE_GLOBAL} = {appearance:?};\
|
||||
window.{INITIAL_APPEARANCE_SOURCE_GLOBAL} = {source:?};"
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn system_appearance() -> Option<Appearance> {
|
||||
if let Some(appearance) = gsettings_system_appearance() {
|
||||
return Some(appearance);
|
||||
}
|
||||
|
||||
match dark_light::detect() {
|
||||
Ok(dark_light::Mode::Dark) => Some(Appearance::Dark),
|
||||
Ok(dark_light::Mode::Light) => Some(Appearance::Light),
|
||||
Ok(dark_light::Mode::Unspecified) => None,
|
||||
Err(err) => {
|
||||
debug!("Failed to detect Linux system appearance: {err:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn system_appearance() -> Option<Appearance> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn watch<R: Runtime>(app_handle: AppHandle<R>) -> Option<SystemAppearanceState> {
|
||||
let last_appearance = system_appearance();
|
||||
if last_appearance.is_none() {
|
||||
debug!("Linux system appearance detection unavailable");
|
||||
return None;
|
||||
}
|
||||
|
||||
let state = SystemAppearanceState { last_appearance: Arc::new(Mutex::new(last_appearance)) };
|
||||
let thread_state = state.clone();
|
||||
let _ = std::thread::spawn(move || {
|
||||
loop {
|
||||
std::thread::sleep(SYSTEM_APPEARANCE_POLL_INTERVAL);
|
||||
emit_change(&app_handle, &thread_state);
|
||||
}
|
||||
});
|
||||
|
||||
Some(state)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn watch<R: Runtime>(_app_handle: AppHandle<R>) -> Option<SystemAppearanceState> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn emit_change<R: Runtime>(app_handle: &AppHandle<R>, state: &SystemAppearanceState) {
|
||||
let appearance = system_appearance();
|
||||
let mut last_appearance =
|
||||
state.last_appearance.lock().expect("system appearance lock poisoned");
|
||||
if appearance == *last_appearance {
|
||||
return;
|
||||
}
|
||||
|
||||
*last_appearance = appearance;
|
||||
if let Some(appearance) = appearance {
|
||||
let appearance = appearance.as_str();
|
||||
debug!("System appearance changed to {appearance}");
|
||||
if let Err(err) = app_handle.emit(SYSTEM_APPEARANCE_CHANGE_EVENT, appearance) {
|
||||
warn!("Failed to emit system appearance change: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn gsettings_system_appearance() -> Option<Appearance> {
|
||||
let color_scheme = std::process::Command::new("gsettings")
|
||||
.args(["get", "org.gnome.desktop.interface", "color-scheme"])
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
if color_scheme.contains("prefer-dark") {
|
||||
return Some(Appearance::Dark);
|
||||
}
|
||||
if color_scheme.contains("prefer-light") {
|
||||
return Some(Appearance::Light);
|
||||
}
|
||||
|
||||
let gtk_theme = std::process::Command::new("gsettings")
|
||||
.args(["get", "org.gnome.desktop.interface", "gtk-theme"])
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
if gtk_theme.to_lowercase().contains("dark") {
|
||||
return Some(Appearance::Dark);
|
||||
}
|
||||
|
||||
(!gtk_theme.trim().is_empty()).then_some(Appearance::Light)
|
||||
}
|
||||
@@ -26,6 +26,7 @@ pub struct CreateWindowConfig<'s> {
|
||||
pub navigation_tx: Option<mpsc::Sender<String>>,
|
||||
pub close_tx: Option<mpsc::Sender<()>>,
|
||||
pub data_dir_key: Option<String>,
|
||||
pub initialization_script: Option<String>,
|
||||
pub hidden: bool,
|
||||
pub hide_titlebar: bool,
|
||||
pub use_native_titlebar: bool,
|
||||
@@ -59,6 +60,10 @@ pub fn create_window<R: Runtime>(
|
||||
.maximized(maximized)
|
||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
|
||||
|
||||
if let Some(script) = config.initialization_script {
|
||||
win_builder = win_builder.initialization_script(script);
|
||||
}
|
||||
|
||||
if let Some(key) = config.data_dir_key {
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
@@ -138,11 +143,12 @@ pub fn create_window<R: Runtime>(
|
||||
Ok(win)
|
||||
}
|
||||
|
||||
pub fn create_main_window(
|
||||
handle: &AppHandle,
|
||||
pub fn create_main_window<R: Runtime>(
|
||||
handle: &AppHandle<R>,
|
||||
url: &str,
|
||||
initialization_script: Option<String>,
|
||||
use_native_titlebar: bool,
|
||||
) -> tauri::Result<WebviewWindow> {
|
||||
) -> tauri::Result<WebviewWindow<R>> {
|
||||
let mut counter = 0;
|
||||
let label = loop {
|
||||
let label = format!("{MAIN_WINDOW_PREFIX}{counter}");
|
||||
@@ -165,6 +171,8 @@ pub fn create_main_window(
|
||||
100.0 + random::<f64>() * 20.0,
|
||||
)),
|
||||
restore_position: Some(counter == 0),
|
||||
initialization_script,
|
||||
hidden: true,
|
||||
hide_titlebar: true,
|
||||
use_native_titlebar,
|
||||
..Default::default()
|
||||
@@ -173,14 +181,15 @@ pub fn create_main_window(
|
||||
create_window(handle, config)
|
||||
}
|
||||
|
||||
pub fn create_child_window(
|
||||
parent_window: &WebviewWindow,
|
||||
pub fn create_child_window<R: Runtime>(
|
||||
parent_window: &WebviewWindow<R>,
|
||||
url: &str,
|
||||
label: &str,
|
||||
title: &str,
|
||||
inner_size: (f64, f64),
|
||||
initialization_script: Option<String>,
|
||||
use_native_titlebar: bool,
|
||||
) -> tauri::Result<WebviewWindow> {
|
||||
) -> tauri::Result<WebviewWindow<R>> {
|
||||
let app_handle = parent_window.app_handle();
|
||||
let state_key = label.to_string();
|
||||
let label = format!("{OTHER_WINDOW_PREFIX}_{label}");
|
||||
@@ -202,6 +211,8 @@ pub fn create_child_window(
|
||||
url,
|
||||
inner_size: Some(inner_size),
|
||||
position: Some(position),
|
||||
initialization_script,
|
||||
hidden: true,
|
||||
hide_titlebar: true,
|
||||
use_native_titlebar,
|
||||
..Default::default()
|
||||
|
||||
Reference in New Issue
Block a user