node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
.idea
# Generated by Cargo
# will have compiled files and executables
/target/
/gen/schemas
# Sidecar binaries
/binaries/*.exe
/binaries/seanime-aarch64-apple-darwin

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
[package]
name = "seanime-desktop"
version = "0.1.0"
description = "Seanime Desktop"
authors = ["you"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.71"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "lib"]
[build-dependencies]
tauri-build = { version = "2.1.1", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "2.4.1", features = ["macos-private-api", "tray-icon", "devtools"] }
tauri-plugin-shell = "2.2.1"
strip-ansi-escapes = "0.2.1"
tokio = "1.43.0"
tauri-plugin-decorum = "1.1.1"
tauri-plugin-os = "2.2.1"
tauri-plugin-clipboard-manager = "2.2.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-single-instance = "2"
tauri-plugin-updater = "2.4.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,15 @@
{
"identifier": "desktop-capability",
"platforms": [
"macOS",
"windows",
"linux"
],
"permissions": [
"updater:default",
"updater:allow-check",
"updater:allow-download",
"updater:allow-download-and-install",
"updater:allow-install"
]
}

View File

@@ -0,0 +1,92 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "enables the default permissions",
"windows": [
"main",
"splashscreen",
"crash_screen"
],
"permissions": [
"os:allow-arch",
"os:allow-hostname",
"os:allow-os-type",
"core:path:default",
"core:event:default",
"core:window:default",
"core:app:default",
"core:image:default",
"core:resources:default",
"core:menu:default",
"core:tray:default",
"shell:allow-open",
"shell:default",
"core:window:allow-set-title-bar-style",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:window:allow-set-closable",
"core:window:allow-set-title",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-close",
"core:window:allow-set-decorations",
"core:window:allow-set-shadow",
"core:window:allow-set-effects",
"core:window:allow-set-always-on-top",
"core:window:allow-set-always-on-bottom",
"core:window:allow-set-content-protected",
"core:window:allow-set-size",
"core:window:allow-set-min-size",
"core:window:allow-set-max-size",
"core:window:allow-set-position",
"core:window:allow-set-fullscreen",
"core:window:allow-set-focus",
"core:window:allow-set-skip-taskbar",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-set-cursor-icon",
"core:window:allow-set-cursor-position",
"core:window:allow-set-ignore-cursor-events",
"core:window:allow-start-dragging",
"core:window:allow-set-progress-bar",
"core:window:allow-set-icon",
"core:window:allow-toggle-maximize",
"core:webview:allow-create-webview-window",
"core:webview:allow-print",
"updater:default",
"updater:allow-check",
"updater:allow-download",
"updater:allow-download-and-install",
"updater:allow-install",
"clipboard-manager:allow-clear",
"clipboard-manager:allow-write-text",
"clipboard-manager:allow-write-html",
"clipboard-manager:allow-write-image",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-read-image",
{
"identifier": "shell:allow-execute",
"allow": [
{
"args": [
"-desktop-sidecar",
"true",
"-datadir",
{
"validator": "[\\S+ ]"
}
],
"name": "binaries/seanime",
"sidecar": true
}
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,3 @@
pub const MAIN_WINDOW_LABEL: &str = "main";
pub const SPLASHSCREEN_WINDOW_LABEL: &str = "splashscreen";
pub const CRASH_SCREEN_WINDOW_LABEL: &str = "crash_screen";

View File

@@ -0,0 +1,193 @@
mod constants;
mod server;
#[cfg(desktop)]
mod tray;
use constants::MAIN_WINDOW_LABEL;
use std::sync::{Arc, Mutex};
#[cfg(target_os = "macos")]
use tauri::utils::TitleBarStyle;
use tauri::{Emitter, Listener, Manager};
use tauri_plugin_os;
pub fn run() {
let server_process = Arc::new(Mutex::new(
None::<tauri_plugin_shell::process::CommandChild>,
));
let server_process_for_setup = Arc::clone(&server_process);
let server_process_for_restart = Arc::clone(&server_process);
//
let is_shutdown = Arc::new(Mutex::new(false));
let is_shutdown_for_setup = Arc::clone(&is_shutdown);
let is_shutdown_for_restart = Arc::clone(&is_shutdown);
let server_started = Arc::new(Mutex::new(false));
let server_started_for_setup = Arc::clone(&server_started);
let server_started_for_restart = Arc::clone(&server_started);
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, _cmd, _args| {
if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
window.show().unwrap();
window.set_focus().unwrap();
}
}))
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_clipboard_manager::init())
.setup(move |app| {
#[cfg(all(desktop))]
{
let handle = app.handle();
tray::create_tray(handle)?;
}
let main_window = app.get_webview_window(MAIN_WINDOW_LABEL).unwrap();
main_window.hide().unwrap();
// Set overlay title bar only when building for macOS
#[cfg(target_os = "macos")]
main_window
.set_title_bar_style(TitleBarStyle::Overlay)
.unwrap();
// Hide the title bar on Windows
#[cfg(any(target_os = "windows"))]
main_window.set_decorations(false).unwrap();
// Open dev tools only when in dev mode
#[cfg(debug_assertions)]
{
main_window.open_devtools();
}
server::launch_seanime_server(
app.handle().clone(),
server_process_for_setup,
is_shutdown_for_setup,
server_started_for_setup,
);
let app_handle = app.handle().clone();
app.listen("restart-server", move |_| {
println!("EVENT restart-server");
let mut child_guard = server_process_for_restart.lock().unwrap();
if let Some(child) = child_guard.take() {
println!("Killing existing server process");
// Kill the existing server process
if let Err(e) = child.kill() {
eprintln!("Failed to kill server process: {}", e);
}
}
server::launch_seanime_server(
app_handle.clone(),
Arc::clone(&server_process_for_restart),
Arc::clone(&is_shutdown_for_restart),
Arc::clone(&server_started_for_restart),
);
});
let app_handle_1 = app.handle().clone();
let main_window_clone = main_window.clone();
main_window.listen("macos-activation-policy-accessory", move |_| {
println!("EVENT macos-activation-policy-accessory");
#[cfg(target_os = "macos")]
{
if let Err(e) = app_handle_1.set_activation_policy(tauri::ActivationPolicy::Accessory) {
eprintln!("Failed to set activation policy to accessory: {}", e);
} else {
if let Err(e) = main_window_clone.show() {
eprintln!("Failed to show main window: {}", e);
}
if let Err(e) = main_window_clone.set_fullscreen(true) {
eprintln!("Failed to set fullscreen: {}", e);
} else {
std::thread::sleep(std::time::Duration::from_millis(150));
if let Err(e) = main_window_clone.set_focus() {
eprintln!("Failed to set focus after fullscreen: {}", e);
}
main_window_clone.emit("macos-activation-policy-accessory-done", "").unwrap();
}
}
}
});
// main_window.on_window_event()
let app_handle_2 = app.handle().clone();
main_window.listen("macos-activation-policy-regular", move |_| {
println!("EVENT macos-activation-policy-regular");
#[cfg(target_os = "macos")]
app_handle_2
.set_activation_policy(tauri::ActivationPolicy::Regular)
.unwrap();
});
Ok(())
})
.build(tauri::generate_context!())
.expect("error while running tauri application")
.run({
let server_process_for_exit = Arc::clone(&server_process);
let is_shutdown_for_exit = Arc::clone(&is_shutdown);
move |app, event| {
let server_process_for_exit_ = Arc::clone(&server_process);
app.listen("kill-server", move |_| {
let mut child_guard = server_process_for_exit_.lock().unwrap();
if let Some(child) = child_guard.take() {
// Kill server process
if let Err(e) = child.kill() {
eprintln!("Failed to kill server process: {}", e);
}
}
});
match event {
tauri::RunEvent::WindowEvent {
label,
event: tauri::WindowEvent::CloseRequested { api, .. },
..
} => {
let is_shutdown_guard = is_shutdown_for_exit.lock().unwrap();
if label.as_str() == MAIN_WINDOW_LABEL && !*is_shutdown_guard {
println!("Main window close request");
// Hide the window when user clicks 'X'
let win = app.get_webview_window(label.as_str()).unwrap();
win.hide().unwrap();
// Prevent the window from being closed
api.prevent_close();
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory)
.unwrap();
}
}
// tauri::RunEvent::Exit => {
// let mut child_guard = server_process_for_exit.lock().unwrap();
// if let Some(child) = child_guard.take() {
// // Kill server process
// if let Err(e) = child.kill() {
// eprintln!("Failed to kill server process: {}", e);
// }
// }
// }
// The app is about to exit
tauri::RunEvent::ExitRequested { .. } => {
println!("Main window exit request");
let mut child_guard = server_process_for_exit.lock().unwrap();
if let Some(child) = child_guard.take() {
// Kill server process
if let Err(e) = child.kill() {
eprintln!("Failed to kill server process: {}", e);
}
}
}
_ => {}
}
}
});
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,12 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
#[cfg(target_os = "linux")]
{
std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1");
}
app_lib::run();
}

View File

@@ -0,0 +1,116 @@
use crate::constants::{CRASH_SCREEN_WINDOW_LABEL, MAIN_WINDOW_LABEL, SPLASHSCREEN_WINDOW_LABEL};
use std::sync::{Arc, Mutex};
use strip_ansi_escapes;
use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_shell::process::CommandEvent;
use tauri_plugin_shell::ShellExt;
use tokio::time::{sleep, Duration};
pub fn launch_seanime_server(
app: AppHandle,
child_process: Arc<Mutex<Option<tauri_plugin_shell::process::CommandChild>>>,
is_shutdown: Arc<Mutex<bool>>,
server_started: Arc<Mutex<bool>>,
) {
tauri::async_runtime::spawn(async move {
let main_window = app.get_webview_window(MAIN_WINDOW_LABEL).unwrap();
// let splashscreen = app.get_webview_window(SPLASHSCREEN_WINDOW_LABEL).unwrap();
// let crash_screen = app.get_webview_window(CRASH_SCREEN_WINDOW_LABEL).unwrap();
let mut sidecar_command = app.shell().sidecar("seanime").unwrap();
// Use test data dir during development
#[cfg(dev)]
{
sidecar_command = sidecar_command.args(["-datadir", env!("TEST_DATADIR")]);
}
sidecar_command = sidecar_command.args(["-desktop-sidecar", "true"]);
let (mut rx, child) = match sidecar_command.spawn() {
Ok(result) => result,
Err(e) => {
// Seanime server failed to open -> close splashscreen and display crash screen
if let Some(splashscreen) = app.get_webview_window(SPLASHSCREEN_WINDOW_LABEL) {
splashscreen.close().unwrap();
}
if let Some(crash_screen) = app.get_webview_window(CRASH_SCREEN_WINDOW_LABEL) {
crash_screen.show().unwrap();
}
app.emit(
"crash",
format!("The server failed to start: {}. Closing in 10 seconds.", e),
)
.expect("failed to emit event");
sleep(Duration::from_secs(10)).await;
std::process::exit(1);
}
};
// Store the child process
*child_process.lock().unwrap() = Some(child);
// let mut server_started = false;
// Read server terminal output
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line) => {
let line_without_colors = strip_ansi_escapes::strip(line);
match String::from_utf8(line_without_colors) {
Ok(line_str) => {
if !server_started.lock().unwrap().clone() {
if line_str.contains("Client connected") {
sleep(Duration::from_secs(2)).await;
*server_started.lock().unwrap() = true;
if let Some(splashscreen) = app.get_webview_window(SPLASHSCREEN_WINDOW_LABEL) {
splashscreen.close().unwrap();
}
main_window.maximize().unwrap();
main_window.show().unwrap();
}
}
// Emit the line to the main window
main_window
.emit("message", Some(format!("{}", line_str)))
.expect("failed to emit event");
println!("{}", line_str);
}
Err(_) => {}
}
}
CommandEvent::Terminated(status) => {
eprintln!(
"Seanime server process terminated with status: {:?} {:?}",
status, server_started.lock().unwrap()
);
*is_shutdown.lock().unwrap() = true;
// Only terminate the app if the desktop app hadn't launched
if !server_started.lock().unwrap().clone() {
if let Some(splashscreen) = app.get_webview_window(SPLASHSCREEN_WINDOW_LABEL) {
splashscreen.close().unwrap();
}
#[cfg(debug_assertions)]
{
main_window.close_devtools();
}
main_window.close().unwrap();
if let Some(crash_screen) = app.get_webview_window(CRASH_SCREEN_WINDOW_LABEL) {
crash_screen.show().unwrap();
}
app.emit("crash", format!("Seanime server process terminated with status: {}. Closing in 10 seconds.", status.code.unwrap_or(1))).expect("failed to emit event");
sleep(Duration::from_secs(10)).await;
app.exit(1);
}
break;
}
_ => {}
}
}
});
}

View File

@@ -0,0 +1,102 @@
use crate::constants::MAIN_WINDOW_LABEL;
use tauri::{
menu::{Menu, MenuItem},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
Manager, Runtime,
};
pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
let quit_i = MenuItem::with_id(app, "quit", "Quit Seanime", true, None::<&str>)?;
// let restart_i = MenuItem::with_id(app, "restart", "Restart Seanime", true, None::<&str>)?;
// let open_web_i = MenuItem::with_id(app, "open_web", "Open Web UI", true, None::<&str>)?;
let toggle_visibility_i = MenuItem::with_id(
app,
"toggle_visibility",
"Toggle visibility",
true,
None::<&str>,
)?;
let accessory_mode_i = MenuItem::with_id(
app,
"accessory_mode",
"Remove from dock",
true,
None::<&str>,
)?;
let mut items: Vec<&dyn tauri::menu::IsMenuItem<R>> = vec![&toggle_visibility_i, &quit_i];
#[cfg(target_os = "macos")]
{
items = vec![&toggle_visibility_i, &accessory_mode_i, &quit_i];
}
let menu = Menu::with_items(app, &items)?;
let _ = TrayIconBuilder::with_id("tray")
.icon(app.default_window_icon().unwrap().clone())
.menu(&menu)
.menu_on_left_click(false)
.on_menu_event(move |app, event| match event.id.as_ref() {
"quit" => {
app.exit(0);
}
// "restart" => app.restart(),
"toggle_visibility" => {
if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
if !window.is_visible().unwrap() {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Regular)
.unwrap();
} else {
let _ = window.hide();
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory)
.unwrap();
}
}
}
"accessory_mode" => {
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory)
.unwrap();
}
// "hide" => {
// if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
// if window.is_minimized().unwrap() {
// let _ = window.show();
// let _ = window.set_focus();
// #[cfg(target_os = "macos")]
// app.set_activation_policy(tauri::ActivationPolicy::Regular).unwrap();
// } else {
// let _ = window.hide();
// #[cfg(target_os = "macos")]
// app.set_activation_policy(tauri::ActivationPolicy::Accessory).unwrap();
// }
// }
// }
// Add more events here
_ => {}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up,
..
} = event
{
let app = tray.app_handle();
if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Regular)
.unwrap();
}
}
})
.build(app);
Ok(())
}

View File

@@ -0,0 +1,89 @@
{
"productName": "Seanime Desktop",
"version": "2.9.10",
"identifier": "app.seanime.desktop",
"build": {
"frontendDist": "../../web-desktop",
"devUrl": "http://127.0.0.1:43210"
},
"app": {
"withGlobalTauri": true,
"macOSPrivateApi": true,
"windows": [
{
"label": "main",
"title": "Seanime",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false,
"visible": false,
"hiddenTitle": true
},
{
"label": "splashscreen",
"title": "Seanime",
"width": 800,
"height": 600,
"resizable": false,
"decorations": false,
"url": "/splashscreen",
"hiddenTitle": true
},
{
"label": "crash_screen",
"title": "Seanime",
"width": 800,
"height": 600,
"resizable": false,
"decorations": false,
"url": "/splashscreen/crash",
"hiddenTitle": true,
"visible": false
}
],
"security": {
"csp": null
}
},
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDg4Q0RFQTc5NTQyRDU4RDYKUldUV1dDMVVlZXJOaU8xMlBhbU1xNG1IY2lLVG1oMXBnWm81VTNKem11N3EzcWk4NHI0SXhtbGkK",
"endpoints": [
"https://github.com/5rahim/seanime/releases/latest/download/latest.json"
]
}
},
"bundle": {
"active": true,
"createUpdaterArtifacts": true,
"targets": [
"appimage",
"nsis",
"app"
],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"linux": {
"appimage": {
"bundleMediaFramework": true
}
},
"windows": {
"nsis": {
"minimumWebview2Version": "110.0.1531.0"
}
},
"macOS": {
"signingIdentity": "-"
},
"externalBin": [
"binaries/seanime"
]
}
}