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:
windowChrome.ts Hinzugefügt: Datum nicht erfasst 5,6 KB · 96 Zeilen
Code Datei herunterladen
1/**
2 * Frameless windows on Windows/Linux. Electron 0.1.0 used `titleBarStyle: hidden` with a
3 * native `titleBarOverlay` (min/max/close drawn by Chromium in the top-right 40 px).
4 * Tauri has no overlay, so the same area gets three equivalent buttons here, and the
5 * existing drag regions keep working through a Core command.
6 */
7import { windowControls } from "../core/window";
8import { call, subscribe } from "../transport";
9import { bootInfo } from "./boot";
11const CSS = `
12chj-window-controls { position: fixed !important; top: 0 !important; right: 0 !important; height: 40px !important; z-index: 2147483647 !important; display: flex !important; }
13chj-window-controls button { width: 46px; height: 40px; border: 0; margin: 0; padding: 0; display: grid; place-items: center; color: #d9eaff; background: transparent; cursor: default; -webkit-app-region: no-drag; }
14chj-window-controls button:hover { background: rgba(217, 234, 255, .12); }
15chj-window-controls button.close:hover { background: #c42b1c; color: #fff; }
16chj-window-controls svg { width: 10px; height: 10px; stroke: currentColor; fill: none; stroke-width: 1.1; shape-rendering: crispEdges; }
17`;
19const ICONS: Record<string, string> = {
20 minimize: '<svg viewBox="0 0 10 10"><path d="M0 5.5h10"/></svg>',
21 toggleMaximize: '<svg viewBox="0 0 10 10"><rect x="0.5" y="0.5" width="9" height="9"/></svg>',
22 close: '<svg viewBox="0 0 10 10"><path d="M0 0l10 10M10 0L0 10"/></svg>'
23};
25function isDragTarget(target: EventTarget | null, selectors: string[]): boolean {
26 if (!(target instanceof Element)) return false;
27 if (target.closest("button, a, input, select, textarea, [contenteditable], chj-window-controls")) return false;
28 return selectors.some((selector) => Boolean(target.closest(selector)));
31export function installWindowChrome(platform: string, dragSelectors: string[], dragRoots: () => Array<Document | ShadowRoot>): void {
32 // This handshake runs on WKWebView, WebView2 and WebKitGTK, including native macOS chrome.
33 window.addEventListener("chj:query-dirty", (event) => {
34 let dirty = true;
35 try {
36 const page = window as Window & { __CHJ_HAS_UNSAVED_CHANGES__?: () => boolean; isDirty?: () => boolean };
37 // NGINX uses a classic script with a global isDirty(), including disconnected drafts.
38 const query = page.__CHJ_HAS_UNSAVED_CHANGES__ || (bootInfo().moduleId === "chj.nginx-manager" ? page.isDirty : undefined);
39 dirty = query ? Boolean(query()) : false;
40 } catch { /* A failed dirty query preserves the draft. */ }
41 void call("window_control", "window:control", { action: "closeQueryReply", queryId: (event as CustomEvent<number>).detail, dirty }).catch(() => {});
42 });
43 if (platform !== "win32" && platform !== "linux") return;
44 const mount = () => {
45 if (document.querySelector("chj-window-controls")) return;
46 const host = document.createElement("chj-window-controls");
47 const shadow = host.attachShadow({ mode: "closed" });
48 const style = document.createElement("style");
49 style.textContent = CSS.replace(/chj-window-controls /g, ":host ").replace(/chj-window-controls \{/g, ":host {");
50 shadow.append(style);
51 let language = document.documentElement.lang || "en";
52 let maximized = false;
53 const buttons = new Map<string, HTMLButtonElement>();
54 const translate = () => {
55 const catalogs = globalThis.__CHJ_I18N__?.catalogs?.chrome || {};
56 for (const [action, button] of buttons) {
57 const key = `chrome.${action === "toggleMaximize" ? (maximized ? "restore" : "maximize") : action}`;
58 const label = catalogs[language]?.[key] || catalogs[language.split("-")[0]]?.[key] || catalogs.en?.[key] || key;
59 button.setAttribute("aria-label", label);
60 button.title = label;
61 if (action === "toggleMaximize") button.setAttribute("aria-pressed", String(maximized));
62 }
63 };
64 const updateMaximized = () => { void windowControls.isMaximized().then((value) => { maximized = value; translate(); }).catch(() => {}); };
65 subscribe<string>("module:ui:languageChanged", (value) => { language = value; translate(); });
66 new MutationObserver(() => { language = document.documentElement.lang || language; translate(); }).observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] });
67 window.addEventListener("resize", updateMaximized);
68 for (const action of ["minimize", "toggleMaximize", "close"] as const) {
69 const button = document.createElement("button");
70 button.type = "button";
71 button.className = action === "close" ? "close" : action;
72 buttons.set(action, button);
73 button.innerHTML = ICONS[action];
74 button.addEventListener("click", () => { void windowControls.perform(action).then(updateMaximized).catch(() => {}); });
75 shadow.append(button);
76 }
77 translate();
78 updateMaximized();
79 const outer = document.createElement("style");
80 outer.textContent = "chj-window-controls { position: fixed; top: 0; right: 0; height: 40px; z-index: 2147483647; display: flex; }";
81 document.head.append(outer);
82 document.body.append(host);
83 const handler = (event: Event) => {
84 const mouse = event as MouseEvent;
85 if (mouse.button !== 0) return;
86 const path = mouse.composedPath();
87 const target = (path[0] as EventTarget) || mouse.target;
88 if (!isDragTarget(target, dragSelectors)) return;
89 if (mouse.detail === 2) void windowControls.perform("toggleMaximize").catch(() => {});
90 else void windowControls.perform("startDrag").catch(() => {});
91 };
92 for (const root of dragRoots()) root.addEventListener("mousedown", handler);
93 };
94 if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", mount, { once: true });
95 else mount();
SHA-256d9c8ff944d86c296d2579e6545af6892c0a3635b6b359d879edba0ad431f6dbb

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