Compare commits

..

5 Commits

Author SHA1 Message Date
f347dcd357 Passage à la 0.17.3 et ajout d'un splash screen pour le démarrage de l'application
Some checks failed
E2E Tests / e2e (push) Has been cancelled
Tests unitaires / Brain (Python · pytest + couverture) (push) Successful in 18s
Tests unitaires / Web (Angular · vitest + couverture) (push) Successful in 28s
Build & Push Images / build (brain) (push) Successful in 1m11s
Tests unitaires / Core (Java · mvn test + JaCoCo) (push) Successful in 2m9s
Build & Push Images / build (web) (push) Successful in 1m48s
Build & Push Images / build-switcher (push) Successful in 44s
Build & Push Images / build (core) (push) Successful in 3m2s
2026-06-22 11:34:55 +02:00
46fea8d53c On arrête de mettre appimagetool dans la release - actif après la release 0.17.2
Some checks failed
E2E Tests / e2e (push) Has been cancelled
Tests unitaires / Brain (Python · pytest + couverture) (push) Successful in 22s
Tests unitaires / Web (Angular · vitest + couverture) (push) Successful in 25s
Tests unitaires / Core (Java · mvn test + JaCoCo) (push) Successful in 1m55s
2026-06-22 11:15:41 +02:00
e05b26563f passage v0.17.2 release officielle
Some checks failed
Tests unitaires / Brain (Python · pytest + couverture) (push) Successful in 20s
Tests unitaires / Web (Angular · vitest + couverture) (push) Successful in 28s
Tests unitaires / Core (Java · mvn test + JaCoCo) (push) Successful in 2m22s
Build & Push Images / build (brain) (push) Successful in 1m20s
E2E Tests / e2e (push) Has been cancelled
Build & Push Images / build (web) (push) Successful in 1m52s
Build & Push Images / build (core) (push) Successful in 3m20s
Build & Push Images / build-switcher (push) Successful in 43s
2026-06-22 10:37:00 +02:00
dc66199177 Passage v0.17.2-beta suite au changement de l'installeur
All checks were successful
Build & Push Images / build (brain) (push) Successful in 1m25s
Build & Push Images / build (core) (push) Successful in 3m20s
Build & Push Images / build (web) (push) Successful in 1m56s
Build & Push Images / build-switcher (push) Successful in 19s
2026-06-22 09:51:26 +02:00
b133d0f16d Correction dans l'installeur : ajout d'une case pour pouvoir lancer le programme automatiquement à la fin de l'installation (sous windows) afin d'éviter que l'utilisateur ne soit perturbé 2026-06-22 09:49:33 +02:00
16 changed files with 348 additions and 9 deletions

View File

@@ -249,7 +249,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.ver.outputs.tag }}
files: core/target/dist-out/*.AppImage
files: core/target/dist-out/DM_Loremind*.AppImage
fail_on_unmatched_files: true
# BETA uniquement : artefact PRIVE (pas de release publique).
@@ -258,5 +258,5 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: loremind-beta-${{ steps.ver.outputs.version }}-appimage
path: core/target/dist-out/*.AppImage
path: core/target/dist-out/DM_Loremind*.AppImage
retention-days: 90

View File

@@ -26,7 +26,7 @@ from app.infrastructure.ollama_model_installer import ensure_ollama_embedding_mo
app = FastAPI(
title="LoreMind Brain",
description="Backend IA pour la génération de contenu narratif.",
version="0.17.1-beta",
version="0.17.3",
)
logger = logging.getLogger(__name__)

View File

@@ -14,7 +14,7 @@
<groupId>com.loremind</groupId>
<artifactId>loremind-core</artifactId>
<version>0.17.1-beta</version>
<version>0.17.3</version>
<name>LoreMind Core</name>
<description>Backend Core - Architecture Hexagonale</description>

View File

@@ -7,6 +7,8 @@ import org.springframework.context.annotation.Profile;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.awt.SplashScreen;
/**
* En mode bureau (profil "local"), ouvre le navigateur par defaut sur
* l'application des que le serveur est pret. L'app n'ayant pas de fenetre
@@ -25,7 +27,26 @@ public class DesktopBrowserOpener {
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
// Ferme le splash natif (affiche par la JVM via -splash des le double-clic)
// juste avant d'ouvrir le navigateur : le relais visuel est assure.
closeSplash();
log.info("[Desktop] Application prete — ouverture du navigateur.");
DesktopSingleInstance.openAppInBrowser();
}
/**
* Ferme l'ecran de demarrage si l'app a ete lancee avec {@code -splash:...}
* (cas des installeurs jpackage). No-op silencieux sinon (lancement dev sans
* splash, ou environnement sans affichage).
*/
private void closeSplash() {
try {
SplashScreen splash = SplashScreen.getSplashScreen();
if (splash != null) {
splash.close();
}
} catch (Exception e) {
log.debug("[Desktop] Pas de splash a fermer : {}", e.getMessage());
}
}
}

View File

@@ -165,6 +165,9 @@ cp "$jar" "$STAGE_DIR/loremind-core.jar"
[[ -d "$BRAIN_EMBED/python" ]] || { err "Brain introuvable (brain/dist-embed-linux). Relancez sans --skip-brain."; exit 1; }
mkdir -p "$STAGE_DIR/brain"
cp -r "$BRAIN_EMBED/." "$STAGE_DIR/brain/"
# Splash : copie dans la charge utile -> atterrit dans $APPDIR/splash.png (cf. -splash plus bas).
[[ -f "$SCRIPT_DIR/splash.png" ]] && cp "$SCRIPT_DIR/splash.png" "$STAGE_DIR/splash.png"
ok "Charge utile prete ($STAGE_DIR)"
# --- 5. jpackage -> app-image ----------------------------------------------
@@ -191,7 +194,8 @@ jpackage \
--main-class org.springframework.boot.loader.launch.JarLauncher \
--dest "$OUT_DIR" \
--java-options '-Dspring.profiles.active=local' \
--java-options "-Dbrain.sidecar.command=$BRAIN_CMD"
--java-options "-Dbrain.sidecar.command=$BRAIN_CMD" \
--java-options '-splash:$APPDIR/splash.png'
APPIMG_SRC="$OUT_DIR/$APP_NAME" # dossier produit par jpackage (avec espace)
[[ -d "$APPIMG_SRC" ]] || { err "app-image jpackage introuvable ($APPIMG_SRC)."; exit 1; }
@@ -235,7 +239,9 @@ chmod +x "$APPDIR/AppRun"
# e) appimagetool (telecharge si absent). --appimage-extract-and-run : evite
# d'exiger FUSE (absent des runners CI).
TOOL="$OUT_DIR/appimagetool-x86_64.AppImage"
# /!\ HORS de $OUT_DIR : sinon ce .AppImage (l'outil) serait pris pour un livrable
# et attache a la release a cote du vrai DM_Loremind-*.AppImage.
TOOL="$CORE_DIR/target/appimagetool-x86_64.AppImage"
if [[ ! -x "$TOOL" ]]; then
curl -fsSL -o "$TOOL" \
https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage

View File

@@ -58,6 +58,8 @@ $CoreDir = Join-Path $RepoRoot 'core'
$StageDir = Join-Path $CoreDir 'target\dist-input' # charge utile jpackage
$OutDir = Join-Path $CoreDir 'target\dist-out' # .msi produit
$IconFile = Join-Path $PSScriptRoot 'app-icon.ico' # icone de l'app (.msi + raccourcis)
$WixDir = Join-Path $PSScriptRoot 'wix' # main.wxs surcharge (case "Lancer" en fin d'install)
$SplashFile = Join-Path $PSScriptRoot 'splash.png' # ecran de demarrage (affiche par la JVM via -splash)
# --- Version (numerique pour MSI) ------------------------------------------
if (-not $Version) {
@@ -191,6 +193,9 @@ if (-not (Test-Path $BrainEmbed)) { Write-Err "Brain introuvable (brain/dist-emb
$stageBrain = Join-Path $StageDir 'brain'
New-Item -ItemType Directory -Force -Path $stageBrain | Out-Null
Copy-Item (Join-Path $BrainEmbed '*') $stageBrain -Recurse
# Splash : copie dans la charge utile -> atterrit dans $APPDIR\splash.png (cf. -splash plus bas).
if (Test-Path $SplashFile) { Copy-Item $SplashFile (Join-Path $StageDir 'splash.png') }
Write-Ok "Charge utile prete ($StageDir)"
# --- 5. jpackage -> .msi ---------------------------------------------------
@@ -229,12 +234,14 @@ jpackage `
--app-version $Version `
--vendor 'IGML Creation' `
--icon $IconFile `
--resource-dir $WixDir `
--input $StageDir `
--main-jar loremind-core.jar `
--main-class org.springframework.boot.loader.launch.JarLauncher `
--dest $OutDir `
--java-options '-Dspring.profiles.active=local' `
--java-options "-Dbrain.sidecar.command=$brainCmd" `
--java-options '-splash:$APPDIR\splash.png' `
--win-per-user-install `
--win-upgrade-uuid 'a7c4e1d2-9b3f-4e6a-8d05-1f2c3b4a5e6d' `
--win-menu --win-menu-group 'DM Loremind' `

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
main.wxs SURCHARGE (option resource-dir de jpackage) : copie du template du
JDK 21, plus une case "Lancer DM Loremind" sur l'ecran de fin (ExitDialog).
A garder synchronise avec le template du JDK du build (JDK 21). Pour re-extraire
la base : jimage extract (include regex main.wxs) depuis $JAVA_HOME/lib/modules.
Les seuls ajouts maison sont marques [DM Loremind].
Lancement : installeur per-user, donc contexte utilisateur ; une custom action
Directory + ExeCommand suffit (pas besoin de WixUtilExtension).
-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?ifdef JpIsSystemWide ?>
<?define JpInstallScope="perMachine"?>
<?else?>
<?define JpInstallScope="perUser"?>
<?endif?>
<?define JpProductLanguage=1033 ?>
<?define JpInstallerVersion=200 ?>
<?define JpCompressedMsi=yes ?>
<?include $(var.JpConfigDir)/overrides.wxi ?>
<?ifdef JpAllowUpgrades ?>
<?define JpUpgradeVersionOnlyDetectUpgrade="no"?>
<?else?>
<?define JpUpgradeVersionOnlyDetectUpgrade="yes"?>
<?endif?>
<?ifdef JpAllowDowngrades ?>
<?define JpUpgradeVersionOnlyDetectDowngrade="no"?>
<?else?>
<?define JpUpgradeVersionOnlyDetectDowngrade="yes"?>
<?endif?>
<Product
Id="$(var.JpProductCode)"
Name="$(var.JpAppName)"
Language="$(var.JpProductLanguage)"
Version="$(var.JpAppVersion)"
Manufacturer="$(var.JpAppVendor)"
UpgradeCode="$(var.JpProductUpgradeCode)">
<Package
Description="$(var.JpAppDescription)"
Manufacturer="$(var.JpAppVendor)"
InstallerVersion="$(var.JpInstallerVersion)"
Compressed="$(var.JpCompressedMsi)"
InstallScope="$(var.JpInstallScope)" Platform="x64"
/>
<Media Id="1" Cabinet="Data.cab" EmbedCab="yes" />
<Upgrade Id="$(var.JpProductUpgradeCode)">
<UpgradeVersion
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectUpgrade)"
Property="JP_UPGRADABLE_FOUND"
Maximum="$(var.JpAppVersion)"
MigrateFeatures="yes"
IncludeMaximum="$(var.JpUpgradeVersionOnlyDetectUpgrade)" />
<UpgradeVersion
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)"
Property="JP_DOWNGRADABLE_FOUND"
Minimum="$(var.JpAppVersion)"
MigrateFeatures="yes"
IncludeMinimum="$(var.JpUpgradeVersionOnlyDetectDowngrade)" />
</Upgrade>
<?ifndef JpAllowUpgrades ?>
<CustomAction Id="JpDisallowUpgrade" Error="!(loc.DisallowUpgradeErrorMessage)" />
<?endif?>
<?ifndef JpAllowDowngrades ?>
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?>
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
<!-- Standard required root -->
<Directory Id="TARGETDIR" Name="SourceDir"/>
<Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
<ComponentGroupRef Id="Shortcuts"/>
<ComponentGroupRef Id="Files"/>
<ComponentGroupRef Id="FileAssociations"/>
</Feature>
<CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
<CustomAction Id="JpSetARPCOMMENTS" Property="ARPCOMMENTS" Value="$(var.JpAppDescription)" />
<CustomAction Id="JpSetARPCONTACT" Property="ARPCONTACT" Value="$(var.JpAppVendor)" />
<CustomAction Id="JpSetARPSIZE" Property="ARPSIZE" Value="$(var.JpAppSizeKb)" />
<?ifdef JpHelpURL ?>
<CustomAction Id="JpSetARPHELPLINK" Property="ARPHELPLINK" Value="$(var.JpHelpURL)" />
<?endif?>
<?ifdef JpAboutURL ?>
<CustomAction Id="JpSetARPURLINFOABOUT" Property="ARPURLINFOABOUT" Value="$(var.JpAboutURL)" />
<?endif?>
<?ifdef JpUpdateURL ?>
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
<?endif?>
<?ifdef JpIcon ?>
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
<?endif?>
<UIRef Id="JpUI"/>
<!-- ===================== [DM Loremind] ===================== -->
<!-- Case « Lancer l'application » sur l'ExitDialog standard de WixUI.
WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT : libellé de la case (la rend visible).
WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 : cochée par défaut. -->
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Lancer $(var.JpAppName)" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
<!-- Lance le launcher jpackage ([INSTALLDIR]<AppName>.exe). asyncNoWait : on
n'attend pas la fin de l'app. Contexte utilisateur (install per-user). -->
<CustomAction Id="JpLaunchApplication"
Directory="INSTALLDIR"
ExeCommand="&quot;[INSTALLDIR]$(var.JpAppName).exe&quot;"
Return="asyncNoWait"
Impersonate="yes" />
<UI>
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="JpLaunchApplication" Order="1">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
</UI>
<!-- =================== fin [DM Loremind] =================== -->
<InstallExecuteSequence>
<Custom Action="JpSetARPINSTALLLOCATION" After="CostFinalize">Not Installed</Custom>
<Custom Action="JpSetARPCOMMENTS" After="CostFinalize">Not Installed</Custom>
<Custom Action="JpSetARPCONTACT" After="CostFinalize">Not Installed</Custom>
<Custom Action="JpSetARPSIZE" After="CostFinalize">Not Installed</Custom>
<?ifdef JpHelpURL ?>
<Custom Action="JpSetARPHELPLINK" After="CostFinalize">Not Installed</Custom>
<?endif?>
<?ifdef JpAboutURL ?>
<Custom Action="JpSetARPURLINFOABOUT" After="CostFinalize">Not Installed</Custom>
<?endif?>
<?ifdef JpUpdateURL ?>
<Custom Action="JpSetARPURLUPDATEINFO" After="CostFinalize">Not Installed</Custom>
<?endif?>
<?ifndef JpAllowUpgrades ?>
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<?endif?>
<?ifndef JpAllowDowngrades ?>
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<?endif?>
<RemoveExistingProducts Before="CostInitialize"/>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</InstallExecuteSequence>
<InstallUISequence>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</InstallUISequence>
</Product>
</Wix>

4
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "loremind-web",
"version": "0.17.1-beta",
"version": "0.17.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "loremind-web",
"version": "0.17.1-beta",
"version": "0.17.3",
"dependencies": {
"@angular/animations": "^21.2.16",
"@angular/common": "^21.2.16",

View File

@@ -1,6 +1,6 @@
{
"name": "loremind-web",
"version": "0.17.1-beta",
"version": "0.17.3",
"description": "LoreMind Frontend - Angular",
"scripts": {
"ng": "ng",

View File

@@ -20,3 +20,6 @@
<app-global-search></app-global-search>
<app-confirm-dialog-host></app-confirm-dialog-host>
<!-- Écran de choix de langue au 1er lancement (s'auto-masque ensuite). -->
<app-first-run-language></app-first-run-language>

View File

@@ -6,6 +6,7 @@ import { SecondarySidebarComponent } from './shared/secondary-sidebar/secondary-
import { GlobalSearchComponent } from './shared/global-search/global-search.component';
import { UpdateBannerComponent } from './shared/update-banner/update-banner.component';
import { ConfirmDialogHostComponent } from './shared/confirm-dialog/confirm-dialog-host.component';
import { FirstRunLanguageComponent } from './shared/first-run-language/first-run-language.component';
import { LayoutService } from './services/layout.service';
import { GlobalSearchService } from './services/global-search.service';
import { VersionCheckerService } from './services/version-checker.service';
@@ -19,6 +20,7 @@ import { VersionCheckerService } from './services/version-checker.service';
GlobalSearchComponent,
UpdateBannerComponent,
ConfirmDialogHostComponent,
FirstRunLanguageComponent,
AsyncPipe
],
templateUrl: './app.component.html',

View File

@@ -88,6 +88,14 @@ export class LanguageService {
return this.defaultLang;
}
/**
* Vrai si une langue a déjà été choisie explicitement (mémorisée en localStorage).
* Faux au tout premier lancement → on propose alors l'écran de choix de langue.
*/
hasExplicitChoice(): boolean {
return this.read() !== null;
}
private read(): string | null {
try {
return localStorage.getItem(STORAGE_KEY);

View File

@@ -0,0 +1,17 @@
@if (visible) {
<div class="flo-overlay" role="dialog" aria-modal="true" aria-label="Language / Langue">
<div class="flo-card">
<img class="flo-logo" src="assets/logo.png" alt="DM Loremind" />
<h2 class="flo-title">Bienvenue&nbsp;·&nbsp;Welcome</h2>
<p class="flo-sub">Choisissez votre langue&nbsp;·&nbsp;Choose your language</p>
<div class="flo-buttons">
@for (opt of options; track opt.code) {
<button type="button" class="flo-btn" (click)="choose(opt.code)">
<span class="flo-flag">{{ opt.flag }}</span>
<span class="flo-native">{{ opt.native }}</span>
</button>
}
</div>
</div>
</div>
}

View File

@@ -0,0 +1,76 @@
.flo-overlay {
position: fixed;
inset: 0;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem;
background: rgba(10, 10, 20, 0.82);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.flo-card {
width: 100%;
max-width: 420px;
padding: 2.5rem 2rem;
text-align: center;
background: #12121f;
border: 1px solid rgba(108, 99, 255, 0.25);
border-radius: 18px;
box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.8);
}
.flo-logo {
width: 64px;
height: 64px;
border-radius: 14px;
object-fit: contain;
}
.flo-title {
margin: 1rem 0 0.3rem;
font-size: 1.5rem;
font-weight: 700;
color: #fff;
}
.flo-sub {
margin: 0 0 1.8rem;
font-size: 0.95rem;
color: #9ca3af;
}
.flo-buttons {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.flo-btn {
display: flex;
align-items: center;
gap: 0.9rem;
width: 100%;
padding: 0.9rem 1.1rem;
font-size: 1.05rem;
font-weight: 600;
color: #e0e0e0;
background: rgba(108, 99, 255, 0.08);
border: 1px solid rgba(108, 99, 255, 0.25);
border-radius: 12px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, transform 0.12s;
}
.flo-btn:hover {
background: rgba(108, 99, 255, 0.18);
border-color: rgba(108, 99, 255, 0.5);
transform: translateY(-1px);
}
.flo-flag {
font-size: 1.5rem;
line-height: 1;
}

View File

@@ -0,0 +1,35 @@
import { Component, inject } from '@angular/core';
import { LanguageService } from '../../services/language.service';
/**
* Écran de choix de langue au TOUT PREMIER lancement (overlay one-shot).
*
* Ne s'affiche que si aucune langue n'a encore été choisie explicitement
* (cf. LanguageService.hasExplicitChoice). Le choix est mémorisé en localStorage
* via LanguageService.use() → aux lancements suivants l'overlay ne réapparaît pas,
* et le sélecteur de langue habituel prend le relais.
*
* Textes volontairement BILINGUES / neutres (l'utilisateur n'a pas encore choisi).
*/
@Component({
selector: 'app-first-run-language',
standalone: true,
templateUrl: './first-run-language.component.html',
styleUrls: ['./first-run-language.component.scss'],
})
export class FirstRunLanguageComponent {
private readonly language = inject(LanguageService);
visible = !this.language.hasExplicitChoice();
/** Libellés NATIFs (indépendants de la langue courante). */
readonly options: ReadonlyArray<{ code: string; native: string; flag: string }> = [
{ code: 'fr', native: 'Français', flag: '🇫🇷' },
{ code: 'en', native: 'English', flag: '🇬🇧' },
];
choose(code: string): void {
this.language.use(code); // applique + mémorise
this.visible = false;
}
}