Click the button in the bottom right corner to open the chat.
++ Use the controls panel to customize the chat appearance. +
+The sidebar is always visible on the right.
+
+ )
+ },
+ CodeHeader,
+})
diff --git a/elements/src/components/assistant-ui/reasoning.tsx b/elements/src/components/assistant-ui/reasoning.tsx
new file mode 100644
index 000000000..f69ac6b47
--- /dev/null
+++ b/elements/src/components/assistant-ui/reasoning.tsx
@@ -0,0 +1,261 @@
+'use client'
+
+import { BrainIcon, ChevronDownIcon } from 'lucide-react'
+import {
+ memo,
+ useCallback,
+ useRef,
+ useState,
+ type FC,
+ type PropsWithChildren,
+} from 'react'
+
+import {
+ useScrollLock,
+ useAssistantState,
+ type ReasoningMessagePartComponent,
+ type ReasoningGroupComponent,
+} from '@assistant-ui/react'
+
+import { MarkdownText } from '@/components/assistant-ui/markdown-text'
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from '@/components/ui/collapsible'
+import { cn } from '@/lib/utils'
+
+const ANIMATION_DURATION = 200
+
+/**
+ * Root collapsible container that manages open/closed state and scroll lock.
+ * Provides animation timing via CSS variable and prevents scroll jumps on collapse.
+ */
+const ReasoningRoot: FC<
+ PropsWithChildren<{
+ className?: string
+ }>
+> = ({ className, children }) => {
+ const collapsibleRef = useRef
+ {JSON.stringify(item, null, 2)}
+
+ )
+ }
+ })}
+
+ {text}
+
+ )
+ return (
+
+ )
+}
+
+function getFormattedText(text: string, language: BundledLanguage | undefined) {
+ if (!language) return text
+ switch (language) {
+ case 'json':
+ return JSON.stringify(JSON.parse(text), null, 2)
+ default:
+ return text
+ }
+}
diff --git a/elements/src/components/assistant-ui/tool-group.tsx b/elements/src/components/assistant-ui/tool-group.tsx
new file mode 100644
index 000000000..9dc6525d7
--- /dev/null
+++ b/elements/src/components/assistant-ui/tool-group.tsx
@@ -0,0 +1,121 @@
+import { useRadius } from '@/hooks/useRadius'
+import { cn } from '@/lib/utils'
+import { useAssistantState } from '@assistant-ui/react'
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon, Loader } from 'lucide-react'
+import { useMemo, useState, type FC, type PropsWithChildren } from 'react'
+import { Button } from '../ui/button'
+import { AnimatePresence, domAnimation, LazyMotion } from 'motion/react'
+import * as m from 'motion/react-m'
+import { EASE_OUT_QUINT } from '@/lib/easing'
+import { useElements } from '@/hooks/useElements'
+import { humanizeToolName } from '@/lib/humanize'
+import { useDensity } from '@/hooks/useDensity'
+
+export const ToolGroup: FC<
+ PropsWithChildren<{ startIndex: number; endIndex: number }>
+> = ({ children }) => {
+ const r = useRadius()
+ const parts = useAssistantState(({ message }) => message).parts
+ const toolCallParts = parts.filter((part) => part.type === 'tool-call')
+ const anyMessagePartsAreRunning = toolCallParts.some(
+ (part) => part.status?.type === 'running'
+ )
+ const icon = useMemo(() => {
+ if (anyMessagePartsAreRunning)
+ return
+ {contentString}
+
+