Veröffentlichte Quellen
Quellen als ZIP herunterladen
CH-J Tools
Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.
CH-J / Server Manager
872 Dateien
core-20261030T220507Z-61e4558d889f
Hinzugefügt: · Erstellt:
1
//! Window factory: main, license and module windows with their initialization scripts2
//! (the scripts replace Electron preloads: they define `window.chjCore`, `window.licenseApi`3
//! or `window.chjModule`/`window.chjPlugin` before any page script runs).5
use crate::core::i18n::I18n;6
use crate::security::caller::{is_app_url, module_url_matches, LICENSE_LABEL, MAIN_LABEL};7
use crate::security::url_policy::is_allowed_external_url;8
use serde_json::{json, Value};9
use std::path::Path;10
use tauri::webview::{NewWindowResponse, PageLoadEvent};11
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindow, WebviewWindowBuilder};13
pub const CORE_BRIDGE: &str = include_str!("../bridges/core-bridge.js");14
pub const MODULE_BRIDGE: &str = include_str!("../bridges/module-bridge.js");15
pub const LICENSE_BRIDGE: &str = include_str!("../bridges/license-bridge.js");16
pub const BACKGROUND: tauri::window::Color = tauri::window::Color(0x07, 0x11, 0x1f, 0xff);18
/// Node-style platform name used by the 0.1.0 UI (`platform-darwin`, …).19
pub fn platform_name() -> &'static str {20
match std::env::consts::OS {21
"macos" => "darwin",22
"windows" => "win32",23
other => other,24
}25
}27
/// Frameless windows with in-page controls on Windows/Linux (Electron used `titleBarOverlay`).28
pub fn frameless() -> bool {29
matches!(platform_name(), "win32" | "linux")30
}32
pub fn init_script(window: &str, module_id: Option<&str>, i18n: Value, bridge: &str) -> String {33
let boot = json!({ "platform": platform_name(), "window": window, "moduleId": module_id });34
let script = format!(35
"(function(){{var d=function(n,v){{Object.defineProperty(globalThis,n,{{value:v,writable:false,configurable:false,enumerable:false}});}};d('__CHJ_BOOT__',Object.freeze({boot}));d('__CHJ_I18N__',{i18n});}})();\n{bridge}"36
);37
smoke_extension(script)38
}40
#[cfg(feature = "smoke")]41
fn smoke_extension(script: String) -> String {42
if let Ok(dir) = std::env::var("CHJ_SMOKE_DUMP") {43
let _ = std::fs::write(std::path::Path::new(&dir).join(format!("init-{}.js", script.len())), &script);44
}45
match std::env::var("CHJ_SMOKE_SCRIPT").ok().and_then(|path| std::fs::read_to_string(path).ok()) {46
Some(scenario) => format!(47
"try {{\n{script}\n}} catch (e) {{ window.__TAURI_INTERNALS__.invoke('smoke_report', {{ payload: {{ step: 'bridge', failure: String(e && e.stack || e) }} }}); }}\n;(function(){{\n{scenario}\n}})();"48
),49
None => script,50
}51
}53
#[cfg(not(feature = "smoke"))]54
fn smoke_extension(script: String) -> String {55
script56
}58
fn open_external_if_allowed(app: &AppHandle, url: &url::Url) {59
if is_allowed_external_url(url.as_str()) {60
use tauri_plugin_opener::OpenerExt;61
let _ = app.opener().open_url(url.as_str(), None::<&str>);62
}63
}65
pub fn create_main_window(app: &AppHandle, i18n: &I18n) -> tauri::Result<WebviewWindow> {66
let handle = app.clone();67
let handle_nav = app.clone();68
let mut builder = WebviewWindowBuilder::new(app, MAIN_LABEL, WebviewUrl::App("index.html".into()))69
.title("CH-J Server Manager")70
.inner_size(1240.0, 780.0)71
.min_inner_size(960.0, 640.0)72
.background_color(BACKGROUND)73
.visible(false)74
.initialization_script(init_script(MAIN_LABEL, None, i18n.main_window_data(), CORE_BRIDGE))75
.disable_drag_drop_handler()76
.on_navigation(move |url| {77
if is_app_url(url) {78
return true;79
}80
open_external_if_allowed(&handle_nav, url);81
false82
})83
.on_new_window(move |url, _features| {84
open_external_if_allowed(&handle, &url);85
NewWindowResponse::Deny86
})87
.on_page_load(|window, payload| {88
if cfg!(debug_assertions) {89
eprintln!("[page] {} {:?} {}", window.label(), payload.event(), payload.url());90
}91
if payload.event() == PageLoadEvent::Finished {92
let _ = window.show();93
let _ = window.set_focus();94
auto_update_check(&window);95
}96
});97
if frameless() {98
builder = builder.decorations(false).shadow(true);99
}100
builder.build()101
}103
/// Automatic update check after the main window loaded (if enabled), like 0.1.0.104
fn auto_update_check(window: &WebviewWindow) {105
let Some(core) = window.app_handle().state::<crate::core::app::CoreSlot>().try_get() else { return };106
if core.config.get()["updates"]["autoCheck"] != true {107
return;108
}109
#[cfg(feature = "smoke")]110
if std::env::var("CHJ_SMOKE_NO_NETWORK").is_ok() {111
return;112
}113
tauri::async_runtime::spawn(async move {114
match core.services.updates.check().await {115
Ok(state) => core.emit_main("core:updateState", state),116
Err(error) => core.logger.warn("Automatic update check failed.", serde_json::json!({ "message": error.message })),117
}118
});119
}121
pub fn create_license_window(app: &AppHandle, i18n: &I18n, language: &str) -> tauri::Result<WebviewWindow> {122
let mut data = i18n.license_window_data();123
data["initialLanguage"] = Value::String(language.to_string());124
WebviewWindowBuilder::new(app, LICENSE_LABEL, WebviewUrl::App("license.html".into()))125
.title("CH-J Server Manager — License")126
.inner_size(900.0, 760.0)127
.min_inner_size(640.0, 520.0)128
.background_color(BACKGROUND)129
.visible(false)130
.initialization_script(init_script(LICENSE_LABEL, None, data, LICENSE_BRIDGE))131
.disable_drag_drop_handler()132
.on_navigation(|url| is_app_url(url))133
.on_new_window(|_, _| NewWindowResponse::Deny)134
.on_page_load(|window, payload| {135
if payload.event() == PageLoadEvent::Finished {136
let _ = window.show();137
let _ = window.set_focus();138
}139
})140
.build()141
}143
/// URL of a module page. Windows (WebView2) serves custom schemes as `http://<scheme>.localhost/`.144
pub fn module_url(module_id: &str, entry: &str) -> url::Url {145
let text = if cfg!(windows) {146
format!("http://chj-module.localhost/{module_id}/{entry}")147
} else {148
format!("chj-module://{module_id}/{entry}")149
};150
url::Url::parse(&text).expect("valid module URL")151
}153
pub struct ModuleWindowSpec<'a> {154
pub label: &'a str,155
pub module_id: &'a str,156
pub name: &'a str,157
pub entry: &'a str,158
pub root: &'a Path,159
pub data_directory: std::path::PathBuf,160
}162
pub fn create_module_window(app: &AppHandle, i18n: &I18n, spec: ModuleWindowSpec<'_>) -> tauri::Result<WebviewWindow> {163
let id_for_nav = spec.module_id.to_string();164
let mut builder = WebviewWindowBuilder::new(app, spec.label, WebviewUrl::CustomProtocol(module_url(spec.module_id, spec.entry)))165
.title(format!("{} · CH-J Server Manager", spec.name))166
.inner_size(980.0, 700.0)167
.min_inner_size(760.0, 520.0)168
.background_color(BACKGROUND)169
.visible(false)170
.incognito(true)171
.data_directory(spec.data_directory)172
.initialization_script(init_script(spec.label, Some(spec.module_id), i18n.module_window_data(spec.root), MODULE_BRIDGE))173
.disable_drag_drop_handler()174
.on_navigation(move |url| module_url_matches(url, &id_for_nav))175
.on_new_window(|_, _| NewWindowResponse::Deny)176
.on_page_load(|window, payload| {177
if payload.event() == PageLoadEvent::Finished {178
if let Some(core) = window.app_handle().state::<crate::core::app::CoreSlot>().try_get() {179
core.services.modules.on_loaded(&core, window.label());180
}181
}182
});183
if let Some(main) = app.get_webview_window(MAIN_LABEL) {184
builder = builder.parent(&main)?;185
}186
if frameless() {187
builder = builder.decorations(false).shadow(true);188
}189
builder.build()190
}SHA-256
82a09466d30c288d248b69e65ddd14d01f56de3e986b8f25ca767a8c06a7a952CH-J Proprietary Software License 1.14 · Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.
SHA-256 des Archivs
77380d4805485ffa4027fbe7858b5836f197a4aa9d52358a43e2d12d018744df