#1360 — On macOS WKWebView, trackpad momentum scrolling fires scroll events that interleave with the _programmaticScroll setTimeout(0) guard. A mid-momentum scroll event either gets swallowed (_programmaticScroll still true) or falsely reports nearBottom (momentum hasn't settled), keeping _scrollPinned=true and snapping the viewport back down. Fix: rAF-debounce the scroll listener so the nearBottom check runs at the next paint frame when the browser's scroll position has settled. Added a hysteresis counter requiring 2 consecutive near-bottom samples before re-pinning, preventing accidental re-pin during deceleration. #1619 — When a custom:* provider (e.g. custom:relay via custom_providers) has models that overlap with auto-detected models from base_url /v1/models, the dedup logic at config.py:2263 skipped them all. The named custom group ended up empty, and the continue at line 2334 silently discarded the auto-detected models. Result: only the default model appeared. Fix 1 (config.py): When custom:* named group has 0 models after dedup, fall back to auto_detected_models_by_provider instead of dropping them. Fix 2 (routes.py): Extended /api/models/live fallback to handle custom:* slugs (not just bare "custom") for both custom_providers config lookup and base_url live fetch.
This commit is contained in:
1 parent
304a422814
commit
324aeaaded
3 files changed
+35
-15
No files matched your search
@@ -2524,6 +2524,11 @@ def get_available_models() -> dict:
|
||||
for pid in sorted(detected_providers):
|
||||
if pid.startswith("custom:") and pid in _named_custom_groups:
|
||||
_nc_display, _nc_models = _named_custom_groups[pid]
|
||||
# If all named-group models were deduped (already auto-detected
|
||||
# from base_url /v1/models), fall back to auto-detected models
|
||||
# instead of silently dropping the group (issue #1619).
|
||||
if not _nc_models:
|
||||
_nc_models = auto_detected_models_by_provider.get(pid, [])
|
||||
if _nc_models:
|
||||
groups.append({"provider": _nc_display, "provider_id": pid, "models": _nc_models})
|
||||
continue
|
||||
|
||||
+8
-7
@@ -4523,11 +4523,12 @@ def _handle_live_models(handler, parsed):
|
||||
ids = []
|
||||
|
||||
if not ids:
|
||||
# For 'custom' provider, provider_model_ids() returns [] because
|
||||
# 'custom' isn't a real endpoint. Fall back to the custom_providers
|
||||
# entries from config.yaml so the live-model enrichment step can
|
||||
# add any models that weren't already in the static list.
|
||||
if provider == "custom":
|
||||
# For 'custom' and 'custom:*' providers, provider_model_ids()
|
||||
# returns [] because they aren't real hermes_cli endpoints.
|
||||
# Fall back to the custom_providers entries from config.yaml so
|
||||
# the live-model enrichment step can add any models that weren't
|
||||
# already in the static list (issue #1619).
|
||||
if provider == "custom" or provider.startswith("custom:"):
|
||||
try:
|
||||
_cp_entries = cfg.get("custom_providers", [])
|
||||
if isinstance(_cp_entries, list):
|
||||
@@ -4539,8 +4540,8 @@ def _handle_live_models(handler, parsed):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If still no ids, try fetching from model.base_url directly (OpenAI-compat endpoint)
|
||||
if not ids and provider == "custom":
|
||||
# If still no ids, try fetching from base_url directly (OpenAI-compat endpoint)
|
||||
if not ids and (provider == "custom" or provider.startswith("custom:")):
|
||||
_base_url = cfg.get("model", {}).get("base_url")
|
||||
_api_key = cfg.get("model", {}).get("api_key")
|
||||
if _base_url and _api_key:
|
||||
|
||||
+22
-8
@@ -1191,21 +1191,35 @@ window.addEventListener('resize',function(){
|
||||
// Uses a guard flag to avoid the race where programmatic scrolls (from
|
||||
// scrollIfPinned / scrollToBottom) re-set _scrollPinned=true, overriding
|
||||
// the user's explicit scroll-up. Fixes #1469 / #1360.
|
||||
// rAF-debounced scroll listener (issue #1360): on macOS WKWebView, trackpad
|
||||
// momentum scrolling fires scroll events that interleave with the
|
||||
// _programmaticScroll setTimeout(0) guard. A mid-momentum scroll event can
|
||||
// either get swallowed (_programmaticScroll still true) or falsely report
|
||||
// nearBottom (momentum hasn't settled). rAF defers the nearBottom check to
|
||||
// the next paint frame when the browser's scroll position has settled.
|
||||
// A hysteresis counter requires two consecutive near-bottom samples before
|
||||
// re-pinning, preventing accidental re-pin during initial deceleration.
|
||||
let _scrollPinned=true;
|
||||
let _programmaticScroll=false;
|
||||
let _nearBottomCount=0;
|
||||
(function(){
|
||||
const el=document.getElementById('messages');
|
||||
if(!el) return;
|
||||
let _scrollRaf=0;
|
||||
el.addEventListener('scroll',()=>{
|
||||
if(_programmaticScroll) return; // ignore scrolls we triggered ourselves
|
||||
const nearBottom=el.scrollHeight-el.scrollTop-el.clientHeight<250;
|
||||
_scrollPinned=nearBottom;
|
||||
const btn=$('scrollToBottomBtn');
|
||||
if(btn) btn.style.display=_scrollPinned?'none':'flex';
|
||||
// Load older messages when scrolled near the top
|
||||
if(el.scrollTop<80 && typeof _messagesTruncated!=='undefined' && _messagesTruncated && typeof _loadOlderMessages==='function'){
|
||||
_loadOlderMessages();
|
||||
}
|
||||
cancelAnimationFrame(_scrollRaf);
|
||||
_scrollRaf=requestAnimationFrame(()=>{
|
||||
const nearBottom=el.scrollHeight-el.scrollTop-el.clientHeight<250;
|
||||
_nearBottomCount=nearBottom?_nearBottomCount+1:0;
|
||||
_scrollPinned=_nearBottomCount>=2;
|
||||
const btn=$('scrollToBottomBtn');
|
||||
if(btn) btn.style.display=_scrollPinned?'none':'flex';
|
||||
// Load older messages when scrolled near the top
|
||||
if(el.scrollTop<80 && typeof _messagesTruncated!=='undefined' && _messagesTruncated && typeof _loadOlderMessages==='function'){
|
||||
_loadOlderMessages();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
function _fmtTokens(n){if(!n||n<0)return'0';if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'k';return String(n);}
|
||||
|
||||
Reference in new issue
Block a user