diff --git a/Cargo.toml b/Cargo.toml index d1b00b1..9ebe9f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 287 +# version: 288 [workspace] resolver = "3" members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.2.12-pre.2" +version = "0.2.12-pre.2.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-app-solprices-desk/frontend/fonts/README.md b/crates/ksp-app-solprices-desk/frontend/fonts/README.md new file mode 100644 index 0000000..5bab5d0 --- /dev/null +++ b/crates/ksp-app-solprices-desk/frontend/fonts/README.md @@ -0,0 +1,25 @@ + + + +# Fonts du splash SOL Prices Desk + +SOL Prices Desk réutilise le gabarit de splash KSP déjà stabilisé dans Config Desk et Wallet Desk et conserve la police locale : + +```text +DOS_Amazigh.ttf +font-family: Dos Amazigh +``` + +Destination : + +```text +crates/ksp-app-solprices-desk/frontend/fonts/DOS_Amazigh.ttf +``` + +Empreinte SHA-256 de l'asset repris du gabarit : + +```text +a01ff4b5a0d699db7cefcf5336bf0e379ff6fbf0d096087571a13c8782f9aa02 +``` + +`splash.scss` déclare `Dos Amazigh` avec `@font-face` et l'applique au titre `#app-name`. diff --git a/crates/ksp-app-solprices-desk/frontend/main.html b/crates/ksp-app-solprices-desk/frontend/main.html index c5735d8..3fef123 100644 --- a/crates/ksp-app-solprices-desk/frontend/main.html +++ b/crates/ksp-app-solprices-desk/frontend/main.html @@ -1,11 +1,11 @@ - + - - + + SOL Prices Desk — Prices @@ -21,7 +21,6 @@ Prices - 0.2.12-pre.2 @@ -51,7 +50,7 @@

Scaffold desktop actif. Le runtime Off-chain et la table SOL/USD arrivent dans les tranches suivantes.

-
+

Surface prix volontairement inactive

@@ -69,7 +68,7 @@
Runtime
-
+
Version
Phase
diff --git a/crates/ksp-app-solprices-desk/frontend/sass/_app.scss b/crates/ksp-app-solprices-desk/frontend/sass/_app.scss index 4a5840f..7fa97cd 100644 --- a/crates/ksp-app-solprices-desk/frontend/sass/_app.scss +++ b/crates/ksp-app-solprices-desk/frontend/sass/_app.scss @@ -1,5 +1,5 @@ // file: crates/ksp-app-solprices-desk/frontend/sass/_app.scss -// version: 2 +// version: 3 $app-header-height: 72px; $app-footer-height: 42px; @@ -46,22 +46,22 @@ body { min-width: 0; } -.app-scrollable { - height: 100%; - max-height: 100%; -} - .app-logo { display: block; width: auto; height: 42px; } +.app-scrollable { + height: 100%; + max-height: 100%; +} + .app-shell-card { max-width: 1320px; } -.app-shell-status { +.app-runtime-list dd { font-family: var(--bs-font-monospace); } @@ -85,10 +85,21 @@ body { white-space: nowrap; } -.app-overview-list dd { - font-family: var(--bs-font-monospace); +.app-placeholder { + min-height: 320px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1rem; + padding: 2rem; + border: 1px dashed var(--bs-border-color); + border-radius: var(--bs-border-radius-lg); + color: var(--bs-secondary-color); + background: var(--bs-body-bg); + text-align: center; } -.scaffold-placeholder { - min-height: 280px; +.app-placeholder i { + font-size: 2.5rem; } diff --git a/crates/ksp-app-solprices-desk/frontend/splash.html b/crates/ksp-app-solprices-desk/frontend/splash.html index 750f941..e4b8204 100644 --- a/crates/ksp-app-solprices-desk/frontend/splash.html +++ b/crates/ksp-app-solprices-desk/frontend/splash.html @@ -1,19 +1,19 @@ - + - - + + - Chargement — SOL Prices Desk + KSP SOL Prices Desk — Chargement
- Chargement de KSP Sol Prices Desk + Chargement de KSP SOL Prices Desk
SOL Prices Desk
diff --git a/crates/ksp-app-solprices-desk/frontend/ts/main.ts b/crates/ksp-app-solprices-desk/frontend/ts/main.ts index 461fcb4..89f099d 100644 --- a/crates/ksp-app-solprices-desk/frontend/ts/main.ts +++ b/crates/ksp-app-solprices-desk/frontend/ts/main.ts @@ -1,23 +1,29 @@ // file: crates/ksp-app-solprices-desk/frontend/ts/main.ts -// version: 1 +// version: 2 import "bootstrap"; +import ResizeObserver from "resize-observer-polyfill"; import "simplebar"; +import { getCurrentWindow } from "@tauri-apps/api/window"; import type { RuntimeStatusDto } from "./bindings/ksp_app_solprices_desk/dto_common/RuntimeStatusDto.ts"; -import { frontendDebug, frontendInfo, frontendTrace, installFrontendConsoleBridge } from "./frontend_log"; +import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; import { invokeKsp } from "./invoke"; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; installFrontendConsoleBridge("main"); -const viewTitles: Record = { +type ViewId = "prices" | "diagnostics"; + +const viewTitles: Record = { prices: "Prices", diagnostics: "Diagnostics", }; -function activateView(viewId: string): void { - if (!(viewId in viewTitles)) { - return; - } +function isViewId(value: string | undefined): value is ViewId { + return value === "prices" || value === "diagnostics"; +} + +function activateView(viewId: ViewId, source: "startup" | "user"): void { document.querySelectorAll("[data-view-panel]").forEach(panel => { panel.hidden = panel.dataset.viewPanel !== viewId; }); @@ -28,7 +34,22 @@ function activateView(viewId: string): void { if (header) { header.textContent = viewTitles[viewId]; } - frontendDebug("main", "SOL Prices Desk view activated", { viewId }); + document.title = `SOL Prices Desk — ${viewTitles[viewId]}`; + if (source !== "startup") { + frontendDebug("main", "SOL Prices Desk view activated", { viewId, source }); + } +} + +function bindNavigation(): void { + document.querySelectorAll("[data-view]").forEach(button => { + button.addEventListener("click", () => { + const viewId = button.dataset.view; + if (isViewId(viewId)) { + activateView(viewId, "user"); + } + }); + }); + frontendTrace("main", "SOL Prices Desk navigation handlers installed"); } function renderRuntimeStatus(status: RuntimeStatusDto): void { @@ -45,10 +66,6 @@ function renderRuntimeStatus(status: RuntimeStatusDto): void { element.textContent = value; } } - const badge = document.querySelector("#runtimeVersionBadge"); - if (badge) { - badge.textContent = status.applicationVersion; - } const diagnostic = document.querySelector("#runtimeDiagnostic"); if (diagnostic) { diagnostic.hidden = status.startupDiagnostic === null; @@ -60,19 +77,26 @@ function renderRuntimeStatus(status: RuntimeStatusDto): void { }); } -async function initializeMain(): Promise { - frontendInfo("main", "SOL Prices Desk scaffold frontend loaded"); - document.querySelectorAll("[data-view]").forEach(button => { - button.addEventListener("click", () => { - const viewId = button.dataset.view; - if (viewId) { - activateView(viewId); - } - }); - }); - const status = await invokeKsp("main", "runtime_status"); +async function loadRuntimeStatus(): Promise { + const status = await invokeKsp("main", "get_runtime_status"); renderRuntimeStatus(status); - activateView("prices"); +} + +async function initializeMain(): Promise { + const windowLabel = getCurrentWindow().label; + frontendInfo("main", "SOL Prices Desk main frontend loaded", { windowLabel }); + bindNavigation(); + activateView("prices", "startup"); + try { + await loadRuntimeStatus(); + } catch { + const diagnostic = document.querySelector("#runtimeDiagnostic"); + if (diagnostic) { + diagnostic.hidden = false; + diagnostic.textContent = "Le statut runtime initial n'a pas pu être chargé."; + } + frontendWarn("main", "SOL Prices Desk startup runtime status load failed"); + } } document.addEventListener("DOMContentLoaded", () => { diff --git a/crates/ksp-app-solprices-desk/package.json b/crates/ksp-app-solprices-desk/package.json index 520f109..96fe2ba 100644 --- a/crates/ksp-app-solprices-desk/package.json +++ b/crates/ksp-app-solprices-desk/package.json @@ -1,7 +1,7 @@ { "name": "ksp-app-solprices-desk", "private": true, - "version": "0.2.12-pre.2", + "version": "0.2.12-pre.2.fix.1", "type": "module", "scripts": { "dev": "vite", diff --git a/crates/ksp-app-solprices-desk/src/tauri.rs b/crates/ksp-app-solprices-desk/src/tauri.rs index 50e00ed..a9d0ea6 100644 --- a/crates/ksp-app-solprices-desk/src/tauri.rs +++ b/crates/ksp-app-solprices-desk/src/tauri.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-solprices-desk/src/tauri.rs -// version: 1 +// version: 3 //! Tauri runtime assembly for the KSP SOL prices desktop application. @@ -72,7 +72,7 @@ fn configure_plugins(builder: tauri::Builder) -> tauri::Builder) -> tauri::Builder { - return builder.invoke_handler(tauri::generate_handler![emit_frontend_log, runtime_status, splash_frontend_ready]); + return builder.invoke_handler(tauri::generate_handler![emit_frontend_log, get_runtime_status, splash_frontend_ready,]); } fn configure_setup(builder: tauri::Builder) -> tauri::Builder { @@ -85,10 +85,20 @@ fn configure_setup(builder: tauri::Builder) -> tauri::Builder) -> std::result::Result { + let result = state.runtime_status(); + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), + }; +} + #[tauri::command] fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Result<(), crate::CommandErrorDto> { let result = crate::emit_frontend_log_event(payload); @@ -98,15 +108,6 @@ fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Resu }; } -#[tauri::command] -fn runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result { - let result = state.runtime_status(); - return match result { - std::result::Result::Ok(value) => std::result::Result::Ok(value), - std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), - }; -} - #[tauri::command] async fn splash_frontend_ready( app: tauri::AppHandle, diff --git a/crates/ksp-app-solprices-desk/tauri.conf.json b/crates/ksp-app-solprices-desk/tauri.conf.json index 6e6cd3d..c8257dc 100644 --- a/crates/ksp-app-solprices-desk/tauri.conf.json +++ b/crates/ksp-app-solprices-desk/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "KSP SOL Prices Desk", - "version": "0.2.12-pre.2", + "version": "0.2.12-pre.2.fix.1", "identifier": "com.sasedev.ksp-app-solprices-desk", "build": { "beforeDevCommand": { diff --git a/crates/ksp-app-solprices-desk/tests/desktop_contract.rs b/crates/ksp-app-solprices-desk/tests/desktop_contract.rs index 4331bbf..abd7c5f 100644 --- a/crates/ksp-app-solprices-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-solprices-desk/tests/desktop_contract.rs @@ -1,7 +1,7 @@ // file: crates/ksp-app-solprices-desk/tests/desktop_contract.rs -// version: 1 +// version: 2 -//! Desktop scaffold contract audits for SOL Prices Desk `0.2.12-pre.002`. +//! Desktop scaffold and shared-template contract audits for SOL Prices Desk `0.2.12-pre.002` and its fixes. fn app_root() -> std::path::PathBuf { return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -37,7 +37,9 @@ fn pre_002_shell_uses_reserved_ports_windows_and_owned_backend_paths() { let tauri = read_json(root.join("tauri.conf.json").as_path()); assert_eq!(tauri.pointer("/productName").and_then(serde_json::Value::as_str), std::option::Option::Some("KSP SOL Prices Desk")); assert_eq!(tauri.pointer("/identifier").and_then(serde_json::Value::as_str), std::option::Option::Some("com.sasedev.ksp-app-solprices-desk")); - assert_eq!(tauri.pointer("/version").and_then(serde_json::Value::as_str), std::option::Option::Some("0.2.12-pre.2")); + assert_eq!(tauri.pointer("/version").and_then(serde_json::Value::as_str), std::option::Option::Some("0.2.12-pre.2.fix.1")); + let package = read_json(root.join("package.json").as_path()); + assert_eq!(package.pointer("/version").and_then(serde_json::Value::as_str), std::option::Option::Some("0.2.12-pre.2.fix.1")); assert_eq!(tauri.pointer("/build/devUrl").and_then(serde_json::Value::as_str), std::option::Option::Some("http://localhost:1434")); assert_eq!(tauri.pointer("/build/beforeDevCommand/script").and_then(serde_json::Value::as_str), std::option::Option::Some("npm run dev")); assert_eq!(tauri.pointer("/build/beforeDevCommand/cwd").and_then(serde_json::Value::as_str), std::option::Option::Some(".")); @@ -75,7 +77,8 @@ fn pre_002_package_is_mixed_lib_bin_and_frontend_is_scaffold_only() { let lib_rs = read_text(root.join("src/lib.rs").as_path()); assert!(lib_rs.contains("pub use self::tauri::run")); let tauri_rs = read_text(root.join("src/tauri.rs").as_path()); - assert!(tauri_rs.contains("tauri::generate_handler![emit_frontend_log, runtime_status, splash_frontend_ready]")); + assert!(tauri_rs.contains("get_runtime_status")); + assert!(!tauri_rs.contains("fn runtime_status(")); let main_html = read_text(root.join("frontend/main.html").as_path()); assert!(main_html.contains("data-view=\"prices\"")); assert!(main_html.contains("data-view=\"diagnostics\"")); @@ -98,3 +101,50 @@ fn pre_002_packaging_contains_current_ten_config_resources_only() { assert!(tauri_source.contains("ksp_config_lib::prepare_packaged_runtime")); assert!(tauri_source.contains("std::env::set_current_dir(layout.runtime_root())")); } + +#[test] +fn pre_002_fix_001_reuses_the_common_ksp_desktop_template() { + let root = app_root(); + let package = read_text(root.join("package.json").as_path()); + for dependency in ["@fltsci/tauri-plugin-tracing", "@fortawesome/fontawesome-free", "@tauri-apps/api", "bootstrap", "resize-observer-polyfill", "simplebar"] + { + assert!(package.contains(dependency), "missing common frontend dependency {dependency}"); + } + let main_typescript = read_text(root.join("frontend/ts/main.ts").as_path()); + assert!(main_typescript.contains(r#"import ResizeObserver from "resize-observer-polyfill""#)); + assert!(main_typescript.contains(".ResizeObserver = ResizeObserver")); + assert!(main_typescript.contains("getCurrentWindow().label")); + assert!(main_typescript.contains(r#""get_runtime_status""#)); + let main_html = read_text(root.join("frontend/main.html").as_path()); + assert!(main_html.contains(r#"