Skip to content

feat(ui): add LRU thread views and on-demand rendering - #136

Open
shallinta wants to merge 3 commits into
deer-flow:mainfrom
shallinta:feat/thread-view-lru-on-demand-rendering
Open

feat(ui): add LRU thread views and on-demand rendering#136
shallinta wants to merge 3 commits into
deer-flow:mainfrom
shallinta:feat/thread-view-lru-on-demand-rendering

Conversation

@shallinta

@shallinta shallinta commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR is now rebased onto the latest main and integrated with the View-retention work from #134. The duplicate LRU implementation has been removed; the remaining changes extend the shared RuntimePaneHost and message renderer with configurable performance controls.

  • Add an On Demand message-rendering mode that keeps Lezer-highlighted static previews while idle and mounts CodeMirror only after the user focuses an editable message.
  • Add independent message virtualization controls: Off, Auto, Custom, and On.
  • Make the shared Thread/Trace View cache configurable from 1–10 Views (default: 3), using the retention mechanism already provided by perf: reduce thread workspace memory retention #134.
  • Make the ordinary Tools Add and System Prompt Examples dropdowns non-modal while preserving keyboard navigation, dismissal, focus restoration, and menu-to-dialog handoff.
  • Reduce message-navigator scroll work and reliably restore the saved message anchor after an LRU-evicted View is remounted.

Relationship to #134

#134 already supplies the shared RuntimePaneHost that keeps only recent pane Views mounted while their longer-lived Sessions continue running. This PR now reuses that implementation rather than maintaining a parallel Thread-specific LRU.

Capabilities that remain specific to this PR:

  • configurable Thread/Trace View-cache size;
  • On Demand static-highlight rendering;
  • configurable and memory-aware message virtualization;
  • optimized navigator-anchor tracking for virtualized and non-virtualized lists;
  • View-local scroll-anchor capture and restoration;
  • non-modal Tools Add and Examples dropdowns.

User-facing changes

The related settings now live under Settings → General → Performance.

Message rendering

  • Full keeps every message CodeMirror mounted.
  • On Demand keeps syntax highlighting while idle, then activates the full editor after focus. The first click focuses/activates editing, so placing the caret precisely can require a second click.
  • Fast uses plain text for the lowest editor overhead.

Full and On Demand share the same visual enhancement definitions. Static previews render Markdown, JSON, XML, prompt-variable, and template-expression colors from the existing CodeMirror theme, while hover, inspection, selection, and autocomplete remain CodeMirror-only behaviors after activation.

Virtualization

  • Off renders the complete message list and preserves uninterrupted fast scrolling.
  • Auto selects a fixed threshold from total physical memory and the active rendering mode.
  • Custom lets the user enter a message-count threshold (default: 20).
  • On virtualizes every non-empty message list.

Auto uses the following Full baselines and applies the rendering multiplier, clamped to 10–200 messages:

Physical memory Full On Demand (×1.5) Fast (×2)
up to 8 GiB 10 15 20
9–16 GiB 15 23 30
17–32 GiB 25 38 50
above 32 GiB 30 45 60

The threshold is calculated at startup and recalculated when the user switches back to Auto. Message rendering and virtualization remain orthogonal, so users can keep Full or On Demand without virtualization until their conversation size requires it.

View cache and lifecycle

View cache controls the maximum recently used Thread and Trace Views kept mounted, including the active View. The default is 3.

Evicting a View does not evict its Session: background streaming, tool execution, persistence, run status, and undo history continue without an attached View. Editor drafts are committed before eviction, and the existing rule that prevents closing a running tab remains unchanged.

The scroll snapshot stores a stable message ID rather than an index. On remount, the latest index is resolved from that ID and aligned to the viewport end. Restoration now retries across layout frames and is consumed only after two stable frames, covering both Radix ScrollArea initialization and asynchronous virtualizer measurement.

Implementation notes

  • Extends perf: reduce thread workspace memory retention #134's shared pane-retention host with the persisted View-cache setting; removes the superseded Thread-only LRU/cache helpers.
  • Adds a shared virtualization provider and a pure policy for Off/Auto/Custom/On decisions.
  • Reads total physical memory through a small desktop RPC without collecting or persisting hardware identifiers.
  • Uses TanStack Virtual for long message lists while retaining message IDs as stable keys.
  • Replaces non-virtual navigator full-row measurement on every scroll frame with center-point hit testing, capped at one progressive update every two frames plus one exact reconciliation after scrolling settles.
  • Keeps virtualized navigation on the virtualizer's existing measurements.
  • Explicitly clears direct virtualizer layout styles when switching Virtualization from On to Off.
  • Preserves pointer and keyboard menu behavior after setting the two ordinary dropdowns to modal={false}.

Performance measurements

The included CEF/CDP benchmark uses isolated generated workspaces. Full methodology and raw-result references are documented in docs/performance/thread-view-performance-2026-08.md.

  • With three retained Views, Full retained DOM decreased from 49,094 to 15,199 nodes and CodeMirror instances from 550 to 165 in the original retained-resource comparison.
  • Idle On Demand retained 162 static message previews and only the three unchanged System Prompt CodeMirror instances; focusing one message temporarily added exactly one CodeMirror.
  • In the 32-GiB calibration, the common complete-list scroll knee appeared between 25 and 30 messages, leading to Auto thresholds of Full 25, On Demand 38, and Fast 50.
  • A 40-message Full Auto sample retained 1,776 DOM nodes, 12 CodeMirror instances, and 9 mounted message rows, versus 11,386 / 123 / 120 with Virtualization Off.
  • The non-virtual navigator smoke recorded zero full-row geometry reads during 20 scroll frames and one exact 40-row scan after settling.

The benchmark intentionally treats virtualization as a retained-DOM/remount optimization rather than a guarantee of smoother large-distance scrolling; this is why Auto is the default and Off remains available.

Validation

  • Focused lifecycle, scroll-restoration, navigator, and policy tests: 37 passed, 0 failed.
  • bun run lint:changed — 30 changed source files passed.
  • bun run typecheck:changed — 29 changed source files passed.
  • git diff --check.
  • Real CEF LRU eviction/remount smoke:
    • Full + Virtualization Off: restored the same message with 0px end-alignment error.
    • Full + Virtualization On: restored the same message with 1.75px end-alignment error.
    • On Demand + Off and Fast + Off: both restored with 0px error.
  • Existing CEF interaction smoke covers pointer/keyboard menu opening, arrow navigation, Escape and outside dismissal, trigger-focus restoration, and menu-to-dialog handoff.

@foreleven

Copy link
Copy Markdown
Collaborator

The core thread-view retention optimization is already supported on main via #134. However, the On Demand rendering mode and configurable view cache in this PR are not, so this is only partially superseded.

@foreleven

Copy link
Copy Markdown
Collaborator

Could you please check the merge conflicts and assess whether you still want to submit the remaining capabilities, such as On Demand rendering and the configurable view cache? If so, please rebase on the latest main and keep the changes scoped to what is not already supported.

@shallinta
shallinta force-pushed the feat/thread-view-lru-on-demand-rendering branch from 294ea01 to d391c62 Compare August 18, 2026 19:27
@shallinta

Copy link
Copy Markdown
Contributor Author

Thanks for pointing this out. I rebased the branch onto the latest main and reconciled it with #134.

The duplicate Thread-specific LRU/cache implementation has been removed. The configurable View cache now drives #134's shared RuntimePaneHost for both Thread and Trace Views, while Sessions remain alive for background streaming and persistence.

The non-overlapping capabilities are retained:

  • On Demand static-highlight rendering with focus-to-edit CodeMirror activation;
  • independent Virtualization Off / Auto / Custom / On controls;
  • memory-tiered Auto thresholds adjusted by the active rendering mode;
  • non-modal Tools Add and Examples dropdowns with the existing keyboard/focus behavior preserved;
  • optimized message-navigator tracking for virtualized and non-virtualized lists;
  • message-ID-based scroll restoration after an LRU eviction/remount.

I also fixed a remount race found by the new CEF smoke: Radix ScrollArea/virtualizer initialization could reset an initially restored position to the top. Restoration now retries across frames and is consumed only after two stable frames, with a bounded retry window.

The updated branch is at d391c62. I also refreshed the PR description with the merged scope, settings, #134 relationship, benchmark results, and current validation. CI is running on the new head.

@shallinta

Copy link
Copy Markdown
Contributor Author

@foreleven need review again, please

@shallinta
shallinta force-pushed the feat/thread-view-lru-on-demand-rendering branch from d391c62 to 9b94f59 Compare August 24, 2026 08:49
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