Chat Sidebar

La barre latérale d’une application de chat. Logo, recherche et Nouveau chat en haut, vos propres liens en dessous, chats épinglés, projets qui se déploient pour montrer leurs chats, récents regroupés par jour, et des lignes avec menus au survol et au clic droit, renommage en ligne, suppression avec annulation et états de réponse en direct.

ChatGPT, Claude, Perplexity et Grok ont tous adopté la même barre latérale : démarrer un chat ou chercher depuis le haut, garder quelques chats épinglés, regrouper le travail en projets et remonter tout le reste jour par jour. Chat Sidebar est cette barre latérale, construite sur la variante inset de la Sidebar de HextaUI, de sorte que la conversation occupe son propre panneau arrondi dans le cadre. Elle se réduit en rail d’icônes sur ordinateur, et sur mobile elle glisse pour remplir l’écran puis disparaît dès que vous choisissez un chat.

L’en-tête contient votre logo, un bouton de recherche et le bouton de réduction. New chat se trouve en dessous avec son raccourci toujours visible, suivi des liens que vous passez, comme Images, Library ou Plugins. Les projets sont des dossiers repliables, chacun avec sa propre icône, et leurs chats y sont indentés plutôt que dans l’historique. Les récents se regroupent en Today, Yesterday, Previous 7 days, Previous 30 days puis par mois, et s’affichent par pages au défilement, donc des milliers de chats s’ouvrent instantanément.

Chaque ligne a un menu au survol et le même menu au clic droit : épingler, renommer, déplacer vers un projet et supprimer. Le renommage se fait sur place par double-clic ou F2. La suppression masque aussitôt le chat et propose Undo pendant cinq secondes avant d’appeler onDelete. Un chat en cours de réponse affiche un spinner et celui qui a terminé ailleurs affiche un point ; les deux s’effacent au profit du bouton de menu au survol. Le menu du compte s’ouvre vers le haut avec votre nom et votre e-mail au-dessus des items que vous lui passez.

  1. Ajouter le registre Pro à components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Ajouter votre token

    Créez un token sur votre page de compte et placez-le dans .env.local sous le nom HEXTAUI_PRO_TOKEN.

  3. Ajouter le block

    pnpm dlx shadcn@latest add @hextaui-pro/chat-sidebar

Branchez-le à votre état

Passez les chats et les handlers que vous prenez en charge. Chaque item de menu n’apparaît que si son handler est défini, donc un historique en lecture seule n’a besoin que de onSelect et onNewChat.

"use client"

import * as React from "react"

import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"

import { ChatSidebar } from "@/components/blocks/chat-sidebar/chat-sidebar"
import type { SidebarChat } from "@/components/blocks/chat-sidebar/chats"

export function App({ initialChats }: { initialChats: SidebarChat[] }) {
  const [chats, setChats] = React.useState(initialChats)
  const [activeId, setActiveId] = React.useState<string | null>(null)

  const update = (id: string, change: Partial<SidebarChat>) =>
    setChats((current) => current.map((chat) => (chat.id === id ? { ...chat, ...change } : chat)))

  return (
    <SidebarProvider>
      <ChatSidebar
        chats={chats}
        activeId={activeId}
        onSelect={setActiveId}
        onNewChat={() => setActiveId(null)}
        onRename={(id, title) => update(id, { title })}
        onPinnedChange={(id, pinned) => update(id, { pinned })}
        onDelete={(id) => setChats((current) => current.filter((chat) => chat.id !== id))}
        user={{ name: "Mia Chen", plan: "Pro plan" }}
      />
      <SidebarInset />
    </SidebarProvider>
  )
}

Projets et routage

Chaque projet est un dossier repliable avec sa propre icône. Ses chats y sont indentés au lieu de figurer dans l’historique daté. onMove ajoute Move to project à chaque ligne.

"use client"

import { useRouter } from "next/navigation"

import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"

import { ChatSidebar } from "@/components/blocks/chat-sidebar/chat-sidebar"
import type { SidebarChat, SidebarProject } from "@/components/blocks/chat-sidebar/chats"

export function Workspace({
  chats,
  projects,
  activeId,
  moveChat,
  children,
}: {
  chats: SidebarChat[]
  projects: SidebarProject[]
  activeId: string | null
  moveChat: (id: string, projectId: string | null) => void
  children: React.ReactNode
}) {
  const router = useRouter()

  return (
    <SidebarProvider>
      <ChatSidebar
        chats={chats}
        projects={projects}
        activeId={activeId}
        onSelect={(id) => router.push(`/c/${id}`)}
        onNewChat={() => router.push("/")}
        defaultOpenProjects={projects.slice(0, 1).map((project) => project.id)}
        onMove={moveChat}
      />
      <SidebarInset>{children}</SidebarInset>
    </SidebarProvider>
  )
}

Anatomie

Les parties à composer, de l’extérieur vers l’intérieur.

PartieDescription
ChatSidebarToute la barre latérale : en-tête, New chat et liens, chats épinglés, projets, historique daté et menu du compte.
groupChatsTrie les chats du plus récent au plus ancien et les répartit dans les groupes datés que montre la barre latérale.
searchChatsRecherche de titres par mots utilisée par la boîte de dialogue de recherche. Renvoie les 8 chats les plus récents pour une requête vide.
PropTypePar défaut
chatsTous les chats, dans n’importe quel ordre. Chacun a id, title, updatedAt (ms) et, facultatifs, projectId, pinned et status ("streaming" ou "unread").
SidebarChat[]–
projectsProjets avec id, name et une icon facultative. Chacun s’affiche comme un dossier repliable contenant ses chats. La section se masque quand elle est vide.
SidebarProject[][]
activeIdLe chat ouvert, marqué comme page courante.
string | null–
onSelectAppelé quand un chat est choisi dans la liste, la recherche ou la carte des récents du rail d’icônes.
(id: string) => void–
onNewChatAppelé depuis le bouton New chat et ⌘⇧O.
() => void–
linksLignes supplémentaires sous New chat, comme Images, Library ou Plugins. Elles restent sous forme d’icônes avec infobulles dans le rail.
{ label, icon, href?, active?, onSelect? }[][]
defaultOpenProjectsProjets qui démarrent dépliés.
string[][]
onProjectOpenChangeAppelé quand un projet est déplié ou replié, pour que vous puissiez le mémoriser.
(id: string, open: boolean) => void–
onRenameActive Rename, le double-clic et F2. Appelé uniquement si le titre a réellement changé.
(id: string, title: string) => void–
onPinnedChangeActive Pin et Unpin.
(id: string, pinned: boolean) => void–
onMoveActive Move to project. null retire le chat de son projet.
(id: string, projectId: string | null) => void–
onDeleteActive Delete. Appelé après la fermeture de la fenêtre Undo de cinq secondes.
(id: string) => void–
brandVotre logo, en haut à côté des boutons de recherche et de réduction.
ReactNode–
userAffiche le bouton de compte dans le pied de page.
{ name, email?, image?, plan? }–
accountMenuItems de DropdownMenu sous le nom et l’e-mail, comme Settings, un sous-menu Help et Log out.
ReactNode–
pageSizeNombre de lignes d’historique affichées à la fois. D’autres se chargent à l’approche de la fin de la liste.
number60
classNameClasses pour la racine de la Sidebar.
string–
ToucheAction
⌘KOuvre ou ferme la recherche de chats. Le bouton de recherche de l’en-tête fait de même.
⌘⇧ODémarre un nouveau chat.
⌘BRéduit au rail d’icônes ou déploie. Sur mobile, ouvre ou ferme la barre latérale plein écran.
F2Renomme le chat ayant le focus. Le double-clic fait de même.
EnterDéplie ou replie le projet ayant le focus. Pendant un renommage, enregistre le nouveau titre ; quitter le champ enregistre aussi.
EscAnnule un renommage, ou ferme un menu ou la recherche.
  • Le chat ouvert est marqué avec aria-current="page", et le bouton de menu de chaque ligne porte le nom de son chat, par exemple « Options for Pricing page copy ». Les états de réponse sont annoncés comme « Replying » et « Unread reply » au lieu d’être montrés seulement par un spinner ou un point.
  • Les boutons de menu apparaissent au survol pour les utilisateurs de souris mais restent accessibles avec Tab, et chaque action de ligne se trouve dans ce menu, donc rien ne dépend du clic droit ou du double-clic. Les projets exposent aria-expanded, et leurs chats restent trouvables avec la recherche du navigateur une fois un projet ouvert.
  • Le titre qui se tape et le glissement plein écran sur mobile respectent la réduction des animations. Undo reste dans le toast jusqu’à son expiration, et le focus reste où il était quand un chat est supprimé.

Construit avec

Les composants HextaUI gratuits dont Chat Sidebar est constitué. Chacun s’installe séparément.

Code

3 fichiers, ajoutés à components/blocks/chat-sidebar.