diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bd051ff0..22003420 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -12,7 +12,6 @@ use crate::uri_scheme::handle_uri_scheme; use error::Result as YaakResult; use eventsource_client::{EventParser, SSE}; use log::{debug, error, warn}; -use rand::random; use regex::Regex; use std::collections::{BTreeMap, HashMap}; use std::fs::{create_dir_all, File}; @@ -20,7 +19,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::Duration; use std::{fs, panic}; -use tauri::{AppHandle, Emitter, RunEvent, State, WebviewWindow}; +use tauri::{is_dev, AppHandle, Emitter, RunEvent, State, WebviewWindow}; use tauri::{Listener, Runtime}; use tauri::{Manager, WindowEvent}; use tauri_plugin_log::fern::colors::ColoredLevelConfig; @@ -68,6 +67,7 @@ use yaak_sse::sse::ServerSentEvent; use yaak_templates::format::format_json; use yaak_templates::{Parser, Tokens}; +mod commands; mod encoding; mod error; mod grpc; @@ -83,15 +83,6 @@ mod uri_scheme; mod window; mod window_menu; -const DEFAULT_WINDOW_WIDTH: f64 = 1100.0; -const DEFAULT_WINDOW_HEIGHT: f64 = 600.0; - -const MIN_WINDOW_WIDTH: f64 = 300.0; -const MIN_WINDOW_HEIGHT: f64 = 300.0; - -const MAIN_WINDOW_PREFIX: &str = "main_"; -const OTHER_WINDOW_PREFIX: &str = "other_"; - #[derive(serde::Serialize)] #[serde(default, rename_all = "camelCase")] struct AppMetaData { @@ -1368,15 +1359,6 @@ async fn cmd_update_folder(folder: Folder, w: WebviewWindow) -> Result Result<(), String> { - if !is_dev() { - panic!("Cannot write arbitrary files when not in dev mode"); - } - - fs::write(pathname, contents).map_err(|e| e.to_string()) -} - #[tauri::command] async fn cmd_delete_folder(w: WebviewWindow, folder_id: &str) -> Result { delete_folder(&w, folder_id, &UpdateSource::Window).await.map_err(|e| e.to_string()) @@ -1632,72 +1614,13 @@ async fn cmd_new_child_window( title: &str, inner_size: (f64, f64), ) -> Result<(), String> { - let app_handle = parent_window.app_handle(); - let label = format!("{OTHER_WINDOW_PREFIX}_{label}"); - let scale_factor = parent_window.scale_factor().unwrap(); - - let current_pos = parent_window.inner_position().unwrap().to_logical::(scale_factor); - let current_size = parent_window.inner_size().unwrap().to_logical::(scale_factor); - - // Position the new window in the middle of the parent - let position = ( - current_pos.x + current_size.width / 2.0 - inner_size.0 / 2.0, - current_pos.y + current_size.height / 2.0 - inner_size.1 / 2.0, - ); - - let config = window::CreateWindowConfig { - label: label.as_str(), - title, - url, - inner_size: Some(inner_size), - position: Some(position), - hide_titlebar: true, - ..Default::default() - }; - - let child_window = window::create_window(&app_handle, config); - - // NOTE: These listeners will remain active even when the windows close. Unfortunately, - // there's no way to unlisten to events for now, so we just have to be defensive. - - { - let parent_window = parent_window.clone(); - let child_window = child_window.clone(); - child_window.clone().on_window_event(move |e| match e { - // When the new window is destroyed, bring the other up behind it - WindowEvent::Destroyed => { - if let Some(w) = parent_window.get_webview_window(child_window.label()) { - w.set_focus().unwrap(); - } - } - _ => {} - }); - } - - { - let parent_window = parent_window.clone(); - let child_window = child_window.clone(); - parent_window.clone().on_window_event(move |e| match e { - // When the parent window is closed, close the child - WindowEvent::CloseRequested { .. } => child_window.destroy().unwrap(), - // When the parent window is focused, bring the child above - WindowEvent::Focused(focus) => { - if *focus { - if let Some(w) = parent_window.get_webview_window(child_window.label()) { - w.set_focus().unwrap(); - }; - } - } - _ => {} - }); - } - + window::create_child_window(&parent_window, url, label, title, inner_size); Ok(()) } #[tauri::command] async fn cmd_new_main_window(app_handle: AppHandle, url: &str) -> Result<(), String> { - create_main_window(&app_handle, url); + window::create_main_window(&app_handle, url); Ok(()) } @@ -1724,33 +1647,6 @@ async fn cmd_check_for_updates( pub fn run() { #[allow(unused_mut)] let mut builder = tauri::Builder::default() - .plugin( - Builder::default() - .targets([ - Target::new(TargetKind::Stdout), - Target::new(TargetKind::LogDir { file_name: None }), - Target::new(TargetKind::Webview), - ]) - .level_for("plugin_runtime", log::LevelFilter::Info) - .level_for("cookie_store", log::LevelFilter::Info) - .level_for("eventsource_client::event_parser", log::LevelFilter::Info) - .level_for("h2", log::LevelFilter::Info) - .level_for("hyper", log::LevelFilter::Info) - .level_for("hyper_util", log::LevelFilter::Info) - .level_for("hyper_rustls", log::LevelFilter::Info) - .level_for("reqwest", log::LevelFilter::Info) - .level_for("sqlx", log::LevelFilter::Warn) - .level_for("tao", log::LevelFilter::Info) - .level_for("tokio_util", log::LevelFilter::Info) - .level_for("tonic", log::LevelFilter::Info) - .level_for("tower", log::LevelFilter::Info) - .level_for("tracing", log::LevelFilter::Warn) - .level_for("swc_ecma_codegen", log::LevelFilter::Off) - .level_for("swc_ecma_transforms_base", log::LevelFilter::Off) - .with_colors(ColoredLevelConfig::default()) - .level(if is_dev() { log::LevelFilter::Debug } else { log::LevelFilter::Info }) - .build(), - ) .plugin( Builder::default() .targets([ @@ -1905,7 +1801,6 @@ pub fn run() { cmd_update_settings, cmd_update_workspace, cmd_update_workspace_meta, - cmd_write_file_dev, ]) .register_uri_scheme_protocol("yaak", handle_uri_scheme) .build(tauri::generate_context!()) @@ -1913,7 +1808,7 @@ pub fn run() { .run(|app_handle, event| { match event { RunEvent::Ready => { - let w = create_main_window(app_handle, "/"); + let w = window::create_main_window(app_handle, "/"); tauri::async_runtime::spawn(async move { let info = history::store_launch_history(&w).await; debug!("Launched Yaak {:?}", info); @@ -1973,45 +1868,6 @@ pub fn run() { }); } -fn is_dev() -> bool { - #[cfg(dev)] - { - return true; - } - #[cfg(not(dev))] - { - return false; - } -} - -fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow { - let mut counter = 0; - let label = loop { - let label = format!("{MAIN_WINDOW_PREFIX}{counter}"); - match handle.webview_windows().get(label.as_str()) { - None => break Some(label), - Some(_) => counter += 1, - } - } - .expect("Failed to generate label for new window"); - - let config = window::CreateWindowConfig { - url, - label: label.as_str(), - title: "Yaak", - inner_size: Some((DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)), - position: Some(( - // Offset by random amount so it's easier to differentiate - 100.0 + random::() * 20.0, - 100.0 + random::() * 20.0, - )), - hide_titlebar: true, - ..Default::default() - }; - - window::create_window(handle, config) -} - async fn get_update_mode(h: &AppHandle) -> UpdateMode { let settings = get_or_create_settings(h).await; UpdateMode::new(settings.update_channel.as_str()) diff --git a/src-tauri/src/tauri_plugin_mac_window.rs b/src-tauri/src/tauri_plugin_mac_window.rs index dd70a2b7..0b09654d 100644 --- a/src-tauri/src/tauri_plugin_mac_window.rs +++ b/src-tauri/src/tauri_plugin_mac_window.rs @@ -1,4 +1,4 @@ -use crate::MAIN_WINDOW_PREFIX; +use crate::window::MAIN_WINDOW_PREFIX; use hex_color::HexColor; use log::warn; use objc::{msg_send, sel, sel_impl}; diff --git a/src-tauri/src/window.rs b/src-tauri/src/window.rs index ae73aff8..c7d31b60 100644 --- a/src-tauri/src/window.rs +++ b/src-tauri/src/window.rs @@ -1,6 +1,6 @@ use crate::window_menu::app_menu; -use crate::{DEFAULT_WINDOW_HEIGHT, DEFAULT_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH}; use log::{info, warn}; +use rand::random; use std::process::exit; use tauri::{ AppHandle, Emitter, LogicalSize, Manager, Runtime, WebviewUrl, WebviewWindow, WindowEvent, @@ -8,6 +8,15 @@ use tauri::{ use tauri_plugin_opener::OpenerExt; use tokio::sync::mpsc; +const DEFAULT_WINDOW_WIDTH: f64 = 1100.0; +const DEFAULT_WINDOW_HEIGHT: f64 = 600.0; + +const MIN_WINDOW_WIDTH: f64 = 300.0; +const MIN_WINDOW_HEIGHT: f64 = 300.0; + +pub(crate) const MAIN_WINDOW_PREFIX: &str = "main_"; +const OTHER_WINDOW_PREFIX: &str = "other_"; + #[derive(Default, Debug)] pub(crate) struct CreateWindowConfig<'s> { pub url: &'s str, @@ -161,3 +170,95 @@ pub(crate) fn create_window( win } + +pub(crate) fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow { + let mut counter = 0; + let label = loop { + let label = format!("{MAIN_WINDOW_PREFIX}{counter}"); + match handle.webview_windows().get(label.as_str()) { + None => break Some(label), + Some(_) => counter += 1, + } + } + .expect("Failed to generate label for new window"); + + let config = CreateWindowConfig { + url, + label: label.as_str(), + title: "Yaak", + inner_size: Some((DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)), + position: Some(( + // Offset by random amount so it's easier to differentiate + 100.0 + random::() * 20.0, + 100.0 + random::() * 20.0, + )), + hide_titlebar: true, + ..Default::default() + }; + + create_window(handle, config) +} + +pub(crate) fn create_child_window(parent_window: &WebviewWindow, url: &str, label: &str, title: &str, inner_size: (f64, f64)) -> WebviewWindow { + let app_handle = parent_window.app_handle(); + let label = format!("{OTHER_WINDOW_PREFIX}_{label}"); + let scale_factor = parent_window.scale_factor().unwrap(); + + let current_pos = parent_window.inner_position().unwrap().to_logical::(scale_factor); + let current_size = parent_window.inner_size().unwrap().to_logical::(scale_factor); + + // Position the new window in the middle of the parent + let position = ( + current_pos.x + current_size.width / 2.0 - inner_size.0 / 2.0, + current_pos.y + current_size.height / 2.0 - inner_size.1 / 2.0, + ); + + let config = CreateWindowConfig { + label: label.as_str(), + title, + url, + inner_size: Some(inner_size), + position: Some(position), + hide_titlebar: true, + ..Default::default() + }; + + let child_window = create_window(&app_handle, config); + + // NOTE: These listeners will remain active even when the windows close. Unfortunately, + // there's no way to unlisten to events for now, so we just have to be defensive. + + { + let parent_window = parent_window.clone(); + let child_window = child_window.clone(); + child_window.clone().on_window_event(move |e| match e { + // When the new window is destroyed, bring the other up behind it + WindowEvent::Destroyed => { + if let Some(w) = parent_window.get_webview_window(child_window.label()) { + w.set_focus().unwrap(); + } + } + _ => {} + }); + } + + { + let parent_window = parent_window.clone(); + let child_window = child_window.clone(); + parent_window.clone().on_window_event(move |e| match e { + // When the parent window is closed, close the child + WindowEvent::CloseRequested { .. } => child_window.destroy().unwrap(), + // When the parent window is focused, bring the child above + WindowEvent::Focused(focus) => { + if *focus { + if let Some(w) = parent_window.get_webview_window(child_window.label()) { + w.set_focus().unwrap(); + }; + } + } + _ => {} + }); + } + + child_window +} diff --git a/src-web/components/Settings/SettingsDesign.tsx b/src-web/components/Settings/SettingsDesign.tsx deleted file mode 100644 index b7f63706..00000000 --- a/src-web/components/Settings/SettingsDesign.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { open } from '@tauri-apps/plugin-dialog'; -import React, { useState } from 'react'; -import { useLocalStorage } from 'react-use'; -import { capitalize } from '../../lib/capitalize'; -import { invokeCmd } from '../../lib/tauri'; -import { getThemes } from '../../lib/theme/themes'; -import { yaakDark } from '../../lib/theme/themes/yaak'; -import { getThemeCSS } from '../../lib/theme/window'; -import { Banner } from '../core/Banner'; -import { Button } from '../core/Button'; -import {Editor} from "../core/Editor/Editor"; -import type { IconProps } from '../core/Icon'; -import { Icon } from '../core/Icon'; -import { IconButton } from '../core/IconButton'; -import { InlineCode } from '../core/InlineCode'; -import { Input } from '../core/Input'; -import { Separator } from '../core/Separator'; -import { HStack, VStack } from '../core/Stacks'; - -const buttonColors = [ - 'primary', - 'secondary', - 'info', - 'success', - 'warning', - 'danger', - 'default', -] as const; - -const icons: IconProps['icon'][] = [ - 'info', - 'box', - 'update', - 'alert_triangle', - 'arrow_big_right_dash', - 'download', - 'copy', - 'magic_wand', - 'settings', - 'trash', - 'sparkles', - 'pencil', - 'paste', - 'search', - 'send_horizontal', -]; - -const themes = getThemes(); - -export function SettingsDesign() { - const [exportDir, setExportDir] = useLocalStorage('theme_export_dir', null); - const [loadingExport, setLoadingExport] = useState(false); - - const saveThemes = () => { - setLoadingExport(true); - setTimeout(async () => { - const allThemesCSS = themes.themes.map(getThemeCSS).join('\n\n'); - const coreThemeCSS = [yaakDark].map(getThemeCSS).join('\n\n'); - - try { - await invokeCmd('cmd_write_file_dev', { - pathname: exportDir + '/themes-all.css', - contents: allThemesCSS, - }); - await invokeCmd('cmd_write_file_dev', { - pathname: exportDir + '/themes-slim.css', - contents: coreThemeCSS, - }); - } catch (err) { - console.log('FAILED', err); - } - setLoadingExport(false); - }, 500); - }; - - return ( -
- - {exportDir} - - - - - - - } - stateKey={null} - /> - -
-
- {buttonColors.map((c, i) => ( - - ))} -
-
- {buttonColors.map((c, i) => ( - - ))} -
-
- {icons.map((v, i) => ( - - ))} -
-
-
- Primary banner - Secondary banner - Danger banner - Warning banner - Success banner -
-
- ); -} diff --git a/src-web/components/Settings/SettingsGeneral.tsx b/src-web/components/Settings/SettingsGeneral.tsx index 811ed403..159b9423 100644 --- a/src-web/components/Settings/SettingsGeneral.tsx +++ b/src-web/components/Settings/SettingsGeneral.tsx @@ -77,14 +77,6 @@ export function SettingsGeneral() { ]} /> - {}} - /> - diff --git a/src-web/lib/tauri.ts b/src-web/lib/tauri.ts index bedd1f1b..a354fb7e 100644 --- a/src-web/lib/tauri.ts +++ b/src-web/lib/tauri.ts @@ -78,8 +78,7 @@ type TauriCmd = | 'cmd_update_http_request' | 'cmd_update_settings' | 'cmd_update_workspace' - | 'cmd_update_workspace_meta' - | 'cmd_write_file_dev'; + | 'cmd_update_workspace_meta'; export async function invokeCmd(cmd: TauriCmd, args?: InvokeArgs): Promise { // console.log('RUN COMMAND', cmd, args);