diff --git a/CHANGELOG.md b/CHANGELOG.md index 6115c51f..7e8eb6bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixed +- **PR #2141** by @franksong2702 — Settings → System version badges and the `Check now` control now wrap on narrow mobile widths instead of forcing the section header past the viewport edge. Adds a mobile CSS regression guard for the section-header stacking and version-control wrapping behavior. Closes #2102. + - **PR #2117** by @ayushere — `ctl.sh start` no longer crashes on macOS (bash 3.2) with `preserved[@]: unbound variable`. The dotenv-preserve loop in `_load_repo_dotenv_preserving_env()` iterated `"${preserved[@]}"` under `set -euo pipefail`, which bash 4+ silently allows on empty arrays but bash 3.2 (still the default `/usr/bin/bash` on macOS) treats as an unbound-variable error. Guards the iteration with `if [[ ${#preserved[@]} -gt 0 ]]; then ... fi` — matches the canonical bash 3.2 strict-mode pattern. This is the third bash 3.2 compat fix to land in `ctl.sh` (prior: `025f137f` guarded `CTL_BOOTSTRAP_ARGS[@]` with the `${arr[@]+...}` pass-through pattern, `630981a0` replaced `[[ -v ${key} ]]` with `[[ -n "${!key+x}" ]]`). Defense-in-depth: added `tests/test_ctl_bash32_compat.py` (5 static-pattern regressions) pinning both empty-array guards plus a denylist for bash 4+ syntax (`declare -A`, `mapfile`, `[[ -v ]]`, `${var^^}`, `${var,,}`) so the next regression surfaces in CI instead of a macOS user's terminal. Stage-343 reviewer added the regression-test file alongside the contributor's 5-LOC fix to ctl.sh. ## [v0.51.49] — 2026-05-12 — Release Y (stage-342 — 3-PR contributor batch — read-only worktree status endpoint + worktree-retained response preference + Codex quota credential-pool fallback) diff --git a/static/style.css b/static/style.css index 7e87920f..e00028ca 100644 --- a/static/style.css +++ b/static/style.css @@ -2471,7 +2471,10 @@ main.main.showing-logs > #mainLogs{display:flex;} /* Responsive: tighten canvas on small screens. */ @media (max-width: 768px){ .settings-main{padding:16px 12px;} + .settings-section-head{flex-direction:column;align-items:flex-start;gap:8px;} .settings-section-title{font-size:16px;} + #checkUpdatesBlock{flex-wrap:wrap;row-gap:6px;width:100%;} + .settings-version-badge{white-space:nowrap;} .hermes-action-grid{grid-template-columns:1fr;} #mainSettings .settings-field{padding:14px;} } diff --git a/tests/test_mobile_layout.py b/tests/test_mobile_layout.py index 10846852..80f97e86 100644 --- a/tests/test_mobile_layout.py +++ b/tests/test_mobile_layout.py @@ -148,6 +148,29 @@ def test_mobile_breakpoint_640px_present(): "Missing @media(max-width:640px) breakpoint in style.css" +def test_settings_system_version_controls_wrap_on_phone_widths(): + """Settings -> System version badges must wrap instead of overflowing phones.""" + mobile_css = "\n".join(_max_width_media_blocks(768)) + assert ".settings-section-head" in mobile_css, ( + "Settings section header needs a mobile rule so title and update controls stack." + ) + assert "flex-direction:column" in mobile_css.replace(" ", ""), ( + "Settings section header should stack vertically on mobile." + ) + assert "#checkUpdatesBlock" in mobile_css, ( + "Settings update/version controls need a mobile rule." + ) + assert "flex-wrap:wrap" in mobile_css.replace(" ", ""), ( + "Version badges and Check now button must wrap instead of overflowing." + ) + assert "width:100%" in mobile_css.replace(" ", ""), ( + "The update controls row should take the available mobile width." + ) + assert ".settings-version-badge" in mobile_css and "white-space:nowrap" in mobile_css.replace(" ", ""), ( + "Individual version badges should stay intact while the group wraps." + ) + + def test_rightpanel_mobile_slide_over_css(): """Right panel must have position:fixed slide-over CSS for mobile.""" # At max-width:900px the rightpanel should be position:fixed, off-screen right