From 0f11673a74e36b5bbd0b3c89324403ac24cd2f41 Mon Sep 17 00:00:00 2001 From: Jake Howell Date: Mon, 13 Jul 2026 10:38:55 +1000 Subject: [PATCH] fix(site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline): hide "Session completed" until every thread has loaded (#26955) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > 🤖 This PR was written by Coder Agents on behalf of Jake Howell. The "Session completed" marker at the bottom of an AI Gateway session timeline was rendered unconditionally, so on long sessions it appeared below still-loading threads while the user scrolled. That is misleading: users read it as the end of the session even when more threads are about to stream in. Only render the session end marker (rows 7 and 8 of the grid: the connecting vertical line, the success dot, and the "Session completed" text) once every thread has loaded, that is, once both `hasNextPage` and `isFetchingNextPage` are false. The dashed timeline box still closes cleanly at the bottom, and the infinite-scroll spinner keeps rendering inside row 5 while more pages fetch. | Old | New | | --- | --- | | preview-old-behaviour | preview-new-heaviour | ## Verification Rendered each SessionTimeline story via a headless Chromium and asserted whether "Session completed" is present: | Story | `hasNextPage` | `isFetchingNextPage` | "Session completed" | | --- | --- | --- | --- | | OneThread | false | false | visible | | MultipleThreads | false | false | visible | | FetchingNextPage | true | true | hidden | | HasMoreThreadsToLoad (new) | true | false | hidden | All checks pass locally: - `pnpm format` (no changes) - `pnpm lint:check` - `pnpm lint:types` - `make pre-commit` via githooks
Implementation notes - `site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.tsx`: wrap the row 7 spacer and row 8 status dot/text in `!hasNextPage && !isFetchingNextPage`. - `SessionTimeline.stories.tsx`: add `HasMoreThreadsToLoad` to cover the between-fetches state. - No prop signature or public API change; `SessionTimelineSkeleton.tsx` is untouched because the skeleton is only shown before any threads have loaded.
--- .../SessionTimeline.stories.tsx | 10 +++++ .../SessionTimeline/SessionTimeline.tsx | 40 +++++++++++-------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.stories.tsx b/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.stories.tsx index 7700797bb2..7beb04b4c6 100644 --- a/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.stories.tsx +++ b/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.stories.tsx @@ -142,3 +142,13 @@ export const MultipleThreads: Story = { export const FetchingNextPage: Story = { args: { hasNextPage: true, isFetchingNextPage: true }, }; + +// The "Session completed" marker should stay hidden while more threads +// remain to be loaded so it cannot be misread as the end of the session. +export const HasMoreThreadsToLoad: Story = { + args: { + threads: [mockThread, mockThreadLong], + hasNextPage: true, + isFetchingNextPage: false, + }, +}; diff --git a/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.tsx b/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.tsx index 00fda27fea..7154e1ca7a 100644 --- a/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.tsx +++ b/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/SessionTimeline.tsx @@ -563,23 +563,31 @@ export const SessionTimeline: FC = ({ {/* bottom right rounded corner */} - {/* row 7: sized intentionally to create the visual space below the timeline border */} -
- {/* vertical line */} -
+ {/* rows 7-8: session end marker. Only rendered once every thread + has loaded so "Session completed" cannot appear below + still-loading threads and be mistaken for the end of the + session. */} + {!hasNextPage && !isFetchingNextPage && ( + <> + {/* row 7: sized intentionally to create the visual space below the timeline border */} +
+ {/* vertical line */} +
- {/* row 8: session completed */} -
- -
-
- - Session completed - -
+ {/* row 8: session completed */} +
+ +
+
+ + Session completed + +
+ + )} );