Merge main (anchored-turn chat scrolling) into perf/switch-and-scroll

Main replaced the chat timeline scroll engine while this branch was in
flight, which obsoletes two of its subareas and reshapes a third:

- Chat timeline: main's LegendList-based MessageList/ChatContainer win;
  the activation-overscan staircase targeted the removed tanstack path
  (LegendList provides adaptive rendering natively) and is dropped along
  with its test.
- Scroll shadows: main's hook-based masks stay (the virtualized list owns
  its scroll element — there is no wrapper to hand the styling to); the
  viewport-wrapper ScrollShadow rewrite, its index.css replacement, its
  test, and the call-site viewportClassName adaptations are reverted to
  main. The chat OverlayScrollbar keeps this branch's disableHorizontal.
- OverlayScrollbar: the direct-DOM rewrite lands, but binding now follows
  the live container node instead of binding once per ref object — the
  chat scroller remounts on every session switch, and a bind-once
  contract left the scrollbar attached to a dead element.
- Markdown renderer: the detached-DOM cache and warm-block fast path
  merge with main's block-commit reveal (enter cascade), streaming code
  highlighting, and gutter reservation; the per-block reconcile keeps
  both the decoration-refresh path and the reveal cascade.
This commit is contained in:
Bohdan Triapitsyn
2026-08-26 00:59:45 +03:00
69 changed files with 3108 additions and 2677 deletions
@@ -628,7 +628,6 @@ const MobileDiffDetail: React.FC<{
<MobileChangesState icon message={t('mobile.changes.diffDetail.imageUnavailable')} />
) : (
<ScrollShadow
viewportClassName="h-full"
className="h-full overflow-y-auto overflow-x-hidden p-3"
data-diff-virtual-root
data-diff-virtual-content
+1 -1
View File
@@ -278,7 +278,7 @@ export const MobileFilesSurface: React.FC<MobileFilesSurfaceProps> = ({ onClose
</div>
</div>
<ScrollShadow viewportClassName="min-h-0 flex-1" className="min-h-0 flex-1 overflow-y-auto px-4 pb-3">
<ScrollShadow className="min-h-0 flex-1 overflow-y-auto px-4 pb-3">
{directoryError ? (
<MobileFilesState message={directoryError} />
) : query.trim() ? (
+1 -1
View File
@@ -1455,7 +1455,7 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
// clipped overflow swallowed the footer.
const surfaceContent = (
<div ref={contentRootRef} className="flex min-h-0 flex-1 flex-col">
<ScrollShadow viewportClassName="min-h-0 flex-1" className="min-h-0 flex-1 overflow-y-auto pb-4">
<ScrollShadow className="min-h-0 flex-1 overflow-y-auto pb-4">
{/* The search bar scrolls WITH the list (iOS-style): the open-time
auto-scroll to the current session naturally tucks it away, and
scrolling to the very top brings it back. */}