fix(chat): resync sidebar after panel switch
This commit is contained in:
parent
5abd142f34
commit
f81d24ec4c
2 changed files with 84 additions and 0 deletions
|
|
@ -182,6 +182,18 @@ function _consumeSettingsTargetPanel(fallback = 'chat') {
|
|||
return target;
|
||||
}
|
||||
|
||||
function _resyncChatSidebarAfterPanelSwitch() {
|
||||
if (_currentPanel !== 'chat') return;
|
||||
if (typeof renderSessionListFromCache !== 'function') return;
|
||||
const run = () => {
|
||||
if (_currentPanel !== 'chat') return;
|
||||
if (typeof _renamingSid !== 'undefined' && _renamingSid) return;
|
||||
renderSessionListFromCache();
|
||||
};
|
||||
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(run);
|
||||
else run();
|
||||
}
|
||||
|
||||
async function switchPanel(name, opts = {}) {
|
||||
const nextPanel = name || 'chat';
|
||||
const prevPanel = _currentPanel;
|
||||
|
|
@ -251,6 +263,7 @@ async function switchPanel(name, opts = {}) {
|
|||
if (sidebar) sidebar.classList.add('mobile-open');
|
||||
if (overlay) overlay.classList.add('visible');
|
||||
}
|
||||
_resyncChatSidebarAfterPanelSwitch();
|
||||
syncAppTitlebar();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
71
tests/test_chat_panel_sidebar_resync.py
Normal file
71
tests/test_chat_panel_sidebar_resync.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
"""Regression coverage for chat sidebar virtualization after panel navigation."""
|
||||
from pathlib import Path
|
||||
|
||||
PANELS_JS = Path(__file__).parent.parent / "static" / "panels.js"
|
||||
|
||||
|
||||
def _read_source() -> str:
|
||||
return PANELS_JS.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _function_block(src: str, name: str) -> str:
|
||||
marker = f"function {name}"
|
||||
start = src.find(marker)
|
||||
if start == -1:
|
||||
marker = f"async function {name}"
|
||||
start = src.find(marker)
|
||||
assert start != -1, f"{name} not found"
|
||||
paren = src.find("(", start)
|
||||
assert paren != -1, f"{name} signature not found"
|
||||
paren_depth = 1
|
||||
j = paren + 1
|
||||
while j < len(src) and paren_depth:
|
||||
if src[j] == "(":
|
||||
paren_depth += 1
|
||||
elif src[j] == ")":
|
||||
paren_depth -= 1
|
||||
j += 1
|
||||
assert paren_depth == 0, f"{name} signature did not close"
|
||||
brace = src.find("{", j)
|
||||
assert brace != -1, f"{name} body not found"
|
||||
depth = 1
|
||||
i = brace + 1
|
||||
while i < len(src) and depth:
|
||||
if src[i] == "{":
|
||||
depth += 1
|
||||
elif src[i] == "}":
|
||||
depth -= 1
|
||||
i += 1
|
||||
assert depth == 0, f"{name} body did not close"
|
||||
return src[start:i]
|
||||
|
||||
|
||||
def test_switching_back_to_chat_schedules_sidebar_resync_after_panel_is_visible():
|
||||
"""Chat navigation must re-render the sidebar after layout becomes visible.
|
||||
|
||||
The sidebar session list is virtualized. After switching away to Settings,
|
||||
Logs, etc. and then back to Chat, the browser can clamp the preserved
|
||||
scrollTop during the layout transition. If no render runs after the chat
|
||||
view is visible again, stale virtual spacer/header DOM can remain until the
|
||||
next manual scroll event.
|
||||
"""
|
||||
src = _read_source()
|
||||
switch_body = _function_block(src, "switchPanel")
|
||||
|
||||
assert "_resyncChatSidebarAfterPanelSwitch();" in switch_body
|
||||
assert switch_body.index("if (panelEl) panelEl.classList.add('active');") < switch_body.index(
|
||||
"_resyncChatSidebarAfterPanelSwitch();"
|
||||
)
|
||||
|
||||
|
||||
def test_chat_sidebar_resync_helper_is_guarded_and_bounded_to_one_animation_frame():
|
||||
"""The resync must not poll or destroy rename state; one guarded rAF is enough."""
|
||||
src = _read_source()
|
||||
helper = _function_block(src, "_resyncChatSidebarAfterPanelSwitch")
|
||||
|
||||
assert "if (_currentPanel !== 'chat') return;" in helper
|
||||
assert "typeof renderSessionListFromCache !== 'function'" in helper
|
||||
assert "requestAnimationFrame" in helper
|
||||
assert "setInterval" not in helper
|
||||
assert "setTimeout" not in helper
|
||||
assert "renderSessionListFromCache();" in helper
|
||||
Loading…
Reference in a new issue