Spaces:
Running
Running
Enhance UI with modal overlays and improve UX
Browse filesUpdated `README.md` to reflect version `0.2.0` and document new features, including a loading screen and a congratulations screen for the final score and tier.
Replaced `st.expander`-based layouts in `_render_score_panel` and `_render_game_over` with HTML-based tables and modals for better styling, readability, and layout control. Added a modal overlay for the game-over screen with a detailed score summary, close button, and "New Game" button.
Introduced query parameter handling in `run_app` to allow dismissal of the game-over overlay, persisting the state in `st.session_state`.
Improved session state handling, fixed minor HTML inconsistencies, and enhanced the overall appearance and user experience.
- README.md +20 -33
- battlewords/__init__.py +1 -1
- battlewords/ui.py +57 -38
README.md
CHANGED
|
@@ -104,52 +104,39 @@ docker run -p 8501:8501 battlewords
|
|
| 104 |
4. **The game ends when all six words are found or all word letters are revealed. Your score tier is displayed.**
|
| 105 |
|
| 106 |
## Changelog
|
| 107 |
-
- 0.
|
| 108 |
-
-
|
| 109 |
-
-
|
| 110 |
-
- Reorganized `run_app` for better layout structure and removed redundant state assignment.
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
- 0.1.11
|
| 113 |
-
-
|
| 114 |
-
-
|
| 115 |
-
|
| 116 |
-
- Update to Game Summary table: shows words, base points, bonus points, total score; styled with Streamlit's `st.table`.
|
| 117 |
-
- Show Grid Ticks and Space between words are now in settings.
|
| 118 |
-
- Increase default font size for better readability.
|
| 119 |
-
- Game end now modifies the grid display to show all words and disables further interaction.
|
| 120 |
-
- Update to model for future enhancements
|
| 121 |
-
|
| 122 |
- 0.1.10
|
| 123 |
-
-
|
| 124 |
-
- Replaced Hit/Miss with Correct/Try Again status indicator for guesses.
|
| 125 |
-
- UI polish: animated ocean background; metal-scope styled radar retained with session-cached GIF.
|
| 126 |
-
- Footer shows version info (commit, Python, Streamlit).
|
| 127 |
-
- Word list handling: defaults to `classic.txt` when present; selection persists across new games; sort action rewrites the file and restarts after 5s notice.
|
| 128 |
-
- Documentation updated.
|
| 129 |
-
- fixed wordlist selection persistence bug
|
| 130 |
|
| 131 |
- 0.1.9
|
| 132 |
-
-
|
| 133 |
-
- Version info surfaced in Settings panel.
|
| 134 |
-
- Mobile layout improvements.
|
| 135 |
|
| 136 |
- 0.1.8
|
| 137 |
-
-
|
| 138 |
|
| 139 |
- 0.1.5
|
| 140 |
-
-
|
| 141 |
-
- Completed words render as non-interactive styled cells.
|
| 142 |
-
- Tooltips show cell coordinates for buttons and revealed cells.
|
| 143 |
-
- Stable letter map rebuild after reveals.
|
| 144 |
|
| 145 |
- 0.1.4
|
| 146 |
-
- Radar
|
| 147 |
-
- Session-level caching of the generated radar GIF to avoid regeneration during a session.
|
| 148 |
-
- Mobile layout improvements: radar above grid on small screens; tighter grid gaps and horizontal scrolling per row.
|
| 149 |
|
| 150 |
- 0.1.3
|
| 151 |
-
-
|
| 152 |
-
-
|
| 153 |
|
| 154 |
## Known Issues / TODO
|
| 155 |
|
|
|
|
| 104 |
4. **The game ends when all six words are found or all word letters are revealed. Your score tier is displayed.**
|
| 105 |
|
| 106 |
## Changelog
|
| 107 |
+
- 0.2.0
|
| 108 |
+
- Added a loading screen when starting a new game.
|
| 109 |
+
- Added a congratulations screen with your final score and tier when the game ends.
|
|
|
|
| 110 |
|
| 111 |
+
- 0.1.13
|
| 112 |
+
- Improved score summary layout for clarity and style.
|
| 113 |
+
|
| 114 |
+
- 0.1.12
|
| 115 |
+
- Improved score summary layout and styling.
|
| 116 |
+
- Enhanced overall appearance and readability.
|
| 117 |
+
|
| 118 |
- 0.1.11
|
| 119 |
+
- Game now ends when all words are found or revealed.
|
| 120 |
+
- Added word spacing logic and improved settings.
|
| 121 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
- 0.1.10
|
| 123 |
+
- Added game mode selector and improved UI feedback.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
- 0.1.9
|
| 126 |
+
- Improved background and mobile layout.
|
|
|
|
|
|
|
| 127 |
|
| 128 |
- 0.1.8
|
| 129 |
+
- Updated to Python 3.12.
|
| 130 |
|
| 131 |
- 0.1.5
|
| 132 |
+
- Added hit/miss indicator and improved grid feedback.
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
- 0.1.4
|
| 135 |
+
- Radar visualization improved and mobile layout enhanced.
|
|
|
|
|
|
|
| 136 |
|
| 137 |
- 0.1.3
|
| 138 |
+
- Added wordlist picker and sort feature.
|
| 139 |
+
- Improved score panel and final score display.
|
| 140 |
|
| 141 |
## Known Issues / TODO
|
| 142 |
|
battlewords/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
| 1 |
-
__version__ = "0.
|
| 2 |
__all__ = ["models", "generator", "logic", "ui"]
|
|
|
|
| 1 |
+
__version__ = "0.2.0"
|
| 2 |
__all__ = ["models", "generator", "logic", "ui"]
|
battlewords/ui.py
CHANGED
|
@@ -834,12 +834,8 @@ def _render_guess_form(state: GameState):
|
|
| 834 |
|
| 835 |
|
| 836 |
def _render_score_panel(state: GameState):
|
| 837 |
-
#
|
| 838 |
-
|
| 839 |
-
# st.metric("Score", state.score)
|
| 840 |
-
# with col2:
|
| 841 |
-
# st.markdown(f"Last action: {state.last_action}")
|
| 842 |
-
if is_game_over(state):
|
| 843 |
_render_game_over(state)
|
| 844 |
else:
|
| 845 |
# Build a simple table with independent column backgrounds and visible gaps
|
|
@@ -863,9 +859,9 @@ def _render_score_panel(state: GameState):
|
|
| 863 |
extra_pts = max(0, pts - letters_display)
|
| 864 |
row_html = (
|
| 865 |
"<tr>"
|
| 866 |
-
f"<td class=\"blue-background \">{word_display}</td>"
|
| 867 |
-
f"<td class=\"blue-background \">{letters_display}</td>"
|
| 868 |
-
f"<td class=\"blue-background \">{extra_pts}</td>"
|
| 869 |
"</tr>"
|
| 870 |
)
|
| 871 |
rows_html.append(row_html)
|
|
@@ -881,33 +877,44 @@ def _render_score_panel(state: GameState):
|
|
| 881 |
|
| 882 |
|
| 883 |
def _render_game_over(state: GameState):
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 890 |
)
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
st.markdown(versions_html(), unsafe_allow_html=True)
|
| 910 |
-
|
| 911 |
st.stop()
|
| 912 |
|
| 913 |
|
|
@@ -932,6 +939,19 @@ def _sort_wordlist(filename):
|
|
| 932 |
|
| 933 |
|
| 934 |
def run_app():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 935 |
_init_session()
|
| 936 |
st.markdown(ocean_background_css, unsafe_allow_html=True)
|
| 937 |
_render_header()
|
|
@@ -956,8 +976,7 @@ def run_app():
|
|
| 956 |
_render_grid(state, st.session_state.letter_map, show_grid_ticks=st.session_state.get("show_grid_ticks", True))
|
| 957 |
st.button("New Game", width=125, on_click=_new_game, key="new_game_btn")
|
| 958 |
|
| 959 |
-
|
| 960 |
-
# End condition
|
| 961 |
state = _to_state()
|
| 962 |
-
if is_game_over(state):
|
| 963 |
-
_render_game_over(state)
|
|
|
|
| 834 |
|
| 835 |
|
| 836 |
def _render_score_panel(state: GameState):
|
| 837 |
+
# Only show modal from here if game is over AND overlay wasn't dismissed
|
| 838 |
+
if is_game_over(state) and not st.session_state.get("hide_gameover_overlay", False):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
_render_game_over(state)
|
| 840 |
else:
|
| 841 |
# Build a simple table with independent column backgrounds and visible gaps
|
|
|
|
| 859 |
extra_pts = max(0, pts - letters_display)
|
| 860 |
row_html = (
|
| 861 |
"<tr>"
|
| 862 |
+
f"<td class=\"blue-background \"'>{word_display}</td>"
|
| 863 |
+
f"<td class=\"blue-background \"'>{letters_display}</td>"
|
| 864 |
+
f"<td class=\"blue-background \"'>{extra_pts}</td>"
|
| 865 |
"</tr>"
|
| 866 |
)
|
| 867 |
rows_html.append(row_html)
|
|
|
|
| 877 |
|
| 878 |
|
| 879 |
def _render_game_over(state: GameState):
|
| 880 |
+
# Prepare table rows for words
|
| 881 |
+
word_rows = []
|
| 882 |
+
for w in state.puzzle.words:
|
| 883 |
+
pts = state.points_by_word.get(w.text, 0)
|
| 884 |
+
extra_pts = max(0, pts - len(w.text))
|
| 885 |
+
word_rows.append(
|
| 886 |
+
f"<tr><td class='blue-background'>{w.text}</td><td class='blue-background'>{len(w.text)}</td><td class='blue-background'>{extra_pts}</td></tr>"
|
| 887 |
+
)
|
| 888 |
+
table_html = (
|
| 889 |
+
"<table class='shiny-border' style=\"background: linear-gradient(-45deg, #a1a1a1, #ffffff, #a1a1a1, #666666); width:100%; margin: 0 auto;border-collapse:separate; border-spacing:0;\">"
|
| 890 |
+
"<tr>"
|
| 891 |
+
"<th class='blue-background bold-text'>Word</th>"
|
| 892 |
+
"<th class='blue-background bold-text'>Letters</th>"
|
| 893 |
+
"<th class='blue-background bold-text'>Extra</th>"
|
| 894 |
+
"</tr>"
|
| 895 |
+
f"{''.join(word_rows)}"
|
| 896 |
+
f"<tr class='blue-background'><td colspan='3'><h3 class='bold-text'>Total: {state.score}</h3></td></tr>"
|
| 897 |
+
"</table>"
|
| 898 |
)
|
| 899 |
+
# Overlay HTML with close link (sets ?overlay=0)
|
| 900 |
+
st.markdown(
|
| 901 |
+
f'''
|
| 902 |
+
<div id="bw-modal-overlay" style="position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9999;background:rgba(20,80,180,0.95);display:flex;flex-direction:column;justify-content:center;align-items:center;">
|
| 903 |
+
<div class="shiny-border" style="position:relative;background: linear-gradient(-45deg, #1d64c8, #ffffff, #1d64c8, #666666);padding:2rem 3rem;box-shadow:0 0 32px #1d64c8;min-width:340px;max-width:90vw; margin:8vh auto 5vh;">
|
| 904 |
+
<a href="?overlay=0" title="Close" style="position:absolute;top:12px;right:12px;display:inline-grid;place-items:center;width:40px;height:40px;border-radius:50%;background:rgba(0,0,0,0.25);color:#fff;text-decoration:none;font-size:1.6rem;font-weight:700;">×</a>
|
| 905 |
+
<h1 style="color:#fff;font-size:2.5rem;margin-bottom:0.5rem;">Congratulations!</h1>
|
| 906 |
+
<h2 style="color:#fff;font-size:2rem;margin-bottom:1rem;">Game Over</h2>
|
| 907 |
+
<div style="font-size:1.5rem;color:#1d64c8;margin-bottom:1rem;">Final score: <span style="color:#1d64c8;font-weight:800;">{state.score}</span></div>
|
| 908 |
+
<div style="font-size:1.2rem;color:#fff;margin-bottom:2rem;">Tier: <strong>{compute_tier(state.score)}</strong></div>
|
| 909 |
+
<div style="margin-bottom:2rem;">{table_html}</div>
|
| 910 |
+
<div style="color:#fff;opacity:0.7;font-size:1rem;margin-bottom:2rem;background:#1d64c8;text-align:center;">Thank you for playing BattleWords!</div>
|
| 911 |
+
</div>
|
| 912 |
+
</div>
|
| 913 |
+
''', unsafe_allow_html=True)
|
| 914 |
+
st.markdown("<div style='height:32px'></div>", unsafe_allow_html=True)
|
| 915 |
+
if st.button("New Game", key="modal_new_game_btn", help="Start a new game", type="primary"):
|
| 916 |
+
_new_game()
|
| 917 |
st.markdown(versions_html(), unsafe_allow_html=True)
|
|
|
|
| 918 |
st.stop()
|
| 919 |
|
| 920 |
|
|
|
|
| 939 |
|
| 940 |
|
| 941 |
def run_app():
|
| 942 |
+
# Handle overlay dismissal via query params using new API
|
| 943 |
+
try:
|
| 944 |
+
params = st.query_params
|
| 945 |
+
except Exception:
|
| 946 |
+
params = {}
|
| 947 |
+
if params.get("overlay") == "0":
|
| 948 |
+
# Clear param and remember to hide overlay this session
|
| 949 |
+
try:
|
| 950 |
+
st.query_params.clear()
|
| 951 |
+
except Exception:
|
| 952 |
+
pass
|
| 953 |
+
st.session_state["hide_gameover_overlay"] = True
|
| 954 |
+
|
| 955 |
_init_session()
|
| 956 |
st.markdown(ocean_background_css, unsafe_allow_html=True)
|
| 957 |
_render_header()
|
|
|
|
| 976 |
_render_grid(state, st.session_state.letter_map, show_grid_ticks=st.session_state.get("show_grid_ticks", True))
|
| 977 |
st.button("New Game", width=125, on_click=_new_game, key="new_game_btn")
|
| 978 |
|
| 979 |
+
# End condition (only show overlay if not dismissed)
|
|
|
|
| 980 |
state = _to_state()
|
| 981 |
+
if is_game_over(state) and not st.session_state.get("hide_gameover_overlay", False):
|
| 982 |
+
_render_game_over(state)
|