diff --git a/vibn-frontend/components/vibn-chat/chat-panel.tsx b/vibn-frontend/components/vibn-chat/chat-panel.tsx
index 81ee9d2e..559e8fd2 100644
--- a/vibn-frontend/components/vibn-chat/chat-panel.tsx
+++ b/vibn-frontend/components/vibn-chat/chat-panel.tsx
@@ -9,7 +9,7 @@ import React, {
type CSSProperties,
} from "react";
import Link from "next/link";
-import { useSession } from "next-auth/react";
+import { ArrowDown, useSession } from "next-auth/react";
import { useParams, usePathname } from "next/navigation";
import {
MessageSquare,
@@ -520,28 +520,21 @@ const MessageBubble = React.memo(function MessageBubble({
style={{
width: 24,
height: 24,
- borderRadius: "50%",
- background: "#f4f4f5", // Zinc-100 instead of black
display: "flex",
alignItems: "center",
justifyContent: "center",
marginRight: 8,
flexShrink: 0,
marginTop: 2,
- border: "1px solid #e4e4e7",
}}
>
-
- V.
-
+
)}
{
const isLast = i === items.length - 1;
if (item.kind === "thought") {
- return
;
+ return
;
}
if (item.kind === "text") {
return
;
@@ -668,6 +661,89 @@ function Timeline({ entries, isActiveStream }: { entries: TimelineEntry[], isAct
* bubble so each round of multi-tool-loop output reads as a discrete
* step instead of concatenating into a wall of text.
*/
+
+function TimelineThought({ text, isStreaming }: { text: string; isStreaming?: boolean }) {
+ const [expanded, setExpanded] = React.useState(true);
+
+ // Auto-collapse when streaming stops
+ const textLenRef = React.useRef(text.length);
+ React.useEffect(() => {
+ let t = setTimeout(() => {
+ if (text.length === textLenRef.current && !isStreaming) {
+ setExpanded(false);
+ }
+ }, 1500);
+ return () => clearTimeout(t);
+ }, [text, isStreaming]);
+
+ React.useEffect(() => {
+ textLenRef.current = text.length;
+ }, [text]);
+
+ return (
+
+
+
+ {expanded && (
+
+ ` : ""),
+ }}
+ />
+
+ )}
+
+ );
+}
+
function TimelineText({ text, isStreaming }: { text: string; isStreaming?: boolean }) {
const proseWrap: React.CSSProperties = {
overflowWrap: "anywhere",
@@ -1068,6 +1144,8 @@ export function ChatPanel({
);
const [showThreads, setShowThreads] = useState(false);
const [mcpToken, setMcpToken] = useState
(null);
+ const [showScrollButton, setShowScrollButton] = useState(false);
+ const scrollContainerRef = useRef(null);
const [isChatMinimized, setIsChatMinimized] = useState(false);
// Auto-minimize when navigating to dashboard, auto-open when navigating to preview
@@ -2920,3 +2998,36 @@ export function ChatPanel({
);
}
+
+
+
+
+function ScrollToBottomButton({ onClick, visible }: { onClick: () => void, visible: boolean }) {
+ if (!visible) return null;
+ return (
+
+ );
+}