From 35bff901d26600ae5173f5392fe56c436c1c6505 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 14 Oct 2025 16:45:49 -0700 Subject: [PATCH] feat: add ideal counts slider UI with smart validation --- .env.example | 3 + CHANGELOG.md | 12 ++ DOCKER.md | 1 + README.md | 1 + RELEASE_NOTES_TEMPLATE.md | 9 +- code/web/app.py | 1 + code/web/routes/build.py | 11 ++ code/web/static/styles.css | 49 +++++++ .../web/templates/build/_new_deck_ideals.html | 124 ++++++++++++++++++ code/web/templates/build/_new_deck_modal.html | 11 +- docker-compose.yml | 3 + dockerhub-docker-compose.yml | 3 + 12 files changed, 217 insertions(+), 11 deletions(-) create mode 100644 code/web/templates/build/_new_deck_ideals.html diff --git a/.env.example b/.env.example index d9c19cd..c01736e 100644 --- a/.env.example +++ b/.env.example @@ -96,6 +96,9 @@ WEB_AUTO_ENFORCE=0 # dockerhub: WEB_AUTO_ENFORCE="0" # Build Stage Ordering WEB_STAGE_ORDER=new # new|legacy. 'new' (default): creatures → spells → lands → fill. 'legacy': lands → creatures → spells → fill +# Ideals UI Mode +WEB_IDEALS_UI=slider # input|slider. 'slider' (default): range sliders with live value display. 'input': text input boxes + # Tagging Refinement Feature Flags TAG_NORMALIZE_KEYWORDS=1 # dockerhub: TAG_NORMALIZE_KEYWORDS="1" # Normalize keywords & filter specialty mechanics TAG_PROTECTION_GRANTS=1 # dockerhub: TAG_PROTECTION_GRANTS="1" # Protection tag only for cards granting shields diff --git a/CHANGELOG.md b/CHANGELOG.md index 860dcb4..f0c04eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,18 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [Unreleased] ### Summary - Enhanced deck building workflow with improved stage ordering, granular skip controls, and one-click Quick Build automation. +- New Ideal Counts section with interactive sliders or text inputs for customizing deck composition targets. - Stage execution order now prioritizes creatures and spells before lands for better mana curve analysis. - New wizard-only skip controls allow auto-advancing through specific stages (lands, creatures, spells) without approval prompts. - Quick Build button provides one-click full automation with clean 5-phase progress indicator. ### Added +- **Ideal Counts UI**: Dedicated section in New Deck wizard for setting ideal card counts (ramp, lands, creatures, removal, wipes, card advantage, protection). + - **Slider Mode** (default): Interactive range sliders with live value display and expanded ranges (e.g., creatures: 0-70, lands: 25-45). + - **Input Mode**: Text input boxes with placeholder defaults (e.g., "10 (Default)"). + - Smart validation warns when estimated total exceeds 99 cards (accounts for overlap: `Lands + Creatures + Spells/2`). + - Sliders start at recommended defaults and remember user preferences across builds. + - Configurable via `WEB_IDEALS_UI` environment variable (`slider` or `input`). - **Quick Build**: One-click automation button in New Deck wizard with live progress tracking (5 phases: Creatures, Spells, Lands, Final Touches, Summary). - **Skip Controls**: Granular stage-skipping toggles in New Deck wizard (21 flags: all land steps, creature stages, spell categories). - Individual land step controls: basics, staples, fetches, duals, triomes, kindred, misc lands. @@ -24,9 +31,12 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - **Stage Reordering**: New default build order executes creatures → spells → lands for improved pip analysis (configurable via `WEB_STAGE_ORDER` environment variable). - Background task execution for Quick Build with HTMX polling progress updates. - Mobile-friendly Quick Build with touch device confirmation dialog. +- Commander session cleanup: Commander selection automatically cleared after build completes. ### Changed - **Default Stage Order**: Creatures and ideal spells now execute before land stages (lands can analyze actual pip requirements instead of estimates). +- **Ideal Counts Display**: Removed collapsible "Advanced options (ideals)" section; replaced with prominent fieldset with slider/input modes. +- Slider ranges expanded to support edge-case strategies (e.g., creature-heavy tribal, spell-heavy control). - Skip controls only available in New Deck wizard (disabled during build execution for consistency). - Skip behavior auto-advances through stages without approval prompts (cards still added, just not gated). - Post-spell land adjustment automatically skipped when any skip flag enabled. @@ -35,6 +45,8 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - Session context properly injected into Quick Build so skip configuration works correctly. - HTMX polling uses continuous trigger (`every 500ms`) instead of one-time (`load delay`) for reliable progress updates. - Progress indicator stops cleanly when build completes (out-of-band swap removes poller div). +- Ideal counts now properly populate from session state, allowing sliders to start at defaults and remember user preferences. +- Commander and commander_name cleared from session after build completes to prevent carryover to next build. ## [2.6.1] - 2025-10-13 ### Summary diff --git a/DOCKER.md b/DOCKER.md index 6447c0c..9ce253d 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -255,6 +255,7 @@ See `.env.example` for the full catalog. Common knobs: | `SHOW_MUST_HAVE_BUTTONS` | `0` | Surface the must include/exclude buttons and quick-add UI (requires `ALLOW_MUST_HAVES=1`). | | `THEME` | `dark` | Initial UI theme (`system`, `light`, or `dark`). | | `WEB_STAGE_ORDER` | `new` | Build stage execution order: `new` (creatures→spells→lands) or `legacy` (lands→creatures→spells). | +| `WEB_IDEALS_UI` | `slider` | Ideal counts interface: `slider` (range inputs with live validation) or `input` (text boxes with placeholders). | ### Random build controls diff --git a/README.md b/README.md index 6e5b757..e12e294 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Start here for interactive deck creation. - `ALLOW_MUST_HAVES=1` (default) enables include/exclude enforcement. - `WEB_AUTO_ENFORCE=1` re-runs bracket enforcement automatically after each build. - `WEB_STAGE_ORDER=new` (default) runs creatures/spells before lands for better pip analysis. Use `legacy` for original lands-first order. +- `WEB_IDEALS_UI=slider` (default) shows interactive range sliders for ideal counts with live validation. Use `input` for traditional text boxes. ### Run a JSON Config Execute saved configs without manual input. diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 37db04c..6792d98 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -1,10 +1,11 @@ # MTG Python Deckbuilder ${VERSION} ### Summary -- Enhanced deck building workflow with improved stage ordering, granular skip controls, and one-click Quick Build automation. +- Enhanced deck building workflow with improved stage ordering, granular skip controls, one-click Quick Build automation, and interactive Ideal Counts UI. - Stage execution order now prioritizes creatures and spells before lands for better mana curve analysis. - New wizard-only skip controls allow auto-advancing through specific stages (lands, creatures, spells) without approval prompts. - Quick Build button provides one-click full automation with clean 5-phase progress indicator. +- Ideal Counts now feature interactive slider UI with live validation and smart overlap detection. ### Added - **Quick Build**: One-click automation button in New Deck wizard with live progress tracking (5 phases: Creatures, Spells, Lands, Final Touches, Summary). @@ -14,6 +15,12 @@ - Creature stage controls: all creatures, primary, secondary, fill. - Mutual exclusivity enforcement: "Skip All Lands" disables individual land toggles; "Skip to Misc Lands" skips early land steps. - **Stage Reordering**: New default build order executes creatures → spells → lands for improved pip analysis (configurable via `WEB_STAGE_ORDER` environment variable). +- **Ideal Counts UI**: Interactive slider interface with live value display and smart validation (configurable via `WEB_IDEALS_UI` environment variable). + - Slider Mode (default): Range sliders for all ideal counts with expanded ranges (creatures: 0-70, lands: 25-45). + - Input Mode: Traditional text inputs with placeholder defaults showing recommended values. + - Smart Validation: Real-time deck size estimation using overlap-aware calculation (`Lands + Creatures + Spells/2`). + - Visual Warnings: Red alert (>99 cards), orange warning (90-99), no warning (<90). + - Session Persistence: Values persist across builds and initialize at defaults on first wizard load. - Background task execution for Quick Build with HTMX polling progress updates. - Mobile-friendly Quick Build with touch device confirmation dialog. diff --git a/code/web/app.py b/code/web/app.py index afdfc49..3c17093 100644 --- a/code/web/app.py +++ b/code/web/app.py @@ -128,6 +128,7 @@ ENABLE_PRESETS = _as_bool(os.getenv("ENABLE_PRESETS"), False) ALLOW_MUST_HAVES = _as_bool(os.getenv("ALLOW_MUST_HAVES"), True) SHOW_MUST_HAVE_BUTTONS = _as_bool(os.getenv("SHOW_MUST_HAVE_BUTTONS"), False) ENABLE_CUSTOM_THEMES = _as_bool(os.getenv("ENABLE_CUSTOM_THEMES"), True) +WEB_IDEALS_UI = os.getenv("WEB_IDEALS_UI", "slider").strip().lower() # 'input' or 'slider' ENABLE_PARTNER_MECHANICS = _as_bool(os.getenv("ENABLE_PARTNER_MECHANICS"), True) ENABLE_PARTNER_SUGGESTIONS = _as_bool(os.getenv("ENABLE_PARTNER_SUGGESTIONS"), True) RANDOM_MODES = _as_bool(os.getenv("RANDOM_MODES"), True) # initial snapshot (legacy) diff --git a/code/web/routes/build.py b/code/web/routes/build.py index 944c075..a3fca96 100644 --- a/code/web/routes/build.py +++ b/code/web/routes/build.py @@ -13,6 +13,7 @@ from ..app import ( _sanitize_theme, ENABLE_PARTNER_MECHANICS, ENABLE_PARTNER_SUGGESTIONS, + WEB_IDEALS_UI, ) from ..services.build_utils import ( step5_base_ctx, @@ -1356,6 +1357,7 @@ async def build_new_modal(request: Request) -> HTMLResponse: "allow_must_haves": ALLOW_MUST_HAVES, # Add feature flag "show_must_have_buttons": SHOW_MUST_HAVE_BUTTONS, "enable_custom_themes": ENABLE_CUSTOM_THEMES, + "ideals_ui_mode": WEB_IDEALS_UI, # 'input' or 'slider' "form": { "prefer_combos": bool(sess.get("prefer_combos")), "combo_count": sess.get("combo_target_count"), @@ -1364,6 +1366,15 @@ async def build_new_modal(request: Request) -> HTMLResponse: "use_owned_only": bool(sess.get("use_owned_only")), "prefer_owned": bool(sess.get("prefer_owned")), "swap_mdfc_basics": bool(sess.get("swap_mdfc_basics")), + # Add ideal values from session (will be None on first load, triggering defaults) + "ramp": sess.get("ideals", {}).get("ramp"), + "lands": sess.get("ideals", {}).get("lands"), + "basic_lands": sess.get("ideals", {}).get("basic_lands"), + "creatures": sess.get("ideals", {}).get("creatures"), + "removal": sess.get("ideals", {}).get("removal"), + "wipes": sess.get("ideals", {}).get("wipes"), + "card_advantage": sess.get("ideals", {}).get("card_advantage"), + "protection": sess.get("ideals", {}).get("protection"), }, "tag_slot_html": None, } diff --git a/code/web/static/styles.css b/code/web/static/styles.css index 6992feb..4c610c3 100644 --- a/code/web/static/styles.css +++ b/code/web/static/styles.css @@ -678,3 +678,52 @@ img.lqip.loaded { filter: blur(0); opacity: 1; } 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } + +/* Ideals Slider Styling */ +.ideals-slider { + -webkit-appearance: none; + appearance: none; + height: 6px; + background: var(--border); + border-radius: 3px; + outline: none; +} + +.ideals-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + background: var(--ring); + border-radius: 50%; + cursor: pointer; + transition: all 0.15s ease; +} + +.ideals-slider::-webkit-slider-thumb:hover { + transform: scale(1.15); + box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2); +} + +.ideals-slider::-moz-range-thumb { + width: 18px; + height: 18px; + background: var(--ring); + border: none; + border-radius: 50%; + cursor: pointer; + transition: all 0.15s ease; +} + +.ideals-slider::-moz-range-thumb:hover { + transform: scale(1.15); + box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2); +} + +.slider-value { + display: inline-block; + padding: 0.25rem 0.5rem; + background: var(--panel); + border: 1px solid var(--border); + border-radius: 4px; +} diff --git a/code/web/templates/build/_new_deck_ideals.html b/code/web/templates/build/_new_deck_ideals.html new file mode 100644 index 0000000..38304c8 --- /dev/null +++ b/code/web/templates/build/_new_deck_ideals.html @@ -0,0 +1,124 @@ +
+ Ideal Counts +
+ Sliders start at recommended defaults. Adjust as needed for your deck strategy. + +
+
+ {% set use_sliders = ideals_ui_mode == 'slider' %} + {% set ideals_data = [ + ('ramp', labels.ramp, defaults.ramp, 0, 30), + ('lands', labels.lands, defaults.lands, 25, 45), + ('basic_lands', labels.basic_lands, defaults.basic_lands, 0, 40), + ('creatures', labels.creatures, defaults.creatures, 0, 70), + ('removal', labels.removal, defaults.removal, 0, 30), + ('wipes', labels.wipes, defaults.wipes, 0, 15), + ('card_advantage', labels.card_advantage, defaults.card_advantage, 0, 30), + ('protection', labels.protection, defaults.protection, 0, 20) + ] %} + + {% for field_name, field_label, default_val, min_val, max_val in ideals_data %} +
+ +
+ {% endfor %} +
+ + +
diff --git a/code/web/templates/build/_new_deck_modal.html b/code/web/templates/build/_new_deck_modal.html index 3aaee52..0ae200e 100644 --- a/code/web/templates/build/_new_deck_modal.html +++ b/code/web/templates/build/_new_deck_modal.html @@ -110,6 +110,7 @@ + {% include "build/_new_deck_ideals.html" %} {% if allow_must_haves %}
Include/Exclude Cards @@ -213,16 +214,6 @@ {% endif %} {% endif %} {% include "build/_new_deck_skip_controls.html" %} -
- Advanced options (ideals) -
- {% for key, label in labels.items() %} - - {% endfor %} -
-