fix(web): clean up wasm32 build warnings and wire /play route to Bevy canvas
Build and Deploy / build-and-push (push) Failing after 44s

- solitaire_data/sync_client.rs: fix SyncPayload/SyncResponse import split
  (SyncResponse is needed by LocalOnlyProvider which compiles on wasm32)
- solitaire_engine/assets/sources.rs: cfg-gate AssetApp/AssetSourceBuilder
  imports (only used in the non-wasm FileAssetReader block)
- solitaire_engine/auto_complete_plugin.rs: cfg-gate AUTO_COMPLETE_CHIME_VOLUME
- solitaire_engine/daily_challenge_plugin.rs: cfg-gate Task/AsyncComputeTaskPool
  imports and DailyChallengeTask struct (server fetch systems are non-wasm only)
- solitaire_engine/resources.rs: cfg-gate std::sync::Arc (TokioRuntimeResource
  is non-wasm only)
- solitaire_engine/settings_plugin.rs: cfg-gate ScanThemes variant, pill_button,
  and their match arms; fix refresh_registry import placement
- solitaire_server/src/lib.rs: point /play route at play.html (Bevy canvas);
  keep /play-classic serving game.html during transition period
- build_wasm.sh: add --no-typescript to wasm-bindgen call for canvas build
- solitaire_server/web/pkg: add canvas.js + canvas_bg.wasm build artifacts

wasm32 build and native clippy --workspace -D warnings both clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-01 13:55:39 -07:00
parent 835a48fe9d
commit f464aab543
11 changed files with 2264 additions and 4 deletions
+1
View File
@@ -58,6 +58,7 @@ wasm-bindgen \
--out-dir "$OUT_DIR" \
--out-name canvas \
--target web \
--no-typescript \
"$REPO_ROOT/target/wasm32-unknown-unknown/release/solitaire_web.wasm"
# Optional size optimisation — Bevy bundles are large (~5-15 MB uncompressed).
+3 -1
View File
@@ -12,7 +12,9 @@
//! without matching on [`SyncBackend`] anywhere else in the codebase.
use async_trait::async_trait;
use solitaire_sync::{ChallengeGoal, LeaderboardEntry, SyncPayload, SyncResponse};
use solitaire_sync::{SyncPayload, SyncResponse};
#[cfg(not(target_arch = "wasm32"))]
use solitaire_sync::{ChallengeGoal, LeaderboardEntry};
use crate::{SyncError, SyncProvider};
+2
View File
@@ -47,7 +47,9 @@
//! comments on each call out the pairing so a future reader doesn't
//! accidentally drop one half.
#[cfg(not(target_arch = "wasm32"))]
use bevy::asset::AssetApp;
#[cfg(not(target_arch = "wasm32"))]
use bevy::asset::io::AssetSourceBuilder;
use bevy::asset::io::embedded::EmbeddedAssetRegistry;
#[cfg(not(target_arch = "wasm32"))]
@@ -22,6 +22,7 @@ use crate::resources::GameStateResource;
///
/// Plays the win fanfare at half volume so it is clearly distinguishable from
/// both normal card-place sounds and the full win fanfare that fires later.
#[cfg(not(target_arch = "wasm32"))]
const AUTO_COMPLETE_CHIME_VOLUME: f64 = 0.5;
/// Seconds between consecutive auto-complete moves.
@@ -13,9 +13,11 @@
use bevy::input::ButtonInput;
use bevy::prelude::*;
use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
use chrono::{DateTime, Duration, Local, NaiveDate, Utc};
use solitaire_data::{daily_seed_for, save_progress_to};
#[cfg(not(target_arch = "wasm32"))]
use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
#[cfg(not(target_arch = "wasm32"))]
use solitaire_sync::ChallengeGoal;
use crate::events::{
@@ -78,8 +80,13 @@ pub struct DailyChallengeCompletedEvent {
/// Holds the in-flight server challenge fetch so the result can be polled
/// each frame without blocking the main thread.
#[derive(Resource, Default)]
#[cfg(not(target_arch = "wasm32"))]
struct DailyChallengeTask(Option<Task<Option<ChallengeGoal>>>);
#[derive(Resource, Default)]
#[cfg(target_arch = "wasm32")]
struct DailyChallengeTask;
/// Tracks which `DailyChallengeResource::date` the expiry-warning toast has
/// already fired for, so the toast spawns at most once per day.
///
+1
View File
@@ -1,5 +1,6 @@
//! Bevy resources owned by the engine crate.
#[cfg(not(target_arch = "wasm32"))]
use std::sync::Arc;
use bevy::math::Vec2;
+6 -2
View File
@@ -33,9 +33,9 @@ use crate::events::{
use crate::font_plugin::FontResource;
use crate::progress_plugin::ProgressResource;
use crate::resources::{SettingsScrollPos, SyncStatus, SyncStatusResource};
use crate::theme::{ThemeThumbnailCache, ThemeThumbnailPair, refresh_registry};
use crate::theme::{ThemeThumbnailCache, ThemeThumbnailPair};
#[cfg(not(target_arch = "wasm32"))]
use crate::theme::{ImportError, import_theme};
use crate::theme::{ImportError, import_theme, refresh_registry};
use crate::ui_focus::{FocusGroup, FocusRow, Focusable, FocusedButton};
use crate::ui_modal::{
ButtonVariant, ModalButton, ModalScrim, spawn_modal, spawn_modal_actions, spawn_modal_button,
@@ -256,6 +256,7 @@ enum SettingsButton {
/// local-only mode.
ToggleAnalytics,
/// Scan `user_theme_dir()` for new `.zip` files and import each one.
#[cfg(not(target_arch = "wasm32"))]
ScanThemes,
SyncNow,
/// Open the sync-server Connect modal (shown when backend = Local).
@@ -318,6 +319,7 @@ impl SettingsButton {
SettingsButton::SelectCardBack(_) => 70,
SettingsButton::SelectBackground(_) => 80,
SettingsButton::SelectTheme(_) => 85,
#[cfg(not(target_arch = "wasm32"))]
SettingsButton::ScanThemes => 86,
// Sync section
SettingsButton::SyncNow => 90,
@@ -1256,6 +1258,7 @@ fn handle_settings_buttons(
changed.write(SettingsChangedEvent(settings.0.clone()));
}
}
#[cfg(not(target_arch = "wasm32"))]
SettingsButton::ScanThemes => {
// Handled by `handle_scan_themes`.
}
@@ -2723,6 +2726,7 @@ fn handle_scan_themes(
}
}
#[cfg(not(target_arch = "wasm32"))]
/// A small pill-shaped settings button, matching the style used in `sync_row`.
fn pill_button(
parent: &mut ChildSpawnerCommands,
+5
View File
@@ -214,6 +214,11 @@ fn build_router_inner(state: AppState, rate_limit: bool) -> Router {
)
.route(
"/play",
get(|| async { Html(include_str!("../web/play.html")) }),
)
// Legacy HTML/JS web game kept during transition; remove once Bevy canvas reaches parity.
.route(
"/play-classic",
get(|| async { Html(include_str!("../web/game.html")) }),
)
.route(
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.