Chat Thread

Yazma alanının çevresindeki tüm konuşma. Yanıt akarken sorunuz üstte sabitlenir, yandaki kontrol noktaları mesajlar arasında atlar ve her yanıt kopyalanabilir, düzenlenebilir, yeniden denenebilir, puanlanabilir ve sürümler arasında değiştirilebilir. Yanıtlar kod blokları, tablolar ve alıntılarla Markdown olarak render edilir; düşünme, araç çağrıları ve Prompt Input doğrudan yerine oturur.

Chat Thread, yazma alanının çevresindeki konuşmadır: mesajlarınız, modelin yanıtları ve onlarla yapabilecekleriniz. Boş durumunuz ve ortalanmış yazma alanıyla başlar, mesajlar olunca yazma alanını yeniden bağlamadan alta yerleştirir; böylece odak ve taslak metin yerinde kalır.

Az önce gönderdiğiniz mesaj, yanıtı altında akarken en üste çıkar ve siz uzaklaşana kadar görünüm yeni metni izler. Sağdaki kontrol noktaları şeridi, hover'da önizlemeyle mesajlarınız arasında atlamanızı sağlar. Eski geçmiş, görünüm hareket etmeden üstten yüklenir.

Kopyalamak, düzenlemek veya yeniden denemek için bir mesajın üzerine gelin. Düzenlemeler ve yeniden denemeler arasında geçiş yapabileceğiniz sürümlere dönüşür ve hiçbir şey kaybolmaz. Başarısız yanıtlar Try again sunar, durdurulmuş yanıtlar bunu belirtir ve başparmak yukarı ya da aşağı tek tıklamadır. Her yanıtın içine diğer bloklardaki Reasoning, ToolCalls ve Markdown doğrudan yerleşir.

  1. Pro registry'yi components.json dosyasına ekleyin

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Token'ınızı ekleyin

    Hesap sayfanızda bir token oluşturun ve .env.local dosyasına HEXTAUI_PRO_TOKEN olarak ekleyin.

  3. Bloğu ekleyin

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

AI SDK ile

useChat mesajlarını thread'e eşleyin. Reasoning parçaları Reasoning'e, araç parçaları ToolCalls'a, metin Markdown'a gider. Son kullanıcı mesajını sabitleyin, Try again için regenerate kullanın ve bir düzenlemeyi o noktadan yeniden gönderin.

"use client"

import { useChat } from "@ai-sdk/react"
import { getToolName, isToolUIPart, type UIMessage } from "ai"

import { PromptInput, PromptInputActions, PromptInputBody, PromptInputSubmit, PromptInputTextarea } from "../prompt-input/prompt-input"
import { Reasoning } from "../thinking/reasoning"
import { ToolCalls } from "../tool-calls/tool-group"
import { getToolPartStatus } from "../tool-calls/tool-part"
import { Markdown } from "../markdown/markdown"
import { ChatAssistantMessage, ChatThread, ChatUserMessage } from "@/components/blocks/chat-thread/chat-thread"

function textOf(message: UIMessage) {
  return message.parts.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("")
}

export function Chat() {
  const { messages, status, sendMessage, regenerate, stop, setMessages } = useChat()
  const busy = status === "submitted" || status === "streaming"
  const lastUser = messages.findLastIndex((message) => message.role === "user")

  return (
    <ChatThread
      busy={busy}
      composer={
        <PromptInput status={status} onStop={stop} onSubmit={({ text }) => sendMessage({ text })}>
          <PromptInputBody>
            <PromptInputTextarea />
            <PromptInputActions>
              <PromptInputSubmit />
            </PromptInputActions>
          </PromptInputBody>
        </PromptInput>
      }
    >
      {messages.map((message, index) => {
        const last = index === messages.length - 1
        if (message.role === "user") {
          return (
            <ChatUserMessage
              key={message.id}
              id={message.id}
              text={textOf(message)}
              pin={index === lastUser}
              onEdit={(text) => {
                setMessages(messages.slice(0, index))
                sendMessage({ text })
              }}
            />
          )
        }
        const reasoning = message.parts.flatMap((part) => (part.type === "reasoning" ? [part.text] : [])).join("\n\n")
        const tools = message.parts.filter(isToolUIPart)
        const streaming = last && busy
        return (
          <ChatAssistantMessage
            key={message.id}
            id={message.id}
            text={textOf(message)}
            streaming={streaming}
            last={last}
            error={last && status === "error" ? "Something went wrong." : null}
            onRetry={last ? () => regenerate() : undefined}
          >
            {reasoning ? <Reasoning text={reasoning} streaming={streaming && !textOf(message)} /> : null}
            {tools.length > 0 ? (
              <ToolCalls
                calls={tools.map((part) => ({
                  id: part.toolCallId,
                  name: getToolName(part),
                  status: getToolPartStatus(part, { stopped: !streaming }),
                }))}
              />
            ) : null}
            <Markdown
              text={textOf(message)}
              streaming={streaming}
              sources={message.parts.flatMap((part) =>
                part.type === "source-url"
                  ? [{ id: part.sourceId, url: part.url, title: part.title ?? part.url }]
                  : []
              )}
            />
          </ChatAssistantMessage>
        )
      })}
    </ChatThread>
  )
}

Yapı

Dıştan içe doğru birleştirdiğiniz parçalar.

ParçaAçıklama
ChatThreadÇerçeve: kaydırıcı, kontrol noktaları, boş durum ve yazma alanı yuvası.
ChatUserMessageMesajınız: ekler, balon, saat, sürümler, kopyalama ve düzenleme.
ChatAssistantMessageBir yanıt. İçine Reasoning, ToolCalls ve Markdown koyun; eylemleri, hata ve durdurulmuş durumlarını ekler.
ChatDividerMesaj grupları arasında etiketli bir çizgi.
ChatAttachments, ChatAction, ChatCopy, ChatBranchSwitch, ChatEditorMesajların kullandığı parçalar, özel düzenler için dışa aktarılır.
PropTürVarsayılan
childrenMesajlar ve ayırıcılar, sırayla.
ReactNode–
composerGenellikle Prompt Input. Boşken ortalanır, aksi halde altta yerleşir.
ReactNode–
emptyMesaj olmadığında yazma alanının üstünde gösterilir.
ReactNode–
busyYanıt akarken konuşmayı yardımcı teknolojiler için meşgul olarak işaretler.
booleanfalse
hasOlderÜstte “Load earlier messages” gösterir.
booleanfalse
loadingOlderO düğmede bir spinner gösterir. false olunca kaydırma konumu geri yüklenir.
booleanfalse
onLoadOlderEski mesajları buraya ekleyin.
() => void–
PropTürVarsayılan
idKararlı id. Görünüm zıplamasın diye sürümler arasında aynı tutun.
string–
textMesaj. Kontrol noktası önizlemesi için de kullanılır.
string–
pinBu mesaj göründüğünde onu en üste kaydırır. Yeni gönderilen mesaja ayarlayın.
booleanfalse
files{ id, name, size?, type?, url? }. url içeren görseller küçük resim olarak gösterilir.
ChatFile[]–
timeHover'da, okuyucunun yerel ayarında gösterilir.
Date–
branchSürüm okları için { index, count, onChange }.
ChatBranch–
onEditEdit gösterir. Değiştiyse yeni metinle çağrılır.
(text: string) => void–
attachmentsBalonun üstünde özel içerik.
ReactNode–
PropTürVarsayılan
idKararlı id.
string–
childrenReasoning, ToolCalls, Markdown ya da başka herhangi bir şey.
ReactNode–
textCopy için düz metin.
string–
streamingYanıt bitene kadar eylemleri gizler.
booleanfalse
lastEylemleri hover'da göstermek yerine görünür tutar.
booleanfalse
metaEylem satırının sağ tarafı, örneğin model ve efor.
ReactNode–
branchYeniden denemeler için sürüm okları.
ChatBranch–
feedbackGeçerli puanlama.
"up" | "down" | nullnull
onFeedbackBaşparmakları gösterir. Bir puanlama geri alındığında null ile çağrılır.
(feedback) => void–
onRetryTry again gösterir.
() => void–
errorMesajı eylemler yerine Try again ile gösterir.
string | null–
stoppedErken durdurulmuş bir yanıtı işaretler.
booleanfalse
PropTürVarsayılan
childrenEtiket, örneğin Today veya “Switched to Nova 3 Max”.
ReactNode–
TuşAction
TabMesajların eylemleri arasında gezinir. Yalnızca hover'da değil, odakta da görünürler.
EnterDüzenlenmiş bir mesajı gönderir.
ShiftEnterDüzenlerken bir satır ekler.
EscBir düzenlemeyi iptal eder.
↑↓PgUpPgDnOdaktayken konuşmayı kaydırır.
  • Konuşma, bir log barındıran etiketli bir bölgedir; böylece yeni mesajlar geldikçe duyurulur ve yanıt akarken bölge meşgul olarak işaretlenir.
  • Her simge eyleminin bir etiketi ve tooltip'i vardır. Başparmaklar aria-pressed kullanır, Copy ve geri bildirim polite bir live region ile onaylanır, hatalar role=alert kullanır.
  • Kontrol noktaları, “Message 2: …” adlı düğmelerden oluşan bir gezinti işaretidir ve geçerli olanda aria-current bulunur.
  • Sürüm okları “Version 1 of 2” adlı bir gruptur.
  • Düzenleyici kendine odaklanır, imleç sonda olur ve Cancel'da mesaja döner. Bir giriş yöntemi yazım yaparken Enter bekler.
  • Odak halkaları kaydırıcı tarafından asla kesilmez ve eylemler dokunmatik ekranlarda görünür kalır.

Şunlarla geliştirildi

Chat Thread bloğunun yapıldığı ücretsiz HextaUI bileşenleri. Her biri tek başına yüklenir.

Kod

2 dosya, components/blocks/chat-thread konumuna eklendi.