v0.2.6-pre.014

This commit is contained in:
2026-08-22 00:21:14 +02:00
parent c07db925b1
commit 6907e4bbb8
39 changed files with 1544 additions and 370 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-config-desk/frontend/main.html -->
<!-- version: 16 -->
<!-- version: 17 -->
<!DOCTYPE html>
<html lang="fr">
@@ -21,20 +21,25 @@
<span class="mx-2 fs-5 text-body-secondary" aria-hidden="true"></span>
<span id="headerViewTitle" class="fs-5 text-body text-nowrap">Vue d'ensemble</span>
</div>
<div class="app-nav nav nav-pills flex-nowrap gap-1 ms-auto" aria-label="Navigation Config Desk">
<button class="nav-link app-nav-button active" type="button" data-view="overview">Vue d'ensemble</button>
<button class="nav-link app-nav-button" type="button" data-view="documents">Documents</button>
<button class="nav-link app-nav-button" type="button" data-view="profiles">Profils</button>
<button class="nav-link app-nav-button" type="button" data-view="environment">Environnement / .env</button>
<button class="nav-link app-nav-button" type="button" data-view="logging">Logging</button>
</div>
</div>
</nav>
</header>
<main class="app-main">
<div class="app-scrollable" data-simplebar>
<div class="container-fluid px-4 py-4">
<main class="app-main container-fluid p-0">
<div class="row g-0 h-100">
<aside class="col-auto app-sidebar border-end bg-body-tertiary">
<div class="app-sidebar-scroll h-100" data-simplebar>
<nav class="nav nav-pills flex-column gap-1 p-3" aria-label="Navigation Config Desk">
<button class="nav-link text-start active" type="button" data-view="overview"><i class="fa-solid fa-gauge-high me-2" aria-hidden="true"></i>Vue d'ensemble</button>
<button class="nav-link text-start" type="button" data-view="documents"><i class="fa-solid fa-file-code me-2" aria-hidden="true"></i>Documents</button>
<button class="nav-link text-start" type="button" data-view="profiles"><i class="fa-solid fa-layer-group me-2" aria-hidden="true"></i>Profils</button>
<button class="nav-link text-start" type="button" data-view="environment"><i class="fa-solid fa-terminal me-2" aria-hidden="true"></i>Environnement / .env</button>
<button class="nav-link text-start" type="button" data-view="logging"><i class="fa-solid fa-list-ul me-2" aria-hidden="true"></i>Logging</button>
</nav>
</div>
</aside>
<section class="col app-content app-scrollable h-100" data-simplebar>
<div class="container-fluid px-4 py-4">
<div class="card app-shell-card mx-auto shadow-sm border-0">
<div class="card-body p-4">
<h1 id="viewTitle" class="h3 mb-2">Vue d'ensemble</h1>
@@ -547,7 +552,7 @@
</div>
</div>
</div>
</div>
</section>
</div>
</main>

View File

@@ -1,8 +1,9 @@
// file: crates/ksp-app-config-desk/frontend/sass/_app.scss
// version: 5
// version: 6
$app-header-height: 72px;
$app-footer-height: 42px;
$app-sidebar-width: 250px;
html,
body {
@@ -59,13 +60,19 @@ body {
font-family: var(--bs-font-monospace);
}
.app-nav {
min-width: 0;
overflow-x: auto;
scrollbar-width: thin;
.app-sidebar {
width: $app-sidebar-width;
min-width: $app-sidebar-width;
max-width: $app-sidebar-width;
}
.app-nav-button {
.app-sidebar-scroll,
.app-content {
height: 100%;
max-height: 100%;
}
.app-sidebar .nav-link {
white-space: nowrap;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/frontend/sass/splash.scss
// version: 3
// version: 4
@font-face {
font-family: "Dos Amazigh";
@@ -9,13 +9,23 @@
font-display: swap;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body {
display: flex;
width: 100vw;
height: 100vh;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
body {
display: flex;
align-items: center;
justify-content: center;
background: transparent;
@@ -25,8 +35,12 @@ body {
#splash-container {
position: relative;
width: 960px;
max-width: 100%;
height: 637px;
max-height: 100%;
overflow: hidden;
opacity: 0;
will-change: opacity;
}
#splash-image {
@@ -35,6 +49,11 @@ body {
width: 100%;
height: 100%;
z-index: 1;
object-fit: contain;
user-select: none;
-webkit-user-select: none;
-webkit-user-drag: none;
pointer-events: none;
}
#app-name {
@@ -49,17 +68,70 @@ body {
font-weight: bold;
text-align: center;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
white-space: nowrap;
user-select: none;
-webkit-user-select: none;
-webkit-user-drag: none;
pointer-events: none;
}
#splash-status {
#debug-info {
position: absolute;
right: 1rem;
bottom: 1rem;
left: 1rem;
top: 0;
left: 0;
z-index: 3;
padding: 0.55rem 0.75rem;
width: 55%;
max-height: 30%;
overflow-y: auto;
padding: 8px;
color: #fff;
background: rgba(0, 0, 0, 0.35);
background: rgba(0, 0, 0, 0.18);
font-family: monospace;
font-size: 0.82rem;
font-size: 12px;
scrollbar-width: none;
}
#messages-container {
position: absolute;
right: 0;
bottom: 0;
left: 0;
z-index: 3;
max-height: 30%;
overflow-y: auto;
padding: 10px 10px 36px;
background: rgba(0, 0, 0, 0.18);
font-family: Arial, sans-serif;
font-size: 14px;
scrollbar-width: none;
}
#debug-info::-webkit-scrollbar,
#messages-container::-webkit-scrollbar {
display: none;
}
.splash-message {
margin-bottom: 8px;
padding: 6px 10px;
border-radius: 4px;
color: #fff;
font-weight: bold;
animation: splashMessageFadeIn 0.3s ease-in-out;
}
.splash-message.info { background: rgba(33, 150, 243, 0.22); }
.splash-message.warning { background: rgba(255, 152, 0, 0.22); }
.splash-message.error { background: rgba(244, 67, 54, 0.22); }
.splash-message.success { background: rgba(76, 175, 80, 0.22); }
@keyframes splashMessageFadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-config-desk/frontend/splash.html -->
<!-- version: 1 -->
<!-- version: 2 -->
<!DOCTYPE html>
<html lang="fr">
@@ -11,10 +11,11 @@
</head>
<body>
<div id="splash-container">
<div id="splash-container" style="opacity: 0;">
<img id="splash-image" src="imgs/splash.png" alt="Chargement de KSP Config Desk">
<div id="app-name">KSP</div>
<div id="splash-status" aria-live="polite">Chargement du shell frontend...</div>
<div id="app-name">Config Desk</div>
<div id="debug-info" aria-live="off" hidden></div>
<div id="messages-container" aria-live="polite"></div>
</div>
<script type="module" src="ts/splash.ts" defer></script>
</body>

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/frontend/ts/splash.ts
// version: 3
// version: 4
import { listen } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window";
@@ -9,21 +9,14 @@ import { invokeKsp } from "./invoke";
installFrontendConsoleBridge("splash");
const defaultFadeDurationMs = 300;
let activeOpacityFrame: number | null = null;
function normalizeDurationMs(value: number | null | undefined): number {
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
return defaultFadeDurationMs;
}
return value;
function easeInOut(value: number): number {
return value < 0.5 ? 2 * value * value : 1 - Math.pow(-2 * value + 2, 2) / 2;
}
function easeInOut(progress: number): number {
if (progress < 0.5) {
return 2 * progress * progress;
}
return 1 - Math.pow(-2 * progress + 2, 2) / 2;
function normalizeDurationMs(value: number | null): number {
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
}
async function animateOpacity(element: HTMLElement, fromOpacity: number, toOpacity: number, durationMs: number): Promise<void> {
@@ -40,7 +33,7 @@ async function animateOpacity(element: HTMLElement, fromOpacity: number, toOpaci
const opacityDelta = toOpacity - fromOpacity;
const updateOpacity = (currentTime: number): void => {
const elapsedMs = currentTime - startedAt;
const rawProgress = Math.min(elapsedMs / durationMs, 1);
const rawProgress = durationMs === 0 ? 1 : Math.min(elapsedMs / durationMs, 1);
element.style.opacity = (fromOpacity + opacityDelta * easeInOut(rawProgress)).toString();
if (rawProgress >= 1) {
element.style.opacity = toOpacity.toString();
@@ -56,21 +49,46 @@ async function animateOpacity(element: HTMLElement, fromOpacity: number, toOpaci
frontendTrace("splash", "Splash opacity animation completed", { toOpacity, durationMs });
}
function replaceStatus(message: string | null): void {
if (!message) {
function scrollToLatest(element: HTMLElement): void {
element.scrollTop = element.scrollHeight;
}
function addMessage(message: string, status: string | null): void {
const container = document.querySelector<HTMLElement>("#messages-container");
if (!container) {
return;
}
const status = document.querySelector<HTMLElement>("#splash-status");
if (status) {
status.textContent = message;
frontendTrace("splash", "Splash status replaced", { message });
const line = document.createElement("div");
const safeStatus = status === "warning" || status === "error" || status === "success" ? status : "info";
line.className = `splash-message ${safeStatus}`;
line.textContent = message;
container.appendChild(line);
scrollToLatest(container);
}
function addDebugMessage(message: string): void {
const container = document.querySelector<HTMLElement>("#debug-info");
if (!container) {
return;
}
container.hidden = false;
const line = document.createElement("div");
line.textContent = `${new Date().toLocaleTimeString()}: ${message}`;
container.appendChild(line);
scrollToLatest(container);
}
async function handleSplashOrder(order: SplashOrderDto): Promise<void> {
frontendTrace("splash", "Splash order received", { action: order.action });
const container = document.querySelector<HTMLElement>("#splash-container");
replaceStatus(order.message);
if (order.action === "add_message" && order.message) {
addMessage(order.message, order.status);
return;
}
if (order.action === "add_debug" && order.message) {
addDebugMessage(order.message);
return;
}
if (!container) {
return;
}
@@ -99,7 +117,7 @@ async function initializeSplash(): Promise<void> {
try {
await invokeKsp<void>("splash", "splash_frontend_ready");
} catch {
replaceStatus("Le lifecycle du splash n'a pas pu démarrer.");
addMessage("Le lifecycle du splash n'a pas pu démarrer.", "error");
if (container) {
container.style.opacity = "1";
container.style.willChange = "auto";