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 @@
-
+
- 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
-

+
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#"