fix(native-chat): show the message rail from the first user message (#25707)

* fix(native-chat): show the message rail from the first user message

The rail on the right of native chat stayed hidden until a conversation had
three user messages, so short chats had no rail at all. Show it whenever there
is at least one user message (still hidden in panes too narrow for it).

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(test): remove duplicate journal fixture handle import

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Brennan Benson
2026-10-05 19:02:40 -07:00
committed by GitHub
co-authored by Claude
parent a47e0f5657
commit 08a970a3f1
3 changed files with 9 additions and 9 deletions
@@ -13,9 +13,6 @@ import type { NativeChatTranscriptSlot } from './native-chat-transcript-slots'
* read at a glance, which is the only thing the rail is for. */
export const NATIVE_CHAT_RAIL_MAX_TICKS = 20
/** Below this a rail is noise — two ticks say nothing a scrollbar doesn't. */
export const NATIVE_CHAT_RAIL_MIN_ITEMS = 3
export type NativeChatRailItem = {
id: string
/** Index into the slot list, i.e. the virtualizer's own index. Null while the
@@ -114,7 +114,7 @@ describe('message rail hook', () => {
expect(scrollSubscriptions).toHaveLength(1)
})
it('ticks every user message and hides below the minimum', () => {
it('ticks every user message and shows from the first one', () => {
const element = document.createElement('div')
const scrollRef = { current: element }
@@ -124,14 +124,19 @@ describe('message rail hook', () => {
expect(result.current.items.map((item) => item.id)).toEqual(['u1', 'u2', 'u3'])
expect(result.current.visible).toBe(true)
const { result: short } = renderHook(() =>
const { result: single } = renderHook(() =>
useNativeChatMessageRail({
scrollRef,
slots: slotsOf([message('u1', 'user'), message('a1', 'assistant')]),
virtualItems: []
})
)
expect(short.current.visible).toBe(false)
expect(single.current.visible).toBe(true)
const { result: empty } = renderHook(() =>
useNativeChatMessageRail({ scrollRef, slots: [], virtualItems: [] })
)
expect(empty.current.visible).toBe(false)
})
it('maps user messages above the loaded window from the outline, before the loaded ones', () => {
@@ -145,7 +150,6 @@ describe('message rail hook', () => {
const { result } = renderHook(() =>
useNativeChatMessageRail({ scrollRef, slots: slotsOf(loaded), virtualItems: [], outline })
)
// One loaded prompt alone would hide the rail; the outline is what makes it a map.
expect(result.current.visible).toBe(true)
expect(result.current.items.map((item) => item.id)).toEqual([
...outline.map((entry) => entry.id),
@@ -11,7 +11,6 @@ import {
buildNativeChatRailItems,
mergeNativeChatRailOutline,
selectNativeChatRailTicks,
NATIVE_CHAT_RAIL_MIN_ITEMS,
type NativeChatRailItem,
type NativeChatRailOutlineEntry
} from './native-chat-message-rail-items'
@@ -135,7 +134,7 @@ export function useNativeChatMessageRail({
ticks,
items,
activeId,
visible: wideEnough && items.length >= NATIVE_CHAT_RAIL_MIN_ITEMS
visible: wideEnough && items.length > 0
}),
[ticks, items, activeId, wideEnough]
)