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
/**2
* Frameless windows on Windows/Linux. Electron 0.1.0 used `titleBarStyle: hidden` with a3
* 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 the5
* existing drag regions keep working through a Core command.6
*/7
import { windowControls } from "../core/window";8
import { call, subscribe } from "../transport";9
import { bootInfo } from "./boot";11
const CSS = `12
chj-window-controls { position: fixed !important; top: 0 !important; right: 0 !important; height: 40px !important; z-index: 2147483647 !important; display: flex !important; }13
chj-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; }14
chj-window-controls button:hover { background: rgba(217, 234, 255, .12); }15
chj-window-controls button.close:hover { background: #c42b1c; color: #fff; }16
chj-window-controls svg { width: 10px; height: 10px; stroke: currentColor; fill: none; stroke-width: 1.1; shape-rendering: crispEdges; }17
`;19
const 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
};25
function 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)));29
}31
export 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();96
}SHA-256
d9c8ff944d86c296d2579e6545af6892c0a3635b6b359d879edba0ad431f6dbbCH-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