feat: Phase 3 Desktop — native menu, auto-update, hidden terminal
CI / lint (push) Successful in 26s
CI / security (push) Successful in 13s
CI / test (push) Successful in 31s
CI / build (push) Successful in 6s
CI / e2e (push) Failing after 33s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
CI / lint (push) Successful in 26s
CI / security (push) Successful in 13s
CI / test (push) Successful in 31s
CI / build (push) Successful in 6s
CI / e2e (push) Failing after 33s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
- #![windows_subsystem = "windows"]: no console window on Windows release builds - CREATE_NO_WINDOW (0x08000000): Python backend subprocess hidden on Windows - Native menu bar: Fichier (N/Fermer/Quitter) + Edition (Undo/Redo/Cut/Copy/Paste/SelectAll) + Aide (About) - Auto-update: tauri-plugin-updater checks Gitea releases endpoint - tauri.conf.json: updater plugin config with passive install mode - ROADMAP: Phase 3 marked as complete (11/11 subtasks done)
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
//! ObsiGate Desktop — Native Tauri shell for ObsiGate web app.
|
||||
//!
|
||||
//! Spawns a Python FastAPI backend, then opens the frontend in a
|
||||
//! native webview. No terminal window on Windows (subsystem = "windows").
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use log::{error, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::{self, OpenOptions};
|
||||
@@ -495,6 +502,7 @@ fn main() {
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||
// Focus existing window when second instance launched
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
@@ -522,6 +530,9 @@ fn main() {
|
||||
list_dirs,
|
||||
])
|
||||
.setup(move |app| {
|
||||
// Build native menu bar (File / Edit / Help)
|
||||
let _ = build_native_menu(app);
|
||||
|
||||
// Restore window position/size
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
if let (Some(x), Some(y)) = (config.window_x, config.window_y) {
|
||||
@@ -635,6 +646,106 @@ fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Native Menu Bar ──────────────────────────────────────────────────────────
|
||||
|
||||
fn build_native_menu(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use tauri::menu::{
|
||||
MenuBuilder, MenuItemBuilder, SubmenuBuilder, PredefinedMenuItem,
|
||||
};
|
||||
|
||||
// File menu
|
||||
let new_window = MenuItemBuilder::with_id("file_new_window", "Nouvelle fenêtre")
|
||||
.accelerator("CmdOrCtrl+N")
|
||||
.build(app)?;
|
||||
let close_window = MenuItemBuilder::with_id("file_close", "Fermer")
|
||||
.accelerator("CmdOrCtrl+W")
|
||||
.build(app)?;
|
||||
let file_sep = PredefinedMenuItem::separator(app)?;
|
||||
let quit = MenuItemBuilder::with_id("file_quit", "Quitter")
|
||||
.accelerator("CmdOrCtrl+Q")
|
||||
.build(app)?;
|
||||
let file_menu = SubmenuBuilder::new(app, "Fichier")
|
||||
.item(&new_window)
|
||||
.item(&close_window)
|
||||
.item(&file_sep)
|
||||
.item(&quit)
|
||||
.build()?;
|
||||
|
||||
// Edit menu
|
||||
let undo = MenuItemBuilder::with_id("edit_undo", "Annuler")
|
||||
.accelerator("CmdOrCtrl+Z")
|
||||
.build(app)?;
|
||||
let redo = MenuItemBuilder::with_id("edit_redo", "Rétablir")
|
||||
.accelerator("CmdOrCtrl+Shift+Z")
|
||||
.build(app)?;
|
||||
let edit_sep = PredefinedMenuItem::separator(app)?;
|
||||
let cut = MenuItemBuilder::with_id("edit_cut", "Couper")
|
||||
.accelerator("CmdOrCtrl+X")
|
||||
.build(app)?;
|
||||
let copy = MenuItemBuilder::with_id("edit_copy", "Copier")
|
||||
.accelerator("CmdOrCtrl+C")
|
||||
.build(app)?;
|
||||
let paste = MenuItemBuilder::with_id("edit_paste", "Coller")
|
||||
.accelerator("CmdOrCtrl+V")
|
||||
.build(app)?;
|
||||
let select_all = MenuItemBuilder::with_id("edit_select_all", "Tout sélectionner")
|
||||
.accelerator("CmdOrCtrl+A")
|
||||
.build(app)?;
|
||||
let edit_menu = SubmenuBuilder::new(app, "Édition")
|
||||
.item(&undo)
|
||||
.item(&redo)
|
||||
.item(&edit_sep)
|
||||
.item(&cut)
|
||||
.item(©)
|
||||
.item(&paste)
|
||||
.item(&edit_sep.clone())
|
||||
.item(&select_all)
|
||||
.build()?;
|
||||
|
||||
// Help menu
|
||||
let about = MenuItemBuilder::with_id("help_about", "À propos d'ObsiGate")
|
||||
.build(app)?;
|
||||
let help_menu = SubmenuBuilder::new(app, "Aide")
|
||||
.item(&about)
|
||||
.build()?;
|
||||
|
||||
let menu = MenuBuilder::new(app)
|
||||
.item(&file_menu)
|
||||
.item(&edit_menu)
|
||||
.item(&help_menu)
|
||||
.build()?;
|
||||
|
||||
app.set_menu(menu)?;
|
||||
|
||||
// Handle menu events
|
||||
app.on_menu_event(move |app, event| {
|
||||
match event.id().as_ref() {
|
||||
"file_quit" => {
|
||||
info!("Quit from menu");
|
||||
app.exit(0);
|
||||
}
|
||||
"file_close" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.close();
|
||||
}
|
||||
}
|
||||
"help_about" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.eval(
|
||||
r#"alert('ObsiGate Desktop v2.0.0\n\nPorte d\'entrée native pour vos vaults Obsidian.\nConstruit avec Tauri + Rust.\n\n© Bruno Charest — Licence MIT\nhttps://git.dracodev.net/Projets/ObsiGate')"#
|
||||
);
|
||||
}
|
||||
}
|
||||
"edit_undo" | "edit_redo" | "edit_cut" | "edit_copy" | "edit_paste" | "edit_select_all" => {
|
||||
// Let the webview handle these natively
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ── Tray Menu ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn build_tray_menu(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Reference in New Issue
Block a user