| # Settings Page Refactoring Plan | |
| ## Goal | |
| Move the sidebar configuration controls (`_render_sidebar`) from `wrdler/ui.py` to a dedicated Settings page (`wrdler/settings_page.py`), accessible via the footer navigation. | |
| ## Files to Create | |
| 1. `wrdler/settings_page.py` | |
| ## Files to Modify | |
| 1. `wrdler/ui.py` | |
| ## Detailed Steps | |
| ### 1. Create `wrdler/settings_page.py` | |
| This file will encapsulate the rendering logic for the settings. | |
| - **Imports**: | |
| - `streamlit as st` | |
| - `os` | |
| - `time` | |
| - From `wrdler.word_loader`: `get_wordlist_files`, `get_wordlist_info` | |
| - From `wrdler.generator`: `sort_word_file`, `filter_word_file` | |
| - From `wrdler.audio`: `get_audio_tracks`, `_inject_audio_control_sync` | |
| - From `wrdler.version_info`: `versions_html` | |
| - **Functions**: | |
| - `render_settings_page(new_game_callback)`: | |
| - Renders the title "Settings". | |
| - Contains the logic previously in `_render_sidebar` (Game Mode, Wordlist Controls, Grid Options, Audio Controls). | |
| - **Important**: Does *not* include `_mount_background_audio` (this will be global). | |
| - Uses `st.container` or main layout instead of `st.sidebar`. | |
| - Accepts `new_game_callback` to trigger a game reset when settings change. | |
| - `_sort_wordlist(filename)`: Moved from `ui.py`. | |
| - `_filter_wordlist(filename)`: Moved from `ui.py`. | |
| - `_filter_results_dialog`: Moved from `ui.py` (if used by `_filter_wordlist`). | |
| - Local callbacks `_on_wordlist_change` and `_on_ai_generate` that utilize `new_game_callback`. | |
| ### 2. Modify `wrdler/ui.py` | |
| - **Extract Audio Logic**: | |
| - Create a helper function `_handle_audio()` that contains the audio initialization and mounting logic previously in `_render_sidebar`. | |
| - This ensures audio persists across pages. | |
| - **Update `run_app()`**: | |
| - Call `_handle_audio()` at the top level (before page routing). | |
| - Add routing logic for `page="settings"`: | |
| - Import `render_settings_page`. | |
| - Render background. | |
| - Call `render_settings_page(_on_game_option_change)`. | |
| - Render footer with `current_page="settings"`. | |
| - Return (stop execution of main game). | |
| - Remove `_render_sidebar()` call. | |
| - **Update `_render_footer()`**: | |
| - Add a link to `?page=settings` with label "?? Settings". | |
| - Highlight it when `current_page="settings"`. | |
| - **Cleanup**: | |
| - Remove `_render_sidebar` function. | |
| - Remove `_sort_wordlist`, `_filter_wordlist` (moved to settings page). | |
| ## Implementation Notes | |
| - **Audio**: The audio *controls* (volume, track) will be in the Settings page, but the audio *player* (hidden HTML/JS) must be mounted on every page load via `_handle_audio()` in `run_app` to ensure continuous playback or proper state. | |
| - **Callbacks**: `_on_game_option_change` in `ui.py` calls `_new_game`. This callback will be passed to `render_settings_page` so that changing settings triggers the necessary state resets. | |
| - **Navigation**: The footer will serve as the primary navigation between "Play", "Leaderboard", and "Settings". | |
| --- | |
| ## Settings Persistence Guidance (UPDATED) | |
| - **Each settings configuration is saved as a separate JSON file in `wrdler/settings/`, not as a single large file.** | |
| - **File naming convention:** Use a unique, human-readable key based on the main settings (e.g., `classic-classic-0.json`). | |
| - Example: `wrdler/settings/classic-classic-0.json` | |
| - This mirrors the leaderboard's convention (e.g., `weekly/2025-W51/classic-classic-0/settings.json`), but is local and not required to match challenge/leaderboard config structure. | |
| - **Settings files should use the same layout as the current settings.json, but each file only contains one configuration.** | |
| - **No need to match leaderboard or challenge settings.json structure exactly.** | |
| - **When saving settings, only the relevant configuration for that file is written.** | |
| - **When loading, look up the file by its unique key.** | |
| - **This approach supports local wordlists and ensures settings are unique per instance.** | |
| --- | |
| ## Plan: Local Settings File Storage and Loading (Implemented in v0.2.8) | |
| 1. **Settings File Naming** | |
| - For each unique settings configuration, generate a filename like `classic-classic-0.json` based on the main settings (e.g., game mode, wordlist, spacer). | |
| - Store these files in `wrdler/settings/`. | |
| 2. **Saving Settings** | |
| - When the user clicks "Save Settings" in the settings page: | |
| - Gather all relevant settings from `st.session_state`. | |
| - Generate the unique filename for the current configuration. | |
| - Save the settings as a JSON file in `wrdler/settings/` using the generated filename. | |
| - Use the same JSON structure as the current settings.json, but only for this configuration. | |
| 3. **Loading Settings in `wrdler/ui.py`** | |
| - On app startup (in `_init_session()` or before applying defaults): | |
| - Determine the intended settings file (e.g., from defaults or user selection). | |
| - If the file exists in `wrdler/settings/`, load it and update `st.session_state` with its values (only for keys not already set). | |
| - If not, proceed with defaults. | |
| 4. **Settings Page Integration** | |
| - The settings page should allow users to select, save, and load settings configurations by their unique keys. | |
| - Optionally, provide a dropdown or list of available settings files for quick switching. | |
| 5. **Directory Management** | |
| - Ensure the `wrdler/settings/` directory is created if it does not exist. | |
| - Handle file I/O errors gracefully and inform the user if saving/loading fails. | |
| 6. **Extensibility** | |
| - When new settings are added, include them in the filename generation and JSON structure as needed. | |
| - This approach allows for easy expansion as more settings or wordlists are introduced. | |