Compare commits
2 Commits
e05b26563f
...
v0.17.3
| Author | SHA1 | Date | |
|---|---|---|---|
| f347dcd357 | |||
| 46fea8d53c |
4
.github/workflows/desktop-release.yml
vendored
4
.github/workflows/desktop-release.yml
vendored
@@ -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
|
||||
|
||||
@@ -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.2",
|
||||
version="0.17.3",
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<groupId>com.loremind</groupId>
|
||||
<artifactId>loremind-core</artifactId>
|
||||
<version>0.17.2</version>
|
||||
<version>0.17.3</version>
|
||||
<name>LoreMind Core</name>
|
||||
<description>Backend Core - Architecture Hexagonale</description>
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,6 +59,7 @@ $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) {
|
||||
@@ -192,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 ---------------------------------------------------
|
||||
@@ -237,6 +241,7 @@ jpackage `
|
||||
--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' `
|
||||
|
||||
BIN
installers/desktop/splash.png
Normal file
BIN
installers/desktop/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
4
web/package-lock.json
generated
4
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "loremind-web",
|
||||
"version": "0.17.2",
|
||||
"version": "0.17.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "loremind-web",
|
||||
"version": "0.17.2",
|
||||
"version": "0.17.3",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^21.2.16",
|
||||
"@angular/common": "^21.2.16",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "loremind-web",
|
||||
"version": "0.17.2",
|
||||
"version": "0.17.3",
|
||||
"description": "LoreMind Frontend - Angular",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 · Welcome</h2>
|
||||
<p class="flo-sub">Choisissez votre langue · 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>
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user