Fix chat timeline hydration bug causing UI run-on text
@@ -71,7 +71,7 @@ export function WizardTop({
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/vibn-logo-circle-black.png"
|
||||
src="/vibn-black-circle-logo.png"
|
||||
alt="VIBN"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function RootLayout({
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#1a1510" />
|
||||
<link rel="apple-touch-icon" href="/vibn-logo-circle-black.png" />
|
||||
<link rel="apple-touch-icon" href="/vibn-logo-circle.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
</head>
|
||||
<body
|
||||
|
||||
@@ -160,7 +160,7 @@ export function VIBNSidebar({ workspace, tabs, activeTab }: VIBNSidebarProps) {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/vibn-logo-circle-black.png"
|
||||
src="/vibn-black-circle-logo.png"
|
||||
alt="VIBN"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
@@ -233,7 +233,7 @@ export function VIBNSidebar({ workspace, tabs, activeTab }: VIBNSidebarProps) {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/vibn-logo-circle-black.png"
|
||||
src="/vibn-black-circle-logo.png"
|
||||
alt="VIBN"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
|
||||
@@ -518,23 +518,30 @@ const MessageBubble = React.memo(function MessageBubble({
|
||||
{!isUser && (
|
||||
<div
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: "50%",
|
||||
background: "#f4f4f5", // Zinc-100 instead of black
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginRight: 10,
|
||||
marginRight: 8,
|
||||
flexShrink: 0,
|
||||
marginTop: 0,
|
||||
overflow: "hidden"
|
||||
marginTop: 2,
|
||||
border: "1px solid #e4e4e7",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/vibn-logo-circle-black.png"
|
||||
alt="Vibn AI"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
color: "#18181b", // Dark gray instead of white
|
||||
fontSize: "0.6rem",
|
||||
fontWeight: 700,
|
||||
fontFamily: "var(--font-lora),serif",
|
||||
fontStyle: "italic",
|
||||
}}
|
||||
>
|
||||
V.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
@@ -1188,6 +1195,7 @@ export function ChatPanel({
|
||||
(m: {
|
||||
role: string;
|
||||
textSegments?: string[];
|
||||
timeline?: TimelineEntry[];
|
||||
toolCalls?: Array<{
|
||||
name: string;
|
||||
args: Record<string, unknown>;
|
||||
@@ -1195,6 +1203,13 @@ export function ChatPanel({
|
||||
}>;
|
||||
}) => {
|
||||
if (m.role !== "assistant") return m as unknown as Message;
|
||||
|
||||
// If the backend provided a perfectly ordered timeline (from our new Postgres schema), use it directly!
|
||||
if (Array.isArray(m.timeline) && m.timeline.length > 0) {
|
||||
return { ...m, content: "" } as unknown as Message;
|
||||
}
|
||||
|
||||
// Fallback for very old messages in the database before the timeline was tracked
|
||||
const segs: string[] = Array.isArray(m.textSegments)
|
||||
? m.textSegments
|
||||
: [];
|
||||
@@ -1203,12 +1218,7 @@ export function ChatPanel({
|
||||
kind: "text",
|
||||
text: t,
|
||||
}));
|
||||
// We don't have round-level interleaving for tool calls in
|
||||
// the persisted shape (the schema flattens them), so we drop
|
||||
// the toolCalls into the timeline at the end. The streamed
|
||||
// shape preserves true ordering; this is just a reload
|
||||
// approximation. Good enough — what the user really cares
|
||||
// about is the text segments not run-on'ing into one blob.
|
||||
|
||||
if (Array.isArray(m.toolCalls)) {
|
||||
for (const tc of m.toolCalls) {
|
||||
timeline.push({ kind: "tool", name: tc.name, status: "done" });
|
||||
@@ -2426,7 +2436,7 @@ export function ChatPanel({
|
||||
style={{ flexShrink: 0, display: "flex" }}
|
||||
>
|
||||
<img
|
||||
src="/vibn-logo-circle-black.png"
|
||||
src="/vibn-black-circle-logo.png"
|
||||
alt="VIBN"
|
||||
width={26}
|
||||
height={26}
|
||||
@@ -2735,7 +2745,7 @@ export function ChatPanel({
|
||||
style={{ flexShrink: 0, display: "flex" }}
|
||||
>
|
||||
<img
|
||||
src="/vibn-logo-circle-black.png"
|
||||
src="/vibn-black-circle-logo.png"
|
||||
alt="VIBN"
|
||||
width={26}
|
||||
height={26}
|
||||
|
||||
|
Before Width: | Height: | Size: 740 KiB |
|
Before Width: | Height: | Size: 797 KiB |
|
Before Width: | Height: | Size: 745 KiB |
|
Before Width: | Height: | Size: 783 KiB |
|
Before Width: | Height: | Size: 812 KiB |
|
Before Width: | Height: | Size: 741 KiB |
|
Before Width: | Height: | Size: 782 KiB |
|
Before Width: | Height: | Size: 758 KiB |
|
Before Width: | Height: | Size: 786 KiB |
|
Before Width: | Height: | Size: 844 KiB |