diff --git a/Cargo.lock b/Cargo.lock
index e557059..8ce19af 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6347,12 +6347,6 @@ dependencies = [
"bytemuck",
]
-[[package]]
-name = "qrcodegen"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
-
[[package]]
name = "quick-error"
version = "2.0.1"
@@ -8720,7 +8714,6 @@ dependencies = [
"leptos",
"leptos_i18n",
"leptos_router",
- "qrcodegen",
"rand 0.9.3",
"serde",
"serde_json",
diff --git a/clients/web/Cargo.toml b/clients/web/Cargo.toml
index 71af23b..04857a6 100644
--- a/clients/web/Cargo.toml
+++ b/clients/web/Cargo.toml
@@ -18,7 +18,6 @@ serde_json = "1"
futures = "0.3"
rand = "0.9"
gloo-storage = "0.3"
-qrcodegen = "1.8"
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
@@ -39,7 +38,4 @@ web-sys = { version = "0.3", features = [
"OscillatorType",
"BaseAudioContext",
"HtmlAudioElement",
- "Clipboard",
- "Navigator",
- "Location",
] }
diff --git a/clients/web/assets/style.css b/clients/web/assets/style.css
index 3de4a9f..b2d89a4 100644
--- a/clients/web/assets/style.css
+++ b/clients/web/assets/style.css
@@ -287,74 +287,10 @@ a:hover { text-decoration: underline; }
.portal-error { color: var(--ui-red-accent); font-size: 0.875rem; margin-top: 0.5rem; }
.portal-success { color: var(--ui-green-accent); font-size: 0.875rem; margin-top: 0.5rem; }
-/* ── Share URL row (lobby waiting card + game top bar) ──────────── */
-.share-url-row {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- background: rgba(0,0,0,0.18);
- border: 1px solid rgba(200,164,72,0.25);
- border-radius: 5px;
- padding: 0.4rem 0.6rem;
-}
-.share-url-text {
- flex: 1;
- font-family: var(--font-ui);
- font-size: 0.72rem;
- color: rgba(242,232,208,0.75);
- word-break: break-all;
- user-select: all;
-}
-.share-copy-btn {
- flex-shrink: 0;
- font-family: var(--font-ui);
- font-size: 0.72rem;
- padding: 0.2rem 0.6rem;
- border: 1px solid rgba(200,164,72,0.4);
- border-radius: 3px;
- background: rgba(200,164,72,0.1);
- color: var(--ui-parchment);
- cursor: pointer;
- transition: background 0.15s;
- white-space: nowrap;
-}
-.share-copy-btn:hover { background: rgba(200,164,72,0.22); }
-
-/* ── QR code container ───────────────────────────────────────────── */
-.qr-container {
- width: 160px;
- height: 160px;
- margin: 0 auto;
- border-radius: 4px;
- overflow: hidden;
-}
-.qr-container svg { width: 100%; height: 100%; display: block; }
-
-/* ── Share popover (in-game top bar) ─────────────────────────────── */
-.share-popover {
- width: 100%;
- background: rgba(0,0,0,0.3);
- border: 1px solid rgba(200,164,72,0.2);
- border-radius: 6px;
- padding: 0.75rem 1rem;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 0.4rem;
- margin-bottom: 0.5rem;
-}
-.share-popover .qr-container { width: 120px; height: 120px; }
-.share-popover-label {
- font-size: 0.75rem;
- color: rgba(242,232,208,0.6);
- text-align: center;
- margin: 0;
-}
-
/* ── Game overlay (full-screen, covers portal during play) ───────── */
.game-overlay {
position: fixed;
- inset: 54px 0 0 0;
+ inset: 0;
background: #8a7050;
background-image:
radial-gradient(ellipse at 20% 10%, rgba(80,48,16,0.35) 0%, transparent 60%),
diff --git a/clients/web/locales/en.json b/clients/web/locales/en.json
index 8ff3548..5d5005d 100644
--- a/clients/web/locales/en.json
+++ b/clients/web/locales/en.json
@@ -58,7 +58,6 @@
"hint_move": "Click a highlighted field to move a checker",
"hint_hold_or_go": "Hold to keep points — Go to reset the setting",
"hint_continue": "Click Continue when ready",
- "anonymous_name": "Anonymous",
"login_failed": "Invalid username or password.",
"sign_in": "Sign in",
"sign_out": "Sign out",
@@ -94,12 +93,5 @@
"anonymous_player": "anonymous",
"started_label": "Started",
"ended_label": "Ended",
- "room_detail_title": "Room",
- "share_link": "Share this link to invite an opponent",
- "copy_link": "Copy link",
- "link_copied": "Copied!",
- "scan_qr": "or scan the QR code",
- "join_code_label": "Join by code",
- "join_code_placeholder": "Room code",
- "share_btn": "Share"
+ "room_detail_title": "Room"
}
diff --git a/clients/web/locales/fr.json b/clients/web/locales/fr.json
index 2346395..8cef7df 100644
--- a/clients/web/locales/fr.json
+++ b/clients/web/locales/fr.json
@@ -58,7 +58,6 @@
"hint_move": "Cliquez un champ surligné pour déplacer",
"hint_hold_or_go": "Tenir pour garder les points — S'en aller pour repartir",
"hint_continue": "Cliquez Continuer quand vous êtes prêt",
- "anonymous_name": "Anonyme",
"login_failed": "Identifiant ou mot de passe incorrect.",
"sign_in": "Se connecter",
"sign_out": "Se déconnecter",
@@ -94,12 +93,5 @@
"anonymous_player": "anonyme",
"started_label": "Début",
"ended_label": "Fin",
- "room_detail_title": "Salle",
- "share_link": "Partagez ce lien pour inviter un adversaire",
- "copy_link": "Copier le lien",
- "link_copied": "Copié !",
- "scan_qr": "ou scannez le QR code",
- "join_code_label": "Rejoindre par code",
- "join_code_placeholder": "Code de salle",
- "share_btn": "Partager"
+ "room_detail_title": "Salle"
}
diff --git a/clients/web/src/app.rs b/clients/web/src/app.rs
index b9ce4aa..8d604b6 100644
--- a/clients/web/src/app.rs
+++ b/clients/web/src/app.rs
@@ -4,7 +4,6 @@ use gloo_storage::{LocalStorage, Storage};
use leptos::prelude::*;
use leptos::task::spawn_local;
use leptos_router::components::{Route, Router, Routes};
-use leptos_router::hooks::use_location;
use leptos_router::path;
use serde::{Deserialize, Serialize};
@@ -12,15 +11,15 @@ use backbone_lib::session::{ConnectError, GameSession, RoomConfig, RoomRole, Ses
use backbone_lib::traits::ViewStateUpdate;
use crate::api;
-use crate::i18n::*;
use crate::game::components::{ConnectingScreen, GameScreen};
use crate::game::session::{
- compute_last_moves, patch_player_name, push_or_show, run_local_bot_game,
+ compute_last_moves, push_or_show, run_local_bot_game,
};
use crate::game::trictrac::backend::TrictracBackend;
use crate::game::trictrac::types::{
GameDelta, PlayerAction, ScoredEvent, SerStage, ViewState,
};
+use crate::i18n::I18nContextProvider;
use crate::nav::SiteNav;
use crate::portal::{account::AccountPage, game_detail::GameDetailPage, lobby::LobbyPage, profile::ProfilePage};
use trictrac_store::CheckerMove;
@@ -129,7 +128,6 @@ async fn submit_game_result(room_code: String, game_state: ViewState) {
#[component]
pub fn App() -> impl IntoView {
- let i18n = use_i18n();
let stored = load_session();
let initial_screen = if stored.is_some() {
Screen::Connecting
@@ -227,10 +225,8 @@ pub fn App() -> impl IntoView {
};
if remote_config.is_none() {
- let player_name = auth_username.get_untracked()
- .unwrap_or_else(|| t_string!(i18n, anonymous_name).to_string());
loop {
- let restart = run_local_bot_game(screen, &mut cmd_rx, pending, player_name.clone()).await;
+ let restart = run_local_bot_game(screen, &mut cmd_rx, pending).await;
if !restart {
break;
}
@@ -270,9 +266,7 @@ pub fn App() -> impl IntoView {
let is_host = session.is_host;
let player_id = session.player_id;
let reconnect_token = session.reconnect_token;
- let my_name = auth_username.get_untracked()
- .unwrap_or_else(|| t_string!(i18n, anonymous_name).to_string());
- let mut vs = ViewState::default_with_names("", "");
+ let mut vs = ViewState::default_with_names("Blancs", "Noirs");
let mut result_submitted = false;
loop {
@@ -296,7 +290,6 @@ pub fn App() -> impl IntoView {
ViewStateUpdate::Full(state) => vs = state,
ViewStateUpdate::Incremental(delta) => vs.apply_delta(&delta),
}
- patch_player_name(&mut vs, player_id, &my_name);
if is_host && !result_submitted && vs.stage == SerStage::Ended {
result_submitted = true;
@@ -350,52 +343,42 @@ pub fn App() -> impl IntoView {
});
view! {
-
-
+
+
+ // Nav: hidden while game overlay is active
+
-
- "Page not found."
}>
-
-
-
-
-
-
+ // Portal pages — always mounted for router stability
+
+ "Page not found." }>
+
+
+
+
+
+
-
-
- }
-}
-
-/// Renders the full-screen game overlay, but only when the current route is "/".
-/// This lets the user navigate to profile/account pages while a game is running.
-#[component]
-fn GameOverlay(
- pending: RwSignal>,
- screen: RwSignal,
-) -> impl IntoView {
- let location = use_location();
-
- move || {
- if location.pathname.get() != "/" {
- return view! { }.into_any();
- }
- let q = pending.get();
- let front = q.front().cloned();
- if let Some(state) = front {
- return view! {
-
- }.into_any(),
- _ => view! { }.into_any(),
- }
+ // Game overlay: fixed, covers portal during play
+ {move || {
+ let q = pending.get();
+ let front = q.front().cloned();
+ if let Some(state) = front {
+ return view! {
+