v0.1.0-pre.044

This commit is contained in:
2026-07-25 18:07:51 +02:00
parent e2a349c739
commit c2dbd207d3
25 changed files with 624 additions and 86 deletions

View File

@@ -1,5 +1,5 @@
# file: Cargo.toml # file: Cargo.toml
# version: 4 # version: 5
[workspace] [workspace]
resolver = "3" resolver = "3"
@@ -14,7 +14,7 @@ members = [
"kb-onchain-transport", "kb-onchain-transport",
"kb-store", "kb-store",
"kb-wallet", "kb-wallet",
"kb-app-demo", "kb-app-demo-desktop",
] ]
[workspace.package] [workspace.package]

View File

@@ -47,7 +47,8 @@
"kb-onchain-transport", "kb-onchain-transport",
"kb-wallet", "kb-wallet",
"kb-pipeline", "kb-pipeline",
"kb_app_demo", "kb-pipeline-demo-scenarios",
"kb-app-demo-desktop",
"kb_executor_metadata_metaplex_token_metadata", "kb_executor_metadata_metaplex_token_metadata",
"kb_executor_metadata_spl_name_service", "kb_executor_metadata_spl_name_service",
"kb_executor_spl_account_compression", "kb_executor_spl_account_compression",
@@ -104,7 +105,7 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
@@ -147,42 +148,42 @@
] ]
}, },
{ {
"name": "file_kb_app_demo_debug", "name": "file_kb-app-demo-desktop_debug",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "debug", "level": "debug",
"path": "logs/devnet/kb_app_demo/debug.log", "path": "logs/devnet/kb-app-demo-desktop/debug.log",
"rotation": "daily", "rotation": "daily",
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
"name": "file_kb_app_demo_info", "name": "file_kb-app-demo-desktop_info",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "info", "level": "info",
"path": "logs/devnet/kb_app_demo/info.log", "path": "logs/devnet/kb-app-demo-desktop/info.log",
"rotation": "daily", "rotation": "daily",
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
"name": "file_kb_app_demo_error", "name": "file_kb-app-demo-desktop_error",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "error", "level": "error",
"path": "logs/devnet/kb_app_demo/error.jsonl", "path": "logs/devnet/kb-app-demo-desktop/error.jsonl",
"rotation": "daily", "rotation": "daily",
"format": "json", "format": "json",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
@@ -936,7 +937,8 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -949,7 +951,8 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -962,7 +965,8 @@
"format": "json", "format": "json",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -1662,7 +1666,7 @@
"format": "compact", "format": "compact",
"ansi": true, "ansi": true,
"targets": [ "targets": [
"kb_app_demo", "kb-app-demo-desktop",
"kb-lib.decoder.metadata.metaplex_token_metadata", "kb-lib.decoder.metadata.metaplex_token_metadata",
"kb-lib.decoder.solana.core", "kb-lib.decoder.solana.core",
"kb-lib.decoder.spl.associated_token_account", "kb-lib.decoder.spl.associated_token_account",
@@ -1691,6 +1695,7 @@
"kb-lib.materializer.token", "kb-lib.materializer.token",
"kb-lib.materializer.transaction", "kb-lib.materializer.transaction",
"kb-pipeline", "kb-pipeline",
"kb-pipeline-demo-scenarios",
"kb-onchain-transport", "kb-onchain-transport",
"kb-store", "kb-store",
"kb-wallet" "kb-wallet"
@@ -1745,7 +1750,7 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
@@ -1788,42 +1793,42 @@
] ]
}, },
{ {
"name": "file_kb_app_demo_debug", "name": "file_kb-app-demo-desktop_debug",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "debug", "level": "debug",
"path": "logs/mainnet_research/kb_app_demo/debug.log", "path": "logs/mainnet_research/kb-app-demo-desktop/debug.log",
"rotation": "daily", "rotation": "daily",
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
"name": "file_kb_app_demo_info", "name": "file_kb-app-demo-desktop_info",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "info", "level": "info",
"path": "logs/mainnet_research/kb_app_demo/info.log", "path": "logs/mainnet_research/kb-app-demo-desktop/info.log",
"rotation": "daily", "rotation": "daily",
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
"name": "file_kb_app_demo_error", "name": "file_kb-app-demo-desktop_error",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "error", "level": "error",
"path": "logs/mainnet_research/kb_app_demo/error.jsonl", "path": "logs/mainnet_research/kb-app-demo-desktop/error.jsonl",
"rotation": "daily", "rotation": "daily",
"format": "json", "format": "json",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
@@ -2577,7 +2582,8 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -2590,7 +2596,8 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -2603,7 +2610,8 @@
"format": "json", "format": "json",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -3503,7 +3511,7 @@
"format": "compact", "format": "compact",
"ansi": true, "ansi": true,
"targets": [ "targets": [
"kb_app_demo", "kb-app-demo-desktop",
"kb-lib.decoder.metadata.metaplex_token_metadata", "kb-lib.decoder.metadata.metaplex_token_metadata",
"kb-lib.decoder.solana.core", "kb-lib.decoder.solana.core",
"kb-lib.decoder.spl.associated_token_account", "kb-lib.decoder.spl.associated_token_account",
@@ -3532,6 +3540,7 @@
"kb-lib.materializer.token", "kb-lib.materializer.token",
"kb-lib.materializer.transaction", "kb-lib.materializer.transaction",
"kb-pipeline", "kb-pipeline",
"kb-pipeline-demo-scenarios",
"kb-onchain-transport", "kb-onchain-transport",
"kb-store", "kb-store",
"kb-wallet" "kb-wallet"
@@ -3586,7 +3595,7 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
@@ -3629,42 +3638,42 @@
] ]
}, },
{ {
"name": "file_kb_app_demo_debug", "name": "file_kb-app-demo-desktop_debug",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "debug", "level": "debug",
"path": "logs/mainnet/kb_app_demo/debug.log", "path": "logs/mainnet/kb-app-demo-desktop/debug.log",
"rotation": "daily", "rotation": "daily",
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
"name": "file_kb_app_demo_info", "name": "file_kb-app-demo-desktop_info",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "info", "level": "info",
"path": "logs/mainnet/kb_app_demo/info.log", "path": "logs/mainnet/kb-app-demo-desktop/info.log",
"rotation": "daily", "rotation": "daily",
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
"name": "file_kb_app_demo_error", "name": "file_kb-app-demo-desktop_error",
"enabled": true, "enabled": true,
"sink": "file", "sink": "file",
"level": "error", "level": "error",
"path": "logs/mainnet/kb_app_demo/error.jsonl", "path": "logs/mainnet/kb-app-demo-desktop/error.jsonl",
"rotation": "daily", "rotation": "daily",
"format": "json", "format": "json",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb_app_demo" "kb-app-demo-desktop"
] ]
}, },
{ {
@@ -4418,7 +4427,8 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -4431,7 +4441,8 @@
"format": "human", "format": "human",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {
@@ -4444,7 +4455,8 @@
"format": "json", "format": "json",
"ansi": false, "ansi": false,
"targets": [ "targets": [
"kb-pipeline" "kb-pipeline",
"kb-pipeline-demo-scenarios"
] ]
}, },
{ {

View File

@@ -0,0 +1,33 @@
# file: kb-app-demo-desktop/Cargo.toml
# version: 1
[package]
name = "kb-app-demo-desktop"
version.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[lib]
name = "kb_app_demo_desktop"
path = "src/lib.rs"
[[bin]]
name = "kb-app-demo-desktop"
path = "src/main.rs"
[build-dependencies]
tauri-build.workspace = true
[dependencies]
fs2.workspace = true
kb-core = { path = "../kb-core" }
serde.workspace = true
serde_json.workspace = true
tauri.workspace = true
tokio.workspace = true
tracing.workspace = true
ts-rs.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,12 @@
// file: kb-app-demo-desktop/build.rs
// version: 1
//! Build script for the desktop demo application.
#![forbid(unsafe_code)]
#![deny(unreachable_pub)]
#![warn(missing_docs)]
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,12 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Default desktop demo capability",
"windows": [
"splash",
"main"
],
"permissions": [
"core:default"
]
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 KiB

View File

@@ -0,0 +1,18 @@
<!-- file: kb-app-demo-desktop/frontend/main.html -->
<!-- version: 1 -->
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Khadhroony Bot3</title>
<style>body{font-family:sans-serif;margin:0;background:#111827;color:#f9fafb}main{max-width:900px;margin:8rem auto;padding:2rem}code{color:#93c5fd}</style>
</head>
<body>
<main>
<h1>Khadhroony Bot3</h1>
<p>La fenêtre splash de <code>kb-app-demo-desktop</code> est migrée.</p>
<p>La fenêtre principale sera complétée dans la prochaine tranche.</p>
</main>
</body>
</html>

View File

@@ -0,0 +1,8 @@
/* file: kb-app-demo-desktop/frontend/sass/splash.scss */
/* version: 1 */
html, body { width: 100%; height: 100%; margin: 0; background: transparent; overflow: hidden; }
body { display: grid; place-items: center; font-family: sans-serif; }
#splash-container { width: 100%; height: 100%; display: grid; place-content: center; justify-items: center; gap: 1rem; opacity: 0; background: radial-gradient(circle, #1f2937, #030712); color: #f9fafb; }
#splash-container img { width: min(55vw, 420px); max-height: 55vh; object-fit: contain; }
#splash-container h1 { margin: 0; letter-spacing: .08em; }
#messages-container { min-height: 1.5rem; color: #d1d5db; }

View File

@@ -0,0 +1,19 @@
<!-- file: kb-app-demo-desktop/frontend/splash.html -->
<!-- version: 1 -->
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Chargement — Khadhroony Bot3</title>
<link rel="stylesheet" href="sass/splash.scss" />
</head>
<body>
<main id="splash-container">
<img src="imgs/splash.png" alt="Khadhroony Bot3" />
<h1>Khadhroony Bot3</h1>
<div id="messages-container" aria-live="polite"></div>
</main>
<script type="module" src="ts/splash.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,40 @@
// file: kb-app-demo-desktop/frontend/ts/splash.ts
// version: 1
import { listen } from "@tauri-apps/api/event";
type SplashOrder = {
order: string;
msg: string | null;
status: string | null;
duration_ms: number | null;
};
const container = document.querySelector<HTMLElement>("#splash-container");
const messages = document.querySelector<HTMLElement>("#messages-container");
function duration(order: SplashOrder): number {
if (typeof order.duration_ms !== "number" || !Number.isFinite(order.duration_ms) || order.duration_ms <= 0) {
return 500;
}
return order.duration_ms;
}
async function applyOrder(order: SplashOrder): Promise<void> {
if (!container) {
return;
}
if (order.order === "add_msg" && messages && order.msg) {
messages.textContent = order.msg;
messages.dataset.status = order.status ?? "info";
return;
}
if (order.order === "fadein" || order.order === "fadeout") {
container.style.transition = `opacity ${duration(order)}ms ease-in-out`;
container.style.opacity = order.order === "fadein" ? "1" : "0";
}
}
void listen<SplashOrder>("splash", event => {
void applyOrder(event.payload);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,34 @@
{
"name": "kb-app-demo-desktop",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@andypf/json-viewer": "^2.8",
"@fltsci/tauri-plugin-tracing": "^0.3",
"@fortawesome/fontawesome-free": "^7.2",
"@tauri-apps/api": "^2.11",
"bootstrap": "^5.3",
"datatables.net-bs5": "^2.3",
"datatables.net-select-bs5": "^3.1",
"echarts": "^6.1",
"markdown-it": "^14.2",
"resize-observer-polyfill": "^1.5",
"simplebar": "^6.3"
},
"devDependencies": {
"@tauri-apps/cli": "^2.11",
"@types/bootstrap": "^5.2",
"@types/markdown-it": "^14.1",
"@types/node": "^25.9",
"sass-embedded": "^1.100",
"typescript": "^5.9",
"vite": "^8.1"
}
}

View File

@@ -0,0 +1,7 @@
// file: kb-app-demo-desktop/src/constants.rs
// version: 1
//! Constants for the desktop demo application.
/// Canonical tracing target for the desktop demo application.
pub(crate) const TRACING_TARGET: &str = "kb-app-demo-desktop";

View File

@@ -0,0 +1,93 @@
// file: kb-app-demo-desktop/src/lib.rs
// version: 1
//! Tauri desktop demo application for `khadhroony-bot3`.
#![forbid(unsafe_code)]
#![deny(unreachable_pub)]
#![warn(missing_docs)]
mod constants;
mod splash;
use tauri::Manager; // rust-rules: trait-import
/// Runs the desktop demo application.
pub async fn run() -> kb_core::Result<()> {
let builder = tauri::Builder::default().setup(|app| {
let splash_window = match app.get_webview_window("splash") {
std::option::Option::Some(window) => window,
std::option::Option::None => {
return std::result::Result::Err(std::boxed::Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"splash window is missing",
)));
},
};
let main_window = match app.get_webview_window("main") {
std::option::Option::Some(window) => window,
std::option::Option::None => {
return std::result::Result::Err(std::boxed::Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"main window is missing",
)));
},
};
tauri::async_runtime::spawn(async move {
let started_at = tokio::time::Instant::now();
crate::emit_splash_order(
&splash_window,
"fadein",
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(crate::SPLASH_FADE_MS),
);
crate::emit_splash_order(
&splash_window,
"add_msg",
std::option::Option::Some("Initialisation de Khadhroony Bot3..."),
std::option::Option::Some("info"),
std::option::Option::None,
);
crate::wait_until_minimum(started_at, crate::SPLASH_MINIMUM_MS).await;
crate::emit_splash_order(
&splash_window,
"fadeout",
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(crate::SPLASH_FADE_MS),
);
tokio::time::sleep(std::time::Duration::from_millis(crate::SPLASH_CLOSE_WAIT_MS)).await;
if let std::result::Result::Err(error) = splash_window.destroy() {
tracing::error!(target: crate::TRACING_TARGET, "cannot destroy splash window: {error:?}");
}
if let std::result::Result::Err(error) = main_window.show() {
tracing::error!(target: crate::TRACING_TARGET, "cannot show main window: {error:?}");
}
if let std::result::Result::Err(error) = main_window.set_focus() {
tracing::error!(target: crate::TRACING_TARGET, "cannot focus main window: {error:?}");
}
});
return std::result::Result::Ok(());
});
let run_result = builder.run(tauri::generate_context!());
return match run_result {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::tauri(format!(
"cannot run desktop demo application: {error:?}"
))),
};
}
/// Minimum startup splash duration before fade-out begins.
pub(crate) use self::splash::SPLASH_MINIMUM_MS;
/// Splash fade duration.
pub(crate) use self::splash::SPLASH_FADE_MS;
/// Delay after fade-out before destroying the splash window.
pub(crate) use self::splash::SPLASH_CLOSE_WAIT_MS;
/// Emits one order to the splash frontend.
pub(crate) use self::splash::emit_splash_order;
/// Waits for the minimum splash duration.
pub(crate) use self::splash::wait_until_minimum;
/// Canonical tracing target for the desktop demo application.
pub(crate) use self::constants::TRACING_TARGET;

View File

@@ -0,0 +1,42 @@
// file: kb-app-demo-desktop/src/main.rs
// version: 1
//! Binary entry point for the desktop demo application.
#![forbid(unsafe_code)]
#![deny(unreachable_pub)]
#![warn(missing_docs)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use fs2::FileExt; // rust-rules: trait-import
/// Starts the single desktop demo application instance.
#[tokio::main]
async fn main() -> std::process::ExitCode {
let mut lock_path = std::env::temp_dir();
lock_path.push("com_sasedev_kb_app_demo_desktop.lock");
let lock_file = match std::fs::File::create(&lock_path) {
std::result::Result::Ok(file) => file,
std::result::Result::Err(error) => {
eprintln!("cannot create application lock '{}': {error}", lock_path.display());
return std::process::ExitCode::FAILURE;
},
};
if let std::result::Result::Err(error) = lock_file.try_lock_exclusive() {
if error.kind() == std::io::ErrorKind::WouldBlock {
eprintln!("another kb-app-demo-desktop instance is already running");
return std::process::ExitCode::FAILURE;
}
eprintln!("cannot acquire application lock '{}': {error}", lock_path.display());
return std::process::ExitCode::FAILURE;
}
let _lock_file = lock_file;
let run_result = kb_app_demo_desktop::run().await;
return match run_result {
std::result::Result::Ok(()) => std::process::ExitCode::SUCCESS,
std::result::Result::Err(error) => {
eprintln!("application error: {error}");
std::process::ExitCode::FAILURE
},
};
}

View File

@@ -0,0 +1,84 @@
// file: kb-app-demo-desktop/src/splash.rs
// version: 1
//! Splash-window payloads and startup sequencing helpers.
use tauri::Emitter; // rust-rules: trait-import
use ts_rs::TS; // rust-rules: derive-import
/// Minimum splash duration before the main window is shown.
pub(crate) const SPLASH_MINIMUM_MS: u64 = 900;
/// Splash fade duration.
pub(crate) const SPLASH_FADE_MS: u32 = 500;
/// Delay after fade-out before destroying the splash window.
pub(crate) const SPLASH_CLOSE_WAIT_MS: u64 = 550;
/// Command sent from Rust to the splash frontend.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/kb_app_demo_desktop/splash/SplashOrder.ts")]
pub(crate) struct SplashOrder {
/// Command name.
pub(crate) order: std::string::String,
/// Optional message.
pub(crate) msg: std::option::Option<std::string::String>,
/// Optional status.
pub(crate) status: std::option::Option<std::string::String>,
/// Optional animation duration in milliseconds.
pub(crate) duration_ms: std::option::Option<u32>,
}
/// Emits one splash command.
pub(crate) fn emit_splash_order(
splash_window: &tauri::WebviewWindow,
order: &str,
msg: std::option::Option<&str>,
status: std::option::Option<&str>,
duration_ms: std::option::Option<u32>,
) {
let payload = SplashOrder {
order: order.to_string(),
msg: msg.map(std::string::ToString::to_string),
status: status.map(std::string::ToString::to_string),
duration_ms,
};
let emit_result = splash_window.emit("splash", payload);
if let std::result::Result::Err(error) = emit_result {
tracing::error!(target: crate::TRACING_TARGET, "cannot emit splash order '{order}': {error:?}");
}
}
/// Waits until the configured minimum splash duration has elapsed.
pub(crate) async fn wait_until_minimum(start: tokio::time::Instant, minimum_ms: u64) {
let minimum = std::time::Duration::from_millis(minimum_ms);
let elapsed = start.elapsed();
if elapsed >= minimum {
return;
}
tokio::time::sleep(minimum - elapsed).await;
}
#[cfg(test)]
mod tests {
#[test]
fn splash_order_serializes_duration_ms() {
let order = super::SplashOrder {
order: "fadein".to_string(),
msg: std::option::Option::None,
status: std::option::Option::None,
duration_ms: std::option::Option::Some(500),
};
let value_result = serde_json::to_value(order);
let value = match value_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("cannot serialize splash order: {error}"),
};
assert_eq!(value["duration_ms"], serde_json::json!(500));
}
#[test]
fn splash_timing_is_ordered() {
assert!(super::SPLASH_MINIMUM_MS > 0);
assert!(super::SPLASH_FADE_MS > 0);
assert!(super::SPLASH_CLOSE_WAIT_MS >= u64::from(super::SPLASH_FADE_MS));
}
}

View File

@@ -0,0 +1,50 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Khadhroony Bot3 Demo Desktop",
"version": "0.1.0",
"identifier": "com.sasedev.kb-app-demo-desktop",
"build": {
"beforeDevCommand": "npm run dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "npm run build",
"frontendDist": "./dist"
},
"app": {
"windows": [
{
"label": "splash",
"url": "splash.html",
"title": "Chargement — Khadhroony Bot3",
"width": 960,
"height": 637,
"resizable": false,
"decorations": false,
"transparent": true,
"center": true,
"alwaysOnTop": true
},
{
"label": "main",
"url": "main.html",
"title": "Khadhroony Bot3",
"width": 1200,
"height": 768,
"minWidth": 800,
"minHeight": 600,
"center": true,
"visible": false
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/favicon.png",
"icons/favicon.ico"
]
}
}

View File

@@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"types": [
"vite/client",
"node"
]
},
"include": [
"frontend",
"vite.config.ts"
]
}

View File

@@ -0,0 +1,84 @@
// file: kb-app-demo-desktop/vite.config.ts
// version: 2
import { defineConfig, normalizePath } from "vite";
import { NodePackageImporter } from "sass-embedded";
import { resolve } from "path";
const host = process.env.TAURI_DEV_HOST;
export default defineConfig(() => ({
envPrefix: ['VITE_', 'TAURI_ENV_*'],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent Vite from obscuring rust errors
clearScreen: false,
root: 'frontend', // Set this to your frontend directory
publicDir: 'public',
build: {
outDir: './dist', // Output directory for the build
emptyOutDir: true,
rollupOptions: {
input: {
"splash": normalizePath(resolve(__dirname, 'frontend/splash.html')),
"main": normalizePath(resolve(__dirname, 'frontend/main.html'))
},
output: {
entryFileNames: 'js/[name]-[hash].js',
chunkFileNames: 'js/chunks/[name]-[hash].js',
assetFileNames: (assetInfo) => {
const originalName = assetInfo.name ?? '';
const ext = originalName.substring(originalName.lastIndexOf('.') + 1).toLowerCase();
if (ext === 'js') {
return 'js/[name]-[hash][extname]';
}
// CSS
if (ext === 'css') {
return 'css/[name]-[hash][extname]';
}
if (['eot', 'otf', 'ttf', 'woff', 'woff2'].includes(ext)) {
return 'fonts/[name]-[hash][extname]';
}
if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'].includes(ext)) {
return 'imgs/[name][extname]';
}
if (['mp4', 'webm'].includes(ext)) {
return 'videos/[name][extname]';
}
return 'otherassets/[name][extname]';
},
},
},
minify: true,
sourcemap: false,
cssCodeSplit: true
},
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
silenceDeprecations: ["import", "color-functions", "global-builtin",],
verbose: false,
api: 'modern',
importers: [new NodePackageImporter()],
}
}
},
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: false,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: {
ignored: ["**/src/**"],
},
},
}));

View File

@@ -1,23 +0,0 @@
# file: kb-app-demo/Cargo.toml
# version: 2
[package]
name = "kb-app-demo"
version.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
kb-config = { path = "../kb-config" }
kb-core = { path = "../kb-core" }
kb-lib = { path = "../kb-lib" }
kb-logging = { path = "../kb-logging" }
kb-pipeline = { path = "../kb-pipeline" }
kb-program-ids = { path = "../kb-program-ids" }
kb-onchain-transport = { path = "../kb-onchain-transport" }
kb-store = { path = "../kb-store" }
kb-wallet = { path = "../kb-wallet" }
[lints]
workspace = true

View File

@@ -1,18 +0,0 @@
// file: kb-app-demo/src/main.rs
// version: 2
#![forbid(unsafe_code)]
#![deny(unreachable_pub)]
#![warn(missing_docs)]
//! Minimal migration smoke-test application.
fn main() {
println!("khadhroony-bot3 workspace scaffold");
println!("logging target: {}", kb_logging::tracing_target());
println!(
"pipeline: {} v{}",
kb_pipeline::CORE_EXTRACTION_PROCESSOR_NAME,
kb_pipeline::CORE_EXTRACTION_PROCESSOR_VERSION
);
}

View File

@@ -1319,7 +1319,7 @@ mod tests {
&& target.path == format!("logs/{directory}/app.log") && target.path == format!("logs/{directory}/app.log")
&& target.level == "debug" && target.level == "debug"
&& target.format == "human" && target.format == "human"
&& target.targets == std::vec!["kb_app_demo".to_string()]; && target.targets == std::vec!["kb-app-demo-desktop".to_string()];
})); }));
for crate_name in &operational_crates { for crate_name in &operational_crates {
for (suffix, level, format) in [ for (suffix, level, format) in [