{"ast":null,"code":"var _jsxFileName = \"/home/node/.openclaw/workspace/flying-hero/projects/meeting-room/frontend/src/App.js\",\n _s = $RefreshSig$(),\n _s2 = $RefreshSig$(),\n _s3 = $RefreshSig$();\nimport React, { useState, useEffect } from 'react';\nimport { BrowserRouter as Router, Routes, Route, Link, useNavigate, useParams } from 'react-router-dom';\nimport axios from 'axios';\nimport { jsxDEV as _jsxDEV } from \"react/jsx-dev-runtime\";\nconst API_BASE = 'http://localhost:8000/api/v1';\naxios.interceptors.request.use(config => {\n const token = localStorage.getItem('token');\n if (token) config.headers.Authorization = `Bearer ${token}`;\n return config;\n});\n\n// ============ 登录页面 ============\nfunction LoginPage() {\n _s();\n const [username, setUsername] = useState('test');\n const [password, setPassword] = useState('test123');\n const [mode, setMode] = useState('solo');\n const [agents, setAgents] = useState([]);\n const [selectedAgents, setSelectedAgents] = useState([]);\n const navigate = useNavigate();\n\n // 扫描本机龙虾\n useEffect(() => {\n if (username) {\n scanAgents();\n }\n }, [username]);\n const scanAgents = async () => {\n try {\n // 传递 username 参数,获取绑定的龙虾信息\n const res = await axios.get(`${API_BASE}/user/scan-local-agents/?username=${username}`);\n setAgents(res.data.agents || []);\n } catch (error) {\n console.error('扫描龙虾失败:', error);\n }\n };\n const handleLogin = async e => {\n e.preventDefault();\n try {\n const payload = {\n username,\n password,\n mode\n };\n if (mode !== 'solo' && selectedAgents.length > 0) {\n payload.agent_ids = selectedAgents;\n }\n const res = await axios.post(`${API_BASE}/auth/login/`, payload);\n localStorage.setItem('token', res.data.token);\n localStorage.setItem('user', JSON.stringify(res.data.user));\n localStorage.setItem('sessions', JSON.stringify(res.data.sessions));\n localStorage.setItem('mode', res.data.mode);\n navigate('/meetings');\n } catch (error) {\n var _error$response, _error$response$data, _error$response2, _error$response2$data;\n alert('登录失败:' + (((_error$response = error.response) === null || _error$response === void 0 ? void 0 : (_error$response$data = _error$response.data) === null || _error$response$data === void 0 ? void 0 : _error$response$data.detail) || ((_error$response2 = error.response) === null || _error$response2 === void 0 ? void 0 : (_error$response2$data = _error$response2.data) === null || _error$response2$data === void 0 ? void 0 : _error$response2$data.error) || error.message));\n }\n };\n const toggleAgent = agentId => {\n setSelectedAgents(prev => prev.includes(agentId) ? prev.filter(id => id !== agentId) : [...prev, agentId]);\n };\n return /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.center,\n children: /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.card,\n children: [/*#__PURE__*/_jsxDEV(\"h1\", {\n style: styles.title,\n children: \"\\uD83C\\uDFDB\\uFE0F \\u9F99\\u867E\\u8BAE\\u4E8B\\u5385\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 74,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"form\", {\n onSubmit: handleLogin,\n style: {\n ...styles.form,\n flexDirection: 'column'\n },\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"text\",\n placeholder: \"\\u7528\\u6237\\u540D\",\n value: username,\n onChange: e => setUsername(e.target.value),\n style: styles.input,\n required: true\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 76,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"input\", {\n type: \"password\",\n placeholder: \"\\u5BC6\\u7801\",\n value: password,\n onChange: e => setPassword(e.target.value),\n style: styles.input,\n required: true\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 77,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: {\n margin: '15px 0'\n },\n children: [/*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'block',\n marginBottom: '10px',\n fontWeight: '600'\n },\n children: \"\\uD83C\\uDFAF \\u51FA\\u6218\\u6A21\\u5F0F\\uFF1A\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 81,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'block',\n marginBottom: '12px',\n cursor: 'pointer',\n padding: '10px',\n background: mode === 'solo' ? '#e7f3ff' : 'white',\n borderRadius: '8px',\n border: '1px solid #2196f3'\n },\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"radio\",\n name: \"mode\",\n value: \"solo\",\n checked: mode === 'solo',\n onChange: e => setMode(e.target.value)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 83,\n columnNumber: 15\n }, this), ' ', \"\\uD83E\\uDD77 \", /*#__PURE__*/_jsxDEV(\"strong\", {\n children: \"\\u5355\\u67AA\\u5339\\u9A6C\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 90,\n columnNumber: 23\n }, this), \"\\uFF08\\u4EBA\\u7C7B\\u5355\\u72EC\\u51FA\\u6218\\uFF0C\\u4E0D\\u5E26\\u9F99\\u867E\\uFF09\"]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 82,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'block',\n marginBottom: '12px',\n cursor: 'pointer',\n padding: '10px',\n background: mode === 'team' ? '#e7f3ff' : 'white',\n borderRadius: '8px',\n border: '1px solid #2196f3'\n },\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"radio\",\n name: \"mode\",\n value: \"team\",\n checked: mode === 'team',\n onChange: e => setMode(e.target.value)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 93,\n columnNumber: 15\n }, this), ' ', \"\\uD83D\\uDEE1\\uFE0F \", /*#__PURE__*/_jsxDEV(\"strong\", {\n children: \"\\u7EC4\\u961F\\u56E2\\u6218\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 100,\n columnNumber: 24\n }, this), \"\\uFF08\\u4EBA\\u7C7B + N \\u53EA\\u9F99\\u867E\\u4E00\\u8D77\\u51FA\\u6218\\uFF09\"]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 92,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'block',\n marginBottom: '12px',\n cursor: 'pointer',\n padding: '10px',\n background: mode === 'agent_only' ? '#e7f3ff' : 'white',\n borderRadius: '8px',\n border: '1px solid #2196f3'\n },\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"radio\",\n name: \"mode\",\n value: \"agent_only\",\n checked: mode === 'agent_only',\n onChange: e => setMode(e.target.value)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 103,\n columnNumber: 15\n }, this), ' ', \"\\u2694\\uFE0F \", /*#__PURE__*/_jsxDEV(\"strong\", {\n children: \"\\u72EC\\u5F53\\u4E00\\u9762\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 110,\n columnNumber: 23\n }, this), \"\\uFF08\\u9F99\\u867E\\u5355\\u72EC\\u51FA\\u5F81\\uFF0C\\u4EBA\\u7C7B\\u4E0D\\u4E0A\\u573A\\uFF09\"]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 102,\n columnNumber: 13\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 80,\n columnNumber: 11\n }, this), mode !== 'solo' && /*#__PURE__*/_jsxDEV(\"div\", {\n style: {\n margin: '15px 0',\n padding: '15px',\n background: '#f9f9f9',\n borderRadius: '8px'\n },\n children: [/*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'block',\n marginBottom: '10px',\n fontWeight: '600'\n },\n children: \"\\uD83E\\uDD90 \\u9009\\u62E9\\u9F99\\u867E\\u961F\\u53CB\\uFF1A\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 117,\n columnNumber: 15\n }, this), agents.length === 0 ? /*#__PURE__*/_jsxDEV(\"p\", {\n style: {\n color: '#999',\n fontSize: '14px'\n },\n children: \"\\u672A\\u627E\\u5230\\u53EF\\u7528\\u9F99\\u867E\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 119,\n columnNumber: 17\n }, this) : agents.map(a => /*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'flex',\n alignItems: 'center',\n marginBottom: '8px',\n cursor: 'pointer'\n },\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"checkbox\",\n checked: selectedAgents.includes(a.agent_id),\n onChange: () => toggleAgent(a.agent_id),\n style: {\n marginRight: '10px'\n }\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 123,\n columnNumber: 21\n }, this), /*#__PURE__*/_jsxDEV(\"span\", {\n style: {\n fontSize: '16px',\n marginRight: '8px'\n },\n children: a.agent_emoji || '🤖'\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 129,\n columnNumber: 21\n }, this), /*#__PURE__*/_jsxDEV(\"span\", {\n children: a.agent_id\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 130,\n columnNumber: 21\n }, this), /*#__PURE__*/_jsxDEV(\"span\", {\n style: {\n color: '#999',\n fontSize: '12px',\n marginLeft: '8px'\n },\n children: [\"(\", a.instance_name, \")\"]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 131,\n columnNumber: 21\n }, this)]\n }, a.agent_id, true, {\n fileName: _jsxFileName,\n lineNumber: 122,\n columnNumber: 19\n }, this)), selectedAgents.length > 0 && /*#__PURE__*/_jsxDEV(\"p\", {\n style: {\n marginTop: '10px',\n color: '#2196f3',\n fontWeight: '600'\n },\n children: [\"\\u5DF2\\u9009 \", selectedAgents.length, \" \\u53EA\\u9F99\\u867E\\u961F\\u53CB \\uD83E\\uDDB8\"]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 136,\n columnNumber: 17\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 116,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n type: \"submit\",\n style: styles.btn,\n children: \"\\uD83D\\uDE80 \\u767B\\u5F55\\u51FA\\u5F81\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 143,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 75,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 73,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 72,\n columnNumber: 5\n }, this);\n}\n\n// ============ 会议列表 ============\n_s(LoginPage, \"fZ8UTwO2MCkNd6X4nAqytpuamXM=\", false, function () {\n return [useNavigate];\n});\n_c = LoginPage;\nfunction MeetingList() {\n _s2();\n const [meetings, setMeetings] = useState([]);\n const [topic, setTopic] = useState('');\n const [autoAddAgents, setAutoAddAgents] = useState(true);\n const navigate = useNavigate();\n const token = localStorage.getItem('token');\n useEffect(() => {\n if (!token) {\n navigate('/login');\n return;\n }\n fetchMeetings();\n }, []);\n const fetchMeetings = async () => {\n try {\n const res = await axios.get(`${API_BASE}/meetings/`);\n setMeetings(res.data);\n } catch (error) {\n console.error(error);\n }\n };\n const createMeeting = async e => {\n e.preventDefault();\n try {\n // 获取当前登录的龙虾\n const sessions = JSON.parse(localStorage.getItem('sessions') || '[]');\n const agentIds = sessions.filter(s => s.session_type === 'agent').map(s => s.agent_id);\n const res = await axios.post(`${API_BASE}/meetings/`, {\n topic,\n auto_add_virtual_agents: agentIds.length === 0,\n // 只有没有龙虾时才添加虚拟的\n host_agent_id: agentIds.length > 0 ? agentIds[0] : null,\n // 第一只作为主持龙虾\n agent_ids: agentIds // 传递所有龙虾\n });\n navigate(`/meeting/${res.data.id}`);\n } catch (error) {\n var _error$response3, _error$response3$data;\n alert('创建失败:' + (((_error$response3 = error.response) === null || _error$response3 === void 0 ? void 0 : (_error$response3$data = _error$response3.data) === null || _error$response3$data === void 0 ? void 0 : _error$response3$data.detail) || error.message));\n }\n };\n const logout = () => {\n localStorage.removeItem('token');\n navigate('/login');\n };\n return /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.container,\n children: [/*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.header,\n children: [/*#__PURE__*/_jsxDEV(\"h1\", {\n children: \"\\uD83D\\uDCCB \\u6211\\u7684\\u4F1A\\u8BAE\\u5BA4\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 194,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n onClick: logout,\n style: styles.smallBtn,\n children: \"\\u9000\\u51FA\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 195,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 193,\n columnNumber: 7\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.card,\n children: [/*#__PURE__*/_jsxDEV(\"h2\", {\n children: \"\\u521B\\u5EFA\\u4F1A\\u8BAE\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 198,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"form\", {\n onSubmit: createMeeting,\n style: styles.form,\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"text\",\n placeholder: \"\\u4F1A\\u8BAE\\u4E3B\\u9898\",\n value: topic,\n onChange: e => setTopic(e.target.value),\n style: styles.input,\n required: true\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 200,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"label\", {\n style: {\n display: 'flex',\n alignItems: 'center',\n gap: '5px',\n whiteSpace: 'nowrap'\n },\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"checkbox\",\n checked: autoAddAgents,\n onChange: e => setAutoAddAgents(e.target.checked)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 202,\n columnNumber: 13\n }, this), \"\\u6DFB\\u52A0\\u865A\\u62DF\\u5750\\u5E2D\"]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 201,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n type: \"submit\",\n style: styles.btn,\n children: \"\\u521B\\u5EFA\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 209,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 199,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"p\", {\n style: {\n fontSize: '12px',\n color: '#666',\n marginTop: '10px'\n },\n children: \"\\uD83D\\uDCA1 \\u52FE\\u9009\\\"\\u6DFB\\u52A0\\u865A\\u62DF\\u5750\\u5E2D\\\"\\u4F1A\\u81EA\\u52A8\\u521B\\u5EFA 2 \\u4E2A\\u865A\\u62DF\\u9F99\\u867E\\u53C2\\u4F1A\\u8005\\uFF0C\\u65B9\\u4FBF\\u6D4B\\u8BD5 @ \\u529F\\u80FD\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 211,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 197,\n columnNumber: 7\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.list,\n children: meetings.map(m => /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.item,\n children: [/*#__PURE__*/_jsxDEV(\"div\", {\n children: [/*#__PURE__*/_jsxDEV(\"h3\", {\n children: m.topic\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 219,\n columnNumber: 15\n }, this), /*#__PURE__*/_jsxDEV(\"p\", {\n children: [\"\\u72B6\\u6001\\uFF1A\", m.status, \" | \\u9080\\u8BF7\\u7801\\uFF1A\", m.invite_code]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 220,\n columnNumber: 15\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 218,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n onClick: () => navigate(`/meeting/${m.id}`),\n style: styles.smallBtn,\n children: \"\\u8FDB\\u5165\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 222,\n columnNumber: 13\n }, this)]\n }, m.id, true, {\n fileName: _jsxFileName,\n lineNumber: 217,\n columnNumber: 11\n }, this))\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 215,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 192,\n columnNumber: 5\n }, this);\n}\n\n// ============ 会议室 ============\n_s2(MeetingList, \"uNteAZxXgR2pMaYhX5ze9khr1nw=\", false, function () {\n return [useNavigate];\n});\n_c2 = MeetingList;\nfunction MeetingRoom() {\n _s3();\n const {\n id\n } = useParams();\n const [messages, setMessages] = useState([]);\n const [content, setContent] = useState('');\n const [participants, setParticipants] = useState([]);\n const [meeting, setMeeting] = useState(null);\n const [hoveredSeat, setHoveredSeat] = useState(null);\n const token = localStorage.getItem('token');\n useEffect(() => {\n if (!token) return;\n fetchMeeting();\n fetchParticipants();\n fetchMessages();\n const interval = setInterval(fetchMessages, 1000);\n return () => clearInterval(interval);\n }, [id]);\n const fetchMeeting = async () => {\n try {\n const res = await axios.get(`${API_BASE}/meetings/${id}/`);\n setMeeting(res.data);\n } catch (error) {\n console.error(error);\n }\n };\n const fetchParticipants = async () => {\n try {\n const res = await axios.get(`${API_BASE}/meetings/${id}/participants/`);\n setParticipants(res.data);\n } catch (error) {\n console.error(error);\n }\n };\n const fetchMessages = async () => {\n try {\n const res = await axios.get(`${API_BASE}/meetings/${id}/messages/?last_id=0`);\n setMessages(res.data.messages || []);\n } catch (error) {\n console.error(error);\n }\n };\n const sendMessage = async e => {\n e.preventDefault();\n if (!content.trim()) return;\n try {\n await axios.post(`${API_BASE}/meetings/${id}/send_message/`, {\n content\n });\n setContent('');\n fetchMessages();\n } catch (error) {\n var _error$response4, _error$response4$data;\n alert('发送失败:' + (((_error$response4 = error.response) === null || _error$response4 === void 0 ? void 0 : (_error$response4$data = _error$response4.data) === null || _error$response4$data === void 0 ? void 0 : _error$response4$data.detail) || error.message));\n }\n };\n const mentionAgent = async (targetAgentId, agentName) => {\n const target = targetAgentId || prompt('@哪个 Agent?输入 agent_id:');\n if (!target || !content.trim()) return;\n const name = agentName || target;\n try {\n await axios.post(`${API_BASE}/meetings/${id}/mention_agent/`, {\n target_agent_id: target,\n content,\n sender_name: localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')).username : 'User'\n });\n setContent('');\n fetchMessages();\n alert(`✅ 已 @${name}`);\n } catch (error) {\n var _error$response5, _error$response5$data;\n alert('发送失败:' + (((_error$response5 = error.response) === null || _error$response5 === void 0 ? void 0 : (_error$response5$data = _error$response5.data) === null || _error$response5$data === void 0 ? void 0 : _error$response5$data.error) || error.message));\n }\n };\n const startMeeting = async () => {\n try {\n await axios.post(`${API_BASE}/meetings/${id}/start/`);\n fetchMeeting();\n alert('✅ 会议已开始');\n } catch (error) {\n var _error$response6, _error$response6$data;\n alert('开始失败:' + (((_error$response6 = error.response) === null || _error$response6 === void 0 ? void 0 : (_error$response6$data = _error$response6.data) === null || _error$response6$data === void 0 ? void 0 : _error$response6$data.error) || error.message));\n }\n };\n const endMeeting = async () => {\n if (!confirm('确定结束会议?')) return;\n try {\n await axios.post(`${API_BASE}/meetings/${id}/end/`);\n fetchMeeting();\n alert('✅ 会议已结束');\n } catch (error) {\n var _error$response7, _error$response7$data;\n alert('结束失败:' + (((_error$response7 = error.response) === null || _error$response7 === void 0 ? void 0 : (_error$response7$data = _error$response7.data) === null || _error$response7$data === void 0 ? void 0 : _error$response7$data.error) || error.message));\n }\n };\n const generateMinutes = async () => {\n try {\n const res = await axios.get(`${API_BASE}/meetings/${id}/minutes/?output=markdown`);\n const blob = new Blob([res.data.markdown], {\n type: 'text/markdown'\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `meeting-${id.slice(0, 8)}.md`;\n a.click();\n URL.revokeObjectURL(url);\n alert('✅ 纪要已导出');\n } catch (error) {\n alert('导出失败:' + error.message);\n }\n };\n return /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.container,\n children: [/*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.header,\n children: [/*#__PURE__*/_jsxDEV(Link, {\n to: \"/meetings\",\n style: styles.link,\n children: \"\\u2190 \\u8FD4\\u56DE\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 339,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"h1\", {\n children: (meeting === null || meeting === void 0 ? void 0 : meeting.topic) || '会议室'\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 340,\n columnNumber: 9\n }, this), meeting && /*#__PURE__*/_jsxDEV(\"span\", {\n style: styles.badge,\n children: meeting.status\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 341,\n columnNumber: 21\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 338,\n columnNumber: 7\n }, this), meeting && /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.infoCard,\n children: [/*#__PURE__*/_jsxDEV(\"p\", {\n children: [/*#__PURE__*/_jsxDEV(\"strong\", {\n children: \"ID:\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 346,\n columnNumber: 14\n }, this), \" \", meeting.id]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 346,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"p\", {\n children: [/*#__PURE__*/_jsxDEV(\"strong\", {\n children: \"\\u9080\\u8BF7\\u7801:\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 347,\n columnNumber: 14\n }, this), \" \", meeting.invite_code]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 347,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.btnGroup,\n children: [/*#__PURE__*/_jsxDEV(\"button\", {\n onClick: startMeeting,\n style: styles.btnGreen,\n children: \"\\u25B6\\uFE0F \\u5F00\\u59CB\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 349,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n onClick: endMeeting,\n style: styles.btnRed,\n children: \"\\u23F9\\uFE0F \\u7ED3\\u675F\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 350,\n columnNumber: 13\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n onClick: generateMinutes,\n style: styles.btnBlue,\n children: \"\\uD83D\\uDCCB \\u7EAA\\u8981\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 351,\n columnNumber: 13\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 348,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 345,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.card,\n children: [/*#__PURE__*/_jsxDEV(\"h2\", {\n children: [\"\\uD83E\\uDE91 \\u5EA7\\u4F4D\\u56FE \", /*#__PURE__*/_jsxDEV(\"span\", {\n style: styles.badge,\n children: participants.length\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 358,\n columnNumber: 20\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 358,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.seats,\n children: participants.map(p => /*#__PURE__*/_jsxDEV(\"div\", {\n style: {\n ...styles.seat,\n ...(hoveredSeat === p.id ? styles.seatHover : {})\n },\n onClick: () => {\n if (p.agent_id) {\n var _document$querySelect;\n setContent(`@${p.nickname} `);\n (_document$querySelect = document.querySelector('input[placeholder=\"输入消息...\"]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.focus();\n }\n },\n onMouseEnter: () => setHoveredSeat(p.id),\n onMouseLeave: () => setHoveredSeat(null),\n title: p.agent_id ? '点击 @ 此人' : '',\n children: [/*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.seatEmoji,\n children: p.agent_emoji || '👤'\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 374,\n columnNumber: 15\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.seatName,\n children: p.nickname\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 375,\n columnNumber: 15\n }, this), p.is_host && /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.hostBadge,\n children: \"\\uD83D\\uDC51\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 376,\n columnNumber: 29\n }, this)]\n }, p.id, true, {\n fileName: _jsxFileName,\n lineNumber: 361,\n columnNumber: 13\n }, this))\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 359,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 357,\n columnNumber: 7\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.card,\n children: [/*#__PURE__*/_jsxDEV(\"h2\", {\n children: [\"\\uD83D\\uDCAC \\u804A\\u5929 \", /*#__PURE__*/_jsxDEV(\"span\", {\n style: styles.badge,\n children: messages.length\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 384,\n columnNumber: 19\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 384,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.messages,\n children: messages.map(msg => /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.msg,\n children: [/*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.msgHeader,\n children: [/*#__PURE__*/_jsxDEV(\"strong\", {\n children: [msg.sender_emoji, \" \", msg.sender_name]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 389,\n columnNumber: 17\n }, this), /*#__PURE__*/_jsxDEV(\"span\", {\n style: styles.msgTime,\n children: new Date(msg.created_at).toLocaleTimeString()\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 390,\n columnNumber: 17\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 388,\n columnNumber: 15\n }, this), /*#__PURE__*/_jsxDEV(\"p\", {\n style: styles.msgContent,\n children: msg.content\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 392,\n columnNumber: 15\n }, this), msg.in_reply_to && /*#__PURE__*/_jsxDEV(\"div\", {\n style: styles.replyTag,\n children: [\"\\u21A9\\uFE0F \\u56DE\\u590D #\", msg.in_reply_to]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 393,\n columnNumber: 35\n }, this)]\n }, msg.id, true, {\n fileName: _jsxFileName,\n lineNumber: 387,\n columnNumber: 13\n }, this))\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 385,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(\"form\", {\n onSubmit: sendMessage,\n style: styles.form,\n children: [/*#__PURE__*/_jsxDEV(\"input\", {\n type: \"text\",\n placeholder: \"\\u8F93\\u5165\\u6D88\\u606F...\",\n value: content,\n onChange: e => setContent(e.target.value),\n style: styles.input\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 398,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n type: \"submit\",\n style: styles.btn,\n children: \"\\u53D1\\u9001\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 399,\n columnNumber: 11\n }, this), /*#__PURE__*/_jsxDEV(\"button\", {\n type: \"button\",\n onClick: mentionAgent,\n style: styles.btnPink,\n children: \"\\uD83D\\uDCCD @Agent\"\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 400,\n columnNumber: 11\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 397,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 383,\n columnNumber: 7\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 337,\n columnNumber: 5\n }, this);\n}\n\n// ============ App ============\n_s3(MeetingRoom, \"fh+UC+M8I83D9S4VTUsmF3cRwwg=\", false, function () {\n return [useParams];\n});\n_c3 = MeetingRoom;\nfunction App() {\n return /*#__PURE__*/_jsxDEV(Router, {\n children: /*#__PURE__*/_jsxDEV(Routes, {\n children: [/*#__PURE__*/_jsxDEV(Route, {\n path: \"/login\",\n element: /*#__PURE__*/_jsxDEV(LoginPage, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 412,\n columnNumber: 39\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 412,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(Route, {\n path: \"/meetings\",\n element: /*#__PURE__*/_jsxDEV(MeetingList, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 413,\n columnNumber: 42\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 413,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(Route, {\n path: \"/meeting/:id\",\n element: /*#__PURE__*/_jsxDEV(MeetingRoom, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 414,\n columnNumber: 45\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 414,\n columnNumber: 9\n }, this), /*#__PURE__*/_jsxDEV(Route, {\n path: \"/\",\n element: /*#__PURE__*/_jsxDEV(LoginPage, {}, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 415,\n columnNumber: 34\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 415,\n columnNumber: 9\n }, this)]\n }, void 0, true, {\n fileName: _jsxFileName,\n lineNumber: 411,\n columnNumber: 7\n }, this)\n }, void 0, false, {\n fileName: _jsxFileName,\n lineNumber: 410,\n columnNumber: 5\n }, this);\n}\n\n// ============ 样式 ============\n_c4 = App;\nconst styles = {\n center: {\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'\n },\n container: {\n maxWidth: '900px',\n margin: '0 auto',\n padding: '20px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif'\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: '15px',\n marginBottom: '20px'\n },\n card: {\n background: 'white',\n borderRadius: '12px',\n padding: '20px',\n marginBottom: '20px',\n boxShadow: '0 4px 6px rgba(0,0,0,0.1)'\n },\n infoCard: {\n background: '#e7f3ff',\n border: '1px solid #2196f3',\n borderRadius: '12px',\n padding: '15px',\n marginBottom: '20px'\n },\n title: {\n margin: '0 0 20px',\n color: '#1a365d',\n textAlign: 'center'\n },\n form: {\n display: 'flex',\n gap: '10px'\n },\n input: {\n flex: 1,\n padding: '12px',\n border: '2px solid #e2e8f0',\n borderRadius: '8px',\n fontSize: '14px'\n },\n btn: {\n padding: '12px 20px',\n background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n color: 'white',\n border: 'none',\n borderRadius: '8px',\n cursor: 'pointer',\n fontWeight: '600'\n },\n btnGreen: {\n padding: '8px 16px',\n background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer',\n marginRight: '8px'\n },\n btnRed: {\n padding: '8px 16px',\n background: 'linear-gradient(135deg, #eb3349 0%, #f45c43 100%)',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer',\n marginRight: '8px'\n },\n btnBlue: {\n padding: '8px 16px',\n background: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer'\n },\n btnPink: {\n padding: '8px 16px',\n background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer'\n },\n smallBtn: {\n padding: '8px 16px',\n background: '#edf2f7',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer'\n },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: '15px'\n },\n item: {\n background: 'white',\n borderRadius: '12px',\n padding: '20px',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n boxShadow: '0 4px 6px rgba(0,0,0,0.1)'\n },\n link: {\n color: '#4299e1',\n textDecoration: 'none',\n fontSize: '16px'\n },\n badge: {\n background: '#667eea',\n color: 'white',\n padding: '4px 10px',\n borderRadius: '20px',\n fontSize: '12px',\n fontWeight: '600'\n },\n btnGroup: {\n display: 'flex',\n marginTop: '10px'\n },\n seats: {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '15px',\n justifyContent: 'center'\n },\n seat: {\n background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n color: 'white',\n padding: '15px',\n borderRadius: '50%',\n width: '90px',\n height: '90px',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n textAlign: 'center',\n cursor: 'pointer',\n transition: 'transform 0.2s',\n ':hover': {\n transform: 'scale(1.1)'\n }\n },\n seatHover: {\n transform: 'scale(1.05)'\n },\n seatEmoji: {\n fontSize: '28px',\n marginBottom: '5px'\n },\n seatName: {\n fontSize: '12px',\n fontWeight: '600'\n },\n hostBadge: {\n fontSize: '10px',\n opacity: '0.8'\n },\n messages: {\n maxHeight: '400px',\n overflowY: 'auto',\n marginBottom: '15px'\n },\n msg: {\n padding: '12px',\n background: '#f7fafc',\n borderRadius: '8px',\n marginBottom: '10px'\n },\n msgHeader: {\n display: 'flex',\n justifyContent: 'space-between',\n marginBottom: '5px'\n },\n msgContent: {\n margin: '5px 0',\n color: '#4a5568'\n },\n msgTime: {\n fontSize: '12px',\n color: '#a0aec0'\n },\n replyTag: {\n fontSize: '11px',\n color: '#a0aec0',\n marginTop: '5px'\n }\n};\nexport default App;\nvar _c, _c2, _c3, _c4;\n$RefreshReg$(_c, \"LoginPage\");\n$RefreshReg$(_c2, \"MeetingList\");\n$RefreshReg$(_c3, \"MeetingRoom\");\n$RefreshReg$(_c4, \"App\");","map":{"version":3,"names":["React","useState","useEffect","BrowserRouter","Router","Routes","Route","Link","useNavigate","useParams","axios","jsxDEV","_jsxDEV","API_BASE","interceptors","request","use","config","token","localStorage","getItem","headers","Authorization","LoginPage","_s","username","setUsername","password","setPassword","mode","setMode","agents","setAgents","selectedAgents","setSelectedAgents","navigate","scanAgents","res","get","data","error","console","handleLogin","e","preventDefault","payload","length","agent_ids","post","setItem","JSON","stringify","user","sessions","_error$response","_error$response$data","_error$response2","_error$response2$data","alert","response","detail","message","toggleAgent","agentId","prev","includes","filter","id","style","styles","center","children","card","title","fileName","_jsxFileName","lineNumber","columnNumber","onSubmit","form","flexDirection","type","placeholder","value","onChange","target","input","required","margin","display","marginBottom","fontWeight","cursor","padding","background","borderRadius","border","name","checked","color","fontSize","map","a","alignItems","agent_id","marginRight","agent_emoji","marginLeft","instance_name","marginTop","btn","_c","MeetingList","_s2","meetings","setMeetings","topic","setTopic","autoAddAgents","setAutoAddAgents","fetchMeetings","createMeeting","parse","agentIds","s","session_type","auto_add_virtual_agents","host_agent_id","_error$response3","_error$response3$data","logout","removeItem","container","header","onClick","smallBtn","gap","whiteSpace","list","m","item","status","invite_code","_c2","MeetingRoom","_s3","messages","setMessages","content","setContent","participants","setParticipants","meeting","setMeeting","hoveredSeat","setHoveredSeat","fetchMeeting","fetchParticipants","fetchMessages","interval","setInterval","clearInterval","sendMessage","trim","_error$response4","_error$response4$data","mentionAgent","targetAgentId","agentName","prompt","target_agent_id","sender_name","_error$response5","_error$response5$data","startMeeting","_error$response6","_error$response6$data","endMeeting","confirm","_error$response7","_error$response7$data","generateMinutes","blob","Blob","markdown","url","URL","createObjectURL","document","createElement","href","download","slice","click","revokeObjectURL","to","link","badge","infoCard","btnGroup","btnGreen","btnRed","btnBlue","seats","p","seat","seatHover","_document$querySelect","nickname","querySelector","focus","onMouseEnter","onMouseLeave","seatEmoji","seatName","is_host","hostBadge","msg","msgHeader","sender_emoji","msgTime","Date","created_at","toLocaleTimeString","msgContent","in_reply_to","replyTag","btnPink","_c3","App","path","element","_c4","justifyContent","minHeight","maxWidth","fontFamily","boxShadow","textAlign","flex","textDecoration","flexWrap","width","height","transition","transform","opacity","maxHeight","overflowY","$RefreshReg$"],"sources":["/home/node/.openclaw/workspace/flying-hero/projects/meeting-room/frontend/src/App.js"],"sourcesContent":["import React, { useState, useEffect } from 'react';\nimport { BrowserRouter as Router, Routes, Route, Link, useNavigate, useParams } from 'react-router-dom';\nimport axios from 'axios';\n\nconst API_BASE = 'http://localhost:8000/api/v1';\n\naxios.interceptors.request.use(config => {\n const token = localStorage.getItem('token');\n if (token) config.headers.Authorization = `Bearer ${token}`;\n return config;\n});\n\n// ============ 登录页面 ============\nfunction LoginPage() {\n const [username, setUsername] = useState('test');\n const [password, setPassword] = useState('test123');\n const [mode, setMode] = useState('solo');\n const [agents, setAgents] = useState([]);\n const [selectedAgents, setSelectedAgents] = useState([]);\n const navigate = useNavigate();\n\n // 扫描本机龙虾\n useEffect(() => {\n if (username) {\n scanAgents();\n }\n }, [username]);\n\n const scanAgents = async () => {\n try {\n // 传递 username 参数,获取绑定的龙虾信息\n const res = await axios.get(`${API_BASE}/user/scan-local-agents/?username=${username}`);\n setAgents(res.data.agents || []);\n } catch (error) {\n console.error('扫描龙虾失败:', error);\n }\n };\n\n const handleLogin = async (e) => {\n e.preventDefault();\n try {\n const payload = {\n username,\n password,\n mode\n };\n \n if (mode !== 'solo' && selectedAgents.length > 0) {\n payload.agent_ids = selectedAgents;\n }\n \n const res = await axios.post(`${API_BASE}/auth/login/`, payload);\n localStorage.setItem('token', res.data.token);\n localStorage.setItem('user', JSON.stringify(res.data.user));\n localStorage.setItem('sessions', JSON.stringify(res.data.sessions));\n localStorage.setItem('mode', res.data.mode);\n navigate('/meetings');\n } catch (error) {\n alert('登录失败:' + (error.response?.data?.detail || error.response?.data?.error || error.message));\n }\n };\n\n const toggleAgent = (agentId) => {\n setSelectedAgents(prev => \n prev.includes(agentId) \n ? prev.filter(id => id !== agentId)\n : [...prev, agentId]\n );\n };\n\n return (\n
\n 💡 勾选\"添加虚拟坐席\"会自动创建 2 个虚拟龙虾参会者,方便测试 @ 功能\n
\n状态:{m.status} | 邀请码:{m.invite_code}
\nID: {meeting.id}
\n邀请码: {meeting.invite_code}
\n{msg.content}
\n {msg.in_reply_to &&