CH-J ToolsSerververwaltung über SSH
Menü
Veröffentlichte Quellen

CH-J Tools

Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.

Quellen als ZIP herunterladen
CH-J / Server Manager 872 Dateien
core-20261030T220507Z-61e4558d889f Hinzugefügt: · Erstellt:
windows.rs Hinzugefügt: Datum nicht erfasst 7,7 KB · 190 Zeilen
Code Datei herunterladen
1//! Window factory: main, license and module windows with their initialization scripts
2//! (the scripts replace Electron preloads: they define `window.chjCore`, `window.licenseApi`
3//! or `window.chjModule`/`window.chjPlugin` before any page script runs).
5use crate::core::i18n::I18n;
6use crate::security::caller::{is_app_url, module_url_matches, LICENSE_LABEL, MAIN_LABEL};
7use crate::security::url_policy::is_allowed_external_url;
8use serde_json::{json, Value};
9use std::path::Path;
10use tauri::webview::{NewWindowResponse, PageLoadEvent};
11use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
13pub const CORE_BRIDGE: &str = include_str!("../bridges/core-bridge.js");
14pub const MODULE_BRIDGE: &str = include_str!("../bridges/module-bridge.js");
15pub const LICENSE_BRIDGE: &str = include_str!("../bridges/license-bridge.js");
16pub 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`, …).
19pub fn platform_name() -> &'static str {
20 match std::env::consts::OS {
21 "macos" => "darwin",
22 "windows" => "win32",
23 other => other,
24 }
27/// Frameless windows with in-page controls on Windows/Linux (Electron used `titleBarOverlay`).
28pub fn frameless() -> bool {
29 matches!(platform_name(), "win32" | "linux")
32pub 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)
40#[cfg(feature = "smoke")]
41fn 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 }
53#[cfg(not(feature = "smoke"))]
54fn smoke_extension(script: String) -> String {
55 script
58fn 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 }
65pub 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 false
82 })
83 .on_new_window(move |url, _features| {
84 open_external_if_allowed(&handle, &url);
85 NewWindowResponse::Deny
86 })
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()
103/// Automatic update check after the main window loaded (if enabled), like 0.1.0.
104fn 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 });
121pub 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()
143/// URL of a module page. Windows (WebView2) serves custom schemes as `http://<scheme>.localhost/`.
144pub 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")
153pub 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,
162pub 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()
SHA-25682a09466d30c288d248b69e65ddd14d01f56de3e986b8f25ca767a8c06a7a952

CH-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 Archivs77380d4805485ffa4027fbe7858b5836f197a4aa9d52358a43e2d12d018744df
Dateien