Merge pull request #5647 from nesquena/release/stage-tiny

Release: sidebar auto-refresh (#5632) + composer-divider fix (#5499)
This commit is contained in:
nesquena-hermes authored and GitHub committed 2026-07-05 14:48:29 -07:00
commit 6d0ff92fa7
4 files changed
+38 -2

No files matched your search

+2
View File
@@ -26,6 +26,8 @@
### Fixed
- **The sidebar now shows a newly created session immediately.** Creating a new conversation from inside the WebUI left the sidebar list stale on some code paths until a manual refresh; `newSession()` now refreshes the list (coalesced, so no double-repaint). Thanks @nankingjing. (#5632, #3874)
- **No more stray composer divider when the left button group is fully hidden.** If you hid all four left composer controls (attach, saved prompts, mic, voice mode), the vertical divider to their right was left orphaned; it's now hidden with them. The divider visibility is also recomputed after the voice-mode preference is applied on both the settings-load boot path and the voice-mode toggle, so it no longer boots against a stale button state. Thanks @silent-reader-cn. (#5499, #5451)
- **Mobile: scrolling up into history during a streaming reply no longer jumps the viewport to the top.** On touch devices, a recent change applied `content-visibility: auto` with a flat 1px intrinsic-size estimate to every off-screen message row; tall assistant rows (long answers / large tool results) collapsed to 1px, `scrollHeight` lurched down by tens of thousands of pixels, and the browser clamped `scrollTop` back to the top. `content-visibility` is now kept only for the short, size-predictable user rows (assistant rows keep layout/style containment without the height-collapse), and when you've scrolled up and the semantic anchor can't be restored, the view now holds position via native scroll-anchoring instead of snapping to a stale offset. Thanks @allenliang2022. (#5638, #5637)
- **Long transcripts no longer jump to the top when the scroll anchor row is recycled out of the virtual window.** The virtual-scroll compensation used to give up (leaving the viewport uncompensated) when its anchor row had been recycled during a re-render; it now recovers the anchor by session message index and falls back to a top-padding delta, so scroll position is preserved across re-renders. Thanks @allenliang2022. (#5635, #4346)
- **Compacted (archived) messages no longer creep back into the model's context.** In-place context compaction marks old messages inactive in `state.db`, but WebUI's reconciliation reader still pulled those archived rows back into the next turn's model-facing context — undoing the compaction and making long conversations re-trigger compression every turn. The reader now excludes inactive rows by default (with an explicit opt-in for recovery/audit views), so compaction actually sticks. Schemas without the active-row marker are unaffected. Thanks @ai-ag2026. (#5626)
+30 -1
View File
@@ -2743,6 +2743,21 @@ function _applyComposerFooterVisibilitySettings(){
if(hidden.hide_composer_reasoning&&typeof closeReasoningDropdown==='function') closeReasoningDropdown();
if(hidden.hide_composer_toolsets&&typeof closeToolsetsDropdown==='function') closeToolsetsDropdown();
if(hidden.hide_composer_mobile_config&&typeof closeMobileComposerConfig==='function') closeMobileComposerConfig();
// Hide the divider when all left-group buttons before it are hidden
// Stops a lone vertical separator from appearing when attach/saved-prompts/mic/voice are all hidden.
const _divider=document.querySelector('.composer-divider');
if(_divider){
const _leftBtnSelectors=['#btnAttach','#btnSavedPrompts','#btnMic','#btnVoiceMode'];
const _allLeftHidden=_leftBtnSelectors.every(sel=>{
const el=document.querySelector(sel);
return !el||el.classList.contains('composer-control-hidden')||el.style.display==='none';
});
// Use classList.toggle directly instead of _setComposerControlHidden
// so we don't strip the intentional aria-hidden="true" on the decorative divider
// when buttons are visible (Greptile feedback).
_divider.classList.toggle('composer-control-hidden',_allLeftHidden);
}
}
window._applyComposerFooterVisibilitySettings=_applyComposerFooterVisibilitySettings;
@@ -2975,8 +2990,14 @@ window._mirrorSpeechSettingsFromServer=_mirrorSpeechSettingsFromServer;
if(typeof applyLocaleToDOM==='function')applyLocaleToDOM();
}
_mirrorSpeechSettingsFromServer(s);
_applyComposerFooterVisibilitySettings();
// Apply voice-mode visibility BEFORE computing the divider so the
// .composer-divider (#5451) sees #btnVoiceMode final display even
// when a server/localStorage sync path flipped the pref between
// module init and settings-load completion (round-2 SILENT race).
// Note: must use window._applyVoiceModePref — the bare name is
// closure-local to the voice-mode IIFE and not visible here.
if(typeof window._applyVoiceModePref==='function') window._applyVoiceModePref();
_applyComposerFooterVisibilitySettings();
// TTS: apply enabled state on boot so buttons show/hide correctly (#499)
if(typeof _applyTtsEnabled==='function') _applyTtsEnabled(localStorage.getItem('hermes-tts-enabled')==='true');
}catch(e){
@@ -3028,6 +3049,14 @@ window._mirrorSpeechSettingsFromServer=_mirrorSpeechSettingsFromServer;
setLocale(_lang);
if(typeof applyLocaleToDOM==='function')applyLocaleToDOM();
}
// Apply voice-mode visibility BEFORE computing the divider so the
// .composer-divider (#5451) sees #btnVoiceMode final display even when
// a server/localStorage sync path flipped the pref between module init
// and settings-load completion (round-2 SILENT race fix; safe no-op on
// the failure-fallback path because _applyVoiceModePref is idempotent).
// Note: must use window._applyVoiceModePref — the bare name is
// closure-local to the voice-mode IIFE and not visible here.
if(typeof window._applyVoiceModePref==='function') window._applyVoiceModePref();
_applyComposerFooterVisibilitySettings();
if(typeof _applyTtsEnabled==='function') _applyTtsEnabled(localStorage.getItem('hermes-tts-enabled')==='true');
}
+4
View File
@@ -8907,6 +8907,9 @@ async function loadSettingsPanel(){
// Voice-mode button visibility (#1488).
// Toggling re-applies immediately via the boot.js helper so the user sees
// the audio-waveform button appear/disappear without a reload.
// Also recomputes composer footer visibility so the .composer-divider
// (which tracks whether all left-group buttons are hidden, see #5451)
// stays in sync when #btnVoiceMode appears or disappears here.
const voiceModeCb=$('settingsVoiceModeEnabled');
if(voiceModeCb){
voiceModeCb.checked=_speechBool('voice_mode_button','hermes-voice-mode-button',false);
@@ -8914,6 +8917,7 @@ async function loadSettingsPanel(){
_markSpeechPreferenceChanged('voice_mode_button');
localStorage.setItem('hermes-voice-mode-button',this.checked?'true':'false');
if(typeof window._applyVoiceModePref==='function') window._applyVoiceModePref();
if(typeof window._applyComposerFooterVisibilitySettings==='function') window._applyComposerFooterVisibilitySettings();
_schedulePreferencesAutosave();
};
}
+2 -1
View File
@@ -1242,7 +1242,8 @@ async function newSession(flash, options={}){
const _dirP=loadDir('.');
if(_dirP&&typeof _dirP.catch==='function') _dirP.catch(()=>{});
}
// don't call renderSessionList here - callers do it when needed
// Refresh sidebar to include the newly created session (#3874).
if(typeof refreshSessionList==='function'){Promise.resolve(refreshSessionList('new-session')).catch(()=>{})}
})();
try{
return await _newSessionInFlight;