Files
supabase/apps/studio/components/layouts/ExplorerLayout/ExplorerNavChats.tsx
Saxon Fletcher fd8d213135 feat: add Explorer chat discovery and controls (#49032)
<img width="1693" height="1038" alt="image"
src="https://github.com/user-attachments/assets/9ef52446-c517-4ea9-ada6-ac6dc57c8af4"
/>


## Summary

- add an Explorer-specific chat toolbar with rename, chat ID,
permission, and branching controls
- list searchable non-support chats in Explorer with reactive updates
and safe rehydrated-date sorting
- add chat creation entry points to Explorer home and navigation menus
- route chat recent items correctly and show chat icons across shared
tab surfaces
- make the assistant sidebar expand action open the active chat in
Explorer

This is PR 3 of 3 and is stacked on #49031. Review #48973 first, then
#49031, then this PR. Compared with its base, this PR contains only
discovery, toolbar, and cross-surface integration work.

## To Test
- visit /explorer
- Create a new chat either via home tab chat input OR the chats sidebar
- Validate chats show up in sidebar
- Validate chat conversation works
- Close the chat tab and open a chat via sidebar
- Change permission settings via the chat tab toolbar and verify it
persists


## Test plan

- `mise exec node@22 -- pnpm --dir apps/studio exec tsc --noEmit`
- focused Vitest suite: 4 files / 5 tests covering reactive chat lists,
navigation filtering and sorting, recent-item routing, and sidebar
handoff
- ESLint on changed TypeScript files
- Prettier check on changed source files


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added chat creation from the Explorer home, navigation, and new-tab
menu.
- Added an Explorer chat toolbar with editable names, ID copying,
permissions, shortcuts, and metadata warnings.
- Added searchable chat history with sorting, active-chat highlighting,
and clear empty states.
  - Chats can now open directly in Explorer from the AI Assistant panel.
  - Recent items now link correctly to chats and notebooks.

- **Bug Fixes**
- Improved chat list updates after creation, deletion, and restored
sessions.
  - Added safer handling for chats without update timestamps.

- **Tests**
- Expanded coverage for chat navigation, creation, metadata warnings,
shortcuts, and recent-item links.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:19:43 +10:00

62 lines
2.2 KiB
TypeScript

import { useParams } from 'common'
import { MessageSquare } from 'lucide-react'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { cn } from 'ui'
import { ExplorerNavResourceWrapper, rowClassName } from './ExplorerLayout.constants'
import { useCreateChat } from '@/components/interfaces/Explorer/hooks'
import type { ChatSession } from '@/state/ai-assistant-state'
import { useAiAssistantChatList } from '@/state/ai-assistant-state'
const getVisibleChats = (chats: ChatSession[], search: string): ChatSession[] => {
const normalizedSearch = search.trim().toLowerCase()
return chats
.filter((chat) => !chat.supportMetadata?.isSupportChat)
.filter((chat) => !normalizedSearch || chat.name.toLowerCase().includes(normalizedSearch))
.sort((a, b) => (b.updatedAt?.getTime() ?? 0) - (a.updatedAt?.getTime() ?? 0))
}
export const ExplorerNavChats = ({ onBack }: { onBack: () => void }) => {
const [search, setSearch] = useState('')
const router = useRouter()
const { id } = useParams()
const { openChat } = useCreateChat()
const chatList = useAiAssistantChatList()
const chats = getVisibleChats(chatList, search)
return (
<ExplorerNavResourceWrapper type="chat" search={search} setSearch={setSearch} onBack={onBack}>
<div className="flex flex-1 flex-col gap-px overflow-y-auto px-3 pb-3">
{chats.length === 0 ? (
<p className="px-2 py-2 text-xs text-foreground-lighter">
{search ? 'No chats found' : 'No chats created yet'}
</p>
) : (
chats.map((chat) => {
const isActive = router.pathname.includes('/explorer/chat/') && id === chat.id
return (
<button
key={chat.id}
type="button"
tabIndex={0}
className={rowClassName(isActive)}
onClick={() => openChat(chat.id)}
>
<MessageSquare
size={14}
className={cn('shrink-0', isActive && 'text-foreground')}
/>
<span className="truncate text-left">{chat.name}</span>
</button>
)
})
)}
</div>
</ExplorerNavResourceWrapper>
)
}