import { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router"; import { Send, Plus, Trash2, LogOut, Menu, X, MessageSquare, User, Bot, Loader2, Database, } from "lucide-react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import rehypeKatex from "rehype-katex"; import type { Components } from "react-markdown"; import KnowledgeManagement from "./KnowledgeManagement"; import PhaseToggle, { type Phase } from "./interview/PhaseToggle"; import InterviewPanel from "./interview/InterviewPanel"; import InterviewResultView from "./interview/InterviewResultView"; import NewChatOnboarding from "./NewChatOnboarding"; import type { InterviewResult } from "../../services/interviewApi"; import { getInterviewResult } from "../../services/interviewApi"; import { getRooms, getRoom, createRoom, deleteRoom, streamChat, type ChatSource, } from "../../services/api"; interface StoredUser { user_id: string; email: string; name: string; loginTime: string; } interface Message { id: string; role: "user" | "assistant"; content: string; timestamp: number; sources?: ChatSource[]; } interface ChatRoom { id: string; title: string; messages: Message[]; createdAt: string; updatedAt: string | null; messagesLoaded: boolean; } // Preprocess markdown to ensure tables are properly separated from surrounding text function preprocessMarkdown(content: string): string { // Step 1: Split concatenated table rows — "| val ||" means end of row + start of next let result = content.replace(/\|\|/g, "|\n|"); // Step 2: Per-line — if a line has non-pipe text before a pipe table row, split them const lines = result.split("\n"); const processed = lines.map((line) => { const tableStart = line.indexOf("|"); if (tableStart > 0) { const tableContent = line.slice(tableStart); // Confirm it looks like a table row (at least 2 pipes) if ((tableContent.match(/\|/g) ?? []).length >= 2) { return line.slice(0, tableStart).trimEnd() + "\n\n" + tableContent; } } return line; }); result = processed.join("\n"); // Step 3: Ensure blank line before table rows preceded by non-table text (not another row ending with |) result = result.replace(/([^|\n])\n(\|)/g, "$1\n\n$2"); return result; } // Markdown component overrides for clean rendering inside chat bubbles const markdownComponents: Components = { p: ({ children }) => (
{children}
), h1: ({ children }) => (
{children}
);
}
return (
{children}
);
},
pre: ({ children }) => (
{children}
),
blockquote: ({ children }) => (
{children}), table: ({ children }) => (
{roomsError}
) : ( chats.map((chat) => (Send a message to begin chatting
{message.content}
) : message.content === "" && streamingMsgId === message.id ? ( // Waiting for first chunk — show typing indicatorSources: