Schnittebenen 2D↔3D Phase 1+2, native Fenster, Hell/Dunkel-Theme, SWISSIMAGE-Import

- Schnittebenen: 3D-Live-Schnitt folgt der gewählten Grundriss-Schnittlinie
  (section3dCutId/sectionPlaneFromLevel), Schalter "Im 3D schneiden" im
  Objekt-Info, Doppelklick auf Schnittlinie springt in 2D-Schnittansicht,
  unsichtbare Ebenen blenden ihre Führungslinie aus
- Eigene native Tauri-Fenster für Kontext-Import/Zeichnungsebenen/
  Ebenen-Einstellungen/Ressourcen/Einstellungen + klassische Menüleiste
  (AppMenuBar) neben der Wortmarke
- Hell/Dunkel-Umschalter (Einstellungen → Darstellung), persistiert,
  flackerfrei vor erstem Render gesetzt
- SWISSIMAGE-Luftbild-Import (swisstopo WMS) als Kontext-Hintergrundebene
- UI-Politur: Werkzeug-Panel Symbole/Liste umschaltbar, Topbar-Quick-Access-
  Icons entfernt, Zahnrad→Einstellungen in Panel-Köpfen, Footerbar/
  Snap-Marker/Maß-HUD auf helle Pillen-Sprache umgestellt
- Neues Dachziegel-Material (RoofingTiles013A)
This commit is contained in:
2026-07-20 10:51:37 +02:00
parent 51e57c368b
commit b9e330ecb7
89 changed files with 7723 additions and 1848 deletions
+78
View File
@@ -0,0 +1,78 @@
// Eigenständiger React-Root für das native Standort-Import-Fenster (siehe
// contextImportWindow.ts für die Hauptfenster-Seite der Sync-Brücke). Von
// main.tsx gerendert, wenn die URL `?window=context-import` trägt.
//
// Rendert `ContextImportDialog` UNVERÄNDERT (windowed=true) — der Dialog
// selbst braucht keinerlei Host-/Store-Zugriff (reine `io/*`-Netzabrufe),
// daher hier kein Proxy-Pattern nötig wie bei den anderen Fenstern: nur
// `onImport`/`onSetGeoAnchor` laufen als einzelne Events ans Hauptfenster.
//
// Bezeichner englisch, UI-Text über i18n (CONVENTIONS.md).
import { useEffect, useState } from "react";
import { ContextImportDialog } from "../ui/ContextImportDialog";
import type { GeoAnchor } from "../ui/ContextImportDialog";
import type { ContextImportSyncState } from "./contextImportWindow";
import { t } from "../i18n";
export function ContextImportWindowApp() {
const [state, setState] = useState<ContextImportSyncState | null>(null);
useEffect(() => {
let disposed = false;
const unlisten: Array<() => void> = [];
void (async () => {
const { listen, emitTo } = await import("@tauri-apps/api/event");
if (disposed) return;
unlisten.push(
await listen<ContextImportSyncState>("context-import-sync-state", (e) =>
setState(e.payload),
),
);
await emitTo("main", "context-import-window-ready").catch(() => {});
})();
return () => {
disposed = true;
unlisten.forEach((u) => u());
};
}, []);
const send = (name: string, ...args: unknown[]) => {
void import("@tauri-apps/api/event").then(({ emitTo }) =>
emitTo("main", "context-import-command", { name, args }).catch(() => {}),
);
};
const onClose = () => {
void import("@tauri-apps/api/window").then(({ getCurrentWindow }) =>
getCurrentWindow().close(),
);
};
if (!state) {
return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100vh",
color: "var(--ink-dim, #8a8a8a)",
fontSize: 13,
}}
>
{t("resources.loading")}
</div>
);
}
return (
<ContextImportDialog
onImport={(objs) => send("onImport", objs)}
onClose={onClose}
geoAnchor={state.geoAnchor}
onSetGeoAnchor={(anchor: GeoAnchor | undefined) => send("onSetGeoAnchor", anchor)}
windowed
/>
);
}