mcpField
T
thread-list

thread-list

github.com/assistant-ui开发296.2k 次安装1 天前更新
代码仓库

关于 thread-list

assistant-ui 的 thread-list 技能指导在聊天界面添加和定制多会话列表,适合需要管理多个对话的开发者。它涵盖托管云端、后端适配器和外部状态存储等路径。

主要功能

  1. 01连接运行时的会话列表
  2. 02托管、远程和外部存储路径
  3. 03会话操作与选择事件
  4. 04自定义列表布局、搜索和分组

适用场景

  1. 01为聊天界面添加会话创建与切换
  2. 02通过后端适配器管理会话元数据
  3. 03为会话列表实现自定义搜索和分组

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/assistant-ui/skills --skill thread-list

安装后在 Claude Code 中输入/thread-list确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
custom-ui.md
management.md
remote-adapter.md

详细介绍

assistant-ui 对话列表

请始终查阅 assistant-ui.com/llms.txt,以获取最新 API。

运行时初始只有一个内存中的对话。只有在用户需要创建、选择和管理多个对话时,才添加对话列表。选择 UI 之前,先决定使用托管云方案、后端适配器还是外部存储。

参考资料

快速开始

使用 npx assistant-ui@latest add thread-list 安装运行时连接列表。它会创建 components/assistant-ui/elements/thread-list.aui.tsx。将 AssistantCloud 实例传递给 useChatRuntime 以使用托管对话,然后在 Provider 下方渲染列表和当前对话。

"use client";

import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";
import { AssistantCloud } from "assistant-cloud";
import { Thread } from "@/components/assistant-ui/elements/thread.aui";
import { ThreadList } from "@/components/assistant-ui/elements/thread-list.aui";

const cloud = new AssistantCloud({
  baseUrl: process.env.NEXT_PUBLIC_ASSISTANT_BASE_URL,
  anonymous: true,
});

export function Chat() {
  const runtime = useChatRuntime({ cloud });

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      <div className="flex h-dvh">
        <aside className="w-72 border-e p-2">
          <ThreadList />
        </aside>
        <main className="min-w-0 flex-1">
          <Thread />
        </main>
      </div>
    </AssistantRuntimeProvider>
  );
}

如果项目已使用 shadcn 侧边栏原语,请使用完整的侧边栏外壳。ThreadListSidebar 会渲染运行时连接的对话列表本身,因此不要在其中再挂载 ThreadList。

import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { Thread } from "@/components/assistant-ui/elements/thread.aui";
import { ThreadListSidebar } from "@/components/assistant-ui/elements/threadlist-sidebar.aui";

export function ChatLayout() {
  return (
    <SidebarProvider>
      <ThreadListSidebar />
      <SidebarInset>
        <Thread />
      </SidebarInset>
    </SidebarProvider>
  );
}

ThreadListSidebar 仍需要第一个示例中的 AssistantRuntimeProvider 祖先节点。它的 SidebarProvider 是独立的布局上下文。

对话操作

使用 useAui() 执行命令。作用域访问器是属性,因此应使用 aui.threads 和 aui.threadListItem,而不是 aui.threads()。使用 useAuiState 分别读取每个状态值。选择器可以返回单个基本类型值或稳定的运行时引用,但不能返回对象或数组字面量。

import { useAui, useAuiState } from "@assistant-ui/react";

export function ThreadControls({ threadId }: { threadId: string }) {
  const aui = useAui();
  const activeThreadId = useAuiState((s) => s.threads.mainThreadId);
  const threadIds = useAuiState((s) => s.threads.threadIds);
  const isLoading = useAuiState((s) => s.threads.isLoading);

  const item = aui.threads.item({ id: threadId });

  return (
    <div>
      <p>{threadIds.length} conversations</p>
      <p>{activeThreadId === threadId ? "Current" : "Available"}</p>
      <button disabled={isLoading} onClick={() => aui.threads.switchToNewThread()}>
        New conversation
      </button>
      <button onClick={() => item.switchTo()}>Open</button>
      <button onClick={() => item.rename("Project planning")}>Rename</button>
      <button onClick={() => item.archive()}>Archive</button>
      <button onClick={() => item.delete()}>Delete</button>
    </div>
  );
}

在对话条目原语内部,s.threadListItem 是作用域中的条目。如果一行只需要一个值,请逐个选择字段,而不是选择整个状态对象。

import { useAuiState } from "@assistant-ui/react";

export function ThreadRunningBadge() {
  const isRunning = useAuiState((s) => s.threadListItem.isRunning);
  return isRunning ? <span>Working</span> : null;
}

组合自定义列表

如果已安装元素的日期分组、搜索或视觉样式与产品不符,请从三个原语命名空间入手。Items 提供当前条目作用域,其子项渲染函数可替代已弃用的 components 属性。

import {
  ThreadListItemMorePrimitive,
  ThreadListItemPrimitive,
  ThreadListPrimitive,
} from "@assistant-ui/react";

export function CustomThreadList() {
  return (
    <ThreadListPrimitive.Root className="flex flex-col gap-1">
      <ThreadListPrimitive.New>New conversation</ThreadListPrimitive.New>
      <ThreadListPrimitive.Items>
        {({ threadListItem }) => (
          <ThreadListItemPrimitive.Root data-thread-id={threadListItem.id}>
            <ThreadListItemPrimitive.Trigger className="flex-1 text-left">
              <ThreadListItemPrimitive.Title fallback="New conversation" />
            </ThreadListItemPrimitive.Trigger>
            <ThreadListItemMorePrimitive.Root sharedFocusGroup>
              <ThreadListItemMorePrimitive.Trigger>
                More
              </ThreadListItemMorePrimitive.Trigger>
              <ThreadListItemMorePrimitive.Content>
                <ThreadListItemPrimitive.Archive asChild>
                  <ThreadListItemMorePrimitive.Item>
                    Archive
                  </ThreadListItemMorePrimitive.Item>
                </ThreadListItemPrimitive.Archive>
                <ThreadListItemMorePrimitive.Separator />
                <ThreadListItemPrimitive.Delete asChild>
                  <ThreadListItemMorePrimitive.Item>
                    Delete
                  </ThreadListItemMorePrimitive.Item>
                </ThreadListItemPrimitive.Delete>
              </ThreadListItemMorePrimitive.Content>
            </ThreadListItemMorePrimitive.Root>
          </ThreadListItemPrimitive.Root>
        )}
      </ThreadListPrimitive.Items>
    </ThreadListPrimitive.Root>
  );
}

有关归档行、加载更多、自定义搜索、分组、键盘行为和完整部件参考,请参阅自定义 UI。

选择多对话方案

方案适用场景对话元数据存储位置
AssistantCloud需要托管身份验证、持久化、同步和标题assistant-cloud
useRemoteThreadListRuntime 与 RemoteThreadListAdapter基于 LocalRuntime 的运行时使用应用的数据库你的后端
ExternalStoreThreadListAdapterExternalStoreRuntime 已拥有消息和选择状态你的状态存储

上方的云端快速入门采用托管方案。远程和外部存储方案的所有权和生命周期要求各不相同。请遵循远程适配器,不要混用它们的 API。

监听选择状态

threads.selectionChanged 取代了旧的逐项切换事件。每次选择状态发生变化时都会触发,并提供两个 ID。初始选择状态不会在挂载时触发此事件。

import { useAuiEvent } from "@assistant-ui/react";

export function SelectionTelemetry() {
  useAuiEvent(
    "threads.selectionChanged",
    ({ threadId, previousThreadId }) => {
      recordThreadSelection({ threadId, previousThreadId });
    },
  );
  return null;
}

如果此监听器位于条目行内,并且只应在该行成为当前项时响应,请将 threadId 与 s.threadListItem.id 进行比较。

常见注意事项

列表正常显示,但对话未持久化

  • 默认运行时是一个内存中的线程。传入 cloud、使用 RemoteThreadListAdapter,或提供 ExternalStoreThreadListAdapter。
  • 远程适配器只管理元数据。为每个对话添加历史记录适配器或其他消息持久化层。

切换线程后显示的是过期消息

  • 对于 RemoteThreadList 存储条目,将线程工厂包装在 withKey(id, ...) 中,以便为选中的 ID 挂载历史记录适配器。
  • 仅当运行应在选择变更后继续,且可以接受将访问过的线程保留在内存中时,才使用 backgroundThreads。

侧边栏没有运行时

  • ThreadList 和 ThreadListSidebar 需要一个 AssistantRuntimeProvider 祖先。
  • ThreadListSidebar 还需要 shadcn SidebarProvider,因为它是侧边栏外壳,而不是运行时提供程序。

每当无关的存储发生变化时,行都会重新渲染

  • 将返回对象或数组字面量的选择器替换为单独的 useAuiState 调用。直接返回 s.threads.threadIds 是安全的,因为这是由运行时提供的稳定引用。

升级后切换处理程序不再触发

  • 订阅 threads.selectionChanged 并使用 previousThreadId。已弃用的 threadListItem.switchedTo 和 threadListItem.switchedAway 事件不是当前的集成入口。

相关技能

  • runtime -- 选择并配置拥有当前对话的运行时
  • cloud -- 配置 AssistantCloud 身份验证、持久化和托管线程
  • elements -- 安装、设置样式并自定义与运行时连接的注册表组件

评论

支持 Markdown