/* * ChatInterface.jsx - Chat Component with Session Protection Integration * * SESSION PROTECTION INTEGRATION: * =============================== * * This component integrates with the Session Protection System to prevent project updates * from interrupting active conversations: * * Key Integration Points: * 1. handleSubmit() - Marks session as active when user sends message (including temp ID for new sessions) * 2. session-created handler - Replaces temporary session ID with real WebSocket session ID * 3. claude-complete handler - Marks session as inactive when conversation finishes * 4. session-aborted handler - Marks session as inactive when conversation is aborted * * This ensures uninterrupted chat experience by coordinating with App.jsx to pause sidebar updates. */ import React, { useState, useEffect, useRef, useMemo, useCallback, memo } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { useDropzone } from 'react-dropzone'; import TodoList from './TodoList'; import ClaudeLogo from './ClaudeLogo.jsx'; import CursorLogo from './CursorLogo.jsx'; import NextTaskBanner from './NextTaskBanner.jsx'; import { useTasksSettings } from '../contexts/TasksSettingsContext'; import ClaudeStatus from './ClaudeStatus'; import TokenUsagePie from './TokenUsagePie'; import { MicButton } from './MicButton.jsx'; import { api, authenticatedFetch } from '../utils/api'; import Fuse from 'fuse.js'; import CommandMenu from './CommandMenu'; // Helper function to decode HTML entities in text function decodeHtmlEntities(text) { if (!text) return text; return text .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, "'") .replace(/&/g, '&'); } // Normalize markdown text where providers mistakenly wrap short inline code with single-line triple fences. // Only convert fences that do NOT contain any newline to avoid touching real code blocks. function normalizeInlineCodeFences(text) { if (!text || typeof text !== 'string') return text; try { // ```code``` -> `code` return text.replace(/```\s*([^\n\r]+?)\s*```/g, '`$1`'); } catch { return text; } } // Small wrapper to keep markdown behavior consistent in one place const Markdown = ({ children, className }) => { const content = normalizeInlineCodeFences(String(children ?? '')); return (
{children}
);
}
const [copied, setCopied] = React.useState(false);
const textToCopy = raw;
const handleCopy = () => {
const doSet = () => {
setCopied(true);
setTimeout(() => setCopied(false), 1500);
};
try {
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(textToCopy).then(doSet).catch(() => {
// Fallback
const ta = document.createElement('textarea');
ta.value = textToCopy;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); } catch {}
document.body.removeChild(ta);
doSet();
});
} else {
const ta = document.createElement('textarea');
ta.value = textToCopy;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); } catch {}
document.body.removeChild(ta);
doSet();
}
} catch {}
};
return (
{children}
{children}), a: ({ href, children }) => ( {children} ), p: ({ children }) =>
{message.toolInput}
{message.toolInput}
{message.toolInput}
{message.toolInput}
{message.toolInput}
{message.toolInput}
{beforePrompt}
{questionLine}
{/* Option buttons */}✓ Claude selected option {selectedOption}
In the CLI, you would select this option interactively using arrow keys or by typing the number.
The file content is displayed in the diff view above
{questionLine}
{/* Option buttons */}⏳ Waiting for your response in the CLI
Please select an option in your terminal where Claude is running.
{formatted}
Select a project to start chatting with Claude
Loading session messages...
Select a provider to start a new conversation
{provider === 'claude' ? 'Ready to use Claude AI. Start typing your message below.' : provider === 'cursor' ? `Ready to use Cursor with ${cursorModel}. Start typing your message below.` : 'Select a provider above to begin' }
{/* Show NextTaskBanner when provider is selected and ready */} {provider && tasksEnabled && (Continue your conversation
Ask questions about your code, request changes, or get help with development tasks
{/* Show NextTaskBanner for existing sessions too */} {tasksEnabled && (Loading older messages...