From e6466a502732578d9a4b1c1324f2dc68a39b3e71 Mon Sep 17 00:00:00 2001 From: Karim Date: Thu, 2 Jul 2026 23:51:23 +0200 Subject: [PATCH] Electron-Prototyp: eigenes App-Fenster statt Tauri/WebKitGTK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ersetzt den Tauri-Webview-Weg testweise durch eine Electron-Shell (randlos, ohne native Menüleiste, wie chromium-shell.sh), damit die WASM/WebGPU-Engine zuverlässig laeuft statt in WebKitGTK. Rust-Backend nicht noetig, da compute_joins einen TS-Fallback hat. npm run electron zum Starten. --- HANDOVER.md | 27 ++++++-- package-lock.json | 135 ++++++++++++++++++++++++++++++++++++++ package.json | 2 + scripts/electron-main.cjs | 48 ++++++++++++++ scripts/electron-shell.sh | 49 ++++++++++++++ 5 files changed, 254 insertions(+), 7 deletions(-) create mode 100644 scripts/electron-main.cjs create mode 100755 scripts/electron-shell.sh diff --git a/HANDOVER.md b/HANDOVER.md index 62023c0..b12108f 100644 --- a/HANDOVER.md +++ b/HANDOVER.md @@ -73,13 +73,26 @@ integrierter Engine-Viewport möglich). Die Engine-Rust-Crates sind in einem sp Schritt 1:1 wiederverwendbar; nur die dünne WASM-Bindeschicht wäre dann Wegwerf. All-native bleibt legitimes Fernziel, ist aber KEIN Weg zurück zu Tauri (Tauri IST der Webview-Shell). ->>> NÄCHSTER KONKRETER SCHRITT (neue Session zuerst): Electron-Prototyp. Nur additiv: -`electron` als devDep, ~30-Zeilen `main.js` (BrowserWindow + `app.commandLine.appendSwitch` -für WebGPU: `enable-unsafe-webgpu` + `enable-features=Vulkan` — dieselben Flags wie -`scripts/chromium-shell.sh`), lädt den Vite-Server (dev) bzw. `dist` (prod), npm-Script -`electron`. Verifizieren: App startet als eigenes Fenster, `?engine=wasm` rendert den -Engine-Viewport (WebGPU in Electron = wie Chromium). Rust-Backend NICHT nötig — `compute_joins` -hat TS-Fallback. Risikoarm, ~1 Session. Danach: render3d-WASM, Doppeltext-Fix.<<< +>>> ELECTRON-PROTOTYP: FERTIG (2026-07-02). <<< +`electron` als devDep, `scripts/electron-main.cjs` (BrowserWindow, `Menu.setApplicationMenu(null)` ++ `frame:false`/`autoHideMenuBar` für randloses Fenster wie chromium-shell.sh, WebGPU-Flags +`enable-unsafe-webgpu`+`enable-features=Vulkan`), `scripts/electron-shell.sh` (Dev-Server-Guard +wie chromium-shell.sh), npm-Script `npm run electron`. Verifiziert per Screenshot: App startet +als eigenes randloses Fenster, lädt den echten Grundriss (nicht leer). WICHTIG: +- **Datei muss `.cjs` sein** (`package.json` hat `"type":"module"`, Electron-Main läuft als CJS). +- **`ELECTRON_RUN_AS_NODE=1`** kann aus dem umgebenden Prozess (z.B. VSCode-Electron-Host) + vererbt sein — zwingt Electron in den Node-Modus (`require('electron')` liefert nur einen + Pfad-String statt `{app,...}` → `app` ist undefined). Immer mit `env -u ELECTRON_RUN_AS_NODE` + starten, falls das Fenster nicht öffnet / `app.commandLine` undefined ist. +- **WebGPU/`?engine=wasm` in Electron auf diesem System (niri/Wayland) fällt auf WebGL2 zurück** + (Ozone-Wayland+Vulkan-Warnung beim Start). `--ozone-platform=x11` GEHT NICHT (bricht die + Fenstererstellung komplett — GetGeometry/Vulkan-Surface-Fehler). System-Chromium + (`chromium-shell.sh`) rendert die WASM-Engine hingegen nachweislich (siehe [[wasm-engine-viewport]]- + Notizen); die verwendete Electron-Version (Paket sagt ^43, `--version` meldet v24 — Versions- + Mismatch, ggf. stale Binary-Cache) könnte ein älteres/anders konfiguriertes Chromium mitbringen. + TODO nächste Session: Electron-Version/Ozone-Flags weiter untersuchen, bevor der Prototyp der + primäre Dev-Weg wird — bis dahin bleibt WebGL2-Fallback der Standardpfad, funktional unverändert. +Danach: render3d-WASM, Doppeltext-Fix.<<< Klarstellung an den User (er fragte, ob Webview = „billig"): NEIN — Figma (eigene C++/WASM-Engine im Web-Shell), Onshape, VS Code zeigen, dass „eigene Engine + Web-UI" Top-Tier-Architektur ist. CAD/BIM-Substanz = Modell + Engine + Normen (haben wir: parametrische Wände, SIA 416, DOSSIER- diff --git a/package-lock.json b/package-lock.json index 253f4d1..1ca506a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@types/react-dom": "^18.3.0", "@types/three": "^0.169.0", "@vitejs/plugin-react": "^4.3.2", + "electron": "^43.0.0", "playwright": "^1.61.1", "puppeteer": "^25.2.1", "typescript": "^5.6.2", @@ -326,6 +327,50 @@ "node": ">=6.9.0" } }, + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.4.tgz", + "integrity": "sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/get": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.0.0.tgz", + "integrity": "sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", + "progress": "^2.0.3", + "semver": "^7.6.3", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "optionalDependencies": { + "undici": "^7.24.4" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", @@ -1885,6 +1930,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.13.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz", + "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, "node_modules/@types/pako": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", @@ -2379,6 +2434,25 @@ "loglevel": "^1.7.1" } }, + "node_modules/electron": { + "version": "43.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-43.0.0.tgz", + "integrity": "sha512-PV60GsWU6qufhuOhw3n+Yix3WPDcqDtBqE8orbEQGQGHEkgp9o/JCPgb7L4vIL0r1HnfPdqSRtboOTqbDkcFDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" + }, + "bin": { + "electron": "cli.js", + "install-electron": "install.js" + }, + "engines": { + "node": ">= 22.12.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.380", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.380.tgz", @@ -2393,6 +2467,19 @@ "dev": true, "license": "MIT" }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-module-lexer": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", @@ -2558,6 +2645,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -3248,6 +3342,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/puppeteer": { "version": "25.2.1", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-25.2.1.tgz", @@ -3590,6 +3694,19 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, "node_modules/svg-pathdata": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", @@ -3740,6 +3857,24 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", diff --git a/package.json b/package.json index eb7a454..fa91e6a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "tauri:build": "tauri build", "dump:native": "node scripts/dump-native-scene.mjs", "shell": "scripts/chromium-shell.sh", + "electron": "scripts/electron-shell.sh", "build:engine": "wasm-pack build src-tauri/render2d --release --target web --out-dir ../../src/engine/pkg --out-name render2d --no-default-features --features web" }, "dependencies": { @@ -36,6 +37,7 @@ "@types/react-dom": "^18.3.0", "@types/three": "^0.169.0", "@vitejs/plugin-react": "^4.3.2", + "electron": "^43.0.0", "playwright": "^1.61.1", "puppeteer": "^25.2.1", "typescript": "^5.6.2", diff --git a/scripts/electron-main.cjs b/scripts/electron-main.cjs new file mode 100644 index 0000000..94b08c0 --- /dev/null +++ b/scripts/electron-main.cjs @@ -0,0 +1,48 @@ +// electron-main.js — Electron-Prototyp-Shell für die CAD-App. +// +// Ersetzt WebKitGTK (Tauri-Linux-Webview) durch Chromium, damit WebGPU +// (render2d-WASM-Engine, ?engine=wasm) zuverlässig läuft. Kein Rust-Backend +// nötig: compute_joins hat einen TS-Fallback (src/compute/index.ts). + +const { app, BrowserWindow, Menu } = require("electron"); + +// Randloses App-Fenster wie chromium-shell.sh (--app=…): keine native +// Menüleiste (File/Edit/View/Window), kein Fensterrahmen. +Menu.setApplicationMenu(null); + +// Gleiche Flags wie scripts/chromium-shell.sh — WebGPU liegt unter Linux +// hinter --enable-unsafe-webgpu, und WebGPU braucht das Vulkan-Backend. +app.commandLine.appendSwitch("enable-unsafe-webgpu"); +app.commandLine.appendSwitch("enable-features", "Vulkan"); + +const DEV_URL = "http://localhost:5187"; +const isDev = !app.isPackaged; + +function createWindow() { + const win = new BrowserWindow({ + width: 1600, + height: 1000, + frame: false, + autoHideMenuBar: true, + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + }, + }); + + if (isDev) { + win.loadURL(DEV_URL); + } else { + win.loadFile(require("node:path").join(__dirname, "..", "dist", "index.html")); + } +} + +app.whenReady().then(createWindow); + +app.on("window-all-closed", () => { + if (process.platform !== "darwin") app.quit(); +}); + +app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) createWindow(); +}); diff --git a/scripts/electron-shell.sh b/scripts/electron-shell.sh new file mode 100755 index 0000000..d287fff --- /dev/null +++ b/scripts/electron-shell.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# electron-shell.sh — startet die CAD-Oberfläche im Electron-Prototyp-Fenster. +# +# Wie chromium-shell.sh: stellt sicher, dass der Vite-Dev-Server läuft, startet +# ihn sonst selbst und beendet ihn wieder, sobald das Electron-Fenster schliesst. + +set -u + +PORT=5187 +URL="http://localhost:${PORT}" + +server_is_up() { + curl --silent --fail --max-time 1 --output /dev/null "${URL}" +} + +DEV_SERVER_PID="" + +if server_is_up; then + echo "electron-shell: Dev-Server läuft bereits auf ${URL}." +else + echo "electron-shell: starte Vite-Dev-Server (Port ${PORT}) …" + npm run dev >/tmp/cad-electron-shell-dev.log 2>&1 & + DEV_SERVER_PID=$! + + ATTEMPTS=0 + MAX_ATTEMPTS=60 + until server_is_up; do + ATTEMPTS=$((ATTEMPTS + 1)) + if [ "${ATTEMPTS}" -ge "${MAX_ATTEMPTS}" ]; then + echo "electron-shell: Dev-Server antwortet nach ${MAX_ATTEMPTS}s nicht, Abbruch." >&2 + kill "${DEV_SERVER_PID}" 2>/dev/null + exit 1 + fi + sleep 1 + done + echo "electron-shell: Dev-Server bereit." +fi + +cleanup() { + if [ -n "${DEV_SERVER_PID}" ]; then + echo "electron-shell: beende selbst gestarteten Dev-Server (PID ${DEV_SERVER_PID}) …" + kill "${DEV_SERVER_PID}" 2>/dev/null + wait "${DEV_SERVER_PID}" 2>/dev/null + fi +} +trap cleanup EXIT + +echo "electron-shell: öffne Electron-Fenster …" +npx electron "$(dirname "$0")/electron-main.cjs"