From d2d487eb979210f177befd8c7919798def3a75a5 Mon Sep 17 00:00:00 2001 From: mawkone Date: Mon, 15 Jun 2026 13:41:44 -0700 Subject: [PATCH] Fix ArrowDown import --- vibn-frontend/app/api/chat/route.ts | 3 +- .../components/vibn-chat/chat-panel.tsx | 123 +++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/vibn-frontend/app/api/chat/route.ts b/vibn-frontend/app/api/chat/route.ts index ed96fb3..d4b4f67 100644 --- a/vibn-frontend/app/api/chat/route.ts +++ b/vibn-frontend/app/api/chat/route.ts @@ -71,6 +71,7 @@ function classifyTurnIntent(message: string): TurnIntent { // High-agency directives if ( + /^(yes|yep|yeah|yup|sure|ok|okay|kk|k|please|fix it)\b/.test(m) || /(keep going|continue|build it|do it|go ahead|proceed|autonomous)/.test(m) ) return "autonomous"; @@ -134,7 +135,7 @@ function classifyTurnIntent(message: string): TurnIntent { m, ) || // Acknowledgements / pleasantries - /^(thanks|thank you|ty|ok|okay|kk|k|yes|yep|yeah|yup|no|nope|nah|sure|cool|nice|great|awesome|perfect|got it|sounds good|will do|nvm|never mind)\b/.test( + /^(thanks|thank you|ty|no|nope|nah|cool|nice|great|awesome|perfect|got it|sounds good|will do|nvm|never mind)\b/.test( m, ) || /(what'?s up|how are you|how'?s it going|good to see you)/.test(m) || diff --git a/vibn-frontend/components/vibn-chat/chat-panel.tsx b/vibn-frontend/components/vibn-chat/chat-panel.tsx index c0eb0ae..e10b286 100644 --- a/vibn-frontend/components/vibn-chat/chat-panel.tsx +++ b/vibn-frontend/components/vibn-chat/chat-panel.tsx @@ -640,7 +640,7 @@ function Timeline({ entries, isActiveStream }: { entries: TimelineEntry[], isAct {items.map((item, i) => { const isLast = i === items.length - 1; if (item.kind === "thought") { - return ; + return ; } if (item.kind === "text") { return ; @@ -665,6 +665,92 @@ 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); + const textLenRef = React.useRef(text.length); + + React.useEffect(() => { + // If not streaming, auto-collapse after a short delay so the user isn't stuck with huge thinking blocks + if (!isStreaming) { + const t = setTimeout(() => setExpanded(false), 500); + return () => clearTimeout(t); + } + }, [isStreaming]); + + const proseWrap: React.CSSProperties = { + overflowWrap: "anywhere", + wordBreak: "break-word", + minWidth: 0, + }; + + return ( +
+ + {expanded && ( +
+ ` : ""), + }} + /> +
+ )} +
+ ); +} + function TimelineText({ text, isStreaming }: { text: string; isStreaming?: boolean }) { const proseWrap: React.CSSProperties = { overflowWrap: "anywhere", @@ -1060,6 +1146,7 @@ export function ChatPanel({ .catch(() => {}); }, [projectId, workspace, status]); const [sending, setSending] = useState(false); + const [showScrollButton, setShowScrollButton] = useState(false); const [currentPhaseLabel, setCurrentPhaseLabel] = useState( null, ); @@ -1890,6 +1977,11 @@ export function ChatPanel({ {/* Messages */}
{ + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + const distanceToBottom = scrollHeight - scrollTop - clientHeight; + setShowScrollButton(distanceToBottom > 150); + }} style={{ flex: 1, minWidth: 0, @@ -2119,6 +2211,35 @@ export function ChatPanel({ + {/* Scroll to bottom button */} + {showScrollButton && ( + + )} + {(selectToggle) => (