fix: guard localStorage quota writes
This commit is contained in:
parent
e3035b3e40
commit
3bb8c7b276
6 changed files with 44 additions and 6 deletions
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **PR #2387** by @Michaelyklam (closes #2386) — Active-session and workspace-panel `localStorage` writes now degrade gracefully when browser storage quota is exhausted. The missed session/panel persistence writes now match the existing best-effort storage pattern so clicking sessions, creating chats, completing streams, and toggling the workspace panel no longer throw uncaught `QuotaExceededError` exceptions.
|
||||
|
||||
## [v0.51.74] — 2026-05-16 — Release AX (stage-367 — 4-PR safe-lane batch — #2362 table-cell spacing + #2363 run-state-consistency RFC + #2365 custom_providers list-format + #2367 settings sidebar i18n)
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ function _setWorkspacePanelMode(mode){
|
|||
// Persist open/closed across refreshes (browse/preview → open; closed → closed)
|
||||
// Do NOT overwrite the user's "keep open" preference — only track runtime state
|
||||
// so that toggleWorkspacePanel(false) from the toolbar doesn't clear the setting.
|
||||
localStorage.setItem('hermes-webui-workspace-panel', open ? 'open' : 'closed');
|
||||
try{localStorage.setItem('hermes-webui-workspace-panel', open ? 'open' : 'closed');}catch(_){}
|
||||
layout.classList.toggle('workspace-panel-collapsed',!open);
|
||||
if(_isCompactWorkspaceViewport()){
|
||||
panel.classList.toggle('mobile-open',open);
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ async function _applyManualCompressionResult(data, focusTopic, visibleCount, com
|
|||
S.messages=data.session.messages||[];
|
||||
S.toolCalls=data.session.tool_calls||[];
|
||||
clearLiveToolCards();
|
||||
localStorage.setItem('hermes-webui-session',S.session.session_id);
|
||||
try{localStorage.setItem('hermes-webui-session',S.session.session_id);}catch(_){}
|
||||
if(typeof _setActiveSessionUrl==='function') _setActiveSessionUrl(S.session.session_id);
|
||||
syncTopbar();
|
||||
renderMessages();
|
||||
|
|
|
|||
|
|
@ -1454,7 +1454,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
|||
const _prevCost=(S.session&&S.session.estimated_cost)||0;
|
||||
S.session=d.session;S.messages=d.session.messages||[];if(typeof _messagesTruncated!=='undefined')_messagesTruncated=!!d.session._messages_truncated;
|
||||
if(S.session&&S.session.session_id){
|
||||
localStorage.setItem('hermes-webui-session',S.session.session_id);
|
||||
try{localStorage.setItem('hermes-webui-session',S.session.session_id);}catch(_){}
|
||||
if(typeof _setActiveSessionUrl==='function') _setActiveSessionUrl(S.session.session_id);
|
||||
}
|
||||
const _markerOnlyAssistantError=_replaceMarkerOnlyAssistantWithStreamError(S.messages);
|
||||
|
|
@ -1824,7 +1824,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
|||
clearLiveToolCards();if(!assistantText)removeThinking();
|
||||
S.session=session;S.messages=(session.messages||[]).filter(m=>m&&m.role);
|
||||
if(S.session&&S.session.session_id){
|
||||
localStorage.setItem('hermes-webui-session',S.session.session_id);
|
||||
try{localStorage.setItem('hermes-webui-session',S.session.session_id);}catch(_){}
|
||||
if(typeof _setActiveSessionUrl==='function') _setActiveSessionUrl(S.session.session_id);
|
||||
}
|
||||
const _markerOnlyAssistantError=_replaceMarkerOnlyAssistantWithStreamError(S.messages);
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ async function newSession(flash, options={}){
|
|||
S.session=data.session;S.messages=data.session.messages||[];
|
||||
S.lastUsage={...(data.session.last_usage||{})};
|
||||
if(flash)S.session._flash=true;
|
||||
localStorage.setItem('hermes-webui-session',S.session.session_id);
|
||||
try{localStorage.setItem('hermes-webui-session',S.session.session_id);}catch(_){}
|
||||
_setActiveSessionUrl(S.session.session_id);
|
||||
_setSessionViewedCount(S.session.session_id, S.session.message_count || 0);
|
||||
// Sync chat-header dropdown to the session's model so the UI reflects
|
||||
|
|
@ -526,7 +526,7 @@ async function loadSession(sid){
|
|||
if(typeof syncTopbar==='function') syncTopbar();
|
||||
_setSessionViewedCount(S.session.session_id, Number(data.session.message_count || 0));
|
||||
_clearSessionCompletionUnread(S.session.session_id);
|
||||
localStorage.setItem('hermes-webui-session',S.session.session_id);
|
||||
try{localStorage.setItem('hermes-webui-session',S.session.session_id);}catch(_){}
|
||||
_setActiveSessionUrl(S.session.session_id);
|
||||
|
||||
const activeStreamId=S.session.active_stream_id||null;
|
||||
|
|
|
|||
34
tests/test_issue2386_localstorage_quota.py
Normal file
34
tests/test_issue2386_localstorage_quota.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _script(path):
|
||||
return (ROOT / path).read_text()
|
||||
|
||||
|
||||
def _assert_storage_setitem_guarded(src, needle):
|
||||
matches = [line.strip() for line in src.splitlines() if needle in line]
|
||||
assert matches, f"expected at least one {needle} write"
|
||||
for line in matches:
|
||||
assert line.startswith("try{localStorage.setItem("), (
|
||||
f"localStorage quota errors must not escape from {needle} writes: {line}"
|
||||
)
|
||||
assert "catch(_)" in line or "catch(e)" in line or "catch{}" in line
|
||||
|
||||
|
||||
def test_active_session_localstorage_writes_ignore_quota_errors():
|
||||
"""Session persistence writes are best-effort when the browser quota is full (#2386)."""
|
||||
for path in ["static/sessions.js", "static/commands.js", "static/messages.js"]:
|
||||
_assert_storage_setitem_guarded(
|
||||
_script(path),
|
||||
"localStorage.setItem('hermes-webui-session'",
|
||||
)
|
||||
|
||||
|
||||
def test_workspace_panel_localstorage_write_ignores_quota_errors():
|
||||
"""Workspace panel state should not break UI toggles if localStorage throws (#2386)."""
|
||||
_assert_storage_setitem_guarded(
|
||||
_script("static/boot.js"),
|
||||
"localStorage.setItem('hermes-webui-workspace-panel'",
|
||||
)
|
||||
Loading…
Reference in a new issue