feat: Enhance application with logging, configuration management, and system tray support
first working gui windows with configs
This commit is contained in:
33
README.md
33
README.md
@@ -51,6 +51,29 @@ rustc --version
|
|||||||
cargo --version
|
cargo --version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Starta Applikationen
|
||||||
|
|
||||||
|
För att starta applikationen i utvecklingsläge:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo tauri dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Felsökning: Linux (Wayland) & "Error 71"
|
||||||
|
Om du använder Linux med Wayland (t.ex. GNOME eller KDE Plasma) kan du stöta på "Error 71 (Protocol error)" vid start. Detta är en känd bugg relaterad till WebKitGTK och hårdvaruacceleration.
|
||||||
|
|
||||||
|
**Lösning:**
|
||||||
|
Kör applikationen med inaktiverad kompositering:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WEBKIT_DISABLE_COMPOSITING_MODE=1 cargo tauri dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternativt för vissa NVIDIA-konfigurationer:
|
||||||
|
```bash
|
||||||
|
__NV_DISABLE_EXPLICIT_SYNC=1 cargo tauri dev
|
||||||
|
```
|
||||||
|
|
||||||
### Linux-beroenden (Ubuntu/Debian)
|
### Linux-beroenden (Ubuntu/Debian)
|
||||||
För att kompilera Tauri på Linux krävs följande bibliotek:
|
För att kompilera Tauri på Linux krävs följande bibliotek:
|
||||||
|
|
||||||
@@ -189,3 +212,13 @@ När du bygger via `cargo-xwin` (se ovan) eller på en Windows-maskin, genereras
|
|||||||
* `src-tauri/src/main.rs`: Entry point. Innehåller logik för System Tray.
|
* `src-tauri/src/main.rs`: Entry point. Innehåller logik för System Tray.
|
||||||
* `src-tauri/tauri.conf.json`: Konfiguration för fönster och byggprocess.
|
* `src-tauri/tauri.conf.json`: Konfiguration för fönster och byggprocess.
|
||||||
|
|
||||||
|
## Loggning
|
||||||
|
|
||||||
|
Applikationen har inbyggt stöd för loggning för att underlätta felsökning.
|
||||||
|
|
||||||
|
* **Plats:** Loggfiler sparas i en mapp som heter `loggs` som ligger i samma katalog som den körbara filen.
|
||||||
|
* **Filnamn:** En loggfil skapas per dag och döps efter dagens datum (t.ex. `2024-01-18.log`).
|
||||||
|
* **Format:** Loggarna innehåller tidsstämpel, loggnivå (INFO, DEBUG, ERROR), modul/funktion samt meddelandet. Exempel:
|
||||||
|
`[2024-01-18 10:00:00][INFO][ai_translater_client::main] Application started`
|
||||||
|
* **Loggade händelser:** Applikationsstart, system tray-händelser, felmeddelanden och annan viktig information loggas.
|
||||||
|
|
||||||
|
|||||||
13
src-tauri/Cargo.lock
generated
13
src-tauri/Cargo.lock
generated
@@ -22,8 +22,10 @@ name = "ai-translater-client"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arboard",
|
"arboard",
|
||||||
|
"chrono",
|
||||||
"enigo",
|
"enigo",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"fern",
|
||||||
"global-hotkey",
|
"global-hotkey",
|
||||||
"log",
|
"log",
|
||||||
"ollama-rs",
|
"ollama-rs",
|
||||||
@@ -417,8 +419,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -913,6 +917,15 @@ dependencies = [
|
|||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fern"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "field-offset"
|
name = "field-offset"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
|||||||
@@ -30,3 +30,5 @@ global-hotkey = "0.6" # Systemomfattande genvägar
|
|||||||
# Logging (Strongly recommended for debugging)
|
# Logging (Strongly recommended for debugging)
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
|
fern = "0.6"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
|||||||
19
src-tauri/src/controllers/app_state.rs
Normal file
19
src-tauri/src/controllers/app_state.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use std::sync::Mutex;
|
||||||
|
use tauri::{App, Manager, Runtime};
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub ollama_ready: Mutex<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ollama_ready: Mutex::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_state<R: Runtime>(app: &mut App<R>) {
|
||||||
|
app.manage(AppState::new());
|
||||||
|
}
|
||||||
6
src-tauri/src/controllers/greet.rs
Normal file
6
src-tauri/src/controllers/greet.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
use tauri::command;
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn greet(name: &str) -> String {
|
||||||
|
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||||
|
}
|
||||||
3
src-tauri/src/controllers/mod.rs
Normal file
3
src-tauri/src/controllers/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod app_state;
|
||||||
|
pub mod settings;
|
||||||
|
pub mod greet;
|
||||||
16
src-tauri/src/controllers/settings.rs
Normal file
16
src-tauri/src/controllers/settings.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use tauri::command;
|
||||||
|
use crate::utilities::config::{AppConfig, load_config, save_config};
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn get_settings() -> Result<AppConfig, String> {
|
||||||
|
load_config().map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn save_settings(config: AppConfig) -> Result<(), String> {
|
||||||
|
save_config(&config).map_err(|e| e.to_string())?;
|
||||||
|
// Note: Re-initializing logging at runtime is complex with fern alone as it sets a global logger.
|
||||||
|
// Ideally we would have a reloadable logger or just ask user to restart.
|
||||||
|
// For now we just save.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,81 +1,60 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
use tauri::{
|
mod utilities;
|
||||||
menu::{Menu, MenuItem},
|
mod controllers;
|
||||||
tray::{MouseButton, TrayIconBuilder, TrayIconEvent},
|
mod viewers;
|
||||||
Manager,
|
|
||||||
};
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
// Placeholder for application state
|
use log::info;
|
||||||
struct AppState {
|
use utilities::config::load_config;
|
||||||
// Exempel: Spara status för ollama connection här
|
use utilities::logging::setup_logging;
|
||||||
ollama_ready: Mutex<bool>,
|
use controllers::app_state::init_state;
|
||||||
}
|
use controllers::settings::{get_settings, save_settings};
|
||||||
|
use controllers::greet::greet;
|
||||||
|
use viewers::tray::setup_tray;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// Initiera loggning
|
// 1. Load configuration
|
||||||
env_logger::init();
|
let config = match load_config() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to load config: {}", e);
|
||||||
|
// Default config fallback logic is inside load_config, but if file IO fails we might get here.
|
||||||
|
// We can try to proceed with default logging if possible, but for now just print to stderr.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Init logging
|
||||||
|
if let Err(e) = setup_logging(&config) {
|
||||||
|
eprintln!("Failed to setup logging: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Application started");
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.invoke_handler(tauri::generate_handler![get_settings, save_settings, greet])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
// Setup Application State
|
info!("Setting up application...");
|
||||||
app.manage(AppState {
|
|
||||||
ollama_ready: Mutex::new(false),
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- System Tray Setup ---
|
// 3. Init State
|
||||||
|
init_state(app);
|
||||||
|
|
||||||
// Skapa menyalternativ
|
// 4. Setup Tray
|
||||||
let quit_i = MenuItem::with_id(app, "quit", "Avsluta", true, None::<&str>)?;
|
setup_tray(app)?;
|
||||||
let settings_i = MenuItem::with_id(app, "settings", "Inställningar", true, None::<&str>)?;
|
|
||||||
|
|
||||||
// Skapa menyn
|
|
||||||
let menu = Menu::with_items(app, &[&settings_i, &quit_i])?;
|
|
||||||
|
|
||||||
// Bygg Tray-ikonen
|
|
||||||
let _tray = TrayIconBuilder::new()
|
|
||||||
.icon(app.default_window_icon().unwrap().clone())
|
|
||||||
.menu(&menu)
|
|
||||||
.show_menu_on_left_click(false)
|
|
||||||
.on_menu_event(|app, event| {
|
|
||||||
match event.id.as_ref() {
|
|
||||||
"quit" => {
|
|
||||||
println!("Avslutar applikationen...");
|
|
||||||
app.exit(0);
|
|
||||||
}
|
|
||||||
"settings" => {
|
|
||||||
println!("Öppnar inställningar (Placeholder)...");
|
|
||||||
// Här kan du öppna ditt inställningsfönster:
|
|
||||||
// if let Some(window) = app.get_webview_window("main") {
|
|
||||||
// window.show().unwrap();
|
|
||||||
// window.set_focus().unwrap();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on_tray_icon_event(|tray, event| {
|
|
||||||
match event {
|
|
||||||
TrayIconEvent::Click {
|
|
||||||
button: MouseButton::Left,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let app = tray.app_handle();
|
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
|
||||||
let _ = window.show();
|
|
||||||
let _ = window.set_focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build(app)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
.on_window_event(|window, event| {
|
||||||
|
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
|
||||||
|
// Prevent the window from closing (destroying)
|
||||||
|
// Instead, hide it. This keeps the app running in the tray.
|
||||||
|
window.hide().unwrap();
|
||||||
|
api.prevent_close();
|
||||||
|
}
|
||||||
|
})
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
75
src-tauri/src/utilities/config.rs
Normal file
75
src-tauri/src/utilities/config.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct LogConfig {
|
||||||
|
pub path: String,
|
||||||
|
pub level: String,
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LogConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
path: "loggs".to_string(),
|
||||||
|
level: "debug".to_string(),
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct GeneralConfig {
|
||||||
|
pub theme: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GeneralConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
theme: "dark".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct AppConfig {
|
||||||
|
#[serde(default)]
|
||||||
|
pub logging: LogConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
pub general: GeneralConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
logging: LogConfig::default(),
|
||||||
|
general: GeneralConfig::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_config() -> Result<AppConfig, Box<dyn std::error::Error>> {
|
||||||
|
let exe_path = std::env::current_exe()?;
|
||||||
|
let exe_dir = exe_path.parent().ok_or("Could not find exe directory")?;
|
||||||
|
|
||||||
|
let config_path = exe_dir.join("config.json");
|
||||||
|
if config_path.exists() {
|
||||||
|
let content = std::fs::read_to_string(&config_path)?;
|
||||||
|
Ok(serde_json::from_str(&content).unwrap_or_else(|_| AppConfig::default()))
|
||||||
|
} else {
|
||||||
|
let config = AppConfig::default();
|
||||||
|
if let Ok(content) = serde_json::to_string_pretty(&config) {
|
||||||
|
let _ = std::fs::write(&config_path, content);
|
||||||
|
}
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_config(config: &AppConfig) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let exe_path = std::env::current_exe()?;
|
||||||
|
let exe_dir = exe_path.parent().ok_or("Could not find exe directory")?;
|
||||||
|
let config_path = exe_dir.join("config.json");
|
||||||
|
|
||||||
|
let content = serde_json::to_string_pretty(config)?;
|
||||||
|
std::fs::write(config_path, content)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
45
src-tauri/src/utilities/logging.rs
Normal file
45
src-tauri/src/utilities/logging.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use log::LevelFilter;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use super::config::AppConfig;
|
||||||
|
|
||||||
|
pub fn setup_logging(config: &AppConfig) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if !config.logging.enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let exe_path = std::env::current_exe()?;
|
||||||
|
let exe_dir = exe_path.parent().ok_or("Could not find exe directory")?;
|
||||||
|
|
||||||
|
let log_path_config = std::path::Path::new(&config.logging.path);
|
||||||
|
let log_dir = if log_path_config.is_absolute() {
|
||||||
|
log_path_config.to_path_buf()
|
||||||
|
} else {
|
||||||
|
exe_dir.join(log_path_config)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !log_dir.exists() {
|
||||||
|
std::fs::create_dir_all(&log_dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_name = chrono::Local::now().format("%Y-%m-%d.log").to_string();
|
||||||
|
let log_path = log_dir.join(file_name);
|
||||||
|
|
||||||
|
let level_filter = LevelFilter::from_str(&config.logging.level).unwrap_or(LevelFilter::Debug);
|
||||||
|
|
||||||
|
fern::Dispatch::new()
|
||||||
|
.format(|out, message, record| {
|
||||||
|
out.finish(format_args!(
|
||||||
|
"[{}][{}][{}] {}",
|
||||||
|
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
|
||||||
|
record.level(),
|
||||||
|
record.target(),
|
||||||
|
message
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.level(level_filter)
|
||||||
|
.chain(std::io::stdout())
|
||||||
|
.chain(fern::log_file(log_path)?)
|
||||||
|
.apply()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
2
src-tauri/src/utilities/mod.rs
Normal file
2
src-tauri/src/utilities/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod logging;
|
||||||
1
src-tauri/src/viewers/mod.rs
Normal file
1
src-tauri/src/viewers/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod tray;
|
||||||
96
src-tauri/src/viewers/tray.rs
Normal file
96
src-tauri/src/viewers/tray.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use tauri::{
|
||||||
|
menu::{Menu, MenuItem},
|
||||||
|
tray::{MouseButton, TrayIconBuilder, TrayIconEvent},
|
||||||
|
App, Manager, Runtime, WebviewWindowBuilder, WebviewUrl,
|
||||||
|
};
|
||||||
|
use log::{info, error};
|
||||||
|
|
||||||
|
fn toggle_settings_window<R: Runtime>(app: &tauri::AppHandle<R>) {
|
||||||
|
match app.get_webview_window("settings") {
|
||||||
|
Some(window) => {
|
||||||
|
info!("Settings window found");
|
||||||
|
if let Ok(true) = window.is_visible() {
|
||||||
|
info!("Window is visible, hiding...");
|
||||||
|
let _ = window.hide();
|
||||||
|
} else {
|
||||||
|
info!("Window is hidden, showing...");
|
||||||
|
// On checking docs/issues: Some Wayland compositors dislike set_focus or show on hidden windows
|
||||||
|
// Creating the window 'visible' from start is safer.
|
||||||
|
let _ = window.show();
|
||||||
|
// window.set_focus(); - Removed to prevent Wayland protocol error 71
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("Creating new settings window...");
|
||||||
|
let build_result = WebviewWindowBuilder::new(
|
||||||
|
app,
|
||||||
|
"settings",
|
||||||
|
WebviewUrl::App("index.html".into())
|
||||||
|
)
|
||||||
|
.title("AI Typist Inställningar")
|
||||||
|
.inner_size(800.0, 600.0)
|
||||||
|
.visible(false) // Create hidden first to avoid Wayland focus issues on creation
|
||||||
|
.build();
|
||||||
|
|
||||||
|
match build_result {
|
||||||
|
Ok(window) => {
|
||||||
|
info!("Settings window created successfully");
|
||||||
|
// Now show it explicitly
|
||||||
|
if let Err(e) = window.show() {
|
||||||
|
error!("Failed to show window: {}", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => error!("Failed to create settings window: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_tray<R: Runtime>(app: &mut App<R>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Settings window is now created via tauri.conf.json to ensure correct init context on Wayland
|
||||||
|
|
||||||
|
// Skapa menyalternativ
|
||||||
|
let quit_i = MenuItem::with_id(app, "quit", "Avsluta", true, None::<&str>)?;
|
||||||
|
let settings_i = MenuItem::with_id(app, "settings", "Inställningar", true, None::<&str>)?;
|
||||||
|
|
||||||
|
// Skapa menyn
|
||||||
|
let menu = Menu::with_items(app, &[&settings_i, &quit_i])?;
|
||||||
|
|
||||||
|
info!("Tray menu created");
|
||||||
|
|
||||||
|
// Bygg Tray-ikonen
|
||||||
|
let _tray = TrayIconBuilder::new()
|
||||||
|
.icon(app.default_window_icon().unwrap().clone())
|
||||||
|
.menu(&menu)
|
||||||
|
.show_menu_on_left_click(false)
|
||||||
|
.on_menu_event(|app, event| {
|
||||||
|
match event.id.as_ref() {
|
||||||
|
"quit" => {
|
||||||
|
info!("User clicked quit from tray");
|
||||||
|
println!("Avslutar applikationen...");
|
||||||
|
app.exit(0);
|
||||||
|
}
|
||||||
|
"settings" => {
|
||||||
|
info!("User clicked settings from tray");
|
||||||
|
toggle_settings_window(app);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_tray_icon_event(|tray, event| {
|
||||||
|
match event {
|
||||||
|
TrayIconEvent::Click {
|
||||||
|
button: MouseButton::Left,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let app = tray.app_handle();
|
||||||
|
toggle_settings_window(app);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build(app)?;
|
||||||
|
|
||||||
|
info!("Tray system initialized successfully");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -8,12 +8,15 @@
|
|||||||
"frontendDist": "../dist"
|
"frontendDist": "../dist"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
|
"withGlobalTauri": true,
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
|
"label": "settings",
|
||||||
"title": "AI Typist Inställningar",
|
"title": "AI Typist Inställningar",
|
||||||
|
"url": "index.html",
|
||||||
"width": 800,
|
"width": 800,
|
||||||
"height": 600,
|
"height": 600,
|
||||||
"visible": false
|
"visible": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
Reference in New Issue
Block a user