aui-components

Custom Welcome Screen

自定义 Thread 欢迎界面,添加快捷提问和品牌化展示

Thread 组件支持通过 Welcome 子组件自定义空状态下的欢迎界面。

基本自定义

最简单的自定义 welcome 界面:

components/chat.tsx
"use client";

import { AssistantRuntimeProvider, useChatRuntime } from "@assistant-ui/react";
import { Thread } from "aui-components";

function Welcome() {
  return (
    <div className="flex h-full flex-col items-center justify-center py-16">
      <h1 className="text-2xl font-bold">AI 助手</h1>
      <p className="text-muted-foreground mt-2">有什么我可以帮你的?</p>
    </div>
  );
}

export function Chat() {
  const runtime = useChatRuntime({ api: "/api/chat" });

  return (
    <AssistantRuntimeProvider aui={runtime.aui} runtime={runtime}>
      <div className="h-screen">
        <Thread components={{ Welcome }} />
      </div>
    </AssistantRuntimeProvider>
  );
}

带快捷提问的欢迎界面

更实用的欢迎界面通常会提供一些快捷提问按钮,帮助用户快速开始对话:

components/chat.tsx
"use client";

import { AssistantRuntimeProvider, useChatRuntime } from "@assistant-ui/react";
import {
  Thread,
  Avatar,
} from "aui-components";
import { Bot, Sparkles, FileText, Code, Zap } from "lucide-react";
import type { FC } from "react";

// 快捷提问列表
const suggestions = [
  {
    icon: FileText,
    label: "总结文档",
    prompt: "请帮我总结这篇文章的要点",
  },
  {
    icon: Code,
    label: "写代码",
    prompt: "帮我写一个 React 组件,实现...",
  },
  {
    icon: Zap,
    label: "头脑风暴",
    prompt: "帮我 brainstorm 一下关于...",
  },
  {
    icon: Sparkles,
    label: "创意写作",
    prompt: "写一篇关于...的短文章",
  },
];

const Welcome: FC = () => {
  const handleSuggestion = (prompt: string) => {
    // 通过 composer 发送消息
    const composer = document.querySelector<HTMLTextAreaElement>(
      '[data-slot="composer-input"]'
    );
    if (composer) {
      composer.focus();
      // 设置文本并触发 input 事件
      const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
        window.HTMLTextAreaElement.prototype,
        "value"
      )?.set;
      nativeInputValueSetter?.call(composer, prompt);
      composer.dispatchEvent(new Event("input", { bubbles: true }));
    }
  };

  return (
    <div className="flex h-full flex-col items-center justify-center px-4">
      {/* 头像 */}
      <Avatar.Root size="lg">
        <Avatar.Fallback>
          <Bot className="size-8" />
        </Avatar.Fallback>
      </Avatar.Root>

      {/* 标题 */}
      <h1 className="mt-6 text-center text-3xl font-bold tracking-tight">
        AI 助手
      </h1>

      {/* 描述 */}
      <p className="text-muted-foreground mt-3 max-w-md text-center text-sm leading-relaxed">
        我可以帮你写代码、分析文档、头脑风暴、创作内容。选择一个话题开始吧。
      </p>

      {/* 快捷提问卡片网格 */}
      <div className="mt-8 grid w-full max-w-2xl grid-cols-2 gap-3">
        {suggestions.map((item) => {
          const { icon: Icon, label, prompt } = item;
          return (
            <button
              key={label}
              onClick={() => handleSuggestion(prompt)}
              className="flex flex-col items-center gap-2 rounded-xl border bg-card p-4 text-center transition-colors hover:bg-accent hover:text-accent-foreground"
            >
              <div className="rounded-lg bg-primary/10 p-2">
                <Icon className="text-primary size-5" />
              </div>
              <span className="text-sm font-medium">{label}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
};

export function Chat() {
  const runtime = useChatRuntime({ api: "/api/chat" });

  return (
    <AssistantRuntimeProvider aui={runtime.aui} runtime={runtime}>
      <div className="h-screen">
        <Thread components={{ Welcome }} />
      </div>
    </AssistantRuntimeProvider>
  );
}

品牌化欢迎界面

为产品定制的欢迎界面可以包含 Logo、品牌色、特性介绍等:

components/welcome.tsx
import { Avatar } from "aui-components";
import { Bot, Shield, Zap, Globe } from "lucide-react";
import type { FC } from "react";

const features = [
  {
    icon: Shield,
    title: "安全可靠",
    description: "数据加密传输,隐私保护",
  },
  {
    icon: Zap,
    title: "快速响应",
    description: "毫秒级推理速度",
  },
  {
    icon: Globe,
    title: "多语言支持",
    description: "支持 50+ 语言对话",
  },
];

export const BrandedWelcome: FC = () => {
  return (
    <div className="flex h-full flex-col items-center justify-center">
      {/* Logo 区域 */}
      <div className="flex items-center gap-3">
        <Avatar.Root size="lg">
          <Avatar.Fallback>
            <Bot className="size-8" />
          </Avatar.Fallback>
        </Avatar.Root>
        <div>
          <h1 className="text-2xl font-bold">MyAI</h1>
          <p className="text-muted-foreground text-xs">Powered by aui-components</p>
        </div>
      </div>

      {/* 特性介绍 */}
      <div className="mt-10 flex gap-8">
        {features.map((feature) => {
          const { icon: Icon, title, description } = feature;
          return (
            <div
              key={title}
              className="flex flex-col items-center gap-2 text-center"
            >
              <div className="rounded-full bg-primary/10 p-3">
                <Icon className="text-primary size-5" />
              </div>
              <h3 className="text-sm font-medium">{title}</h3>
              <p className="text-muted-foreground max-w-[160px] text-xs">
                {description}
              </p>
            </div>
          );
        })}
      </div>

      {/* 底部提示 */}
      <p className="text-muted-foreground mt-12 text-sm">
        在下方输入框开始对话
      </p>
    </div>
  );
};

相关链接

On this page