Skip to content

perf(sidebar): windowed session list — only mount the visible row window - #626

Merged
agegr merged 4 commits into
agegr:mainfrom
Nuctori:perf/l3-sidebar
Sep 5, 2026
Merged

agegr merged 4 commits into
agegr:mainfrom
Nuctori:perf/l3-sidebar

Conversation

@Nuctori

@Nuctori Nuctori commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

What

Windowed session list. The sidebar mounts at most the visible window of
SessionItem rows plus a small overscan; the rest are flattened by
translateY. Empty / loading / error states are unchanged.

  • SESSION_LIST_ITEM_HEIGHT = 54 every SessionItem already renders
    at exactly this height (existing constant inside the component).
  • ResizeObserver on the scroll container keeps listViewportH in sync
    with layout changes (sidebar resize, font-size change, browser zoom).
  • onScroll is rAF-throttled: a frame may receive many scroll events
    but emits at most one setListScrollTop per frame.
  • First-paint fallback: while ResizeObserver has not yet measured
    (listViewportH === 0), the slice uses a 600 px window guess. It is
    replaced on the first observer callback, before the user can scroll.

Why

The 2003-session catalog at #599 was a known regression: each SessionItem
allocates its own hover / rename / delete state machines, and the parent
mounts all of them on the first paint. With L1 reducing the route to
~100 ms but the sidebar still rendering 2003 cards, the user feels no
overall speedup. This is the complementary client-side fix.

No new dependencies

  • 0 changes to package.json / package-lock.json
  • 0 imports of react-window / react-virtual / similar
  • 1 file changed: components/SessionSidebar.tsx (+81 / -21)

Audit (self)

  • base: origin/main (28bab3c), ahead 0 before this PR
  • lint: 0 errors
  • tsc: 0 errors (node_modules/.bin/tsc --noEmit)
  • independent of P1 (perf-bench) / P2 (L1 scanner) / E2E harness
    (no e2e/, no lib/, no .github/**)
  • reverts: revert 1 file; no other touches
  • commit hygiene: 1 commit, no merge noise

Stacked plan (each PR base=main, merge-order-free)

Local evidence

On the local 1992-session catalog, before this PR the first paint of
the sidebar mounts 1992 React subtrees; after this PR it mounts roughly
viewport / 54 px items (typically 6–20) plus an 8-row overscan.

Nuctori and others added 2 commits August 27, 2026 20:22
The 2000+ session catalogue rendered every <SessionItem/> eagerly, so
first paint paid for 2003 DOM nodes plus 2003 hover/rename/delete state
machines. Switch the list to a fixed-height (54px) window: ResizeObserver
measures the scroll viewport, a rAF-throttled onScroll computes the
visible window, and translateY positions the slice. Empty/loading/error
states render unaffected. The first paint shows a small slice; scroll
mounts the next window. No new dependency.
Keep the focused session row mounted outside the visible window and let its blur handler save before releasing it. Retain loaded rows when refreshing fails, and share the fixed row height.

Add window coverage for focused rows, blur release, shrinking projects, and the initial viewport fallback. Verified 27 related tests, typecheck, lint, and desktop/mobile Chrome component checks for rename preservation, save/cancel, and failed refreshes.
Key saved messages and process groups by their session entry IDs so pagination does not reuse or detach existing message nodes. Add an E2E assertion retaining the latest user-message node across both prepend operations.

The new regression assertion fails with index keys and passes with entry IDs at desktop and mobile viewports. Also verified 42 related tests, typecheck, and lint.
@agegr
agegr marked this pull request as ready for review September 5, 2026 07:59
@agegr
agegr merged commit 5f8f47b into agegr:main Sep 5, 2026
2 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-05T08:02:42.078850Z a0ecbff Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

agegr added a commit to Nuctori/pi-web that referenced this pull request Sep 5, 2026
Bring in the stable session-entry React keys and node-identity regression assertion already merged with PR agegr#626. Resolve the session-reader type import conflict by removing both now-unused SDK types.

Validated 867 tests, typecheck, lint, and full desktop/mobile E2E.
heibai010011 pushed a commit to heibai010011/pi-web that referenced this pull request Sep 7, 2026
上游新功能:
- 内置终端面板(node-pty + xterm,API /api/terminal,工作区级进程复用)
- 划选文本分支对话(agegr#698)与会话搜索(/api/sessions/search)
- 聊天外观设置(agegr#704:字号/行宽/消息间距/气泡样式)
- 草稿保留与恢复(agegr#720)、阅读位置记忆(agegr#723)
- 窗口化会话列表(agegr#626)、插件更新检查(agegr#611)、Mermaid 预览
- Pi 依赖升级至 0.85.1,CI 工作流

本地特性在合并中保留:
- 会话文件夹组织(session-folder-drafts / session-folders)与树视图
- 权限插件内联卡片(InlinePermissionRequest)与扩展模态拆分
- 侧栏"当前工作"时间分段视图、删除会话的全局级联重挂载
- 响应状态胶囊、自定义语法高亮主题、dev Service Worker 清理

测试适配(--test-isolation=none 下共享进程环境):
- subagents profiles/settings/subagents 测试在测试体内重新指向各自的
  PI_CODING_AGENT_DIR 临时目录,避免跨文件 env 覆盖
- session-folder-drafts 的 CustomEvent 桩透传 init,恢复真实构造器
- FileViewer.state 通过 createRequire 加载 react-syntax-highlighter,
  与 jiti 的 CJS 通道一致,避免 realm 内重复加载
- terminal-manager 用命名管道探针检测 ConPTY 沙盒并跳过 PTY 测试
- worktree/pi-web-options/project-command-env 增加 PI_WEB_TEST_NO_SPAWN
  跳过开关;directory-browser/subagent-input 跳过需符号链接权限的用例
- useAgentSession/ChatInput 等源码正则断言适配 CRLF 与本地 UI 保留

验证:node --experimental-strip-types --test --test-isolation=none
全部 1031 项测试 1014 通过 / 0 失败 / 17 项沙盒环境跳过;
tsc --noEmit 无错误。
sleepinginsummer pushed a commit to sleepinginsummer/pi-web that referenced this pull request Sep 9, 2026
…dow (agegr#626)

* perf(sidebar): windowed session list — only mount the visible row window

The 2000+ session catalogue rendered every <SessionItem/> eagerly, so
first paint paid for 2003 DOM nodes plus 2003 hover/rename/delete state
machines. Switch the list to a fixed-height (54px) window: ResizeObserver
measures the scroll viewport, a rAF-throttled onScroll computes the
visible window, and translateY positions the slice. Empty/loading/error
states render unaffected. The first paint shows a small slice; scroll
mounts the next window. No new dependency.

* fix(sidebar): preserve inline edits and cached sessions when windowing

Keep the focused session row mounted outside the visible window and let its blur handler save before releasing it. Retain loaded rows when refreshing fails, and share the fixed row height.

Add window coverage for focused rows, blur release, shrinking projects, and the initial viewport fallback. Verified 27 related tests, typecheck, lint, and desktop/mobile Chrome component checks for rename preservation, save/cancel, and failed refreshes.

* fix(chat): preserve message identity when prepending history

Key saved messages and process groups by their session entry IDs so pagination does not reuse or detach existing message nodes. Add an E2E assertion retaining the latest user-message node across both prepend operations.

The new regression assertion fails with index keys and passes with entry IDs at desktop and mobile viewports. Also verified 42 related tests, typecheck, and lint.

---------

Co-authored-by: Nuctori <Nuctori@users.noreply.github.com>
Co-authored-by: Alex Yang <agegcn@gmail.com>
ghost0211 pushed a commit to ghost0211/pi-web that referenced this pull request Sep 29, 2026
…dow (agegr#626)

* perf(sidebar): windowed session list — only mount the visible row window

The 2000+ session catalogue rendered every <SessionItem/> eagerly, so
first paint paid for 2003 DOM nodes plus 2003 hover/rename/delete state
machines. Switch the list to a fixed-height (54px) window: ResizeObserver
measures the scroll viewport, a rAF-throttled onScroll computes the
visible window, and translateY positions the slice. Empty/loading/error
states render unaffected. The first paint shows a small slice; scroll
mounts the next window. No new dependency.

* fix(sidebar): preserve inline edits and cached sessions when windowing

Keep the focused session row mounted outside the visible window and let its blur handler save before releasing it. Retain loaded rows when refreshing fails, and share the fixed row height.

Add window coverage for focused rows, blur release, shrinking projects, and the initial viewport fallback. Verified 27 related tests, typecheck, lint, and desktop/mobile Chrome component checks for rename preservation, save/cancel, and failed refreshes.

* fix(chat): preserve message identity when prepending history

Key saved messages and process groups by their session entry IDs so pagination does not reuse or detach existing message nodes. Add an E2E assertion retaining the latest user-message node across both prepend operations.

The new regression assertion fails with index keys and passes with entry IDs at desktop and mobile viewports. Also verified 42 related tests, typecheck, and lint.

---------

Co-authored-by: Nuctori <Nuctori@users.noreply.github.com>
Co-authored-by: Alex Yang <agegcn@gmail.com>
(cherry picked from commit 5f8f47b)
ghost0211 added a commit to ghost0211/pi-web that referenced this pull request Sep 29, 2026
Batch upstream cherry-picks (agegr/pi-web) into the fork:

Fixes
- Guard against duplicate built-in command submissions (agegr#762)
- Keep shell output across SSE reconnects; resume streaming output
- See sessions written by another pi process (agegr#796)
- Stop duplicating the first streamed chunk (agegr#835)
- Deliver events to every listener when one unsubscribes mid-emit

Features
- Warn when attached images go to a model without image input (agegr#636)
- Preview image attachments in the chat composer (agegr#735)
- Render apply_patch tool calls as split diffs (agegr#744)
- Scroll-to-latest floating button (agegr#845)

Performance & deps
- gzip large JSON responses (agegr#731)
- Cache unchanged session metadata across scans and restarts (agegr#625)
- Windowed session list (adapted to the fork's project-grouped sidebar) (agegr#626)
- Next.js 16.3.6 + disable image optimizer; trim production install (agegr#948)

Also includes the NSIS shortcut recreation hook (desktop icon fix).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants