Native macOS-Titelleiste: titleBarStyle Overlay (echte Ampeln + gerundete Ecken), Zeile links eingerückt; WindowControls Electron-only
This commit is contained in:
@@ -15,7 +15,8 @@
|
|||||||
"width": 1400,
|
"width": 1400,
|
||||||
"height": 900,
|
"height": 900,
|
||||||
"dragDropEnabled": false,
|
"dragDropEnabled": false,
|
||||||
"decorations": false
|
"titleBarStyle": "Overlay",
|
||||||
|
"hiddenTitle": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
@@ -174,6 +174,12 @@ body {
|
|||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: transparent transparent;
|
scrollbar-color: transparent transparent;
|
||||||
}
|
}
|
||||||
|
/* Tauri/macOS: native Ampeln (titleBarStyle "Overlay") liegen oben links über
|
||||||
|
dem Inhalt → Zeile links einrücken, damit `dossier.` frei bleibt (~3 Ampeln +
|
||||||
|
Rand). */
|
||||||
|
.topbar.topbar-native-inset {
|
||||||
|
padding-left: 78px;
|
||||||
|
}
|
||||||
/* Sehr ruhige eigene Scrollleiste (WebKit/Blink): Thumb nur beim Scrollen
|
/* Sehr ruhige eigene Scrollleiste (WebKit/Blink): Thumb nur beim Scrollen
|
||||||
(`is-scrolling`, von TopBar gesetzt) oder beim Überfahren sichtbar. */
|
(`is-scrolling`, von TopBar gesetzt) oder beim Überfahren sichtbar. */
|
||||||
.topbar::-webkit-scrollbar {
|
.topbar::-webkit-scrollbar {
|
||||||
|
|||||||
+17
-7
@@ -1298,14 +1298,24 @@ export function TopBar({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Unter Tauri (native macOS-Ampeln oben links via titleBarStyle "Overlay")
|
||||||
|
// braucht die Zeile links Platz, damit `dossier.` nicht darunter liegt.
|
||||||
|
const tauriNativeTitlebar =
|
||||||
|
typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// `data-tauri-drag-region`: die randlose (decorations:false) Titelleiste ist
|
// `data-tauri-drag-region`: die Titelleiste ist selbst der Fenster-Ziehbereich
|
||||||
// selbst der Fenster-Ziehbereich. Kind-Buttons/Dropdowns bleiben klickbar;
|
// (macOS titleBarStyle "Overlay"). Kind-Buttons/Dropdowns bleiben klickbar;
|
||||||
// nur die leeren Zonen ziehen das Fenster.
|
// nur die leeren Zonen ziehen das Fenster. Unter Tauri bekommt die Zeile links
|
||||||
<header className="topbar" ref={headerRef} data-tauri-drag-region>
|
// einen Inset, damit `dossier.` nicht unter den nativen macOS-Ampeln liegt.
|
||||||
{/* Fensterknöpfe (Schliessen/Minimieren/Maximieren) GANZ LINKS — die
|
<header
|
||||||
randlose Titelleiste beginnt mit ihnen (macOS-Konvention). Nur unter
|
className={`topbar${tauriNativeTitlebar ? " topbar-native-inset" : ""}`}
|
||||||
Electron/Tauri sichtbar (sonst null). */}
|
ref={headerRef}
|
||||||
|
data-tauri-drag-region
|
||||||
|
>
|
||||||
|
{/* Fensterknöpfe NUR unter Electron (randloses frame:false-Fenster) — unter
|
||||||
|
Tauri liefern die NATIVEN macOS-Ampeln Schliessen/Minimieren/Maximieren,
|
||||||
|
daher rendert WindowControls dort null. */}
|
||||||
<WindowControls />
|
<WindowControls />
|
||||||
|
|
||||||
{/* Kleine Wortmarke „dossier" (OCS-Stil, klein/kleingeschrieben) + Quick-
|
{/* Kleine Wortmarke „dossier" (OCS-Stil, klein/kleingeschrieben) + Quick-
|
||||||
|
|||||||
+11
-53
@@ -1,75 +1,33 @@
|
|||||||
// Eigene Fenstersteuerung (Minimieren/Maximieren/Schliessen) für das RANDLOSE
|
// Eigene Fenstersteuerung (Minimieren/Maximieren/Schliessen) für das randlose
|
||||||
// Fenster — sowohl Electron (frame:false, s. scripts/electron-main.cjs) ALS AUCH
|
// ELECTRON-Fenster (frame:false, s. scripts/electron-main.cjs) — dort gibt es
|
||||||
// Tauri (decorations:false, s. tauri.conf.json). Ohne native Titelleiste gibt es
|
// ohne native Titelleiste sonst keine Fenstersteuerung.
|
||||||
// sonst keine Möglichkeit, das Fenster zu steuern.
|
|
||||||
//
|
//
|
||||||
// Rendert NUR, wenn eine Fenster-API vorhanden ist: unter Electron
|
// Unter TAURI werden die NATIVEN macOS-Ampeln verwendet (tauri.conf.json:
|
||||||
// `window.dossierWindow` (Preload), unter Tauri `getCurrentWindow()`
|
// titleBarStyle "Overlay" + hiddenTitle, native Dekoration mit gerundeten Ecken),
|
||||||
// (@tauri-apps/api/window, dynamisch geladen wie in nativeSync.ts). Im reinen
|
// daher rendert diese Komponente dort NICHT (kein `window.dossierWindow`) — sonst
|
||||||
// Browser-Dev-Modus (?vite) bleibt die Komponente unsichtbar (`null`) — dort
|
// gäbe es doppelte Knöpfe. Im Browser-Dev-Modus ebenfalls unsichtbar (`null`).
|
||||||
// steuert der Browser das Fenster. Die Tauri-Permissions liegen in
|
|
||||||
// src-tauri/capabilities/default.json (core:window:allow-minimize/…/close).
|
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
/** Gemeinsame, minimale Fenster-API (Electron-Preload ODER Tauri-Adapter). */
|
/** Von electron-preload.cjs via contextBridge injiziert (nur unter Electron). */
|
||||||
interface WindowApi {
|
interface DossierWindowApi {
|
||||||
minimize: () => void;
|
minimize: () => void;
|
||||||
toggleMaximize: () => void;
|
toggleMaximize: () => void;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
isMaximized: () => Promise<boolean>;
|
isMaximized: () => Promise<boolean>;
|
||||||
/** Abo auf Maximiert-Wechsel; liefert eine Abmelde-Funktion. */
|
|
||||||
onMaximizedChange: (callback: (isMaximized: boolean) => void) => () => void;
|
onMaximizedChange: (callback: (isMaximized: boolean) => void) => () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
dossierWindow?: WindowApi;
|
dossierWindow?: DossierWindowApi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tauri-Laufzeit erkannt (analog nativeSync.ts). */
|
|
||||||
function isTauri(): boolean {
|
|
||||||
return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function WindowControls() {
|
export function WindowControls() {
|
||||||
const [api, setApi] = useState<WindowApi | null>(null);
|
const api = window.dossierWindow;
|
||||||
const [maximized, setMaximized] = useState(false);
|
const [maximized, setMaximized] = useState(false);
|
||||||
|
|
||||||
// Passende Fenster-API einmalig auflösen: Electron synchron, Tauri asynchron
|
|
||||||
// (dynamischer Import, damit Browser-/Build-Pfad ohne @tauri-apps grün bleibt).
|
|
||||||
useEffect(() => {
|
|
||||||
if (window.dossierWindow) {
|
|
||||||
setApi(window.dossierWindow);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isTauri()) return;
|
|
||||||
let disposed = false;
|
|
||||||
import(/* @vite-ignore */ "@tauri-apps/api/window")
|
|
||||||
.then(({ getCurrentWindow }) => {
|
|
||||||
if (disposed) return;
|
|
||||||
const w = getCurrentWindow();
|
|
||||||
setApi({
|
|
||||||
minimize: () => void w.minimize(),
|
|
||||||
toggleMaximize: () => void w.toggleMaximize(),
|
|
||||||
close: () => void w.close(),
|
|
||||||
isMaximized: () => w.isMaximized(),
|
|
||||||
onMaximizedChange: (cb) => {
|
|
||||||
// Bei jeder Größenänderung den Maximiert-Zustand neu melden.
|
|
||||||
const unlisten = w.onResized(() => {
|
|
||||||
void w.isMaximized().then(cb);
|
|
||||||
});
|
|
||||||
return () => void unlisten.then((fn) => fn());
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
return () => {
|
|
||||||
disposed = true;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!api) return;
|
if (!api) return;
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user