import React, { useState, useEffect, useMemo, useRef } from 'react'; import './App.css'; import DOMPurify from 'dompurify'; import { marked } from 'marked'; // 自定义 hook:防抖 function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value); React.useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => clearTimeout(handler); }, [value, delay]); return debouncedValue; } function MarkdownEditor({ value }) { const containerRef = useRef(null); const htmlContent = marked(value || ''); const sanitizedHtml = DOMPurify.sanitize(htmlContent); const [userScrolled, setUserScrolled] = useState(false); useEffect(() => { const container = containerRef.current; if (container && !userScrolled) { requestAnimationFrame(() => { container.scrollTop = container.scrollHeight; }); } }, [value, userScrolled]); useEffect(() => { const container = containerRef.current; if (container) { const handleScroll = () => { const atBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 10; setUserScrolled(!atBottom); }; container.addEventListener('scroll', handleScroll); return () => container.removeEventListener('scroll', handleScroll); } }, []); // 复制 Markdown 内容 const handleCopy = () => { navigator.clipboard.writeText(value || '') .then(() => alert('Markdown 已复制到剪贴板')) .catch(err => console.error('复制失败:', err)); }; // 下载 Markdown 文件 const handleDownload = () => { const blob = new Blob([value || ''], { type: 'text/markdown;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'document.md'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; return (
{/*
*/}
); } function SendRequestToBackend() { const [inputValue, setInputValue] = useState(''); const handleSendRequest = async () => { try { const response = await fetch('http://localhost:8001/generate_survey', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query: inputValue }), }); if (!response.ok) { throw new Error('Failed to send request'); } const data = await response.json(); console.log('Response from backend:', data); } catch (error) { console.error('Error sending request:', error); } }; return (
setInputValue(e.target.value)} className="neon-input" placeholder="Enter text to send" rows={3} />
); } function App() { const [inputs, setInputs] = useState({ query: { title: 'Query', displayText: '', targetText: '', isTyping: false }, nowUpdate: { title: 'Now Update', displayText: '', targetText: '', isTyping: false }, nextUpdate: { title: 'Next Update', displayText: '', targetText: '', isTyping: false }, searchKeywords: { title: 'Search Keywords', displayText: '', targetText: '', isTyping: false }, papers: { title: 'Papers', displayText: '', targetText: '', isTyping: false }, }); const [markdownContent, setMarkdownContent] = useState(''); const inputKeyMap = { query: inputs.query, nowUpdate: inputs.nowUpdate, nextUpdate: inputs.nextUpdate, searchKeywords: inputs.searchKeywords, papers: inputs.papers, markdown: markdownContent }; const updateInputsFromPostData = (postData) => { let newMarkdownContent = markdownContent; Object.entries(postData).forEach(([key, value]) => { if (key in inputKeyMap) { if (key === 'markdown') { if (markdownContent !== value) { newMarkdownContent = value; setMarkdownContent(newMarkdownContent); } } else if (inputKeyMap[key] && inputKeyMap[key].targetText !== value) { const updatedInput = { ...inputKeyMap[key], targetText: value, isTyping: true, }; setInputs((prevInputs) => ({ ...prevInputs, [key]: updatedInput, })); // startTypingAnimationForTextbox(value, (newText) => { // setInputs((prevInputs) => ({ // ...prevInputs, // [key]: { // ...prevInputs[key], // displayText: newText, // }, // })); // }); } } }); }; // const startTypingAnimationForTextbox = (text, setText) => { // setText(''); // let charIndex = 0; // const timer = setInterval(() => { // if (charIndex < text.length) { // setText((prev) => prev + text[charIndex]); // charIndex++; // } else { // clearInterval(timer); // } // }, 50); // Reduced interval for faster typing animation // }; useEffect(() => { const ws = new WebSocket('ws://localhost:8001/ws'); ws.onmessage = (event) => { try { const data = JSON.parse(event.data); updateInputsFromPostData(data); console.log('Received data:', data); } catch (e) { console.error('Invalid WebSocket message:', e); } }; ws.onerror = (err) => { console.error('WebSocket error:', err); }; // return () => ws.close(); }, []); const leftInputs = [inputs.nowUpdate, inputs.nextUpdate,inputs.searchKeywords]; const rightInputs = [inputs.papers]; return (
{leftInputs.map((input, index) => (

{input.title}