From af479df65a7c5df5ccd68b33e093dc8bdbc396ec Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Thu, 21 May 2026 19:17:07 +0200 Subject: [PATCH] fix(v1.0.3): fix startup window display, single instance lock, relative assets and obj packaging --- Eve/{EVE.obj => EVE.obj.bin} | 0 main.js | 28 ++++++++++++++++++++++++++++ package.json | 1 + public/Eve/{EVE.obj => EVE.obj.bin} | 0 rename-model.js | 17 +++++++++++++++++ src/components/AvatarModelLoader.tsx | 14 +++++++++++--- src/components/EveModel.tsx | 2 +- 7 files changed, 58 insertions(+), 4 deletions(-) rename Eve/{EVE.obj => EVE.obj.bin} (100%) rename public/Eve/{EVE.obj => EVE.obj.bin} (100%) create mode 100644 rename-model.js diff --git a/Eve/EVE.obj b/Eve/EVE.obj.bin similarity index 100% rename from Eve/EVE.obj rename to Eve/EVE.obj.bin diff --git a/main.js b/main.js index ba38660..9b1cf11 100644 --- a/main.js +++ b/main.js @@ -3,8 +3,26 @@ const path = require('path'); const fs = require('fs'); const os = require('os'); +// Autoriser le chargement de fichiers locaux via fetch sous le protocole file:// +app.commandLine.appendSwitch('disable-web-security'); +app.commandLine.appendSwitch('allow-file-access-from-files'); + let mainWindow; +// ── Single Instance Lock ─────────────────────────────────────────────────── +const gotTheLock = app.requestSingleInstanceLock(); +if (!gotTheLock) { + app.quit(); + process.exit(0); +} + +app.on('second-instance', () => { + if (mainWindow) { + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.focus(); + } +}); + // ── Logging Setup ─────────────────────────────────────────────────────────── function getLogPath() { const userDataDir = app.getPath('userData'); @@ -207,6 +225,7 @@ function createWindow() { transparent: true, // Permet la transparence (fond de fenêtre transparent) backgroundColor: '#00000000', // Fond transparent hasShadow: true, + show: false, // Cache la fenêtre au démarrage pour éviter les problèmes visuels de DWM webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, // Sécurité Zero Trust active @@ -216,6 +235,15 @@ function createWindow() { }, }); + mainWindow.once('ready-to-show', () => { + // Petit délai de sécurité (80ms) pour garantir la synchronisation DWM sous Windows + setTimeout(() => { + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.show(); + } + }, 80); + }); + loadApp().catch((error) => { console.error("Echec du chargement de l'application.", error); loadFallbackPage("Une erreur inattendue a interrompu le chargement de l'interface."); diff --git a/package.json b/package.json index 04aea5b..870bcd3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "appId": "com.romik3.eveflow", "productName": "EveFlow", "copyright": "Copyright © 2026 R0m1k3", + "asar": false, "directories": { "output": "out" }, diff --git a/public/Eve/EVE.obj b/public/Eve/EVE.obj.bin similarity index 100% rename from public/Eve/EVE.obj rename to public/Eve/EVE.obj.bin diff --git a/rename-model.js b/rename-model.js new file mode 100644 index 0000000..41e613f --- /dev/null +++ b/rename-model.js @@ -0,0 +1,17 @@ +const fs = require('fs'); +const path = require('path'); + +const paths = [ + path.join(__dirname, 'public', 'Eve', 'EVE.obj'), + path.join(__dirname, 'Eve', 'EVE.obj') +]; + +paths.forEach(p => { + const newPath = p + '.bin'; + if (fs.existsSync(p)) { + fs.renameSync(p, newPath); + console.log(`Renamed: ${p} -> ${newPath}`); + } else { + console.log(`Not found: ${p}`); + } +}); diff --git a/src/components/AvatarModelLoader.tsx b/src/components/AvatarModelLoader.tsx index 3e7dcce..d7888ed 100644 --- a/src/components/AvatarModelLoader.tsx +++ b/src/components/AvatarModelLoader.tsx @@ -13,7 +13,7 @@ interface AvatarModelLoaderProps { type AvatarManifest = Partial, string | null>>; -const AVATAR_MANIFEST_PATH = '/avatars/avatar-manifest.json'; +const AVATAR_MANIFEST_PATH = './avatars/avatar-manifest.json'; const LoadedGlbAvatar: React.FC<{ path: string; @@ -81,8 +81,16 @@ export const AvatarModelLoader: React.FC = ({ avatarId, }) .then((manifest) => { if (cancelled || !manifest) return; - const nextAssetPath = manifest[avatarId]; - setAssetPath(typeof nextAssetPath === 'string' && nextAssetPath.trim() ? nextAssetPath : null); + let nextAssetPath = manifest[avatarId]; + if (typeof nextAssetPath === 'string' && nextAssetPath.trim()) { + // Si le chemin commence par un slash, on le rend relatif pour fonctionner sous le protocole file:// en production + if (nextAssetPath.startsWith('/')) { + nextAssetPath = '.' + nextAssetPath; + } + setAssetPath(nextAssetPath); + } else { + setAssetPath(null); + } }) .catch(() => { if (!cancelled) setAssetPath(null); diff --git a/src/components/EveModel.tsx b/src/components/EveModel.tsx index 29eac67..5e51af4 100644 --- a/src/components/EveModel.tsx +++ b/src/components/EveModel.tsx @@ -269,7 +269,7 @@ export const EveModel: React.FC = ({ emotion, isSpeaking, avatarI // Charger les matériaux et le modèle OBJ depuis le répertoire public const materials = useLoader(MTLLoader, './Eve/EVE.mtl'); - const obj = useLoader(OBJLoader, './Eve/EVE.obj', (loader) => { + const obj = useLoader(OBJLoader, './Eve/EVE.obj.bin', (loader) => { materials.preload(); loader.setMaterials(materials); });