v0.50.25: mobile scroll, import timestamps, profile security, mic fallback (#404)
* fix: restore mobile chat scrolling and drawer close (#397) - static/style.css: add min-height:0 to .layout and .main (flex shrink chain fix for mobile scroll) - static/style.css: add -webkit-overflow-scrolling:touch, touch-action:pan-y, overscroll-behavior-y:contain to .messages - static/boot.js: call closeMobileSidebar() on new-conversation button onclick and Ctrl+K shortcut - tests/test_mobile_layout.py: 41 new lines covering all three CSS fixes and both JS call sites Original PR by @Jordan-SkyLF * fix: preserve imported session timestamps (#395) - api/models.py: add touch_updated_at: bool = True param to Session.save(); import_cli_session() accepts created_at/updated_at kwargs and saves with touch_updated_at=False - api/routes.py: extract created_at/updated_at from get_cli_sessions() metadata and forward to import_cli_session(); use touch_updated_at=False on post-import save - tests/test_gateway_sync.py: +53 lines — integration test verifying imported session keeps original timestamp and sorts correctly vs newer sessions; also fix: add WebUI session file cleanup in finally block Original PR by @Jordan-SkyLF * fix(profiles): block path traversal in profile switch and delete flows (#399) Master was vulnerable: switch_profile and delete_profile_api joined user-supplied profile names directly into filesystem paths with no validation. An attacker could send '../../etc/passwd' as a profile name to traverse outside the profiles directory. - api/profiles.py: add _resolve_named_profile_home(name) — validates name with ^[a-z0-9][a-z0-9_-]{0,63}$ regex then enforces path containment via candidate.resolve().relative_to(profiles_root); use in switch_profile() - api/profiles.py: add _validate_profile_name() call to delete_profile_api() entry - api/routes.py: add _validate_profile_name() call at HTTP handler level for both /api/profile/switch and /api/profile/delete (fail-fast at API boundary) - tests/test_profile_path_security.py: 3 tests — traversal rejected, valid name passes Cherry-picked commit aae7a30 from @Hinotoi-agent (PR was 62 commits behind master) * feat: add desktop microphone transcription fallback (#396) Mic button now works in browsers that support getUserMedia/MediaRecorder but lack SpeechRecognition (e.g. Firefox desktop, some Chromium builds). - static/boot.js: detect _canRecordAudio (navigator.mediaDevices + getUserMedia + MediaRecorder); keep mic button enabled when either SpeechRecognition or MediaRecorder is available; MediaRecorder fallback records audio, sends blob to /api/transcribe, inserts transcript into the composer; _stopMic() handles all three states (recognition, mediaRecorder, neither) - api/upload.py: add transcribe_audio() helper — saves uploaded blob to temp file, calls transcription_tools.transcribe_audio(), always cleans up temp file - api/routes.py: add /api/transcribe POST handler — CSRF protected, auth-gated, 20MB limit, returns {text:...} or {error:...} - api/helpers.py: change Permissions-Policy microphone=() to microphone=(self) (required to allow getUserMedia in the same origin) - tests/test_voice_transcribe_endpoint.py: 87 new lines — 3 tests with mocked transcription - tests/test_sprint19.py: +1 regression guard (microphone=(self) in Permissions-Policy) - tests/test_sprint20.py: 3 updated tests for new fallback-capability checks Original PR by @Jordan-SkyLF * docs: v0.50.25 release — version badge and CHANGELOG --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
This commit is contained in:
@@ -133,6 +133,21 @@ def test_toggle_mobile_files_js_defined():
|
||||
"toggleMobileFiles() must toggle mobile-open class on the right panel"
|
||||
|
||||
|
||||
def test_new_conversation_closes_mobile_sidebar():
|
||||
"""New conversation must close the mobile drawer so the chat pane is visible immediately."""
|
||||
boot_js = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
|
||||
click_line = next((ln for ln in boot_js.splitlines() if "$('btnNewChat').onclick" in ln), "")
|
||||
assert click_line, "btnNewChat onclick handler missing from static/boot.js"
|
||||
assert "closeMobileSidebar" in click_line, \
|
||||
"btnNewChat handler must closeMobileSidebar() after creating the new session"
|
||||
|
||||
shortcut_line = next((ln for ln in boot_js.splitlines() if "e.key==='k'" in ln or "e.key === 'k'" in ln), "")
|
||||
assert shortcut_line, "Cmd/Ctrl+K new chat shortcut missing from static/boot.js"
|
||||
shortcut_block = "\n".join(boot_js.splitlines()[boot_js.splitlines().index(shortcut_line):boot_js.splitlines().index(shortcut_line)+4])
|
||||
assert "closeMobileSidebar" in shortcut_block, \
|
||||
"Cmd/Ctrl+K new chat shortcut must closeMobileSidebar() after creating the new session"
|
||||
|
||||
|
||||
# ── Viewport and scroll safety ────────────────────────────────────────────────
|
||||
|
||||
def test_body_overflow_hidden():
|
||||
@@ -143,6 +158,32 @@ def test_body_overflow_hidden():
|
||||
"body must have overflow:hidden to prevent double scrollbars"
|
||||
|
||||
|
||||
def test_flex_parents_allow_message_scroller_to_shrink():
|
||||
"""The top-level flex containers must opt into min-height:0 so .messages can scroll on mobile.
|
||||
|
||||
Mobile Safari/Chrome can trap scroll when a flex child with overflow:auto sits inside
|
||||
parents whose min-height remains auto. Both .layout and .main need min-height:0.
|
||||
"""
|
||||
assert re.search(r'\.layout\{[^}]*min-height:0', CSS), \
|
||||
".layout must set min-height:0 so the chat column can shrink and scroll"
|
||||
assert re.search(r'\.main\{[^}]*min-height:0', CSS), \
|
||||
".main must set min-height:0 so .messages remains scrollable while busy"
|
||||
|
||||
|
||||
def test_messages_touch_scrolling_hints_present():
|
||||
"""The messages scroller must advertise touch-friendly scrolling behavior.
|
||||
|
||||
On mobile browsers, momentum scrolling and explicit pan-y/overscroll behavior help
|
||||
prevent the chat area from feeling locked while the app body itself stays overflow:hidden.
|
||||
"""
|
||||
assert re.search(r'\.messages\{[^}]*-webkit-overflow-scrolling:\s*touch', CSS), \
|
||||
".messages must enable -webkit-overflow-scrolling:touch for mobile momentum scroll"
|
||||
assert re.search(r'\.messages\{[^}]*touch-action:\s*pan-y', CSS), \
|
||||
".messages must set touch-action:pan-y so vertical swipe gestures scroll the transcript"
|
||||
assert re.search(r'\.messages\{[^}]*overscroll-behavior-y:\s*contain', CSS), \
|
||||
".messages must contain vertical overscroll so the transcript keeps the gesture"
|
||||
|
||||
|
||||
def test_100dvh_viewport_height():
|
||||
"""Layout must use 100dvh (dynamic viewport height) for correct mobile sizing.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user