Some cleanup around window creation

This commit is contained in:
Gregory Schier
2025-03-20 06:05:17 -07:00
parent 6aea343d4f
commit 2c41b243b6
6 changed files with 109 additions and 328 deletions

View File

@@ -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<Folder, S
upsert_folder(&w, folder, &UpdateSource::Window).await.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_write_file_dev(pathname: &str, contents: &str) -> 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<Folder, String> {
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::<f64>(scale_factor);
let current_size = parent_window.inner_size().unwrap().to_logical::<f64>(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::<f64>() * 20.0,
100.0 + random::<f64>() * 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())

View File

@@ -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};

View File

@@ -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<R: Runtime>(
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::<f64>() * 20.0,
100.0 + random::<f64>() * 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::<f64>(scale_factor);
let current_size = parent_window.inner_size().unwrap().to_logical::<f64>(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
}

View File

@@ -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<string | null>('theme_export_dir', null);
const [loadingExport, setLoadingExport] = useState<boolean>(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 (
<div className="p-2 flex flex-col gap-3">
<VStack space={2}>
<InlineCode>{exportDir}</InlineCode>
<HStack space={2}>
<Button
size="sm"
color="secondary"
variant="border"
onClick={() => {
open({ directory: true }).then(setExportDir);
}}
>
Change Export Dir
</Button>
<Button
disabled={exportDir == null}
isLoading={loadingExport}
size="sm"
color="primary"
variant="border"
onClick={saveThemes}
>
Export CSS
</Button>
</HStack>
</VStack>
<Separator className="my-6" />
<Input
label="Field Label"
name="demo"
placeholder="Placeholder"
size="sm"
rightSlot={<IconButton title="search" size="xs" className="w-8 m-0.5" icon="search" />}
stateKey={null}
/>
<Editor
defaultValue={[
'// Demo code editor',
'let foo = {',
' foo: ("bar" || "baz" ?? \'qux\'),',
' baz: [1, 10.2, null, false, true],',
'};',
].join('\n')}
heightMode="auto"
language="javascript"
stateKey={null}
/>
<div className="flex flex-col gap-1">
<div className="flex flex-wrap gap-1">
{buttonColors.map((c, i) => (
<Button key={c} color={c} size="sm" leftSlot={<Icon size="sm" icon={icons[i]!} />}>
{capitalize(c).slice(0, 4)}
</Button>
))}
</div>
<div className="flex flex-wrap gap-1">
{buttonColors.map((c, i) => (
<Button
key={c}
color={c}
variant="border"
size="sm"
leftSlot={<Icon size="sm" icon={icons[i]!} />}
>
{capitalize(c).slice(0, 4)}
</Button>
))}
</div>
<div className="flex gap-1">
{icons.map((v, i) => (
<IconButton
color={buttonColors[i % buttonColors.length]}
title={v}
variant="border"
size="sm"
key={v}
icon={v}
/>
))}
</div>
</div>
<div className="flex flex-col gap-1">
<Banner color="primary">Primary banner</Banner>
<Banner color="secondary">Secondary banner</Banner>
<Banner color="danger">Danger banner</Banner>
<Banner color="warning">Warning banner</Banner>
<Banner color="success">Success banner</Banner>
</div>
</div>
);
}

View File

@@ -77,14 +77,6 @@ export function SettingsGeneral() {
]}
/>
<Checkbox
className="mt-3"
checked={false}
title="Send Usage Statistics (all tracking was removed in 2025.1.2)"
disabled
onChange={() => {}}
/>
<Separator className="my-4" />
<Heading level={2}>

View File

@@ -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<T>(cmd: TauriCmd, args?: InvokeArgs): Promise<T> {
// console.log('RUN COMMAND', cmd, args);