Fix Appearance endless scroll label markup

This commit is contained in:
Frank Song
2026-05-14 10:39:12 +08:00
parent 3f4e82b4d1
commit 3d2f9cf699
5 changed files with 23 additions and 0 deletions

View File

@@ -4,6 +4,8 @@
### Fixed
- **PR #2222** by @franksong2702 — Settings → Appearance now wraps the "Load older messages while scrolling up" checkbox in its own label instead of leaving it after the session-jump description with a stray closing label. This keeps the endless-scroll setting layout stable when Appearance controls such as font size are changed.
- **PR #2217** by @franksong2702 (refs #2215 Fix B) — Drops the leftover `re.MULTILINE` flag from the "the user is asking" pre-amble strip pattern in `api/streaming.py:695`. PR #2213 removed `re.MULTILINE` from the three sibling wrapper-strip patterns (`<think>`, MiniMax, Gemma) but missed this one instance. With `re.MULTILINE`, `^` matched the start of any line in the response, so a mid-response line that legitimately started with "The user is asking us to wait" could be stripped silently. Now the pattern only matches when the entire response leads with that wrapper, consistent with the other strips. One-flag, two-character change + regression test pinning the new behavior.
- **PR #2216** by @franksong2702 (closes #2215 Fix A) — Caps the `_summary_cache` for per-target update summaries with an `OrderedDict`-backed LRU bounded at 16 entries. Pre-fix the cache was an unbounded plain dict introduced in PR #2207; cardinality is small in practice (0-2 active update ranges per server lifetime) so this is defensive future-proofing rather than a leak being hit today. Cache hits call `move_to_end()` to refresh recency; cache writes call `popitem(last=False)` to evict the oldest entry when at capacity. Overwrites of existing keys bypass eviction. Both operations run under the existing `_cache_lock` for thread safety. With Fix A and Fix B both shipped, issue #2215 is closed.

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -893,6 +893,7 @@
<span data-i18n="settings_label_session_jump_buttons">Show session jump buttons</span>
</label>
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_session_jump_buttons">Show floating Start and End buttons while reading long session histories.</div>
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin-top:10px">
<input type="checkbox" id="settingsSessionEndlessScroll" style="width:15px;height:15px;accent-color:var(--accent)">
<span data-i18n="settings_label_session_endless_scroll">Load older messages while scrolling up</span>
</label>

View File

@@ -1,4 +1,5 @@
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parents[1]
CONFIG_PY = (ROOT / "api" / "config.py").read_text(encoding="utf-8")
@@ -20,6 +21,25 @@ def test_endless_scroll_is_opt_in_setting():
assert "window._sessionEndlessScrollEnabled=false" in BOOT_JS
def test_endless_scroll_setting_has_its_own_label():
match = re.search(
r"<label[^>]*>\s*"
r'<input type="checkbox" id="settingsSessionEndlessScroll"[^>]*>\s*'
r'<span data-i18n="settings_label_session_endless_scroll">[^<]+</span>\s*'
r"</label>\s*"
r'<div[^>]*data-i18n="settings_desc_session_endless_scroll"',
INDEX_HTML,
)
assert match, "endless-scroll checkbox must be wrapped by its own label before its description"
def test_session_jump_and_endless_scroll_labels_are_separate():
jump_label_end = INDEX_HTML.index('data-i18n="settings_desc_session_jump_buttons"')
endless_label_start = INDEX_HTML.index('id="settingsSessionEndlessScroll"')
between = INDEX_HTML[jump_label_end:endless_label_start]
assert "<label" in between, "endless-scroll checkbox must not share the session-jump label"
def test_scroll_listener_prefetches_older_messages_only_when_enabled():
assert "function _isSessionEndlessScrollEnabled" in UI_JS
assert "const olderPrefetchPx=Math.max(600,el.clientHeight*1.5)" in UI_JS