feat(desktop): count-free progress indicator + infinite-scroll stream list

Follow-up to windowed particle pagination: adapt the stream UI now that the
loaded set is a window rather than the whole stream.

Progress indicator: drop the absolute "{n} / {total}" counter (the true count
is unknown when paginated) in favour of a streaming, count-free scrubber. It
renders a sliding window of segments around the current position (newest on
the right); the edge stubs page through the loaded window and pull in older
history via onLoadOlder when scrubbing past the oldest loaded segment.

Stream list sidebar: auto-fetch older particles via an IntersectionObserver
sentinel as the user scrolls toward the top, with viewport scroll-anchoring so
prepended history doesn't jolt the view. Auto-scroll-to-current is now gated on
genuine selection changes (not index shifts from prepends). Header count gains
a "+" while more history exists, and a spinner shows while paging.

ScrollArea gains an optional `viewportRef` to expose the scroll viewport for
anchoring and observers.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01V8gsmdVd7R8PJtn4UFnC4J
This commit is contained in:
Claude
2026-06-21 02:22:04 +00:00
parent af69f98583
commit 362a22bbc6
5 changed files with 148 additions and 38 deletions
+6 -1
View File
@@ -6,8 +6,12 @@ import { cn } from '@/lib/utils';
function ScrollArea({
className,
children,
viewportRef,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root> & {
/** Ref to the scrollable viewport, e.g. for scroll anchoring or observers. */
viewportRef?: React.Ref<HTMLDivElement>;
}) {
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
@@ -15,6 +19,7 @@ function ScrollArea({
{...props}
>
<ScrollAreaPrimitive.Viewport
ref={viewportRef}
data-slot="scroll-area-viewport"
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&>div]:!w-full"
>