Skip to content

Commit 783a0a7

Browse files
gergeshclaude
andcommitted
Add mobile message navigation in header
Add a dropdown button next to the terminal icon that shows current message position (e.g. "2/5") and allows jumping between messages. Only visible on mobile when there are multiple messages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c4b5a4b commit 783a0a7

3 files changed

Lines changed: 79 additions & 1 deletion

File tree

packages/desktop/src/context/layout.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,17 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
7777
filesCount?: number
7878
onOpen?: () => void
7979
}
80+
mobileMessageNav: {
81+
visible?: boolean
82+
messages?: { id: string; title?: string }[]
83+
currentIndex?: number
84+
onSelect?: (index: number) => void
85+
}
8086
}>({
8187
connect: {},
8288
dialog: {},
8389
mobileReview: {},
90+
mobileMessageNav: {},
8491
})
8592
const usedColors = new Set<AvatarColorKey>()
8693

@@ -280,6 +287,21 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
280287
setEphemeral("mobileReview", { visible: false, filesCount: 0, onOpen: undefined })
281288
},
282289
},
290+
mobileMessageNav: {
291+
visible: createMemo(() => ephemeral.mobileMessageNav?.visible ?? false),
292+
messages: createMemo(() => ephemeral.mobileMessageNav?.messages ?? []),
293+
currentIndex: createMemo(() => ephemeral.mobileMessageNav?.currentIndex ?? 0),
294+
onSelect: createMemo(() => ephemeral.mobileMessageNav?.onSelect),
295+
register(messages: { id: string; title?: string }[], currentIndex: number, onSelect: (index: number) => void) {
296+
setEphemeral("mobileMessageNav", { visible: true, messages, currentIndex, onSelect })
297+
},
298+
update(currentIndex: number) {
299+
setEphemeral("mobileMessageNav", "currentIndex", currentIndex)
300+
},
301+
unregister() {
302+
setEphemeral("mobileMessageNav", { visible: false, messages: [], currentIndex: 0, onSelect: undefined })
303+
},
304+
},
283305
theme: {
284306
current: createMemo(() => store.theme),
285307
set(themeId: string) {

packages/desktop/src/pages/layout.tsx

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,44 @@ export default function Layout(props: ParentProps) {
857857
<FontPicker />
858858
<ThemePicker />
859859
</div>
860-
<div class="flex items-center gap-4">
860+
<div class="flex items-center gap-2">
861+
{/* Mobile message navigation */}
862+
<Show when={layout.mobileMessageNav.visible()}>
863+
<div class="sm:hidden">
864+
<DropdownMenu>
865+
<DropdownMenu.Trigger
866+
as={Button}
867+
variant="ghost"
868+
size="small"
869+
class="gap-1 px-2"
870+
>
871+
<span class="text-12-medium">
872+
{layout.mobileMessageNav.currentIndex() + 1}/{layout.mobileMessageNav.messages().length}
873+
</span>
874+
<Icon name="chevron-down" size="small" />
875+
</DropdownMenu.Trigger>
876+
<DropdownMenu.Portal>
877+
<DropdownMenu.Content class="max-w-[calc(100vw-2rem)] max-h-80 overflow-y-auto">
878+
<For each={layout.mobileMessageNav.messages()}>
879+
{(msg, index) => (
880+
<DropdownMenu.Item onSelect={() => layout.mobileMessageNav.onSelect()?.(index())}>
881+
<DropdownMenu.ItemLabel class="flex items-center gap-3">
882+
<span class="text-text-weak shrink-0">{index() + 1}</span>
883+
<span class="truncate">{msg.title || "Message"}</span>
884+
</DropdownMenu.ItemLabel>
885+
<Show when={index() === layout.mobileMessageNav.currentIndex()}>
886+
<DropdownMenu.ItemIndicator>
887+
<Icon name="check-small" size="small" />
888+
</DropdownMenu.ItemIndicator>
889+
</Show>
890+
</DropdownMenu.Item>
891+
)}
892+
</For>
893+
</DropdownMenu.Content>
894+
</DropdownMenu.Portal>
895+
</DropdownMenu>
896+
</div>
897+
</Show>
861898
<Tooltip
862899
class="shrink-0"
863900
value={

packages/desktop/src/pages/session.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,25 @@ export default function Page() {
153153
layout.mobileReview.unregister()
154154
})
155155

156+
// Register mobile message navigation in header when there are multiple messages
157+
createEffect(() => {
158+
const messages = visibleUserMessages()
159+
if (messages.length > 1) {
160+
const currentIndex = messages.findIndex((m) => m.id === activeMessage()?.id)
161+
layout.mobileMessageNav.register(
162+
messages.map((m) => ({ id: m.id, title: m.summary?.title })),
163+
currentIndex >= 0 ? currentIndex : 0,
164+
(index) => setActiveMessage(messages[index])
165+
)
166+
} else {
167+
layout.mobileMessageNav.unregister()
168+
}
169+
})
170+
171+
onCleanup(() => {
172+
layout.mobileMessageNav.unregister()
173+
})
174+
156175
createEffect(() => {
157176
if (layout.terminal.opened()) {
158177
if (terminal.all().length === 0) {

0 commit comments

Comments
 (0)