diff --git a/app/beam-demo/page.tsx b/app/beam-demo/page.tsx
new file mode 100644
index 00000000..555c10b1
--- /dev/null
+++ b/app/beam-demo/page.tsx
@@ -0,0 +1,39 @@
+"use client"
+
+import { AssistantRuntimeProvider } from "@assistant-ui/react"
+import { Thread } from "@/components/assistant-ui/thread"
+import { ThreadListSidebar } from "@/components/assistant-ui/threadlist-sidebar"
+import { AssistantTools } from "@/components/assistant-ui/tools"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/components/ui/sidebar"
+import { useThreadChatRuntime } from "@/lib/chat/use-thread-chat-runtime"
+
+/**
+ * border-beam 风格 demo:assistant-ui 的 ThreadListSidebar(floating 变体)
+ * 与 Thread 组合,侧边栏面板和 composer 都带 pulse-inner 呼吸光晕,
+ * 聊天走与首页相同的完整 runtime(MiniMax 流式 + Postgres 持久化)。
+ */
+export default function BeamDemoPage() {
+ const runtime = useThreadChatRuntime()
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/page.tsx b/app/page.tsx
index d32c3676..4cd18b2d 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,43 +1,12 @@
"use client"
-import { useMemo } from "react"
-import { AssistantRuntimeProvider, useRemoteThreadListRuntime } from "@assistant-ui/react"
-import { AssistantChatTransport, useChatRuntime } from "@assistant-ui/react-ai-sdk"
+import { AssistantRuntimeProvider } from "@assistant-ui/react"
import { Base } from "@/components/examples/base"
import { AssistantTools } from "@/components/assistant-ui/tools"
-import { postgresThreadListAdapter } from "@/lib/chat/thread-list-adapter"
-import { usePostgresThreadHistoryAdapter } from "@/lib/chat/use-thread-history-adapter"
-import { r2AttachmentAdapter } from "@/lib/chat/attachment-adapter"
-import { useResearchMode } from "@/lib/chat/research-mode"
-
-function useMyChatRuntime() {
- const history = usePostgresThreadHistoryAdapter()
- // 把「深度研究」开关状态随每条消息发给 chat route。用 getState() 而非闭包快照,
- // 保证读到发送时的最新开关值。
- const transport = useMemo(
- () =>
- new AssistantChatTransport({
- prepareSendMessagesRequest: ({ id, messages, trigger, messageId, body }) => ({
- body: {
- ...body,
- id,
- messages,
- trigger,
- messageId,
- deepResearch: useResearchMode.getState().enabled,
- },
- }),
- }),
- [],
- )
- return useChatRuntime({ transport, adapters: { history, attachments: r2AttachmentAdapter } })
-}
+import { useThreadChatRuntime } from "@/lib/chat/use-thread-chat-runtime"
export default function Page() {
- const runtime = useRemoteThreadListRuntime({
- runtimeHook: useMyChatRuntime,
- adapter: postgresThreadListAdapter,
- })
+ const runtime = useThreadChatRuntime()
return (
diff --git a/components/assistant-ui/composer-beam.tsx b/components/assistant-ui/composer-beam.tsx
new file mode 100644
index 00000000..b1d63551
--- /dev/null
+++ b/components/assistant-ui/composer-beam.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import type { FC, ReactNode } from "react"
+import { useAuiState } from "@assistant-ui/react"
+import { PulseBeam } from "@/components/assistant-ui/pulse-beam"
+
+/**
+ * composer 外壳专用的 pulse-inner 呼吸光晕,绑定 AI 回复状态的混合方案:
+ * 空闲时色相定格 + 低强度微光(仍轻微呼吸),回复中恢复全彩流动与全亮度,
+ * 作为「正在生成」的运行状态反馈。
+ * wrapper 自带 overflow:hidden 会裁掉外壳的投影,
+ * 所以把浅色模式的投影从外壳复制到 wrapper 上。
+ */
+export const ComposerBeam: FC<{ children: ReactNode }> = ({ children }) => {
+ const isRunning = useAuiState((s) => s.thread.isRunning)
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/components/assistant-ui/pulse-beam.tsx b/components/assistant-ui/pulse-beam.tsx
new file mode 100644
index 00000000..ebf98b5f
--- /dev/null
+++ b/components/assistant-ui/pulse-beam.tsx
@@ -0,0 +1,63 @@
+"use client"
+
+import { useSyncExternalStore, type FC, type ReactNode } from "react"
+import { BorderBeam } from "border-beam"
+import { useTheme } from "next-themes"
+
+// 水合安全的挂载检测:服务端快照为 false,水合完成后 React 以客户端快照 true 重渲染。
+const noopSubscribe = () => () => {}
+function useHydrated() {
+ return useSyncExternalStore(
+ noopSubscribe,
+ () => true,
+ () => false
+ )
+}
+
+/**
+ * border-beam pulse-inner 呼吸光晕的通用包装,
+ * 复刻 https://beam.jakubantalik.com/pulse 的光影效果:
+ * 彩色光晕贴着元素内侧四周弥散、缓慢呼吸(裁剪在边框以内)。
+ * 跟随 next-themes 的明暗主题;圆角默认自动探测子元素,
+ * 子元素圆角探测不到时(如首子元素是无圆角的内部布局)可显式传 borderRadius。
+ * active 控制光晕开关(带淡入/淡出过渡),用于绑定 AI 回复中等运行状态。
+ * staticColors 冻结色相流转(呼吸仍在),strength 控制整体亮度(0-1),
+ * 两者组合可实现「空闲微光、运行全彩流动」的混合状态。
+ */
+export const PulseBeam: FC<{
+ children: ReactNode
+ className?: string
+ borderRadius?: number
+ active?: boolean
+ staticColors?: boolean
+ strength?: number
+}> = ({
+ children,
+ className,
+ borderRadius,
+ active = true,
+ staticColors,
+ strength,
+}) => {
+ const { resolvedTheme } = useTheme()
+
+ // SSR 时 resolvedTheme 为 undefined,客户端首帧可能已解析出 light,
+ // 直接使用会导致 BorderBeam 内联