0.1.0-1-alpha.2-fix.2
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -64,5 +64,7 @@ __pycache__/
|
|||||||
*.keystore
|
*.keystore
|
||||||
local.properties
|
local.properties
|
||||||
# Generated by scripts/build_reflex_tauri_wasm.py.
|
# Generated by scripts/build_reflex_tauri_wasm.py.
|
||||||
Tauri/game-reflex-poc/frontend/game_reflex_poc_tauri.js
|
crates/apps/game-reflex-poc-tauri/frontend/wasm/game_reflex_poc_wasm.js
|
||||||
Tauri/game-reflex-poc/frontend/game_reflex_poc_tauri_bg.wasm
|
crates/apps/game-reflex-poc-tauri/frontend/wasm/game_reflex_poc_wasm_bg.wasm
|
||||||
|
crates/apps/game-reflex-poc-tauri/frontend/wasm/game_reflex_poc_wasm.d.ts
|
||||||
|
crates/apps/game-reflex-poc-tauri/frontend/wasm/game_reflex_poc_wasm_bg.wasm.d.ts
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-reflex-poc/build.gradle
|
// file: Android/game-reflex-poc/build.gradle
|
||||||
// version: 12
|
// version: 13
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -18,7 +18,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-1-alpha.2.fix.1'
|
versionName '0.1.0-1-alpha.2.fix.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-snake-poc/build.gradle
|
// file: Android/game-snake-poc/build.gradle
|
||||||
// version: 12
|
// version: 13
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -18,7 +18,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-1-alpha.2.fix.1'
|
versionName '0.1.0-1-alpha.2.fix.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 29
|
# version: 30
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -15,10 +15,11 @@ members = [
|
|||||||
"crates/common/game-logging-lib",
|
"crates/common/game-logging-lib",
|
||||||
"crates/apps/game-android-entrypoint",
|
"crates/apps/game-android-entrypoint",
|
||||||
"crates/apps/game-reflex-poc-tauri",
|
"crates/apps/game-reflex-poc-tauri",
|
||||||
|
"crates/apps/game-reflex-poc-wasm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0-1-alpha.2.fix.1"
|
version = "0.1.0-1-alpha.2.fix.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
@@ -32,6 +33,7 @@ tracing-appender = "0.2.5"
|
|||||||
tracing-subscriber = "0.3.23"
|
tracing-subscriber = "0.3.23"
|
||||||
tauri = "2"
|
tauri = "2"
|
||||||
tauri-build = "2"
|
tauri-build = "2"
|
||||||
|
tauri-plugin-tracing = "^0.3"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
<!-- file: Tauri/README.md -->
|
|
||||||
<!-- version: 1 -->
|
|
||||||
|
|
||||||
# Tauri
|
|
||||||
|
|
||||||
Cette arborescence contient les frontends statiques des variantes Desktop Tauri.
|
|
||||||
|
|
||||||
Le backend Rust reste une crate sous `crates/apps/`, conformément à la règle du workspace Cargo unique.
|
|
||||||
|
|
||||||
Les fichiers générés par `wasm-bindgen` ne sont pas versionnés.
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
// file: Tauri/game-reflex-poc/frontend/main.js
|
|
||||||
// version: 1
|
|
||||||
|
|
||||||
import init, { ReflexWasmGame } from "./game_reflex_poc_tauri.js";
|
|
||||||
|
|
||||||
const FRAME_MS = 16;
|
|
||||||
|
|
||||||
function color(red, green, blue) {
|
|
||||||
return `rgb(${red} ${green} ${blue})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeCanvas(canvas) {
|
|
||||||
const ratio = window.devicePixelRatio || 1;
|
|
||||||
const width = Math.max(1, Math.floor(canvas.clientWidth * ratio));
|
|
||||||
const height = Math.max(1, Math.floor(canvas.clientHeight * ratio));
|
|
||||||
if (canvas.width !== width || canvas.height !== height) {
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(game, canvas, context, score) {
|
|
||||||
resizeCanvas(canvas);
|
|
||||||
context.fillStyle = color(game.background_red(), game.background_green(), game.background_blue());
|
|
||||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
const count = game.rectangle_count();
|
|
||||||
for (let index = 0; index < count; index += 1) {
|
|
||||||
context.fillStyle = color(game.rectangle_red(index), game.rectangle_green(index), game.rectangle_blue(index));
|
|
||||||
context.fillRect(
|
|
||||||
game.rectangle_x(index) * canvas.width,
|
|
||||||
game.rectangle_y(index) * canvas.height,
|
|
||||||
game.rectangle_width(index) * canvas.width,
|
|
||||||
game.rectangle_height(index) * canvas.height,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
score.value = `Score : ${game.score()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function start() {
|
|
||||||
await init();
|
|
||||||
|
|
||||||
const canvas = document.querySelector("#game");
|
|
||||||
const score = document.querySelector("#score");
|
|
||||||
const context = canvas.getContext("2d");
|
|
||||||
if (!(canvas instanceof HTMLCanvasElement) || !(score instanceof HTMLOutputElement) || context === null) {
|
|
||||||
throw new Error("Reflex Tauri frontend DOM is incomplete");
|
|
||||||
}
|
|
||||||
|
|
||||||
const game = new ReflexWasmGame();
|
|
||||||
canvas.addEventListener("pointerdown", (event) => {
|
|
||||||
const bounds = canvas.getBoundingClientRect();
|
|
||||||
const x = (event.clientX - bounds.left) / bounds.width;
|
|
||||||
const y = (event.clientY - bounds.top) / bounds.height;
|
|
||||||
game.pointer_down(x, y);
|
|
||||||
render(game, canvas, context, score);
|
|
||||||
});
|
|
||||||
|
|
||||||
let previous = performance.now();
|
|
||||||
function frame(now) {
|
|
||||||
while (now - previous >= FRAME_MS) {
|
|
||||||
game.tick();
|
|
||||||
previous += FRAME_MS;
|
|
||||||
}
|
|
||||||
render(game, canvas, context, score);
|
|
||||||
window.requestAnimationFrame(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(game, canvas, context, score);
|
|
||||||
window.requestAnimationFrame(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
start().catch((error) => {
|
|
||||||
console.error("Reflex Tauri WASM startup failed", error);
|
|
||||||
});
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/apps/game-reflex-poc-tauri/Cargo.toml
|
# file: crates/apps/game-reflex-poc-tauri/Cargo.toml
|
||||||
# version: 1
|
# version: 3
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "game-reflex-poc-tauri"
|
name = "game-reflex-poc-tauri"
|
||||||
@@ -9,24 +9,24 @@ license.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
name = "game_reflex_poc_tauri_lib"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[[bin]]
|
||||||
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
name = "game-reflex-poc-tauri"
|
||||||
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
path = "src/main.rs"
|
||||||
game-reflex-poc = { path = "../../games/game-reflex-poc" }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
||||||
tauri.workspace = true
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
wasm-bindgen.workspace = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build.workspace = true
|
tauri-build.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
engine-v1-platform-api = { path = "../../engines/engine-v1-platform-api" }
|
||||||
|
tauri.workspace = true
|
||||||
|
tauri-plugin-tracing.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
// file: crates/apps/game-reflex-poc-tauri/build.rs
|
// file: crates/apps/game-reflex-poc-tauri/build.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Tauri build script skipped for the WebAssembly library target.
|
//! Native Tauri build script for the Reflex Desktop WebView runner.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH");
|
|
||||||
match target_arch {
|
|
||||||
std::result::Result::Ok(value) if value == "wasm32" => return,
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
tauri_build::build();
|
tauri_build::build();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "Default capability for Reflex Tauri POC",
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": ["core:default", "tracing:default"]
|
||||||
|
}
|
||||||
@@ -4,14 +4,14 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>Reflex POC Tauri</title>
|
<title>Reflex POC Tauri</title>
|
||||||
<link rel="stylesheet" href="./style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main id="app">
|
<main id="app">
|
||||||
<canvas id="game" aria-label="Reflex POC"></canvas>
|
<canvas id="game" aria-label="Reflex POC"></canvas>
|
||||||
<output id="score">Score : 0</output>
|
<output id="score">Score : 0</output>
|
||||||
<output id="runtime">Tauri · WebView · WASM</output>
|
<output id="runtime">Initialisation…</output>
|
||||||
</main>
|
</main>
|
||||||
<script type="module" src="./main.js"></script>
|
<script type="module" src="/ts/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* file: Tauri/game-reflex-poc/frontend/style.css */
|
/* file: crates/apps/game-reflex-poc-tauri/frontend/style.css */
|
||||||
/* version: 1 */
|
/* version: 1 */
|
||||||
|
|
||||||
html,
|
html,
|
||||||
12
crates/apps/game-reflex-poc-tauri/frontend/ts/bridge.ts
Normal file
12
crates/apps/game-reflex-poc-tauri/frontend/ts/bridge.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/bridge.ts
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
|
export async function getRuntimeLabel(): Promise<string> {
|
||||||
|
return await invoke<string>("get_runtime_label");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function notifyFrontendReady(): Promise<void> {
|
||||||
|
await invoke("frontend_ready");
|
||||||
|
}
|
||||||
67
crates/apps/game-reflex-poc-tauri/frontend/ts/game.ts
Normal file
67
crates/apps/game-reflex-poc-tauri/frontend/ts/game.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/game.ts
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
import init, { ReflexWasmGame } from "../wasm/game_reflex_poc_wasm.js";
|
||||||
|
import { tracingDebug, tracingTrace } from "./logging";
|
||||||
|
|
||||||
|
const FRAME_MS = 16;
|
||||||
|
|
||||||
|
function color(red: number, green: number, blue: number): string {
|
||||||
|
return `rgb(${red} ${green} ${blue})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeCanvas(canvas: HTMLCanvasElement): void {
|
||||||
|
const ratio = window.devicePixelRatio || 1;
|
||||||
|
const width = Math.max(1, Math.floor(canvas.clientWidth * ratio));
|
||||||
|
const height = Math.max(1, Math.floor(canvas.clientHeight * ratio));
|
||||||
|
if (canvas.width !== width || canvas.height !== height) {
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(game: ReflexWasmGame, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, score: HTMLOutputElement): void {
|
||||||
|
resizeCanvas(canvas);
|
||||||
|
context.fillStyle = color(game.background_red(), game.background_green(), game.background_blue());
|
||||||
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
const count = game.rectangle_count();
|
||||||
|
for (let index = 0; index < count; index += 1) {
|
||||||
|
context.fillStyle = color(game.rectangle_red(index), game.rectangle_green(index), game.rectangle_blue(index));
|
||||||
|
context.fillRect(
|
||||||
|
game.rectangle_x(index) * canvas.width,
|
||||||
|
game.rectangle_y(index) * canvas.height,
|
||||||
|
game.rectangle_width(index) * canvas.width,
|
||||||
|
game.rectangle_height(index) * canvas.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
score.value = `Score : ${game.score()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startGame(canvas: HTMLCanvasElement, score: HTMLOutputElement): Promise<void> {
|
||||||
|
await init();
|
||||||
|
await tracingDebug("Reflex WASM module initialized");
|
||||||
|
const context = canvas.getContext("2d");
|
||||||
|
if (context === null) {
|
||||||
|
throw new Error("2D canvas context is unavailable");
|
||||||
|
}
|
||||||
|
const game = new ReflexWasmGame();
|
||||||
|
canvas.addEventListener("pointerdown", event => {
|
||||||
|
const bounds = canvas.getBoundingClientRect();
|
||||||
|
const x = (event.clientX - bounds.left) / bounds.width;
|
||||||
|
const y = (event.clientY - bounds.top) / bounds.height;
|
||||||
|
game.pointer_down(x, y);
|
||||||
|
render(game, canvas, context, score);
|
||||||
|
void tracingTrace(`pointerdown x=${x.toFixed(4)} y=${y.toFixed(4)} score=${game.score()}`);
|
||||||
|
});
|
||||||
|
let previous = performance.now();
|
||||||
|
function frame(now: number): void {
|
||||||
|
while (now - previous >= FRAME_MS) {
|
||||||
|
game.tick();
|
||||||
|
previous += FRAME_MS;
|
||||||
|
}
|
||||||
|
render(game, canvas, context, score);
|
||||||
|
window.requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
|
render(game, canvas, context, score);
|
||||||
|
window.requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
38
crates/apps/game-reflex-poc-tauri/frontend/ts/logging.ts
Normal file
38
crates/apps/game-reflex-poc-tauri/frontend/ts/logging.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/logging.ts
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
import { attachConsole, debug, error, info, trace, warn } from "@fltsci/tauri-plugin-tracing";
|
||||||
|
|
||||||
|
let detachRustConsole: (() => void) | null = null;
|
||||||
|
|
||||||
|
export async function initializeTracing(): Promise<void> {
|
||||||
|
detachRustConsole = await attachConsole();
|
||||||
|
await info("Reflex Tauri frontend tracing initialized", "games::tauri::reflex::frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function tracingTrace(message: string): Promise<void> {
|
||||||
|
await trace(message, "games::tauri::reflex::frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function tracingDebug(message: string): Promise<void> {
|
||||||
|
await debug(message, "games::tauri::reflex::frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function tracingInfo(message: string): Promise<void> {
|
||||||
|
await info(message, "games::tauri::reflex::frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function tracingWarn(message: string): Promise<void> {
|
||||||
|
await warn(message, "games::tauri::reflex::frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function tracingError(message: string): Promise<void> {
|
||||||
|
await error(message, "games::tauri::reflex::frontend");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detachTracingConsole(): void {
|
||||||
|
if (detachRustConsole !== null) {
|
||||||
|
detachRustConsole();
|
||||||
|
detachRustConsole = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts
Normal file
25
crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/frontend/ts/main.ts
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
import { getRuntimeLabel, notifyFrontendReady } from "./bridge";
|
||||||
|
import { startGame } from "./game";
|
||||||
|
import { initializeTracing, tracingError, tracingInfo } from "./logging";
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
await initializeTracing();
|
||||||
|
const canvas = document.querySelector<HTMLCanvasElement>("#game");
|
||||||
|
const score = document.querySelector<HTMLOutputElement>("#score");
|
||||||
|
const runtime = document.querySelector<HTMLOutputElement>("#runtime");
|
||||||
|
if (canvas === null || score === null || runtime === null) {
|
||||||
|
throw new Error("Reflex Tauri frontend DOM is incomplete");
|
||||||
|
}
|
||||||
|
runtime.value = await getRuntimeLabel();
|
||||||
|
await startGame(canvas, score);
|
||||||
|
await notifyFrontendReady();
|
||||||
|
await tracingInfo("Reflex Tauri frontend started");
|
||||||
|
}
|
||||||
|
|
||||||
|
void main().catch(async caughtError => {
|
||||||
|
const message = caughtError instanceof Error ? caughtError.stack ?? caughtError.message : String(caughtError);
|
||||||
|
await tracingError(`Reflex Tauri frontend startup failed: ${message}`);
|
||||||
|
});
|
||||||
20
crates/apps/game-reflex-poc-tauri/package.json
Normal file
20
crates/apps/game-reflex-poc-tauri/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "game-reflex-poc-tauri",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0-1-alpha.2.fix.2",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fltsci/tauri-plugin-tracing": "^0.3",
|
||||||
|
"@tauri-apps/api": "^2.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tauri-apps/cli": "^2.11",
|
||||||
|
"@types/node": "^26.1",
|
||||||
|
"typescript": "^7.0",
|
||||||
|
"vite": "^8.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
// file: crates/apps/game-reflex-poc-tauri/src/lib.rs
|
// file: crates/apps/game-reflex-poc-tauri/src/lib.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
|
//! Tauri desktop application facade for the Reflex WebAssembly POC.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![deny(unreachable_pub)]
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! WebAssembly adapter for the Reflex POC when hosted by the Tauri variant.
|
mod runtime;
|
||||||
|
mod tauri;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
/// Runs the Reflex Tauri desktop application.
|
||||||
mod wasm_runtime;
|
pub use self::tauri::run;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
/// Records that the Vite/TypeScript frontend reached its ready state.
|
||||||
/// Re-export of the Reflex WebAssembly runtime adapter.
|
pub(crate) use self::runtime::record_frontend_ready;
|
||||||
pub use self::wasm_runtime::ReflexWasmGame;
|
/// Returns the runtime label exposed by the Tauri bridge.
|
||||||
|
pub(crate) use self::runtime::runtime_label;
|
||||||
|
|||||||
@@ -1,25 +1,20 @@
|
|||||||
// file: crates/apps/game-reflex-poc-tauri/src/main.rs
|
// file: crates/apps/game-reflex-poc-tauri/src/main.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
|
//! Binary entry point for the Reflex Tauri desktop application.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![deny(unreachable_pub)]
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
//! Native Tauri host for the local Reflex WebAssembly POC.
|
fn main() -> std::process::ExitCode {
|
||||||
|
let result = game_reflex_poc_tauri_lib::run();
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
return match result {
|
||||||
fn main() {
|
std::result::Result::Ok(()) => std::process::ExitCode::SUCCESS,
|
||||||
let result = tauri::Builder::default().run(tauri::generate_context!());
|
|
||||||
match result {
|
|
||||||
std::result::Result::Ok(()) => {},
|
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
eprintln!("Reflex POC Tauri runner failed: {error}");
|
eprintln!("Reflex Tauri application error: {error}");
|
||||||
|
std::process::ExitCode::FAILURE
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn main() {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
crates/apps/game-reflex-poc-tauri/src/runtime.rs
Normal file
18
crates/apps/game-reflex-poc-tauri/src/runtime.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/src/runtime.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Stable label for the current Tauri/WASM runtime combination.
|
||||||
|
pub(crate) fn runtime_label() -> &'static str {
|
||||||
|
return "Desktop / TauriWebView / Wasm / KeyboardMouse";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Records that the frontend completed its startup sequence.
|
||||||
|
pub(crate) fn record_frontend_ready() {
|
||||||
|
tracing::info!(
|
||||||
|
target: "games::tauri::reflex",
|
||||||
|
action = "frontend_ready",
|
||||||
|
runtime = crate::runtime_label(),
|
||||||
|
"Reflex Tauri frontend ready"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
39
crates/apps/game-reflex-poc-tauri/src/tauri.rs
Normal file
39
crates/apps/game-reflex-poc-tauri/src/tauri.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/src/tauri.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_runtime_label() -> String {
|
||||||
|
return crate::runtime_label().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn frontend_ready() {
|
||||||
|
crate::record_frontend_ready();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
||||||
|
let tracing_plugin = tauri_plugin_tracing::Builder::new().build::<tauri::Wry>();
|
||||||
|
return builder.plugin(tracing_plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::question_mark_used)] // Tauri generates the question-mark operator internally for command dispatch.
|
||||||
|
fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
||||||
|
return builder.invoke_handler(tauri::generate_handler![get_runtime_label, frontend_ready]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the Reflex Tauri desktop application.
|
||||||
|
pub fn run() -> std::result::Result<(), String> {
|
||||||
|
tracing::info!(
|
||||||
|
target: "games::tauri::reflex",
|
||||||
|
action = "runtime_start",
|
||||||
|
"Starting Reflex Tauri runtime"
|
||||||
|
);
|
||||||
|
let builder = tauri::Builder::default();
|
||||||
|
let builder = configure_plugins(builder);
|
||||||
|
let builder = configure_commands(builder);
|
||||||
|
return match builder.run(tauri::generate_context!()) {
|
||||||
|
std::result::Result::Ok(()) => std::result::Result::Ok(()),
|
||||||
|
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,14 +1,24 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Reflex POC Tauri",
|
"productName": "Reflex POC Tauri",
|
||||||
"version": "0.1.0-1-alpha.2.fix.1",
|
"version": "0.1.0-1-alpha.2.fix.2",
|
||||||
"identifier": "com.sasedev.games.reflex.tauri",
|
"identifier": "com.sasedev.games.reflex.tauri",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../../../Tauri/game-reflex-poc/frontend"
|
"beforeDevCommand": {
|
||||||
|
"script": "python3 ../../../scripts/build_reflex_tauri_wasm.py && npm run dev",
|
||||||
|
"cwd": "."
|
||||||
|
},
|
||||||
|
"devUrl": "http://localhost:1432",
|
||||||
|
"beforeBuildCommand": {
|
||||||
|
"script": "python3 ../../../scripts/build_reflex_tauri_wasm.py && npm run build",
|
||||||
|
"cwd": "."
|
||||||
|
},
|
||||||
|
"frontendDist": "../../../builds/sasedev-games/game-reflex-poc-tauri/dist"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
|
"label": "main",
|
||||||
"title": "Reflex POC Tauri",
|
"title": "Reflex POC Tauri",
|
||||||
"width": 405,
|
"width": 405,
|
||||||
"height": 720,
|
"height": 720,
|
||||||
@@ -16,10 +26,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost"
|
"csp": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost http://localhost:1432 ws://localhost:1433"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": false
|
"active": false,
|
||||||
|
"icon": ["icons/icon.png"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
crates/apps/game-reflex-poc-tauri/tsconfig.json
Normal file
19
crates/apps/game-reflex-poc-tauri/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["frontend/**/*.ts", "vite.config.ts"]
|
||||||
|
}
|
||||||
36
crates/apps/game-reflex-poc-tauri/vite.config.ts
Normal file
36
crates/apps/game-reflex-poc-tauri/vite.config.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-tauri/vite.config.ts
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { resolve } from "node:path";
|
||||||
|
import { defineConfig, normalizePath } from "vite";
|
||||||
|
|
||||||
|
const appRoot = fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
const frontendRoot = normalizePath(resolve(appRoot, "frontend"));
|
||||||
|
const frontendDist = normalizePath(resolve(appRoot, "../../../builds/sasedev-games/game-reflex-poc-tauri/dist"));
|
||||||
|
const devHost = process.env.TAURI_DEV_HOST;
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
clearScreen: false,
|
||||||
|
root: frontendRoot,
|
||||||
|
publicDir: false,
|
||||||
|
build: {
|
||||||
|
outDir: frontendDist,
|
||||||
|
emptyOutDir: true,
|
||||||
|
minify: true,
|
||||||
|
sourcemap: false,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 1432,
|
||||||
|
strictPort: true,
|
||||||
|
host: devHost || false,
|
||||||
|
ws: {
|
||||||
|
protocol: "ws",
|
||||||
|
host: devHost || "localhost",
|
||||||
|
port: 1433,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
ignored: ["**/src/**"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
22
crates/apps/game-reflex-poc-wasm/Cargo.toml
Normal file
22
crates/apps/game-reflex-poc-wasm/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# file: crates/apps/game-reflex-poc-wasm/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "game-reflex-poc-wasm"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
engine-v1-common = { path = "../../engines/engine-v1-common" }
|
||||||
|
game-reflex-poc = { path = "../../games/game-reflex-poc" }
|
||||||
|
wasm-bindgen.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
13
crates/apps/game-reflex-poc-wasm/src/lib.rs
Normal file
13
crates/apps/game-reflex-poc-wasm/src/lib.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// file: crates/apps/game-reflex-poc-wasm/src/lib.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! WebAssembly adapter facade for the Reflex POC.
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![deny(unreachable_pub)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
mod runtime;
|
||||||
|
|
||||||
|
/// Re-export of the Reflex WebAssembly runtime adapter.
|
||||||
|
pub use self::runtime::ReflexWasmGame;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// file: crates/apps/game-reflex-poc-tauri/src/wasm_runtime.rs
|
// file: crates/apps/game-reflex-poc-wasm/src/runtime.rs
|
||||||
// version: 1
|
// version: 1
|
||||||
|
|
||||||
/// WebAssembly-owned Reflex game state consumed by the local Tauri WebView.
|
/// WebAssembly-owned Reflex game state consumed by the Tauri WebView.
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||||
pub struct ReflexWasmGame {
|
pub struct ReflexWasmGame {
|
||||||
runner: engine_v1_common::FixedStepRunner,
|
runner: engine_v1_common::FixedStepRunner,
|
||||||
@@ -10,7 +10,7 @@ pub struct ReflexWasmGame {
|
|||||||
|
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||||
impl ReflexWasmGame {
|
impl ReflexWasmGame {
|
||||||
/// Creates a fresh Reflex POC session using the same gameplay crate as native Desktop.
|
/// Creates a fresh Reflex POC session using the shared gameplay crate.
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen(constructor)]
|
#[wasm_bindgen::prelude::wasm_bindgen(constructor)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
@@ -65,7 +65,7 @@ impl ReflexWasmGame {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's normalized left coordinate or `-1.0` if the index is absent.
|
/// Returns one rectangle's normalized left coordinate or `-1.0` if absent.
|
||||||
pub fn rectangle_x(&self, index: u32) -> f32 {
|
pub fn rectangle_x(&self, index: u32) -> f32 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.rect().x(),
|
Some(rectangle) => rectangle.rect().x(),
|
||||||
@@ -73,7 +73,7 @@ impl ReflexWasmGame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's normalized top coordinate or `-1.0` if the index is absent.
|
/// Returns one rectangle's normalized top coordinate or `-1.0` if absent.
|
||||||
pub fn rectangle_y(&self, index: u32) -> f32 {
|
pub fn rectangle_y(&self, index: u32) -> f32 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.rect().y(),
|
Some(rectangle) => rectangle.rect().y(),
|
||||||
@@ -81,7 +81,7 @@ impl ReflexWasmGame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's normalized width or `0.0` if the index is absent.
|
/// Returns one rectangle's normalized width or `0.0` if absent.
|
||||||
pub fn rectangle_width(&self, index: u32) -> f32 {
|
pub fn rectangle_width(&self, index: u32) -> f32 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.rect().width(),
|
Some(rectangle) => rectangle.rect().width(),
|
||||||
@@ -89,7 +89,7 @@ impl ReflexWasmGame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's normalized height or `0.0` if the index is absent.
|
/// Returns one rectangle's normalized height or `0.0` if absent.
|
||||||
pub fn rectangle_height(&self, index: u32) -> f32 {
|
pub fn rectangle_height(&self, index: u32) -> f32 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.rect().height(),
|
Some(rectangle) => rectangle.rect().height(),
|
||||||
@@ -97,7 +97,7 @@ impl ReflexWasmGame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's red channel or zero if the index is absent.
|
/// Returns one rectangle's red channel or zero if absent.
|
||||||
pub fn rectangle_red(&self, index: u32) -> u8 {
|
pub fn rectangle_red(&self, index: u32) -> u8 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.color().red(),
|
Some(rectangle) => rectangle.color().red(),
|
||||||
@@ -105,7 +105,7 @@ impl ReflexWasmGame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's green channel or zero if the index is absent.
|
/// Returns one rectangle's green channel or zero if absent.
|
||||||
pub fn rectangle_green(&self, index: u32) -> u8 {
|
pub fn rectangle_green(&self, index: u32) -> u8 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.color().green(),
|
Some(rectangle) => rectangle.color().green(),
|
||||||
@@ -113,7 +113,7 @@ impl ReflexWasmGame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one rectangle's blue channel or zero if the index is absent.
|
/// Returns one rectangle's blue channel or zero if absent.
|
||||||
pub fn rectangle_blue(&self, index: u32) -> u8 {
|
pub fn rectangle_blue(&self, index: u32) -> u8 {
|
||||||
return match self.rectangle(index) {
|
return match self.rectangle(index) {
|
||||||
Some(rectangle) => rectangle.color().blue(),
|
Some(rectangle) => rectangle.color().blue(),
|
||||||
3
deltas/0.1.0/1-alpha.2.fix.2.delete.txt
Normal file
3
deltas/0.1.0/1-alpha.2.fix.2.delete.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Tauri/game-reflex-poc
|
||||||
|
Tauri/README.md
|
||||||
|
crates/apps/game-reflex-poc-tauri/src/wasm_runtime.rs
|
||||||
154
deltas/0.1.0/1-alpha.2.fix.2.md
Normal file
154
deltas/0.1.0/1-alpha.2.fix.2.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<!-- file: deltas/0.1.0/1-alpha.2.fix.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.1.0-1-alpha.2.fix.2
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
Base déclarée : `0.1.0-1-alpha.2.fix.1`, techniquement compilable mais architecture Tauri/WASM rejetée avant validation fonctionnelle.
|
||||||
|
|
||||||
|
## Réorganisation
|
||||||
|
|
||||||
|
Le POC est réaligné sur le modèle des apps Desk KSP.
|
||||||
|
|
||||||
|
### Tauri
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/apps/game-reflex-poc-tauri/
|
||||||
|
├── src/lib.rs
|
||||||
|
├── src/tauri.rs
|
||||||
|
├── src/runtime.rs
|
||||||
|
├── src/main.rs
|
||||||
|
├── frontend/
|
||||||
|
├── capabilities/
|
||||||
|
├── package.json
|
||||||
|
├── tsconfig.json
|
||||||
|
├── vite.config.ts
|
||||||
|
└── tauri.conf.json
|
||||||
|
```
|
||||||
|
|
||||||
|
`lib.rs` reste une façade. `tauri.rs` assemble Tauri et expose les commandes qui délèguent aux modules propriétaires.
|
||||||
|
|
||||||
|
### WASM
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/apps/game-reflex-poc-wasm/
|
||||||
|
├── src/lib.rs
|
||||||
|
└── src/runtime.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
La crate WASM ne dépend pas de Tauri. La crate Tauri ne compile plus elle-même en `wasm32`.
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
Le frontend utilise Vite + TypeScript.
|
||||||
|
|
||||||
|
Le hook Tauri `beforeDevCommand` exécute :
|
||||||
|
|
||||||
|
```text
|
||||||
|
python3 ../../../scripts/build_reflex_tauri_wasm.py && npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Le hook `beforeBuildCommand` exécute le build WASM puis `npm run build`.
|
||||||
|
|
||||||
|
`npm run build` n'est donc pas une gate manuelle indépendante.
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
Backend :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tracing
|
||||||
|
tauri-plugin-tracing
|
||||||
|
```
|
||||||
|
|
||||||
|
Frontend :
|
||||||
|
|
||||||
|
```text
|
||||||
|
@fltsci/tauri-plugin-tracing
|
||||||
|
```
|
||||||
|
|
||||||
|
La capability inclut :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tracing:default
|
||||||
|
```
|
||||||
|
|
||||||
|
Le frontend trace startup, chargement WASM, pointeur, score et erreurs via le plugin.
|
||||||
|
|
||||||
|
## Suppressions nécessaires
|
||||||
|
|
||||||
|
Un ZIP delta ne supprime pas les anciens fichiers lors d'un simple overlay.
|
||||||
|
|
||||||
|
Après extraction du delta, appliquer la liste :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
while IFS= read -r path; do
|
||||||
|
rm -rf -- "$path"
|
||||||
|
done < deltas/0.1.0/1-alpha.2.fix.2.delete.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette suppression retire uniquement l'ancien prototype statique et l'ancien module WASM interne à la crate Tauri.
|
||||||
|
|
||||||
|
## Installation frontend
|
||||||
|
|
||||||
|
Depuis :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd crates/apps/game-reflex-poc-tauri
|
||||||
|
npm install
|
||||||
|
cd ../../..
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas history
|
||||||
|
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
cargo test -p engine-v1-platform-api --all-targets --all-features
|
||||||
|
cargo test -p game-reflex-poc --all-targets --all-features
|
||||||
|
cargo test -p game-reflex-poc-wasm --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gate WASM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/build_reflex_tauri_wasm.py
|
||||||
|
|
||||||
|
test -f crates/apps/game-reflex-poc-tauri/frontend/wasm/game_reflex_poc_wasm.js
|
||||||
|
test -f crates/apps/game-reflex-poc-tauri/frontend/wasm/game_reflex_poc_wasm_bg.wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Smoke Tauri
|
||||||
|
|
||||||
|
Depuis la crate :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd crates/apps/game-reflex-poc-tauri
|
||||||
|
cargo tauri dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Tauri lance lui-même Vite et le build WASM.
|
||||||
|
|
||||||
|
Critères :
|
||||||
|
|
||||||
|
- fenêtre locale 405 × 720 ;
|
||||||
|
- frontend Vite/TypeScript ;
|
||||||
|
- cible Reflex visible ;
|
||||||
|
- clic comptabilisé et cible déplacée ;
|
||||||
|
- score visible ;
|
||||||
|
- traces frontend visibles via le plugin tracing ;
|
||||||
|
- aucun site distant.
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
Si les gates et le smoke passent, `0.1.0-1-alpha.2.fix.2` valide le POC restructuré.
|
||||||
|
|
||||||
|
En cas d'échec imputable au projet, produire `0.1.0-1-alpha.2.fix.3`.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/000-README.md -->
|
<!-- file: docs/000-README.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 15 -->
|
||||||
|
|
||||||
# Documentation games.sasedev
|
# Documentation games.sasedev
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/001-WORKSPACE_ARCHITECTURE.md -->
|
<!-- file: docs/architecture/001-WORKSPACE_ARCHITECTURE.md -->
|
||||||
<!-- version: 4 -->
|
<!-- version: 5 -->
|
||||||
|
|
||||||
# Architecture du workspace
|
# Architecture du workspace
|
||||||
|
|
||||||
@@ -21,13 +21,12 @@ games.sasedev/
|
|||||||
│ └── apps/
|
│ └── apps/
|
||||||
│ ├── game-reflex-poc-desktop/
|
│ ├── game-reflex-poc-desktop/
|
||||||
│ ├── game-snake-poc-desktop/
|
│ ├── game-snake-poc-desktop/
|
||||||
│ └── game-reflex-poc-tauri/
|
│ ├── game-reflex-poc-tauri/
|
||||||
|
│ └── game-reflex-poc-wasm/
|
||||||
├── assets/
|
├── assets/
|
||||||
│ ├── common/
|
│ ├── common/
|
||||||
│ ├── game-reflex-poc/
|
│ ├── game-reflex-poc/
|
||||||
│ └── game-snake-poc/
|
│ └── game-snake-poc/
|
||||||
├── Tauri/
|
|
||||||
│ └── game-reflex-poc/
|
|
||||||
├── Android/
|
├── Android/
|
||||||
│ ├── common/
|
│ ├── common/
|
||||||
│ ├── game-reflex-poc/
|
│ ├── game-reflex-poc/
|
||||||
@@ -80,3 +79,5 @@ La variante Tauri est distincte du runner SDL3 natif.
|
|||||||
- un binaire natif Tauri qui héberge la WebView locale.
|
- un binaire natif Tauri qui héberge la WebView locale.
|
||||||
|
|
||||||
Le frontend statique sous `Tauri/game-reflex-poc/frontend/` pilote un Canvas et appelle l'adaptateur WASM. Les règles Reflex restent exclusivement dans `game-reflex-poc`.
|
Le frontend statique sous `Tauri/game-reflex-poc/frontend/` pilote un Canvas et appelle l'adaptateur WASM. Les règles Reflex restent exclusivement dans `game-reflex-poc`.
|
||||||
|
|
||||||
|
La crate Tauri suit le modèle des apps Desk KSP : façade `lib.rs`, pont `tauri.rs`, modules propriétaires séparés et frontend Vite/TypeScript local à la crate.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/development/008-WASM_TAURI_POC.md -->
|
<!-- file: docs/development/008-WASM_TAURI_POC.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# POC WebAssembly embarqué dans Tauri
|
# POC WebAssembly embarqué dans Tauri
|
||||||
|
|
||||||
@@ -111,3 +111,37 @@ crates/apps/game-reflex-poc-tauri/icons/icon.png
|
|||||||
```
|
```
|
||||||
|
|
||||||
Il s'agit d'une icône minimale de développement ; l'identité graphique définitive viendra plus tard.
|
Il s'agit d'une icône minimale de développement ; l'identité graphique définitive viendra plus tard.
|
||||||
|
|
||||||
|
## Réorganisation fix.2
|
||||||
|
|
||||||
|
La crate Tauri et la crate WASM sont désormais séparées.
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/apps/game-reflex-poc-tauri/
|
||||||
|
├── src/lib.rs
|
||||||
|
├── src/tauri.rs
|
||||||
|
├── src/runtime.rs
|
||||||
|
├── src/main.rs
|
||||||
|
├── frontend/
|
||||||
|
├── package.json
|
||||||
|
├── tsconfig.json
|
||||||
|
├── vite.config.ts
|
||||||
|
└── tauri.conf.json
|
||||||
|
|
||||||
|
crates/apps/game-reflex-poc-wasm/
|
||||||
|
├── src/lib.rs
|
||||||
|
└── src/runtime.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
`lib.rs` joue le rôle de façade. `tauri.rs` contient le pont Web/Rust et l'assemblage Tauri. Les fonctions métier/runtime sont portées par leurs modules puis appelées par les commandes Tauri.
|
||||||
|
|
||||||
|
Le frontend utilise Vite + TypeScript. Le build WASM reste séparé et produit les bindings `wasm-bindgen` dans `frontend/wasm/`.
|
||||||
|
|
||||||
|
Le logging est unifié avec `tracing` :
|
||||||
|
|
||||||
|
- `tracing` côté Rust ;
|
||||||
|
- `tauri-plugin-tracing` côté Tauri ;
|
||||||
|
- `@fltsci/tauri-plugin-tracing` côté TypeScript ;
|
||||||
|
- capability `tracing:default`.
|
||||||
|
|
||||||
|
Les événements frontend utiles sont donc remontés dans le même système de traces que le backend Rust.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/FILE_CONTRACTS.md -->
|
<!-- file: docs/rules/FILE_CONTRACTS.md -->
|
||||||
<!-- version: 4 -->
|
<!-- version: 5 -->
|
||||||
|
|
||||||
# Contrats des fichiers principaux
|
# Contrats des fichiers principaux
|
||||||
|
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
## Tauri
|
## Tauri
|
||||||
|
|
||||||
- `Tauri/<game>/frontend/` contient le frontend Web statique versionné d'une variante Tauri.
|
- `crates/apps/<game>-tauri/frontend/` contient le frontend Vite/TypeScript versionné.
|
||||||
|
- `crates/apps/<game>-tauri/src/lib.rs` est la façade Rust de l'app Tauri.
|
||||||
|
- `crates/apps/<game>-tauri/src/tauri.rs` assemble Tauri et porte le pont Web/Rust.
|
||||||
|
- `crates/apps/<game>-wasm/` contient l'adaptation WebAssembly distincte.
|
||||||
- Les bindings JavaScript et modules `.wasm` générés restent ignorés par Git.
|
- Les bindings JavaScript et modules `.wasm` générés restent ignorés par Git.
|
||||||
- Le backend Rust Tauri reste sous `crates/apps/`.
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Règles d'exécution des commandes
|
# Règles d'exécution des commandes
|
||||||
|
|
||||||
@@ -53,10 +53,12 @@
|
|||||||
|
|
||||||
- **CMD-WEB-001** — Aucun gestionnaire de paquets JavaScript ni build Web n'est exécuté tant qu'un frontend Web réel n'a pas été introduit dans le dépôt.
|
- **CMD-WEB-001** — Aucun gestionnaire de paquets JavaScript ni build Web n'est exécuté tant qu'un frontend Web réel n'a pas été introduit dans le dépôt.
|
||||||
- **CMD-WEB-002** — Lorsqu'une cible Web existe, ses commandes de build et test sont documentées avant d'être ajoutées aux gates.
|
- **CMD-WEB-002** — Lorsqu'une cible Web existe, ses commandes de build et test sont documentées avant d'être ajoutées aux gates.
|
||||||
- **CMD-WEB-003** — Le POC Tauri/WASM utilise `scripts/build_reflex_tauri_wasm.py`; aucun gestionnaire de paquets JavaScript ni serveur Web externe n'est requis pour cette variante.
|
- **CMD-WEB-003** — Le POC Tauri/WASM utilise `scripts/build_reflex_tauri_wasm.py` pour WASM et Vite/TypeScript pour le frontend local ; aucun site distant n'est requis.
|
||||||
- **CMD-WEB-004** — Les fichiers produits par `wasm-bindgen` sont générés localement et ne sont pas commités.
|
- **CMD-WEB-004** — Les fichiers produits par `wasm-bindgen` sont générés localement et ne sont pas commités.
|
||||||
|
|
||||||
## Git et fichiers générés
|
## Git et fichiers générés
|
||||||
|
|
||||||
- **CMD-GIT-001** — Les commandes Git destructives (`reset --hard`, nettoyage forcé, réécriture non demandée) ne sont jamais utilisées pour remettre artificiellement le workspace en état.
|
- **CMD-GIT-001** — Les commandes Git destructives (`reset --hard`, nettoyage forcé, réécriture non demandée) ne sont jamais utilisées pour remettre artificiellement le workspace en état.
|
||||||
- **CMD-GIT-002** — Les fichiers générés ne sont pas commités sauf contrat explicite du dépôt ou exigence de distribution.
|
- **CMD-GIT-002** — Les fichiers générés ne sont pas commités sauf contrat explicite du dépôt ou exigence de distribution.
|
||||||
|
|
||||||
|
- **CMD-WEB-005** — `npm run dev` et `npm run build` du frontend Tauri sont pilotés par les hooks Tauri ; ils ne constituent pas des gates manuelles indépendantes.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_PROJECT.md -->
|
<!-- file: docs/rules/RULES_PROJECT.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Règles spécifiques games.sasedev
|
# Règles spécifiques games.sasedev
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
- **GAME-PLATFORM-004** — Ads et Billing sont des capacités optionnelles ; le gameplay reste fonctionnel lorsqu'elles sont disponibles, désactivées ou non supportées.
|
- **GAME-PLATFORM-004** — Ads et Billing sont des capacités optionnelles ; le gameplay reste fonctionnel lorsqu'elles sont disponibles, désactivées ou non supportées.
|
||||||
- **GAME-PLATFORM-005** — Un backend de monétisation est spécifique à sa plateforme et à sa distribution ; une intégration Android n'est pas réutilisée implicitement sur Web ou Desktop.
|
- **GAME-PLATFORM-005** — Un backend de monétisation est spécifique à sa plateforme et à sa distribution ; une intégration Android n'est pas réutilisée implicitement sur Web ou Desktop.
|
||||||
- **GAME-PLATFORM-006** — Le runner Desktop natif SDL3 est la forme Desktop par défaut. Une variante Tauri peut coexister uniquement lorsqu'un besoin explicite le justifie et consomme la même crate lib de jeu.
|
- **GAME-PLATFORM-006** — Le runner Desktop natif SDL3 est la forme Desktop par défaut. Une variante Tauri peut coexister uniquement lorsqu'un besoin explicite le justifie et consomme la même crate lib de jeu.
|
||||||
- **GAME-PLATFORM-007** — Une variante Tauri conserve son backend Rust sous `crates/apps/`; son frontend Web statique réside sous `Tauri/<game>/` et ne duplique pas le gameplay Rust.
|
- **GAME-PLATFORM-007** — Une variante Tauri conserve son backend Rust sous `crates/apps/`; son frontend Vite/TypeScript réside dans la même crate sous `frontend/` et ne duplique pas le gameplay Rust.
|
||||||
- **GAME-PLATFORM-008** — Le gameplay compilé en WebAssembly est adapté par une frontière dédiée ; le code JavaScript orchestre la WebView, les événements et le rendu Canvas mais ne réimplémente pas les règles du jeu.
|
- **GAME-PLATFORM-008** — Le gameplay compilé en WebAssembly est adapté par une frontière dédiée ; le code JavaScript orchestre la WebView, les événements et le rendu Canvas mais ne réimplémente pas les règles du jeu.
|
||||||
|
|
||||||
## POC
|
## POC
|
||||||
@@ -63,3 +63,7 @@
|
|||||||
- **GAME-TRACE-002** — `tracing-subscriber` compose les subscribers applicatifs et de test ; une librairie métier ne configure pas silencieusement le subscriber global.
|
- **GAME-TRACE-002** — `tracing-subscriber` compose les subscribers applicatifs et de test ; une librairie métier ne configure pas silencieusement le subscriber global.
|
||||||
- **GAME-TRACE-003** — `tracing-appender` est utilisé lorsque l’écriture non bloquante ou les fichiers de logs deviennent nécessaires ; le guard associé reste vivant pendant toute la durée utile.
|
- **GAME-TRACE-003** — `tracing-appender` est utilisé lorsque l’écriture non bloquante ou les fichiers de logs deviennent nécessaires ; le guard associé reste vivant pendant toute la durée utile.
|
||||||
- **GAME-TRACE-004** — La configuration de logging commune réside dans une crate transverse et ne doit pas être dupliquée par jeu.
|
- **GAME-TRACE-004** — La configuration de logging commune réside dans une crate transverse et ne doit pas être dupliquée par jeu.
|
||||||
|
|
||||||
|
- **GAME-PLATFORM-009** — Une adaptation WebAssembly réutilisable est une crate dédiée distincte de la crate Tauri.
|
||||||
|
- **GAME-PLATFORM-010** — Dans une app Tauri, `lib.rs` reste une façade/reexport ; `tauri.rs` assemble Tauri et expose les commandes qui délèguent aux modules propriétaires.
|
||||||
|
- **GAME-PLATFORM-011** — Les traces frontend Tauri passent par `@fltsci/tauri-plugin-tracing` vers `tauri-plugin-tracing`/`tracing`; les `console.*` applicatifs directs sont interdits hors fallback interne.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# file: scripts/build_reflex_tauri_wasm.py
|
# file: scripts/build_reflex_tauri_wasm.py
|
||||||
# version: 1
|
# version: 2
|
||||||
|
|
||||||
"""Build the Reflex WASM adapter and generate browser bindings for the local Tauri frontend."""
|
"""Build the dedicated Reflex WASM adapter for the Vite/Tauri frontend."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -12,15 +12,15 @@ import sys
|
|||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
"""Build the WASM library and run wasm-bindgen into the committed Tauri frontend directory."""
|
"""Build the WASM crate and generate browser bindings inside the Tauri frontend."""
|
||||||
|
|
||||||
root = pathlib.Path(__file__).resolve().parent.parent
|
root = pathlib.Path(__file__).resolve().parent.parent
|
||||||
target_dir = root.parent / "builds" / "sasedev-games" / "target"
|
target_dir = root.parent / "builds" / "sasedev-games" / "target"
|
||||||
wasm_path = target_dir / "wasm32-unknown-unknown" / "debug" / "game_reflex_poc_tauri.wasm"
|
wasm_path = target_dir / "wasm32-unknown-unknown" / "debug" / "game_reflex_poc_wasm.wasm"
|
||||||
output_dir = root / "Tauri" / "game-reflex-poc" / "frontend"
|
output_dir = root / "crates" / "apps" / "game-reflex-poc-tauri" / "frontend" / "wasm"
|
||||||
|
|
||||||
build = subprocess.run(
|
build = subprocess.run(
|
||||||
["cargo", "build", "-p", "game-reflex-poc-tauri", "--lib", "--target", "wasm32-unknown-unknown"],
|
["cargo", "build", "-p", "game-reflex-poc-wasm", "--target", "wasm32-unknown-unknown"],
|
||||||
cwd=root,
|
cwd=root,
|
||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
@@ -30,6 +30,7 @@ def main() -> int:
|
|||||||
print(f"WASM artifact not found: {wasm_path}", file=sys.stderr)
|
print(f"WASM artifact not found: {wasm_path}", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
bindings = subprocess.run(
|
bindings = subprocess.run(
|
||||||
[
|
[
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -39,8 +40,7 @@ def main() -> int:
|
|||||||
"--out-dir",
|
"--out-dir",
|
||||||
str(output_dir),
|
str(output_dir),
|
||||||
"--out-name",
|
"--out-name",
|
||||||
"game_reflex_poc_tauri",
|
"game_reflex_poc_wasm",
|
||||||
"--no-typescript",
|
|
||||||
],
|
],
|
||||||
cwd=root,
|
cwd=root,
|
||||||
check=False,
|
check=False,
|
||||||
@@ -48,7 +48,7 @@ def main() -> int:
|
|||||||
if bindings.returncode != 0:
|
if bindings.returncode != 0:
|
||||||
return bindings.returncode
|
return bindings.returncode
|
||||||
|
|
||||||
print(f"Reflex Tauri WASM frontend generated in {output_dir}")
|
print(f"Reflex WASM bindings generated in {output_dir}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user