Spaces:
Running
Running
Fade out update
Browse files- battlewords/ui.py +25 -7
- battlewords/ui_helpers.py +18 -14
battlewords/ui.py
CHANGED
|
@@ -161,6 +161,9 @@ def _init_session() -> None:
|
|
| 161 |
if "enable_sound_effects" not in st.session_state:
|
| 162 |
st.session_state.enable_sound_effects = False
|
| 163 |
|
|
|
|
|
|
|
|
|
|
| 164 |
def _new_game() -> None:
|
| 165 |
spinner_placeholder = st.empty()
|
| 166 |
with CustomSpinner(spinner_placeholder, "Initializing New Game..."):
|
|
@@ -771,7 +774,14 @@ def _render_guess_form(state: GameState):
|
|
| 771 |
<style>
|
| 772 |
.st-key-guess_input .stTextInput label .st-emotion-cache-lyi571, .st-key-guess_input .stTextInput label .st-emotion-cache-on6lu2 {
|
| 773 |
font-size: 1.2em;
|
|
|
|
|
|
|
| 774 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 775 |
.st-key-guess_input .stTextInput input {
|
| 776 |
font-size: 1.1em;
|
| 777 |
}
|
|
@@ -863,7 +873,8 @@ def _render_guess_form(state: GameState):
|
|
| 863 |
color: #ff9999;
|
| 864 |
}
|
| 865 |
|
| 866 |
-
.stForm { padding-bottom: 30px;
|
|
|
|
| 867 |
|
| 868 |
.stColumn .st-emotion-cache-6023h2 {margin-top:0px;}
|
| 869 |
/*.stColumn .st-emotion-cache-1w9nhvb { display:none;} hide OK BUTTON col spacing under form */
|
|
@@ -1468,7 +1479,7 @@ def _on_game_option_change() -> None:
|
|
| 1468 |
_new_game()
|
| 1469 |
|
| 1470 |
def run_app():
|
| 1471 |
-
start_root_fade_in(0.
|
| 1472 |
# Render PWA service worker registration (meta tags in <head> via Docker)
|
| 1473 |
# Streamlit reruns the script frequently; inject this only once per session.
|
| 1474 |
if not st.session_state.get("pwa_injected", False):
|
|
@@ -1486,6 +1497,10 @@ def run_app():
|
|
| 1486 |
except Exception:
|
| 1487 |
params = {}
|
| 1488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1489 |
# Handle overlay dismissal
|
| 1490 |
if params.get("overlay") == "0":
|
| 1491 |
# Clear param and remember to hide overlay this session
|
|
@@ -1528,7 +1543,8 @@ def run_app():
|
|
| 1528 |
#inject_ocean_layers()
|
| 1529 |
inject_styles()
|
| 1530 |
render_settings_page(_on_game_option_change)
|
| 1531 |
-
|
|
|
|
| 1532 |
return
|
| 1533 |
|
| 1534 |
if page == "leaderboard":
|
|
@@ -1539,7 +1555,8 @@ def run_app():
|
|
| 1539 |
inject_styles()
|
| 1540 |
st.header("Leaderboard")
|
| 1541 |
st.caption("Leaderboard UI will be added as part of the upgrade checklist.")
|
| 1542 |
-
|
|
|
|
| 1543 |
return
|
| 1544 |
|
| 1545 |
# Handle game_id for loading shared games
|
|
@@ -1592,14 +1609,15 @@ def run_app():
|
|
| 1592 |
state = _to_state()
|
| 1593 |
if is_game_over(state) and not st.session_state.get("hide_gameover_overlay", False):
|
| 1594 |
_render_game_over(state)
|
| 1595 |
-
|
| 1596 |
-
|
|
|
|
| 1597 |
|
| 1598 |
def _render_game_tab():
|
| 1599 |
"""
|
| 1600 |
Render the main game tab layout.
|
| 1601 |
"""
|
| 1602 |
-
# start_root_fade_in(0.
|
| 1603 |
_init_session()
|
| 1604 |
st.markdown(ocean_background_css, unsafe_allow_html=True)
|
| 1605 |
#inject_ocean_layers() # <-- add the animated layers
|
|
|
|
| 161 |
if "enable_sound_effects" not in st.session_state:
|
| 162 |
st.session_state.enable_sound_effects = False
|
| 163 |
|
| 164 |
+
if "hide_footer" not in st.session_state:
|
| 165 |
+
st.session_state["hide_footer"] = False
|
| 166 |
+
|
| 167 |
def _new_game() -> None:
|
| 168 |
spinner_placeholder = st.empty()
|
| 169 |
with CustomSpinner(spinner_placeholder, "Initializing New Game..."):
|
|
|
|
| 774 |
<style>
|
| 775 |
.st-key-guess_input .stTextInput label .st-emotion-cache-lyi571, .st-key-guess_input .stTextInput label .st-emotion-cache-on6lu2 {
|
| 776 |
font-size: 1.2em;
|
| 777 |
+
transition: opacity 1.5s ease-in, visibility 1.5s ease-in, display 1.5s allow-discrete;
|
| 778 |
+
transition-behavior: allow-discrete;
|
| 779 |
}
|
| 780 |
+
.st-emotion-cache-on6lu2, .st-emotion-cache-6c7yup , .st-emotion-cache-1pcf7lv {
|
| 781 |
+
visibility: inherit !important;
|
| 782 |
+
transition: opacity 1.5s ease-in, visibility 1.5s ease-in, display 1.5s allow-discrete;
|
| 783 |
+
transition-behavior: allow-discrete;
|
| 784 |
+
}
|
| 785 |
.st-key-guess_input .stTextInput input {
|
| 786 |
font-size: 1.1em;
|
| 787 |
}
|
|
|
|
| 873 |
color: #ff9999;
|
| 874 |
}
|
| 875 |
|
| 876 |
+
.stForm { padding-bottom: 30px; transition: opacity 1.5s ease-in, visibility 1.5s ease; display 1.5s allow-discrete;
|
| 877 |
+
transition-behavior: allow-discrete;}
|
| 878 |
|
| 879 |
.stColumn .st-emotion-cache-6023h2 {margin-top:0px;}
|
| 880 |
/*.stColumn .st-emotion-cache-1w9nhvb { display:none;} hide OK BUTTON col spacing under form */
|
|
|
|
| 1479 |
_new_game()
|
| 1480 |
|
| 1481 |
def run_app():
|
| 1482 |
+
start_root_fade_in(0.2)
|
| 1483 |
# Render PWA service worker registration (meta tags in <head> via Docker)
|
| 1484 |
# Streamlit reruns the script frequently; inject this only once per session.
|
| 1485 |
if not st.session_state.get("pwa_injected", False):
|
|
|
|
| 1497 |
except Exception:
|
| 1498 |
params = {}
|
| 1499 |
|
| 1500 |
+
# handle no footer query string param (for embedding use cases)
|
| 1501 |
+
if "nofooter" in params:
|
| 1502 |
+
st.session_state["hide_footer"] = True
|
| 1503 |
+
|
| 1504 |
# Handle overlay dismissal
|
| 1505 |
if params.get("overlay") == "0":
|
| 1506 |
# Clear param and remember to hide overlay this session
|
|
|
|
| 1543 |
#inject_ocean_layers()
|
| 1544 |
inject_styles()
|
| 1545 |
render_settings_page(_on_game_option_change)
|
| 1546 |
+
if st.session_state["hide_footer"] is not True:
|
| 1547 |
+
render_footer(current_page="settings", params=params)
|
| 1548 |
return
|
| 1549 |
|
| 1550 |
if page == "leaderboard":
|
|
|
|
| 1555 |
inject_styles()
|
| 1556 |
st.header("Leaderboard")
|
| 1557 |
st.caption("Leaderboard UI will be added as part of the upgrade checklist.")
|
| 1558 |
+
if st.session_state["hide_footer"] is not True:
|
| 1559 |
+
render_footer(current_page="leaderboard", params=params)
|
| 1560 |
return
|
| 1561 |
|
| 1562 |
# Handle game_id for loading shared games
|
|
|
|
| 1609 |
state = _to_state()
|
| 1610 |
if is_game_over(state) and not st.session_state.get("hide_gameover_overlay", False):
|
| 1611 |
_render_game_over(state)
|
| 1612 |
+
if st.session_state["hide_footer"] is not True:
|
| 1613 |
+
render_footer(current_page="play", params=params)
|
| 1614 |
+
finish_root_fade_in(2.0)
|
| 1615 |
|
| 1616 |
def _render_game_tab():
|
| 1617 |
"""
|
| 1618 |
Render the main game tab layout.
|
| 1619 |
"""
|
| 1620 |
+
# start_root_fade_in(0.0)
|
| 1621 |
_init_session()
|
| 1622 |
st.markdown(ocean_background_css, unsafe_allow_html=True)
|
| 1623 |
#inject_ocean_layers() # <-- add the animated layers
|
battlewords/ui_helpers.py
CHANGED
|
@@ -329,7 +329,7 @@ def start_root_fade_in(duration_s: float = 0.35) -> None:
|
|
| 329 |
root.style.setProperty('visibility', 'hidden', 'important');
|
| 330 |
// Force style/layout flush so opacity=0 commits before re-enabling transition.
|
| 331 |
void root.offsetHeight;
|
| 332 |
-
root.style.setProperty('transition', 'opacity {duration_s:.3f}s ease', 'important');
|
| 333 |
}} catch (e) {{ /* no-op */ }}
|
| 334 |
}})();
|
| 335 |
</script>
|
|
@@ -350,7 +350,7 @@ def finish_root_fade_in(duration_s: float = 0.35) -> None:
|
|
| 350 |
if (!root) return;
|
| 351 |
root.style.opacity = '0';
|
| 352 |
root.style.visibility = 'visible';
|
| 353 |
-
root.style.transition = 'opacity {duration_s:.3f}s ease';
|
| 354 |
root.style.opacity = '1';
|
| 355 |
root.classList.remove('bw-fadein-prep');
|
| 356 |
}} catch (e) {{ /* no-op */ }}
|
|
@@ -435,7 +435,8 @@ def inject_styles() -> None:
|
|
| 435 |
font-size: 1.8rem;
|
| 436 |
min-height: 2.5rem;
|
| 437 |
min-width: 0.5em;
|
| 438 |
-
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
|
|
|
| 439 |
background: #1d64c8; /* Base cell color */
|
| 440 |
color: #FFFFFF; /* Base text color for contrast */
|
| 441 |
}
|
|
@@ -471,11 +472,12 @@ def inject_styles() -> None:
|
|
| 471 |
.bw-fadein-prep {visibility: hidden !important;}
|
| 472 |
.bw-fadein-prep #bw-spinner-overlay {
|
| 473 |
visibility: visible !important;
|
| 474 |
-
opacity: 1 !important;
|
| 475 |
}
|
| 476 |
|
| 477 |
div[data-testid="column"], .st-emotion-cache-zh2fnc, .st-emotion-cache-1tj828o, .st-emotion-cache-1anq8dj { width: auto !important; flex: 1 1 auto !important; min-width: 100% !important; max-width: 100% !important; border-radius:0; background-color: #1e69d2; border: 1px solid transparent;}
|
| 478 |
-
.st-emotion-cache-1permvm, .st-emotion-cache-1n6tfoc { gap:0.15rem !important; min-height: 2.5rem; min-width: 2.5rem;}
|
|
|
|
| 479 |
|
| 480 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] { flex-wrap: nowrap !important; overflow-x: auto !important; margin: 2px 0 !important; }
|
| 481 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] > div[data-testid="column"] { flex: 0 0 auto !important; }
|
|
@@ -517,7 +519,7 @@ def inject_styles() -> None:
|
|
| 517 |
.shiny-border:hover::before { left: 100%; }
|
| 518 |
|
| 519 |
.st-emotion-cache-1n1mx2j p {font-size: 1rem;}
|
| 520 |
-
.bw-radio-group { display:flex; align-items:flex-start; gap: 5px; flex-flow: row; min-height: 92px;}
|
| 521 |
.bw-radio-item { display:flex; flex-direction:column; align-items:center; gap:6px; text-align:center;}
|
| 522 |
.bw-radio-circle { width: 45px; height: 45px; border-radius: 50%; border: 4px solid; background: rgba(255,255,255,0.06); display: grid; place-items: center; color:#fff; font-weight:700; }
|
| 523 |
.bw-radio-circle .dot { width: 14px; height: 14px; border-radius: 50%; background:#777; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.25); }
|
|
@@ -581,22 +583,21 @@ def inject_styles() -> None:
|
|
| 581 |
)
|
| 582 |
|
| 583 |
# --- Footer Navigation ---
|
| 584 |
-
def render_footer(current_page: str = "play") -> None:
|
| 585 |
"""Render footer navigation.
|
| 586 |
|
| 587 |
Args:
|
| 588 |
current_page: Active page key ("play", "leaderboard", "settings").
|
| 589 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 590 |
play_active = "active" if current_page == "play" else ""
|
| 591 |
leaderboard_active = "active" if current_page == "leaderboard" else ""
|
| 592 |
settings_active = "active" if current_page == "settings" else ""
|
| 593 |
|
| 594 |
-
params = {}
|
| 595 |
-
try:
|
| 596 |
-
params = st.query_params
|
| 597 |
-
except Exception:
|
| 598 |
-
params = {}
|
| 599 |
-
|
| 600 |
game_id: Optional[str] = None
|
| 601 |
if isinstance(params, dict):
|
| 602 |
game_id = params.get("game_id")
|
|
@@ -689,13 +690,16 @@ def show_spinner(message: str = "Loading..."):
|
|
| 689 |
.bw-spinner-overlay {{
|
| 690 |
position: fixed; z-index: 99999; top: 0; left: 0; width: 100vw; height: 100vh;
|
| 691 |
opacity: 1;
|
| 692 |
-
transition: opacity 1.5s ease;
|
|
|
|
| 693 |
background: rgb(29, 100, 200); display: flex; align-items: center; justify-content: center;
|
| 694 |
visibility: visible;
|
| 695 |
}}
|
| 696 |
.bw-spinner-overlay.bw-spinner-fadeout {{
|
| 697 |
opacity: 0;
|
| 698 |
pointer-events: none;
|
|
|
|
|
|
|
| 699 |
}}
|
| 700 |
.bw-spinner-img {{
|
| 701 |
width: 150px; height: 150px; border-radius: 16px; box-shadow: 0 0 8px #1d64c8;
|
|
|
|
| 329 |
root.style.setProperty('visibility', 'hidden', 'important');
|
| 330 |
// Force style/layout flush so opacity=0 commits before re-enabling transition.
|
| 331 |
void root.offsetHeight;
|
| 332 |
+
// root.style.setProperty('transition', 'opacity {duration_s:.3f}s ease, visibility {duration_s:.3f}s ease', 'important');
|
| 333 |
}} catch (e) {{ /* no-op */ }}
|
| 334 |
}})();
|
| 335 |
</script>
|
|
|
|
| 350 |
if (!root) return;
|
| 351 |
root.style.opacity = '0';
|
| 352 |
root.style.visibility = 'visible';
|
| 353 |
+
root.style.transition = 'opacity {duration_s:.3f}s ease-in, visibility {duration_s:.3f}s ease-in !important';
|
| 354 |
root.style.opacity = '1';
|
| 355 |
root.classList.remove('bw-fadein-prep');
|
| 356 |
}} catch (e) {{ /* no-op */ }}
|
|
|
|
| 435 |
font-size: 1.8rem;
|
| 436 |
min-height: 2.5rem;
|
| 437 |
min-width: 0.5em;
|
| 438 |
+
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease display 0.15s allow-discrete;
|
| 439 |
+
transition-behavior: allow-discrete;
|
| 440 |
background: #1d64c8; /* Base cell color */
|
| 441 |
color: #FFFFFF; /* Base text color for contrast */
|
| 442 |
}
|
|
|
|
| 472 |
.bw-fadein-prep {visibility: hidden !important;}
|
| 473 |
.bw-fadein-prep #bw-spinner-overlay {
|
| 474 |
visibility: visible !important;
|
| 475 |
+
opacity: 1 !important;
|
| 476 |
}
|
| 477 |
|
| 478 |
div[data-testid="column"], .st-emotion-cache-zh2fnc, .st-emotion-cache-1tj828o, .st-emotion-cache-1anq8dj { width: auto !important; flex: 1 1 auto !important; min-width: 100% !important; max-width: 100% !important; border-radius:0; background-color: #1e69d2; border: 1px solid transparent;}
|
| 479 |
+
.st-emotion-cache-1permvm, .st-emotion-cache-1n6tfoc { gap:0.15rem !important; min-height: 2.5rem; min-width: 2.5rem;}
|
| 480 |
+
.stColumn.st-emotion-cache-1ruy632, .stColumn.st-emotion-cache-t2z0eb {margin-top:0;}
|
| 481 |
|
| 482 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] { flex-wrap: nowrap !important; overflow-x: auto !important; margin: 2px 0 !important; }
|
| 483 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] > div[data-testid="column"] { flex: 0 0 auto !important; }
|
|
|
|
| 519 |
.shiny-border:hover::before { left: 100%; }
|
| 520 |
|
| 521 |
.st-emotion-cache-1n1mx2j p {font-size: 1rem;}
|
| 522 |
+
.bw-radio-group { display:flex; align-items:flex-start; gap: 5px; flex-flow: row; min-height: 92px; transition: opacity 1.5s ease-in, visibility 1.5s ease-in, display 1.5s allow-discrete; transition-behavior: allow-discrete; visibility: inherit;}
|
| 523 |
.bw-radio-item { display:flex; flex-direction:column; align-items:center; gap:6px; text-align:center;}
|
| 524 |
.bw-radio-circle { width: 45px; height: 45px; border-radius: 50%; border: 4px solid; background: rgba(255,255,255,0.06); display: grid; place-items: center; color:#fff; font-weight:700; }
|
| 525 |
.bw-radio-circle .dot { width: 14px; height: 14px; border-radius: 50%; background:#777; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.25); }
|
|
|
|
| 583 |
)
|
| 584 |
|
| 585 |
# --- Footer Navigation ---
|
| 586 |
+
def render_footer(current_page: str = "play", params: dict = {}) -> None:
|
| 587 |
"""Render footer navigation.
|
| 588 |
|
| 589 |
Args:
|
| 590 |
current_page: Active page key ("play", "leaderboard", "settings").
|
| 591 |
"""
|
| 592 |
+
|
| 593 |
+
# If nofooter=1 is present, do not render the footer
|
| 594 |
+
if isinstance(params, dict) and params.get("nofooter") == "1":
|
| 595 |
+
return
|
| 596 |
+
|
| 597 |
play_active = "active" if current_page == "play" else ""
|
| 598 |
leaderboard_active = "active" if current_page == "leaderboard" else ""
|
| 599 |
settings_active = "active" if current_page == "settings" else ""
|
| 600 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 601 |
game_id: Optional[str] = None
|
| 602 |
if isinstance(params, dict):
|
| 603 |
game_id = params.get("game_id")
|
|
|
|
| 690 |
.bw-spinner-overlay {{
|
| 691 |
position: fixed; z-index: 99999; top: 0; left: 0; width: 100vw; height: 100vh;
|
| 692 |
opacity: 1;
|
| 693 |
+
transition: opacity 1.5s ease-out, visibility 1.5s ease-out, display 1.5s allow-discrete;
|
| 694 |
+
transition-behavior: allow-discrete;
|
| 695 |
background: rgb(29, 100, 200); display: flex; align-items: center; justify-content: center;
|
| 696 |
visibility: visible;
|
| 697 |
}}
|
| 698 |
.bw-spinner-overlay.bw-spinner-fadeout {{
|
| 699 |
opacity: 0;
|
| 700 |
pointer-events: none;
|
| 701 |
+
transition: opacity 1.5s ease, visibility 1.5s ease, display 1.5s allow-discrete;
|
| 702 |
+
transition-behavior: allow-discrete;
|
| 703 |
}}
|
| 704 |
.bw-spinner-img {{
|
| 705 |
width: 150px; height: 150px; border-radius: 16px; box-shadow: 0 0 8px #1d64c8;
|